@sanseng/livekit-ws-sdk 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,3 @@
1
- 中文[./guide_zh.md]
2
-
3
1
  # 🤖 Real-time Audio-Video Interaction SDK User Guide
4
2
 
5
3
  This SDK provides an integrated communication solution combining **LiveKit (RTC)** , specifically designed for digital human conversations, real-time voice interactions, and similar scenarios. It handles complex underlying logic such as media track synchronization, audio capture, streaming text parsing, and automatic reconnection.
package/dist/index.cjs.js CHANGED
@@ -107,4 +107,4 @@ class AudioResamplerProcessor extends AudioWorkletProcessor {
107
107
  }
108
108
 
109
109
  registerProcessor('audio-resampler', AudioResamplerProcessor);
110
- `;function be(){const o=new Blob([Ie],{type:"text/javascript"});return URL.createObjectURL(o)}class we extends ye{constructor(e){super(),this.options=e,this._stream=null,this._audioContext=null,this._sourceNode=null,this._workletNode=null,this._targetSampleRate=k,this._bitDepth=O,this._channels=1;const t=e?.input;this._targetSampleRate=t?.sampleRate||k,this._bitDepth=t?.bitDepth||O,this._channels=t?.channels||1}async start(){this.ensureNotDisposed(),(this._stream||this._audioContext||this._sourceNode||this._workletNode)&&this.stop();try{const e=this.options?.input,t={audio:e?.constraints?e.constraints:{channelCount:this._channels}};this._stream=await navigator.mediaDevices.getUserMedia(t);const r=this._stream.getAudioTracks()[0].getSettings().sampleRate||44100;this._audioContext=new AudioContext({sampleRate:r});const a=be();await this._audioContext.audioWorklet.addModule(a),this._sourceNode=this._audioContext.createMediaStreamSource(this._stream),this._workletNode=new AudioWorkletNode(this._audioContext,"audio-resampler",{processorOptions:{targetSampleRate:this._targetSampleRate,bitDepth:this._bitDepth,channels:this._channels},numberOfInputs:1,numberOfOutputs:0}),this._workletNode.port.onmessage=c=>{const{type:d,data:_,sampleRate:g}=c.data;d==="audioData"&&_&&this.emitAudioData(_,g)},this._sourceNode.connect(this._workletNode),s.info(`Audio capture started: input=${r}Hz -> output=${this._targetSampleRate}Hz, ${this._bitDepth}bit`)}catch(e){throw l.fromError(e,h.AUDIO_CAPTURE_FAILED)}}stop(){this._stream&&(this._stream.getTracks().forEach(e=>e.stop()),this._stream=null),this._workletNode&&(this._workletNode.port.onmessage=null,this._workletNode.disconnect(),this._workletNode=null),this._sourceNode&&(this._sourceNode.disconnect(),this._sourceNode=null),this._audioContext&&(this._audioContext.close(),this._audioContext=null),s.info("Audio capture stopped")}onDispose(){this.stop()}}class ke{create(e){return new we(e)}}class Ne extends f{constructor(e,t){super(),this._source=null,this._isWsCapturing=!1,this._currentOutputTrackId=null,this._inputPipeline=new De(e),this._outputPipeline=new Re,this._sourceFactory=t||new ke,s.info("Audio Controller created")}setContext(e){super.setContext(e),this._wireLocalInputToEventBus()}_wireLocalInputToEventBus(){this._inputPipeline.onProcessedAudio=(e,t)=>{this._context.eventBus.emit("inner:audio:frame:received",{data:e,sampleRate:t})}}async startWsCapture(){if(this.ensureNotDisposed(),this._isWsCapturing){s.warn("Audio capture already started");return}if(!this._context.mediaAudioCaptureState.tryBegin("ws"))throw new l(h.SDK_INVALID_STATE_TRANSITION,"RTC audio capture is active");try{this._source||(this._source=this._sourceFactory.create(this._context.options.audio),this._source.onAudioData=(e,t)=>{this._inputPipeline.process(e,t)}),await this._source.start(),this._isWsCapturing=!0,this._context.eventBus.emit("inner:audio:input:started",void 0),s.info("Audio capture started (ws)")}catch(e){throw this._context.mediaAudioCaptureState.end("ws"),this._context.emitError(e,h.AUDIO_CAPTURE_START_FAILED)}}stopWsCapture(){this.ensureNotDisposed(),this._isWsCapturing&&(this._source&&this._source.stop(),this._isWsCapturing=!1,this._context.mediaAudioCaptureState.end("ws"),this._context.eventBus.emit("inner:audio:input:stopped",void 0),s.info("Audio capture stopped (ws)"))}get isWsCapturing(){return this._isWsCapturing}setOutputTrack(e,t,i){this.ensureNotDisposed(),this._outputPipeline.setTrack(e),this._currentOutputTrackId=t,this._context.eventBus.emit("inner:audio:track:added",{trackId:t,participantId:i}),s.info("Audio output track ready")}removeOutputTrack(e){this.ensureNotDisposed(),this._currentOutputTrackId===e&&(this._outputPipeline.removeTrack(),this._currentOutputTrackId=null,this._context.eventBus.emit("inner:audio:track:removed",{trackId:e}),s.info("Audio output track removed"))}setVolume(e){this.ensureNotDisposed(),this._outputPipeline.setVolume(e),this._context.eventBus.emit("inner:audio:volume:changed",{volume:e}),s.debug(`Audio volume set to: ${e}`)}getVolume(){return this._outputPipeline.getVolume()}mute(){this.ensureNotDisposed(),this._outputPipeline.mute(),this._context.eventBus.emit("inner:audio:muted",void 0),s.debug("Audio muted")}unmute(){this.ensureNotDisposed(),this._outputPipeline.unmute(),this._context.eventBus.emit("inner:audio:unmuted",void 0),s.debug("Audio unmuted")}getOutputElement(){return this._outputPipeline.getAudioElement()}isMuted(){return this._outputPipeline.isMuted()}onDispose(){this.stopWsCapture(),this._source&&(this._source.dispose(),this._source=null),this._inputPipeline.dispose(),this._outputPipeline.dispose(),s.info("Audio Controller disposed")}}class xe extends u{constructor(){super(...arguments),this._stream=null,this._videoElement=null}async start(){if(this.ensureNotDisposed(),this._stream){s.warn("Camera capture already started");return}try{const e=await navigator.mediaDevices.getUserMedia({video:!0});if(this._stream=e,!this._videoElement){const t=document.createElement("video");t.autoplay=!0,t.muted=!0,t.playsInline=!0,t.style.position="fixed",t.style.opacity="0",t.style.pointerEvents="none",t.style.width="0",t.style.height="0",t.style.zIndex="-1",typeof document<"u"&&document.body&&document.body.appendChild(t),this._videoElement=t}this._videoElement.srcObject=this._stream,this._videoElement.play().catch(t=>{s.error("Failed to start camera video playback",t)})}catch(e){throw l.fromError(e,h.SDK_ERROR)}}stop(){if(this._stream&&(this._stream.getTracks().forEach(e=>e.stop()),this._stream=null),this._videoElement){this._videoElement.srcObject=null;const e=this._videoElement.parentNode;e&&e.removeChild(this._videoElement),this._videoElement=null}s.info("Camera capture stopped")}getStream(){return this.ensureNotDisposed(),this._stream}getVideoElement(){return this.ensureNotDisposed(),this._videoElement}getTrack(){if(this.ensureNotDisposed(),!this._stream)return null;const e=this._stream.getVideoTracks();return e.length>0?e[0]:null}onDispose(){this.stop()}}const D=640,T=360;class Le extends u{constructor(e,t){super(),this._videoElement=null,this._canvas=null,this._videoElement=e,this._onFrame=t,this._handleServerCommand=this._onServerCommand.bind(this)}setContext(e){this._context=e}setVideoElement(e){this._videoElement=e}start(){this.ensureNotDisposed(),this._canvas||(typeof OffscreenCanvas<"u"?this._canvas=new OffscreenCanvas(D,T):(this._canvas=document.createElement("canvas"),this._canvas.width=D,this._canvas.height=T)),this._context.eventBus.on("inner:conversation:server:command",this._handleServerCommand)}_onServerCommand({code:e}){e===500&&this._captureFrame()}stop(){this._context.eventBus.off("inner:conversation:server:command",this._handleServerCommand)}_captureFrame(){const e=this._videoElement,t=this._canvas;if(!e||!t||e.readyState<2||e.videoWidth===0||e.videoHeight===0)return;const i=t.getContext("2d");if(!i)return;const n=e.videoWidth,r=e.videoHeight;let a=D,c=D*r/n;c>T&&(c=T,a=T*n/r),t.width!==a&&(t.width=a),t.height!==c&&(t.height=c),i.clearRect(0,0,t.width,t.height),i.drawImage(e,0,0,n,r,0,0,a,c),typeof OffscreenCanvas<"u"&&t instanceof OffscreenCanvas&&typeof t.convertToBlob=="function"?t.convertToBlob({type:"image/jpeg",quality:S}).then(d=>{if(d)try{this._onFrame(d,a,c,0,S)}catch(_){s.error("ScreenshotScheduler onFrame callback error",_)}}).catch(d=>{s.error("ScreenshotScheduler convertToBlob error",d)}):t instanceof HTMLCanvasElement&&t.toBlob(d=>{if(d)try{this._onFrame(d,a,c,0,S)}catch(_){s.error("ScreenshotScheduler onFrame callback error",_)}},"image/jpeg",S)}onDispose(){this.stop(),this._canvas=null,this._videoElement=null}}async function Oe(o,e){const t=await o.arrayBuffer();return{data:new Uint8Array(t),width:e.width,height:e.height,format:e.format,quality:e.quality}}const Pe=12,Fe=2,Me=0,F=0,M=4,V=65535,U=4294967295;function Ve(o){const e=Math.floor(o);return e<0?0:e>255?255:e}function b(o){const e=Math.floor(o);return e<0?0:e>V?V:e}function Ue(o){const e=Math.floor(o);return e<0?0:e>U?U:e}function Ke(o){const e=Math.floor(o);return e<F?F:e>M?M:e}function He(o){const e=new ArrayBuffer(Pe),t=new DataView(e),i=Ke(o.format)&15,n=Fe<<6|Me<<4|i;return t.setUint8(0,n),t.setUint8(1,Ve(o.quality)),t.setUint16(2,b(o.id),!1),t.setUint16(4,b(o.width),!1),t.setUint16(6,b(o.height),!1),t.setUint32(8,Ue(o.payloadLength),!1),new Uint8Array(e)}const Be=65535;class qe extends u{constructor(){super(...arguments),this._wsSender=null,this._imageId=0}send(e,t){if(this.ensureNotDisposed(),!this._wsSender||!this._wsSender.isConnected())return;const i=e.byteLength,n=He({format:t.format,quality:t.quality,id:this._nextId(),width:t.width,height:t.height,payloadLength:i}),r=new Uint8Array(n.byteLength+i);r.set(n,0),r.set(e,n.byteLength),this._wsSender.sendBinary(r)}_nextId(){const e=this._imageId;return this._imageId=this._imageId>=Be?0:this._imageId+1,e}onDispose(){this._wsSender=null}}class We extends f{constructor(){super(),this._isRunning=!1,this._source=new xe,this._sender=new qe;const e=(t,i,n,r,a=S)=>{Oe(t,{width:i,height:n,format:r,quality:a}).then(c=>{this._sender.send(c.data,{width:c.width,height:c.height,format:c.format,quality:c.quality})})};this._scheduler=new Le(null,e)}setContext(e){super.setContext(e),this._scheduler.setContext(e)}async startCamera(){if(this.ensureNotDisposed(),this._isRunning){s.warn("Camera already running");return}await this._source.start();const e=this._source.getVideoElement();this._scheduler.setVideoElement(e),this._scheduler.start(),this._isRunning=!0,this._context.eventBus.emit("inner:camera:started",void 0),s.info("Camera started")}stopCamera(){this.ensureNotDisposed(),this._isRunning&&(this._scheduler.stop(),this._source.stop(),this._isRunning=!1,this._context.eventBus.emit("inner:camera:stopped",void 0),s.info("Camera stopped"))}isCameraRunning(){return this._isRunning}getStream(){return this.ensureNotDisposed(),this._source.getStream()}getTrack(){return this.ensureNotDisposed(),this._source.getTrack()}attachTo(e){this.ensureNotDisposed();const t=this._source.getStream();t&&(e.srcObject=t,e.play().catch(i=>s.error("attachTo play failed",i)))}onDispose(){this.stopCamera(),this._source.dispose(),this._scheduler.dispose(),this._sender.dispose()}}exports.ConversationStateType=void 0;(function(o){o.IDLE="idle",o.WAITING="waiting",o.STREAMING="streaming",o.COMPLETED="completed",o.ERROR="error"})(exports.ConversationStateType||(exports.ConversationStateType={}));class Ge extends X{constructor(){super(...arguments),this._currentState=exports.ConversationStateType.IDLE,this.transitionTable=[{from:exports.ConversationStateType.IDLE,to:[exports.ConversationStateType.WAITING,exports.ConversationStateType.ERROR]},{from:exports.ConversationStateType.WAITING,to:[exports.ConversationStateType.STREAMING,exports.ConversationStateType.ERROR]},{from:exports.ConversationStateType.STREAMING,to:[exports.ConversationStateType.COMPLETED,exports.ConversationStateType.ERROR]},{from:exports.ConversationStateType.COMPLETED,to:[exports.ConversationStateType.IDLE,exports.ConversationStateType.WAITING,exports.ConversationStateType.ERROR]},{from:exports.ConversationStateType.ERROR,to:[exports.ConversationStateType.IDLE]}]}reset(){this._currentState=exports.ConversationStateType.IDLE}}class K extends u{constructor(e){super(),this._currentQuestion=null,this._currentAnswer=null,this._sessionId=e,this._stateMachine=new Ge,s.info(`Conversation session created: ${e}`)}get sessionId(){return this._sessionId}get state(){return this._stateMachine.getCurrentState()}_setQuestion(e){this._currentQuestion=e,this._stateMachine.transitionTo(exports.ConversationStateType.WAITING),this._syncContextState()}get currentQuestion(){return this._currentQuestion}_startAnswer(e){this._currentAnswer={questionId:e,text:"",isComplete:!1,timestamp:Date.now()},this._stateMachine.transitionTo(exports.ConversationStateType.STREAMING),this._syncContextState()}_appendAnswerChunk(e){this._currentAnswer&&(this._currentAnswer.text+=e)}_completeAnswer(){this._currentAnswer&&(this._currentAnswer.isComplete=!0,this._currentAnswer.completedAt=Date.now()),this._stateMachine.transitionTo(exports.ConversationStateType.COMPLETED),this._syncContextState()}get currentAnswer(){return this._currentAnswer}_setError(e){this._stateMachine.transitionTo(exports.ConversationStateType.ERROR),this._syncContextState()}_syncContextState(){}handleEvent(e){const t=this._stateMachine.getCurrentState();switch(e.type){case"QUESTION":if(t!==exports.ConversationStateType.IDLE&&t!==exports.ConversationStateType.COMPLETED){s.warn(`Ignoring QUESTION event in invalid state: ${t}`);return}this._setQuestion(e.payload);break;case"ANSWER_START":if(t!==exports.ConversationStateType.WAITING){s.warn(`Ignoring ANSWER_START event in invalid state: ${t}. Expected: WAITING`);return}this._startAnswer(e.payload);break;case"ANSWER_CHUNK":if(t!==exports.ConversationStateType.STREAMING){s.warn(`Ignoring ANSWER_CHUNK event in invalid state: ${t}. Expected: STREAMING`);return}this._appendAnswerChunk(e.payload&&e.payload.chunk||""),e.payload&&e.payload.isComplete&&this._completeAnswer();break;case"ANSWER_COMPLETE":if(t!==exports.ConversationStateType.STREAMING){s.warn(`Ignoring ANSWER_COMPLETE event in invalid state: ${t}. Expected: STREAMING`);return}this._completeAnswer();break;case"ERROR":this._setError(e.payload);break;default:s.warn(`Unknown event type: ${e.type}`);break}}reset(){this._stateMachine.reset(),this._currentQuestion=null,this._currentAnswer=null}onDispose(){this.reset(),s.info(`Conversation session disposed: ${this._sessionId}`)}}class $e extends u{constructor(){super(...arguments),this._buffers=new Map}appendChunk(e,t){this.ensureNotDisposed();const n=(this._buffers.get(e)||"")+t;return this._buffers.set(e,n),s.debug(`Message chunk appended for question ${e}, length: ${n.length}`),n}getMessage(e){return this._buffers.get(e)||null}complete(e){const t=this.getMessage(e)||"";return this._buffers.delete(e),s.debug(`Message assembly completed for question ${e}, length: ${t.length}`),t}clear(e){this._buffers.delete(e)}onDispose(){this._buffers.clear(),s.info("Message Assembler disposed")}}class ze{generateSessionId(){return`session_${Date.now()}_${Math.random().toString(36).substring(2,9)}`}generateQuestionId(){return Math.random().toString(36).substring(2,6)}}class je extends f{constructor(e){super(),this._rtcTextSender=null,this._sessions=new Map,this._assembler=new $e,this._idService=new ze,s.info("Conversation Manager created")}setRtcTextSender(e){this._rtcTextSender=e}async sendQuestion(e,t={speed:100,mood:0,vol:50}){this.ensureNotDisposed();const i=this._idService.generateQuestionId(),n={id:i,text:e,timestamp:Date.now(),inputMode:"text"},r=this._idService.generateSessionId(),a=new K(r);return a.handleEvent({type:"QUESTION",payload:n}),this._sessions.set(i,a),await this._dispatchServerTextUplink(i,e,t,a),this._context.eventBus.emit("inner:conversation:question:sent",{questionId:i,text:e}),this._context.eventBus.emit("inner:conversation:answer:waiting",{questionId:i}),s.info(`Question sent: ${i}`),i}async sendSystemEvent(e,t){if(this.ensureNotDisposed(),this._rtcTextSender)try{await this._rtcTextSender.sendSystemEvent(e,t)}catch(i){s.error("Error sending video avaliable state:",i)}}async _dispatchServerTextUplink(e,t,i,n){if(this._rtcTextSender)try{await this._rtcTextSender.sendTextData(e,t,"input.text",i)}catch(r){throw s.error("Error sending question via RTC:",r),n.handleEvent({type:"ERROR",payload:"Failed to send question"}),this._context.eventBus.emit("inner:sdk:error",{error:l.fromError(r,h.LIVEKIT_SEND_TEXT_DATA_FAILED)}),r}else s.warn("RTC text sender not set, question not sent")}handleServerPong(){this.ensureNotDisposed()}willDisconnect(){this.ensureNotDisposed()}handleAnswerChunk(e,t,i){this.ensureNotDisposed();const n=this._sessions.get(e);if(!n){s.warn(`No active session for answer chunk: ${e}`);return}if(n.state===exports.ConversationStateType.WAITING&&n.handleEvent({type:"ANSWER_START",payload:e}),i){n.handleEvent({type:"ANSWER_COMPLETE"});const a=this._assembler.complete(e);this._context.eventBus.emit("inner:conversation:answer:completed",{questionId:e,fullAnswer:a});try{n.dispose()}catch(c){s.error("Error disposing session:",c)}this._sessions.delete(e)}else n.handleEvent({type:"ANSWER_CHUNK",payload:{uid:e,chunk:t,isComplete:i}}),this._assembler.appendChunk(e,t),this._context.eventBus.emit("inner:conversation:answer:chunk",{questionId:e,chunk:t,isComplete:i})}handleServerInitiatedMessage(e,t){this.ensureNotDisposed();const i=e??this._idService.generateQuestionId();this._context.eventBus.emit("inner:conversation:server:message",{questionId:i,message:t,type:"text"})}handleAsrTextReceived(e,t,i,n){this.ensureNotDisposed();const r=this._idService.generateSessionId(),a=new K(r);a.handleEvent({type:"QUESTION",payload:{id:e,text:t,timestamp:Date.now(),inputMode:"voice"}}),this._sessions.set(e,a),i?this._context.eventBus.emit("inner:conversation:asr:chunk",{questionId:e,text:t,isComplete:n??!1}):this._context.eventBus.emit("inner:conversation:asr:received",{questionId:e,text:t})}handleServerCommand(e,t){this.ensureNotDisposed(),this._context.eventBus.emit("inner:conversation:server:command",{code:e,command:t})}onDispose(){for(const e of this._sessions.values())try{e.dispose()}catch(t){s.error("Error disposing session:",t)}this._sessions.clear(),this._assembler.dispose(),s.info("Conversation Manager disposed")}}class Qe extends f{constructor(e){super(),this._eventUnsubscribers=[],this._manager=new je(e),s.info("Conversation Controller created")}setContext(e){super.setContext(e),this._manager.setContext(e),this._bindEventBus()}get manager(){return this._manager}setRtcTextSender(e){this._manager.setRtcTextSender(e)}async sendQuestion(e,t){return this.ensureNotDisposed(),this._manager.sendQuestion(e,t)}async interrupt(){return this.ensureNotDisposed(),this._manager.sendSystemEvent("control.interrupt")}_clearEventListeners(){this._eventUnsubscribers.forEach(e=>{try{e()}catch(t){s.error("Performance monitor unsubscribe failed",t)}}),this._eventUnsubscribers=[]}_bindEventBus(){this._eventUnsubscribers.push(this._context.eventBus.on("inner:rtc:video:available",()=>{this.sendVideoAvaliableState()}))}async sendVideoAvaliableState(){return this.ensureNotDisposed(),this._manager.sendSystemEvent("scene.ready")}onDispose(){this._clearEventListeners(),this._manager.dispose(),s.info("Conversation Controller disposed")}}class Xe extends u{constructor(){super(...arguments),this._tracks=new Map,this._participants=new Map,this._trackEndedHandlers=new Map}register(e,t,i){this.ensureNotDisposed(),this._tracks.set(e,t),this._participants.has(i)||this._participants.set(i,new Set),this._participants.get(i).add(e);const n=()=>{s.debug(`Track ended: ${e}`),this.unregister(e)};this._trackEndedHandlers.set(e,n),t.on("ended",n),s.debug(`Track registered: ${e} from participant ${i}`)}unregister(e){const t=this._tracks.get(e);if(t){const i=this._trackEndedHandlers.get(e);i&&(t.off("ended",i),this._trackEndedHandlers.delete(e)),t.stop(),this._tracks.delete(e);for(const[n,r]of this._participants.entries())if(r.has(e)){r.delete(e),r.size===0&&this._participants.delete(n);break}s.debug(`Track unregistered: ${e}`)}}getTrack(e){return this._tracks.get(e)||null}getParticipantTracks(e){const t=this._participants.get(e);return t?Array.from(t):[]}getAllTrackIds(){return Array.from(this._tracks.keys())}getVideoAvailability(){let e=!1,t=!1,i=!1;for(const n of this._tracks.values()){const r=n.mediaStreamTrack;if(r.kind==="video"&&(e=!0,r.readyState==="live"&&(t=!0),r.enabled&&(i=!0),r.readyState==="live"&&r.enabled))return{available:!0}}return e?t?i?{available:!1,reason:E.NO_VIDEO_TRACK}:{available:!1,reason:E.TRACK_MUTED}:{available:!1,reason:E.TRACK_ENDED}:{available:!1,reason:E.NO_VIDEO_TRACK}}clear(){const e=Array.from(this._tracks.keys());for(const t of e)this.unregister(t);s.info("All tracks cleared")}onDispose(){this.clear()}}class H extends f{constructor(e){super(),this._videoController=null,this._room=null,this._trackRegistry=e,this._loosTrackTimer=null,s.info("Video Track Handler created")}setRoom(e){this._room=e}setVideoController(e){this._videoController=e}handleTrackSubscribed(e,t,i){this.ensureNotDisposed(),this._loosTrackTimer&&clearTimeout(this._loosTrackTimer),this._trackRegistry.register(t,e,i),this._videoController&&e.attach(this._videoController.getSource().getInternalElement()),e.on("videoPlaybackStarted",()=>{s.info("inner:rtc:video:available"),this._context.eventBus.emit("inner:rtc:video:available",void 0)}),this._context.eventBus.emit("inner:video:track:added",{trackId:t,participantId:i}),s.debug(`Video track subscribed: ${t}`)}handleTrackUnsubscribed(e,t,i){this.ensureNotDisposed(),this._videoController&&e.detach(this._videoController.getSource().getInternalElement()),e.removeAllListeners(),this._context.eventBus.emit("inner:video:track:removed",{trackId:t,participantId:i}),this._loosTrackTimer&&clearTimeout(this._loosTrackTimer),this._loosTrackTimer=setTimeout(()=>{this._context.eventBus.emit("inner:rtc:video:unavailable",void 0)},re),this._trackRegistry.unregister(t),s.debug(`Video track unsubscribed: ${t}`)}supports(e){return e.mediaStreamTrack.kind==="video"}onDispose(){this._loosTrackTimer&&(clearTimeout(this._loosTrackTimer),this._loosTrackTimer=null),s.info("Video Track Handler disposed")}}class B extends f{constructor(e){super(),this._audioController=null,this._room=null,this._trackRegistry=e,s.info("Audio Track Handler created")}setRoom(e){this._room=e}setAudioController(e){this._audioController=e}handleTrackSubscribed(e,t,i){this.ensureNotDisposed(),this._trackRegistry.register(t,e,i),this._audioController?e.attach(this._audioController.getOutputElement()):this._context.eventBus.emit("inner:audio:track:added",{trackId:t,participantId:i}),s.debug(`Audio track subscribed: ${t}`)}handleTrackUnsubscribed(e,t,i){this.ensureNotDisposed(),this._audioController?(this._audioController.removeOutputTrack(t),e.detach(this._audioController.getOutputElement())):this._context.eventBus.emit("inner:audio:track:removed",{trackId:t}),this._trackRegistry.unregister(t),s.debug(`Audio track unsubscribed: ${t}`)}supports(e){return e.mediaStreamTrack.kind==="audio"}onDispose(){s.info("Audio Track Handler disposed")}}class Ye extends f{constructor(e){super(),this._handlers=[],this._room=null,this._trackRegistry=e,s.info("LiveKit Event Adapter created")}setContext(e){if(super.setContext(e),this._handlers.length===0){const t=new H(this._trackRegistry),i=new B(this._trackRegistry);t.setContext(e),i.setContext(e),this._handlers.push(t,i),this._room&&(t.setRoom(this._room),i.setRoom(this._room))}}setRoom(e){this._room=e,this._handlers.forEach(t=>{t.setRoom(e)}),s.info("LiveKit room set:",this._room)}setVideoController(e){const t=this._handlers.find(i=>i instanceof H);t&&t.setVideoController(e)}setAudioController(e){const t=this._handlers.find(i=>i instanceof B);t&&t.setAudioController(e)}handleTrackSubscribed(e,t,i){this.ensureNotDisposed();const n=this._findHandler(e);n?n.handleTrackSubscribed(e,t,i):s.warn(`No handler found for track, trackId: ${t}`)}handleTrackUnsubscribed(e,t){this.ensureNotDisposed();const i=this._trackRegistry.getTrack(e);if(i){const n=this._findHandler(i);n?n.handleTrackUnsubscribed(i,e,t):(this._trackRegistry.unregister(e),s.warn(`No handler found for track, trackId: ${e}`))}}_findHandler(e){return this._handlers.find(t=>t.supports(e))||null}onDispose(){this._handlers.forEach(e=>{try{e.dispose()}catch(t){s.error("Error disposing track handler:",t)}}),this._handlers.length=0,s.info("LiveKit Event Adapter disposed")}}class Je extends f{constructor(){super(),this._room=null,this._rtcRoom=null,this._micTrack=null,this._cameraVideoTrack=null,this._protocolDispatch=null,this._maxAttempts=y,this._maxDelay=w*I,this._speakingHandlers=new Map,this._trackRegistry=new Xe,this._eventAdapter=new Ye(this._trackRegistry),s.info("LiveKit Service created")}setContext(e){super.setContext(e),this._maxAttempts=e.options.reconnect?.maxAttempts||y,this._maxDelay=(e.options.reconnect?.delay??w)*I,this._eventAdapter.setContext(e)}async connect(e,t,i,n){if(this.ensureNotDisposed(),this._rtcRoom&&this._rtcRoom.state===p.ConnectionState.Connected){s.warn("Already connected to LiveKit room");return}let r;try{await this._tearDownStaleRoom(),s.info(`Connecting to LiveKit room: ${i} at ${e}`),r=new p.Room({reconnectPolicy:{nextRetryDelayInMs:a=>a.elapsedMs<this._maxDelay?Math.min(I*Math.pow(2,a.retryCount),this._maxDelay):null}}),r.prepareConnection(e,t),this._registerRoomListeners(r),s.info("Connecting to LiveKit room..."),await r.connect(e,t,{autoSubscribe:!0,maxRetries:this._context.options.reconnect?.maxAttempts||y}),this._eventAdapter.setRoom(r),this._rtcRoom=r,this._room=r}catch(a){if(r){try{r.removeAllListeners()}catch{}try{await r.disconnect()}catch{}}throw this._rtcRoom=null,this._room=null,l.fromError(a,h.LIVEKIT_CONNECT_FAILED)}}_registerRoomListeners(e){e.on(p.RoomEvent.TrackSubscribed,(t,i,n)=>{this._handleTrackSubscribed(t,i,n)}),e.on(p.RoomEvent.TrackUnsubscribed,(t,i,n)=>{this._handleTrackUnsubscribed(t,i,n)}),e.on(p.RoomEvent.TrackMuted,(t,i)=>{this._handleTrackMuted(t,i)}),e.on(p.RoomEvent.TrackUnmuted,(t,i)=>{this._handleTrackUnmuted(t,i)}),e.on(p.RoomEvent.Connected,()=>{s.info("Connected to LiveKit room"),this._context.eventBus.emit("inner:rtc:connected",void 0)}),e.on(p.RoomEvent.Disconnected,t=>{t?s.info(`LiveKit room disconnected: ${t}`):s.info("LiveKit room disconnected"),t!==p.DisconnectReason.CLIENT_INITIATED&&(this._context.eventBus.emit("inner:rtc:disconnected",{reason:t?String(t):void 0}),this._context.emitError(new l(h.LIVEKIT_CONNECT_FAILED,t?`LiveKit disconnected: ${String(t)}`:"LiveKit disconnected"),h.LIVEKIT_CONNECT_FAILED)),this._micTrack?.stop(),this._micTrack=null,this._cameraVideoTrack?.stop(),this._cameraVideoTrack=null,this._clearSpeakingHandlers(),this._context.mediaAudioCaptureState.end("rtc"),this._room=null,this._rtcRoom=null}),e.on(p.RoomEvent.DataReceived,t=>{this._onRoomDataReceived(t)})}async _tearDownStaleRoom(){if(!this._rtcRoom)return;await this.stopMicrophone(),await this.stopCamera(),this._clearSpeakingHandlers();const e=this._rtcRoom;this._rtcRoom=null,this._room=null;try{e.removeAllListeners()}catch{}try{await e.disconnect()}catch{}}_clearSpeakingHandlers(){if(!this._rtcRoom){this._speakingHandlers.clear();return}for(const[e,t]of this._speakingHandlers.entries()){const i=this._findRemoteParticipantByIdentity(e);try{i?.off(p.ParticipantEvent.IsSpeakingChanged,t)}catch{}}this._speakingHandlers.clear()}_findRemoteParticipantByIdentity(e){const t=this._rtcRoom;if(t){for(const i of t.remoteParticipants.values())if(i.identity===e)return i}}_removeSpeakingListenerIfNoAudioTracks(e){const t=this._speakingHandlers.get(e);if(!t)return;const i=this._trackRegistry.getParticipantTracks(e);for(const r of i)if(this._trackRegistry.getTrack(r)?.kind==="audio")return;const n=this._findRemoteParticipantByIdentity(e);try{n?.off(p.ParticipantEvent.IsSpeakingChanged,t)}catch{}this._speakingHandlers.delete(e)}async startMicrophone(){this.ensureNotDisposed();const e=this._rtcRoom;if(!e||!this.isConnected())throw new l(h.LIVEKIT_CONNECT_FAILED,"LiveKit is not connected");if(!this._context.mediaAudioCaptureState.tryBegin("rtc"))throw new l(h.SDK_INVALID_STATE_TRANSITION,"WebSocket audio capture is active");try{if(this._micTrack){s.warn("Microphone already published");return}const t=await p.createLocalAudioTrack(this._context.options.audio?.input??{});await e.localParticipant.publishTrack(t),this._micTrack=t,s.info("Microphone published (rtc)")}catch(t){throw this._context.mediaAudioCaptureState.end("rtc"),this._context.emitError(t,h.AUDIO_CAPTURE_START_FAILED)}}async stopMicrophone(){const e=this._rtcRoom,t=this._micTrack;if(t&&e)try{await e.localParticipant.unpublishTrack(t,!0)}catch(i){throw this._context.emitError(i,h.LIVEKIT_UNPUBLISH_MICROPHONE_FAILED)}this._micTrack?.stop(),this._micTrack=null,this._context.mediaAudioCaptureState.end("rtc"),s.info("Microphone stopped (rtc)")}isMicrophoneActive(){return!!this._micTrack}async sendTextData(e,t,i,n){this.ensureNotDisposed();const r=this._rtcRoom;if(!r||!this.isConnected())throw new l(h.LIVEKIT_CONNECT_FAILED,"LiveKit is not connected");try{const a={event:i,requestId:e,data:{...n,text:t}},c=new TextEncoder().encode(JSON.stringify(a));await r.localParticipant.publishData(c,{reliable:!0}),s.debug("Text message sent via RTC data:",e)}catch(a){throw this._context.emitError(a,h.LIVEKIT_SEND_TEXT_DATA_FAILED)}}async sendSystemEvent(e,t){this.ensureNotDisposed();const i=this._rtcRoom;if(!i||!this.isConnected())throw new l(h.LIVEKIT_CONNECT_FAILED,"LiveKit is not connected");try{const n={event:e,data:t??{}},r=new TextEncoder().encode(JSON.stringify(n));await i.localParticipant.publishData(r,{reliable:!0}),s.debug("System event :",e,"sent via RTC data")}catch(n){throw this._context.emitError(n,h.LIVEKIT_SEND_TEXT_DATA_FAILED)}}setProtocolMessageDispatcher(e){this._protocolDispatch=e}async startCamera(){this.ensureNotDisposed();const e=this._rtcRoom;if(!e||!this.isConnected())throw new l(h.LIVEKIT_CONNECT_FAILED,"LiveKit is not connected");if(this._cameraVideoTrack){s.warn("RTC camera already published");return}try{const t=await p.createLocalVideoTrack({});await e.localParticipant.publishTrack(t),this._cameraVideoTrack=t,s.info("Camera published (rtc)")}catch(t){throw this._context.emitError(t,h.LIVEKIT_CONNECT_FAILED)}}async stopCamera(){const e=this._rtcRoom,t=this._cameraVideoTrack;if(t&&e)try{await e.localParticipant.unpublishTrack(t,!0)}catch(i){s.error("Error unpublishing RTC camera:",i)}this._cameraVideoTrack?.stop(),this._cameraVideoTrack=null,s.info("Camera stopped (rtc)")}attachCameraTo(e){if(this.ensureNotDisposed(),!this._cameraVideoTrack)throw new l(h.LIVEKIT_CONNECT_FAILED,"Camera track is not published");this._cameraVideoTrack.attach(e)}detachCameraFrom(e){if(this.ensureNotDisposed(),!this._cameraVideoTrack)throw new l(h.LIVEKIT_CONNECT_FAILED,"Camera track is not published");this._cameraVideoTrack.detach(e)}isRtcCameraActive(){return this._cameraVideoTrack!==null}_onRoomDataReceived(e){if(!(!this._protocolDispatch||e.length===0))try{const t=new TextDecoder().decode(e),i=JSON.parse(t);if(!i||typeof i!="object")return;s.debug("RTC data message received:",t),this._protocolDispatch(i)}catch(t){s.error("RTC data message parse error:",t),this._context.emitError(t,h.LIVEKIT_DATA_MESSAGE_PARSE_ERROR)}}_handleTrackSubscribed(e,t,i){try{if(!e.mediaStreamTrack){s.warn("Track does not have MediaStreamTrack");return}if(e.kind==="audio"){const c=i.identity;if(!this._speakingHandlers.has(c)){const d=_=>{this._context.eventBus.emit("inner:audio:speaking:changed",{participantId:c,isSpeaking:_}),s.info(`Participant ${c} is speaking: ${_}`)};i.on(p.ParticipantEvent.IsSpeakingChanged,d),this._speakingHandlers.set(c,d)}}const r=t.trackSid,a=i.identity;this._eventAdapter.handleTrackSubscribed(e,r,a)}catch(n){s.error("Error handling track subscribed:",n)}}_handleTrackUnsubscribed(e,t,i){try{const n=t,r=i,a=n.trackSid,c=r.identity,d=this._trackRegistry.getTrack(a),_=d?.kind==="audio";d?.kind==="video"&&this._context.eventBus.emit("inner:rtc:video:unavailable",void 0),this._eventAdapter.handleTrackUnsubscribed(a,c),_&&this._removeSpeakingListenerIfNoAudioTracks(c)}catch(n){s.error("Error handling track unsubscribed:",n)}}_handleTrackMuted(e,t){try{e.kind==="video"&&this._context.eventBus.emit("inner:rtc:video:unavailable",void 0)}catch(i){s.error("Error handling track muted:",i)}}_handleTrackUnmuted(e,t){try{e.kind==="video"&&this._context.eventBus.emit("inner:rtc:video:available",void 0)}catch(i){s.error("Error handling track unmuted:",i)}}async disconnect(){await this.stopMicrophone(),await this.stopCamera(),this._clearSpeakingHandlers();const e=this._rtcRoom;if(e)try{try{e.removeAllListeners()}catch{}await e.disconnect(),this._room=null,this._rtcRoom=null,this._context.eventBus.emit("inner:rtc:disconnected",{reason:"Disconnected from LiveKit room"}),s.info("Disconnected from LiveKit room")}catch(t){s.error("Error disconnecting from LiveKit room:",t),this._room=null,this._rtcRoom=null}else this._room=null}isConnected(){return this._room?this._room.state===p.ConnectionState.Connected:!1}getTrackRegistry(){return this._trackRegistry}get trackRegistry(){return this._trackRegistry}getEventAdapter(){return this._eventAdapter}onDispose(){this._protocolDispatch=null,this.disconnect(),this._trackRegistry.dispose(),this._eventAdapter.dispose(),s.info("LiveKit Service disposed")}}class Ze extends f{constructor(){super(),this._conversationManager=null,s.info("ProtocolMessageDispatcher created")}setConversationManager(e){this._conversationManager=e,s.debug("Conversation manager set in ProtocolMessageDispatcher")}dispatch(e){this.ensureNotDisposed();try{if(e.event){this._handleLivekitServerResponseMessage(e);return}}catch(t){s.error("Error dispatching protocol message:",t)}}_handleLivekitServerResponseMessage(e){switch(e.event){case"response.chunk":this._conversationManager?.handleAnswerChunk(e.requestId,e.data.text,!1);break;case"response.done":this._conversationManager?.handleAnswerChunk(e.requestId,"",!0);break;case"input.asr.partial":this._conversationManager?.handleAsrTextReceived(e.requestId,e.data.text,!0,e.data.final??!1);break;case"input.asr.final":this._conversationManager?.handleAsrTextReceived(e.requestId,e.data.text,!1,!0);break;case"system.prompt":this._conversationManager?.handleServerInitiatedMessage(void 0,e.data.text);break;case"session.closing":break;case"input.voice.start":this._context.eventBus.emit("inner:conversation:voice:start",{requestId:e.requestId});break;case"input.voice.finish":this._context.eventBus.emit("inner:conversation:voice:finish",{requestId:e.requestId});break;default:s.warn("Unknown event:",e.event)}}onDispose(){this._conversationManager=null,s.info("ProtocolMessageDispatcher disposed")}}const R=["sdk:connected","sdk:disconnected","sdk:error","media:video:available","media:video:unavailable","media:video:trackAdded","media:video:trackRemoved","media:audio:trackAdded","media:audio:trackRemoved","media:audio:captureStarted","media:audio:captureStopped","media:camera:started","media:camera:stopped","media:audio:volumeChanged","media:audio:muted","media:audio:unmuted","conversation:question:sent","conversation:answer:waiting","conversation:server:message","conversation:asr:received","conversation:asr:chunk","conversation:answer:chunk","conversation:answer:completed"],q=/^[a-z]+:[a-zA-z]+(:[a-zA-Z]+)?$/;class et{constructor(e){this.inner=e,this.listeners=new Map,this.forwardedVideoTrackIds=new Set,this.livekitConnected=!1,this.httpConnected=!1,this.internalMap={"inner:sdk:error":{public:"sdk:error",sanitizer:t=>({message:t?.error?.message,code:t?.error?.code})},"inner:rtc:video:available":{public:"media:video:available"},"inner:rtc:video:unavailable":{public:"media:video:unavailable"},"inner:video:track:added":{public:"media:video:trackAdded"},"inner:video:track:removed":{public:"media:video:trackRemoved"},"inner:audio:track:added":{public:"media:audio:trackAdded"},"inner:audio:track:removed":{public:"media:audio:trackRemoved"},"inner:audio:input:started":{public:"media:audio:captureStarted"},"inner:audio:input:stopped":{public:"media:audio:captureStopped"},"inner:camera:started":{public:"media:camera:started"},"inner:camera:stopped":{public:"media:camera:stopped"},"inner:audio:volume:changed":{public:"media:audio:volumeChanged",sanitizer:t=>({volume:t?.volume})},"inner:audio:muted":{public:"media:audio:muted"},"inner:audio:unmuted":{public:"media:audio:unmuted"},"inner:conversation:question:sent":{public:"conversation:question:sent",sanitizer:t=>({questionId:t?.questionId,text:String(t?.text||"")})},"inner:conversation:answer:waiting":{public:"conversation:answer:waiting",sanitizer:t=>({questionId:t?.questionId})},"inner:conversation:server:message":{public:"conversation:server:message",sanitizer:t=>({questionId:t?.questionId,message:String(t?.message||""),type:String(t?.type||"")})},"inner:conversation:asr:received":{public:"conversation:asr:received",sanitizer:t=>({questionId:t?.questionId,text:String(t?.text||"")})},"inner:conversation:asr:chunk":{public:"conversation:asr:chunk",sanitizer:t=>({questionId:t?.questionId,text:String(t?.text||""),isComplete:t?.isComplete??!1})},"inner:conversation:answer:chunk":{public:"conversation:answer:chunk",sanitizer:t=>({questionId:t?.questionId,chunk:String(t?.chunk||"")})},"inner:conversation:answer:completed":{public:"conversation:answer:completed",sanitizer:t=>({questionId:t?.questionId,fullAnswer:String(t?.fullAnswer||"")})}},Object.keys(this.internalMap).forEach(t=>{this.inner.on(t,i=>this.handleInternalEvent(t,i))}),this.inner.on("inner:sdk:connected",t=>this.handleConnected(t,!0)),this.inner.on("inner:sdk:disconnected",t=>this.handleConnected(t,!1)),R.forEach(t=>{this.inner.on(t,i=>this.handlePublicEvent(t,i))})}publicAPI(){return{on:this.on.bind(this),off:this.off.bind(this),once:this.once.bind(this)}}on(e,t){const i=String(e);return this.assertAllowed(i),this.listeners.has(i)||this.listeners.set(i,new Set),this.listeners.get(i).add(t),()=>this.off(e,t)}once(e,t){const i=String(e);this.assertAllowed(i);const n=r=>{try{t(r)}finally{this.off(e,n)}};return this.on(e,n)}off(e,t){const i=String(e);this.assertAllowed(i);const n=this.listeners.get(i);n&&(n.delete(t),n.size===0&&this.listeners.delete(i))}handleInternalEvent(e,t){const i=this.internalMap[e];if(!i)return;const n=i.public;if(R.indexOf(n)===-1)return;if(n==="media:video:trackAdded"){const c=t?.trackId||t?.id;if(typeof c=="string"){if(this.forwardedVideoTrackIds.has(c))return;this.forwardedVideoTrackIds.add(c)}}const r=i.sanitizer?i.sanitizer(t):t;if(!q.test(n))return;const a=this.listeners.get(n);!a||a.size===0||a.forEach(c=>{try{c(r)}catch(d){s.error("PublicEventEmitter listener error",d)}})}handlePublicEvent(e,t){if(R.indexOf(e)===-1)return;const i=Object.values(this.internalMap).find(a=>a.public===e),n=i&&i.sanitizer?i.sanitizer(t):t;if(e==="media:video:trackAdded"){const a=t?.trackId||t?.id;if(typeof a=="string"){if(this.forwardedVideoTrackIds.has(a))return;this.forwardedVideoTrackIds.add(a)}}const r=this.listeners.get(e);!r||r.size===0||r.forEach(a=>{try{a(n)}catch(c){s.error("PublicEventEmitter listener error",c)}})}handleConnected(e,t){e.source==="livekit"?this.livekitConnected=t:e.source==="http"&&(this.httpConnected=t),this.maybeEmitSdkConnected(t?"connected":"disconnected")}maybeEmitSdkConnected(e){const t=e==="connected",i=t?!!this.livekitConnected:!this.livekitConnected,n=t?!!this.httpConnected:!this.httpConnected,a={livekit:i,http:n,all:i&&n},c=JSON.stringify(a);this.lastConnectedPayloadJson!==c&&(this.lastConnectedPayloadJson=c,this.inner.emit(`sdk:${e}`,a))}assertAllowed(e){if(R.indexOf(e)===-1)throw new Error(`Event "${e}" is not allowed for public consumers`);if(!q.test(e))throw new Error(`Event name "${e}" does not follow namespace:domain[:action] naming`)}}class tt extends u{constructor(e,t){super(),this._requestCounter=0,this._baseURL=e||"",this._defaultHeaders=t||{},s.info(`HTTP Service created with baseURL: ${this._baseURL||"none"}`)}setAuthToken(e){this._defaultHeaders.Authorization=`Bearer ${e}`}request(e){this.ensureNotDisposed();const t=e.requestId||`req_${Date.now()}_${++this._requestCounter}`,i=this._buildURL(e.url,e.params),n=this._mergeHeaders(e.headers),r={method:e.method||"GET",headers:n};e.data!==void 0&&(e.method||"GET")!=="GET"&&(typeof e.data=="string"?r.body=e.data:(r.body=JSON.stringify(e.data),!n["Content-Type"]&&!n["content-type"]&&(n["Content-Type"]="application/json")));const a=e.timeout||3e4,c=new AbortController;return r.signal=c.signal,new Promise((d,_)=>{const g=setTimeout(()=>c.abort(),a);fetch(i,r).then(async m=>{clearTimeout(g);const A={};m.headers.forEach((ie,se)=>{A[se]=ie});let C;const L=m.headers.get("content-type");L&&L.includes("application/json")?C=await m.json():C=await m.text();const te={requestId:t,status:m.status,headers:A,data:C,config:e};s.debug(`HTTP request succeeded: ${e.method} ${i} [${t}]`),d(te)}).catch(m=>{clearTimeout(g);const A=m&&m.name==="AbortError",C={requestId:t,status:m&&m.status||void 0,message:A?"Request aborted (timeout)":m&&m.message||"HTTP request failed",error:m,config:e};s.error(`HTTP request failed: ${e.method} ${i} [${t}]`,m),_(C)})})}get(e,t,i){return this.request({...i,url:e,method:"GET",params:t})}post(e,t,i){return this.request({...i,url:e,method:"POST",data:t})}put(e,t,i){return this.request({...i,url:e,method:"PUT",data:t})}delete(e,t){return this.request({...t,url:e,method:"DELETE"})}patch(e,t,i){return this.request({...i,url:e,method:"PATCH",data:t})}_buildURL(e,t){if(e.startsWith("http://")||e.startsWith("https://"))return this._appendParams(e,t);let i=this._baseURL;return i&&!i.endsWith("/")&&!e.startsWith("/")?i+="/":i&&i.endsWith("/")&&e.startsWith("/")&&(i=i.slice(0,-1)),i+=e,this._appendParams(i,t)}_appendParams(e,t){if(!t||Object.keys(t).length===0)return e;const i=new URLSearchParams;for(const[r,a]of Object.entries(t))i.append(r,String(a));const n=e.includes("?")?"&":"?";return`${e}${n}${i.toString()}`}_mergeHeaders(e){const t={...this._defaultHeaders};return e&&Object.assign(t,e),t}onDispose(){s.info("HTTP Service disposed")}}function it(o,e="https"){return o.includes("://")?o:`${e.replace(/:$/,"")}://${o}`}class st extends f{constructor(e,t){super(),this._httpService=new tt(e||de,t)}getAuthToken(){if(this.ensureNotDisposed(),this._context.authToken)return this._httpService?.setAuthToken(this._context.authToken),Promise.resolve();throw this._context.emitError("Auth token not found",h.SDK_AUTH_TOKEN_FAILED)}async fetchConnectionConfig(){this.ensureNotDisposed();const e=await this._httpService.request({url:"/v1/session/start",method:"POST",data:{avatarId:this._context.options.connectConfig.type==="auth"?this._context.options.connectConfig.config.avatarId:"",voice:this._context.options.connectConfig.type==="auth"&&this._context.options.connectConfig.config.avatarVoice||""},headers:this._context.options.sandbox?{"X-Env-Sandbox":"true"}:void 0}).catch(t=>{throw this._context.emitError(t,h.SDK_GET_LIVEKIT_CONFIG_FAILED)});if(e.data.code!==he||!e.data.data)throw this._context.emitError(new l(h.SDK_GET_LIVEKIT_CONFIG_FAILED,e.data.msg||"LiveKit config response invalid"),h.SDK_GET_LIVEKIT_CONFIG_FAILED);return{roomId:"",roomToken:e.data.data.userToken,livekitUrl:it(e.data.data.sfuUrl,"wss")}}onDispose(){this._httpService.dispose()}}class W extends f{constructor(e,t){super(),this._connectStartAt=null,this._hasReportedFirstFrame=!1,this._videoElement=null,this._videoFirstFrameHandler=null,this._pendingTextResponse=new Map,this._pendingTextAudioQueue=[],this._pendingTextAudioStartAt=new Map,this._pendingNoSpeechStartAt=null,this._eventUnsubscribers=[],this._reporter=e??null,this._now=t??(()=>Date.now())}setReporter(e){this._reporter=e??null}startConnectMeasurement(){this._connectStartAt=this._now(),this._hasReportedFirstFrame=!1}bindVideoElement(e){this._unbindVideoElement(),this._videoElement=e,this._videoFirstFrameHandler=this._handleVideoFirstFrame.bind(this),this._videoElement.addEventListener("loadeddata",this._videoFirstFrameHandler,{once:!1})}setContext(e){super.setContext(e),this._bindEventBus()}_bindEventBus(){this._clearEventListeners(),this._subscribeConversationMetrics(),this._subscribeAudioMetrics()}_subscribeConversationMetrics(){this._eventUnsubscribers.push(this._context.eventBus.on("inner:conversation:question:sent",({questionId:e})=>{const t=this._now();this._pendingTextResponse.set(e,t),this._pendingTextAudioQueue.push(e),this._pendingTextAudioStartAt.set(e,t)})),this._eventUnsubscribers.push(this._context.eventBus.on("inner:conversation:answer:chunk",({questionId:e})=>{this._tryCompleteTextResponse(e)})),this._eventUnsubscribers.push(this._context.eventBus.on("inner:conversation:answer:completed",({questionId:e})=>{this._tryCompleteTextResponse(e)})),this._eventUnsubscribers.push(this._context.eventBus.on("inner:conversation:server:message",({questionId:e})=>{this._tryCompleteTextResponse(e)}))}_subscribeAudioMetrics(){this._eventUnsubscribers.push(this._context.eventBus.on("inner:audio:speaking:changed",({isSpeaking:e})=>{e&&(this._tryCompleteTextToAudio(),this._tryCompleteNoSpeechToAudio())})),this._eventUnsubscribers.push(this._context.eventBus.on("inner:audio:no_speech:reported",({timestamp:e})=>{this._pendingNoSpeechStartAt===null&&(this._pendingNoSpeechStartAt=e)}))}_handleVideoFirstFrame(){this._hasReportedFirstFrame||this._connectStartAt===null||(this._hasReportedFirstFrame=!0,this._report({metric:"connect_to_first_frame_ms",durationMs:this._now()-this._connectStartAt,startedAt:this._connectStartAt,endedAt:this._now()}))}_tryCompleteTextResponse(e){const t=this._pendingTextResponse.get(e);if(t===void 0)return;this._pendingTextResponse.delete(e);const i=this._now();this._report({metric:"text_send_to_text_response_ms",durationMs:i-t,startedAt:t,endedAt:i,questionId:e})}_tryCompleteTextToAudio(){let e;for(;this._pendingTextAudioQueue.length>0;){const n=this._pendingTextAudioQueue.shift();if(n&&this._pendingTextAudioStartAt.has(n)){e=n;break}}if(!e)return;const t=this._pendingTextAudioStartAt.get(e);if(t===void 0)return;this._pendingTextAudioStartAt.delete(e);const i=this._now();this._report({metric:"text_send_to_audio_response_ms",durationMs:i-t,startedAt:t,endedAt:i,questionId:e})}_tryCompleteNoSpeechToAudio(){if(this._pendingNoSpeechStartAt===null)return;const e=this._pendingNoSpeechStartAt;this._pendingNoSpeechStartAt=null;const t=this._now();this._report({metric:"no_speech_report_to_audio_response_ms",durationMs:t-e,startedAt:e,endedAt:t})}_report(e){const t=this._reporter??this._defaultReporter;try{t(e)}catch(i){s.error("Performance metric reporter failed",i)}}_defaultReporter(e){s.info("[performance-metric]",e)}_unbindVideoElement(){this._videoElement&&this._videoFirstFrameHandler&&this._videoElement.removeEventListener("loadeddata",this._videoFirstFrameHandler),this._videoElement=null,this._videoFirstFrameHandler=null}_clearEventListeners(){this._eventUnsubscribers.forEach(e=>{try{e()}catch(t){s.error("Performance monitor unsubscribe failed",t)}}),this._eventUnsubscribers=[]}onDispose(){this._unbindVideoElement(),this._clearEventListeners(),this._pendingTextResponse.clear(),this._pendingTextAudioQueue.length=0,this._pendingTextAudioStartAt.clear(),this._pendingNoSpeechStartAt=null,this._reporter=null}}class v extends f{constructor(e,t=ue){super(),this._cachedConfig=null,this._cachedAt=null,this._inflightPromise=null,this._nextDirectConfig=null,this._httpController=e,this._ttlMs=t}async preConnect(){this.ensureNotDisposed();const e=this.getValidConfig();return e||(this._inflightPromise?this._inflightPromise:(this._inflightPromise=this._fetchConfig().finally(()=>{this._inflightPromise=null}),this._inflightPromise))}getValidConfig(){return this.ensureNotDisposed(),!this._cachedConfig||this._cachedAt===null||Date.now()-this._cachedAt>=this._ttlMs?null:this._cachedConfig}async refreshConfig(){return this.ensureNotDisposed(),this._cachedConfig=null,this._cachedAt=null,this._inflightPromise?this._inflightPromise:(this._inflightPromise=this._fetchConfig().finally(()=>{this._inflightPromise=null}),this._inflightPromise)}replaceDirectConfig(e){this.ensureNotDisposed();const t=this._validateDirectConfig(e);this._nextDirectConfig=t,this._cachedConfig=null,this._cachedAt=null}async _fetchConfig(){const e=this._context.options.connectConfig;if(e.type==="direct"){const t=this._nextDirectConfig??this._validateDirectConfig(e.config),i={livekitUrl:t.sfuUrl,token:t.clientToken};return this._cache(i),this._nextDirectConfig=null,i}return this._fetchAuthConfigWithRetry(e.config.authToken||"")}_fromAuthPayload(e){if(!e.livekitUrl||!e.roomToken)throw this._context.emitError("Invalid connection config payload",h.SDK_GET_LIVEKIT_CONFIG_FAILED);return{livekitUrl:e.livekitUrl,token:e.roomToken,roomId:e.roomId,videoOptions:{renderMode:e.greenScreen?.enabled?"processed":"raw",greenScreen:{enabled:!!e.greenScreen?.enabled,...e.greenScreen}}}}_cache(e){this._cachedConfig=e,this._cachedAt=Date.now()}async _fetchAuthConfigWithRetry(e){let t=null;for(let i=1;i<=v.AUTH_RETRY_MAX_ATTEMPTS;i++)try{const n=this._context.authToken||e;this._context.setAuthToken(n),await this._httpController.getAuthToken();const r=await this._httpController.fetchConnectionConfig(),a=this._fromAuthPayload(r);return this._cache(a),a}catch(n){if(t=n,i>=v.AUTH_RETRY_MAX_ATTEMPTS)break;await this._delay(v.AUTH_RETRY_BASE_DELAY_MS*i)}throw this._context.emitError(t||"Failed to fetch auth connection config",h.SDK_GET_LIVEKIT_CONFIG_FAILED)}_delay(e){return new Promise(t=>setTimeout(t,e))}_validateDirectConfig(e){const t=(e.sfuUrl||"").trim(),i=(e.clientToken||"").trim();if(!t||!i)throw this._context.emitError("Invalid direct connection config",h.SDK_PRECONNECT_FAILED);return{sfuUrl:t,clientToken:i}}onDispose(){this._cachedConfig=null,this._cachedAt=null,this._inflightPromise=null,this._nextDirectConfig=null}}v.AUTH_RETRY_MAX_ATTEMPTS=2;v.AUTH_RETRY_BASE_DELAY_MS=300;class ee extends u{constructor(e){super(),this._videoController=null,this._audioController=null,this._conversationController=null,this._cameraController=null,this._liveKitService=null,this._connectionCoordinator=null,this._httpController=null,this._configManager=null,this._performanceMonitor=null,this._pendingDirectConfig=null,this._isReconnectInProgress=!1,this._reconnectPromise=null,this._liveKitProtocolDispatcher=null,this._context=new pe(e),s.info("SDK Client created")}get events(){return this.ensureNotDisposed(),this._publicEventAPI||(this._publicEmitter=new et(this._context.eventBus),this._publicEventAPI=this._publicEmitter.publicAPI()),this._publicEventAPI}sendTextQuestion(e){if(this.ensureNotDisposed(),this._ensureConnected(),this._conversationController)return this._conversationController.sendQuestion(e);throw new l(h.CONVERSATION_CONTROLLER_NOT_AVAILABLE,"Conversation controller is not available")}async interrupt(){if(this.ensureNotDisposed(),this._ensureConnected(),this._conversationController)return this._conversationController.interrupt();throw new l(h.CONVERSATION_CONTROLLER_NOT_AVAILABLE,"Conversation controller is not available")}setPerformanceMetricReporter(e){if(this.ensureNotDisposed(),!this._performanceMonitor){this._performanceMonitor=new W(e),this._performanceMonitor.setContext(this._context);return}this._performanceMonitor.setReporter(e)}setRenderFitMode(e){this.ensureNotDisposed(),this._ensureConnected(),this._videoController&&this._videoController.setRenderFitMode(e)}async startAudioCapture(){this.ensureNotDisposed(),this._ensureConnected(),await this._startAudioCaptureRtc()}async stopAudioCapture(){this.ensureNotDisposed(),this._ensureConnected(),await this._stopAudioCaptureRtc()}setVolume(e){this.ensureNotDisposed(),this._ensureConnected(),this._requireAudioController().setVolume(e)}getVolume(){return this.ensureNotDisposed(),this._ensureConnected(),this._requireAudioController().getVolume()||1}mute(){this.ensureNotDisposed(),this._ensureConnected(),this._requireAudioController().mute()}unmute(){this.ensureNotDisposed(),this._ensureConnected(),this._requireAudioController().unmute()}get isMuted(){return this.ensureNotDisposed(),this._ensureConnected(),this._requireAudioController().isMuted()}get isAudioCapturing(){return this.ensureNotDisposed(),this._ensureConnected(),this._liveKitService?.isMicrophoneActive()??!1}setAuthToken(e){this.ensureNotDisposed(),this._context.setAuthToken(e),this._httpController&&this._httpController.getAuthToken()}updateConnectionConfig(e){if(this.ensureNotDisposed(),this._context.options.connectConfig.type!=="direct")throw this._context.emitError(new l(h.SDK_INVALID_STATE_TRANSITION,"updateConnectionConfig is only available in direct mode"),h.SDK_INVALID_STATE_TRANSITION);this._pendingDirectConfig=this._validateDirectConnectionConfig(e)}async startCamera(){this.ensureNotDisposed(),this._ensureConnected(),await this._requireLiveKitService().startCamera()}stopCamera(){this.ensureNotDisposed(),this._ensureConnected(),this._liveKitService?.stopCamera()}getCameraStream(){return this.ensureNotDisposed(),this._ensureConnected(),this._cameraController?.getStream()??null}getCameraTrack(){return this.ensureNotDisposed(),this._ensureConnected(),this._cameraController?.getTrack()??null}attachCameraTo(e){this.ensureNotDisposed(),this._ensureConnected(),this._requireLiveKitService().attachCameraTo(e)}async preConnect(){this.ensureNotDisposed();try{this._ensureConfigManagerReady();const e=await this._configManager.preConnect();return this._applyConnectionConfig(e),!0}catch(e){throw this._context.emitError(e,h.SDK_PRECONNECT_FAILED)}}async connect(){if(this.ensureNotDisposed(),this._ensurePerformanceMonitorReady(),this._performanceMonitor?.startConnectMeasurement(),this._connectionCoordinator&&this._context.sessionState.isConnected){s.warn("SDK already initialized");return}try{this._ensureConfigManagerReady();const e=this._configManager.getValidConfig();if(e)this._applyConnectionConfig(e);else if(!await this.preConnect())throw new l(h.SDK_CONNECT_FAILED,"PreConnect failed without explicit error")}catch(e){throw this._context.emitError(e,h.SDK_CONNECT_FAILED)}try{await this._disposeConnectionRuntimeBeforeConnect(),s.info("Initializing SDK..."),this._connectionCoordinator=new fe,this._connectionCoordinator.setContext(this._context),this._context.bindConnectionState(()=>this._connectionCoordinator.getState()),this._connectionCoordinator.start(),this._connectionCoordinator.startConnecting(),this._bootstrapDomainControllers(),this._bootstrapLiveKitService(),this._wireLiveKitEventAdapterToControllers(),this._setupRtcProtocolDispatcher(),await this._establishMediaAndSessionTransports(),s.info("SDK initialized and connected")}catch(e){throw this._connectionCoordinator&&this._connectionCoordinator.transitionToIdle(),await this._cleanup(),this._context.emitError(e,h.SDK_INITIALIZATION_FAILED)}}async disconnect(){if(this.ensureNotDisposed(),!(!this._connectionCoordinator||this._connectionCoordinator.getState()==="idle"||this._connectionCoordinator.getState()==="disposed")){s.info("Disconnecting SDK..."),this._connectionCoordinator.startDisconnecting();try{this._audioController&&this._audioController.stopWsCapture(),this._liveKitService&&(await this._liveKitService.sendSystemEvent("session.stop"),await this._liveKitService.disconnect()),this._cameraController&&this._cameraController.stopCamera(),this._connectionCoordinator.completeDisconnecting(),s.info("SDK disconnected")}catch(e){throw s.error("Error during disconnect:",e),this._context.emitError(e,h.SDK_DISCONNECT_FAILED)}}}async reconnect(){if(this.ensureNotDisposed(),this._reconnectPromise)return this._reconnectPromise;const e=this._connectionCoordinator;if(!e?.canStartReconnectingManual()){const i=e?e.getSnapshot():this.connectionSnapshot;return s.warn("SDK reconnect not allowed in current state, returning current snapshot"),i}const t=e.captureSnapshotAndStartReconnectingManual();return this._reconnectPromise=(async()=>{this._isReconnectInProgress=!0;try{return await this.disconnect(),this._stageDirectConfigForNextReconnect(),await this._refreshConnectionConfigForReconnect(),await this.connect(),t}catch(i){throw s.error("Error during reconnect:",i),this._context.emitError(i,h.SDK_RECONNECT_FAILED)}finally{this._isReconnectInProgress=!1,this._reconnectPromise=null}})(),this._reconnectPromise}get connectionSnapshot(){return this.ensureNotDisposed(),this._connectionCoordinator?this._connectionCoordinator.getSnapshot():Q(j("idle"))}async _cleanup(){if(this._conversationController?.setRtcTextSender(null),this._liveKitService?.setProtocolMessageDispatcher(null),this._liveKitProtocolDispatcher&&(this._liveKitProtocolDispatcher.dispose(),this._liveKitProtocolDispatcher=null),this._audioController&&this._audioController.stopWsCapture(),this._liveKitService)try{await this._liveKitService.disconnect()}catch(e){s.error("Error disconnecting LiveKit:",e)}this._cameraController&&this._cameraController.stopCamera()}async _disposeConnectionRuntimeBeforeConnect(){await this._cleanup(),this._conversationController?.setRtcTextSender(null),this._liveKitService?.setProtocolMessageDispatcher(null),this._liveKitProtocolDispatcher&&(this._liveKitProtocolDispatcher.dispose(),this._liveKitProtocolDispatcher=null),this._videoController?.dispose(),this._videoController=null,this._audioController?.dispose(),this._audioController=null,this._cameraController?.dispose(),this._cameraController=null,this._conversationController?.dispose(),this._conversationController=null,this._liveKitService?.dispose(),this._liveKitService=null,this._connectionCoordinator?.dispose(),this._connectionCoordinator=null}_bootstrapDomainControllers(){this._videoController=new Se(this._context.options.video),this._videoController.setContext(this._context),this._performanceMonitor?.bindVideoElement(this._videoController.getSource().getInternalElement()),this._audioController=new Ne(this._context.options.audio),this._audioController.setContext(this._context),this._cameraController=new We,this._cameraController.setContext(this._context),this._conversationController=new Qe,this._conversationController.setContext(this._context)}_bootstrapLiveKitService(){this._liveKitService=new Je,this._liveKitService.setContext(this._context)}_wireLiveKitEventAdapterToControllers(){if(this._liveKitService&&this._videoController&&this._audioController){const e=this._liveKitService.getEventAdapter();e&&(typeof e.setVideoController=="function"&&e.setVideoController(this._videoController),typeof e.setAudioController=="function"&&e.setAudioController(this._audioController))}}_setupRtcProtocolDispatcher(){this._liveKitProtocolDispatcher&&(this._liveKitProtocolDispatcher.dispose(),this._liveKitProtocolDispatcher=null),this._liveKitProtocolDispatcher=new Ze,this._liveKitProtocolDispatcher.setContext(this._context),this._liveKitProtocolDispatcher.setConversationManager(this._conversationController.manager),this._liveKitService.setProtocolMessageDispatcher(e=>{this._liveKitProtocolDispatcher.dispatch(e)}),this._conversationController.setRtcTextSender(this._liveKitService)}async _establishMediaAndSessionTransports(){await this._liveKitService.connect(this._context.livekitUrl,this._context.token,this._context.options.connectConfig.type==="auth"?this._context.options.connectConfig.config.avatarId:"")}async _refreshConnectionConfigForReconnect(){this._ensureConfigManagerReady();const e=await this._configManager.refreshConfig();this._applyConnectionConfig(e)}_ensureConfigManagerReady(){s.info("Creating HTTP controller..."),this._httpController=this._httpController??new st(this._context.options.http?.baseURL,this._context.options.http?.headers),this._httpController.setContext(this._context),this._configManager=this._configManager??new v(this._httpController),this._configManager.setContext(this._context)}_applyConnectionConfig(e){this._context.setLivekitConfig(e.livekitUrl,e.token,e.roomId||""),e.videoOptions&&this._context.setVideoOptions(e.videoOptions),this._context.options.connectConfig.type==="auth"&&this._context.eventBus.emit("inner:sdk:connected",{source:"http"})}_stageDirectConfigForNextReconnect(){this._context.options.connectConfig.type==="direct"&&this._pendingDirectConfig&&(this._ensureConfigManagerReady(),this._configManager.replaceDirectConfig(this._pendingDirectConfig),this._pendingDirectConfig=null)}_validateDirectConnectionConfig(e){const t=(e?.sfuUrl||"").trim(),i=(e?.clientToken||"").trim();if(!t||!i)throw this._context.emitError(new l(h.SDK_PRECONNECT_FAILED,"Invalid direct connection config"),h.SDK_PRECONNECT_FAILED);return{sfuUrl:t,clientToken:i}}_ensurePerformanceMonitorReady(){const e=this._context.options.performanceMonitor;if(!(e?.enabled!==!1)){this._performanceMonitor?.dispose(),this._performanceMonitor=null;return}if(this._performanceMonitor){this._performanceMonitor.setReporter(e?.reporter);return}this._performanceMonitor=new W(e?.reporter),this._performanceMonitor.setContext(this._context)}get isConnected(){return this.ensureNotDisposed(),this._context.sessionState.isConnected}_ensureConnected(){if(!this._context.sessionState.isConnected)throw new l(h.SDK_NOT_CONNECTED,"SDK is not connected. Call connect() first.")}async _startAudioCaptureRtc(){await this._requireLiveKitService().startMicrophone()}async _stopAudioCaptureRtc(){await this._requireLiveKitService().stopMicrophone()}_requireAudioController(){if(!this._audioController)throw new l(h.AUDIO_CONTROLLER_NOT_AVAILABLE,"Audio controller is not available");return this._audioController}_requireLiveKitService(){if(!this._liveKitService)throw new l(h.LIVEKIT_CONNECT_FAILED,"LiveKit service is not available");return this._liveKitService}onDispose(){this.disconnect().catch(e=>{s.error("Error during disconnect in dispose:",e)}),this._videoController&&(this._videoController.dispose(),this._videoController=null),this._audioController&&(this._audioController.dispose(),this._audioController=null),this._cameraController&&(this._cameraController.dispose(),this._cameraController=null),this._conversationController?.setRtcTextSender(null),this._liveKitService?.setProtocolMessageDispatcher(null),this._liveKitProtocolDispatcher&&(this._liveKitProtocolDispatcher.dispose(),this._liveKitProtocolDispatcher=null),this._conversationController&&(this._conversationController.dispose(),this._conversationController=null),this._liveKitService&&(this._liveKitService.dispose(),this._liveKitService=null),this._httpController&&(this._httpController.dispose(),this._httpController=null),this._configManager&&(this._configManager.dispose(),this._configManager=null),this._performanceMonitor&&(this._performanceMonitor.dispose(),this._performanceMonitor=null),this._connectionCoordinator&&(this._connectionCoordinator.transitionToDisposed(),this._connectionCoordinator.dispose(),this._connectionCoordinator=null),this._context.dispose(),s.info("SDK Client disposed")}}function nt(o){const e=o.connectConfig;if(e.type==="direct"&&(!e.config.sfuUrl||!e.config.clientToken))throw new l(h.INVALID_CONNECT_CONFIG,"Invalid direct connect config");if(e.type==="auth"&&!e.config.avatarId)throw new l(h.NO_AVATARID,"avatarId is required");return new ee(o)}class G extends u{constructor(e,t){super(),this._videoElement=null,this._videoUrl=null,this._isPlaying=!1,this._frameCallbackId=null,this._videoUrl=e,t?this._videoElement=t:(this._videoElement=document.createElement("video"),this._videoElement.style.opacity="0",this._videoElement.muted=!0,this._videoElement.playsInline=!0,this._videoElement.loop=!0,document.querySelector("#video-container")?.appendChild(this._videoElement)),s.info("Local Video Debug Source created")}get videoElement(){return this._videoElement}async load(){if(!this._videoElement)throw new Error("Video element or URL not set");return new Promise((e,t)=>{const i=this._videoElement,n=()=>{i.removeEventListener("loadedmetadata",n),i.removeEventListener("error",r),s.info(`Video loaded: ${i.videoWidth}x${i.videoHeight}`),e()},r=a=>{i.removeEventListener("loadedmetadata",n),i.removeEventListener("error",r),t(new Error(`Failed to load video: ${a}`))};i.addEventListener("loadedmetadata",n),i.addEventListener("error",r),i.src=this._videoUrl||"",i.load()})}async play(){if(!this._videoElement)throw new Error("Video element not set");if(!this._isPlaying)try{await this._videoElement.play(),this._videoElement.style.opacity="0",this._videoElement.setAttribute("loop","true"),this._isPlaying=!0,s.info("Video playback started")}catch(e){throw s.error("Failed to play video:",e),e}}pause(){this._videoElement&&this._isPlaying&&(this._videoElement.pause(),this._isPlaying=!1,s.info("Video playback paused"))}stop(){this._videoElement&&(this._videoElement.pause(),this._videoElement.currentTime=0,this._isPlaying=!1,s.info("Video playback stopped"))}get isPlaying(){return this._isPlaying&&this._videoElement!==null&&!this._videoElement.paused}get videoWidth(){return this._videoElement?.videoWidth||0}get videoHeight(){return this._videoElement?.videoHeight||0}async captureFrame(){if(!this._videoElement||this._videoElement.readyState<2)return null;try{if("VideoFrame"in window&&typeof window.VideoFrame=="function")try{return new VideoFrame(this._videoElement,{timestamp:this._videoElement.currentTime*1e6})}catch(i){s.error("",i)}const e=await createImageBitmap(this._videoElement),t=new VideoFrame(e,{timestamp:this._videoElement.currentTime*1e6});return e.close(),t}catch(e){return s.error("Failed to capture video frame:",e),null}}onFrame(e){if(!this._videoElement)return;this.offFrame();const t=this._videoElement;if("requestVideoFrameCallback"in t){const i=(n,r)=>{!this._isPlaying||!this._videoElement||this.captureFrame().then(a=>{a&&(e(a),a.close()),this._isPlaying&&this._videoElement&&(this._frameCallbackId=t.requestVideoFrameCallback(i))}).catch(a=>{s.error("Error in frame callback:",a)})};this._frameCallbackId=t.requestVideoFrameCallback(i)}else{const i=()=>{!this._isPlaying||!this._videoElement||this.captureFrame().then(n=>{n&&(e(n),n.close()),this._isPlaying&&this._videoElement&&(this._frameCallbackId=requestAnimationFrame(i))}).catch(n=>{s.error("Error in frame callback:",n)})};this._frameCallbackId=requestAnimationFrame(i)}}offFrame(){this._frameCallbackId!==null&&(this._videoElement&&"cancelVideoFrameCallback"in this._videoElement?this._videoElement.cancelVideoFrameCallback(this._frameCallbackId):cancelAnimationFrame(this._frameCallbackId),this._frameCallbackId=null)}onDispose(){this.stop(),this.offFrame(),this._videoElement&&this._videoElement.parentElement===document.body&&document.body.removeChild(this._videoElement),this._videoElement=null,this._videoUrl=null,s.info("Local Video Debug Source disposed")}}class rt extends u{constructor(e,t,i){super(),this._outputCanvas=null,this._outputRenderer=null,this._source=new Y;const n={...e,renderMode:"processed"};this._pipeline=new Z(this._source,n),this._outputRenderer=new N(i,t),this._pipeline.setRenderer(this._outputRenderer),this._outputCanvas=this._outputRenderer.getCanvas(),s.info("Video Pipeline Runner created")}get outputCanvas(){return this._outputCanvas}get processor(){return this._pipeline.strategy.getProcessors()[0]||null}setProcessor(e){this._pipeline.setProcessor(e),s.info("Processor set on pipeline")}async processFrame(e){if(this.ensureNotDisposed(),!this._outputRenderer){s.error("Output renderer not available"),e.close();return}try{const t=this._pipeline.strategy.getProcessors()[0]||null;let i=null;if(t){try{i=t.process(e)}catch(n){s.error("Error processing frame:",n),i=e}if(i===null&&"getImageData"in t){const n=t.getImageData();if(n&&this._outputCanvas&&this._outputRenderer.getContext()){const r=this._outputRenderer.getContext();r&&((this._outputCanvas.width!==n.width||this._outputCanvas.height!==n.height)&&(this._outputCanvas.width=n.width,this._outputCanvas.height=n.height),r.putImageData(n,0,0))}e.close();return}i&&i!==e?(this._outputRenderer.render(i),e.close()):i===e?this._outputRenderer.render(e):e.close()}else this._outputRenderer.render(e)}catch(t){s.error("Error in processFrame:",t),e.close()}}setOutputSize(e,t){this._outputCanvas&&(this._outputCanvas.width=e,this._outputCanvas.height=t,s.info(`Output canvas size set to ${e}x${t}`))}onDispose(){this._pipeline.dispose(),this._outputRenderer&&(this._outputRenderer.dispose(),this._outputRenderer=null),this._outputCanvas&&this._outputCanvas.parentElement===document.body&&document.body.removeChild(this._outputCanvas),this._outputCanvas=null,s.info("Video Pipeline Runner disposed")}}class ot extends u{constructor(e,t,i,n){if(super(),this._isRunning=!1,this._videoSource=new G(e,t),this._options={renderMode:"processed",containerElement:t,...n},this._pipelineRunner=new rt(this._options,t,i),this._options.greenScreen?.enabled){const r=new x(this._options.greenScreen);this._pipelineRunner.setProcessor(r),s.info("Green screen processor enabled in debugger")}s.info("Standalone Video Debugger created")}async switchSource(e,t){this.ensureNotDisposed();const i=this._videoSource,n=this._isRunning,r=new G(e,t);try{await r.load(),n&&await r.play(),r.onFrame(d=>{this._pipelineRunner.processFrame(d).catch(_=>{s.error("Error processing frame:",_)})}),this._videoSource=r;try{i.offFrame(),i.stop(),i.dispose()}catch(d){s.warn("Error disposing old video source during switch:",d)}const a=this._videoSource.videoWidth,c=this._videoSource.videoHeight;a>0&&c>0&&this._pipelineRunner.setOutputSize(a,c)}catch(a){s.error("Failed to switch video source:",a);try{r.dispose()}catch(c){s.warn("Error disposing new video source after failed switch:",c)}throw a}}get outputCanvas(){return this._pipelineRunner.outputCanvas}get videoSource(){return this._videoSource}get processor(){return this._pipelineRunner.processor}setProcessor(e){this._pipelineRunner.setProcessor(e)}updateGreenScreenOptions(e){const t=this._pipelineRunner.processor;if(t&&"updateOptions"in t&&typeof t.updateOptions=="function")t.updateOptions(e),s.info("Green screen options updated");else{const i=this._options.greenScreen||{enabled:!1},n={...i,...e,enabled:e.enabled!==void 0?e.enabled:i.enabled},r=new x(n);this._pipelineRunner.setProcessor(r),this._options.greenScreen=n,s.info("Green screen processor recreated with new options")}}async initialize(){this.ensureNotDisposed();try{await this._videoSource.load();const e=this._videoSource.videoWidth,t=this._videoSource.videoHeight;e>0&&t>0&&this._pipelineRunner.setOutputSize(e,t),s.info("Video debugger initialized")}catch(e){throw s.error("Failed to initialize video debugger:",e),e}}async start(){if(this.ensureNotDisposed(),this._isRunning){s.warn("Debugger is already running");return}try{this._videoSource.videoWidth===0&&await this.initialize(),await this._videoSource.play(),this._videoSource.onFrame(e=>{this._pipelineRunner.processFrame(e).catch(t=>{s.error("Error processing frame:",t)})}),this._isRunning=!0,s.info("Video debugger started")}catch(e){throw s.error("Failed to start video debugger:",e),e}}stop(){this._isRunning&&(this._videoSource.stop(),this._videoSource.offFrame(),this._isRunning=!1,s.info("Video debugger stopped"))}pause(){this._isRunning&&(this._videoSource.pause(),s.info("Video debugger paused"))}async resume(){this._isRunning&&(await this._videoSource.play(),s.info("Video debugger resumed"))}get isRunning(){return this._isRunning}onDispose(){this.stop(),this._videoSource.dispose(),this._pipelineRunner.dispose(),s.info("Standalone Video Debugger disposed")}}exports.DEFAULT_AUDIO_BUFFER_SIZE=ce;exports.DEFAULT_AUDIO_CHANNELS=ae;exports.DEFAULT_AUDIO_SAMPLE_RATE=k;exports.DEFAULT_RECONNECT_ATTEMPTS=y;exports.DEFAULT_RECONNECT_DELAY=w;exports.DEFAULT_WS_TIMEOUT=oe;exports.Disposable=u;exports.EventBus=$;exports.Logger=s;exports.SDKClient=ee;exports.SDKError=l;exports.SessionState=z;exports.StandaloneVideoDebugger=ot;exports.createClient=nt;
110
+ `;function be(){const o=new Blob([Ie],{type:"text/javascript"});return URL.createObjectURL(o)}class we extends ye{constructor(e){super(),this.options=e,this._stream=null,this._audioContext=null,this._sourceNode=null,this._workletNode=null,this._targetSampleRate=k,this._bitDepth=O,this._channels=1;const t=e?.input;this._targetSampleRate=t?.sampleRate||k,this._bitDepth=t?.bitDepth||O,this._channels=t?.channels||1}async start(){this.ensureNotDisposed(),(this._stream||this._audioContext||this._sourceNode||this._workletNode)&&this.stop();try{const e=this.options?.input,t={audio:e?.constraints?e.constraints:{channelCount:this._channels}};this._stream=await navigator.mediaDevices.getUserMedia(t);const r=this._stream.getAudioTracks()[0].getSettings().sampleRate||44100;this._audioContext=new AudioContext({sampleRate:r});const a=be();await this._audioContext.audioWorklet.addModule(a),this._sourceNode=this._audioContext.createMediaStreamSource(this._stream),this._workletNode=new AudioWorkletNode(this._audioContext,"audio-resampler",{processorOptions:{targetSampleRate:this._targetSampleRate,bitDepth:this._bitDepth,channels:this._channels},numberOfInputs:1,numberOfOutputs:0}),this._workletNode.port.onmessage=c=>{const{type:d,data:_,sampleRate:g}=c.data;d==="audioData"&&_&&this.emitAudioData(_,g)},this._sourceNode.connect(this._workletNode),s.info(`Audio capture started: input=${r}Hz -> output=${this._targetSampleRate}Hz, ${this._bitDepth}bit`)}catch(e){throw l.fromError(e,h.AUDIO_CAPTURE_FAILED)}}stop(){this._stream&&(this._stream.getTracks().forEach(e=>e.stop()),this._stream=null),this._workletNode&&(this._workletNode.port.onmessage=null,this._workletNode.disconnect(),this._workletNode=null),this._sourceNode&&(this._sourceNode.disconnect(),this._sourceNode=null),this._audioContext&&(this._audioContext.close(),this._audioContext=null),s.info("Audio capture stopped")}onDispose(){this.stop()}}class ke{create(e){return new we(e)}}class Ne extends f{constructor(e,t){super(),this._source=null,this._isWsCapturing=!1,this._currentOutputTrackId=null,this._inputPipeline=new De(e),this._outputPipeline=new Re,this._sourceFactory=t||new ke,s.info("Audio Controller created")}setContext(e){super.setContext(e),this._wireLocalInputToEventBus()}_wireLocalInputToEventBus(){this._inputPipeline.onProcessedAudio=(e,t)=>{this._context.eventBus.emit("inner:audio:frame:received",{data:e,sampleRate:t})}}async startWsCapture(){if(this.ensureNotDisposed(),this._isWsCapturing){s.warn("Audio capture already started");return}if(!this._context.mediaAudioCaptureState.tryBegin("ws"))throw new l(h.SDK_INVALID_STATE_TRANSITION,"RTC audio capture is active");try{this._source||(this._source=this._sourceFactory.create(this._context.options.audio),this._source.onAudioData=(e,t)=>{this._inputPipeline.process(e,t)}),await this._source.start(),this._isWsCapturing=!0,this._context.eventBus.emit("inner:audio:input:started",void 0),s.info("Audio capture started (ws)")}catch(e){throw this._context.mediaAudioCaptureState.end("ws"),this._context.emitError(e,h.AUDIO_CAPTURE_START_FAILED)}}stopWsCapture(){this.ensureNotDisposed(),this._isWsCapturing&&(this._source&&this._source.stop(),this._isWsCapturing=!1,this._context.mediaAudioCaptureState.end("ws"),this._context.eventBus.emit("inner:audio:input:stopped",void 0),s.info("Audio capture stopped (ws)"))}get isWsCapturing(){return this._isWsCapturing}setOutputTrack(e,t,i){this.ensureNotDisposed(),this._outputPipeline.setTrack(e),this._currentOutputTrackId=t,this._context.eventBus.emit("inner:audio:track:added",{trackId:t,participantId:i}),s.info("Audio output track ready")}removeOutputTrack(e){this.ensureNotDisposed(),this._currentOutputTrackId===e&&(this._outputPipeline.removeTrack(),this._currentOutputTrackId=null,this._context.eventBus.emit("inner:audio:track:removed",{trackId:e}),s.info("Audio output track removed"))}setVolume(e){this.ensureNotDisposed(),this._outputPipeline.setVolume(e),this._context.eventBus.emit("inner:audio:volume:changed",{volume:e}),s.debug(`Audio volume set to: ${e}`)}getVolume(){return this._outputPipeline.getVolume()}mute(){this.ensureNotDisposed(),this._outputPipeline.mute(),this._context.eventBus.emit("inner:audio:muted",void 0),s.debug("Audio muted")}unmute(){this.ensureNotDisposed(),this._outputPipeline.unmute(),this._context.eventBus.emit("inner:audio:unmuted",void 0),s.debug("Audio unmuted")}getOutputElement(){return this._outputPipeline.getAudioElement()}isMuted(){return this._outputPipeline.isMuted()}onDispose(){this.stopWsCapture(),this._source&&(this._source.dispose(),this._source=null),this._inputPipeline.dispose(),this._outputPipeline.dispose(),s.info("Audio Controller disposed")}}class xe extends u{constructor(){super(...arguments),this._stream=null,this._videoElement=null}async start(){if(this.ensureNotDisposed(),this._stream){s.warn("Camera capture already started");return}try{const e=await navigator.mediaDevices.getUserMedia({video:!0});if(this._stream=e,!this._videoElement){const t=document.createElement("video");t.autoplay=!0,t.muted=!0,t.playsInline=!0,t.style.position="fixed",t.style.opacity="0",t.style.pointerEvents="none",t.style.width="0",t.style.height="0",t.style.zIndex="-1",typeof document<"u"&&document.body&&document.body.appendChild(t),this._videoElement=t}this._videoElement.srcObject=this._stream,this._videoElement.play().catch(t=>{s.error("Failed to start camera video playback",t)})}catch(e){throw l.fromError(e,h.SDK_ERROR)}}stop(){if(this._stream&&(this._stream.getTracks().forEach(e=>e.stop()),this._stream=null),this._videoElement){this._videoElement.srcObject=null;const e=this._videoElement.parentNode;e&&e.removeChild(this._videoElement),this._videoElement=null}s.info("Camera capture stopped")}getStream(){return this.ensureNotDisposed(),this._stream}getVideoElement(){return this.ensureNotDisposed(),this._videoElement}getTrack(){if(this.ensureNotDisposed(),!this._stream)return null;const e=this._stream.getVideoTracks();return e.length>0?e[0]:null}onDispose(){this.stop()}}const D=640,T=360;class Le extends u{constructor(e,t){super(),this._videoElement=null,this._canvas=null,this._videoElement=e,this._onFrame=t,this._handleServerCommand=this._onServerCommand.bind(this)}setContext(e){this._context=e}setVideoElement(e){this._videoElement=e}start(){this.ensureNotDisposed(),this._canvas||(typeof OffscreenCanvas<"u"?this._canvas=new OffscreenCanvas(D,T):(this._canvas=document.createElement("canvas"),this._canvas.width=D,this._canvas.height=T)),this._context.eventBus.on("inner:conversation:server:command",this._handleServerCommand)}_onServerCommand({code:e}){e===500&&this._captureFrame()}stop(){this._context.eventBus.off("inner:conversation:server:command",this._handleServerCommand)}_captureFrame(){const e=this._videoElement,t=this._canvas;if(!e||!t||e.readyState<2||e.videoWidth===0||e.videoHeight===0)return;const i=t.getContext("2d");if(!i)return;const n=e.videoWidth,r=e.videoHeight;let a=D,c=D*r/n;c>T&&(c=T,a=T*n/r),t.width!==a&&(t.width=a),t.height!==c&&(t.height=c),i.clearRect(0,0,t.width,t.height),i.drawImage(e,0,0,n,r,0,0,a,c),typeof OffscreenCanvas<"u"&&t instanceof OffscreenCanvas&&typeof t.convertToBlob=="function"?t.convertToBlob({type:"image/jpeg",quality:S}).then(d=>{if(d)try{this._onFrame(d,a,c,0,S)}catch(_){s.error("ScreenshotScheduler onFrame callback error",_)}}).catch(d=>{s.error("ScreenshotScheduler convertToBlob error",d)}):t instanceof HTMLCanvasElement&&t.toBlob(d=>{if(d)try{this._onFrame(d,a,c,0,S)}catch(_){s.error("ScreenshotScheduler onFrame callback error",_)}},"image/jpeg",S)}onDispose(){this.stop(),this._canvas=null,this._videoElement=null}}async function Oe(o,e){const t=await o.arrayBuffer();return{data:new Uint8Array(t),width:e.width,height:e.height,format:e.format,quality:e.quality}}const Pe=12,Fe=2,Me=0,F=0,M=4,V=65535,U=4294967295;function Ve(o){const e=Math.floor(o);return e<0?0:e>255?255:e}function b(o){const e=Math.floor(o);return e<0?0:e>V?V:e}function Ue(o){const e=Math.floor(o);return e<0?0:e>U?U:e}function Ke(o){const e=Math.floor(o);return e<F?F:e>M?M:e}function He(o){const e=new ArrayBuffer(Pe),t=new DataView(e),i=Ke(o.format)&15,n=Fe<<6|Me<<4|i;return t.setUint8(0,n),t.setUint8(1,Ve(o.quality)),t.setUint16(2,b(o.id),!1),t.setUint16(4,b(o.width),!1),t.setUint16(6,b(o.height),!1),t.setUint32(8,Ue(o.payloadLength),!1),new Uint8Array(e)}const Be=65535;class qe extends u{constructor(){super(...arguments),this._wsSender=null,this._imageId=0}send(e,t){if(this.ensureNotDisposed(),!this._wsSender||!this._wsSender.isConnected())return;const i=e.byteLength,n=He({format:t.format,quality:t.quality,id:this._nextId(),width:t.width,height:t.height,payloadLength:i}),r=new Uint8Array(n.byteLength+i);r.set(n,0),r.set(e,n.byteLength),this._wsSender.sendBinary(r)}_nextId(){const e=this._imageId;return this._imageId=this._imageId>=Be?0:this._imageId+1,e}onDispose(){this._wsSender=null}}class We extends f{constructor(){super(),this._isRunning=!1,this._source=new xe,this._sender=new qe;const e=(t,i,n,r,a=S)=>{Oe(t,{width:i,height:n,format:r,quality:a}).then(c=>{this._sender.send(c.data,{width:c.width,height:c.height,format:c.format,quality:c.quality})})};this._scheduler=new Le(null,e)}setContext(e){super.setContext(e),this._scheduler.setContext(e)}async startCamera(){if(this.ensureNotDisposed(),this._isRunning){s.warn("Camera already running");return}await this._source.start();const e=this._source.getVideoElement();this._scheduler.setVideoElement(e),this._scheduler.start(),this._isRunning=!0,this._context.eventBus.emit("inner:camera:started",void 0),s.info("Camera started")}stopCamera(){this.ensureNotDisposed(),this._isRunning&&(this._scheduler.stop(),this._source.stop(),this._isRunning=!1,this._context.eventBus.emit("inner:camera:stopped",void 0),s.info("Camera stopped"))}isCameraRunning(){return this._isRunning}getStream(){return this.ensureNotDisposed(),this._source.getStream()}getTrack(){return this.ensureNotDisposed(),this._source.getTrack()}attachTo(e){this.ensureNotDisposed();const t=this._source.getStream();t&&(e.srcObject=t,e.play().catch(i=>s.error("attachTo play failed",i)))}onDispose(){this.stopCamera(),this._source.dispose(),this._scheduler.dispose(),this._sender.dispose()}}exports.ConversationStateType=void 0;(function(o){o.IDLE="idle",o.WAITING="waiting",o.STREAMING="streaming",o.COMPLETED="completed",o.ERROR="error"})(exports.ConversationStateType||(exports.ConversationStateType={}));class Ge extends X{constructor(){super(...arguments),this._currentState=exports.ConversationStateType.IDLE,this.transitionTable=[{from:exports.ConversationStateType.IDLE,to:[exports.ConversationStateType.WAITING,exports.ConversationStateType.ERROR]},{from:exports.ConversationStateType.WAITING,to:[exports.ConversationStateType.STREAMING,exports.ConversationStateType.ERROR]},{from:exports.ConversationStateType.STREAMING,to:[exports.ConversationStateType.COMPLETED,exports.ConversationStateType.ERROR]},{from:exports.ConversationStateType.COMPLETED,to:[exports.ConversationStateType.IDLE,exports.ConversationStateType.WAITING,exports.ConversationStateType.ERROR]},{from:exports.ConversationStateType.ERROR,to:[exports.ConversationStateType.IDLE]}]}reset(){this._currentState=exports.ConversationStateType.IDLE}}class K extends u{constructor(e){super(),this._currentQuestion=null,this._currentAnswer=null,this._sessionId=e,this._stateMachine=new Ge,s.info(`Conversation session created: ${e}`)}get sessionId(){return this._sessionId}get state(){return this._stateMachine.getCurrentState()}_setQuestion(e){this._currentQuestion=e,this._stateMachine.transitionTo(exports.ConversationStateType.WAITING),this._syncContextState()}get currentQuestion(){return this._currentQuestion}_startAnswer(e){this._currentAnswer={questionId:e,text:"",isComplete:!1,timestamp:Date.now()},this._stateMachine.transitionTo(exports.ConversationStateType.STREAMING),this._syncContextState()}_appendAnswerChunk(e){this._currentAnswer&&(this._currentAnswer.text+=e)}_completeAnswer(){this._currentAnswer&&(this._currentAnswer.isComplete=!0,this._currentAnswer.completedAt=Date.now()),this._stateMachine.transitionTo(exports.ConversationStateType.COMPLETED),this._syncContextState()}get currentAnswer(){return this._currentAnswer}_setError(e){this._stateMachine.transitionTo(exports.ConversationStateType.ERROR),this._syncContextState()}_syncContextState(){}handleEvent(e){const t=this._stateMachine.getCurrentState();switch(e.type){case"QUESTION":if(t!==exports.ConversationStateType.IDLE&&t!==exports.ConversationStateType.COMPLETED){s.warn(`Ignoring QUESTION event in invalid state: ${t}`);return}this._setQuestion(e.payload);break;case"ANSWER_START":if(t!==exports.ConversationStateType.WAITING){s.warn(`Ignoring ANSWER_START event in invalid state: ${t}. Expected: WAITING`);return}this._startAnswer(e.payload);break;case"ANSWER_CHUNK":if(t!==exports.ConversationStateType.STREAMING){s.warn(`Ignoring ANSWER_CHUNK event in invalid state: ${t}. Expected: STREAMING`);return}this._appendAnswerChunk(e.payload&&e.payload.chunk||""),e.payload&&e.payload.isComplete&&this._completeAnswer();break;case"ANSWER_COMPLETE":if(t!==exports.ConversationStateType.STREAMING){s.warn(`Ignoring ANSWER_COMPLETE event in invalid state: ${t}. Expected: STREAMING`);return}this._completeAnswer();break;case"ERROR":this._setError(e.payload);break;default:s.warn(`Unknown event type: ${e.type}`);break}}reset(){this._stateMachine.reset(),this._currentQuestion=null,this._currentAnswer=null}onDispose(){this.reset(),s.info(`Conversation session disposed: ${this._sessionId}`)}}class $e extends u{constructor(){super(...arguments),this._buffers=new Map}appendChunk(e,t){this.ensureNotDisposed();const n=(this._buffers.get(e)||"")+t;return this._buffers.set(e,n),s.debug(`Message chunk appended for question ${e}, length: ${n.length}`),n}getMessage(e){return this._buffers.get(e)||null}complete(e){const t=this.getMessage(e)||"";return this._buffers.delete(e),s.debug(`Message assembly completed for question ${e}, length: ${t.length}`),t}clear(e){this._buffers.delete(e)}onDispose(){this._buffers.clear(),s.info("Message Assembler disposed")}}class ze{generateSessionId(){return`session_${Date.now()}_${Math.random().toString(36).substring(2,9)}`}generateQuestionId(){return Math.random().toString(36).substring(2,6)}}class je extends f{constructor(e){super(),this._rtcTextSender=null,this._sessions=new Map,this._assembler=new $e,this._idService=new ze,s.info("Conversation Manager created")}setRtcTextSender(e){this._rtcTextSender=e}async sendQuestion(e,t={speed:100,mood:0,vol:50}){this.ensureNotDisposed();const i=this._idService.generateQuestionId(),n={id:i,text:e,timestamp:Date.now(),inputMode:"text"},r=this._idService.generateSessionId(),a=new K(r);return a.handleEvent({type:"QUESTION",payload:n}),this._sessions.set(i,a),await this._dispatchServerTextUplink(i,e,t,a),this._context.eventBus.emit("inner:conversation:question:sent",{questionId:i,text:e}),this._context.eventBus.emit("inner:conversation:answer:waiting",{questionId:i}),s.info(`Question sent: ${i}`),i}async sendSystemEvent(e,t){if(this.ensureNotDisposed(),this._rtcTextSender)try{await this._rtcTextSender.sendSystemEvent(e,t)}catch(i){s.error("Error sending video avaliable state:",i)}}async _dispatchServerTextUplink(e,t,i,n){if(this._rtcTextSender)try{await this._rtcTextSender.sendTextData(e,t,"input.text",i)}catch(r){throw s.error("Error sending question via RTC:",r),n.handleEvent({type:"ERROR",payload:"Failed to send question"}),this._context.eventBus.emit("inner:sdk:error",{error:l.fromError(r,h.LIVEKIT_SEND_TEXT_DATA_FAILED)}),r}else s.warn("RTC text sender not set, question not sent")}handleServerPong(){this.ensureNotDisposed()}willDisconnect(){this.ensureNotDisposed()}handleAnswerChunk(e,t,i){this.ensureNotDisposed();const n=this._sessions.get(e);if(!n){s.warn(`No active session for answer chunk: ${e}`);return}if(n.state===exports.ConversationStateType.WAITING&&n.handleEvent({type:"ANSWER_START",payload:e}),i){n.handleEvent({type:"ANSWER_COMPLETE"});const a=this._assembler.complete(e);this._context.eventBus.emit("inner:conversation:answer:completed",{questionId:e,fullAnswer:a});try{n.dispose()}catch(c){s.error("Error disposing session:",c)}this._sessions.delete(e)}else n.handleEvent({type:"ANSWER_CHUNK",payload:{uid:e,chunk:t,isComplete:i}}),this._assembler.appendChunk(e,t),this._context.eventBus.emit("inner:conversation:answer:chunk",{questionId:e,chunk:t,isComplete:i})}handleServerInitiatedMessage(e,t){this.ensureNotDisposed();const i=e??this._idService.generateQuestionId();this._context.eventBus.emit("inner:conversation:server:message",{questionId:i,message:t,type:"text"})}handleAsrTextReceived(e,t,i,n){this.ensureNotDisposed();const r=this._idService.generateSessionId(),a=new K(r);a.handleEvent({type:"QUESTION",payload:{id:e,text:t,timestamp:Date.now(),inputMode:"voice"}}),this._sessions.set(e,a),i?this._context.eventBus.emit("inner:conversation:asr:chunk",{questionId:e,text:t,isComplete:n??!1}):this._context.eventBus.emit("inner:conversation:asr:received",{questionId:e,text:t})}handleServerCommand(e,t){this.ensureNotDisposed(),this._context.eventBus.emit("inner:conversation:server:command",{code:e,command:t})}onDispose(){for(const e of this._sessions.values())try{e.dispose()}catch(t){s.error("Error disposing session:",t)}this._sessions.clear(),this._assembler.dispose(),s.info("Conversation Manager disposed")}}class Qe extends f{constructor(e){super(),this._eventUnsubscribers=[],this._manager=new je(e),s.info("Conversation Controller created")}setContext(e){super.setContext(e),this._manager.setContext(e),this._bindEventBus()}get manager(){return this._manager}setRtcTextSender(e){this._manager.setRtcTextSender(e)}async sendQuestion(e,t){return this.ensureNotDisposed(),this._manager.sendQuestion(e,t)}async interrupt(){return this.ensureNotDisposed(),this._manager.sendSystemEvent("control.interrupt")}_clearEventListeners(){this._eventUnsubscribers.forEach(e=>{try{e()}catch(t){s.error("Performance monitor unsubscribe failed",t)}}),this._eventUnsubscribers=[]}_bindEventBus(){this._eventUnsubscribers.push(this._context.eventBus.on("inner:rtc:video:available",()=>{this.sendVideoAvaliableState()}))}async sendVideoAvaliableState(){return this.ensureNotDisposed(),this._manager.sendSystemEvent("scene.ready")}onDispose(){this._clearEventListeners(),this._manager.dispose(),s.info("Conversation Controller disposed")}}class Xe extends u{constructor(){super(...arguments),this._tracks=new Map,this._participants=new Map,this._trackEndedHandlers=new Map}register(e,t,i){this.ensureNotDisposed(),this._tracks.set(e,t),this._participants.has(i)||this._participants.set(i,new Set),this._participants.get(i).add(e);const n=()=>{s.debug(`Track ended: ${e}`),this.unregister(e)};this._trackEndedHandlers.set(e,n),t.on("ended",n),s.debug(`Track registered: ${e} from participant ${i}`)}unregister(e){const t=this._tracks.get(e);if(t){const i=this._trackEndedHandlers.get(e);i&&(t.off("ended",i),this._trackEndedHandlers.delete(e)),t.stop(),this._tracks.delete(e);for(const[n,r]of this._participants.entries())if(r.has(e)){r.delete(e),r.size===0&&this._participants.delete(n);break}s.debug(`Track unregistered: ${e}`)}}getTrack(e){return this._tracks.get(e)||null}getParticipantTracks(e){const t=this._participants.get(e);return t?Array.from(t):[]}getAllTrackIds(){return Array.from(this._tracks.keys())}getVideoAvailability(){let e=!1,t=!1,i=!1;for(const n of this._tracks.values()){const r=n.mediaStreamTrack;if(r.kind==="video"&&(e=!0,r.readyState==="live"&&(t=!0),r.enabled&&(i=!0),r.readyState==="live"&&r.enabled))return{available:!0}}return e?t?i?{available:!1,reason:E.NO_VIDEO_TRACK}:{available:!1,reason:E.TRACK_MUTED}:{available:!1,reason:E.TRACK_ENDED}:{available:!1,reason:E.NO_VIDEO_TRACK}}clear(){const e=Array.from(this._tracks.keys());for(const t of e)this.unregister(t);s.info("All tracks cleared")}onDispose(){this.clear()}}class H extends f{constructor(e){super(),this._videoController=null,this._room=null,this._trackRegistry=e,this._loosTrackTimer=null,s.info("Video Track Handler created")}setRoom(e){this._room=e}setVideoController(e){this._videoController=e}handleTrackSubscribed(e,t,i){this.ensureNotDisposed(),this._loosTrackTimer&&clearTimeout(this._loosTrackTimer),this._trackRegistry.register(t,e,i),this._videoController&&e.attach(this._videoController.getSource().getInternalElement()),e.on("videoPlaybackStarted",()=>{s.info("inner:rtc:video:available"),this._context.eventBus.emit("inner:rtc:video:available",void 0)}),this._context.eventBus.emit("inner:video:track:added",{trackId:t,participantId:i}),s.debug(`Video track subscribed: ${t}`)}handleTrackUnsubscribed(e,t,i){this.ensureNotDisposed(),this._videoController&&e.detach(this._videoController.getSource().getInternalElement()),e.removeAllListeners(),this._context.eventBus.emit("inner:video:track:removed",{trackId:t,participantId:i}),this._loosTrackTimer&&clearTimeout(this._loosTrackTimer),this._loosTrackTimer=setTimeout(()=>{this._context.eventBus.emit("inner:rtc:video:unavailable",void 0)},re),this._trackRegistry.unregister(t),s.debug(`Video track unsubscribed: ${t}`)}supports(e){return e.mediaStreamTrack.kind==="video"}onDispose(){this._loosTrackTimer&&(clearTimeout(this._loosTrackTimer),this._loosTrackTimer=null),s.info("Video Track Handler disposed")}}class B extends f{constructor(e){super(),this._audioController=null,this._room=null,this._trackRegistry=e,s.info("Audio Track Handler created")}setRoom(e){this._room=e}setAudioController(e){this._audioController=e}handleTrackSubscribed(e,t,i){this.ensureNotDisposed(),this._trackRegistry.register(t,e,i),this._audioController?e.attach(this._audioController.getOutputElement()):this._context.eventBus.emit("inner:audio:track:added",{trackId:t,participantId:i}),s.debug(`Audio track subscribed: ${t}`)}handleTrackUnsubscribed(e,t,i){this.ensureNotDisposed(),this._audioController?(this._audioController.removeOutputTrack(t),e.detach(this._audioController.getOutputElement())):this._context.eventBus.emit("inner:audio:track:removed",{trackId:t}),this._trackRegistry.unregister(t),s.debug(`Audio track unsubscribed: ${t}`)}supports(e){return e.mediaStreamTrack.kind==="audio"}onDispose(){s.info("Audio Track Handler disposed")}}class Ye extends f{constructor(e){super(),this._handlers=[],this._room=null,this._trackRegistry=e,s.info("LiveKit Event Adapter created")}setContext(e){if(super.setContext(e),this._handlers.length===0){const t=new H(this._trackRegistry),i=new B(this._trackRegistry);t.setContext(e),i.setContext(e),this._handlers.push(t,i),this._room&&(t.setRoom(this._room),i.setRoom(this._room))}}setRoom(e){this._room=e,this._handlers.forEach(t=>{t.setRoom(e)}),s.info("LiveKit room set:",this._room)}setVideoController(e){const t=this._handlers.find(i=>i instanceof H);t&&t.setVideoController(e)}setAudioController(e){const t=this._handlers.find(i=>i instanceof B);t&&t.setAudioController(e)}handleTrackSubscribed(e,t,i){this.ensureNotDisposed();const n=this._findHandler(e);n?n.handleTrackSubscribed(e,t,i):s.warn(`No handler found for track, trackId: ${t}`)}handleTrackUnsubscribed(e,t){this.ensureNotDisposed();const i=this._trackRegistry.getTrack(e);if(i){const n=this._findHandler(i);n?n.handleTrackUnsubscribed(i,e,t):(this._trackRegistry.unregister(e),s.warn(`No handler found for track, trackId: ${e}`))}}_findHandler(e){return this._handlers.find(t=>t.supports(e))||null}onDispose(){this._handlers.forEach(e=>{try{e.dispose()}catch(t){s.error("Error disposing track handler:",t)}}),this._handlers.length=0,s.info("LiveKit Event Adapter disposed")}}class Je extends f{constructor(){super(),this._room=null,this._rtcRoom=null,this._micTrack=null,this._cameraVideoTrack=null,this._protocolDispatch=null,this._maxAttempts=y,this._maxDelay=w*I,this._speakingHandlers=new Map,this._trackRegistry=new Xe,this._eventAdapter=new Ye(this._trackRegistry),s.info("LiveKit Service created")}setContext(e){super.setContext(e),this._maxAttempts=e.options.reconnect?.maxAttempts||y,this._maxDelay=(e.options.reconnect?.delay??w)*I,this._eventAdapter.setContext(e)}async connect(e,t,i,n){if(this.ensureNotDisposed(),this._rtcRoom&&this._rtcRoom.state===p.ConnectionState.Connected){s.warn("Already connected to LiveKit room");return}let r;try{await this._tearDownStaleRoom(),s.info(`Connecting to LiveKit room: ${i} at ${e}`),r=new p.Room({reconnectPolicy:{nextRetryDelayInMs:a=>a.elapsedMs<this._maxDelay?Math.min(I*Math.pow(2,a.retryCount),this._maxDelay):null}}),r.prepareConnection(e,t),this._registerRoomListeners(r),s.info("Connecting to LiveKit room..."),await r.connect(e,t,{autoSubscribe:!0,maxRetries:this._context.options.reconnect?.maxAttempts||y}),this._eventAdapter.setRoom(r),this._rtcRoom=r,this._room=r}catch(a){if(r){try{r.removeAllListeners()}catch{}try{await r.disconnect()}catch{}}throw this._rtcRoom=null,this._room=null,l.fromError(a,h.LIVEKIT_CONNECT_FAILED)}}_registerRoomListeners(e){e.on(p.RoomEvent.TrackSubscribed,(t,i,n)=>{this._handleTrackSubscribed(t,i,n)}),e.on(p.RoomEvent.TrackUnsubscribed,(t,i,n)=>{this._handleTrackUnsubscribed(t,i,n)}),e.on(p.RoomEvent.TrackMuted,(t,i)=>{this._handleTrackMuted(t,i)}),e.on(p.RoomEvent.TrackUnmuted,(t,i)=>{this._handleTrackUnmuted(t,i)}),e.on(p.RoomEvent.Connected,()=>{s.info("Connected to LiveKit room"),this._context.eventBus.emit("inner:rtc:connected",void 0)}),e.on(p.RoomEvent.Disconnected,t=>{t?s.info(`LiveKit room disconnected: ${t}`):s.info("LiveKit room disconnected"),t!==p.DisconnectReason.CLIENT_INITIATED&&(this._context.eventBus.emit("inner:rtc:disconnected",{reason:t?String(t):void 0}),this._context.emitError(new l(h.LIVEKIT_CONNECT_FAILED,t?`LiveKit disconnected: ${String(t)}`:"LiveKit disconnected"),h.LIVEKIT_CONNECT_FAILED)),this._micTrack?.stop(),this._micTrack=null,this._cameraVideoTrack?.stop(),this._cameraVideoTrack=null,this._clearSpeakingHandlers(),this._context.mediaAudioCaptureState.end("rtc"),this._room=null,this._rtcRoom=null}),e.on(p.RoomEvent.DataReceived,t=>{this._onRoomDataReceived(t)})}async _tearDownStaleRoom(){if(!this._rtcRoom)return;await this.stopMicrophone(),await this.stopCamera(),this._clearSpeakingHandlers();const e=this._rtcRoom;this._rtcRoom=null,this._room=null;try{e.removeAllListeners()}catch{}try{await e.disconnect()}catch{}}_clearSpeakingHandlers(){if(!this._rtcRoom){this._speakingHandlers.clear();return}for(const[e,t]of this._speakingHandlers.entries()){const i=this._findRemoteParticipantByIdentity(e);try{i?.off(p.ParticipantEvent.IsSpeakingChanged,t)}catch{}}this._speakingHandlers.clear()}_findRemoteParticipantByIdentity(e){const t=this._rtcRoom;if(t){for(const i of t.remoteParticipants.values())if(i.identity===e)return i}}_removeSpeakingListenerIfNoAudioTracks(e){const t=this._speakingHandlers.get(e);if(!t)return;const i=this._trackRegistry.getParticipantTracks(e);for(const r of i)if(this._trackRegistry.getTrack(r)?.kind==="audio")return;const n=this._findRemoteParticipantByIdentity(e);try{n?.off(p.ParticipantEvent.IsSpeakingChanged,t)}catch{}this._speakingHandlers.delete(e)}async startMicrophone(){this.ensureNotDisposed();const e=this._rtcRoom;if(!e||!this.isConnected())throw new l(h.LIVEKIT_CONNECT_FAILED,"LiveKit is not connected");if(!this._context.mediaAudioCaptureState.tryBegin("rtc"))throw new l(h.SDK_INVALID_STATE_TRANSITION,"WebSocket audio capture is active");try{if(this._micTrack){s.warn("Microphone already published");return}const t=await p.createLocalAudioTrack(this._context.options.audio?.input??{});await e.localParticipant.publishTrack(t),this._micTrack=t,s.info("Microphone published (rtc)")}catch(t){throw this._context.mediaAudioCaptureState.end("rtc"),this._context.emitError(t,h.AUDIO_CAPTURE_START_FAILED)}}async stopMicrophone(){const e=this._rtcRoom,t=this._micTrack;if(t&&e)try{await e.localParticipant.unpublishTrack(t,!0)}catch(i){throw this._context.emitError(i,h.LIVEKIT_UNPUBLISH_MICROPHONE_FAILED)}this._micTrack?.stop(),this._micTrack=null,this._context.mediaAudioCaptureState.end("rtc"),s.info("Microphone stopped (rtc)")}isMicrophoneActive(){return!!this._micTrack}async sendTextData(e,t,i,n){this.ensureNotDisposed();const r=this._rtcRoom;if(!r||!this.isConnected())throw new l(h.LIVEKIT_CONNECT_FAILED,"LiveKit is not connected");try{const a={event:i,requestId:e,data:{...n,text:t}},c=new TextEncoder().encode(JSON.stringify(a));await r.localParticipant.publishData(c,{reliable:!0}),s.debug("Text message sent via RTC data:",e)}catch(a){throw this._context.emitError(a,h.LIVEKIT_SEND_TEXT_DATA_FAILED)}}async sendSystemEvent(e,t){this.ensureNotDisposed();const i=this._rtcRoom;if(!i||!this.isConnected())throw new l(h.LIVEKIT_CONNECT_FAILED,"LiveKit is not connected");try{const n={event:e,data:t??{}},r=new TextEncoder().encode(JSON.stringify(n));await i.localParticipant.publishData(r,{reliable:!0}),s.debug("System event :",e,"sent via RTC data")}catch(n){throw this._context.emitError(n,h.LIVEKIT_SEND_TEXT_DATA_FAILED)}}setProtocolMessageDispatcher(e){this._protocolDispatch=e}async startCamera(){this.ensureNotDisposed();const e=this._rtcRoom;if(!e||!this.isConnected())throw new l(h.LIVEKIT_CONNECT_FAILED,"LiveKit is not connected");if(this._cameraVideoTrack){s.warn("RTC camera already published");return}try{const t=await p.createLocalVideoTrack({});await e.localParticipant.publishTrack(t),this._cameraVideoTrack=t,s.info("Camera published (rtc)")}catch(t){throw this._context.emitError(t,h.LIVEKIT_CONNECT_FAILED)}}async stopCamera(){const e=this._rtcRoom,t=this._cameraVideoTrack;if(t&&e)try{await e.localParticipant.unpublishTrack(t,!0)}catch(i){s.error("Error unpublishing RTC camera:",i)}this._cameraVideoTrack?.stop(),this._cameraVideoTrack=null,s.info("Camera stopped (rtc)")}attachCameraTo(e){if(this.ensureNotDisposed(),!this._cameraVideoTrack)throw new l(h.LIVEKIT_CONNECT_FAILED,"Camera track is not published");this._cameraVideoTrack.attach(e)}detachCameraFrom(e){if(this.ensureNotDisposed(),!this._cameraVideoTrack)throw new l(h.LIVEKIT_CONNECT_FAILED,"Camera track is not published");this._cameraVideoTrack.detach(e)}isRtcCameraActive(){return this._cameraVideoTrack!==null}_onRoomDataReceived(e){if(!(!this._protocolDispatch||e.length===0))try{const t=new TextDecoder().decode(e),i=JSON.parse(t);if(!i||typeof i!="object")return;s.debug("RTC data message received:",t),this._protocolDispatch(i)}catch(t){s.error("RTC data message parse error:",t),this._context.emitError(t,h.LIVEKIT_DATA_MESSAGE_PARSE_ERROR)}}_handleTrackSubscribed(e,t,i){try{if(!e.mediaStreamTrack){s.warn("Track does not have MediaStreamTrack");return}if(e.kind==="audio"){const c=i.identity;if(!this._speakingHandlers.has(c)){const d=_=>{this._context.eventBus.emit("inner:audio:speaking:changed",{participantId:c,isSpeaking:_}),s.info(`Participant ${c} is speaking: ${_}`)};i.on(p.ParticipantEvent.IsSpeakingChanged,d),this._speakingHandlers.set(c,d)}}const r=t.trackSid,a=i.identity;this._eventAdapter.handleTrackSubscribed(e,r,a)}catch(n){s.error("Error handling track subscribed:",n)}}_handleTrackUnsubscribed(e,t,i){try{const n=t,r=i,a=n.trackSid,c=r.identity,d=this._trackRegistry.getTrack(a),_=d?.kind==="audio";d?.kind==="video"&&this._context.eventBus.emit("inner:rtc:video:unavailable",void 0),this._eventAdapter.handleTrackUnsubscribed(a,c),_&&this._removeSpeakingListenerIfNoAudioTracks(c)}catch(n){s.error("Error handling track unsubscribed:",n)}}_handleTrackMuted(e,t){try{e.kind==="video"&&this._context.eventBus.emit("inner:rtc:video:unavailable",void 0)}catch(i){s.error("Error handling track muted:",i)}}_handleTrackUnmuted(e,t){try{e.kind==="video"&&this._context.eventBus.emit("inner:rtc:video:available",void 0)}catch(i){s.error("Error handling track unmuted:",i)}}async disconnect(){await this.stopMicrophone(),await this.stopCamera(),this._clearSpeakingHandlers();const e=this._rtcRoom;if(e)try{try{e.removeAllListeners()}catch{}await e.disconnect(),this._room=null,this._rtcRoom=null,this._context.eventBus.emit("inner:rtc:disconnected",{reason:"Disconnected from LiveKit room"}),s.info("Disconnected from LiveKit room")}catch(t){s.error("Error disconnecting from LiveKit room:",t),this._room=null,this._rtcRoom=null}else this._room=null}isConnected(){return this._room?this._room.state===p.ConnectionState.Connected:!1}getTrackRegistry(){return this._trackRegistry}get trackRegistry(){return this._trackRegistry}getEventAdapter(){return this._eventAdapter}onDispose(){this._protocolDispatch=null,this.disconnect(),this._trackRegistry.dispose(),this._eventAdapter.dispose(),s.info("LiveKit Service disposed")}}class Ze extends f{constructor(){super(),this._conversationManager=null,s.info("ProtocolMessageDispatcher created")}setConversationManager(e){this._conversationManager=e,s.debug("Conversation manager set in ProtocolMessageDispatcher")}dispatch(e){this.ensureNotDisposed();try{if(e.event){this._handleLivekitServerResponseMessage(e);return}}catch(t){s.error("Error dispatching protocol message:",t)}}_handleLivekitServerResponseMessage(e){switch(e.event){case"response.chunk":this._conversationManager?.handleAnswerChunk(e.requestId,e.data.text,!1);break;case"response.done":this._conversationManager?.handleAnswerChunk(e.requestId,"",!0);break;case"input.asr.partial":this._conversationManager?.handleAsrTextReceived(e.requestId,e.data.text,!0,e.data.final??!1);break;case"input.asr.final":this._conversationManager?.handleAsrTextReceived(e.requestId,e.data.text,!1,!0);break;case"system.prompt":this._conversationManager?.handleServerInitiatedMessage(void 0,e.data.text);break;case"session.closing":break;case"input.voice.start":this._context.eventBus.emit("inner:conversation:voice:start",{requestId:e.requestId});break;case"input.voice.finish":this._context.eventBus.emit("inner:conversation:voice:finish",{requestId:e.requestId});break;default:s.warn("Unknown event:",e.event)}}onDispose(){this._conversationManager=null,s.info("ProtocolMessageDispatcher disposed")}}const R=["sdk:connected","sdk:disconnected","sdk:error","media:video:available","media:video:unavailable","media:video:trackAdded","media:video:trackRemoved","media:audio:trackAdded","media:audio:trackRemoved","media:audio:captureStarted","media:audio:captureStopped","media:camera:started","media:camera:stopped","media:audio:volumeChanged","media:audio:muted","media:audio:unmuted","conversation:question:sent","conversation:answer:waiting","conversation:server:message","conversation:asr:received","conversation:asr:chunk","conversation:answer:chunk","conversation:answer:completed"],q=/^[a-z]+:[a-zA-z]+(:[a-zA-Z]+)?$/;class et{constructor(e){this.inner=e,this.listeners=new Map,this.forwardedVideoTrackIds=new Set,this.livekitConnected=!1,this.httpConnected=!1,this.internalMap={"inner:sdk:error":{public:"sdk:error",sanitizer:t=>({message:t?.error?.message,code:t?.error?.code})},"inner:rtc:video:available":{public:"media:video:available"},"inner:rtc:video:unavailable":{public:"media:video:unavailable"},"inner:video:track:added":{public:"media:video:trackAdded"},"inner:video:track:removed":{public:"media:video:trackRemoved"},"inner:audio:track:added":{public:"media:audio:trackAdded"},"inner:audio:track:removed":{public:"media:audio:trackRemoved"},"inner:audio:input:started":{public:"media:audio:captureStarted"},"inner:audio:input:stopped":{public:"media:audio:captureStopped"},"inner:camera:started":{public:"media:camera:started"},"inner:camera:stopped":{public:"media:camera:stopped"},"inner:audio:volume:changed":{public:"media:audio:volumeChanged",sanitizer:t=>({volume:t?.volume})},"inner:audio:muted":{public:"media:audio:muted"},"inner:audio:unmuted":{public:"media:audio:unmuted"},"inner:conversation:question:sent":{public:"conversation:question:sent",sanitizer:t=>({questionId:t?.questionId,text:String(t?.text||"")})},"inner:conversation:answer:waiting":{public:"conversation:answer:waiting",sanitizer:t=>({questionId:t?.questionId})},"inner:conversation:server:message":{public:"conversation:server:message",sanitizer:t=>({questionId:t?.questionId,message:String(t?.message||""),type:String(t?.type||"")})},"inner:conversation:asr:received":{public:"conversation:asr:received",sanitizer:t=>({questionId:t?.questionId,text:String(t?.text||"")})},"inner:conversation:asr:chunk":{public:"conversation:asr:chunk",sanitizer:t=>({questionId:t?.questionId,text:String(t?.text||""),isComplete:t?.isComplete??!1})},"inner:conversation:answer:chunk":{public:"conversation:answer:chunk",sanitizer:t=>({questionId:t?.questionId,chunk:String(t?.chunk||"")})},"inner:conversation:answer:completed":{public:"conversation:answer:completed",sanitizer:t=>({questionId:t?.questionId,fullAnswer:String(t?.fullAnswer||"")})}},Object.keys(this.internalMap).forEach(t=>{this.inner.on(t,i=>this.handleInternalEvent(t,i))}),this.inner.on("inner:sdk:connected",t=>this.handleConnected(t,!0)),this.inner.on("inner:sdk:disconnected",t=>this.handleConnected(t,!1)),R.forEach(t=>{this.inner.on(t,i=>this.handlePublicEvent(t,i))})}publicAPI(){return{on:this.on.bind(this),off:this.off.bind(this),once:this.once.bind(this)}}on(e,t){const i=String(e);return this.assertAllowed(i),this.listeners.has(i)||this.listeners.set(i,new Set),this.listeners.get(i).add(t),()=>this.off(e,t)}once(e,t){const i=String(e);this.assertAllowed(i);const n=r=>{try{t(r)}finally{this.off(e,n)}};return this.on(e,n)}off(e,t){const i=String(e);this.assertAllowed(i);const n=this.listeners.get(i);n&&(n.delete(t),n.size===0&&this.listeners.delete(i))}handleInternalEvent(e,t){const i=this.internalMap[e];if(!i)return;const n=i.public;if(R.indexOf(n)===-1)return;if(n==="media:video:trackAdded"){const c=t?.trackId||t?.id;if(typeof c=="string"){if(this.forwardedVideoTrackIds.has(c))return;this.forwardedVideoTrackIds.add(c)}}const r=i.sanitizer?i.sanitizer(t):t;if(!q.test(n))return;const a=this.listeners.get(n);!a||a.size===0||a.forEach(c=>{try{c(r)}catch(d){s.error("PublicEventEmitter listener error",d)}})}handlePublicEvent(e,t){if(R.indexOf(e)===-1)return;const i=Object.values(this.internalMap).find(a=>a.public===e),n=i&&i.sanitizer?i.sanitizer(t):t;if(e==="media:video:trackAdded"){const a=t?.trackId||t?.id;if(typeof a=="string"){if(this.forwardedVideoTrackIds.has(a))return;this.forwardedVideoTrackIds.add(a)}}const r=this.listeners.get(e);!r||r.size===0||r.forEach(a=>{try{a(n)}catch(c){s.error("PublicEventEmitter listener error",c)}})}handleConnected(e,t){e.source==="livekit"?this.livekitConnected=t:e.source==="http"&&(this.httpConnected=t),this.maybeEmitSdkConnected(t?"connected":"disconnected")}maybeEmitSdkConnected(e){const t=e==="connected",i=t?!!this.livekitConnected:!this.livekitConnected,n=t?!!this.httpConnected:!this.httpConnected,a={livekit:i,http:n,all:i&&n},c=JSON.stringify(a);this.lastConnectedPayloadJson!==c&&(this.lastConnectedPayloadJson=c,this.inner.emit(`sdk:${e}`,a))}assertAllowed(e){if(R.indexOf(e)===-1)throw new Error(`Event "${e}" is not allowed for public consumers`);if(!q.test(e))throw new Error(`Event name "${e}" does not follow namespace:domain[:action] naming`)}}class tt extends u{constructor(e,t){super(),this._requestCounter=0,this._baseURL=e||"",this._defaultHeaders=t||{},s.info(`HTTP Service created with baseURL: ${this._baseURL||"none"}`)}setAuthToken(e){this._defaultHeaders.Authorization=`Bearer ${e}`}request(e){this.ensureNotDisposed();const t=e.requestId||`req_${Date.now()}_${++this._requestCounter}`,i=this._buildURL(e.url,e.params),n=this._mergeHeaders(e.headers),r={method:e.method||"GET",headers:n};e.data!==void 0&&(e.method||"GET")!=="GET"&&(typeof e.data=="string"?r.body=e.data:(r.body=JSON.stringify(e.data),!n["Content-Type"]&&!n["content-type"]&&(n["Content-Type"]="application/json")));const a=e.timeout||3e4,c=new AbortController;return r.signal=c.signal,new Promise((d,_)=>{const g=setTimeout(()=>c.abort(),a);fetch(i,r).then(async m=>{clearTimeout(g);const A={};m.headers.forEach((ie,se)=>{A[se]=ie});let C;const L=m.headers.get("content-type");L&&L.includes("application/json")?C=await m.json():C=await m.text();const te={requestId:t,status:m.status,headers:A,data:C,config:e};s.debug(`HTTP request succeeded: ${e.method} ${i} [${t}]`),d(te)}).catch(m=>{clearTimeout(g);const A=m&&m.name==="AbortError",C={requestId:t,status:m&&m.status||void 0,message:A?"Request aborted (timeout)":m&&m.message||"HTTP request failed",error:m,config:e};s.error(`HTTP request failed: ${e.method} ${i} [${t}]`,m),_(C)})})}get(e,t,i){return this.request({...i,url:e,method:"GET",params:t})}post(e,t,i){return this.request({...i,url:e,method:"POST",data:t})}put(e,t,i){return this.request({...i,url:e,method:"PUT",data:t})}delete(e,t){return this.request({...t,url:e,method:"DELETE"})}patch(e,t,i){return this.request({...i,url:e,method:"PATCH",data:t})}_buildURL(e,t){if(e.startsWith("http://")||e.startsWith("https://"))return this._appendParams(e,t);let i=this._baseURL;return i&&!i.endsWith("/")&&!e.startsWith("/")?i+="/":i&&i.endsWith("/")&&e.startsWith("/")&&(i=i.slice(0,-1)),i+=e,this._appendParams(i,t)}_appendParams(e,t){if(!t||Object.keys(t).length===0)return e;const i=new URLSearchParams;for(const[r,a]of Object.entries(t))i.append(r,String(a));const n=e.includes("?")?"&":"?";return`${e}${n}${i.toString()}`}_mergeHeaders(e){const t={...this._defaultHeaders};return e&&Object.assign(t,e),t}onDispose(){s.info("HTTP Service disposed")}}function it(o,e="https"){return o.includes("://")?o:`${e.replace(/:$/,"")}://${o}`}class st extends f{constructor(e,t){super(),this._httpService=new tt(e||de,t)}getAuthToken(){if(this.ensureNotDisposed(),this._context.authToken)return this._httpService?.setAuthToken(this._context.authToken),Promise.resolve();throw this._context.emitError("Auth token not found",h.SDK_AUTH_TOKEN_FAILED)}async fetchConnectionConfig(){this.ensureNotDisposed();const e=await this._httpService.request({url:"/v1/session/start",method:"POST",data:{avatarId:this._context.options.connectConfig.type==="auth"?this._context.options.connectConfig.config.avatarId:"",voice:this._context.options.connectConfig.type==="auth"&&this._context.options.connectConfig.config.avatarVoice||""},headers:this._context.options.sandbox?{"X-Env-Sandbox":"true"}:void 0}).catch(t=>{throw this._context.emitError(t,h.SDK_GET_LIVEKIT_CONFIG_FAILED)});if(e.data.code!==he||!e.data.data)throw this._context.emitError(new l(h.SDK_GET_LIVEKIT_CONFIG_FAILED,e.data.msg||"LiveKit config response invalid"),h.SDK_GET_LIVEKIT_CONFIG_FAILED);return{roomId:"",roomToken:e.data.data.userToken,livekitUrl:it(e.data.data.sfuUrl,"wss")}}onDispose(){this._httpService.dispose()}}class W extends f{constructor(e,t){super(),this._connectStartAt=null,this._hasReportedFirstFrame=!1,this._videoElement=null,this._videoFirstFrameHandler=null,this._pendingTextResponse=new Map,this._pendingTextAudioQueue=[],this._pendingTextAudioStartAt=new Map,this._pendingNoSpeechStartAt=null,this._eventUnsubscribers=[],this._reporter=e??null,this._now=t??(()=>Date.now())}setReporter(e){this._reporter=e??null}startConnectMeasurement(){this._connectStartAt=this._now(),this._hasReportedFirstFrame=!1}bindVideoElement(e){this._unbindVideoElement(),this._videoElement=e,this._videoFirstFrameHandler=this._handleVideoFirstFrame.bind(this),this._videoElement.addEventListener("loadeddata",this._videoFirstFrameHandler,{once:!1})}setContext(e){super.setContext(e),this._bindEventBus()}_bindEventBus(){this._clearEventListeners(),this._subscribeConversationMetrics(),this._subscribeAudioMetrics()}_subscribeConversationMetrics(){this._eventUnsubscribers.push(this._context.eventBus.on("inner:conversation:question:sent",({questionId:e})=>{const t=this._now();this._pendingTextResponse.set(e,t),this._pendingTextAudioQueue.push(e),this._pendingTextAudioStartAt.set(e,t)})),this._eventUnsubscribers.push(this._context.eventBus.on("inner:conversation:answer:chunk",({questionId:e})=>{this._tryCompleteTextResponse(e)})),this._eventUnsubscribers.push(this._context.eventBus.on("inner:conversation:answer:completed",({questionId:e})=>{this._tryCompleteTextResponse(e)})),this._eventUnsubscribers.push(this._context.eventBus.on("inner:conversation:server:message",({questionId:e})=>{this._tryCompleteTextResponse(e)}))}_subscribeAudioMetrics(){this._eventUnsubscribers.push(this._context.eventBus.on("inner:audio:speaking:changed",({isSpeaking:e})=>{e&&(this._tryCompleteTextToAudio(),this._tryCompleteNoSpeechToAudio())})),this._eventUnsubscribers.push(this._context.eventBus.on("inner:audio:no_speech:reported",({timestamp:e})=>{this._pendingNoSpeechStartAt===null&&(this._pendingNoSpeechStartAt=e)}))}_handleVideoFirstFrame(){this._hasReportedFirstFrame||this._connectStartAt===null||(this._hasReportedFirstFrame=!0,this._report({metric:"connect_to_first_frame_ms",durationMs:this._now()-this._connectStartAt,startedAt:this._connectStartAt,endedAt:this._now()}))}_tryCompleteTextResponse(e){const t=this._pendingTextResponse.get(e);if(t===void 0)return;this._pendingTextResponse.delete(e);const i=this._now();this._report({metric:"text_send_to_text_response_ms",durationMs:i-t,startedAt:t,endedAt:i,questionId:e})}_tryCompleteTextToAudio(){let e;for(;this._pendingTextAudioQueue.length>0;){const n=this._pendingTextAudioQueue.shift();if(n&&this._pendingTextAudioStartAt.has(n)){e=n;break}}if(!e)return;const t=this._pendingTextAudioStartAt.get(e);if(t===void 0)return;this._pendingTextAudioStartAt.delete(e);const i=this._now();this._report({metric:"text_send_to_audio_response_ms",durationMs:i-t,startedAt:t,endedAt:i,questionId:e})}_tryCompleteNoSpeechToAudio(){if(this._pendingNoSpeechStartAt===null)return;const e=this._pendingNoSpeechStartAt;this._pendingNoSpeechStartAt=null;const t=this._now();this._report({metric:"no_speech_report_to_audio_response_ms",durationMs:t-e,startedAt:e,endedAt:t})}_report(e){const t=this._reporter??this._defaultReporter;try{t(e)}catch(i){s.error("Performance metric reporter failed",i)}}_defaultReporter(e){s.info("[performance-metric]",e)}_unbindVideoElement(){this._videoElement&&this._videoFirstFrameHandler&&this._videoElement.removeEventListener("loadeddata",this._videoFirstFrameHandler),this._videoElement=null,this._videoFirstFrameHandler=null}_clearEventListeners(){this._eventUnsubscribers.forEach(e=>{try{e()}catch(t){s.error("Performance monitor unsubscribe failed",t)}}),this._eventUnsubscribers=[]}onDispose(){this._unbindVideoElement(),this._clearEventListeners(),this._pendingTextResponse.clear(),this._pendingTextAudioQueue.length=0,this._pendingTextAudioStartAt.clear(),this._pendingNoSpeechStartAt=null,this._reporter=null}}class v extends f{constructor(e,t=ue){super(),this._cachedConfig=null,this._cachedAt=null,this._inflightPromise=null,this._nextDirectConfig=null,this._httpController=e,this._ttlMs=t}async preConnect(){this.ensureNotDisposed();const e=this.getValidConfig();return e||(this._inflightPromise?this._inflightPromise:(this._inflightPromise=this._fetchConfig().finally(()=>{this._inflightPromise=null}),this._inflightPromise))}getValidConfig(){return this.ensureNotDisposed(),!this._cachedConfig||this._cachedAt===null||Date.now()-this._cachedAt>=this._ttlMs?null:this._cachedConfig}async refreshConfig(){return this.ensureNotDisposed(),this._cachedConfig=null,this._cachedAt=null,this._inflightPromise?this._inflightPromise:(this._inflightPromise=this._fetchConfig().finally(()=>{this._inflightPromise=null}),this._inflightPromise)}replaceDirectConfig(e){this.ensureNotDisposed();const t=this._validateDirectConfig(e);this._nextDirectConfig=t,this._cachedConfig=null,this._cachedAt=null}async _fetchConfig(){const e=this._context.options.connectConfig;if(e.type==="direct"){const t=this._nextDirectConfig??this._validateDirectConfig(e.config),i={livekitUrl:t.sfuUrl,token:t.userToken};return this._cache(i),this._nextDirectConfig=null,i}return this._fetchAuthConfigWithRetry(e.config.authToken||"")}_fromAuthPayload(e){if(!e.livekitUrl||!e.roomToken)throw this._context.emitError("Invalid connection config payload",h.SDK_GET_LIVEKIT_CONFIG_FAILED);return{livekitUrl:e.livekitUrl,token:e.roomToken,roomId:e.roomId,videoOptions:{renderMode:e.greenScreen?.enabled?"processed":"raw",greenScreen:{enabled:!!e.greenScreen?.enabled,...e.greenScreen}}}}_cache(e){this._cachedConfig=e,this._cachedAt=Date.now()}async _fetchAuthConfigWithRetry(e){let t=null;for(let i=1;i<=v.AUTH_RETRY_MAX_ATTEMPTS;i++)try{const n=this._context.authToken||e;this._context.setAuthToken(n),await this._httpController.getAuthToken();const r=await this._httpController.fetchConnectionConfig(),a=this._fromAuthPayload(r);return this._cache(a),a}catch(n){if(t=n,i>=v.AUTH_RETRY_MAX_ATTEMPTS)break;await this._delay(v.AUTH_RETRY_BASE_DELAY_MS*i)}throw this._context.emitError(t||"Failed to fetch auth connection config",h.SDK_GET_LIVEKIT_CONFIG_FAILED)}_delay(e){return new Promise(t=>setTimeout(t,e))}_validateDirectConfig(e){const t=(e.sfuUrl||"").trim(),i=(e.userToken||"").trim();if(!t||!i)throw this._context.emitError("Invalid direct connection config",h.SDK_PRECONNECT_FAILED);return{sfuUrl:t,userToken:i}}onDispose(){this._cachedConfig=null,this._cachedAt=null,this._inflightPromise=null,this._nextDirectConfig=null}}v.AUTH_RETRY_MAX_ATTEMPTS=2;v.AUTH_RETRY_BASE_DELAY_MS=300;class ee extends u{constructor(e){super(),this._videoController=null,this._audioController=null,this._conversationController=null,this._cameraController=null,this._liveKitService=null,this._connectionCoordinator=null,this._httpController=null,this._configManager=null,this._performanceMonitor=null,this._pendingDirectConfig=null,this._isReconnectInProgress=!1,this._reconnectPromise=null,this._liveKitProtocolDispatcher=null,this._context=new pe(e),s.info("SDK Client created")}get events(){return this.ensureNotDisposed(),this._publicEventAPI||(this._publicEmitter=new et(this._context.eventBus),this._publicEventAPI=this._publicEmitter.publicAPI()),this._publicEventAPI}sendTextQuestion(e){if(this.ensureNotDisposed(),this._ensureConnected(),this._conversationController)return this._conversationController.sendQuestion(e);throw new l(h.CONVERSATION_CONTROLLER_NOT_AVAILABLE,"Conversation controller is not available")}async interrupt(){if(this.ensureNotDisposed(),this._ensureConnected(),this._conversationController)return this._conversationController.interrupt();throw new l(h.CONVERSATION_CONTROLLER_NOT_AVAILABLE,"Conversation controller is not available")}setPerformanceMetricReporter(e){if(this.ensureNotDisposed(),!this._performanceMonitor){this._performanceMonitor=new W(e),this._performanceMonitor.setContext(this._context);return}this._performanceMonitor.setReporter(e)}setRenderFitMode(e){this.ensureNotDisposed(),this._ensureConnected(),this._videoController&&this._videoController.setRenderFitMode(e)}async startAudioCapture(){this.ensureNotDisposed(),this._ensureConnected(),await this._startAudioCaptureRtc()}async stopAudioCapture(){this.ensureNotDisposed(),this._ensureConnected(),await this._stopAudioCaptureRtc()}setVolume(e){this.ensureNotDisposed(),this._ensureConnected(),this._requireAudioController().setVolume(e)}getVolume(){return this.ensureNotDisposed(),this._ensureConnected(),this._requireAudioController().getVolume()||1}mute(){this.ensureNotDisposed(),this._ensureConnected(),this._requireAudioController().mute()}unmute(){this.ensureNotDisposed(),this._ensureConnected(),this._requireAudioController().unmute()}get isMuted(){return this.ensureNotDisposed(),this._ensureConnected(),this._requireAudioController().isMuted()}get isAudioCapturing(){return this.ensureNotDisposed(),this._ensureConnected(),this._liveKitService?.isMicrophoneActive()??!1}setAuthToken(e){this.ensureNotDisposed(),this._context.setAuthToken(e),this._httpController&&this._httpController.getAuthToken()}updateConnectionConfig(e){if(this.ensureNotDisposed(),this._context.options.connectConfig.type!=="direct")throw this._context.emitError(new l(h.SDK_INVALID_STATE_TRANSITION,"updateConnectionConfig is only available in direct mode"),h.SDK_INVALID_STATE_TRANSITION);this._pendingDirectConfig=this._validateDirectConnectionConfig(e)}async startCamera(){this.ensureNotDisposed(),this._ensureConnected(),await this._requireLiveKitService().startCamera()}stopCamera(){this.ensureNotDisposed(),this._ensureConnected(),this._liveKitService?.stopCamera()}getCameraStream(){return this.ensureNotDisposed(),this._ensureConnected(),this._cameraController?.getStream()??null}getCameraTrack(){return this.ensureNotDisposed(),this._ensureConnected(),this._cameraController?.getTrack()??null}attachCameraTo(e){this.ensureNotDisposed(),this._ensureConnected(),this._requireLiveKitService().attachCameraTo(e)}async preConnect(){this.ensureNotDisposed();try{this._ensureConfigManagerReady();const e=await this._configManager.preConnect();return this._applyConnectionConfig(e),!0}catch(e){throw this._context.emitError(e,h.SDK_PRECONNECT_FAILED)}}async connect(){if(this.ensureNotDisposed(),this._ensurePerformanceMonitorReady(),this._performanceMonitor?.startConnectMeasurement(),this._connectionCoordinator&&this._context.sessionState.isConnected){s.warn("SDK already initialized");return}try{this._ensureConfigManagerReady();const e=this._configManager.getValidConfig();if(e)this._applyConnectionConfig(e);else if(!await this.preConnect())throw new l(h.SDK_CONNECT_FAILED,"PreConnect failed without explicit error")}catch(e){throw this._context.emitError(e,h.SDK_CONNECT_FAILED)}try{await this._disposeConnectionRuntimeBeforeConnect(),s.info("Initializing SDK..."),this._connectionCoordinator=new fe,this._connectionCoordinator.setContext(this._context),this._context.bindConnectionState(()=>this._connectionCoordinator.getState()),this._connectionCoordinator.start(),this._connectionCoordinator.startConnecting(),this._bootstrapDomainControllers(),this._bootstrapLiveKitService(),this._wireLiveKitEventAdapterToControllers(),this._setupRtcProtocolDispatcher(),await this._establishMediaAndSessionTransports(),s.info("SDK initialized and connected")}catch(e){throw this._connectionCoordinator&&this._connectionCoordinator.transitionToIdle(),await this._cleanup(),this._context.emitError(e,h.SDK_INITIALIZATION_FAILED)}}async disconnect(){if(this.ensureNotDisposed(),!(!this._connectionCoordinator||this._connectionCoordinator.getState()==="idle"||this._connectionCoordinator.getState()==="disposed")){s.info("Disconnecting SDK..."),this._connectionCoordinator.startDisconnecting();try{this._audioController&&this._audioController.stopWsCapture(),this._liveKitService&&(await this._liveKitService.sendSystemEvent("session.stop"),await this._liveKitService.disconnect()),this._cameraController&&this._cameraController.stopCamera(),this._connectionCoordinator.completeDisconnecting(),s.info("SDK disconnected")}catch(e){throw s.error("Error during disconnect:",e),this._context.emitError(e,h.SDK_DISCONNECT_FAILED)}}}async reconnect(){if(this.ensureNotDisposed(),this._reconnectPromise)return this._reconnectPromise;const e=this._connectionCoordinator;if(!e?.canStartReconnectingManual()){const i=e?e.getSnapshot():this.connectionSnapshot;return s.warn("SDK reconnect not allowed in current state, returning current snapshot"),i}const t=e.captureSnapshotAndStartReconnectingManual();return this._reconnectPromise=(async()=>{this._isReconnectInProgress=!0;try{return await this.disconnect(),this._stageDirectConfigForNextReconnect(),await this._refreshConnectionConfigForReconnect(),await this.connect(),t}catch(i){throw s.error("Error during reconnect:",i),this._context.emitError(i,h.SDK_RECONNECT_FAILED)}finally{this._isReconnectInProgress=!1,this._reconnectPromise=null}})(),this._reconnectPromise}get connectionSnapshot(){return this.ensureNotDisposed(),this._connectionCoordinator?this._connectionCoordinator.getSnapshot():Q(j("idle"))}async _cleanup(){if(this._conversationController?.setRtcTextSender(null),this._liveKitService?.setProtocolMessageDispatcher(null),this._liveKitProtocolDispatcher&&(this._liveKitProtocolDispatcher.dispose(),this._liveKitProtocolDispatcher=null),this._audioController&&this._audioController.stopWsCapture(),this._liveKitService)try{await this._liveKitService.disconnect()}catch(e){s.error("Error disconnecting LiveKit:",e)}this._cameraController&&this._cameraController.stopCamera()}async _disposeConnectionRuntimeBeforeConnect(){await this._cleanup(),this._conversationController?.setRtcTextSender(null),this._liveKitService?.setProtocolMessageDispatcher(null),this._liveKitProtocolDispatcher&&(this._liveKitProtocolDispatcher.dispose(),this._liveKitProtocolDispatcher=null),this._videoController?.dispose(),this._videoController=null,this._audioController?.dispose(),this._audioController=null,this._cameraController?.dispose(),this._cameraController=null,this._conversationController?.dispose(),this._conversationController=null,this._liveKitService?.dispose(),this._liveKitService=null,this._connectionCoordinator?.dispose(),this._connectionCoordinator=null}_bootstrapDomainControllers(){this._videoController=new Se(this._context.options.video),this._videoController.setContext(this._context),this._performanceMonitor?.bindVideoElement(this._videoController.getSource().getInternalElement()),this._audioController=new Ne(this._context.options.audio),this._audioController.setContext(this._context),this._cameraController=new We,this._cameraController.setContext(this._context),this._conversationController=new Qe,this._conversationController.setContext(this._context)}_bootstrapLiveKitService(){this._liveKitService=new Je,this._liveKitService.setContext(this._context)}_wireLiveKitEventAdapterToControllers(){if(this._liveKitService&&this._videoController&&this._audioController){const e=this._liveKitService.getEventAdapter();e&&(typeof e.setVideoController=="function"&&e.setVideoController(this._videoController),typeof e.setAudioController=="function"&&e.setAudioController(this._audioController))}}_setupRtcProtocolDispatcher(){this._liveKitProtocolDispatcher&&(this._liveKitProtocolDispatcher.dispose(),this._liveKitProtocolDispatcher=null),this._liveKitProtocolDispatcher=new Ze,this._liveKitProtocolDispatcher.setContext(this._context),this._liveKitProtocolDispatcher.setConversationManager(this._conversationController.manager),this._liveKitService.setProtocolMessageDispatcher(e=>{this._liveKitProtocolDispatcher.dispatch(e)}),this._conversationController.setRtcTextSender(this._liveKitService)}async _establishMediaAndSessionTransports(){await this._liveKitService.connect(this._context.livekitUrl,this._context.token,this._context.options.connectConfig.type==="auth"?this._context.options.connectConfig.config.avatarId:"")}async _refreshConnectionConfigForReconnect(){this._ensureConfigManagerReady();const e=await this._configManager.refreshConfig();this._applyConnectionConfig(e)}_ensureConfigManagerReady(){s.info("Creating HTTP controller..."),this._httpController=this._httpController??new st(this._context.options.http?.baseURL,this._context.options.http?.headers),this._httpController.setContext(this._context),this._configManager=this._configManager??new v(this._httpController),this._configManager.setContext(this._context)}_applyConnectionConfig(e){this._context.setLivekitConfig(e.livekitUrl,e.token,e.roomId||""),e.videoOptions&&this._context.setVideoOptions(e.videoOptions),this._context.options.connectConfig.type==="auth"&&this._context.eventBus.emit("inner:sdk:connected",{source:"http"})}_stageDirectConfigForNextReconnect(){this._context.options.connectConfig.type==="direct"&&this._pendingDirectConfig&&(this._ensureConfigManagerReady(),this._configManager.replaceDirectConfig(this._pendingDirectConfig),this._pendingDirectConfig=null)}_validateDirectConnectionConfig(e){const t=(e?.sfuUrl||"").trim(),i=(e?.userToken||"").trim();if(!t||!i)throw this._context.emitError(new l(h.SDK_PRECONNECT_FAILED,"Invalid direct connection config"),h.SDK_PRECONNECT_FAILED);return{sfuUrl:t,userToken:i}}_ensurePerformanceMonitorReady(){const e=this._context.options.performanceMonitor;if(!(e?.enabled!==!1)){this._performanceMonitor?.dispose(),this._performanceMonitor=null;return}if(this._performanceMonitor){this._performanceMonitor.setReporter(e?.reporter);return}this._performanceMonitor=new W(e?.reporter),this._performanceMonitor.setContext(this._context)}get isConnected(){return this.ensureNotDisposed(),this._context.sessionState.isConnected}_ensureConnected(){if(!this._context.sessionState.isConnected)throw new l(h.SDK_NOT_CONNECTED,"SDK is not connected. Call connect() first.")}async _startAudioCaptureRtc(){await this._requireLiveKitService().startMicrophone()}async _stopAudioCaptureRtc(){await this._requireLiveKitService().stopMicrophone()}_requireAudioController(){if(!this._audioController)throw new l(h.AUDIO_CONTROLLER_NOT_AVAILABLE,"Audio controller is not available");return this._audioController}_requireLiveKitService(){if(!this._liveKitService)throw new l(h.LIVEKIT_CONNECT_FAILED,"LiveKit service is not available");return this._liveKitService}onDispose(){this.disconnect().catch(e=>{s.error("Error during disconnect in dispose:",e)}),this._videoController&&(this._videoController.dispose(),this._videoController=null),this._audioController&&(this._audioController.dispose(),this._audioController=null),this._cameraController&&(this._cameraController.dispose(),this._cameraController=null),this._conversationController?.setRtcTextSender(null),this._liveKitService?.setProtocolMessageDispatcher(null),this._liveKitProtocolDispatcher&&(this._liveKitProtocolDispatcher.dispose(),this._liveKitProtocolDispatcher=null),this._conversationController&&(this._conversationController.dispose(),this._conversationController=null),this._liveKitService&&(this._liveKitService.dispose(),this._liveKitService=null),this._httpController&&(this._httpController.dispose(),this._httpController=null),this._configManager&&(this._configManager.dispose(),this._configManager=null),this._performanceMonitor&&(this._performanceMonitor.dispose(),this._performanceMonitor=null),this._connectionCoordinator&&(this._connectionCoordinator.transitionToDisposed(),this._connectionCoordinator.dispose(),this._connectionCoordinator=null),this._context.dispose(),s.info("SDK Client disposed")}}function nt(o){const e=o.connectConfig;if(e.type==="direct"&&(!e.config.sfuUrl||!e.config.userToken))throw new l(h.INVALID_CONNECT_CONFIG,"Invalid direct connect config");if(e.type==="auth"&&!e.config.avatarId)throw new l(h.NO_AVATARID,"avatarId is required");return new ee(o)}class G extends u{constructor(e,t){super(),this._videoElement=null,this._videoUrl=null,this._isPlaying=!1,this._frameCallbackId=null,this._videoUrl=e,t?this._videoElement=t:(this._videoElement=document.createElement("video"),this._videoElement.style.opacity="0",this._videoElement.muted=!0,this._videoElement.playsInline=!0,this._videoElement.loop=!0,document.querySelector("#video-container")?.appendChild(this._videoElement)),s.info("Local Video Debug Source created")}get videoElement(){return this._videoElement}async load(){if(!this._videoElement)throw new Error("Video element or URL not set");return new Promise((e,t)=>{const i=this._videoElement,n=()=>{i.removeEventListener("loadedmetadata",n),i.removeEventListener("error",r),s.info(`Video loaded: ${i.videoWidth}x${i.videoHeight}`),e()},r=a=>{i.removeEventListener("loadedmetadata",n),i.removeEventListener("error",r),t(new Error(`Failed to load video: ${a}`))};i.addEventListener("loadedmetadata",n),i.addEventListener("error",r),i.src=this._videoUrl||"",i.load()})}async play(){if(!this._videoElement)throw new Error("Video element not set");if(!this._isPlaying)try{await this._videoElement.play(),this._videoElement.style.opacity="0",this._videoElement.setAttribute("loop","true"),this._isPlaying=!0,s.info("Video playback started")}catch(e){throw s.error("Failed to play video:",e),e}}pause(){this._videoElement&&this._isPlaying&&(this._videoElement.pause(),this._isPlaying=!1,s.info("Video playback paused"))}stop(){this._videoElement&&(this._videoElement.pause(),this._videoElement.currentTime=0,this._isPlaying=!1,s.info("Video playback stopped"))}get isPlaying(){return this._isPlaying&&this._videoElement!==null&&!this._videoElement.paused}get videoWidth(){return this._videoElement?.videoWidth||0}get videoHeight(){return this._videoElement?.videoHeight||0}async captureFrame(){if(!this._videoElement||this._videoElement.readyState<2)return null;try{if("VideoFrame"in window&&typeof window.VideoFrame=="function")try{return new VideoFrame(this._videoElement,{timestamp:this._videoElement.currentTime*1e6})}catch(i){s.error("",i)}const e=await createImageBitmap(this._videoElement),t=new VideoFrame(e,{timestamp:this._videoElement.currentTime*1e6});return e.close(),t}catch(e){return s.error("Failed to capture video frame:",e),null}}onFrame(e){if(!this._videoElement)return;this.offFrame();const t=this._videoElement;if("requestVideoFrameCallback"in t){const i=(n,r)=>{!this._isPlaying||!this._videoElement||this.captureFrame().then(a=>{a&&(e(a),a.close()),this._isPlaying&&this._videoElement&&(this._frameCallbackId=t.requestVideoFrameCallback(i))}).catch(a=>{s.error("Error in frame callback:",a)})};this._frameCallbackId=t.requestVideoFrameCallback(i)}else{const i=()=>{!this._isPlaying||!this._videoElement||this.captureFrame().then(n=>{n&&(e(n),n.close()),this._isPlaying&&this._videoElement&&(this._frameCallbackId=requestAnimationFrame(i))}).catch(n=>{s.error("Error in frame callback:",n)})};this._frameCallbackId=requestAnimationFrame(i)}}offFrame(){this._frameCallbackId!==null&&(this._videoElement&&"cancelVideoFrameCallback"in this._videoElement?this._videoElement.cancelVideoFrameCallback(this._frameCallbackId):cancelAnimationFrame(this._frameCallbackId),this._frameCallbackId=null)}onDispose(){this.stop(),this.offFrame(),this._videoElement&&this._videoElement.parentElement===document.body&&document.body.removeChild(this._videoElement),this._videoElement=null,this._videoUrl=null,s.info("Local Video Debug Source disposed")}}class rt extends u{constructor(e,t,i){super(),this._outputCanvas=null,this._outputRenderer=null,this._source=new Y;const n={...e,renderMode:"processed"};this._pipeline=new Z(this._source,n),this._outputRenderer=new N(i,t),this._pipeline.setRenderer(this._outputRenderer),this._outputCanvas=this._outputRenderer.getCanvas(),s.info("Video Pipeline Runner created")}get outputCanvas(){return this._outputCanvas}get processor(){return this._pipeline.strategy.getProcessors()[0]||null}setProcessor(e){this._pipeline.setProcessor(e),s.info("Processor set on pipeline")}async processFrame(e){if(this.ensureNotDisposed(),!this._outputRenderer){s.error("Output renderer not available"),e.close();return}try{const t=this._pipeline.strategy.getProcessors()[0]||null;let i=null;if(t){try{i=t.process(e)}catch(n){s.error("Error processing frame:",n),i=e}if(i===null&&"getImageData"in t){const n=t.getImageData();if(n&&this._outputCanvas&&this._outputRenderer.getContext()){const r=this._outputRenderer.getContext();r&&((this._outputCanvas.width!==n.width||this._outputCanvas.height!==n.height)&&(this._outputCanvas.width=n.width,this._outputCanvas.height=n.height),r.putImageData(n,0,0))}e.close();return}i&&i!==e?(this._outputRenderer.render(i),e.close()):i===e?this._outputRenderer.render(e):e.close()}else this._outputRenderer.render(e)}catch(t){s.error("Error in processFrame:",t),e.close()}}setOutputSize(e,t){this._outputCanvas&&(this._outputCanvas.width=e,this._outputCanvas.height=t,s.info(`Output canvas size set to ${e}x${t}`))}onDispose(){this._pipeline.dispose(),this._outputRenderer&&(this._outputRenderer.dispose(),this._outputRenderer=null),this._outputCanvas&&this._outputCanvas.parentElement===document.body&&document.body.removeChild(this._outputCanvas),this._outputCanvas=null,s.info("Video Pipeline Runner disposed")}}class ot extends u{constructor(e,t,i,n){if(super(),this._isRunning=!1,this._videoSource=new G(e,t),this._options={renderMode:"processed",containerElement:t,...n},this._pipelineRunner=new rt(this._options,t,i),this._options.greenScreen?.enabled){const r=new x(this._options.greenScreen);this._pipelineRunner.setProcessor(r),s.info("Green screen processor enabled in debugger")}s.info("Standalone Video Debugger created")}async switchSource(e,t){this.ensureNotDisposed();const i=this._videoSource,n=this._isRunning,r=new G(e,t);try{await r.load(),n&&await r.play(),r.onFrame(d=>{this._pipelineRunner.processFrame(d).catch(_=>{s.error("Error processing frame:",_)})}),this._videoSource=r;try{i.offFrame(),i.stop(),i.dispose()}catch(d){s.warn("Error disposing old video source during switch:",d)}const a=this._videoSource.videoWidth,c=this._videoSource.videoHeight;a>0&&c>0&&this._pipelineRunner.setOutputSize(a,c)}catch(a){s.error("Failed to switch video source:",a);try{r.dispose()}catch(c){s.warn("Error disposing new video source after failed switch:",c)}throw a}}get outputCanvas(){return this._pipelineRunner.outputCanvas}get videoSource(){return this._videoSource}get processor(){return this._pipelineRunner.processor}setProcessor(e){this._pipelineRunner.setProcessor(e)}updateGreenScreenOptions(e){const t=this._pipelineRunner.processor;if(t&&"updateOptions"in t&&typeof t.updateOptions=="function")t.updateOptions(e),s.info("Green screen options updated");else{const i=this._options.greenScreen||{enabled:!1},n={...i,...e,enabled:e.enabled!==void 0?e.enabled:i.enabled},r=new x(n);this._pipelineRunner.setProcessor(r),this._options.greenScreen=n,s.info("Green screen processor recreated with new options")}}async initialize(){this.ensureNotDisposed();try{await this._videoSource.load();const e=this._videoSource.videoWidth,t=this._videoSource.videoHeight;e>0&&t>0&&this._pipelineRunner.setOutputSize(e,t),s.info("Video debugger initialized")}catch(e){throw s.error("Failed to initialize video debugger:",e),e}}async start(){if(this.ensureNotDisposed(),this._isRunning){s.warn("Debugger is already running");return}try{this._videoSource.videoWidth===0&&await this.initialize(),await this._videoSource.play(),this._videoSource.onFrame(e=>{this._pipelineRunner.processFrame(e).catch(t=>{s.error("Error processing frame:",t)})}),this._isRunning=!0,s.info("Video debugger started")}catch(e){throw s.error("Failed to start video debugger:",e),e}}stop(){this._isRunning&&(this._videoSource.stop(),this._videoSource.offFrame(),this._isRunning=!1,s.info("Video debugger stopped"))}pause(){this._isRunning&&(this._videoSource.pause(),s.info("Video debugger paused"))}async resume(){this._isRunning&&(await this._videoSource.play(),s.info("Video debugger resumed"))}get isRunning(){return this._isRunning}onDispose(){this.stop(),this._videoSource.dispose(),this._pipelineRunner.dispose(),s.info("Standalone Video Debugger disposed")}}exports.DEFAULT_AUDIO_BUFFER_SIZE=ce;exports.DEFAULT_AUDIO_CHANNELS=ae;exports.DEFAULT_AUDIO_SAMPLE_RATE=k;exports.DEFAULT_RECONNECT_ATTEMPTS=y;exports.DEFAULT_RECONNECT_DELAY=w;exports.DEFAULT_WS_TIMEOUT=oe;exports.Disposable=u;exports.EventBus=$;exports.Logger=s;exports.SDKClient=ee;exports.SDKError=l;exports.SessionState=z;exports.StandaloneVideoDebugger=ot;exports.createClient=nt;
package/dist/index.d.ts CHANGED
@@ -793,7 +793,7 @@ interface ClientOptions extends BaseOptions {
793
793
  type: 'direct';
794
794
  config: {
795
795
  sfuUrl: string;
796
- clientToken: string;
796
+ userToken: string;
797
797
  };
798
798
  }
799
799
  | {
@@ -1051,7 +1051,7 @@ interface PublicEmitterAPI {
1051
1051
 
1052
1052
  interface DirectConnectionConfig {
1053
1053
  sfuUrl: string;
1054
- clientToken: string;
1054
+ userToken: string;
1055
1055
  }
1056
1056
 
1057
1057
  /**