@moveo-ai/web-client 0.105.0-true.6 → 0.105.0-true.8
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/dist/147.min.js.LICENSE.txt +1 -1
- package/dist/329.min.js.LICENSE.txt +1 -1
- package/dist/882.min.js +1 -1
- package/dist/882.min.js.LICENSE.txt +1 -1
- package/dist/882.min.js.map +1 -1
- package/dist/929.min.js.LICENSE.txt +1 -1
- package/dist/954.min.js.LICENSE.txt +1 -1
- package/dist/audio-client.min.js.LICENSE.txt +1 -1
- package/dist/iframe.min.js.LICENSE.txt +1 -1
- package/dist/iframe.v2.min.js +1 -1
- package/dist/iframe.v2.min.js.LICENSE.txt +1 -1
- package/dist/iframe.v2.min.js.map +1 -1
- package/dist/index.html +1 -1
- package/dist/preview/index.html +2 -2
- package/dist/preview.css.min.js.LICENSE.txt +1 -1
- package/dist/telnyx-client.min.js.LICENSE.txt +1 -1
- package/dist/web-client.min.js.LICENSE.txt +1 -1
- package/dist/webrtc-client.min.js +1 -1
- package/dist/webrtc-client.min.js.LICENSE.txt +1 -1
- package/dist/webrtc-client.min.js.map +1 -1
- package/package.json +1 -1
package/dist/882.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/*! For license information please see 882.min.js.LICENSE.txt */
|
|
2
|
-
"use strict";(this.webpackChunkMoveoAI=this.webpackChunkMoveoAI||[]).push([[882],{2229:function(t,e,i){i.d(e,{h:function(){return n}});class n{constructor(){this.analyserNode=null,this.audioSource=null,this.animationId=null,this.audioContext=null}start(t,e){try{this.audioContext=new AudioContext,this.audioSource=this.audioContext.createMediaStreamSource(t),this.analyserNode=this.audioContext.createAnalyser(),this.analyserNode.fftSize=256,this.analyserNode.smoothingTimeConstant=.3,this.audioSource.connect(this.analyserNode);const i=new Float32Array(this.analyserNode.fftSize),n=()=>{if(!this.analyserNode)return void this.stop();this.analyserNode.getFloatTimeDomainData(i);let t=0;for(const e of i)t+=e*e;const s=Math.sqrt(t/i.length),o=Math.min(1,10*s);e((o>.01?o:0).toFixed(3)),this.animationId=requestAnimationFrame(n)};n()}catch(t){}}stop(){if(null!==this.animationId&&(cancelAnimationFrame(this.animationId),this.animationId=null),this.audioSource){try{this.audioSource.disconnect()}catch(t){}this.audioSource=null}this.audioContext&&(this.audioContext.close().catch(()=>{}),this.audioContext=null),this.analyserNode=null}}},6815:function(t,e,i){i.d(e,{n:function(){return n}});const n={NotAllowedError:"Microphone permission denied",NotFoundError:"No microphone found",NotReadableError:"Microphone is in use by another application",OverconstrainedError:"Microphone does not support required settings"}},3564:function(t,e,i){i.d(e,{M:function(){return s}});const n={"high-rtt":"High network latency detected","low-mos":"Poor call quality detected","high-jitter":"Network instability detected","high-packet-loss":"Packet loss detected"};class s{constructor(t,e,i,n=5e3){this.pc=t,this.onWarning=e,this.onWarningCleared=i,this.pollIntervalMs=n,this.interval=null,this.prevStats=null,this.activeWarnings=new Set}start(){this.interval=setInterval(()=>{this.pollStats()},this.pollIntervalMs)}stop(){null!==this.interval&&(clearInterval(this.interval),this.interval=null),this.prevStats=null,this.activeWarnings.clear()}async pollStats(){try{const t=await this.pc.getStats();let e=null,i=null,n=0,s=0;t.forEach(t=>{var o,r,a;if("candidate-pair"===t.type&&t.nominated){const i=t.currentRoundTripTime;e=null!=i?1e3*i:null}if("inbound-rtp"===t.type&&"audio"===t.kind){const e=t;i=null!==(o=e.jitter)&&void 0!==o?o:null,n=null!==(r=e.packetsReceived)&&void 0!==r?r:0,s=null!==(a=e.packetsLost)&&void 0!==a?a:0}});let o=0;if(this.prevStats){const t=n-this.prevStats.packetsReceived,e=s-this.prevStats.packetsLost,i=t+e;o=i>0?e/i:0}if(this.prevStats={packetsReceived:n,packetsLost:s},this.checkWarning("high-rtt",null!==e&&e>400),this.checkWarning("high-jitter",null!==i&&i>.03),this.checkWarning("high-packet-loss",o>.03),null!==e&&null!==i){const t=93.2-1e3*i*2-100*o*40-.1*Math.max(0,e-150),n=1+.035*t+t*(t-60)*(100-t)*7e-6;this.checkWarning("low-mos",n<3)}}catch(t){}}checkWarning(t,e){e&&!this.activeWarnings.has(t)?(this.activeWarnings.add(t),this.onWarning(t,n[t]||t)):!e&&this.activeWarnings.has(t)&&(this.activeWarnings.delete(t),this.onWarningCleared(t))}}},6845:function(t,e,i){i.d(e,{PA:function(){return n},TM:function(){return o},Xq:function(){return s}});class n extends Error{constructor(t="Voice service is busy, please try again."){super(t),this.name="ServerBusyError"}}class s extends Error{constructor(t){super(t),this.name="ConfigUnavailableError"}}class o extends Error{constructor(t="Too many attempts. Please try again in a moment."){super(t),this.name="RateLimitedError"}}},7943:function(t,e,i){i.d(e,{$:function(){return n}});class n{constructor(t,e,i,n=1e3,s=3){this.pc=t,this.onStall=e,this.onRecover=i,this.pollIntervalMs=n,this.flatPollsThreshold=s,this.interval=null,this.prevTimestamp=-1,this.flatPolls=0,this.stalled=!1}start(){this.interval=setInterval(()=>{this.poll()},this.pollIntervalMs)}stop(){null!==this.interval&&(clearInterval(this.interval),this.interval=null),this.prevTimestamp=-1,this.flatPolls=0,this.stalled=!1}async poll(){try{let t;if((await this.pc.getStats()).forEach(e=>{"candidate-pair"===e.type&&e.nominated&&(t=e.lastPacketReceivedTimestamp)}),void 0===t)return;this.prevTimestamp>=0&&t===this.prevTimestamp?(this.flatPolls++,this.flatPolls>=this.flatPollsThreshold&&!this.stalled&&(this.stalled=!0,this.onStall())):(this.stalled&&(this.stalled=!1,this.onRecover()),this.flatPolls=0),this.prevTimestamp=t}catch(t){}}}},4623:function(t,e,i){i.d(e,{g:function(){return n}});class n{constructor(t,e,i,n,s){this.url=t,this.sessionToken=e,this.onMessage=i,this.onClose=n,this.onError=s,this.ws=null}connect(){return new Promise((t,e)=>{const i=`${this.url}?token=${encodeURIComponent(this.sessionToken)}`;this.ws=new WebSocket(i);const n=setTimeout(()=>{var t;null===(t=this.ws)||void 0===t||t.close(),e(new Error("Signaling connection timed out"))},1e4);this.ws.onopen=()=>{},this.ws.onmessage=e=>{let i;try{i=JSON.parse(e.data)}catch(t){return}"welcome"===i.type&&(clearTimeout(n),t()),this.onMessage(i)},this.ws.onerror=t=>{clearTimeout(n),this.onError(t),e(new Error("Signaling WebSocket error"))},this.ws.onclose=t=>{clearTimeout(n),e(new Error("Signaling WebSocket closed before connection established")),this.onClose(t.code,t.reason)}})}connectForReconnect(t){return new Promise((e,i)=>{const n=`${this.url}?token=${encodeURIComponent(this.sessionToken)}`;this.ws=new WebSocket(n);let s=!1;const o=setTimeout(()=>{var t;s||(s=!0,null===(t=this.ws)||void 0===t||t.close(),i(new Error("Signaling reconnect timed out")))},1e4);this.ws.onopen=()=>{var e;null===(e=this.ws)||void 0===e||e.send(JSON.stringify(t))},this.ws.onmessage=t=>{let n;try{n=JSON.parse(t.data)}catch(t){return}this.onMessage(n),s||("reconnect_ack"===n.type?(s=!0,clearTimeout(o),e()):"error"===n.type&&(s=!0,clearTimeout(o),i(new Error(`reconnect rejected: ${n.error.code}`))))},this.ws.onerror=t=>{this.onError(t),s||(s=!0,clearTimeout(o),i(new Error("Signaling WebSocket error")))},this.ws.onclose=t=>{s||(s=!0,clearTimeout(o),i(new Error("Signaling WebSocket closed before reconnect established"))),this.onClose(t.code,t.reason)}})}send(t){this.ws&&this.ws.readyState===WebSocket.OPEN&&this.ws.send(JSON.stringify(t))}close(){this.ws&&(this.ws.onclose=null,this.ws.onerror=null,this.ws.onmessage=null,this.ws.close(1e3,"Client closed"),this.ws=null)}get isConnected(){var t;return(null===(t=this.ws)||void 0===t?void 0:t.readyState)===WebSocket.OPEN}}},5882:function(t,e,i){i.r(e),i.d(e,{WebRTCProvider:function(){return u}}),i(4954);var n=i(2229),s=i(6815),o=i(3564),r=i(6845),a=i(7943),c=i(4623);const l=new Set(["session_expired","session_inactive","invalid_credential","max_recovery_exceeded","unexpected_first_frame"]),h={session_expired:"Your session has expired.",session_inactive:"The call has ended.",invalid_credential:"Could not resume the call.",max_recovery_exceeded:"Could not resume the call after several attempts.",unexpected_first_frame:"Could not resume the call."},d=Object.assign(Object.assign({},s.n),{setRemoteDescription:"Connection setup failed",setLocalDescription:"Connection setup failed",addIceCandidate:"Network configuration failed",createOffer:"Browser audio setup failed"});class u{constructor(t,e){this.wsUrl=t,this.fetchConfig=e,this.pc=null,this.signaling=null,this.localStream=null,this.remoteAudio=null,this.remoteAudioMonitor=null,this.audioMonitor=null,this.qualityMonitor=null,this.stallDetector=null,this.pendingCandidates=[],this.state="idle",this.eventListeners=new Map,this.iceRestarting=!1,this.iceRestartAttempts=0,this.iceRestartTimeout=null,this.connectResolve=null,this.connectReject=null,this.drainReceived=!1,this.reconnectAttempts=0,this.reconnectErrorCode=null}setConfig(t){this.config=t}async connect(){if(!this.config)throw new Error("Voice config not set; call setConfig() before connect().");this.setState("connecting");try{this.localStream=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0}})}catch(t){const e=t instanceof Error?t.name:"",i=d[e]||"Microphone access failed";throw this.emitError(i),new Error(i)}this.signaling=new c.g(this.wsUrl,this.config.sessionToken,this.handleSignalingMessage.bind(this),this.handleSignalingClose.bind(this),this.handleSignalingError.bind(this));try{await this.signaling.connect()}catch(t){this.cleanupLocalStream();const e=t instanceof Error?t.message:"Signaling connection failed";throw this.emitError(e),new Error(e)}this.pc=new RTCPeerConnection({iceServers:this.config.iceServers,iceTransportPolicy:this.config.iceTransportPolicy||"relay"});const t=this.localStream.getAudioTracks()[0];this.pc.addTrack(t,this.localStream),this.pc.ontrack=this.handleRemoteTrack.bind(this),this.pc.onicecandidate=t=>{var e;t.candidate&&(null===(e=this.signaling)||void 0===e||e.send({type:"ice",candidate:t.candidate.toJSON()}))},this.pc.onconnectionstatechange=this.handleConnectionStateChange.bind(this);try{const t=await this.pc.createOffer();if(!t.sdp)throw new Error("createOffer returned no SDP");await this.pc.setLocalDescription(t),this.signaling.send({type:"offer",sdp:t.sdp})}catch(t){this.cleanup();const e=t instanceof Error?t.name:"createOffer",i=d[e]||"Failed to create call offer";throw this.emitError(i),new Error(i)}return this.audioMonitor=new n.h,this.audioMonitor.start(this.localStream,t=>{this.emit("audio-level",t,Number(t))}),new Promise((t,e)=>{this.connectResolve=t,this.connectReject=e})}disconnect(){this.disconnectInternal(!1)}disconnectInternal(t){var e,i,n,s,o,r,a;if(!t)try{null===(e=this.signaling)||void 0===e||e.send({type:"hangup"})}catch(t){}try{null===(i=this.qualityMonitor)||void 0===i||i.stop(),this.qualityMonitor=null}catch(t){}try{null===(n=this.stallDetector)||void 0===n||n.stop(),this.stallDetector=null}catch(t){}try{null===(s=this.audioMonitor)||void 0===s||s.stop(),this.audioMonitor=null}catch(t){}try{null===(o=this.remoteAudioMonitor)||void 0===o||o.stop(),this.remoteAudioMonitor=null}catch(t){}try{null===(r=this.pc)||void 0===r||r.close(),this.pc=null}catch(t){}this.cleanupLocalStream();try{null===(a=this.signaling)||void 0===a||a.close(),this.signaling=null}catch(t){}try{this.remoteAudio&&(this.remoteAudio.srcObject=null,this.remoteAudio.remove(),this.remoteAudio=null)}catch(t){}this.clearIceRestart(),this.iceRestartAttempts=0,this.connectReject&&this.connectReject(new Error("Call disconnected before connection completed")),this.connectResolve=null,this.connectReject=null,this.drainReceived=!1,this.reconnectAttempts=0,this.reconnectErrorCode=null,this.originalSessionID=void 0,this.originalConnectionID=void 0,this.pendingCandidates=[],this.setState("idle")}toggleMute(){var t,e;const i=null===(t=this.localStream)||void 0===t?void 0:t.getAudioTracks()[0];if(i){i.enabled=!i.enabled;const t=!i.enabled;return null===(e=this.signaling)||void 0===e||e.send({type:"mute",muted:t}),t}return!1}isMuted(){var t,e,i;return!(null===(i=null===(e=null===(t=this.localStream)||void 0===t?void 0:t.getAudioTracks()[0])||void 0===e?void 0:e.enabled)||void 0===i||i)}getLocalStream(){return this.localStream}sendDigits(t){var e;for(const i of t)null===(e=this.signaling)||void 0===e||e.send({type:"dtmf",digit:i})}on(t,e){var i;const n=null!==(i=this.eventListeners.get(t))&&void 0!==i?i:new Set;return n.add(e),this.eventListeners.set(t,n),()=>this.off(t,e)}off(t,e){const i=this.eventListeners.get(t);i&&(i.delete(e),0===i.size&&this.eventListeners.delete(t))}handleRemoteTrack(t){this.remoteAudio=document.createElement("audio"),this.remoteAudio.srcObject=t.streams[0],this.remoteAudio.autoplay=!0,document.body.appendChild(this.remoteAudio),this.remoteAudioMonitor=new n.h,this.remoteAudioMonitor.start(t.streams[0],t=>{Number(t)>.02&&this.emit("remote-audio",t,Number(t))})}handleConnectionStateChange(){var t,e,i,n;if(this.pc)switch(this.pc.connectionState){case"connecting":this.setState("connecting");break;case"connected":{const n="reconnecting"===this.state;this.setState("connected"),this.clearIceRestart(),this.iceRestartAttempts=0,n&&this.emit("warning-cleared",void 0,"reconnect"),null===(t=this.qualityMonitor)||void 0===t||t.stop(),this.qualityMonitor=new o.M(this.pc,(t,e)=>this.emit("warning",e,t),t=>this.emit("warning-cleared",void 0,t)),this.qualityMonitor.start(),null===(e=this.stallDetector)||void 0===e||e.stop(),this.stallDetector=new a.$(this.pc,()=>{this.triggerIceRestart()},()=>{this.clearIceRestart(),this.iceRestartAttempts=0,"reconnecting"===this.state&&this.setState("connected"),this.emit("warning-cleared",void 0,"reconnect")}),this.stallDetector.start(),null===(i=this.connectResolve)||void 0===i||i.call(this),this.connectResolve=null,this.connectReject=null;break}case"disconnected":if(this.drainReceived)break;this.triggerIceRestart();break;case"failed":if(this.drainReceived){null===(n=this.connectReject)||void 0===n||n.call(this,new Error("ICE connection failed")),this.connectResolve=null,this.connectReject=null;break}if(this.connectReject){this.connectReject(new Error("ICE connection failed")),this.connectResolve=null,this.connectReject=null,this.emitError("Connection failed"),this.cleanup();break}this.iceRestarting?this.onRestartAttemptExpired():this.triggerIceRestart();break;case"closed":this.cleanup()}}async triggerIceRestart(){if(this.drainReceived||!this.pc||!this.signaling)return;if(this.iceRestarting||this.iceRestartAttempts>=2)return;if(!this.signaling.isConnected)return this.emitError("Connection lost"),void this.cleanup();const t=this.pc,e=this.signaling;this.iceRestarting=!0,this.iceRestartAttempts++,this.setState("reconnecting"),this.emit("warning","Reconnecting…","reconnect");try{const i=await t.createOffer({iceRestart:!0});if(!i.sdp)throw new Error("createOffer returned no SDP");await t.setLocalDescription(i),e.send({type:"offer",sdp:i.sdp})}catch(t){return this.iceRestarting=!1,this.emitError("Connection lost"),void this.cleanup()}this.iceRestarting&&this.pc===t&&(this.iceRestartTimeout=setTimeout(()=>{this.onRestartAttemptExpired()},1e4))}onRestartAttemptExpired(){this.iceRestartTimeout&&(clearTimeout(this.iceRestartTimeout),this.iceRestartTimeout=null),this.iceRestarting=!1,this.iceRestartAttempts<2?this.triggerIceRestart():(this.emitError("Connection lost"),this.cleanup())}clearIceRestart(){this.iceRestartTimeout&&(clearTimeout(this.iceRestartTimeout),this.iceRestartTimeout=null),this.iceRestarting=!1}handleSignalingMessage(t){var e;switch(t.type){case"answer":this.handleAnswer(t.sdp);break;case"ice":this.addIceCandidate(t.candidate);break;case"transcript":this.emit("transcript",t.text,{text:t.text,is_final:t.is_final});break;case"agent_response":this.emit("agent-response",t.text,{text:t.text,message_id:t.message_id});break;case"hangup":this.disconnectInternal(!0);break;case"session_ready":this.originalSessionID=t.session_id,this.originalConnectionID=t.connection_id;break;case"reconnect_ack":this.originalConnectionID=t.connection_id;break;case"drain":this.handleDrain(t);break;case"error":this.drainReceived?(this.reconnectErrorCode=t.error.code,null===(e=this.connectReject)||void 0===e||e.call(this,new Error(`reconnect rejected: ${t.error.code}`))):this.emitError(t.error.message)}}handleSignalingClose(t,e){var i;this.drainReceived?null===(i=this.connectReject)||void 0===i||i.call(this,new Error("Signaling closed during resume")):"idle"!==this.state&&this.disconnect()}handleSignalingError(t){"connected"===this.state&&this.emitError("Signaling error")}async handleDrain(t){if(this.drainReceived)return;if("connected"!==this.state&&"reconnecting"!==this.state||void 0===this.originalSessionID||void 0===this.originalConnectionID)return this.emitError("Could not resume the call."),void this.disconnectInternal(!0);this.drainReceived=!0,this.setState("reconnecting"),this.clearIceRestart();const e=Math.max(0,t.reconnect_delay_ms||0);await this.sleep(Math.random()*e),this.drainReceived&&await this.reconnect()}async reconnect(){var t,e,i;try{null===(t=this.pc)||void 0===t||t.getSenders().forEach(t=>{var e;t.track&&(null===(e=this.pc)||void 0===e||e.removeTrack(t))})}catch(t){}const n=null===(e=this.localStream)||void 0===e?void 0:e.getAudioTracks()[0];if(!n||"live"!==n.readyState)return this.emitError("Microphone is no longer available."),void this.disconnectInternal(!0);for(this.teardownForReconnect();this.reconnectAttempts<3;){this.reconnectErrorCode=null;try{return this.config=await this.fetchConfig(),this.signaling=new c.g(this.wsUrl,this.config.sessionToken,this.handleSignalingMessage.bind(this),this.handleSignalingClose.bind(this),this.handleSignalingError.bind(this)),await this.signaling.connectForReconnect({type:"reconnect",session_id:this.originalSessionID,connection_id:this.originalConnectionID}),await this.buildReconnectPeerConnection(n),this.reconnectAttempts=0,this.drainReceived=!1,void this.emit("warning-cleared",void 0,"reconnect")}catch(t){const e=this.reconnectErrorCode;if(e&&l.has(e))return this.emitError(null!==(i=h[e])&&void 0!==i?i:"Could not resume the call."),void this.disconnectInternal(!0);if(t instanceof r.Xq)return this.emitError(t.message),void this.disconnectInternal(!0);const n=t instanceof r.PA?t.message:"Reconnecting…";if(this.emit("warning",n,"reconnect"),this.teardownForReconnect(),this.reconnectAttempts++,this.reconnectAttempts>=3)break;if(await this.sleep(500*this.reconnectAttempts),!this.drainReceived)return}}this.emitError("Could not resume the call."),this.disconnectInternal(!0)}buildReconnectPeerConnection(t){return this.pc=new RTCPeerConnection({iceServers:this.config.iceServers,iceTransportPolicy:this.config.iceTransportPolicy||"relay"}),this.pc.addTrack(t,this.localStream),this.pc.ontrack=this.handleRemoteTrack.bind(this),this.pc.onicecandidate=t=>{var e;t.candidate&&(null===(e=this.signaling)||void 0===e||e.send({type:"ice",candidate:t.candidate.toJSON()}))},this.pc.onconnectionstatechange=this.handleConnectionStateChange.bind(this),this.audioMonitor=new n.h,this.audioMonitor.start(this.localStream,t=>{this.emit("audio-level",t,Number(t))}),new Promise((t,e)=>{const i=setTimeout(()=>{this.connectResolve=null,this.connectReject=null,e(new Error("Reconnect media resume timed out"))},15e3);this.connectResolve=()=>{clearTimeout(i),t()},this.connectReject=t=>{clearTimeout(i),e(t)},(async()=>{var t;try{const t=await this.pc.createOffer();if(!t.sdp)throw new Error("createOffer returned no SDP");await this.pc.setLocalDescription(t),this.signaling.send({type:"offer",sdp:t.sdp})}catch(e){const i=e instanceof Error?e.name:"createOffer";null===(t=this.connectReject)||void 0===t||t.call(this,new Error(d[i]||"Failed to create call offer"))}})()})}teardownForReconnect(){var t,e,i,n,s,o;try{null===(t=this.qualityMonitor)||void 0===t||t.stop(),this.qualityMonitor=null}catch(t){}try{null===(e=this.stallDetector)||void 0===e||e.stop(),this.stallDetector=null}catch(t){}try{null===(i=this.audioMonitor)||void 0===i||i.stop(),this.audioMonitor=null}catch(t){}try{null===(n=this.remoteAudioMonitor)||void 0===n||n.stop(),this.remoteAudioMonitor=null}catch(t){}try{null===(s=this.pc)||void 0===s||s.close(),this.pc=null}catch(t){}try{null===(o=this.signaling)||void 0===o||o.close(),this.signaling=null}catch(t){}try{this.remoteAudio&&(this.remoteAudio.srcObject=null,this.remoteAudio.remove(),this.remoteAudio=null)}catch(t){}this.clearIceRestart(),this.iceRestartAttempts=0,this.connectResolve=null,this.connectReject=null,this.pendingCandidates=[]}sleep(t){return new Promise(e=>setTimeout(e,t))}async handleAnswer(t){if(this.pc)try{await this.pc.setRemoteDescription({type:"answer",sdp:t}),await this.flushPendingCandidates()}catch(t){this.emitError(d.setRemoteDescription)}}async addIceCandidate(t){var e;if(null===(e=this.pc)||void 0===e?void 0:e.remoteDescription)try{await this.pc.addIceCandidate(t)}catch(t){}else this.pendingCandidates.push(t)}async flushPendingCandidates(){for(const t of this.pendingCandidates){if(!this.pc)break;try{await this.pc.addIceCandidate(t)}catch(t){}}this.pendingCandidates=[]}cleanup(){this.disconnect()}cleanupLocalStream(){var t;try{null===(t=this.localStream)||void 0===t||t.getTracks().forEach(t=>t.stop()),this.localStream=null}catch(t){}}setState(t){this.state=t,this.emit("state-change",t,t)}emitError(t){this.emit("error",t),this.setState("error")}emit(t,e,i){const n={state:t,message:e,data:i},s=t=>{const e=this.eventListeners.get(t);e&&e.forEach(t=>{try{t(n)}catch(t){}})};s(t),s("any")}}}}]);
|
|
2
|
+
"use strict";(this.webpackChunkMoveoAI=this.webpackChunkMoveoAI||[]).push([[882],{2229:function(t,e,i){i.d(e,{h:function(){return n}});class n{constructor(){this.analyserNode=null,this.audioSource=null,this.animationId=null,this.audioContext=null}start(t,e){try{this.audioContext=new AudioContext,this.audioSource=this.audioContext.createMediaStreamSource(t),this.analyserNode=this.audioContext.createAnalyser(),this.analyserNode.fftSize=256,this.analyserNode.smoothingTimeConstant=.3,this.audioSource.connect(this.analyserNode);const i=new Float32Array(this.analyserNode.fftSize),n=()=>{if(!this.analyserNode)return void this.stop();this.analyserNode.getFloatTimeDomainData(i);let t=0;for(const e of i)t+=e*e;const s=Math.sqrt(t/i.length),o=Math.min(1,10*s);e((o>.01?o:0).toFixed(3)),this.animationId=requestAnimationFrame(n)};n()}catch(t){}}stop(){if(null!==this.animationId&&(cancelAnimationFrame(this.animationId),this.animationId=null),this.audioSource){try{this.audioSource.disconnect()}catch(t){}this.audioSource=null}this.audioContext&&(this.audioContext.close().catch(()=>{}),this.audioContext=null),this.analyserNode=null}}},6815:function(t,e,i){i.d(e,{n:function(){return n}});const n={NotAllowedError:"Microphone permission denied",NotFoundError:"No microphone found",NotReadableError:"Microphone is in use by another application",OverconstrainedError:"Microphone does not support required settings"}},3564:function(t,e,i){i.d(e,{M:function(){return o}});var n=i(2076);const s={"high-rtt":"High network latency detected","low-mos":"Poor call quality detected","high-jitter":"Network instability detected","high-packet-loss":"Packet loss detected"};class o{constructor(t,e,i,n=5e3){this.pc=t,this.onWarning=e,this.onWarningCleared=i,this.pollIntervalMs=n,this.interval=null,this.prevStats=null,this.activeWarnings=new Set}start(){this.interval=setInterval(()=>{this.pollStats()},this.pollIntervalMs)}stop(){null!==this.interval&&(clearInterval(this.interval),this.interval=null),this.prevStats=null,this.activeWarnings.clear()}async pollStats(){try{const t=await this.pc.getStats();let e=null,i=0,s=0;const o=(0,n._)(t),r=null!=(null==o?void 0:o.currentRoundTripTime)?1e3*o.currentRoundTripTime:null;t.forEach(t=>{var n,o,r;if("inbound-rtp"===t.type&&"audio"===t.kind){const a=t;e=null!==(n=a.jitter)&&void 0!==n?n:null,i=null!==(o=a.packetsReceived)&&void 0!==o?o:0,s=null!==(r=a.packetsLost)&&void 0!==r?r:0}});let a=0;if(this.prevStats){const t=i-this.prevStats.packetsReceived,e=s-this.prevStats.packetsLost,n=t+e;a=n>0?e/n:0}if(this.prevStats={packetsReceived:i,packetsLost:s},this.checkWarning("high-rtt",null!==r&&r>400),this.checkWarning("high-jitter",null!==e&&e>.03),this.checkWarning("high-packet-loss",a>.03),null!==r&&null!==e){const t=93.2-1e3*e*2-100*a*40-.1*Math.max(0,r-150),i=1+.035*t+t*(t-60)*(100-t)*7e-6;this.checkWarning("low-mos",i<3)}}catch(t){}}checkWarning(t,e){e&&!this.activeWarnings.has(t)?(this.activeWarnings.add(t),this.onWarning(t,s[t]||t)):!e&&this.activeWarnings.has(t)&&(this.activeWarnings.delete(t),this.onWarningCleared(t))}}},6845:function(t,e,i){i.d(e,{PA:function(){return n},TM:function(){return o},Xq:function(){return s}});class n extends Error{constructor(t="Voice service is busy, please try again."){super(t),this.name="ServerBusyError"}}class s extends Error{constructor(t){super(t),this.name="ConfigUnavailableError"}}class o extends Error{constructor(t="Too many attempts. Please try again in a moment."){super(t),this.name="RateLimitedError"}}},7943:function(t,e,i){i.d(e,{$:function(){return r}});var n=i(1931),s=i.n(n),o=i(2076);class r{constructor(t,e,i,n=1e3,o=3){this.pc=t,this.onStall=e,this.onRecover=i,this.pollIntervalMs=n,this.flatPollsThreshold=o,this.log=s()("moveo:webrtc:media"),this.interval=null,this.prevTimestamp=-1,this.prevPairId=null,this.flatPolls=0,this.stalled=!1,this.cannotAssessLogged=!1}start(){this.interval=setInterval(()=>{this.poll()},this.pollIntervalMs)}stop(){null!==this.interval&&(clearInterval(this.interval),this.interval=null),this.prevTimestamp=-1,this.prevPairId=null,this.flatPolls=0,this.stalled=!1,this.cannotAssessLogged=!1}async poll(){try{const t=await this.pc.getStats(),e=(0,o._)(t),i=null==e?void 0:e.lastPacketReceivedTimestamp;if(void 0===e||void 0===i)return void(this.cannotAssessLogged||(this.cannotAssessLogged=!0,this.log("cannot assess: pair=%s hasTimestamp=%s",null==e?void 0:e.id,void 0!==i)));if(this.cannotAssessLogged=!1,e.id!==this.prevPairId)return this.log("assessed pair changed %s -> %s (%s)",this.prevPairId,e.id,(0,o.t)(t,e)),this.prevPairId=e.id,this.prevTimestamp=i,void(this.flatPolls=0);i===this.prevTimestamp?(this.flatPolls++,this.log("flat poll pair=%s ts=%d flat=%d",e.id,i,this.flatPolls),this.flatPolls>=this.flatPollsThreshold&&!this.stalled&&(this.stalled=!0,this.log("stall declared on pair %s (%s) after %d flat polls",e.id,(0,o.t)(t,e),this.flatPolls),this.onStall())):(this.stalled&&(this.stalled=!1,this.log("recovered on pair %s (%s)",e.id,(0,o.t)(t,e)),this.onRecover()),this.flatPolls=0),this.prevTimestamp=i}catch(t){}}}},2076:function(t,e,i){function n(t){let e;const i=[];t.forEach(t=>{if("transport"===t.type){const{selectedCandidatePairId:i}=t;i&&!e&&(e=i)}else"candidate-pair"===t.type&&i.push(t)});const n=e&&i.find(t=>t.id===e)||i.find(t=>!0===t.selected);if(n)return n;let s;for(const t of i){if(!t.nominated)continue;if(void 0===s){s=t;continue}const e=s.lastPacketReceivedTimestamp,i=t.lastPacketReceivedTimestamp;void 0!==i&&(void 0===e||i>e)&&(s=t)}return s}function s(t,e){const i=e=>{var i,n;if(!e)return"?";const s=t.get(e);if(!s)return"?";const o=`${null!==(i=s.candidateType)&&void 0!==i?i:"?"}/${null!==(n=s.protocol)&&void 0!==n?n:"?"}`;return s.relayProtocol?`${o}(${s.relayProtocol})`:o};return`${i(e.localCandidateId)} -> ${i(e.remoteCandidateId)}`}i.d(e,{_:function(){return n},t:function(){return s}})},4623:function(t,e,i){i.d(e,{g:function(){return o}});var n=i(1931),s=i.n(n);class o{constructor(t,e,i,n,o){this.url=t,this.sessionToken=e,this.onMessage=i,this.onClose=n,this.onError=o,this.log=s()("moveo:webrtc:media"),this.ws=null}connect(){return new Promise((t,e)=>{this.log("connecting to %s",this.url);const i=`${this.url}?token=${encodeURIComponent(this.sessionToken)}`;this.ws=new WebSocket(i);const n=setTimeout(()=>{var t;null===(t=this.ws)||void 0===t||t.close(),e(new Error("Signaling connection timed out"))},1e4);this.ws.onopen=()=>{},this.ws.onmessage=e=>{let i;try{i=JSON.parse(e.data)}catch(t){return}this.log("recv %s",i.type),"welcome"===i.type&&(clearTimeout(n),t()),this.onMessage(i)},this.ws.onerror=t=>{clearTimeout(n),this.log("websocket error"),this.onError(t),e(new Error("Signaling WebSocket error"))},this.ws.onclose=t=>{clearTimeout(n),this.log("closed code=%d reason=%s",t.code,t.reason),e(new Error("Signaling WebSocket closed before connection established")),this.onClose(t.code,t.reason)}})}connectForReconnect(t){return new Promise((e,i)=>{this.log("reconnecting to %s",this.url);const n=`${this.url}?token=${encodeURIComponent(this.sessionToken)}`;this.ws=new WebSocket(n);let s=!1;const o=setTimeout(()=>{var t;s||(s=!0,null===(t=this.ws)||void 0===t||t.close(),i(new Error("Signaling reconnect timed out")))},1e4);this.ws.onopen=()=>{var e;null===(e=this.ws)||void 0===e||e.send(JSON.stringify(t))},this.ws.onmessage=t=>{let n;try{n=JSON.parse(t.data)}catch(t){return}this.log("recv %s",n.type),this.onMessage(n),s||("reconnect_ack"===n.type?(s=!0,clearTimeout(o),e()):"error"===n.type&&(s=!0,clearTimeout(o),i(new Error(`reconnect rejected: ${n.error.code}`))))},this.ws.onerror=t=>{this.log("websocket error"),this.onError(t),s||(s=!0,clearTimeout(o),i(new Error("Signaling WebSocket error")))},this.ws.onclose=t=>{this.log("closed code=%d reason=%s",t.code,t.reason),s||(s=!0,clearTimeout(o),i(new Error("Signaling WebSocket closed before reconnect established"))),this.onClose(t.code,t.reason)}})}send(t){this.ws&&this.ws.readyState===WebSocket.OPEN&&(this.log("send %s",t.type),this.ws.send(JSON.stringify(t)))}close(){this.ws&&(this.ws.onclose=null,this.ws.onerror=null,this.ws.onmessage=null,this.ws.close(1e3,"Client closed"),this.ws=null)}get isConnected(){var t;return(null===(t=this.ws)||void 0===t?void 0:t.readyState)===WebSocket.OPEN}}},5882:function(t,e,i){i.r(e),i.d(e,{WebRTCProvider:function(){return m}}),i(4954);var n=i(1931),s=i.n(n),o=i(2229),r=i(6815),a=i(3564),c=i(6845),l=i(7943),h=i(4623);const d=new Set(["session_expired","session_inactive","invalid_credential","max_recovery_exceeded","unexpected_first_frame"]),u={session_expired:"Your session has expired.",session_inactive:"The call has ended.",invalid_credential:"Could not resume the call.",max_recovery_exceeded:"Could not resume the call after several attempts.",unexpected_first_frame:"Could not resume the call."},g=Object.assign(Object.assign({},r.n),{setRemoteDescription:"Connection setup failed",setLocalDescription:"Connection setup failed",addIceCandidate:"Network configuration failed",createOffer:"Browser audio setup failed"});class m{constructor(t,e){this.wsUrl=t,this.fetchConfig=e,this.log=s()("moveo:webrtc:media"),this.pc=null,this.signaling=null,this.localStream=null,this.remoteStream=null,this.remoteAudio=null,this.remoteAudioMonitor=null,this.audioMonitor=null,this.qualityMonitor=null,this.stallDetector=null,this.pendingCandidates=[],this.state="idle",this.eventListeners=new Map,this.iceRestarting=!1,this.iceRestartAttempts=0,this.iceRestartTimeout=null,this.connectResolve=null,this.connectReject=null,this.drainReceived=!1,this.reconnectAttempts=0,this.reconnectErrorCode=null}setConfig(t){this.config=t}async connect(){if(!this.config)throw new Error("Voice config not set; call setConfig() before connect().");this.setState("connecting");try{this.localStream=await navigator.mediaDevices.getUserMedia({audio:{echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0}})}catch(t){const e=t instanceof Error?t.name:"",i=g[e]||"Microphone access failed";throw this.emitError(i),new Error(i)}this.signaling=new h.g(this.wsUrl,this.config.sessionToken,this.handleSignalingMessage.bind(this),this.handleSignalingClose.bind(this),this.handleSignalingError.bind(this));try{await this.signaling.connect()}catch(t){this.cleanupLocalStream();const e=t instanceof Error?t.message:"Signaling connection failed";throw this.emitError(e),new Error(e)}this.pc=new RTCPeerConnection({iceServers:this.config.iceServers,iceTransportPolicy:this.config.iceTransportPolicy||"relay"});const t=this.localStream.getAudioTracks()[0];this.pc.addTrack(t,this.localStream),this.pc.ontrack=this.handleRemoteTrack.bind(this),this.pc.onicecandidate=t=>{var e;t.candidate&&(null===(e=this.signaling)||void 0===e||e.send({type:"ice",candidate:t.candidate.toJSON()}))},this.pc.onconnectionstatechange=this.handleConnectionStateChange.bind(this);try{const t=await this.pc.createOffer();if(!t.sdp)throw new Error("createOffer returned no SDP");await this.pc.setLocalDescription(t),this.signaling.send({type:"offer",sdp:t.sdp})}catch(t){this.cleanup();const e=t instanceof Error?t.name:"createOffer",i=g[e]||"Failed to create call offer";throw this.emitError(i),new Error(i)}return this.audioMonitor=new o.h,this.audioMonitor.start(this.localStream,t=>{this.emit("audio-level",t,Number(t))}),new Promise((t,e)=>{this.connectResolve=t,this.connectReject=e})}disconnect(){this.disconnectInternal(!1)}disconnectInternal(t){var e,i,n,s,o,r,a;if(!t)try{null===(e=this.signaling)||void 0===e||e.send({type:"hangup"})}catch(t){}try{null===(i=this.qualityMonitor)||void 0===i||i.stop(),this.qualityMonitor=null}catch(t){}try{null===(n=this.stallDetector)||void 0===n||n.stop(),this.stallDetector=null}catch(t){}try{null===(s=this.audioMonitor)||void 0===s||s.stop(),this.audioMonitor=null}catch(t){}try{null===(o=this.remoteAudioMonitor)||void 0===o||o.stop(),this.remoteAudioMonitor=null}catch(t){}try{null===(r=this.pc)||void 0===r||r.close(),this.pc=null}catch(t){}this.cleanupLocalStream();try{null===(a=this.signaling)||void 0===a||a.close(),this.signaling=null}catch(t){}try{this.remoteAudio&&(this.remoteAudio.srcObject=null,this.remoteAudio.remove(),this.remoteAudio=null),this.remoteStream=null}catch(t){}this.clearIceRestart(),this.iceRestartAttempts=0,this.connectReject&&this.connectReject(new Error("Call disconnected before connection completed")),this.connectResolve=null,this.connectReject=null,this.drainReceived=!1,this.reconnectAttempts=0,this.reconnectErrorCode=null,this.originalSessionID=void 0,this.originalConnectionID=void 0,this.pendingCandidates=[],this.setState("idle")}toggleMute(){var t,e;const i=null===(t=this.localStream)||void 0===t?void 0:t.getAudioTracks()[0];if(i){i.enabled=!i.enabled;const t=!i.enabled;return null===(e=this.signaling)||void 0===e||e.send({type:"mute",muted:t}),t}return!1}isMuted(){var t,e,i;return!(null===(i=null===(e=null===(t=this.localStream)||void 0===t?void 0:t.getAudioTracks()[0])||void 0===e?void 0:e.enabled)||void 0===i||i)}getLocalStream(){return this.localStream}getRemoteStream(){return this.remoteStream}sendDigits(t){var e;for(const i of t)null===(e=this.signaling)||void 0===e||e.send({type:"dtmf",digit:i})}on(t,e){var i;const n=null!==(i=this.eventListeners.get(t))&&void 0!==i?i:new Set;return n.add(e),this.eventListeners.set(t,n),()=>this.off(t,e)}off(t,e){const i=this.eventListeners.get(t);i&&(i.delete(e),0===i.size&&this.eventListeners.delete(t))}handleRemoteTrack(t){const[e]=t.streams;this.remoteStream=null!=e?e:null,this.remoteAudio=document.createElement("audio"),this.remoteAudio.srcObject=e,this.remoteAudio.autoplay=!0,document.body.appendChild(this.remoteAudio),this.remoteAudio.play().catch(()=>{}),this.remoteAudioMonitor=new o.h,this.remoteAudioMonitor.start(e,t=>{Number(t)>.02&&this.emit("remote-audio",t,Number(t))})}handleConnectionStateChange(){var t,e,i,n;if(this.pc)switch(this.log("connectionState %s",this.pc.connectionState),this.pc.connectionState){case"connecting":this.setState("connecting");break;case"connected":{const n="reconnecting"===this.state;this.setState("connected"),this.clearIceRestart(),this.iceRestartAttempts=0,n&&this.emit("warning-cleared",void 0,"reconnect"),null===(t=this.qualityMonitor)||void 0===t||t.stop(),this.qualityMonitor=new a.M(this.pc,(t,e)=>this.emit("warning",e,t),t=>this.emit("warning-cleared",void 0,t)),this.qualityMonitor.start(),null===(e=this.stallDetector)||void 0===e||e.stop(),this.stallDetector=new l.$(this.pc,()=>{this.log("media stall -> ICE restart"),this.triggerIceRestart()},()=>{this.log("media recovered"),this.clearIceRestart(),this.iceRestartAttempts=0,"reconnecting"===this.state&&this.setState("connected"),this.emit("warning-cleared",void 0,"reconnect")}),this.stallDetector.start(),null===(i=this.connectResolve)||void 0===i||i.call(this),this.connectResolve=null,this.connectReject=null;break}case"disconnected":if(this.drainReceived)break;this.triggerIceRestart();break;case"failed":if(this.drainReceived){null===(n=this.connectReject)||void 0===n||n.call(this,new Error("ICE connection failed")),this.connectResolve=null,this.connectReject=null;break}if(this.connectReject){this.connectReject(new Error("ICE connection failed")),this.connectResolve=null,this.connectReject=null,this.emitError("Connection failed"),this.cleanup();break}this.iceRestarting?this.onRestartAttemptExpired():this.triggerIceRestart();break;case"closed":this.cleanup()}}async triggerIceRestart(){if(this.drainReceived||!this.pc||!this.signaling)return;if(this.iceRestarting||this.iceRestartAttempts>=2)return;if(!this.signaling.isConnected)return this.emitError("Connection lost"),void this.cleanup();const t=this.pc,e=this.signaling;this.iceRestarting=!0,this.iceRestartAttempts++,this.log("ICE restart attempt %d/%d",this.iceRestartAttempts,2),this.setState("reconnecting"),this.emit("warning","Reconnecting…","reconnect");try{const i=await t.createOffer({iceRestart:!0});if(!i.sdp)throw new Error("createOffer returned no SDP");await t.setLocalDescription(i),e.send({type:"offer",sdp:i.sdp})}catch(t){return this.iceRestarting=!1,this.emitError("Connection lost"),void this.cleanup()}this.iceRestarting&&this.pc===t&&(this.iceRestartTimeout=setTimeout(()=>{this.onRestartAttemptExpired()},1e4))}onRestartAttemptExpired(){this.log("ICE restart attempt expired (attempts=%d)",this.iceRestartAttempts),this.iceRestartTimeout&&(clearTimeout(this.iceRestartTimeout),this.iceRestartTimeout=null),this.iceRestarting=!1,this.iceRestartAttempts<2?this.triggerIceRestart():(this.emitError("Connection lost"),this.cleanup())}clearIceRestart(){this.iceRestarting&&this.log("ICE restart cleared"),this.iceRestartTimeout&&(clearTimeout(this.iceRestartTimeout),this.iceRestartTimeout=null),this.iceRestarting=!1}handleSignalingMessage(t){var e;switch(t.type){case"answer":this.handleAnswer(t.sdp);break;case"ice":this.addIceCandidate(t.candidate);break;case"transcript":this.emit("transcript",t.text,{text:t.text,is_final:t.is_final});break;case"agent_response":this.emit("agent-response",t.text,{text:t.text,message_id:t.message_id});break;case"hangup":this.disconnectInternal(!0);break;case"session_ready":this.originalSessionID=t.session_id,this.originalConnectionID=t.connection_id;break;case"reconnect_ack":this.originalConnectionID=t.connection_id;break;case"drain":this.handleDrain(t);break;case"error":this.drainReceived?(this.reconnectErrorCode=t.error.code,null===(e=this.connectReject)||void 0===e||e.call(this,new Error(`reconnect rejected: ${t.error.code}`))):this.emitError(t.error.message)}}handleSignalingClose(t,e){var i;this.drainReceived?null===(i=this.connectReject)||void 0===i||i.call(this,new Error("Signaling closed during resume")):"idle"!==this.state&&this.disconnect()}handleSignalingError(t){"connected"===this.state&&this.emitError("Signaling error")}async handleDrain(t){if(this.drainReceived)return;if("connected"!==this.state&&"reconnecting"!==this.state||void 0===this.originalSessionID||void 0===this.originalConnectionID)return this.emitError("Could not resume the call."),void this.disconnectInternal(!0);this.drainReceived=!0,this.setState("reconnecting"),this.clearIceRestart();const e=Math.max(0,t.reconnect_delay_ms||0);await this.sleep(Math.random()*e),this.drainReceived&&await this.reconnect()}async reconnect(){var t,e,i;try{null===(t=this.pc)||void 0===t||t.getSenders().forEach(t=>{var e;t.track&&(null===(e=this.pc)||void 0===e||e.removeTrack(t))})}catch(t){}const n=null===(e=this.localStream)||void 0===e?void 0:e.getAudioTracks()[0];if(!n||"live"!==n.readyState)return this.emitError("Microphone is no longer available."),void this.disconnectInternal(!0);for(this.teardownForReconnect();this.reconnectAttempts<3;){this.reconnectErrorCode=null;try{return this.config=await this.fetchConfig(),this.signaling=new h.g(this.wsUrl,this.config.sessionToken,this.handleSignalingMessage.bind(this),this.handleSignalingClose.bind(this),this.handleSignalingError.bind(this)),await this.signaling.connectForReconnect({type:"reconnect",session_id:this.originalSessionID,connection_id:this.originalConnectionID}),await this.buildReconnectPeerConnection(n),this.reconnectAttempts=0,this.drainReceived=!1,void this.emit("warning-cleared",void 0,"reconnect")}catch(t){const e=this.reconnectErrorCode;if(e&&d.has(e))return this.emitError(null!==(i=u[e])&&void 0!==i?i:"Could not resume the call."),void this.disconnectInternal(!0);if(t instanceof c.Xq)return this.emitError(t.message),void this.disconnectInternal(!0);const n=t instanceof c.PA?t.message:"Reconnecting…";if(this.emit("warning",n,"reconnect"),this.teardownForReconnect(),this.reconnectAttempts++,this.reconnectAttempts>=3)break;if(await this.sleep(500*this.reconnectAttempts),!this.drainReceived)return}}this.emitError("Could not resume the call."),this.disconnectInternal(!0)}buildReconnectPeerConnection(t){return this.pc=new RTCPeerConnection({iceServers:this.config.iceServers,iceTransportPolicy:this.config.iceTransportPolicy||"relay"}),this.pc.addTrack(t,this.localStream),this.pc.ontrack=this.handleRemoteTrack.bind(this),this.pc.onicecandidate=t=>{var e;t.candidate&&(null===(e=this.signaling)||void 0===e||e.send({type:"ice",candidate:t.candidate.toJSON()}))},this.pc.onconnectionstatechange=this.handleConnectionStateChange.bind(this),this.audioMonitor=new o.h,this.audioMonitor.start(this.localStream,t=>{this.emit("audio-level",t,Number(t))}),new Promise((t,e)=>{const i=setTimeout(()=>{this.connectResolve=null,this.connectReject=null,e(new Error("Reconnect media resume timed out"))},15e3);this.connectResolve=()=>{clearTimeout(i),t()},this.connectReject=t=>{clearTimeout(i),e(t)},(async()=>{var t;try{const t=await this.pc.createOffer();if(!t.sdp)throw new Error("createOffer returned no SDP");await this.pc.setLocalDescription(t),this.signaling.send({type:"offer",sdp:t.sdp})}catch(e){const i=e instanceof Error?e.name:"createOffer";null===(t=this.connectReject)||void 0===t||t.call(this,new Error(g[i]||"Failed to create call offer"))}})()})}teardownForReconnect(){var t,e,i,n,s,o;try{null===(t=this.qualityMonitor)||void 0===t||t.stop(),this.qualityMonitor=null}catch(t){}try{null===(e=this.stallDetector)||void 0===e||e.stop(),this.stallDetector=null}catch(t){}try{null===(i=this.audioMonitor)||void 0===i||i.stop(),this.audioMonitor=null}catch(t){}try{null===(n=this.remoteAudioMonitor)||void 0===n||n.stop(),this.remoteAudioMonitor=null}catch(t){}try{null===(s=this.pc)||void 0===s||s.close(),this.pc=null}catch(t){}try{null===(o=this.signaling)||void 0===o||o.close(),this.signaling=null}catch(t){}try{this.remoteAudio&&(this.remoteAudio.srcObject=null,this.remoteAudio.remove(),this.remoteAudio=null),this.remoteStream=null}catch(t){}this.clearIceRestart(),this.iceRestartAttempts=0,this.connectResolve=null,this.connectReject=null,this.pendingCandidates=[]}sleep(t){return new Promise(e=>setTimeout(e,t))}async handleAnswer(t){if(this.pc)try{await this.pc.setRemoteDescription({type:"answer",sdp:t}),await this.flushPendingCandidates()}catch(t){this.emitError(g.setRemoteDescription)}}async addIceCandidate(t){var e;if(null===(e=this.pc)||void 0===e?void 0:e.remoteDescription)try{await this.pc.addIceCandidate(t)}catch(t){}else this.pendingCandidates.push(t)}async flushPendingCandidates(){for(const t of this.pendingCandidates){if(!this.pc)break;try{await this.pc.addIceCandidate(t)}catch(t){}}this.pendingCandidates=[]}cleanup(){this.disconnect()}cleanupLocalStream(){var t;try{null===(t=this.localStream)||void 0===t||t.getTracks().forEach(t=>t.stop()),this.localStream=null}catch(t){}}setState(t){this.state=t,this.emit("state-change",t,t)}emitError(t){this.emit("error",t),this.setState("error")}emit(t,e,i){const n={state:t,message:e,data:i},s=t=>{const e=this.eventListeners.get(t);e&&e.forEach(t=>{try{t(n)}catch(t){}})};s(t),s("any")}}}}]);
|
package/dist/882.min.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"882.min.js","mappings":"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","sources":["webpack://MoveoAI/./src/controller/shared/audio-level-monitor.ts","webpack://MoveoAI/./src/controller/shared/media-errors.ts","webpack://MoveoAI/./src/controller/webrtc/call-quality-monitor.ts","webpack://MoveoAI/./src/controller/webrtc/config-errors.ts","webpack://MoveoAI/./src/controller/webrtc/media-stall-detector.ts","webpack://MoveoAI/./src/controller/webrtc/signaling-client.ts","webpack://MoveoAI/./src/controller/webrtc/webrtc-provider.ts"],"sourcesContent":["const NOISE_GATE_THRESHOLD = 0.01;\n/**\n * Monitors a MediaStream's audio level via Web Audio API.\n * Extracted from AudioController for reuse across voice providers.\n *\n * Uses AnalyserNode with RMS calculation:\n * fftSize: 256, smoothingTimeConstant: 0.3\n * Noise gate: 0.01 threshold\n * Normalized output: 0.0 - 1.0\n */\nexport class AudioLevelMonitor {\n constructor() {\n this.analyserNode = null;\n this.audioSource = null;\n this.animationId = null;\n this.audioContext = null;\n }\n start(stream, onLevel) {\n try {\n this.audioContext = new AudioContext();\n this.audioSource = this.audioContext.createMediaStreamSource(stream);\n this.analyserNode = this.audioContext.createAnalyser();\n this.analyserNode.fftSize = 256;\n this.analyserNode.smoothingTimeConstant = 0.3;\n this.audioSource.connect(this.analyserNode);\n const dataArray = new Float32Array(this.analyserNode.fftSize);\n const checkAudioLevel = () => {\n if (!this.analyserNode) {\n this.stop();\n return;\n }\n this.analyserNode.getFloatTimeDomainData(dataArray);\n let sum = 0;\n for (const amplitude of dataArray) {\n sum += amplitude * amplitude;\n }\n const rms = Math.sqrt(sum / dataArray.length);\n const normalizedLevel = Math.min(1, rms * 10);\n const gatedLevel = normalizedLevel > NOISE_GATE_THRESHOLD ? normalizedLevel : 0;\n onLevel(gatedLevel.toFixed(3));\n this.animationId = requestAnimationFrame(checkAudioLevel);\n };\n checkAudioLevel();\n }\n catch (_a) {\n // Silently fail — audio monitoring is non-critical\n }\n }\n stop() {\n if (this.animationId !== null) {\n cancelAnimationFrame(this.animationId);\n this.animationId = null;\n }\n if (this.audioSource) {\n try {\n this.audioSource.disconnect();\n }\n catch (_a) {\n // ignore\n }\n this.audioSource = null;\n }\n if (this.audioContext) {\n void this.audioContext.close().catch(() => {\n // ignore\n });\n this.audioContext = null;\n }\n this.analyserNode = null;\n }\n}\n","/**\n * User-friendly messages for the common getUserMedia DOMException names.\n * Shared across voice providers (native WebRTC, Telnyx) so a single string\n * surfaces in the widget regardless of provider.\n */\nexport const MEDIA_ERRORS = {\n NotAllowedError: 'Microphone permission denied',\n NotFoundError: 'No microphone found',\n NotReadableError: 'Microphone is in use by another application',\n OverconstrainedError: 'Microphone does not support required settings',\n};\n","const QUALITY_WARNINGS = {\n 'high-rtt': 'High network latency detected',\n 'low-mos': 'Poor call quality detected',\n 'high-jitter': 'Network instability detected',\n 'high-packet-loss': 'Packet loss detected',\n};\nconst THRESHOLDS = {\n RTT: 400, // ms\n JITTER: 0.03, // seconds (30ms)\n PACKET_LOSS: 0.03, // 3%\n MOS: 3.0,\n};\n/**\n * Replaces Twilio Voice Insights with standard WebRTC getStats().\n * Polls pc.getStats() and maps to warning events that AudioWidget handles.\n */\nexport class CallQualityMonitor {\n constructor(pc, onWarning, onWarningCleared, pollIntervalMs = 5000) {\n this.pc = pc;\n this.onWarning = onWarning;\n this.onWarningCleared = onWarningCleared;\n this.pollIntervalMs = pollIntervalMs;\n this.interval = null;\n this.prevStats = null;\n this.activeWarnings = new Set();\n }\n start() {\n this.interval = setInterval(() => {\n void this.pollStats();\n }, this.pollIntervalMs);\n }\n stop() {\n if (this.interval !== null) {\n clearInterval(this.interval);\n this.interval = null;\n }\n this.prevStats = null;\n this.activeWarnings.clear();\n }\n async pollStats() {\n try {\n const stats = await this.pc.getStats();\n let rtt = null;\n let jitter = null;\n let packetsReceived = 0;\n let packetsLost = 0;\n stats.forEach((report) => {\n var _a, _b, _c;\n if (report.type === 'candidate-pair' && report.nominated) {\n const crt = report\n .currentRoundTripTime;\n rtt = crt != null ? crt * 1000 : null;\n }\n if (report.type === 'inbound-rtp' && report.kind === 'audio') {\n const rtp = report;\n jitter = (_a = rtp.jitter) !== null && _a !== void 0 ? _a : null;\n packetsReceived = (_b = rtp.packetsReceived) !== null && _b !== void 0 ? _b : 0;\n packetsLost = (_c = rtp.packetsLost) !== null && _c !== void 0 ? _c : 0;\n }\n });\n // Compute packet loss rate over delta\n let lossRate = 0;\n if (this.prevStats) {\n const deltaReceived = packetsReceived - this.prevStats.packetsReceived;\n const deltaLost = packetsLost - this.prevStats.packetsLost;\n const total = deltaReceived + deltaLost;\n lossRate = total > 0 ? deltaLost / total : 0;\n }\n this.prevStats = { packetsReceived, packetsLost };\n // Check thresholds\n this.checkWarning('high-rtt', rtt !== null && rtt > THRESHOLDS.RTT);\n this.checkWarning('high-jitter', jitter !== null && jitter > THRESHOLDS.JITTER);\n this.checkWarning('high-packet-loss', lossRate > THRESHOLDS.PACKET_LOSS);\n // MOS estimation (simplified E-model)\n if (rtt !== null && jitter !== null) {\n const jitterMs = jitter * 1000;\n const lossPercent = lossRate * 100;\n const R = 93.2 - jitterMs * 2 - lossPercent * 40 - Math.max(0, rtt - 150) * 0.1;\n const mos = 1 + 0.035 * R + R * (R - 60) * (100 - R) * 7e-6;\n this.checkWarning('low-mos', mos < THRESHOLDS.MOS);\n }\n }\n catch (_a) {\n // pc may have closed mid-poll; quality stats are non-critical\n }\n }\n checkWarning(name, isExceeded) {\n if (isExceeded && !this.activeWarnings.has(name)) {\n this.activeWarnings.add(name);\n this.onWarning(name, QUALITY_WARNINGS[name] || name);\n }\n else if (!isExceeded && this.activeWarnings.has(name)) {\n this.activeWarnings.delete(name);\n this.onWarningCleared(name);\n }\n }\n}\n","/**\n * Typed errors for the GET /config flow, shared by WebRTCController (initial\n * connect) and WebRTCProvider (reconnect-time re-fetch) so both can branch on\n * a server-busy 503 without parsing message strings.\n */\n/**\n * Raised when /config returns 503 with error_code \"server_busy\" — the pod is at\n * its concurrent-session cap. Retryable with backoff against a sibling pod.\n */\nexport class ServerBusyError extends Error {\n constructor(message = 'Voice service is busy, please try again.') {\n super(message);\n this.name = 'ServerBusyError';\n }\n}\n/**\n * Raised when /config fails in a way retrying can't fix (e.g. TURN or signing\n * key not configured, or origin not allowed). Terminal — the reconnect loop\n * must not burn retries on it.\n */\nexport class ConfigUnavailableError extends Error {\n constructor(message) {\n super(message);\n this.name = 'ConfigUnavailableError';\n }\n}\n/**\n * Raised when /config returns 429 — the integration hit its per-integration\n * rate limit. The caller should back off and let the user retry shortly.\n */\nexport class RateLimitedError extends Error {\n constructor(message = 'Too many attempts. Please try again in a moment.') {\n super(message);\n this.name = 'RateLimitedError';\n }\n}\n","/**\n * Detects a mid-call media-path stall (and its recovery) so the provider can\n * drive an ICE restart and confirm when media returns. Separate from\n * CallQualityMonitor: that polls every 5s for UX warnings, but recovery needs a\n * faster signal that beats the server's ~15s give-up.\n *\n * Codec-independent: it watches the selected candidate-pair's\n * lastPacketReceivedTimestamp, which advances on ANY inbound packet (media RTP,\n * RTCP, or STUN consent) regardless of codec or DTX. On a healthy call it moves\n * well within the debounce window (PCMU sends continuously; Opus-DTX still sends\n * SID frames sub-second); a dead path freezes it. This is why it does not depend\n * on a specific codec the way an inbound-rtp bytesReceived check would.\n *\n * If a browser ever omits lastPacketReceivedTimestamp on the candidate-pair, the\n * detector simply never fires (no false positives, no recovery) — revisit with a\n * fallback signal then.\n */\nexport class MediaStallDetector {\n constructor(pc, onStall, onRecover, pollIntervalMs = 1000, \n // Consecutive polls with no inbound packet before declaring a stall;\n // debounces a brief blip while still beating the 15s server close.\n flatPollsThreshold = 3) {\n this.pc = pc;\n this.onStall = onStall;\n this.onRecover = onRecover;\n this.pollIntervalMs = pollIntervalMs;\n this.flatPollsThreshold = flatPollsThreshold;\n this.interval = null;\n this.prevTimestamp = -1;\n this.flatPolls = 0;\n this.stalled = false;\n }\n start() {\n this.interval = setInterval(() => {\n void this.poll();\n }, this.pollIntervalMs);\n }\n stop() {\n if (this.interval !== null) {\n clearInterval(this.interval);\n this.interval = null;\n }\n this.prevTimestamp = -1;\n this.flatPolls = 0;\n this.stalled = false;\n }\n async poll() {\n try {\n const stats = await this.pc.getStats();\n let lastPacketTs;\n stats.forEach((report) => {\n if (report.type === 'candidate-pair' && report.nominated) {\n lastPacketTs = report\n .lastPacketReceivedTimestamp;\n }\n });\n // No nominated pair yet, or the field is unsupported: can't assess.\n if (lastPacketTs === undefined) {\n return;\n }\n if (this.prevTimestamp >= 0 && lastPacketTs === this.prevTimestamp) {\n this.flatPolls++;\n if (this.flatPolls >= this.flatPollsThreshold && !this.stalled) {\n this.stalled = true;\n this.onStall();\n }\n }\n else {\n // A packet arrived: media is flowing. Signal recovery if we were stalled.\n if (this.stalled) {\n this.stalled = false;\n this.onRecover();\n }\n this.flatPolls = 0;\n }\n this.prevTimestamp = lastPacketTs;\n }\n catch (_a) {\n // pc may have closed mid-poll; non-critical\n }\n }\n}\n","const CONNECT_TIMEOUT_MS = 10000;\n/**\n * JSON-over-WebSocket signaling for WebRTC voice sessions.\n * Stateless thin wrapper — all connection state lives in WebRTCProvider.\n */\nexport class SignalingClient {\n constructor(url, sessionToken, onMessage, onClose, onError) {\n this.url = url;\n this.sessionToken = sessionToken;\n this.onMessage = onMessage;\n this.onClose = onClose;\n this.onError = onError;\n this.ws = null;\n }\n connect() {\n return new Promise((resolve, reject) => {\n const wsUrl = `${this.url}?token=${encodeURIComponent(this.sessionToken)}`;\n this.ws = new WebSocket(wsUrl);\n const timeout = setTimeout(() => {\n var _a;\n (_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();\n reject(new Error('Signaling connection timed out'));\n }, CONNECT_TIMEOUT_MS);\n this.ws.onopen = () => {\n // Wait for welcome message before resolving\n };\n this.ws.onmessage = (event) => {\n let msg;\n try {\n msg = JSON.parse(event.data);\n }\n catch (_a) {\n // Ignore malformed JSON\n return;\n }\n if (msg.type === 'welcome') {\n clearTimeout(timeout);\n resolve();\n }\n this.onMessage(msg);\n };\n this.ws.onerror = (event) => {\n clearTimeout(timeout);\n this.onError(event);\n reject(new Error('Signaling WebSocket error'));\n };\n this.ws.onclose = (event) => {\n clearTimeout(timeout);\n reject(new Error('Signaling WebSocket closed before connection established'));\n this.onClose(event.code, event.reason);\n };\n });\n }\n /**\n * Open a fresh WS to resume a drained session. Sends `reconnect` as the\n * first frame on open and resolves on `reconnect_ack` (not `welcome` — a\n * resumed session does not re-issue welcome). The fresh-call `connect()`\n * path is intentionally left untouched.\n */\n connectForReconnect(reconnectFrame) {\n return new Promise((resolve, reject) => {\n const wsUrl = `${this.url}?token=${encodeURIComponent(this.sessionToken)}`;\n this.ws = new WebSocket(wsUrl);\n let settled = false;\n const timeout = setTimeout(() => {\n var _a;\n if (settled)\n return;\n settled = true;\n (_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();\n reject(new Error('Signaling reconnect timed out'));\n }, CONNECT_TIMEOUT_MS);\n this.ws.onopen = () => {\n var _a;\n // reconnect MUST be the first frame, before any offer.\n (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify(reconnectFrame));\n };\n this.ws.onmessage = (event) => {\n let msg;\n try {\n msg = JSON.parse(event.data);\n }\n catch (_a) {\n // Ignore malformed JSON\n return;\n }\n // Deliver to the provider first so it records the credential rotation\n // (reconnect_ack) or the reject code (error) before we settle.\n this.onMessage(msg);\n if (settled)\n return;\n if (msg.type === 'reconnect_ack') {\n settled = true;\n clearTimeout(timeout);\n resolve();\n }\n else if (msg.type === 'error') {\n // Server rejected the resume. Settle now rather than waiting on the\n // WS close that follows — avoids a per-attempt CONNECT_TIMEOUT stall\n // and makes the reject deterministic w.r.t. the captured error code.\n settled = true;\n clearTimeout(timeout);\n reject(new Error(`reconnect rejected: ${msg.error.code}`));\n }\n };\n this.ws.onerror = (event) => {\n this.onError(event);\n if (settled)\n return;\n settled = true;\n clearTimeout(timeout);\n reject(new Error('Signaling WebSocket error'));\n };\n this.ws.onclose = (event) => {\n if (!settled) {\n settled = true;\n clearTimeout(timeout);\n reject(new Error('Signaling WebSocket closed before reconnect established'));\n }\n this.onClose(event.code, event.reason);\n };\n });\n }\n send(msg) {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n return;\n }\n this.ws.send(JSON.stringify(msg));\n }\n close() {\n if (this.ws) {\n this.ws.onclose = null;\n this.ws.onerror = null;\n this.ws.onmessage = null;\n this.ws.close(1000, 'Client closed');\n this.ws = null;\n }\n }\n get isConnected() {\n var _a;\n return ((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN;\n }\n}\n","import 'webrtc-adapter';\nimport { AudioLevelMonitor } from '../shared/audio-level-monitor';\nimport { MEDIA_ERRORS } from '../shared/media-errors';\nimport { CallQualityMonitor } from './call-quality-monitor';\nimport { ConfigUnavailableError, ServerBusyError } from './config-errors';\nimport { MediaStallDetector } from './media-stall-detector';\nimport { SignalingClient } from './signaling-client';\nconst REMOTE_AUDIO_THRESHOLD = 0.02;\n// ICE-restart recovery bounds (mid-call media drop).\nconst MAX_ICE_RESTART_ATTEMPTS = 2;\nconst ICE_RESTART_TIMEOUT_MS = 10000; // per-attempt window to reach 'connected'\n// Pod-drain reconnect bounds.\nconst MAX_CLIENT_RETRIES = 3;\nconst RECONNECT_BACKOFF_MS = 500; // scaled by attempt number\n// Settle window for the post-ack media resume; on the drain path the WS-close\n// teardown is disabled, so this is the only guard against a wedged resume.\nconst RECONNECT_ICE_TIMEOUT_MS = 15000;\n// Reconnect error codes that cannot be resumed — give up without retrying.\n// Any other failure (transient WS/ICE, server_busy) is retryable.\nconst TERMINAL_RECONNECT_CODES = new Set([\n 'session_expired',\n 'session_inactive',\n 'invalid_credential',\n 'max_recovery_exceeded',\n 'unexpected_first_frame',\n]);\n/** User-facing copy for terminal reconnect failures. Infra-agnostic. */\nconst RECONNECT_ERROR_MESSAGES = {\n session_expired: 'Your session has expired.',\n session_inactive: 'The call has ended.',\n invalid_credential: 'Could not resume the call.',\n max_recovery_exceeded: 'Could not resume the call after several attempts.',\n unexpected_first_frame: 'Could not resume the call.',\n};\n/** User-friendly error messages for WebRTC API failures */\nconst WEBRTC_ERRORS = Object.assign(Object.assign({}, MEDIA_ERRORS), { setRemoteDescription: 'Connection setup failed', setLocalDescription: 'Connection setup failed', addIceCandidate: 'Network configuration failed', createOffer: 'Browser audio setup failed' });\n/** Native WebRTC voice provider for Moveo web-voice integrations. */\nexport class WebRTCProvider {\n constructor(wsUrl, \n // Re-fetches /config (fresh TURN + session token) for a resume; throws\n // ServerBusyError on a server-busy 503. Injected so signaling stays here.\n fetchConfig) {\n this.wsUrl = wsUrl;\n this.fetchConfig = fetchConfig;\n this.pc = null;\n this.signaling = null;\n this.localStream = null;\n this.remoteAudio = null;\n this.remoteAudioMonitor = null;\n this.audioMonitor = null;\n this.qualityMonitor = null;\n this.stallDetector = null;\n this.pendingCandidates = [];\n this.state = 'idle';\n this.eventListeners = new Map();\n // --- ICE-restart recovery state ---\n this.iceRestarting = false;\n this.iceRestartAttempts = 0;\n this.iceRestartTimeout = null;\n this.connectResolve = null;\n this.connectReject = null;\n // Set between a drain frame and a completed resume; gates the ICE-level\n // disconnect/failed handlers so the drain flow owns teardown.\n this.drainReceived = false;\n this.reconnectAttempts = 0;\n // Captured from an error frame mid-resume to classify terminal vs retryable.\n this.reconnectErrorCode = null;\n }\n /** Supply the /config result (TURN + session token) for the next connect(). */\n setConfig(config) {\n this.config = config;\n }\n async connect() {\n if (!this.config) {\n throw new Error('Voice config not set; call setConfig() before connect().');\n }\n this.setState('connecting');\n // 1. Get microphone access\n try {\n this.localStream = await navigator.mediaDevices.getUserMedia({\n audio: {\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n },\n });\n }\n catch (error) {\n const name = error instanceof Error ? error.name : '';\n const message = WEBRTC_ERRORS[name] || 'Microphone access failed';\n this.emitError(message);\n throw new Error(message);\n }\n // 2. Open signaling WebSocket\n this.signaling = new SignalingClient(this.wsUrl, this.config.sessionToken, this.handleSignalingMessage.bind(this), this.handleSignalingClose.bind(this), this.handleSignalingError.bind(this));\n try {\n await this.signaling.connect();\n }\n catch (error) {\n this.cleanupLocalStream();\n const message = error instanceof Error ? error.message : 'Signaling connection failed';\n this.emitError(message);\n throw new Error(message);\n }\n // 3. Create PeerConnection\n this.pc = new RTCPeerConnection({\n iceServers: this.config.iceServers,\n iceTransportPolicy: this.config.iceTransportPolicy || 'relay',\n });\n // 4. Add local audio track\n const audioTrack = this.localStream.getAudioTracks()[0];\n this.pc.addTrack(audioTrack, this.localStream);\n // 5. Set up PC handlers\n this.pc.ontrack = this.handleRemoteTrack.bind(this);\n this.pc.onicecandidate = (event) => {\n var _a;\n if (event.candidate) {\n (_a = this.signaling) === null || _a === void 0 ? void 0 : _a.send({\n type: 'ice',\n candidate: event.candidate.toJSON(),\n });\n }\n };\n this.pc.onconnectionstatechange =\n this.handleConnectionStateChange.bind(this);\n // 6. Create and send offer\n try {\n const offer = await this.pc.createOffer();\n if (!offer.sdp) {\n throw new Error('createOffer returned no SDP');\n }\n await this.pc.setLocalDescription(offer);\n this.signaling.send({ type: 'offer', sdp: offer.sdp });\n }\n catch (error) {\n this.cleanup();\n const name = error instanceof Error ? error.name : 'createOffer';\n const message = WEBRTC_ERRORS[name] || 'Failed to create call offer';\n this.emitError(message);\n throw new Error(message);\n }\n // 7. Start local audio monitoring\n this.audioMonitor = new AudioLevelMonitor();\n this.audioMonitor.start(this.localStream, (level) => {\n this.emit('audio-level', level, Number(level));\n });\n // 8. Wait for PeerConnection to reach 'connected' state\n return new Promise((resolve, reject) => {\n this.connectResolve = resolve;\n this.connectReject = reject;\n });\n }\n disconnect() {\n this.disconnectInternal(false);\n }\n disconnectInternal(suppressOutboundHangup) {\n // Fault-tolerant disconnect: each step wrapped in try/catch\n var _a, _b, _c, _d, _e, _f, _g;\n if (!suppressOutboundHangup) {\n try {\n (_a = this.signaling) === null || _a === void 0 ? void 0 : _a.send({ type: 'hangup' });\n }\n catch (_h) {\n // continue cleanup\n }\n }\n try {\n (_b = this.qualityMonitor) === null || _b === void 0 ? void 0 : _b.stop();\n this.qualityMonitor = null;\n }\n catch (_j) {\n // continue cleanup\n }\n try {\n (_c = this.stallDetector) === null || _c === void 0 ? void 0 : _c.stop();\n this.stallDetector = null;\n }\n catch (_k) {\n // continue cleanup\n }\n try {\n (_d = this.audioMonitor) === null || _d === void 0 ? void 0 : _d.stop();\n this.audioMonitor = null;\n }\n catch (_l) {\n // continue cleanup\n }\n try {\n (_e = this.remoteAudioMonitor) === null || _e === void 0 ? void 0 : _e.stop();\n this.remoteAudioMonitor = null;\n }\n catch (_m) {\n // continue cleanup\n }\n try {\n (_f = this.pc) === null || _f === void 0 ? void 0 : _f.close();\n this.pc = null;\n }\n catch (_o) {\n // continue cleanup\n }\n this.cleanupLocalStream();\n try {\n (_g = this.signaling) === null || _g === void 0 ? void 0 : _g.close();\n this.signaling = null;\n }\n catch (_p) {\n // continue cleanup\n }\n try {\n if (this.remoteAudio) {\n this.remoteAudio.srcObject = null;\n this.remoteAudio.remove();\n this.remoteAudio = null;\n }\n }\n catch (_q) {\n // continue cleanup\n }\n this.clearIceRestart();\n this.iceRestartAttempts = 0;\n if (this.connectReject) {\n this.connectReject(new Error('Call disconnected before connection completed'));\n }\n this.connectResolve = null;\n this.connectReject = null;\n // Clear pod-drain reconnect state so a torn-down provider can't leave\n // drainReceived stuck true (which would make handleSignalingClose swallow a\n // future legitimate close), carry a stale retry count, or resume a new call\n // with the prior session's credentials before its own session_ready lands.\n this.drainReceived = false;\n this.reconnectAttempts = 0;\n this.reconnectErrorCode = null;\n this.originalSessionID = undefined;\n this.originalConnectionID = undefined;\n this.pendingCandidates = [];\n this.setState('idle');\n }\n toggleMute() {\n var _a, _b;\n const track = (_a = this.localStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks()[0];\n if (track) {\n track.enabled = !track.enabled;\n const muted = !track.enabled;\n (_b = this.signaling) === null || _b === void 0 ? void 0 : _b.send({ type: 'mute', muted });\n return muted;\n }\n return false;\n }\n isMuted() {\n var _a, _b, _c;\n return !((_c = (_b = (_a = this.localStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks()[0]) === null || _b === void 0 ? void 0 : _b.enabled) !== null && _c !== void 0 ? _c : true);\n }\n getLocalStream() {\n return this.localStream;\n }\n sendDigits(digits) {\n var _a;\n // Server expects one digit per signaling message (matches Twilio path,\n // which lets the SDK split a multi-char string internally).\n for (const ch of digits) {\n (_a = this.signaling) === null || _a === void 0 ? void 0 : _a.send({ type: 'dtmf', digit: ch });\n }\n }\n on(event, handler) {\n var _a;\n const listeners = (_a = this.eventListeners.get(event)) !== null && _a !== void 0 ? _a : new Set();\n listeners.add(handler);\n this.eventListeners.set(event, listeners);\n return () => this.off(event, handler);\n }\n off(event, handler) {\n const listeners = this.eventListeners.get(event);\n if (!listeners)\n return;\n listeners.delete(handler);\n if (listeners.size === 0) {\n this.eventListeners.delete(event);\n }\n }\n // --- Private methods ---\n handleRemoteTrack(event) {\n this.remoteAudio = document.createElement('audio');\n this.remoteAudio.srcObject = event.streams[0];\n this.remoteAudio.autoplay = true;\n document.body.appendChild(this.remoteAudio);\n // Remote audio monitoring via AnalyserNode for thinking indicator\n this.remoteAudioMonitor = new AudioLevelMonitor();\n this.remoteAudioMonitor.start(event.streams[0], (level) => {\n if (Number(level) > REMOTE_AUDIO_THRESHOLD) {\n this.emit('remote-audio', level, Number(level));\n }\n });\n }\n handleConnectionStateChange() {\n var _a, _b, _c, _d;\n if (!this.pc)\n return;\n switch (this.pc.connectionState) {\n case 'connecting':\n this.setState('connecting');\n break;\n case 'connected': {\n const wasReconnecting = this.state === 'reconnecting';\n this.setState('connected');\n // Reaching 'connected' means any in-flight ICE restart recovered.\n this.clearIceRestart();\n this.iceRestartAttempts = 0;\n // A disconnected → connected recovery fires no stall onRecover, so the\n // \"Reconnecting…\" indicator is cleared here.\n if (wasReconnecting) {\n this.emit('warning-cleared', undefined, 'reconnect');\n }\n // 'connected' can re-fire after a recovery, so stop existing monitors\n // before recreating to avoid leaking their poll intervals.\n (_a = this.qualityMonitor) === null || _a === void 0 ? void 0 : _a.stop();\n this.qualityMonitor = new CallQualityMonitor(this.pc, (name, message) => this.emit('warning', message, name), (name) => this.emit('warning-cleared', undefined, name));\n this.qualityMonitor.start();\n (_b = this.stallDetector) === null || _b === void 0 ? void 0 : _b.stop();\n this.stallDetector = new MediaStallDetector(this.pc, () => {\n void this.triggerIceRestart();\n }, () => {\n // Media is flowing again: the restart (or a passive recovery) worked.\n this.clearIceRestart();\n this.iceRestartAttempts = 0;\n // A same-PC ICE restart fires no connectionState transition, so\n // recovery is confirmed here: leave 'reconnecting' and clear the\n // indicator.\n if (this.state === 'reconnecting') {\n this.setState('connected');\n }\n this.emit('warning-cleared', undefined, 'reconnect');\n });\n this.stallDetector.start();\n // Resolve connect() promise\n (_c = this.connectResolve) === null || _c === void 0 ? void 0 : _c.call(this);\n this.connectResolve = null;\n this.connectReject = null;\n break;\n }\n case 'disconnected':\n // A pod-drain resume owns the lifecycle; don't start a recovery while we\n // are intentionally tearing the PC down.\n if (this.drainReceived) {\n break;\n }\n void this.triggerIceRestart();\n break;\n case 'failed':\n // During a resume, just fail the pending handshake; reconnect() handles\n // retry/teardown. Do not emitError or tear the call down here.\n if (this.drainReceived) {\n (_d = this.connectReject) === null || _d === void 0 ? void 0 : _d.call(this, new Error('ICE connection failed'));\n this.connectResolve = null;\n this.connectReject = null;\n break;\n }\n // Initial connect that never reached 'connected': no session to recover.\n if (this.connectReject) {\n this.connectReject(new Error('ICE connection failed'));\n this.connectResolve = null;\n this.connectReject = null;\n this.emitError('Connection failed');\n this.cleanup();\n break;\n }\n // Established call: recover via ICE restart. A 'failed' while a restart is\n // already in flight means that attempt died, so react now (retry or give\n // up) instead of waiting out the recovery timeout.\n if (this.iceRestarting) {\n this.onRestartAttemptExpired();\n }\n else {\n void this.triggerIceRestart();\n }\n break;\n case 'closed':\n this.cleanup();\n break;\n }\n }\n /**\n * Recover a mid-call media drop by restarting ICE on the live PeerConnection\n * (same session, same WS). Bounded attempts; each waits ICE_RESTART_TIMEOUT_MS\n * to reach 'connected' before retrying or giving up. Never runs during a\n * pod-drain resume, which owns its own teardown.\n */\n async triggerIceRestart() {\n if (this.drainReceived || !this.pc || !this.signaling) {\n return;\n }\n // One restart at a time; bound total attempts.\n if (this.iceRestarting ||\n this.iceRestartAttempts >= MAX_ICE_RESTART_ATTEMPTS) {\n return;\n }\n // A re-offer can't reach the server over a dead WS (a combined media +\n // signaling drop needs WS reconnect, not ICE restart); don't burn attempts.\n if (!this.signaling.isConnected) {\n this.emitError('Connection lost');\n this.cleanup();\n return;\n }\n const pc = this.pc;\n const signaling = this.signaling;\n this.iceRestarting = true;\n this.iceRestartAttempts++;\n this.setState('reconnecting');\n // 'reconnect' warning drives the widgets' \"Reconnecting…\" indicator.\n this.emit('warning', 'Reconnecting…', 'reconnect');\n try {\n const offer = await pc.createOffer({ iceRestart: true });\n if (!offer.sdp) {\n throw new Error('createOffer returned no SDP');\n }\n await pc.setLocalDescription(offer);\n signaling.send({ type: 'offer', sdp: offer.sdp });\n }\n catch (_a) {\n this.iceRestarting = false;\n this.emitError('Connection lost');\n this.cleanup();\n return;\n }\n // Teardown, a drain resume, or a new call on a reused provider may have run\n // during the awaits (clearing iceRestarting or replacing pc); don't arm it.\n if (!this.iceRestarting || this.pc !== pc) {\n return;\n }\n // Recovery is confirmed by media resuming (the detector's onRecover); this\n // timer only fires if it never does, then retries or gives up.\n this.iceRestartTimeout = setTimeout(() => {\n this.onRestartAttemptExpired();\n }, ICE_RESTART_TIMEOUT_MS);\n }\n /** A restart attempt yielded no recovery (timeout, or a hard ICE 'failed'). */\n onRestartAttemptExpired() {\n if (this.iceRestartTimeout) {\n clearTimeout(this.iceRestartTimeout);\n this.iceRestartTimeout = null;\n }\n this.iceRestarting = false;\n if (this.iceRestartAttempts < MAX_ICE_RESTART_ATTEMPTS) {\n void this.triggerIceRestart();\n }\n else {\n this.emitError('Connection lost');\n this.cleanup();\n }\n }\n /** Cancel an in-flight ICE-restart recovery (on recovery or teardown). */\n clearIceRestart() {\n if (this.iceRestartTimeout) {\n clearTimeout(this.iceRestartTimeout);\n this.iceRestartTimeout = null;\n }\n this.iceRestarting = false;\n }\n handleSignalingMessage(msg) {\n var _a;\n switch (msg.type) {\n case 'answer':\n void this.handleAnswer(msg.sdp);\n break;\n case 'ice':\n void this.addIceCandidate(msg.candidate);\n break;\n // 'transcript' and 'agent_response' are forward-looking — channels\n // Phase 1 (PR #756) does not emit them over signaling. Kept so the\n // client is ready when channels wires them up.\n case 'transcript':\n this.emit('transcript', msg.text, {\n text: msg.text,\n is_final: msg.is_final,\n });\n break;\n case 'agent_response':\n this.emit('agent-response', msg.text, {\n text: msg.text,\n message_id: msg.message_id,\n });\n break;\n case 'hangup':\n this.disconnectInternal(true);\n break;\n // session_ready (not welcome) is the resume-eligible signal: cache the\n // credentials for a future reconnect.\n case 'session_ready':\n this.originalSessionID = msg.session_id;\n this.originalConnectionID = msg.connection_id;\n break;\n // Rotated credential for the next reconnect.\n case 'reconnect_ack':\n this.originalConnectionID = msg.connection_id;\n break;\n case 'drain':\n void this.handleDrain(msg);\n break;\n case 'error':\n if (this.drainReceived) {\n // Record for reconnect()'s terminal-vs-retryable check. If a resume\n // is awaiting ICE (connectReject set), fail it now so the retry loop\n // reacts immediately instead of waiting out RECONNECT_ICE_TIMEOUT_MS.\n this.reconnectErrorCode = msg.error.code;\n (_a = this.connectReject) === null || _a === void 0 ? void 0 : _a.call(this, new Error(`reconnect rejected: ${msg.error.code}`));\n }\n else {\n this.emitError(msg.error.message);\n }\n break;\n }\n }\n handleSignalingClose(_code, _reason) {\n var _a;\n // The reconnect flow owns teardown during a resume. If a resume is awaiting\n // ICE (connectReject set), fail it now so the retry loop reacts to the\n // dropped WS immediately; otherwise (pre-ack / jitter window) just ignore.\n if (this.drainReceived) {\n (_a = this.connectReject) === null || _a === void 0 ? void 0 : _a.call(this, new Error('Signaling closed during resume'));\n return;\n }\n if (this.state !== 'idle') {\n this.disconnect();\n }\n }\n handleSignalingError(_error) {\n if (this.state === 'connected') {\n this.emitError('Signaling error');\n }\n }\n /**\n * Server is draining this pod: resume the same session on a sibling after a\n * jittered delay. Distinct from the ICE-level 'disconnected' path (a blip) —\n * this is a server-initiated handoff.\n */\n async handleDrain(msg) {\n // Already resuming: a second drain (sibling pod drains before ICE connects)\n // is handled by the in-flight retry loop. Re-entering would hard-kill it.\n if (this.drainReceived) {\n return;\n }\n // Only a fully-established session is resumable: a drain before\n // session_ready (no cached credentials) or while not connected can't resume.\n // 'reconnecting' here is an in-flight ICE restart (drainReceived was checked\n // above); the session is still established and resumable, so don't kill it.\n if ((this.state !== 'connected' && this.state !== 'reconnecting') ||\n this.originalSessionID === undefined ||\n this.originalConnectionID === undefined) {\n this.emitError('Could not resume the call.');\n this.disconnectInternal(true);\n return;\n }\n this.drainReceived = true;\n this.setState('reconnecting');\n // The drain owns recovery now: cancel any in-flight ICE restart so its timer\n // can't fire during the jitter wait and abort this resumable drain.\n this.clearIceRestart();\n // Jitter 0..delay to avoid a thundering herd against the sibling pods.\n const delayMs = Math.max(0, msg.reconnect_delay_ms || 0);\n await this.sleep(Math.random() * delayMs);\n // A teardown (user hangup / destroy) during the jitter clears drainReceived;\n // don't resurrect an ended call.\n if (!this.drainReceived) {\n return;\n }\n await this.reconnect();\n }\n /**\n * Tear down the pod-local PC/WS and resume on a sibling pod, reusing the\n * existing mic MediaStream (no second permission prompt). Retries transient\n * failures up to MAX_CLIENT_RETRIES; gives up on a terminal server rejection.\n */\n async reconnect() {\n var _a, _b, _c;\n // Detach senders before pc.close() (ghost-sender defense), then confirm the\n // mic track survived the drain (device unplug / OS reset / BT switch kill it).\n try {\n (_a = this.pc) === null || _a === void 0 ? void 0 : _a.getSenders().forEach((sender) => {\n var _a;\n if (sender.track) {\n (_a = this.pc) === null || _a === void 0 ? void 0 : _a.removeTrack(sender);\n }\n });\n }\n catch (_d) {\n // continue — closing the pc below releases the senders anyway\n }\n const track = (_b = this.localStream) === null || _b === void 0 ? void 0 : _b.getAudioTracks()[0];\n if (!track || track.readyState !== 'live') {\n this.emitError('Microphone is no longer available.');\n this.disconnectInternal(true);\n return;\n }\n // Drop pod-local resources but KEEP the MediaStream alive for reuse.\n this.teardownForReconnect();\n while (this.reconnectAttempts < MAX_CLIENT_RETRIES) {\n this.reconnectErrorCode = null;\n try {\n // Fresh /config → fresh TURN creds for the sibling pod.\n this.config = await this.fetchConfig();\n this.signaling = new SignalingClient(this.wsUrl, this.config.sessionToken, this.handleSignalingMessage.bind(this), this.handleSignalingClose.bind(this), this.handleSignalingError.bind(this));\n // Sends `reconnect` as the first frame; resolves on reconnect_ack.\n await this.signaling.connectForReconnect({\n type: 'reconnect',\n session_id: this.originalSessionID,\n connection_id: this.originalConnectionID,\n });\n // Resumed — rebuild the media plane and wait for ICE to reconnect.\n await this.buildReconnectPeerConnection(track);\n this.reconnectAttempts = 0;\n this.drainReceived = false;\n // Clear any \"reconnecting…\" banner raised by a retried attempt.\n this.emit('warning-cleared', undefined, 'reconnect');\n return;\n }\n catch (error) {\n // A terminal server rejection (bad credential, expired/ended session,\n // recovery cap) is not retryable — surface and stop.\n const code = this.reconnectErrorCode;\n if (code && TERMINAL_RECONNECT_CODES.has(code)) {\n this.emitError((_c = RECONNECT_ERROR_MESSAGES[code]) !== null && _c !== void 0 ? _c : 'Could not resume the call.');\n this.disconnectInternal(true);\n return;\n }\n // A terminal /config failure (e.g. TURN unavailable) won't fix by\n // retrying — surface its message and stop.\n if (error instanceof ConfigUnavailableError) {\n this.emitError(error.message);\n this.disconnectInternal(true);\n return;\n }\n // Transient (WS/ICE, server_busy 503, rate-limited): back off and retry.\n // Only server_busy surfaces its message; a rate-limit reconnect stays\n // generic since \"too many attempts\" misreads an automatic retry.\n const message = error instanceof ServerBusyError ? error.message : 'Reconnecting…';\n this.emit('warning', message, 'reconnect');\n this.teardownForReconnect();\n this.reconnectAttempts++;\n if (this.reconnectAttempts >= MAX_CLIENT_RETRIES) {\n break;\n }\n await this.sleep(RECONNECT_BACKOFF_MS * this.reconnectAttempts);\n // Torn down (hangup / destroy) during the backoff — stop retrying.\n if (!this.drainReceived) {\n return;\n }\n }\n }\n this.emitError('Could not resume the call.');\n this.disconnectInternal(true);\n }\n /**\n * Build a fresh PeerConnection for a resume using the existing audio track.\n * Mirrors the fresh-connect PC setup but rejects on offer failure so the\n * reconnect retry loop can decide what to do (the fresh path tears down).\n */\n buildReconnectPeerConnection(audioTrack) {\n this.pc = new RTCPeerConnection({\n iceServers: this.config.iceServers,\n iceTransportPolicy: this.config.iceTransportPolicy || 'relay',\n });\n this.pc.addTrack(audioTrack, this.localStream);\n this.pc.ontrack = this.handleRemoteTrack.bind(this);\n this.pc.onicecandidate = (event) => {\n var _a;\n if (event.candidate) {\n (_a = this.signaling) === null || _a === void 0 ? void 0 : _a.send({\n type: 'ice',\n candidate: event.candidate.toJSON(),\n });\n }\n };\n this.pc.onconnectionstatechange =\n this.handleConnectionStateChange.bind(this);\n this.audioMonitor = new AudioLevelMonitor();\n this.audioMonitor.start(this.localStream, (level) => {\n this.emit('audio-level', level, Number(level));\n });\n return new Promise((resolve, reject) => {\n // Bound the resume: if ICE never reaches 'connected' (stalled, or the\n // sibling-pod WS drops after the ack — both swallowed by the drain-path\n // handlers), reject so the retry loop can take over instead of hanging.\n const timer = setTimeout(() => {\n this.connectResolve = null;\n this.connectReject = null;\n reject(new Error('Reconnect media resume timed out'));\n }, RECONNECT_ICE_TIMEOUT_MS);\n // Resolved/rejected by handleConnectionStateChange ('connected'/'failed').\n this.connectResolve = () => {\n clearTimeout(timer);\n resolve();\n };\n this.connectReject = (error) => {\n clearTimeout(timer);\n reject(error);\n };\n void (async () => {\n var _a;\n try {\n const offer = await this.pc.createOffer();\n if (!offer.sdp) {\n throw new Error('createOffer returned no SDP');\n }\n await this.pc.setLocalDescription(offer);\n this.signaling.send({ type: 'offer', sdp: offer.sdp });\n }\n catch (error) {\n const name = error instanceof Error ? error.name : 'createOffer';\n (_a = this.connectReject) === null || _a === void 0 ? void 0 : _a.call(this, new Error(WEBRTC_ERRORS[name] || 'Failed to create call offer'));\n }\n })();\n });\n }\n /**\n * Stop pod-local resources (PC, signaling, monitors, stall detector, remote\n * audio element) but deliberately KEEP localStream so the mic is reused.\n */\n teardownForReconnect() {\n var _a, _b, _c, _d, _e, _f;\n try {\n (_a = this.qualityMonitor) === null || _a === void 0 ? void 0 : _a.stop();\n this.qualityMonitor = null;\n }\n catch (_g) {\n // continue cleanup\n }\n try {\n (_b = this.stallDetector) === null || _b === void 0 ? void 0 : _b.stop();\n this.stallDetector = null;\n }\n catch (_h) {\n // continue cleanup\n }\n try {\n (_c = this.audioMonitor) === null || _c === void 0 ? void 0 : _c.stop();\n this.audioMonitor = null;\n }\n catch (_j) {\n // continue cleanup\n }\n try {\n (_d = this.remoteAudioMonitor) === null || _d === void 0 ? void 0 : _d.stop();\n this.remoteAudioMonitor = null;\n }\n catch (_k) {\n // continue cleanup\n }\n try {\n (_e = this.pc) === null || _e === void 0 ? void 0 : _e.close();\n this.pc = null;\n }\n catch (_l) {\n // continue cleanup\n }\n try {\n (_f = this.signaling) === null || _f === void 0 ? void 0 : _f.close();\n this.signaling = null;\n }\n catch (_m) {\n // continue cleanup\n }\n try {\n if (this.remoteAudio) {\n this.remoteAudio.srcObject = null;\n this.remoteAudio.remove();\n this.remoteAudio = null;\n }\n }\n catch (_o) {\n // continue cleanup\n }\n this.clearIceRestart();\n this.iceRestartAttempts = 0;\n this.connectResolve = null;\n this.connectReject = null;\n this.pendingCandidates = [];\n }\n sleep(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n async handleAnswer(sdp) {\n if (!this.pc)\n return;\n try {\n await this.pc.setRemoteDescription({ type: 'answer', sdp });\n await this.flushPendingCandidates();\n }\n catch (_a) {\n this.emitError(WEBRTC_ERRORS['setRemoteDescription']);\n }\n }\n async addIceCandidate(candidate) {\n var _a;\n if ((_a = this.pc) === null || _a === void 0 ? void 0 : _a.remoteDescription) {\n try {\n await this.pc.addIceCandidate(candidate);\n }\n catch (_b) {\n // Non-critical — log but don't fail\n }\n }\n else {\n this.pendingCandidates.push(candidate);\n }\n }\n async flushPendingCandidates() {\n for (const candidate of this.pendingCandidates) {\n if (!this.pc)\n break;\n try {\n await this.pc.addIceCandidate(candidate);\n }\n catch (_a) {\n // Non-critical\n }\n }\n this.pendingCandidates = [];\n }\n cleanup() {\n this.disconnect();\n }\n cleanupLocalStream() {\n var _a;\n try {\n (_a = this.localStream) === null || _a === void 0 ? void 0 : _a.getTracks().forEach((t) => t.stop());\n this.localStream = null;\n }\n catch (_b) {\n // continue cleanup\n }\n }\n setState(newState) {\n this.state = newState;\n this.emit('state-change', newState, newState);\n }\n emitError(message) {\n this.emit('error', message);\n this.setState('error');\n }\n emit(event, message, data) {\n const detail = { state: event, message, data };\n const invoke = (name) => {\n const listeners = this.eventListeners.get(name);\n if (!listeners)\n return;\n listeners.forEach((listener) => {\n try {\n listener(detail);\n }\n catch (_a) {\n // Ignore listener errors\n }\n });\n };\n invoke(event);\n invoke('any');\n }\n}\n"],"names":["AudioLevelMonitor","constructor","this","analyserNode","audioSource","animationId","audioContext","start","stream","onLevel","AudioContext","createMediaStreamSource","createAnalyser","fftSize","smoothingTimeConstant","connect","dataArray","Float32Array","checkAudioLevel","stop","getFloatTimeDomainData","sum","amplitude","rms","Math","sqrt","length","normalizedLevel","min","toFixed","requestAnimationFrame","_a","cancelAnimationFrame","disconnect","close","catch","MEDIA_ERRORS","NotAllowedError","NotFoundError","NotReadableError","OverconstrainedError","QUALITY_WARNINGS","CallQualityMonitor","pc","onWarning","onWarningCleared","pollIntervalMs","interval","prevStats","activeWarnings","Set","setInterval","pollStats","clearInterval","clear","stats","getStats","rtt","jitter","packetsReceived","packetsLost","forEach","report","_b","_c","type","nominated","crt","currentRoundTripTime","kind","rtp","lossRate","deltaReceived","deltaLost","total","checkWarning","R","max","mos","name","isExceeded","has","add","delete","ServerBusyError","Error","message","super","ConfigUnavailableError","RateLimitedError","MediaStallDetector","onStall","onRecover","flatPollsThreshold","prevTimestamp","flatPolls","stalled","poll","lastPacketTs","lastPacketReceivedTimestamp","undefined","SignalingClient","url","sessionToken","onMessage","onClose","onError","ws","Promise","resolve","reject","wsUrl","encodeURIComponent","WebSocket","timeout","setTimeout","onopen","onmessage","event","msg","JSON","parse","data","clearTimeout","onerror","onclose","code","reason","connectForReconnect","reconnectFrame","settled","send","stringify","error","readyState","OPEN","isConnected","TERMINAL_RECONNECT_CODES","RECONNECT_ERROR_MESSAGES","session_expired","session_inactive","invalid_credential","max_recovery_exceeded","unexpected_first_frame","WEBRTC_ERRORS","Object","assign","setRemoteDescription","setLocalDescription","addIceCandidate","createOffer","WebRTCProvider","fetchConfig","signaling","localStream","remoteAudio","remoteAudioMonitor","audioMonitor","qualityMonitor","stallDetector","pendingCandidates","state","eventListeners","Map","iceRestarting","iceRestartAttempts","iceRestartTimeout","connectResolve","connectReject","drainReceived","reconnectAttempts","reconnectErrorCode","setConfig","config","setState","navigator","mediaDevices","getUserMedia","audio","echoCancellation","noiseSuppression","autoGainControl","emitError","handleSignalingMessage","bind","handleSignalingClose","handleSignalingError","cleanupLocalStream","RTCPeerConnection","iceServers","iceTransportPolicy","audioTrack","getAudioTracks","addTrack","ontrack","handleRemoteTrack","onicecandidate","candidate","toJSON","onconnectionstatechange","handleConnectionStateChange","offer","sdp","cleanup","level","emit","Number","disconnectInternal","suppressOutboundHangup","_d","_e","_f","_g","_h","_j","_k","_l","_m","_o","_p","srcObject","remove","_q","clearIceRestart","originalSessionID","originalConnectionID","toggleMute","track","enabled","muted","isMuted","getLocalStream","sendDigits","digits","ch","digit","on","handler","listeners","get","set","off","size","document","createElement","streams","autoplay","body","appendChild","connectionState","wasReconnecting","triggerIceRestart","call","onRestartAttemptExpired","iceRestart","handleAnswer","text","is_final","message_id","session_id","connection_id","handleDrain","_code","_reason","_error","delayMs","reconnect_delay_ms","sleep","random","reconnect","getSenders","sender","removeTrack","teardownForReconnect","buildReconnectPeerConnection","timer","ms","flushPendingCandidates","remoteDescription","push","getTracks","t","newState","detail","invoke","listener"],"sourceRoot":""}
|
|
1
|
+
{"version":3,"file":"882.min.js","mappings":"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","sources":["webpack://MoveoAI/./src/controller/shared/audio-level-monitor.ts","webpack://MoveoAI/./src/controller/shared/media-errors.ts","webpack://MoveoAI/./src/controller/webrtc/call-quality-monitor.ts","webpack://MoveoAI/./src/controller/webrtc/config-errors.ts","webpack://MoveoAI/./src/controller/webrtc/media-stall-detector.ts","webpack://MoveoAI/./src/controller/webrtc/select-candidate-pair.ts","webpack://MoveoAI/./src/controller/webrtc/signaling-client.ts","webpack://MoveoAI/./src/controller/webrtc/webrtc-provider.ts"],"sourcesContent":["const NOISE_GATE_THRESHOLD = 0.01;\n/**\n * Monitors a MediaStream's audio level via Web Audio API.\n * Extracted from AudioController for reuse across voice providers.\n *\n * Uses AnalyserNode with RMS calculation:\n * fftSize: 256, smoothingTimeConstant: 0.3\n * Noise gate: 0.01 threshold\n * Normalized output: 0.0 - 1.0\n */\nexport class AudioLevelMonitor {\n constructor() {\n this.analyserNode = null;\n this.audioSource = null;\n this.animationId = null;\n this.audioContext = null;\n }\n start(stream, onLevel) {\n try {\n this.audioContext = new AudioContext();\n this.audioSource = this.audioContext.createMediaStreamSource(stream);\n this.analyserNode = this.audioContext.createAnalyser();\n this.analyserNode.fftSize = 256;\n this.analyserNode.smoothingTimeConstant = 0.3;\n this.audioSource.connect(this.analyserNode);\n const dataArray = new Float32Array(this.analyserNode.fftSize);\n const checkAudioLevel = () => {\n if (!this.analyserNode) {\n this.stop();\n return;\n }\n this.analyserNode.getFloatTimeDomainData(dataArray);\n let sum = 0;\n for (const amplitude of dataArray) {\n sum += amplitude * amplitude;\n }\n const rms = Math.sqrt(sum / dataArray.length);\n const normalizedLevel = Math.min(1, rms * 10);\n const gatedLevel = normalizedLevel > NOISE_GATE_THRESHOLD ? normalizedLevel : 0;\n onLevel(gatedLevel.toFixed(3));\n this.animationId = requestAnimationFrame(checkAudioLevel);\n };\n checkAudioLevel();\n }\n catch (_a) {\n // Silently fail — audio monitoring is non-critical\n }\n }\n stop() {\n if (this.animationId !== null) {\n cancelAnimationFrame(this.animationId);\n this.animationId = null;\n }\n if (this.audioSource) {\n try {\n this.audioSource.disconnect();\n }\n catch (_a) {\n // ignore\n }\n this.audioSource = null;\n }\n if (this.audioContext) {\n void this.audioContext.close().catch(() => {\n // ignore\n });\n this.audioContext = null;\n }\n this.analyserNode = null;\n }\n}\n","/**\n * User-friendly messages for the common getUserMedia DOMException names.\n * Shared across voice providers (native WebRTC, Telnyx) so a single string\n * surfaces in the widget regardless of provider.\n */\nexport const MEDIA_ERRORS = {\n NotAllowedError: 'Microphone permission denied',\n NotFoundError: 'No microphone found',\n NotReadableError: 'Microphone is in use by another application',\n OverconstrainedError: 'Microphone does not support required settings',\n};\n","import { selectCandidatePair } from './select-candidate-pair';\nconst QUALITY_WARNINGS = {\n 'high-rtt': 'High network latency detected',\n 'low-mos': 'Poor call quality detected',\n 'high-jitter': 'Network instability detected',\n 'high-packet-loss': 'Packet loss detected',\n};\nconst THRESHOLDS = {\n RTT: 400, // ms\n JITTER: 0.03, // seconds (30ms)\n PACKET_LOSS: 0.03, // 3%\n MOS: 3.0,\n};\n/**\n * Replaces Twilio Voice Insights with standard WebRTC getStats().\n * Polls pc.getStats() and maps to warning events that AudioWidget handles.\n */\nexport class CallQualityMonitor {\n constructor(pc, onWarning, onWarningCleared, pollIntervalMs = 5000) {\n this.pc = pc;\n this.onWarning = onWarning;\n this.onWarningCleared = onWarningCleared;\n this.pollIntervalMs = pollIntervalMs;\n this.interval = null;\n this.prevStats = null;\n this.activeWarnings = new Set();\n }\n start() {\n this.interval = setInterval(() => {\n void this.pollStats();\n }, this.pollIntervalMs);\n }\n stop() {\n if (this.interval !== null) {\n clearInterval(this.interval);\n this.interval = null;\n }\n this.prevStats = null;\n this.activeWarnings.clear();\n }\n async pollStats() {\n try {\n const stats = await this.pc.getStats();\n let jitter = null;\n let packetsReceived = 0;\n let packetsLost = 0;\n // RTT from the pair carrying media, not any nominated one: a stale pair\n // would skew high-rtt / MOS warnings on relay networks.\n const pair = selectCandidatePair(stats);\n const rtt = (pair === null || pair === void 0 ? void 0 : pair.currentRoundTripTime) != null\n ? pair.currentRoundTripTime * 1000\n : null;\n stats.forEach((report) => {\n var _a, _b, _c;\n if (report.type === 'inbound-rtp' && report.kind === 'audio') {\n const rtp = report;\n jitter = (_a = rtp.jitter) !== null && _a !== void 0 ? _a : null;\n packetsReceived = (_b = rtp.packetsReceived) !== null && _b !== void 0 ? _b : 0;\n packetsLost = (_c = rtp.packetsLost) !== null && _c !== void 0 ? _c : 0;\n }\n });\n // Compute packet loss rate over delta\n let lossRate = 0;\n if (this.prevStats) {\n const deltaReceived = packetsReceived - this.prevStats.packetsReceived;\n const deltaLost = packetsLost - this.prevStats.packetsLost;\n const total = deltaReceived + deltaLost;\n lossRate = total > 0 ? deltaLost / total : 0;\n }\n this.prevStats = { packetsReceived, packetsLost };\n // Check thresholds\n this.checkWarning('high-rtt', rtt !== null && rtt > THRESHOLDS.RTT);\n this.checkWarning('high-jitter', jitter !== null && jitter > THRESHOLDS.JITTER);\n this.checkWarning('high-packet-loss', lossRate > THRESHOLDS.PACKET_LOSS);\n // MOS estimation (simplified E-model)\n if (rtt !== null && jitter !== null) {\n const jitterMs = jitter * 1000;\n const lossPercent = lossRate * 100;\n const R = 93.2 - jitterMs * 2 - lossPercent * 40 - Math.max(0, rtt - 150) * 0.1;\n const mos = 1 + 0.035 * R + R * (R - 60) * (100 - R) * 7e-6;\n this.checkWarning('low-mos', mos < THRESHOLDS.MOS);\n }\n }\n catch (_a) {\n // pc may have closed mid-poll; quality stats are non-critical\n }\n }\n checkWarning(name, isExceeded) {\n if (isExceeded && !this.activeWarnings.has(name)) {\n this.activeWarnings.add(name);\n this.onWarning(name, QUALITY_WARNINGS[name] || name);\n }\n else if (!isExceeded && this.activeWarnings.has(name)) {\n this.activeWarnings.delete(name);\n this.onWarningCleared(name);\n }\n }\n}\n","/**\n * Typed errors for the GET /config flow, shared by WebRTCController (initial\n * connect) and WebRTCProvider (reconnect-time re-fetch) so both can branch on\n * a server-busy 503 without parsing message strings.\n */\n/**\n * Raised when /config returns 503 with error_code \"server_busy\" — the pod is at\n * its concurrent-session cap. Retryable with backoff against a sibling pod.\n */\nexport class ServerBusyError extends Error {\n constructor(message = 'Voice service is busy, please try again.') {\n super(message);\n this.name = 'ServerBusyError';\n }\n}\n/**\n * Raised when /config fails in a way retrying can't fix (e.g. TURN or signing\n * key not configured, or origin not allowed). Terminal — the reconnect loop\n * must not burn retries on it.\n */\nexport class ConfigUnavailableError extends Error {\n constructor(message) {\n super(message);\n this.name = 'ConfigUnavailableError';\n }\n}\n/**\n * Raised when /config returns 429 — the integration hit its per-integration\n * rate limit. The caller should back off and let the user retry shortly.\n */\nexport class RateLimitedError extends Error {\n constructor(message = 'Too many attempts. Please try again in a moment.') {\n super(message);\n this.name = 'RateLimitedError';\n }\n}\n","import debug from 'debug';\nimport { describeCandidatePair, selectCandidatePair, } from './select-candidate-pair';\n/**\n * Detects a mid-call media-path stall (and its recovery) so the provider can\n * drive an ICE restart and confirm when media returns. Separate from\n * CallQualityMonitor: that polls every 5s for UX warnings, but recovery needs a\n * faster signal that beats the server's ~15s give-up.\n *\n * Codec-independent: it watches the selected candidate-pair's\n * lastPacketReceivedTimestamp, which advances on ANY inbound packet (media RTP,\n * RTCP, or STUN consent) regardless of codec or DTX. On a healthy call it moves\n * well within the debounce window (PCMU sends continuously; Opus-DTX still sends\n * SID frames sub-second); a dead path freezes it. This is why it does not depend\n * on a specific codec the way an inbound-rtp bytesReceived check would.\n *\n * \"Selected\" is the pair `selectCandidatePair` resolves, not \"any nominated\":\n * relay networks leave several pairs nominated and a stale one would otherwise\n * trigger false stalls. The assessed pair's identity is tracked so a mid-call\n * re-nomination reseeds rather than comparing timestamps across pairs.\n *\n * If a browser ever omits lastPacketReceivedTimestamp on the selected pair, the\n * detector simply never fires (no false positives, no recovery) — revisit with\n * a fallback signal then.\n */\nexport class MediaStallDetector {\n constructor(pc, onStall, onRecover, pollIntervalMs = 1000, \n // Consecutive polls with no inbound packet before declaring a stall;\n // debounces a brief blip while still beating the 15s server close.\n flatPollsThreshold = 3) {\n this.pc = pc;\n this.onStall = onStall;\n this.onRecover = onRecover;\n this.pollIntervalMs = pollIntervalMs;\n this.flatPollsThreshold = flatPollsThreshold;\n this.log = debug('moveo:webrtc:media');\n this.interval = null;\n this.prevTimestamp = -1;\n this.prevPairId = null;\n this.flatPolls = 0;\n this.stalled = false;\n // Logs the \"cannot assess\" reason once per episode instead of every poll.\n this.cannotAssessLogged = false;\n }\n start() {\n this.interval = setInterval(() => {\n void this.poll();\n }, this.pollIntervalMs);\n }\n stop() {\n if (this.interval !== null) {\n clearInterval(this.interval);\n this.interval = null;\n }\n this.prevTimestamp = -1;\n this.prevPairId = null;\n this.flatPolls = 0;\n this.stalled = false;\n this.cannotAssessLogged = false;\n }\n async poll() {\n try {\n const stats = await this.pc.getStats();\n const pair = selectCandidatePair(stats);\n const lastPacketTs = pair === null || pair === void 0 ? void 0 : pair.lastPacketReceivedTimestamp;\n // No selected pair yet, or the field is unsupported (e.g. Safari): can't\n // assess. Log once per episode so a silently-inert detector is visible.\n if (pair === undefined || lastPacketTs === undefined) {\n if (!this.cannotAssessLogged) {\n this.cannotAssessLogged = true;\n this.log('cannot assess: pair=%s hasTimestamp=%s', pair === null || pair === void 0 ? void 0 : pair.id, lastPacketTs !== undefined);\n }\n return;\n }\n this.cannotAssessLogged = false;\n // Pair changed (restart / re-nomination): reseed rather than compare\n // across pairs. `stalled` is left as-is, so recovery lands one poll later.\n if (pair.id !== this.prevPairId) {\n this.log('assessed pair changed %s -> %s (%s)', this.prevPairId, pair.id, describeCandidatePair(stats, pair));\n this.prevPairId = pair.id;\n this.prevTimestamp = lastPacketTs;\n this.flatPolls = 0;\n return;\n }\n if (lastPacketTs === this.prevTimestamp) {\n this.flatPolls++;\n this.log('flat poll pair=%s ts=%d flat=%d', pair.id, lastPacketTs, this.flatPolls);\n if (this.flatPolls >= this.flatPollsThreshold && !this.stalled) {\n this.stalled = true;\n this.log('stall declared on pair %s (%s) after %d flat polls', pair.id, describeCandidatePair(stats, pair), this.flatPolls);\n this.onStall();\n }\n }\n else {\n // A packet arrived: media is flowing. Signal recovery if we were stalled.\n if (this.stalled) {\n this.stalled = false;\n this.log('recovered on pair %s (%s)', pair.id, describeCandidatePair(stats, pair));\n this.onRecover();\n }\n this.flatPolls = 0;\n }\n this.prevTimestamp = lastPacketTs;\n }\n catch (_a) {\n // pc may have closed mid-poll; non-critical\n }\n }\n}\n","/**\n * The candidate pair actually carrying media, resolved by\n * transport.selectedCandidatePairId → legacy `selected` flag → best nominated.\n * \"Any nominated\" is not enough: relay paths leave several pairs nominated and\n * a stale one can win, driving false stalls. Best nominated prefers the newest\n * lastPacketReceivedTimestamp (a frozen pair's is in the past), never displacing\n * a timestamped pair with a field-less one, and falls back to the first\n * nominated when none report a timestamp. Callers read the field they need.\n */\nexport function selectCandidatePair(stats) {\n let selectedPairId;\n const pairs = [];\n stats.forEach((report) => {\n if (report.type === 'transport') {\n const { selectedCandidatePairId } = report;\n if (selectedCandidatePairId && !selectedPairId) {\n selectedPairId = selectedCandidatePairId;\n }\n }\n else if (report.type === 'candidate-pair') {\n pairs.push(report);\n }\n });\n // A dangling transport id falls through to the legacy `selected` flag before\n // the nominated fallback.\n const selected = (selectedPairId && pairs.find((p) => p.id === selectedPairId)) ||\n pairs.find((p) => p.selected === true);\n if (selected) {\n return selected;\n }\n let best;\n for (const p of pairs) {\n if (!p.nominated) {\n continue;\n }\n if (best === undefined) {\n best = p;\n continue;\n }\n const bt = best.lastPacketReceivedTimestamp;\n const pt = p.lastPacketReceivedTimestamp;\n if (pt !== undefined && (bt === undefined || pt > bt)) {\n best = p;\n }\n }\n return best;\n}\n/**\n * Compact `local -> remote` candidate-type summary for a pair, e.g.\n * `relay/udp(udp) -> prflx/udp`. Resolves the pair's candidate stats so the\n * media path (relayed vs direct vs asymmetric) is visible in logs without a\n * webrtc-internals dump. Addresses are deliberately omitted (they are PII).\n */\nexport function describeCandidatePair(stats, pair) {\n const describe = (id) => {\n var _a, _b;\n if (!id) {\n return '?';\n }\n const c = stats.get(id);\n if (!c) {\n return '?';\n }\n const base = `${(_a = c.candidateType) !== null && _a !== void 0 ? _a : '?'}/${(_b = c.protocol) !== null && _b !== void 0 ? _b : '?'}`;\n return c.relayProtocol ? `${base}(${c.relayProtocol})` : base;\n };\n return `${describe(pair.localCandidateId)} -> ${describe(pair.remoteCandidateId)}`;\n}\n","import debug from 'debug';\nconst CONNECT_TIMEOUT_MS = 10000;\n/**\n * JSON-over-WebSocket signaling for WebRTC voice sessions.\n * Stateless thin wrapper — all connection state lives in WebRTCProvider.\n */\nexport class SignalingClient {\n constructor(url, sessionToken, onMessage, onClose, onError) {\n this.url = url;\n this.sessionToken = sessionToken;\n this.onMessage = onMessage;\n this.onClose = onClose;\n this.onError = onError;\n this.log = debug('moveo:webrtc:media');\n this.ws = null;\n }\n connect() {\n return new Promise((resolve, reject) => {\n this.log('connecting to %s', this.url);\n const wsUrl = `${this.url}?token=${encodeURIComponent(this.sessionToken)}`;\n this.ws = new WebSocket(wsUrl);\n const timeout = setTimeout(() => {\n var _a;\n (_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();\n reject(new Error('Signaling connection timed out'));\n }, CONNECT_TIMEOUT_MS);\n this.ws.onopen = () => {\n // Wait for welcome message before resolving\n };\n this.ws.onmessage = (event) => {\n let msg;\n try {\n msg = JSON.parse(event.data);\n }\n catch (_a) {\n // Ignore malformed JSON\n return;\n }\n this.log('recv %s', msg.type);\n if (msg.type === 'welcome') {\n clearTimeout(timeout);\n resolve();\n }\n this.onMessage(msg);\n };\n this.ws.onerror = (event) => {\n clearTimeout(timeout);\n this.log('websocket error');\n this.onError(event);\n reject(new Error('Signaling WebSocket error'));\n };\n this.ws.onclose = (event) => {\n clearTimeout(timeout);\n this.log('closed code=%d reason=%s', event.code, event.reason);\n reject(new Error('Signaling WebSocket closed before connection established'));\n this.onClose(event.code, event.reason);\n };\n });\n }\n /**\n * Open a fresh WS to resume a drained session. Sends `reconnect` as the\n * first frame on open and resolves on `reconnect_ack` (not `welcome` — a\n * resumed session does not re-issue welcome). The fresh-call `connect()`\n * path is intentionally left untouched.\n */\n connectForReconnect(reconnectFrame) {\n return new Promise((resolve, reject) => {\n this.log('reconnecting to %s', this.url);\n const wsUrl = `${this.url}?token=${encodeURIComponent(this.sessionToken)}`;\n this.ws = new WebSocket(wsUrl);\n let settled = false;\n const timeout = setTimeout(() => {\n var _a;\n if (settled)\n return;\n settled = true;\n (_a = this.ws) === null || _a === void 0 ? void 0 : _a.close();\n reject(new Error('Signaling reconnect timed out'));\n }, CONNECT_TIMEOUT_MS);\n this.ws.onopen = () => {\n var _a;\n // reconnect MUST be the first frame, before any offer.\n (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(JSON.stringify(reconnectFrame));\n };\n this.ws.onmessage = (event) => {\n let msg;\n try {\n msg = JSON.parse(event.data);\n }\n catch (_a) {\n // Ignore malformed JSON\n return;\n }\n this.log('recv %s', msg.type);\n // Deliver to the provider first so it records the credential rotation\n // (reconnect_ack) or the reject code (error) before we settle.\n this.onMessage(msg);\n if (settled)\n return;\n if (msg.type === 'reconnect_ack') {\n settled = true;\n clearTimeout(timeout);\n resolve();\n }\n else if (msg.type === 'error') {\n // Server rejected the resume. Settle now rather than waiting on the\n // WS close that follows — avoids a per-attempt CONNECT_TIMEOUT stall\n // and makes the reject deterministic w.r.t. the captured error code.\n settled = true;\n clearTimeout(timeout);\n reject(new Error(`reconnect rejected: ${msg.error.code}`));\n }\n };\n this.ws.onerror = (event) => {\n this.log('websocket error');\n this.onError(event);\n if (settled)\n return;\n settled = true;\n clearTimeout(timeout);\n reject(new Error('Signaling WebSocket error'));\n };\n this.ws.onclose = (event) => {\n this.log('closed code=%d reason=%s', event.code, event.reason);\n if (!settled) {\n settled = true;\n clearTimeout(timeout);\n reject(new Error('Signaling WebSocket closed before reconnect established'));\n }\n this.onClose(event.code, event.reason);\n };\n });\n }\n send(msg) {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n return;\n }\n this.log('send %s', msg.type);\n this.ws.send(JSON.stringify(msg));\n }\n close() {\n if (this.ws) {\n this.ws.onclose = null;\n this.ws.onerror = null;\n this.ws.onmessage = null;\n this.ws.close(1000, 'Client closed');\n this.ws = null;\n }\n }\n get isConnected() {\n var _a;\n return ((_a = this.ws) === null || _a === void 0 ? void 0 : _a.readyState) === WebSocket.OPEN;\n }\n}\n","import 'webrtc-adapter';\nimport debug from 'debug';\nimport { AudioLevelMonitor } from '../shared/audio-level-monitor';\nimport { MEDIA_ERRORS } from '../shared/media-errors';\nimport { CallQualityMonitor } from './call-quality-monitor';\nimport { ConfigUnavailableError, ServerBusyError } from './config-errors';\nimport { MediaStallDetector } from './media-stall-detector';\nimport { SignalingClient } from './signaling-client';\nconst REMOTE_AUDIO_THRESHOLD = 0.02;\n// ICE-restart recovery bounds (mid-call media drop).\nconst MAX_ICE_RESTART_ATTEMPTS = 2;\nconst ICE_RESTART_TIMEOUT_MS = 10000; // per-attempt window to reach 'connected'\n// Pod-drain reconnect bounds.\nconst MAX_CLIENT_RETRIES = 3;\nconst RECONNECT_BACKOFF_MS = 500; // scaled by attempt number\n// Settle window for the post-ack media resume; on the drain path the WS-close\n// teardown is disabled, so this is the only guard against a wedged resume.\nconst RECONNECT_ICE_TIMEOUT_MS = 15000;\n// Reconnect error codes that cannot be resumed — give up without retrying.\n// Any other failure (transient WS/ICE, server_busy) is retryable.\nconst TERMINAL_RECONNECT_CODES = new Set([\n 'session_expired',\n 'session_inactive',\n 'invalid_credential',\n 'max_recovery_exceeded',\n 'unexpected_first_frame',\n]);\n/** User-facing copy for terminal reconnect failures. Infra-agnostic. */\nconst RECONNECT_ERROR_MESSAGES = {\n session_expired: 'Your session has expired.',\n session_inactive: 'The call has ended.',\n invalid_credential: 'Could not resume the call.',\n max_recovery_exceeded: 'Could not resume the call after several attempts.',\n unexpected_first_frame: 'Could not resume the call.',\n};\n/** User-friendly error messages for WebRTC API failures */\nconst WEBRTC_ERRORS = Object.assign(Object.assign({}, MEDIA_ERRORS), { setRemoteDescription: 'Connection setup failed', setLocalDescription: 'Connection setup failed', addIceCandidate: 'Network configuration failed', createOffer: 'Browser audio setup failed' });\n/** Native WebRTC voice provider for Moveo web-voice integrations. */\nexport class WebRTCProvider {\n constructor(wsUrl, \n // Re-fetches /config (fresh TURN + session token) for a resume; throws\n // ServerBusyError on a server-busy 503. Injected so signaling stays here.\n fetchConfig) {\n this.wsUrl = wsUrl;\n this.fetchConfig = fetchConfig;\n this.log = debug('moveo:webrtc:media');\n this.pc = null;\n this.signaling = null;\n this.localStream = null;\n this.remoteStream = null;\n this.remoteAudio = null;\n this.remoteAudioMonitor = null;\n this.audioMonitor = null;\n this.qualityMonitor = null;\n this.stallDetector = null;\n this.pendingCandidates = [];\n this.state = 'idle';\n this.eventListeners = new Map();\n // --- ICE-restart recovery state ---\n this.iceRestarting = false;\n this.iceRestartAttempts = 0;\n this.iceRestartTimeout = null;\n this.connectResolve = null;\n this.connectReject = null;\n // Set between a drain frame and a completed resume; gates the ICE-level\n // disconnect/failed handlers so the drain flow owns teardown.\n this.drainReceived = false;\n this.reconnectAttempts = 0;\n // Captured from an error frame mid-resume to classify terminal vs retryable.\n this.reconnectErrorCode = null;\n }\n /** Supply the /config result (TURN + session token) for the next connect(). */\n setConfig(config) {\n this.config = config;\n }\n async connect() {\n if (!this.config) {\n throw new Error('Voice config not set; call setConfig() before connect().');\n }\n this.setState('connecting');\n // 1. Get microphone access\n try {\n this.localStream = await navigator.mediaDevices.getUserMedia({\n audio: {\n echoCancellation: true,\n noiseSuppression: true,\n autoGainControl: true,\n },\n });\n }\n catch (error) {\n const name = error instanceof Error ? error.name : '';\n const message = WEBRTC_ERRORS[name] || 'Microphone access failed';\n this.emitError(message);\n throw new Error(message);\n }\n // 2. Open signaling WebSocket\n this.signaling = new SignalingClient(this.wsUrl, this.config.sessionToken, this.handleSignalingMessage.bind(this), this.handleSignalingClose.bind(this), this.handleSignalingError.bind(this));\n try {\n await this.signaling.connect();\n }\n catch (error) {\n this.cleanupLocalStream();\n const message = error instanceof Error ? error.message : 'Signaling connection failed';\n this.emitError(message);\n throw new Error(message);\n }\n // 3. Create PeerConnection\n this.pc = new RTCPeerConnection({\n iceServers: this.config.iceServers,\n iceTransportPolicy: this.config.iceTransportPolicy || 'relay',\n });\n // 4. Add local audio track\n const audioTrack = this.localStream.getAudioTracks()[0];\n this.pc.addTrack(audioTrack, this.localStream);\n // 5. Set up PC handlers\n this.pc.ontrack = this.handleRemoteTrack.bind(this);\n this.pc.onicecandidate = (event) => {\n var _a;\n if (event.candidate) {\n (_a = this.signaling) === null || _a === void 0 ? void 0 : _a.send({\n type: 'ice',\n candidate: event.candidate.toJSON(),\n });\n }\n };\n this.pc.onconnectionstatechange =\n this.handleConnectionStateChange.bind(this);\n // 6. Create and send offer\n try {\n const offer = await this.pc.createOffer();\n if (!offer.sdp) {\n throw new Error('createOffer returned no SDP');\n }\n await this.pc.setLocalDescription(offer);\n this.signaling.send({ type: 'offer', sdp: offer.sdp });\n }\n catch (error) {\n this.cleanup();\n const name = error instanceof Error ? error.name : 'createOffer';\n const message = WEBRTC_ERRORS[name] || 'Failed to create call offer';\n this.emitError(message);\n throw new Error(message);\n }\n // 7. Start local audio monitoring\n this.audioMonitor = new AudioLevelMonitor();\n this.audioMonitor.start(this.localStream, (level) => {\n this.emit('audio-level', level, Number(level));\n });\n // 8. Wait for PeerConnection to reach 'connected' state\n return new Promise((resolve, reject) => {\n this.connectResolve = resolve;\n this.connectReject = reject;\n });\n }\n disconnect() {\n this.disconnectInternal(false);\n }\n disconnectInternal(suppressOutboundHangup) {\n // Fault-tolerant disconnect: each step wrapped in try/catch\n var _a, _b, _c, _d, _e, _f, _g;\n if (!suppressOutboundHangup) {\n try {\n (_a = this.signaling) === null || _a === void 0 ? void 0 : _a.send({ type: 'hangup' });\n }\n catch (_h) {\n // continue cleanup\n }\n }\n try {\n (_b = this.qualityMonitor) === null || _b === void 0 ? void 0 : _b.stop();\n this.qualityMonitor = null;\n }\n catch (_j) {\n // continue cleanup\n }\n try {\n (_c = this.stallDetector) === null || _c === void 0 ? void 0 : _c.stop();\n this.stallDetector = null;\n }\n catch (_k) {\n // continue cleanup\n }\n try {\n (_d = this.audioMonitor) === null || _d === void 0 ? void 0 : _d.stop();\n this.audioMonitor = null;\n }\n catch (_l) {\n // continue cleanup\n }\n try {\n (_e = this.remoteAudioMonitor) === null || _e === void 0 ? void 0 : _e.stop();\n this.remoteAudioMonitor = null;\n }\n catch (_m) {\n // continue cleanup\n }\n try {\n (_f = this.pc) === null || _f === void 0 ? void 0 : _f.close();\n this.pc = null;\n }\n catch (_o) {\n // continue cleanup\n }\n this.cleanupLocalStream();\n try {\n (_g = this.signaling) === null || _g === void 0 ? void 0 : _g.close();\n this.signaling = null;\n }\n catch (_p) {\n // continue cleanup\n }\n try {\n if (this.remoteAudio) {\n this.remoteAudio.srcObject = null;\n this.remoteAudio.remove();\n this.remoteAudio = null;\n }\n this.remoteStream = null;\n }\n catch (_q) {\n // continue cleanup\n }\n this.clearIceRestart();\n this.iceRestartAttempts = 0;\n if (this.connectReject) {\n this.connectReject(new Error('Call disconnected before connection completed'));\n }\n this.connectResolve = null;\n this.connectReject = null;\n // Clear pod-drain reconnect state so a torn-down provider can't leave\n // drainReceived stuck true (which would make handleSignalingClose swallow a\n // future legitimate close), carry a stale retry count, or resume a new call\n // with the prior session's credentials before its own session_ready lands.\n this.drainReceived = false;\n this.reconnectAttempts = 0;\n this.reconnectErrorCode = null;\n this.originalSessionID = undefined;\n this.originalConnectionID = undefined;\n this.pendingCandidates = [];\n this.setState('idle');\n }\n toggleMute() {\n var _a, _b;\n const track = (_a = this.localStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks()[0];\n if (track) {\n track.enabled = !track.enabled;\n const muted = !track.enabled;\n (_b = this.signaling) === null || _b === void 0 ? void 0 : _b.send({ type: 'mute', muted });\n return muted;\n }\n return false;\n }\n isMuted() {\n var _a, _b, _c;\n return !((_c = (_b = (_a = this.localStream) === null || _a === void 0 ? void 0 : _a.getAudioTracks()[0]) === null || _b === void 0 ? void 0 : _b.enabled) !== null && _c !== void 0 ? _c : true);\n }\n getLocalStream() {\n return this.localStream;\n }\n getRemoteStream() {\n return this.remoteStream;\n }\n sendDigits(digits) {\n var _a;\n // Server expects one digit per signaling message (matches Twilio path,\n // which lets the SDK split a multi-char string internally).\n for (const ch of digits) {\n (_a = this.signaling) === null || _a === void 0 ? void 0 : _a.send({ type: 'dtmf', digit: ch });\n }\n }\n on(event, handler) {\n var _a;\n const listeners = (_a = this.eventListeners.get(event)) !== null && _a !== void 0 ? _a : new Set();\n listeners.add(handler);\n this.eventListeners.set(event, listeners);\n return () => this.off(event, handler);\n }\n off(event, handler) {\n const listeners = this.eventListeners.get(event);\n if (!listeners)\n return;\n listeners.delete(handler);\n if (listeners.size === 0) {\n this.eventListeners.delete(event);\n }\n }\n // --- Private methods ---\n handleRemoteTrack(event) {\n const [remoteStream] = event.streams;\n this.remoteStream = remoteStream !== null && remoteStream !== void 0 ? remoteStream : null;\n this.remoteAudio = document.createElement('audio');\n this.remoteAudio.srcObject = remoteStream;\n this.remoteAudio.autoplay = true;\n document.body.appendChild(this.remoteAudio);\n // Safari (incl. iOS, and stricter in Private mode) does not reliably\n // autoplay an <audio> whose source is a MediaStream — the `autoplay`\n // attribute alone leaves it silent. Kick off an explicit play(); the call\n // already ran getUserMedia behind a user gesture, so the audio session is\n // unlocked. Swallow a rejection so a blocked attempt stays silent, not fatal.\n void this.remoteAudio.play().catch(() => {\n // Playback blocked — the element stays live; nothing else to do here.\n });\n // Remote audio monitoring via AnalyserNode for thinking indicator\n this.remoteAudioMonitor = new AudioLevelMonitor();\n this.remoteAudioMonitor.start(remoteStream, (level) => {\n if (Number(level) > REMOTE_AUDIO_THRESHOLD) {\n this.emit('remote-audio', level, Number(level));\n }\n });\n }\n handleConnectionStateChange() {\n var _a, _b, _c, _d;\n if (!this.pc)\n return;\n this.log('connectionState %s', this.pc.connectionState);\n switch (this.pc.connectionState) {\n case 'connecting':\n this.setState('connecting');\n break;\n case 'connected': {\n const wasReconnecting = this.state === 'reconnecting';\n this.setState('connected');\n // Reaching 'connected' means any in-flight ICE restart recovered.\n this.clearIceRestart();\n this.iceRestartAttempts = 0;\n // A disconnected → connected recovery fires no stall onRecover, so the\n // \"Reconnecting…\" indicator is cleared here.\n if (wasReconnecting) {\n this.emit('warning-cleared', undefined, 'reconnect');\n }\n // 'connected' can re-fire after a recovery, so stop existing monitors\n // before recreating to avoid leaking their poll intervals.\n (_a = this.qualityMonitor) === null || _a === void 0 ? void 0 : _a.stop();\n this.qualityMonitor = new CallQualityMonitor(this.pc, (name, message) => this.emit('warning', message, name), (name) => this.emit('warning-cleared', undefined, name));\n this.qualityMonitor.start();\n (_b = this.stallDetector) === null || _b === void 0 ? void 0 : _b.stop();\n this.stallDetector = new MediaStallDetector(this.pc, () => {\n this.log('media stall -> ICE restart');\n void this.triggerIceRestart();\n }, () => {\n this.log('media recovered');\n // Media is flowing again: the restart (or a passive recovery) worked.\n this.clearIceRestart();\n this.iceRestartAttempts = 0;\n // A same-PC ICE restart fires no connectionState transition, so\n // recovery is confirmed here: leave 'reconnecting' and clear the\n // indicator.\n if (this.state === 'reconnecting') {\n this.setState('connected');\n }\n this.emit('warning-cleared', undefined, 'reconnect');\n });\n this.stallDetector.start();\n // Resolve connect() promise\n (_c = this.connectResolve) === null || _c === void 0 ? void 0 : _c.call(this);\n this.connectResolve = null;\n this.connectReject = null;\n break;\n }\n case 'disconnected':\n // A pod-drain resume owns the lifecycle; don't start a recovery while we\n // are intentionally tearing the PC down.\n if (this.drainReceived) {\n break;\n }\n void this.triggerIceRestart();\n break;\n case 'failed':\n // During a resume, just fail the pending handshake; reconnect() handles\n // retry/teardown. Do not emitError or tear the call down here.\n if (this.drainReceived) {\n (_d = this.connectReject) === null || _d === void 0 ? void 0 : _d.call(this, new Error('ICE connection failed'));\n this.connectResolve = null;\n this.connectReject = null;\n break;\n }\n // Initial connect that never reached 'connected': no session to recover.\n if (this.connectReject) {\n this.connectReject(new Error('ICE connection failed'));\n this.connectResolve = null;\n this.connectReject = null;\n this.emitError('Connection failed');\n this.cleanup();\n break;\n }\n // Established call: recover via ICE restart. A 'failed' while a restart is\n // already in flight means that attempt died, so react now (retry or give\n // up) instead of waiting out the recovery timeout.\n if (this.iceRestarting) {\n this.onRestartAttemptExpired();\n }\n else {\n void this.triggerIceRestart();\n }\n break;\n case 'closed':\n this.cleanup();\n break;\n }\n }\n /**\n * Recover a mid-call media drop by restarting ICE on the live PeerConnection\n * (same session, same WS). Bounded attempts; each waits ICE_RESTART_TIMEOUT_MS\n * to reach 'connected' before retrying or giving up. Never runs during a\n * pod-drain resume, which owns its own teardown.\n */\n async triggerIceRestart() {\n if (this.drainReceived || !this.pc || !this.signaling) {\n return;\n }\n // One restart at a time; bound total attempts.\n if (this.iceRestarting ||\n this.iceRestartAttempts >= MAX_ICE_RESTART_ATTEMPTS) {\n return;\n }\n // A re-offer can't reach the server over a dead WS (a combined media +\n // signaling drop needs WS reconnect, not ICE restart); don't burn attempts.\n if (!this.signaling.isConnected) {\n this.emitError('Connection lost');\n this.cleanup();\n return;\n }\n const pc = this.pc;\n const signaling = this.signaling;\n this.iceRestarting = true;\n this.iceRestartAttempts++;\n this.log('ICE restart attempt %d/%d', this.iceRestartAttempts, MAX_ICE_RESTART_ATTEMPTS);\n this.setState('reconnecting');\n // 'reconnect' warning drives the widgets' \"Reconnecting…\" indicator.\n this.emit('warning', 'Reconnecting…', 'reconnect');\n try {\n const offer = await pc.createOffer({ iceRestart: true });\n if (!offer.sdp) {\n throw new Error('createOffer returned no SDP');\n }\n await pc.setLocalDescription(offer);\n signaling.send({ type: 'offer', sdp: offer.sdp });\n }\n catch (_a) {\n this.iceRestarting = false;\n this.emitError('Connection lost');\n this.cleanup();\n return;\n }\n // Teardown, a drain resume, or a new call on a reused provider may have run\n // during the awaits (clearing iceRestarting or replacing pc); don't arm it.\n if (!this.iceRestarting || this.pc !== pc) {\n return;\n }\n // Recovery is confirmed by media resuming (the detector's onRecover); this\n // timer only fires if it never does, then retries or gives up.\n this.iceRestartTimeout = setTimeout(() => {\n this.onRestartAttemptExpired();\n }, ICE_RESTART_TIMEOUT_MS);\n }\n /** A restart attempt yielded no recovery (timeout, or a hard ICE 'failed'). */\n onRestartAttemptExpired() {\n this.log('ICE restart attempt expired (attempts=%d)', this.iceRestartAttempts);\n if (this.iceRestartTimeout) {\n clearTimeout(this.iceRestartTimeout);\n this.iceRestartTimeout = null;\n }\n this.iceRestarting = false;\n if (this.iceRestartAttempts < MAX_ICE_RESTART_ATTEMPTS) {\n void this.triggerIceRestart();\n }\n else {\n this.emitError('Connection lost');\n this.cleanup();\n }\n }\n /** Cancel an in-flight ICE-restart recovery (on recovery or teardown). */\n clearIceRestart() {\n if (this.iceRestarting) {\n this.log('ICE restart cleared');\n }\n if (this.iceRestartTimeout) {\n clearTimeout(this.iceRestartTimeout);\n this.iceRestartTimeout = null;\n }\n this.iceRestarting = false;\n }\n handleSignalingMessage(msg) {\n var _a;\n switch (msg.type) {\n case 'answer':\n void this.handleAnswer(msg.sdp);\n break;\n case 'ice':\n void this.addIceCandidate(msg.candidate);\n break;\n // 'transcript' and 'agent_response' are forward-looking — channels\n // Phase 1 (PR #756) does not emit them over signaling. Kept so the\n // client is ready when channels wires them up.\n case 'transcript':\n this.emit('transcript', msg.text, {\n text: msg.text,\n is_final: msg.is_final,\n });\n break;\n case 'agent_response':\n this.emit('agent-response', msg.text, {\n text: msg.text,\n message_id: msg.message_id,\n });\n break;\n case 'hangup':\n this.disconnectInternal(true);\n break;\n // session_ready (not welcome) is the resume-eligible signal: cache the\n // credentials for a future reconnect.\n case 'session_ready':\n this.originalSessionID = msg.session_id;\n this.originalConnectionID = msg.connection_id;\n break;\n // Rotated credential for the next reconnect.\n case 'reconnect_ack':\n this.originalConnectionID = msg.connection_id;\n break;\n case 'drain':\n void this.handleDrain(msg);\n break;\n case 'error':\n if (this.drainReceived) {\n // Record for reconnect()'s terminal-vs-retryable check. If a resume\n // is awaiting ICE (connectReject set), fail it now so the retry loop\n // reacts immediately instead of waiting out RECONNECT_ICE_TIMEOUT_MS.\n this.reconnectErrorCode = msg.error.code;\n (_a = this.connectReject) === null || _a === void 0 ? void 0 : _a.call(this, new Error(`reconnect rejected: ${msg.error.code}`));\n }\n else {\n this.emitError(msg.error.message);\n }\n break;\n }\n }\n handleSignalingClose(_code, _reason) {\n var _a;\n // The reconnect flow owns teardown during a resume. If a resume is awaiting\n // ICE (connectReject set), fail it now so the retry loop reacts to the\n // dropped WS immediately; otherwise (pre-ack / jitter window) just ignore.\n if (this.drainReceived) {\n (_a = this.connectReject) === null || _a === void 0 ? void 0 : _a.call(this, new Error('Signaling closed during resume'));\n return;\n }\n if (this.state !== 'idle') {\n this.disconnect();\n }\n }\n handleSignalingError(_error) {\n if (this.state === 'connected') {\n this.emitError('Signaling error');\n }\n }\n /**\n * Server is draining this pod: resume the same session on a sibling after a\n * jittered delay. Distinct from the ICE-level 'disconnected' path (a blip) —\n * this is a server-initiated handoff.\n */\n async handleDrain(msg) {\n // Already resuming: a second drain (sibling pod drains before ICE connects)\n // is handled by the in-flight retry loop. Re-entering would hard-kill it.\n if (this.drainReceived) {\n return;\n }\n // Only a fully-established session is resumable: a drain before\n // session_ready (no cached credentials) or while not connected can't resume.\n // 'reconnecting' here is an in-flight ICE restart (drainReceived was checked\n // above); the session is still established and resumable, so don't kill it.\n if ((this.state !== 'connected' && this.state !== 'reconnecting') ||\n this.originalSessionID === undefined ||\n this.originalConnectionID === undefined) {\n this.emitError('Could not resume the call.');\n this.disconnectInternal(true);\n return;\n }\n this.drainReceived = true;\n this.setState('reconnecting');\n // The drain owns recovery now: cancel any in-flight ICE restart so its timer\n // can't fire during the jitter wait and abort this resumable drain.\n this.clearIceRestart();\n // Jitter 0..delay to avoid a thundering herd against the sibling pods.\n const delayMs = Math.max(0, msg.reconnect_delay_ms || 0);\n await this.sleep(Math.random() * delayMs);\n // A teardown (user hangup / destroy) during the jitter clears drainReceived;\n // don't resurrect an ended call.\n if (!this.drainReceived) {\n return;\n }\n await this.reconnect();\n }\n /**\n * Tear down the pod-local PC/WS and resume on a sibling pod, reusing the\n * existing mic MediaStream (no second permission prompt). Retries transient\n * failures up to MAX_CLIENT_RETRIES; gives up on a terminal server rejection.\n */\n async reconnect() {\n var _a, _b, _c;\n // Detach senders before pc.close() (ghost-sender defense), then confirm the\n // mic track survived the drain (device unplug / OS reset / BT switch kill it).\n try {\n (_a = this.pc) === null || _a === void 0 ? void 0 : _a.getSenders().forEach((sender) => {\n var _a;\n if (sender.track) {\n (_a = this.pc) === null || _a === void 0 ? void 0 : _a.removeTrack(sender);\n }\n });\n }\n catch (_d) {\n // continue — closing the pc below releases the senders anyway\n }\n const track = (_b = this.localStream) === null || _b === void 0 ? void 0 : _b.getAudioTracks()[0];\n if (!track || track.readyState !== 'live') {\n this.emitError('Microphone is no longer available.');\n this.disconnectInternal(true);\n return;\n }\n // Drop pod-local resources but KEEP the MediaStream alive for reuse.\n this.teardownForReconnect();\n while (this.reconnectAttempts < MAX_CLIENT_RETRIES) {\n this.reconnectErrorCode = null;\n try {\n // Fresh /config → fresh TURN creds for the sibling pod.\n this.config = await this.fetchConfig();\n this.signaling = new SignalingClient(this.wsUrl, this.config.sessionToken, this.handleSignalingMessage.bind(this), this.handleSignalingClose.bind(this), this.handleSignalingError.bind(this));\n // Sends `reconnect` as the first frame; resolves on reconnect_ack.\n await this.signaling.connectForReconnect({\n type: 'reconnect',\n session_id: this.originalSessionID,\n connection_id: this.originalConnectionID,\n });\n // Resumed — rebuild the media plane and wait for ICE to reconnect.\n await this.buildReconnectPeerConnection(track);\n this.reconnectAttempts = 0;\n this.drainReceived = false;\n // Clear any \"reconnecting…\" banner raised by a retried attempt.\n this.emit('warning-cleared', undefined, 'reconnect');\n return;\n }\n catch (error) {\n // A terminal server rejection (bad credential, expired/ended session,\n // recovery cap) is not retryable — surface and stop.\n const code = this.reconnectErrorCode;\n if (code && TERMINAL_RECONNECT_CODES.has(code)) {\n this.emitError((_c = RECONNECT_ERROR_MESSAGES[code]) !== null && _c !== void 0 ? _c : 'Could not resume the call.');\n this.disconnectInternal(true);\n return;\n }\n // A terminal /config failure (e.g. TURN unavailable) won't fix by\n // retrying — surface its message and stop.\n if (error instanceof ConfigUnavailableError) {\n this.emitError(error.message);\n this.disconnectInternal(true);\n return;\n }\n // Transient (WS/ICE, server_busy 503, rate-limited): back off and retry.\n // Only server_busy surfaces its message; a rate-limit reconnect stays\n // generic since \"too many attempts\" misreads an automatic retry.\n const message = error instanceof ServerBusyError ? error.message : 'Reconnecting…';\n this.emit('warning', message, 'reconnect');\n this.teardownForReconnect();\n this.reconnectAttempts++;\n if (this.reconnectAttempts >= MAX_CLIENT_RETRIES) {\n break;\n }\n await this.sleep(RECONNECT_BACKOFF_MS * this.reconnectAttempts);\n // Torn down (hangup / destroy) during the backoff — stop retrying.\n if (!this.drainReceived) {\n return;\n }\n }\n }\n this.emitError('Could not resume the call.');\n this.disconnectInternal(true);\n }\n /**\n * Build a fresh PeerConnection for a resume using the existing audio track.\n * Mirrors the fresh-connect PC setup but rejects on offer failure so the\n * reconnect retry loop can decide what to do (the fresh path tears down).\n */\n buildReconnectPeerConnection(audioTrack) {\n this.pc = new RTCPeerConnection({\n iceServers: this.config.iceServers,\n iceTransportPolicy: this.config.iceTransportPolicy || 'relay',\n });\n this.pc.addTrack(audioTrack, this.localStream);\n this.pc.ontrack = this.handleRemoteTrack.bind(this);\n this.pc.onicecandidate = (event) => {\n var _a;\n if (event.candidate) {\n (_a = this.signaling) === null || _a === void 0 ? void 0 : _a.send({\n type: 'ice',\n candidate: event.candidate.toJSON(),\n });\n }\n };\n this.pc.onconnectionstatechange =\n this.handleConnectionStateChange.bind(this);\n this.audioMonitor = new AudioLevelMonitor();\n this.audioMonitor.start(this.localStream, (level) => {\n this.emit('audio-level', level, Number(level));\n });\n return new Promise((resolve, reject) => {\n // Bound the resume: if ICE never reaches 'connected' (stalled, or the\n // sibling-pod WS drops after the ack — both swallowed by the drain-path\n // handlers), reject so the retry loop can take over instead of hanging.\n const timer = setTimeout(() => {\n this.connectResolve = null;\n this.connectReject = null;\n reject(new Error('Reconnect media resume timed out'));\n }, RECONNECT_ICE_TIMEOUT_MS);\n // Resolved/rejected by handleConnectionStateChange ('connected'/'failed').\n this.connectResolve = () => {\n clearTimeout(timer);\n resolve();\n };\n this.connectReject = (error) => {\n clearTimeout(timer);\n reject(error);\n };\n void (async () => {\n var _a;\n try {\n const offer = await this.pc.createOffer();\n if (!offer.sdp) {\n throw new Error('createOffer returned no SDP');\n }\n await this.pc.setLocalDescription(offer);\n this.signaling.send({ type: 'offer', sdp: offer.sdp });\n }\n catch (error) {\n const name = error instanceof Error ? error.name : 'createOffer';\n (_a = this.connectReject) === null || _a === void 0 ? void 0 : _a.call(this, new Error(WEBRTC_ERRORS[name] || 'Failed to create call offer'));\n }\n })();\n });\n }\n /**\n * Stop pod-local resources (PC, signaling, monitors, stall detector, remote\n * audio element) but deliberately KEEP localStream so the mic is reused.\n */\n teardownForReconnect() {\n var _a, _b, _c, _d, _e, _f;\n try {\n (_a = this.qualityMonitor) === null || _a === void 0 ? void 0 : _a.stop();\n this.qualityMonitor = null;\n }\n catch (_g) {\n // continue cleanup\n }\n try {\n (_b = this.stallDetector) === null || _b === void 0 ? void 0 : _b.stop();\n this.stallDetector = null;\n }\n catch (_h) {\n // continue cleanup\n }\n try {\n (_c = this.audioMonitor) === null || _c === void 0 ? void 0 : _c.stop();\n this.audioMonitor = null;\n }\n catch (_j) {\n // continue cleanup\n }\n try {\n (_d = this.remoteAudioMonitor) === null || _d === void 0 ? void 0 : _d.stop();\n this.remoteAudioMonitor = null;\n }\n catch (_k) {\n // continue cleanup\n }\n try {\n (_e = this.pc) === null || _e === void 0 ? void 0 : _e.close();\n this.pc = null;\n }\n catch (_l) {\n // continue cleanup\n }\n try {\n (_f = this.signaling) === null || _f === void 0 ? void 0 : _f.close();\n this.signaling = null;\n }\n catch (_m) {\n // continue cleanup\n }\n try {\n if (this.remoteAudio) {\n this.remoteAudio.srcObject = null;\n this.remoteAudio.remove();\n this.remoteAudio = null;\n }\n this.remoteStream = null;\n }\n catch (_o) {\n // continue cleanup\n }\n this.clearIceRestart();\n this.iceRestartAttempts = 0;\n this.connectResolve = null;\n this.connectReject = null;\n this.pendingCandidates = [];\n }\n sleep(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n async handleAnswer(sdp) {\n if (!this.pc)\n return;\n try {\n await this.pc.setRemoteDescription({ type: 'answer', sdp });\n await this.flushPendingCandidates();\n }\n catch (_a) {\n this.emitError(WEBRTC_ERRORS['setRemoteDescription']);\n }\n }\n async addIceCandidate(candidate) {\n var _a;\n if ((_a = this.pc) === null || _a === void 0 ? void 0 : _a.remoteDescription) {\n try {\n await this.pc.addIceCandidate(candidate);\n }\n catch (_b) {\n // Non-critical — log but don't fail\n }\n }\n else {\n this.pendingCandidates.push(candidate);\n }\n }\n async flushPendingCandidates() {\n for (const candidate of this.pendingCandidates) {\n if (!this.pc)\n break;\n try {\n await this.pc.addIceCandidate(candidate);\n }\n catch (_a) {\n // Non-critical\n }\n }\n this.pendingCandidates = [];\n }\n cleanup() {\n this.disconnect();\n }\n cleanupLocalStream() {\n var _a;\n try {\n (_a = this.localStream) === null || _a === void 0 ? void 0 : _a.getTracks().forEach((t) => t.stop());\n this.localStream = null;\n }\n catch (_b) {\n // continue cleanup\n }\n }\n setState(newState) {\n this.state = newState;\n this.emit('state-change', newState, newState);\n }\n emitError(message) {\n this.emit('error', message);\n this.setState('error');\n }\n emit(event, message, data) {\n const detail = { state: event, message, data };\n const invoke = (name) => {\n const listeners = this.eventListeners.get(name);\n if (!listeners)\n return;\n listeners.forEach((listener) => {\n try {\n listener(detail);\n }\n catch (_a) {\n // Ignore listener errors\n }\n });\n };\n invoke(event);\n invoke('any');\n }\n}\n"],"names":["AudioLevelMonitor","constructor","this","analyserNode","audioSource","animationId","audioContext","start","stream","onLevel","AudioContext","createMediaStreamSource","createAnalyser","fftSize","smoothingTimeConstant","connect","dataArray","Float32Array","checkAudioLevel","stop","getFloatTimeDomainData","sum","amplitude","rms","Math","sqrt","length","normalizedLevel","min","toFixed","requestAnimationFrame","_a","cancelAnimationFrame","disconnect","close","catch","MEDIA_ERRORS","NotAllowedError","NotFoundError","NotReadableError","OverconstrainedError","QUALITY_WARNINGS","CallQualityMonitor","pc","onWarning","onWarningCleared","pollIntervalMs","interval","prevStats","activeWarnings","Set","setInterval","pollStats","clearInterval","clear","stats","getStats","jitter","packetsReceived","packetsLost","pair","rtt","currentRoundTripTime","forEach","report","_b","_c","type","kind","rtp","lossRate","deltaReceived","deltaLost","total","checkWarning","R","max","mos","name","isExceeded","has","add","delete","ServerBusyError","Error","message","super","ConfigUnavailableError","RateLimitedError","MediaStallDetector","onStall","onRecover","flatPollsThreshold","log","prevTimestamp","prevPairId","flatPolls","stalled","cannotAssessLogged","poll","lastPacketTs","lastPacketReceivedTimestamp","undefined","id","selectCandidatePair","selectedPairId","pairs","selectedCandidatePairId","push","selected","find","p","best","nominated","bt","pt","describeCandidatePair","describe","c","get","base","candidateType","protocol","relayProtocol","localCandidateId","remoteCandidateId","SignalingClient","url","sessionToken","onMessage","onClose","onError","ws","Promise","resolve","reject","wsUrl","encodeURIComponent","WebSocket","timeout","setTimeout","onopen","onmessage","event","msg","JSON","parse","data","clearTimeout","onerror","onclose","code","reason","connectForReconnect","reconnectFrame","settled","send","stringify","error","readyState","OPEN","isConnected","TERMINAL_RECONNECT_CODES","RECONNECT_ERROR_MESSAGES","session_expired","session_inactive","invalid_credential","max_recovery_exceeded","unexpected_first_frame","WEBRTC_ERRORS","Object","assign","setRemoteDescription","setLocalDescription","addIceCandidate","createOffer","WebRTCProvider","fetchConfig","signaling","localStream","remoteStream","remoteAudio","remoteAudioMonitor","audioMonitor","qualityMonitor","stallDetector","pendingCandidates","state","eventListeners","Map","iceRestarting","iceRestartAttempts","iceRestartTimeout","connectResolve","connectReject","drainReceived","reconnectAttempts","reconnectErrorCode","setConfig","config","setState","navigator","mediaDevices","getUserMedia","audio","echoCancellation","noiseSuppression","autoGainControl","emitError","handleSignalingMessage","bind","handleSignalingClose","handleSignalingError","cleanupLocalStream","RTCPeerConnection","iceServers","iceTransportPolicy","audioTrack","getAudioTracks","addTrack","ontrack","handleRemoteTrack","onicecandidate","candidate","toJSON","onconnectionstatechange","handleConnectionStateChange","offer","sdp","cleanup","level","emit","Number","disconnectInternal","suppressOutboundHangup","_d","_e","_f","_g","_h","_j","_k","_l","_m","_o","_p","srcObject","remove","_q","clearIceRestart","originalSessionID","originalConnectionID","toggleMute","track","enabled","muted","isMuted","getLocalStream","getRemoteStream","sendDigits","digits","ch","digit","on","handler","listeners","set","off","size","streams","document","createElement","autoplay","body","appendChild","play","connectionState","wasReconnecting","triggerIceRestart","call","onRestartAttemptExpired","iceRestart","handleAnswer","text","is_final","message_id","session_id","connection_id","handleDrain","_code","_reason","_error","delayMs","reconnect_delay_ms","sleep","random","reconnect","getSenders","sender","removeTrack","teardownForReconnect","buildReconnectPeerConnection","timer","ms","flushPendingCandidates","remoteDescription","getTracks","t","newState","detail","invoke","listener"],"sourceRoot":""}
|