@xmov/avatar 2.4.0-beta.1 → 2.4.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- import e from"@xmov/avatar";function t(){return t=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var i in n)({}).hasOwnProperty.call(n,i)&&(e[i]=n[i])}return e},t.apply(null,arguments)}var n,i,s,r;!function(e){e[e.CONTAINER_NOT_FOUND=10001]="CONTAINER_NOT_FOUND",e[e.CONNECT_SOCKET_ERROR=10002]="CONNECT_SOCKET_ERROR",e[e.START_SESSION_ERROR=10003]="START_SESSION_ERROR",e[e.STOP_SESSION_ERROR=10004]="STOP_SESSION_ERROR",e[e.INIT_ERROR=10005]="INIT_ERROR",e[e.SOCKET_QUIT=10006]="SOCKET_QUIT",e[e.SOCKET_DISCONNECT=10007]="SOCKET_DISCONNECT",e[e.VIDEO_FRAME_EXTRACT_ERROR=20001]="VIDEO_FRAME_EXTRACT_ERROR",e[e.INIT_WORKER_ERROR=20002]="INIT_WORKER_ERROR",e[e.PROCESS_VIDEO_STREAM_ERROR=20003]="PROCESS_VIDEO_STREAM_ERROR",e[e.FACE_PROCESSING_ERROR=20004]="FACE_PROCESSING_ERROR",e[e.RENDER_BODY_ERROR=20005]="RENDER_BODY_ERROR",e[e.RENDER_FACE_ERROR=20006]="RENDER_FACE_ERROR",e[e.WebGL_RENDER_ERROR=20007]="WebGL_RENDER_ERROR",e[e.RENDER_DATA_MISSING=20008]="RENDER_DATA_MISSING",e[e.RENDER_FACE_DATA_MISSING=20009]="RENDER_FACE_DATA_MISSING",e[e.EVENT_WITHOUT_SPEECH_ID=20010]="EVENT_WITHOUT_SPEECH_ID",e[e.EVENT_TRIGGERED=20011]="EVENT_TRIGGERED",e[e.BACKGROUND_IMAGE_LOAD_ERROR=30001]="BACKGROUND_IMAGE_LOAD_ERROR",e[e.FACE_BIN_LOAD_ERROR=30002]="FACE_BIN_LOAD_ERROR",e[e.INVALID_BODY_NAME=30003]="INVALID_BODY_NAME",e[e.VIDEO_DOWNLOAD_ERROR=30004]="VIDEO_DOWNLOAD_ERROR",e[e.BODY_DATA_EXPIRED=30005]="BODY_DATA_EXPIRED",e[e.EVENT_SPEAK_START_EXPIRED=30006]="EVENT_SPEAK_START_EXPIRED",e[e.EVENT_VOICE_START_EXPIRED=30007]="EVENT_VOICE_START_EXPIRED",e[e.AUDIO_DECODE_ERROR=40001]="AUDIO_DECODE_ERROR",e[e.FACE_DECODE_ERROR=40002]="FACE_DECODE_ERROR",e[e.VIDEO_DECODE_ERROR=40003]="VIDEO_DECODE_ERROR",e[e.EVENT_DECODE_ERROR=40004]="EVENT_DECODE_ERROR",e[e.INVALID_DATA_STRUCTURE=40005]="INVALID_DATA_STRUCTURE",e[e.TTSA_ERROR=40006]="TTSA_ERROR",e[e.AUDIO_DATA_EXPIRED=40007]="AUDIO_DATA_EXPIRED",e[e.NETWORK_DOWN=50001]="NETWORK_DOWN",e[e.NETWORK_UP=50002]="NETWORK_UP",e[e.NETWORK_RETRY=50003]="NETWORK_RETRY",e[e.NETWORK_BREAK=50004]="NETWORK_BREAK",e[e.WEBGL_CONTEXT_LOST=60001]="WEBGL_CONTEXT_LOST",e[e.WEBGL_CONTEXT_RESTORED=60002]="WEBGL_CONTEXT_RESTORED",e[e.WEBGL_CONTEXT_RESTORE_TIMEOUT=60003]="WEBGL_CONTEXT_RESTORE_TIMEOUT",e[e.WEBGL_CONTEXT_RECOVERED=60004]="WEBGL_CONTEXT_RECOVERED",e[e.WEBGL_CONTEXT_RECOVERY_FAILED=60005]="WEBGL_CONTEXT_RECOVERY_FAILED",e[e.WEBGL_CONTEXT_RECOVERY_MAX_ATTEMPTS=60006]="WEBGL_CONTEXT_RECOVERY_MAX_ATTEMPTS",e[e.WEBGL_RESOLUTION_RESTORED=60007]="WEBGL_RESOLUTION_RESTORED",e[e.WEBGL_CONTEXT_MANUAL_RESTARTED=60008]="WEBGL_CONTEXT_MANUAL_RESTARTED"}(n||(n={})),function(e){e[e.online=0]="online",e[e.offline=1]="offline",e[e.network_on=2]="network_on",e[e.network_off=3]="network_off",e[e.close=4]="close",e[e.visible=5]="visible",e[e.invisible=6]="invisible",e[e.stopped=7]="stopped"}(i||(i={})),function(e){e.init="init",e.rendering="rendering",e.pausing="pausing",e.paused="paused",e.resumed="resumed",e.stopped="stopped"}(s||(s={})),function(e){e.normal="normal",e.invisible="invisible"}(r||(r={}));const a=2097152;class o{constructor(e){var t;this.ws=null,this.connectPromise=null,this.rejectConnect=null,this.options=e,this.audioInputEnabled=null==(t=e.audioInputEnabled)||t;const n=e.WebSocketCtor||globalThis.WebSocket;if(!n)throw new Error("当前环境不支持 WebSocket");this.WebSocketCtor=n}get isOpen(){var e;return(null==(e=this.ws)?void 0:e.readyState)===this.WebSocketCtor.OPEN}connect(){var e,t;if(this.isOpen)return Promise.resolve();if(this.connectPromise)return this.connectPromise;let n;return null==(e=(t=this.options).onStateChange)||e.call(t,"connecting"),n=new Promise((e,t)=>{this.rejectConnect=t;let i=!1;const s=new this.WebSocketCtor(this.buildWsUrl(this.options.wsUrl,this.options.token));this.ws=s,s.onopen=()=>{var t,r;this.ws===s&&(i=!0,this.connectPromise===n&&(this.connectPromise=null),this.rejectConnect=null,null==(t=(r=this.options).onStateChange)||t.call(r,"open"),e())},s.onmessage=e=>{if(this.ws!==s)return;const t=this.parseMessage(e.data);var n,i;t&&(null==(n=(i=this.options).onEvent)||n.call(i,t))},s.onerror=()=>{var e,r,a,o;if(this.ws!==s)return;const l=new Error("E2E WebSocket 连接异常");i||(i=!0,this.connectPromise===n&&(this.connectPromise=null),this.rejectConnect=null,t(l)),null==(e=(r=this.options).onStateChange)||e.call(r,"error"),null==(a=(o=this.options).onError)||a.call(o,l)},s.onclose=e=>{var r,a,o,l;this.ws===s&&(this.ws=null,i||(i=!0,this.connectPromise===n&&(this.connectPromise=null),this.rejectConnect=null,t(new Error(`E2E WebSocket 已关闭: ${e.code} ${e.reason||""}`.trim()))),null==(r=(a=this.options).onStateChange)||r.call(a,"closed"),null==(o=(l=this.options).onClose)||o.call(l,e))}}),this.connectPromise=n,this.connectPromise}disconnect(e=1e3,t="client_close"){const n=this.detachSocket();n&&this.closeSocket(n,e,t)}disconnectAndWait(e=1e3,t="client_close",n=3e3){const i=this.ws;return i?(this.cancelPendingConnection(),new Promise(s=>{const r=i.onclose;let a,o=!1;const l=e=>{o||(o=!0,void 0!==a&&globalThis.clearTimeout(a),this.ws===i&&(this.ws=null),i.onclose=r,s(e))};i.onclose=e=>{try{null==r||r.call(i,e)}finally{l(!0)}},a=globalThis.setTimeout(()=>l(!1),Math.max(0,n));try{this.closeSocket(i,e,t)}catch(e){l(!1)}})):Promise.resolve(!0)}detachSocket(){if(!this.ws)return null;const e=this.ws;return this.ws=null,this.cancelPendingConnection(),e}cancelPendingConnection(){this.connectPromise=null;const e=this.rejectConnect;this.rejectConnect=null,null==e||e(new Error("E2E WebSocket 连接已取消"))}closeSocket(e,t,n){e.readyState!==this.WebSocketCtor.OPEN&&e.readyState!==this.WebSocketCtor.CONNECTING||e.close(t,n)}send(e){var t;this.assertOpen(),null==(t=this.ws)||t.send(JSON.stringify(e))}trySend(e){var t;return this.assertOpen(),!(this.ws&&this.ws.bufferedAmount>a||(null==(t=this.ws)||t.send(JSON.stringify(e)),0))}sendBinary(e){var t;if(this.assertOpen(),this.ws&&this.ws.bufferedAmount>a){const e=new Error("E2E WebSocket 发送缓冲区超限");throw e.name="WebSocketBackpressureError",e}null==(t=this.ws)||t.send(e)}ping(){this.send({type:"ping"})}setAudioInputEnabled(e){this.audioInputEnabled=e}assertOpen(){if(!this.isOpen)throw new Error("E2E WebSocket 未连接")}parseMessage(e){if("string"!=typeof e)return null;try{const t=JSON.parse(e);return t&&"string"==typeof t.type?t:null}catch(e){var t,n;return null==(t=(n=this.options).onError)||t.call(n,new Error("E2E WebSocket 消息不是合法 JSON")),null}}buildWsUrl(e,t){try{const n=new URL(e);return!t||n.searchParams.has("token")||n.searchParams.has("e2e_token")||n.searchParams.set("token",t),this.updateAudioInputQuery(n.searchParams),n.toString()}catch(n){const[i,s=""]=e.split("?",2),r=new URLSearchParams(s);!t||r.has("token")||r.has("e2e_token")||r.set("token",t),this.updateAudioInputQuery(r);const a=r.toString();return a?`${i}?${a}`:i}}updateAudioInputQuery(e){this.audioInputEnabled?e.delete("audio_input_off"):e.set("audio_input_off","1")}}const l={format:"webm_opus",mimeType:"audio/webm;codecs=opus",sampleRate:16e3,channels:1,chunkMs:100},h=16e3;class c{constructor(e={}){this.inputTrack=null,this.reader=null,this.writer=null,this.generator=null,this.audioContext=null,this.audioSource=null,this.audioDestination=null,this.pumpPromise=null,this.pumpFinished=!1,this.outputClosed=!1,this.draining=!1,this.inputSampleRate=null,this.resampleBuffer=[],this.resamplePosition=0,this.outputBuffer=[],this.outputTimestamp=null,this.startGeneration=0,this.options=e}async start(e){const t=++this.startGeneration;this.inputTrack=e,this.inputSampleRate=null,this.resampleBuffer=[],this.resamplePosition=0,this.outputBuffer=[],this.outputTimestamp=null,this.pumpFinished=!1,this.outputClosed=!1,this.draining=!1;const n=globalThis,i=n.MediaStreamTrackProcessor,s=n.MediaStreamTrackGenerator,r=n.AudioData;if(!i||!s||!r)return this.startWebAudio(e,n,t);const a=new i({track:e}),o=new s({kind:"audio"}),l=new MediaStream([o]),c=l.getAudioTracks()[0],u=null==c?void 0:c.getSettings();if(void 0!==(null==u?void 0:u.sampleRate)&&u.sampleRate!==h||void 0!==(null==u?void 0:u.channelCount)&&1!==u.channelCount)throw o.stop(),this.unsupported(`浏览器音频输出格式不符合要求: ${(null==u?void 0:u.sampleRate)||"unknown"} Hz / ${(null==u?void 0:u.channelCount)||"unknown"} channel`);try{this.reader=a.readable.getReader(),this.writer=o.writable.getWriter()}catch(t){throw o.stop(),"ended"!==e.readyState&&e.stop(),this.inputTrack=null,t}return this.generator=o,this.pumpPromise=this.pump(r),l}async startWebAudio(e,t,n){const i=globalThis.AudioContext||t.webkitAudioContext;if(!i)throw this.unsupported("当前浏览器不支持 16 kHz mono 音频处理");let s=null,r=null,a=null;try{if(s=new i({sampleRate:h}),this.audioContext=s,s.sampleRate!==h)throw this.unsupported(`浏览器无法创建 16 kHz AudioContext: ${s.sampleRate} Hz`);if("suspended"===s.state)try{await s.resume()}catch(e){throw this.contextSuspended(e)}if(this.assertStartActive(n),"running"!==s.state)throw this.contextSuspended();r=s.createMediaStreamSource(new MediaStream([e])),this.audioSource=r,a=s.createMediaStreamDestination(),this.audioDestination=a,a.channelCount=1,a.channelCountMode="explicit",a.channelInterpretation="speakers",r.connect(a);const t=a.stream.getAudioTracks()[0];if(!t)throw new Error("Web Audio 未返回音频输出轨道");return this.validateOutputTrack(t),this.assertStartActive(n),a.stream}catch(t){var o,l;throw null==(o=r)||o.disconnect(),null==(l=a)||l.stream.getTracks().forEach(e=>e.stop()),s&&"closed"!==s.state&&await s.close().catch(()=>{}),"ended"!==e.readyState&&e.stop(),this.inputTrack=null,this.audioContext===s&&(this.audioContext=null,this.audioSource=null,this.audioDestination=null),this.normalizeWebAudioError(t)}}async stop(){await this.drain(),this.audioContext?await this.closeWebAudioOutput():await this.closeOutput()}async drain(){var e;if(this.startGeneration+=1,this.draining=!0,null==(e=this.inputTrack)||e.stop(),this.inputTrack=null,this.audioContext)return;const t=this.pumpPromise;if(t){const e=t.then(()=>!0,()=>!0);var n,i;await Promise.race([e,this.delay(1e3)])||(await Promise.race([null==(n=this.reader)?void 0:n.cancel().catch(()=>{}),this.delay(100)]),await Promise.race([null==(i=this.writer)?void 0:i.abort(new Error("16 kHz mono 音频处理停止超时")).catch(()=>{}),this.delay(100)]),this.stopGenerator())}}async closeOutput(){const e=this.writer;this.outputClosed=!0,e&&this.pumpFinished?await Promise.race([e.close().catch(()=>{}),this.delay(100)]):await Promise.race([null==e?void 0:e.abort(new Error("16 kHz mono 音频处理停止超时")).catch(()=>{}),this.delay(100)]),this.stopGenerator(),e&&this.pumpFinished&&e.releaseLock(),this.reader=null,this.writer=null,this.generator=null,this.pumpPromise=null}async closeWebAudioOutput(){const e=this.audioContext,t=this.audioSource,n=this.audioDestination;this.audioContext=null,this.audioSource=null,this.audioDestination=null,null==t||t.disconnect(),null==n||n.disconnect(),null==n||n.stream.getTracks().forEach(e=>e.stop()),e&&"closed"!==e.state&&await e.close().catch(()=>{})}async pump(e){const t=this.reader,n=this.writer;if(t&&n)try{var i;let a=!1;for(;;){const i=await t.read();if(i.done){a=!0;break}if(!i.value)continue;const s=i.value;try{this.validateAudioData(s);const t=this.toMono(s),i=this.resample(t,s.sampleRate,!1);null===this.outputTimestamp&&(this.outputTimestamp=Number.isFinite(s.timestamp)?s.timestamp:0);for(const t of i)await this.writeChunk(n,e,t)}finally{s.close()}}for(const t of this.resample(new Float32Array,this.inputSampleRate||h,!0))await this.writeChunk(n,e,t);if(a&&!this.draining&&"ended"===(null==(i=this.inputTrack)?void 0:i.readyState)){var s,r;const e=new Error("麦克风音频轨道已结束");e.name="AudioTransformError",null==(s=(r=this.options).onError)||s.call(r,e),this.stopGenerator()}}catch(e){var a,o;const t=e instanceof Error?e:new Error("16 kHz mono 音频处理失败");t.name="AudioTransformError",null==(a=(o=this.options).onError)||a.call(o,t)}finally{t.releaseLock(),this.pumpFinished=!0,this.outputClosed&&n.releaseLock()}}toMono(e){const t=Math.max(1,e.numberOfChannels),n=new Float32Array(e.numberOfFrames);for(let i=0;i<t;i+=1){const s=new Float32Array(e.numberOfFrames);e.copyTo(s,{format:"f32-planar",planeIndex:i});for(let e=0;e<s.length;e+=1)n[e]+=s[e]/t}return n}resample(e,t,n){if(null===this.inputSampleRate)this.inputSampleRate=t;else if(this.inputSampleRate!==t)throw new Error(`输入音频采样率发生变化: ${this.inputSampleRate} -> ${t}`);for(const t of e)this.resampleBuffer.push(t);if(this.resampleBuffer.length>192e3)throw new Error("输入音频重采样缓冲区超限");n&&this.resampleBuffer.length>0&&this.resampleBuffer.push(this.resampleBuffer[this.resampleBuffer.length-1]);const i=t/h,s=[];for(;this.resamplePosition+1<this.resampleBuffer.length;){const e=Math.floor(this.resamplePosition),t=Math.min(e+1,this.resampleBuffer.length-1),n=this.resamplePosition-e;this.outputBuffer.push(this.resampleBuffer[e]*(1-n)+this.resampleBuffer[t]*n),this.resamplePosition+=i;const r=Math.floor(this.resamplePosition);r>0&&(this.resampleBuffer.splice(0,r),this.resamplePosition-=r),this.outputBuffer.length>=320&&s.push(new Float32Array(this.outputBuffer.splice(0,320)))}return n&&this.outputBuffer.length>0&&s.push(new Float32Array(this.outputBuffer.splice(0))),s}async writeChunk(e,t,n){var i;if(!n.length)return;const s=this.outputTimestamp||0,r=new t({format:"f32-planar",sampleRate:h,numberOfFrames:n.length,numberOfChannels:1,timestamp:s,data:n});this.outputTimestamp=s+Math.round(1e6*n.length/h),await e.write(r),r.close();const a=null==(i=this.generator)?void 0:i.getSettings();if(a&&(void 0!==a.sampleRate&&a.sampleRate!==h||void 0!==a.channelCount&&1!==a.channelCount))throw this.unsupported(`浏览器音频输出格式不符合要求: ${a.sampleRate||"unknown"} Hz / ${a.channelCount||"unknown"} channel`)}validateAudioData(e){if(!Number.isFinite(e.sampleRate)||!Number.isInteger(e.sampleRate)||e.sampleRate<8e3||e.sampleRate>192e3)throw new Error(`输入音频采样率无效: ${e.sampleRate}`);if(!Number.isInteger(e.numberOfChannels)||e.numberOfChannels<1||e.numberOfChannels>8)throw new Error(`输入音频声道数无效: ${e.numberOfChannels}`);if(!Number.isInteger(e.numberOfFrames)||e.numberOfFrames<1||e.numberOfFrames>96e3)throw new Error(`输入音频帧数无效: ${e.numberOfFrames}`);if(!Number.isFinite(e.timestamp)||Math.abs(e.timestamp)>1e15)throw new Error(`输入音频时间戳无效: ${e.timestamp}`)}delay(e){return new Promise(t=>{globalThis.setTimeout(()=>t(!1),e)})}stopGenerator(){this.generator&&"ended"!==this.generator.readyState&&this.generator.stop()}unsupported(e,t){const n=new Error(e);return n.name="NotSupportedError",n.agentCode="AUDIO_FIXED_TRACK_UNSUPPORTED",n.retryable=!1,n.cause=t,n}contextSuspended(e){const t=new Error("AudioContext 未运行,请在用户操作后重试");return t.name="NotAllowedError",t.agentCode="AUDIO_CONTEXT_SUSPENDED",t.retryable=!0,t.cause=e,t}assertStartActive(e){if(e!==this.startGeneration){const e=new Error("16 kHz mono 音频处理启动已取消");throw e.name="AbortError",e.agentCode="AUDIO_FIXED_TRACK_CANCELLED",e.retryable=!0,e}}normalizeWebAudioError(e){const t=e instanceof Error?e:new Error(String(e));if(t.agentCode)return t;if("NotSupportedError"===t.name)return this.unsupported(`浏览器无法初始化 16 kHz mono Web Audio: ${t.message}`,t);const n=new Error(`16 kHz mono Web Audio 初始化失败: ${t.message}`);return n.name=t.name,n.agentCode="AUDIO_FIXED_TRACK_FAILED",n.retryable=!0,n.cause=t,n}validateOutputTrack(e){const t=e.getSettings();if(void 0!==t.sampleRate&&t.sampleRate!==h||void 0!==t.channelCount&&1!==t.channelCount)throw this.unsupported(`浏览器音频输出格式不符合要求: ${t.sampleRate||"unknown"} Hz / ${t.channelCount||"unknown"} channel`)}}function u(e){return e.toString(16).padStart(2,"0")}async function d(e,n,i={},s=64){try{const r=n instanceof Blob?await n.arrayBuffer():ArrayBuffer.isView(n)?n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength):n,a=new Uint8Array(r),o=Array.from(a.slice(0,s)),l=n instanceof Blob?n.type:i.type;console.log(e,t({},i,{size:a.byteLength,type:l,previewLength:o.length,bytes:o,hex:o.map(u).join(" ")}))}catch(t){console.warn(e,"failed to read audio chunk",t)}}const p=new WeakMap,m=new WeakMap;function g(e){return e.audioSession}function E(e,t){try{return e.type=t,e.type===t}catch(e){return!1}}function f(){const e=globalThis.performance;return e&&Number.isFinite(e.timeOrigin)&&"function"==typeof e.now?e.timeOrigin+e.now():Date.now()}class S{constructor(e,t=f){this.timecodeScale=null,this.firstTimecode=null,this.lastTimecode=null,this.lastObservedAt=null,this.lastTimestamp=null,this.chunkMs=e,this.now=t,this.startedAt=t()}resolve(e){const t=this.now(),n=Number.isFinite(e)&&e>=0;if(n&&null===this.firstTimecode&&(this.firstTimecode=e),n&&null===this.timecodeScale&&null!==this.lastTimecode&&e>this.lastTimecode){const n=e-this.lastTimecode,i=null===this.lastObservedAt?0:t-this.lastObservedAt,s=i>=this.chunkMs/4?i:this.chunkMs,r=Math.abs(n-s),a=Math.abs(1e3*n-s);this.timecodeScale=a<r?1e3:1}let i=t-this.chunkMs;const s=n&&null!==this.firstTimecode?e-this.firstTimecode:null;var r;null!==s&&s>=0&&(null!==this.timecodeScale||0===s)&&(i=this.startedAt+s*(null!=(r=this.timecodeScale)?r:1)),null!==this.lastTimestamp&&(i=Math.max(i,this.lastTimestamp+this.chunkMs));const a=Math.max(Math.round(this.startedAt),Math.round(i));return this.lastTimecode=n?e:this.lastTimecode,this.lastObservedAt=t,this.lastTimestamp=a,a}}const R=2e4;function A(e){return{codec:"opus",sampleRate:e.sampleRate,numberOfChannels:e.channels,bitrate:e.bitrate,opus:{format:"opus",frameDuration:R}}}class y{constructor(e={}){this.encoder=null,this.isInitialized=!1,this.pendingEncode=null,this.timestamp=0,this.config=t({sampleRate:16e3,channels:1,bitrate:24e3,frameSize:1600},e)}static async isSupported(){if("undefined"==typeof AudioEncoder)return!1;try{var e;const t=await AudioEncoder.isConfigSupported(A({sampleRate:16e3,channels:1,bitrate:24e3,frameSize:1600}));return!0===t.supported&&(null==(e=t.config)||null==(e=e.opus)?void 0:e.frameDuration)===R}catch(e){return!1}}async init(){this.isInitialized||(this.encoder=this.createEncoder(),this.isInitialized=!0)}async encode(e){if(!this.isInitialized||!this.encoder)return Promise.reject(new Error("WebCodecOpusEncoder 未初始化"));if(this.pendingEncode)return Promise.reject(new Error("WebCodecs Opus 编码器仍在处理上一音频块"));const t=new Int16Array(e),n=this.timestamp,i=Math.round(1e6*e.length/this.config.sampleRate),s=n+i,r=i/R;if(!Number.isInteger(r))return Promise.reject(new Error("PCM 音频块无法按 20ms Opus packet 完整分包"));const a=new AudioData({format:"s16",sampleRate:this.config.sampleRate,numberOfFrames:t.length,numberOfChannels:this.config.channels,timestamp:n,data:t}),o=new Promise((e,t)=>{const i=globalThis.setTimeout(()=>{this.failPending(new Error("WebCodecs Opus 编码输出超过 2 秒"))},2e3);this.pendingEncode={startTimestampUs:n,endTimestampUs:s,expectedFrameCount:r,chunks:[],resolve:e,reject:t,timeoutId:i}});try{this.encoder.encode(a),this.timestamp=s}catch(e){this.failPending(e instanceof Error?e:new Error("WebCodecs Opus 编码失败"))}finally{a.close()}return o}reset(){if(this.timestamp=0,this.failPending(new Error("WebCodecs Opus 编码已重置")),this.encoder){try{this.encoder.close()}catch(e){}this.encoder=this.createEncoder()}}async destroy(){const e=this.encoder;if(e){try{await e.flush()}catch(e){}this.failPending(new Error("WebCodecs Opus 编码器已销毁"));try{e.close()}catch(e){}}this.encoder=null,this.isInitialized=!1}createEncoder(){let e;return e=new AudioEncoder({output:t=>{this.encoder===e&&this.handleOutput(t)},error:t=>{if(this.encoder!==e)return;const n=t instanceof Error?t:new Error("WebCodecs Opus 编码失败");(globalThis.avatarSDKLogger||console).error("[WebCodecOpusEncoder] 编码错误:",n),this.failPending(n)}}),e.configure(A(this.config)),e}handleOutput(e){const t=this.pendingEncode;if(!t)return;if(e.timestamp<t.startTimestampUs||e.timestamp>=t.endTimestampUs||null!==e.duration&&e.duration!==R)return void this.failPending(new Error("WebCodecs Opus 未按 20ms 分包,无法生成固定 100ms AU 音频"));if(t.chunks.push(e),t.chunks.length>t.expectedFrameCount)return void this.failPending(new Error("WebCodecs Opus 未按 20ms 分包,无法生成固定 100ms AU 音频"));if(t.chunks.length<t.expectedFrameCount)return;const n=t.chunks.map((e,n)=>{var i,s;const r=new Uint8Array(e.byteLength);e.copyTo(r);const a=null!=(i=null==(s=t.chunks[n+1])?void 0:s.timestamp)?i:t.endTimestampUs;return{data:r,durationMs:(e.duration&&e.duration>0?e.duration:Math.max(1,a-e.timestamp))/1e3}}),i=n.reduce((e,t)=>e+t.durationMs,0);n.some(e=>20!==e.durationMs)||i!==(t.endTimestampUs-t.startTimestampUs)/1e3?this.failPending(new Error("WebCodecs Opus 未按 20ms 分包,无法生成固定 100ms AU 音频")):(globalThis.clearTimeout(t.timeoutId),this.pendingEncode=null,t.resolve(n))}failPending(e){const t=this.pendingEncode;t&&(globalThis.clearTimeout(t.timeoutId),this.pendingEncode=null,t.reject(e))}}class v{constructor(e=16e3){this.chunks=[],this.hasHeader=!1,this.clusterTimestamp=0,this.frameDuration=100,this.sampleRate=e}writeHeader(){if(this.hasHeader)return;const e=this.buildEBMLHeader(),t=this.buildSegmentInfo(),n=this.buildOpusTracks();this.chunks.push(e),this.chunks.push(new Uint8Array([24,83,128,103,1,255,255,255,255,255,255,255])),this.chunks.push(t),this.chunks.push(n),this.hasHeader=!0,this.clusterTimestamp=0}feedOpusFrame(e,t=this.frameDuration){this.hasHeader||this.writeHeader();const n=this.buildCluster(Math.round(this.clusterTimestamp),e);this.chunks.push(n),this.clusterTimestamp+=t}flush(){if(0===this.chunks.length)return new ArrayBuffer(0);const e=this.chunks.reduce((e,t)=>e+t.length,0),t=new Uint8Array(e);let n=0;for(const e of this.chunks)t.set(e,n),n+=e.length;return this.chunks=[],t.buffer}reset(){this.chunks=[],this.hasHeader=!1,this.clusterTimestamp=0}encodeVINT(e,t){if(void 0!==t){const n=new Uint8Array(t);for(let i=t-1;i>=0;i--)n[i]=255&e,e>>>=8;return n[0]|=1<<8-t,n}if(e<127)return new Uint8Array([128|e]);if(e<16383)return new Uint8Array([64|e>>8,255&e]);if(e<2097151)return new Uint8Array([32|e>>16,e>>8&255,255&e]);if(e<268435455)return new Uint8Array([16|e>>24,e>>16&255,e>>8&255,255&e]);throw new Error(`VINT value too large: ${e}`)}encodeID(e){return e<=255?new Uint8Array([e]):e<=65535?new Uint8Array([e>>8,255&e]):e<=16777215?new Uint8Array([e>>16,e>>8&255,255&e]):new Uint8Array([e>>>24,e>>16&255,e>>8&255,255&e])}encodeUint(e){if(0===e)return new Uint8Array([0]);const t=[];let n=e;for(;n>0;)t.unshift(255&n),n=Math.floor(n/256);return new Uint8Array(t)}encodeString(e){return(new TextEncoder).encode(e)}encodeFloat32(e){const t=new ArrayBuffer(4);return new DataView(t).setFloat32(0,e,!1),new Uint8Array(t)}encodeElement(e,t){const n=this.encodeID(e),i=this.encodeVINT(t.length),s=new Uint8Array(n.length+i.length+t.length);return s.set(n,0),s.set(i,n.length),s.set(t,n.length+i.length),s}encodeUintElement(e,t){return this.encodeElement(e,this.encodeUint(t))}encodeStringElement(e,t){return this.encodeElement(e,this.encodeString(t))}encodeFloat32Element(e,t){return this.encodeElement(e,this.encodeFloat32(t))}concatArrays(e){const t=e.reduce((e,t)=>e+t.length,0),n=new Uint8Array(t);let i=0;for(const t of e)n.set(t,i),i+=t.length;return n}buildEBMLHeader(){const e=this.concatArrays([this.encodeUintElement(17030,1),this.encodeUintElement(17143,1),this.encodeUintElement(17138,4),this.encodeUintElement(17139,8),this.encodeStringElement(17026,"webm"),this.encodeUintElement(17031,4),this.encodeUintElement(17029,2)]);return this.encodeElement(440786851,e)}buildSegmentInfo(){const e=this.concatArrays([this.encodeUintElement(2807729,1e6),this.encodeStringElement(19840,"xmov-avatar-sdk"),this.encodeStringElement(22337,"xmov-avatar-sdk")]);return this.encodeElement(357149030,e)}buildOpusTracks(){const e=new Uint8Array([79,112,117,115,72,101,97,100,1,1,0,0,128,62,0,0,0,0,0]),t=this.concatArrays([this.encodeFloat32Element(181,this.sampleRate),this.encodeUintElement(159,1)]),n=this.concatArrays([this.encodeUintElement(215,1),this.encodeUintElement(29637,1),this.encodeUintElement(131,2),this.encodeUintElement(156,0),this.encodeStringElement(2274716,"und"),this.encodeStringElement(134,"A_OPUS"),this.encodeElement(25506,e),this.encodeUintElement(22186,0),this.encodeUintElement(22203,8e7),this.encodeElement(225,t)]),i=this.encodeElement(174,n);return this.encodeElement(374648427,i)}buildCluster(e,t){const n=this.encodeUintElement(231,e),i=this.encodeVINT(1),s=new Uint8Array([0,0]),r=new Uint8Array([128]),a=this.concatArrays([i,s,r,t]),o=this.encodeElement(163,a),l=this.concatArrays([n,o]);return this.encodeElement(524531317,l)}}class w{constructor(){this.opusEncoder=new y({sampleRate:16e3,channels:1,bitrate:24e3,frameSize:1600}),this.muxer=new v(16e3),this.queue=Promise.resolve(),this.generation=0,this.destroyed=!1,this.pendingBlocks=0}static isSupported(){return y.isSupported()}async init(){await this.opusEncoder.init()}async encode(e){var t=this;if(1600!==e.length)throw new Error(`PCM 音频块必须为 1600 samples,实际为 ${e.length}`);if(this.pendingBlocks>=20)throw new Error("PCM/Opus 编码队列超过 2 秒");this.pendingBlocks+=1;const n=this.generation,i=new Int16Array(e),s=this.queue.then(async function(){if(t.destroyed||n!==t.generation)return new ArrayBuffer(0);const e=await t.opusEncoder.encode(i);if(t.destroyed||n!==t.generation)return new ArrayBuffer(0);for(const n of e)t.muxer.feedOpusFrame(n.data,n.durationMs);return t.muxer.flush()}).finally(()=>{this.pendingBlocks-=1});return this.queue=s.then(()=>{},()=>{}),s}reset(){const e=++this.generation;this.queue=this.queue.then(()=>{this.destroyed||e!==this.generation||(this.opusEncoder.reset(),this.muxer.reset())})}async destroy(){this.destroyed=!0,this.generation+=1,await this.opusEncoder.destroy(),await this.queue,this.muxer.reset()}}const _="audio/webm;codecs=opus",b=16e3,T="agent-pcm-capture-processor",k=`\nclass AgentPcmCaptureProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.buffer = new Float32Array(1600);\n this.bufferIndex = 0;\n this.sequence = 0;\n this.port.onmessage = (event) => {\n if (event.data?.type === 'flush') {\n this.emitBlock(true);\n this.port.postMessage({ type: 'flushed' });\n }\n };\n }\n\n emitBlock(padPartial = false) {\n if (this.bufferIndex === 0 || (!padPartial && this.bufferIndex < 1600)) return;\n const pcm = new Int16Array(1600);\n for (let index = 0; index < this.bufferIndex; index += 1) {\n const sample = Math.max(-1, Math.min(1, this.buffer[index]));\n pcm[index] = sample < 0 ? sample * 0x8000 : sample * 0x7fff;\n }\n this.port.postMessage(\n { type: 'frame', pcm: pcm.buffer, sequence: this.sequence++ },\n [pcm.buffer],\n );\n this.buffer.fill(0);\n this.bufferIndex = 0;\n }\n\n process(inputs, outputs) {\n for (const output of outputs) {\n for (const channel of output) channel.fill(0);\n }\n const channels = inputs[0];\n if (!channels?.length || !channels[0]?.length) return true;\n for (let frame = 0; frame < channels[0].length; frame += 1) {\n let sample = 0;\n for (let channel = 0; channel < channels.length; channel += 1) {\n sample += channels[channel][frame] || 0;\n }\n this.buffer[this.bufferIndex++] = sample / channels.length;\n if (this.bufferIndex === 1600) this.emitBlock();\n }\n return true;\n }\n}\nregisterProcessor('${T}', AgentPcmCaptureProcessor);\n`;class C{constructor(e){var n,i;this.stream=null,this.recordingStream=null,this.audioTransformer=null,this.mediaRecorder=null,this.pcmAudioContext=null,this.pcmSource=null,this.pcmWorklet=null,this.pcmEncoder=null,this.pcmEncodeQueue=Promise.resolve(),this.pcmStartedAt=0,this.pcmGeneration=0,this.resolvePcmFlush=null,this.startGeneration=0,this.starting=!1,this.hasAudioSessionCapture=!1,this.options=e,this.pendingInputStream=null!=(n=e.inputStream)?n:null,this.usesProvidedInput=Boolean(e.inputStream),this.metadata=t({},l,{chunkMs:(null==(i=e.audio)?void 0:i.chunkMs)||l.chunkMs})}get isRecording(){return this.starting||Boolean(this.stream)||Boolean(this.pendingInputStream)}getAudioMetadata(){return this.metadata}async start(){if(this.stream||this.starting)return;const e=++this.startGeneration;this.starting=!0;try{var n,i;const a=this.pendingInputStream;this.stream=null!=a?a:null,this.pendingInputStream=null;const o=navigator.mediaDevices;if(!(this.stream||null!=o&&o.getUserMedia))throw new Error("当前浏览器不支持麦克风采集");const l=globalThis.MediaRecorder;if(this.options.usePcmWebCodecs){if(!await w.isSupported())throw this.webCodecsOpusUnsupported()}else if(null==l||null==l.isTypeSupported||!l.isTypeSupported(_))throw this.webMOpusUnsupported();if(this.assertStartActive(e),!this.stream){if(this.usesProvidedInput)throw a?this.cancelled():new Error("ASR 输入流已释放");this.hasAudioSessionCapture=function(e=navigator){var t,n;const i=g(e);if(!i)return!1;const s=p.get(i),r=null!=(t=null==s?void 0:s.previousType)?t:i.type;return!!E(i,"play-and-record")&&(p.set(i,{count:(null!=(n=null==s?void 0:s.count)?n:0)+1,previousType:r}),!0)}(),this.stream=await o.getUserMedia({audio:{sampleRate:{ideal:b},channelCount:{ideal:1},echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0}})}this.assertStartActive(e);const h=this.stream.getAudioTracks()[0];if(!h)throw new Error("麦克风未返回音频轨道");var s,r;if(this.options.usePcmWebCodecs)return await this.startPcmWebMRecorder(this.stream),this.assertStartActive(e),this.metadata=t({},this.metadata,{sampleRate:b,channels:1,chunkMs:100}),void(null==(s=(r=this.options).onMetadata)||s.call(r,this.metadata));const u=new c({onError:e=>{var t,n;const i=e instanceof Error?e:new Error("16 kHz mono 音频处理失败");i.name="MediaRecorderError",null==(t=(n=this.options).onError)||t.call(n,i)}});this.audioTransformer=u,this.recordingStream=await u.start(h),this.assertStartActive(e);const d=this.recordingStream.getAudioTracks()[0],m=null==d?void 0:d.getSettings();this.metadata=t({},this.metadata,{sampleRate:(null==m?void 0:m.sampleRate)||b,channels:(null==m?void 0:m.channelCount)||1}),this.startWebMRecorder(l),null==(n=(i=this.options).onMetadata)||n.call(i,this.metadata)}catch(t){const n=e!==this.startGeneration;await this.stop();const i=n?this.cancelled():t instanceof Error?t:new Error("麦克风启动失败");var a,o;throw"AUDIO_FIXED_TRACK_CANCELLED"!==i.agentCode&&(null==(a=(o=this.options).onError)||a.call(o,i)),i}finally{this.startGeneration===e&&(this.starting=!1)}}async stop(){this.startGeneration+=1,this.starting=!1;try{var e;const t=this.audioTransformer;try{await(null==t?void 0:t.drain())}finally{await this.stopPcmWebMRecorder(),await this.stopMediaRecorder(),await(null==t?void 0:t.stop()),this.audioTransformer=null}this.recordingStream=null,this.stream&&!t&&this.stream.getTracks().forEach(e=>e.stop()),this.stream=null,null==(e=this.pendingInputStream)||e.getTracks().forEach(e=>e.stop()),this.pendingInputStream=null}finally{this.hasAudioSessionCapture&&(this.hasAudioSessionCapture=!1,function(e=navigator){const t=g(e);if(!t)return!1;const n=p.get(t);if(!n)return function(e=navigator){const t=g(e);if(!t)return!1;const n=p.get(t);return E(t,null!=n&&n.count||"play-and-record"===t.type?"play-and-record":"playback")}(e);if(n.count-=1,n.count>0)return E(t,"play-and-record");p.delete(t);const i="play-and-record"===n.previousType?"play-and-record":"playback";E(t,i)&&"playback"===i&&function(e){const t=m.get(e);if(t)for(const e of[...t])try{e()}catch(e){}}(t)}())}}startWebMRecorder(e){if(!this.recordingStream)throw new Error("16 kHz mono 音频流未就绪");let t;try{t=new e(this.recordingStream,{mimeType:_,audioBitsPerSecond:24e3})}catch(e){if(e instanceof Error&&"NotSupportedError"===e.name)throw this.webMOpusUnsupported(e);throw e}const n=new S(this.metadata.chunkMs);t.ondataavailable=e=>{if(e.data.size>0){var i;const s=n.resolve(e.timecode);null==(i=globalThis.avatarSDKLogger)||null==i.log||i.log("[Agent][Microphone]","audio chunk",{size:e.data.size,type:e.data.type,chunkMs:this.metadata.chunkMs,sampleRate:this.metadata.sampleRate,channels:this.metadata.channels,recorderState:t.state,timecode:e.timecode}),this.options.debugAudioChunks&&d("[Agent][Microphone] nearend WebM/Opus chunk",e.data,{chunkMs:this.metadata.chunkMs,sampleRate:this.metadata.sampleRate,channels:this.metadata.channels,recorderState:t.state,timecode:e.timecode}),this.options.onFrame(e.data,s)}},t.onerror=e=>{var t,n;const i=e.error,s=new Error(i instanceof Error?i.message:"WebM/Opus 麦克风录制失败");s.name="MediaRecorderError",null==(t=(n=this.options).onError)||t.call(n,s)},this.mediaRecorder=t;try{t.start(this.metadata.chunkMs)}catch(e){if(e instanceof Error&&"NotSupportedError"===e.name)throw this.webMOpusUnsupported(e);throw e}}assertStartActive(e){if(e!==this.startGeneration)throw this.cancelled()}cancelled(){const e=new Error("麦克风启动已取消");return e.name="AbortError",e.agentCode="AUDIO_FIXED_TRACK_CANCELLED",e.retryable=!0,e}webMOpusUnsupported(e){const t=new Error("当前浏览器不支持 audio/webm;codecs=opus");return t.name="NotSupportedError",t.agentCode="AUDIO_WEBM_OPUS_UNSUPPORTED",t.retryable=!1,t.cause=e,t}webCodecsOpusUnsupported(e){const t=new Error("当前浏览器不支持 WebCodecs AudioEncoder Opus");return t.name="NotSupportedError",t.agentCode="AUDIO_WEBCODECS_OPUS_UNSUPPORTED",t.retryable=!1,t.cause=e,t}async startPcmWebMRecorder(e){var t=this;const n=globalThis,i=globalThis.AudioContext||n.webkitAudioContext;if(!i||void 0===globalThis.AudioWorkletNode)throw this.webCodecsOpusUnsupported(new Error("AudioWorklet 不可用"));const s=new w;await s.init();const r=new i({sampleRate:b});if(r.sampleRate!==b)throw await s.destroy(),await r.close().catch(()=>{}),this.webCodecsOpusUnsupported(new Error("浏览器无法创建 16000 Hz AudioContext"));this.pcmAudioContext=r,this.pcmEncoder=s;const a=URL.createObjectURL(new Blob([k],{type:"application/javascript"}));try{await r.audioWorklet.addModule(a)}finally{URL.revokeObjectURL(a)}if("suspended"===r.state&&await r.resume(),"running"!==r.state)throw this.audioContextSuspended();const o=r.createMediaStreamSource(e),l=new AudioWorkletNode(r,T,{numberOfInputs:1,numberOfOutputs:1,outputChannelCount:[1]}),h=++this.pcmGeneration;this.pcmSource=o,this.pcmWorklet=l,this.pcmEncodeQueue=Promise.resolve(),this.pcmStartedAt=this.monotonicEpochNow(),l.port.onmessage=e=>{var n,i,r;if("flushed"===(null==(n=e.data)?void 0:n.type))return null==(r=this.resolvePcmFlush)||r.call(this),void(this.resolvePcmFlush=null);if("frame"!==(null==(i=e.data)?void 0:i.type)||!(e.data.pcm instanceof ArrayBuffer))return;const a=this.pcmStartedAt+100*Number(e.data.sequence||0),o=new Int16Array(e.data.pcm);this.pcmEncodeQueue=this.pcmEncodeQueue.then(async function(){const n=await s.encode(o);if(h!==t.pcmGeneration||0===n.byteLength)return;const i=new Blob([n],{type:_});t.options.debugAudioChunks&&d("[Agent][Microphone] nearend PCM/WebCodecs chunk",i,{chunkMs:100,sampleRate:b,channels:1,sequence:e.data.sequence}),t.options.onFrame(i,a)}).catch(e=>{var t,n;const i=e instanceof Error?e:new Error("PCM/Opus 麦克风编码失败");i.name="AudioEncoderError",null==(t=(n=this.options).onError)||t.call(n,i)})},o.connect(l),l.connect(r.destination)}async stopPcmWebMRecorder(){const e=this.pcmWorklet,t=this.pcmSource,n=this.pcmAudioContext,i=this.pcmEncoder;if(!e&&!n&&!i)return;null==t||t.disconnect();let s=!0;e&&(s=await Promise.race([new Promise(t=>{this.resolvePcmFlush=()=>t(!0),e.port.postMessage({type:"flush"})}),new Promise(e=>globalThis.setTimeout(()=>e(!1),100))]),s||(this.resolvePcmFlush=null,e.port.onmessage=null)),await this.pcmEncodeQueue,this.pcmGeneration+=1,this.resolvePcmFlush=null,e&&(e.port.onmessage=null,e.disconnect(),e.port.close()),n&&"closed"!==n.state&&await n.close().catch(()=>{}),await(null==i?void 0:i.destroy()),this.pcmWorklet=null,this.pcmSource=null,this.pcmAudioContext=null,this.pcmEncoder=null,this.pcmEncodeQueue=Promise.resolve()}monotonicEpochNow(){const e=globalThis.performance;return e&&Number.isFinite(e.timeOrigin)?Math.round(e.timeOrigin+e.now()):Date.now()}audioContextSuspended(){const e=new Error("AudioContext 未运行,请在用户操作后重试");return e.name="NotAllowedError",e.agentCode="AUDIO_CONTEXT_SUSPENDED",e.retryable=!0,e}async stopMediaRecorder(){const e=this.mediaRecorder;if(this.mediaRecorder=null,!e)return;const t="inactive"!==e.state;await new Promise(n=>{let i=!1;const s=()=>{i||(i=!0,globalThis.clearTimeout(a),n())},r=e.onerror,a=globalThis.setTimeout(s,1e3);if(e.onstop=s,e.onerror=t=>{null==r||r.call(e,t),s()},t)try{e.stop()}catch(e){s()}}),e.ondataavailable=null,e.onerror=null,e.onstop=null}}const O=new Uint8Array([26,69,223,163]);function D(e,t){const n=globalThis.avatarSDKLogger;null==n||null==n.log||n.log("[Agent][AudioUplink]",e,t)}function I(e,t,n){if(1!==e&&2!==e)throw new Error(`AU stream_id 无效: ${e}`);if(!Number.isFinite(t)||t<0||t>4294967295)throw new Error(`AU ts_ms 无效: ${t}`);const i=n instanceof ArrayBuffer?new Uint8Array(n):new Uint8Array(n.buffer,n.byteOffset,n.byteLength),s=new ArrayBuffer(8+i.byteLength),r=new DataView(s);return r.setUint8(0,65),r.setUint8(1,85),r.setUint8(2,1),r.setUint8(3,e),r.setUint32(4,Math.floor(t),!1),new Uint8Array(s,8).set(i),s}async function M(e){if(Array.isArray(e)){const t=await Promise.all(e.map(M)),n=new Uint8Array(t.reduce((e,t)=>e+t.byteLength,0));let i=0;for(const e of t)n.set(e,i),i+=e.byteLength;return n}return e instanceof Blob?new Uint8Array(await e.arrayBuffer()):e instanceof ArrayBuffer?new Uint8Array(e):new Uint8Array(e.buffer,e.byteOffset,e.byteLength)}class P{constructor(e){this.connectionStartedAt=null,this.connectionGeneration=0,this.sendChain=Promise.resolve(),this.failed=!1,this.pendingFarend=new Map,this.pendingNearend=new Map,this.closedNearendBuckets=new Set,this.lastTimestampByStream=new Map,this.lastSentTimestampByStream=new Map,this.pendingWebmPrefixByStream=new Map,this.farendStreamState="unknown",this.options=e}reset(e=null){this.connectionGeneration+=1,this.connectionStartedAt=e,this.pendingFarend.clear(),this.pendingNearend.clear(),this.closedNearendBuckets.clear(),this.lastTimestampByStream.clear(),this.lastSentTimestampByStream.clear(),this.pendingWebmPrefixByStream.clear(),this.farendStreamState="unknown",this.failed=!1,this.sendChain=Promise.resolve()}startInput(){this.failed=!1,this.pendingFarend.clear(),this.pendingNearend.clear(),this.closedNearendBuckets.clear(),this.pendingWebmPrefixByStream.clear(),this.farendStreamState="unknown"}clearFarend(){this.pendingFarend.clear(),this.pendingWebmPrefixByStream.delete(2),this.farendStreamState="unknown"}offerFarend(e){var t;if(!1===e.isFirstChunk&&"ready"!==this.farendStreamState)return void D("farend.drop",{reason:"awaiting_webm_header",timestamp:e.timestamp,isFirstChunk:!1});const n=this.normalizeBlock(2,e);if(!n)return void(e.isFirstChunk&&this.abandonFarendStream());e.isFirstChunk&&(this.pendingFarend.clear(),this.pendingWebmPrefixByStream.delete(2),this.farendStreamState="ready");const i=null!=(t=this.pendingFarend.get(n.frameKey))?t:[];i.push(n),this.pendingFarend.set(n.frameKey,i),D("farend.offer",{tsMs:n.tsMs,frameKey:n.frameKey,isFirstChunk:Boolean(n.isFirstChunk),pendingNearend:[...this.pendingNearend.keys()]}),this.flushOlderNearend(n.frameKey),this.prune(n.frameKey)}offerNearend(e){var t;const n=this.normalizeBlock(1,e);if(!n||this.closedNearendBuckets.has(n.frameKey))return;const i=null!=(t=this.pendingNearend.get(n.frameKey))?t:[];i.push(n),this.pendingNearend.set(n.frameKey,i),this.flushExpiredNearend(n.frameKey),this.prune(n.frameKey)}drain(){for(const e of[...this.pendingNearend.keys()].sort((e,t)=>e-t))this.flushBucket(e,!0);return this.sendChain}normalizeBlock(e,n){const i=this.connectionStartedAt;if(null===i||!Number.isFinite(n.timestamp))return null;const s=Math.max(0,Math.floor(n.timestamp-i)),r=Math.floor(s/100),a=this.lastTimestampByStream.get(e);return void 0!==a&&s<=a?null:(this.lastTimestampByStream.set(e,s),t({},n,{streamId:e,tsMs:s,frameKey:r}))}enqueue(e){var t=this;if(this.failed)return;const n=this.connectionGeneration;this.sendChain=this.sendChain.then(async function(){if(!t.failed)for(const s of e){var i;const e=await M(null!=(i=s.dataParts)?i:s.data);if(t.failed||n!==t.connectionGeneration||null===t.connectionStartedAt||0===e.byteLength)continue;2===s.streamId&&s.isFirstChunk&&t.pendingWebmPrefixByStream.delete(2);const r=t.coalesceSplitWebmMagic(s.streamId,e);if(!r)continue;const a=t.lastSentTimestampByStream.get(s.streamId);void 0!==a&&s.tsMs<=a||(t.options.sendBinary(I(s.streamId,s.tsMs,r)),t.lastSentTimestampByStream.set(s.streamId,s.tsMs),D("frame.send",{streamId:s.streamId,tsMs:s.tsMs,bytes:r.byteLength}))}}).catch(e=>{var t,i;if(n!==this.connectionGeneration)return;this.failed=!0,this.pendingFarend.clear(),this.pendingNearend.clear();const s=e instanceof Error?e:new Error("AU 音频上行失败");null==(t=(i=this.options).onError)||t.call(i,s)})}coalesceSplitWebmMagic(e,t){const n=this.pendingWebmPrefixByStream.get(e);if(n){const i=new Uint8Array(n.byteLength+t.byteLength);i.set(n),i.set(t,n.byteLength),this.pendingWebmPrefixByStream.delete(e),t=i}return t.byteLength<O.byteLength&&t.every((e,t)=>e===O[t])?(this.pendingWebmPrefixByStream.set(e,t.slice()),null):t}flushOlderNearend(e){for(const t of[...this.pendingNearend.keys()].sort((e,t)=>e-t)){if(t>=e)break;this.flushBucket(t,!0)}}flushExpiredNearend(e){this.flushOlderNearend(e-1)}flushBucket(e,t=!1){const n=this.takeNearend(e);if(!n)return;const i=e-1,s=this.pendingFarend.has(e)||t&&this.pendingFarend.has(i)?this.takeFarendThrough(e):null;this.closedNearendBuckets.add(e),D("bucket.flush",{nearendFrameKey:e,pairedFarend:Boolean(s),pendingFarend:[...this.pendingFarend.keys()]}),this.enqueue(s?[s,n]:[n])}takeNearend(e){const n=this.pendingNearend.get(e);return this.pendingNearend.delete(e),null!=n&&n.length?t({},n[0],{dataParts:n.flatMap(e=>{var t;return null!=(t=e.dataParts)?t:[e.data]})}):null}takeFarendThrough(e){const n=[];for(const t of[...this.pendingFarend.keys()].sort((e,t)=>e-t)){if(t>e)break;n.push(...this.pendingFarend.get(t)),this.pendingFarend.delete(t)}return 0===n.length?null:t({},n[n.length-1],{dataParts:n.map(e=>e.data),isFirstChunk:n.some(e=>e.isFirstChunk)})}abandonFarendStream(){D("farend.drop",{reason:"stale_bucket_abandons_webm_stream",pendingFarend:[...this.pendingFarend.keys()]}),this.pendingFarend.clear(),this.pendingWebmPrefixByStream.delete(2),this.farendStreamState="awaiting-header"}prune(e){const t=e-3;[...this.pendingFarend.keys()].some(e=>e<t)&&this.abandonFarendStream();for(const e of this.pendingNearend.keys())e<t&&this.flushBucket(e,!0);for(const e of this.closedNearendBuckets)e<t&&this.closedNearendBuckets.delete(e)}}const N=["e2eServer","authToken","audio","reconnect","agentCallbacks","webSocketCtor","asr_id","asr_config","tts_config","features","extras","llm_id","brain_config","session_speak_req_id"],F=2147483647;function U(e){if(void 0===e)return!1;const t=JSON.stringify(e);return void 0!==t&&"{}"!==t}const B={1006:"E2E WebSocket 异常断开:未收到 Close frame,请检查 E2EMPServer 日志或中间网络",4e3:"E2E WebSocket 路径未知",4001:"E2E Token 无效",4002:"E2E Session 不存在",4009:"E2E 配额超限",4010:"E2E 服务端主动断开"},x=new Set([1001,1006,1011,1012,1013,1014]);function W(e,t,n){if(void 0!==n&&(!n||Array.isArray(n)||"object"!=typeof n))throw new Error(`Agent ${e} JSON Config 必须是对象`);if(void 0!==t&&U(n))throw new Error(`Agent ${e} 配置不能同时提供 ID 和 JSON Config`)}function L(e,t){if(void 0!==t&&(!t||Array.isArray(t)||"object"!=typeof t))throw new Error(`Agent ${e} 必须是对象`)}class G extends e{constructor(e){var n,i,s;W("ASR",e.asr_id,e.asr_config),W("TTS",void 0,e.tts_config),W("Brain",e.llm_id,e.brain_config),L("features",e.features),L("extras",e.extras);const{e2eServer:r,authToken:a,audio:o,reconnect:l,agentCallbacks:h,webSocketCtor:c,asr_id:u,asr_config:d,tts_config:p,features:m,extras:g,llm_id:E,brain_config:f,session_speak_req_id:S}=e,R=function(e,t){if(null==e)return{};var n={};for(var i in e)if({}.hasOwnProperty.call(e,i)){if(-1!==t.indexOf(i))continue;n[i]=e[i]}return n}(e,N),A=R.onMessage,y=R.onRenderChange,v=R.onNetworkInfo,w=R.onSpeakStateChange,_=R.onVoiceStateChange,b=R.onAudioPlaybackData,T=e=>{var t;null==(t=O)||t.forwardFarendAudio(e),null==b||b(e)},k=null!=S?S:1,C=function(e){if(!e||!U(e))return;const n=e.extra_body;if(void 0!==n&&(!n||Array.isArray(n)||"object"!=typeof n))throw new Error("Agent Brain extra_body 必须是 JSON 对象");return t({},e)}(f);let O;super(t({},R,{session_speak_req_id:k,sessionRequestData:t({},void 0!==u?{asr_id:u}:{},U(d)?{asr_config:d}:{},U(p)?{tts_config:p}:{},U(m)?{features:m}:{},U(g)?{extras:g}:{},void 0!==E?{llm_id:E}:{},void 0!==C?{brain_config:C}:{},{session_speak_req_id:k}),enableClientInterrupt:null==(n=R.enableClientInterrupt)||n,onMessage:e=>{var t,n;A(e),null!=(t=O)&&t.reportingInitializationFailure||null==(n=O)||n.emitError("sdk",String(e.code||"AVATAR_ERROR"),e.message||"Avatar SDK 错误",!0,e)},onRenderChange:e=>{var t;null==y||y(e),null==(t=O)||t.safeEmit(()=>null==h||null==h.onRenderChange?void 0:h.onRenderChange(e))},onNetworkInfo:e=>{var t;null==v||v(e),null==(t=O)||t.safeEmit(()=>null==h||null==h.onNetworkInfo?void 0:h.onNetworkInfo(e))},onSpeakStateChange:(e,t)=>{var n,i,s;null==w||w(e,t),null==(n=O)||n.safeEmit(()=>null==h||null==h.onSpeakStateChange?void 0:h.onSpeakStateChange({state:e,clientSpeakId:t})),["speak_start","start","started"].includes(e)&&(O&&(O.interactionPending=!0,O.voicePlaying=!0),null==(i=O)||i.emitConversation({state:"speaking"})),["speak_end","end","ended","completed","finish","finished"].includes(e)&&(null==(s=O)||s.emitConversation({state:"completed"}))},onVoiceStateChange:(e,t,n)=>{var i;O&&"start"===e&&(O.voicePlaying=!0),"end"===e&&(O&&(O.voicePlaying=!1),null==(i=O)||i.forwardVoiceEnd()),null==_||_(e,t,n)}},b?{onAudioPlaybackData:T}:{})),this.e2eClient=null,this.microphone=null,this.stopASRPromise=null,this.initializationPromise=null,this.rejectInitialization=null,this.initialSessionAttempt=null,this.initialSessionGeneration=0,this.initialTtsaReadySessionIds=new Set,this.initialTtsaReadyBeforeSession=!1,this.asrStartGeneration=0,this.agentState="idle",this.asrState="idle",this.agentDestroyed=!1,this.agentDestroying=!1,this.destroyPromise=null,this.suppressNextVoiceEnd=!1,this.replaySending=!1,this.farendAudioEnabled=!1,this.reconnectTimer=null,this.reconnectAttempt=0,this.reconnectInFlight=!1,this.sessionReloadFallback=!1,this.waitingForTtsaReload=!1,this.stateBeforeReconnect="ready",this.e2eSessionId="",this.currentE2EUrl="",this.serviceReconnect=null,this.serviceMigration=null,this.interactionPending=!1,this.llmPending=!1,this.llmProducedText=!1,this.directSpeakOpen=!1,this.pendingASR=!1,this.autoSendASRToLLM=!0,this.voicePlaying=!1,this.reportingInitializationFailure=!1,this.handleOnlineForReconnect=()=>{this.serviceMigration||"reconnecting"!==this.agentState||this.reconnectInFlight||this.sessionReloadFallback||(this.clearReconnectTimer(),this.scheduleE2EReconnect(!0))},O=this,this.agentOptions={e2eServer:r,authToken:a,audio:o,requestedEnableAec:"boolean"==typeof(null==m||null==(i=m.speech_frontend)?void 0:i.enable_aec)?m.speech_frontend.enable_aec:void 0,callbacks:h,webSocketCtor:c,debugAudioChunks:Boolean(R.enableDebugger)},this.audioInputEnabled=null!=(s=null==o?void 0:o.inputEnabled)&&s,this.autoSendASRToLLM=!1!==(null==m?void 0:m.auto_send_asr_to_llm),this.originalOnAudioPlaybackData=b,this.playbackDataHandler=T,this.reconnectOptions=this.normalizeReconnectOptions(l),this.audioUplink=new P({sendBinary:e=>{var t;return null==(t=this.e2eClient)?void 0:t.sendBinary(e)},onError:e=>this.handleAudioUplinkError(e)})}getAgentState(){return this.agentState}getState(){return this.getAgentState()}getASRState(){return this.asrState}init(e={}){if(this.initializationPromise)return this.initializationPromise;const t=this.initialize(e).finally(()=>{this.initializationPromise===t&&(this.initializationPromise=null)});return this.initializationPromise=t,t}async initialize(e){this.assertNotDestroyed(),this.setAgentState("initializing");const n=new Promise((e,t)=>{this.rejectInitialization=t});n.catch(()=>{});try{const i=t({},e,{onDownloadProgress:e.onDownloadProgress||(()=>{})}),s=await super.initializeSession(i);if(this.assertNotDestroyed(),!s)throw new Error("XmovAvatar 初始化未返回 sessionInfo");for(this.applySessionFeatures(s),this.assertNotDestroyed(),this.beginInitialSession(s);;){const e=this.initialSessionAttempt;if(!e)throw new Error("Agent 初始化缺少会话连接任务");if(await Promise.race([Promise.all([e.e2ePromise,e.ttsaReadyPromise]),n]),e===this.initialSessionAttempt)break}for(this.assertNotDestroyed();this.serviceMigration;)await this.serviceMigration.completion;this.assertE2EOpen(),this.setAgentState("ready"),this.start()}catch(e){if(this.serviceMigration&&!this.agentDestroying&&!this.agentDestroyed)throw this.failServiceMigration("E2E_MIGRATION_FAILED","E2E 更新迁移失败"),e;if(!this.agentDestroyed&&!this.agentDestroying){var i;null==(i=this.e2eClient)||i.disconnect(1e3,"agent_init_failed"),this.e2eClient=null,await this.cleanupBeforeInitComplete("agent_init_failed");try{this.setAgentState("failed")}catch(e){}this.reportingInitializationFailure=!0;try{this.reportInitializationFailure("Agent 初始化失败",e)}catch(e){}finally{this.reportingInitializationFailure=!1}try{this.emitError("sdk","AGENT_INIT_FAILED","Agent 初始化失败",!0,e)}catch(e){}this.agentDestroyed=!0}throw e}finally{var s;this.rejectInitialization=null,this.initialSessionGeneration+=1,null==(s=this.initialSessionAttempt)||s.resolveTtsaReady(),this.initialSessionAttempt=null,this.initialTtsaReadySessionIds.clear(),this.initialTtsaReadyBeforeSession=!1}}beginInitialSession(e){this.e2eSessionId=e.session_id;const t=this.initialSessionAttempt,n=++this.initialSessionGeneration;null==t||t.resolveTtsaReady();let i=()=>{};const s=new Promise(e=>{i=e}),r=this.replaceE2EConnection(e.e2e_resp).catch(e=>{if(n===this.initialSessionGeneration)throw e}),a={sessionInfo:e,e2ePromise:r,ttsaReadyPromise:s,resolveTtsaReady:i};return this.initialSessionAttempt=a,this.initialTtsaReadySessionIds.delete(e.session_id)?a.resolveTtsaReady():!t&&this.initialTtsaReadyBeforeSession&&(this.initialTtsaReadyBeforeSession=!1,a.resolveTtsaReady()),r.catch(()=>{}),r}start(){var e;"initializing"!==this.agentState&&"running"!==this.agentState&&("reconnecting"!==this.agentState?(this.assertE2EOpen(),super.start(),this.setAgentState("running")):this.waitingForTtsaReload&&null!=(e=this.e2eClient)&&e.isOpen&&("running"===this.stateBeforeReconnect&&super.start(),this.finishReconnect()))}onTtsaReady(e){if(this.rejectInitialization){const t=this.initialSessionAttempt,n=null==e?void 0:e.session_id;!t||n&&t.sessionInfo.session_id!==n?n?this.initialTtsaReadySessionIds.add(n):this.initialTtsaReadyBeforeSession=!0:t.resolveTtsaReady()}else"reconnecting"!==this.agentState?"running"===this.agentState&&super.start():this.start()}abortInitialization(e){var t;null==(t=this.rejectInitialization)||t.call(this,e)}async stop(){if(this.agentDestroyed)return;const e="reconnecting"===this.agentState;e&&(this.stateBeforeReconnect="stopped",this.serviceMigration&&(this.serviceMigration.previousState="stopped")),await this.stopASR(),await super.stop(),e||this.setAgentState("stopped")}async switchInvisibleMode(){const e=this.getStatus(),t=e===i.online||e===i.visible;super.switchInvisibleMode(),t&&await this.stopASR()}destroy(e="js_sdk_exit"){return this.destroyPromise?this.destroyPromise:this.agentDestroyed?("destroyed"!==this.agentState&&this.setAgentState("destroyed"),Promise.resolve()):(this.agentDestroying=!0,this.destroyPromise=Promise.resolve().then(()=>this.finishDestroy(e)),this.destroyPromise)}async finishDestroy(e){this.abortInitialization(new Error("Agent 初始化已取消")),this.cancelServiceMigration(),this.serviceReconnect=null,this.cancelReconnect(),this.cancelSessionRestart();try{await this.stopASR()}catch(e){this.emitError("sdk","AGENT_ASR_STOP_FAILED","Agent 销毁时停止 ASR 失败",!1,e)}const t=this.e2eClient;try{await(null==t?void 0:t.disconnectAndWait(1e3,e))}finally{this.e2eClient===t&&(this.e2eClient=null),this.audioUplink.reset()}try{await this.disconnectClientForDestroy(e)}finally{this.agentDestroyed=!0,this.agentDestroying=!1,this.setAgentState("destroyed")}}async ask(e){this.assertRunning(),this.interactionPending=!0,this.llmPending=!0,this.llmProducedText=!1,this.suppressNextVoiceEnd=!1,this.emitConversation({state:"asking",text:e}),this.sendControl({type:"ask",message:{text:e}})}speak(e,t=!0,n=!0){var i;if(this.replaySending)return n&&(this.suppressNextVoiceEnd=!0),super.speak(e,t,n,arguments[3]);"draining"===(null==(i=this.serviceMigration)?void 0:i.phase)&&this.directSpeakOpen&&!t?this.assertE2EOpen():this.assertRunning(),this.interactionPending=!0,this.directSpeakOpen=!n,this.suppressNextVoiceEnd=!1,t&&this.emitConversation({state:"speaking-directly",text:e}),this.sendControl({type:"speak",message:{text:e,is_start:t,is_end:n}})}handleReplaySend(e){if(this.serviceMigration)return;const t=this.replaySending;this.replaySending=!0;try{super.handleReplaySend(e)}finally{this.replaySending=t}}async speakByE2E(e){this.speak(e)}async startASR(e={}){var t;if(this.assertASRStartAllowed(),this.suppressNextVoiceEnd=!1,this.stopASRPromise&&(await this.stopASRPromise,this.assertASRStartAllowed()),null!=(t=this.microphone)&&t.isRecording)return;this.assertAudioChunkDuration();const n=e.inputStream?this.cloneASRInputStream(e.inputStream):void 0,i=++this.asrStartGeneration,s=!this.audioInputEnabled,r=new C({audio:this.agentOptions.audio,debugAudioChunks:this.agentOptions.debugAudioChunks,inputStream:n,usePcmWebCodecs:this.farendAudioEnabled,onFrame:(e,t)=>{var n,i;if(("running"===this.agentState||"draining"===(null==(n=this.serviceMigration)?void 0:n.phase))&&null!=(i=this.e2eClient)&&i.isOpen)try{var s;this.sendAudioFrame({data:e,timestamp:null!=t?t:Date.now()-((null==(s=this.agentOptions.audio)?void 0:s.chunkMs)||l.chunkMs)})}catch(e){this.stopASRAfterSocketClose()}},onError:e=>this.handleMicrophoneError(r,e)});this.microphone=r;try{this.audioUplink.startInput(),s&&this.enableAudioInput(),this.emitConversation({state:"asking"}),this.assertASRStartActive(i),this.setASRState("requesting-permission"),this.assertASRStartActive(i),this.setASRState("starting"),this.assertASRStartActive(i),await r.start(),this.assertRunning(),this.farendAudioEnabled&&this.restartAudioPlaybackCapture(),this.setASRState("listening")}catch(e){var a,o;await r.stop();const t="AUDIO_FIXED_TRACK_CANCELLED"===(null==e?void 0:e.agentCode),n=i===this.asrStartGeneration,l=!this.agentDestroyed&&!this.agentDestroying&&"running"===this.agentState&&Boolean(null==(a=this.e2eClient)?void 0:a.isOpen);if(this.microphone===r&&(this.microphone=null),n&&!t&&s&&null!=(o=this.e2eClient)&&o.isOpen)try{this.disableAudioInput()}catch(e){}throw!n||this.agentDestroyed||this.agentDestroying||(t&&l?this.setASRState("idle"):t||this.setASRState("failed")),t&&!l&&this.assertRunning(),e}}cloneASRInputStream(e){const t=e.getAudioTracks().find(e=>"ended"!==e.readyState);if(!t)throw new Error("ASR 输入流不包含有效音频轨道");return new MediaStream([t.clone()])}assertASRStartActive(e){if(e===this.asrStartGeneration)return;const t=new Error("麦克风启动已取消");throw t.name="AbortError",t.agentCode="AUDIO_FIXED_TRACK_CANCELLED",t.retryable=!0,t}stopASR(){if(this.asrStartGeneration+=1,this.stopASRPromise)return this.stopASRPromise;const e=this.microphone;var t;return null!=e&&e.isRecording?(this.microphone=null,this.stopASRPromise=this.finishStopASR(e).finally(()=>{this.stopASRPromise=null}),this.stopASRPromise):(null!=(t=this.e2eClient)&&t.isOpen&&this.disableAudioInput(),Promise.resolve())}async finishStopASR(e,t=!0){this.setASRState("stopping"),await e.stop(),await this.audioUplink.drain(),t?(this.disableAudioInput(),this.setASRState("idle")):this.setASRState("failed")}interrupt(){if(this.agentDestroyed||this.agentDestroying||this.serviceMigration&&"draining"!==this.serviceMigration.phase)return 0;this.sendControl({type:"interrupt"});const e=super.interrupt("speak");return this.interactionPending=!1,this.llmPending=!1,this.llmProducedText=!1,this.directSpeakOpen=!1,this.pendingASR=!1,this.voicePlaying=!1,this.emitConversation({state:"interrupted"}),this.checkServiceMigration(),e}async interruptConversation(e="user"){this.interrupt()}async onSessionReloaded(e){if(!this.agentDestroyed&&!this.agentDestroying){if(this.cancelServiceMigration(),this.serviceReconnect=null,this.e2eSessionId=e.session_id,this.rejectInitialization)return this.applySessionFeatures(e),void await this.beginInitialSession(e);"reconnecting"!==this.agentState&&(this.stateBeforeReconnect=this.readRestorableState(),this.setAgentState("reconnecting")),this.clearReconnectTimer(),this.sessionReloadFallback=!0,this.applySessionFeatures(e),await this.replaceE2EConnection(e.e2e_resp),this.waitingForTtsaReload=!0,this.sessionReloadFallback=!1}}onSessionReloadExhausted(e){this.rejectInitialization?this.abortInitialization(e instanceof Error?e:new Error("Agent 初始化期间 TTSA Session 恢复失败")):"reconnecting"===this.agentState&&this.failReconnect(e)}async replaceE2EConnection(e,t=!0){const{wsUrl:n,token:i}=this.resolveE2EConnection(e);this.currentE2EUrl=n;const s=this.e2eClient;let r;this.audioUplink.reset(),r=new o({wsUrl:n,token:i,audioInputEnabled:this.audioInputEnabled,WebSocketCtor:this.agentOptions.webSocketCtor,onStateChange:e=>{this.e2eClient===r&&("open"===e&&this.audioUplink.reset(Date.now()),this.safeEmit(()=>{var t;return null==(t=this.agentOptions.callbacks)||null==t.onSocketStateChange?void 0:t.onSocketStateChange(e)}))},onEvent:e=>{var t,n;this.e2eClient===r&&"closing"!==(null==(t=this.serviceMigration)?void 0:t.phase)&&"failed"!==(null==(n=this.serviceMigration)?void 0:n.phase)&&this.handleServerEvent(e)},onError:e=>{this.e2eClient===r&&"initializing"!==this.agentState&&"reconnecting"!==this.agentState&&this.emitError("network","E2E_SOCKET_ERROR",e.message,!0,e)},onClose:e=>{this.e2eClient===r&&this.handleSocketClose(e)}}),this.e2eClient=r,null==s||s.disconnect(1e3,"e2e_connection_replaced"),t&&await r.connect()}resolveE2EConnection(e){const t=Boolean(null==e?void 0:e.ws_url),n=Boolean(null==e?void 0:e.e2e_token);if(t!==n)throw new Error("统一 session 响应的 e2e_resp 必须同时返回 ws_url 和 e2e_token");if(t&&n)return{wsUrl:null==e?void 0:e.ws_url,token:null==e?void 0:e.e2e_token};const i=Boolean(this.agentOptions.e2eServer);if(i!==Boolean(this.agentOptions.authToken))throw new Error("e2eServer 和 authToken 必须同时配置");if(!i)throw new Error("统一 session 响应未返回 e2e_resp.ws_url,且未配置 e2eServer");return{wsUrl:this.agentOptions.e2eServer,token:this.agentOptions.authToken}}sendControl(e){try{var t;null==(t=this.e2eClient)||t.send(e)}catch(e){throw this.emitError("network","E2E_SEND_FAILED","E2E WebSocket 发送失败",!0,e),e}}assertAudioChunkDuration(){var e;const t=(null==(e=this.agentOptions.audio)?void 0:e.chunkMs)||l.chunkMs;if(!this.farendAudioEnabled||t===l.chunkMs)return;const n=new Error("AU v1 音频分片固定为 100ms");throw n.name="NotSupportedError",n.agentCode="AUDIO_CHUNK_DURATION_UNSUPPORTED",n.retryable=!1,this.emitError("sdk",n.agentCode,n.message,n.retryable,n),n}sendAudioFrame(e){if(this.farendAudioEnabled)this.agentOptions.debugAudioChunks&&d("[Agent][E2E] nearend AU chunk",e.data,{timestamp:e.timestamp,stream_id:1}),this.audioUplink.offerNearend(e);else{this.agentOptions.debugAudioChunks&&d("[Agent][E2E] nearend raw WebM/Opus chunk",e.data,{timestamp:e.timestamp});try{var t;null==(t=this.e2eClient)||t.sendBinary(e.data)}catch(e){this.handleAudioUplinkError(e instanceof Error?e:new Error("E2E nearend 音频帧发送失败"))}}}handleAudioUplinkError(e){this.emitError("network","E2E_AUDIO_SEND_FAILED",this.farendAudioEnabled?"E2E AU 音频帧发送失败":"E2E 音频帧发送失败",!0,e);const t=this.microphone;if(null==t||!t.isRecording||this.stopASRPromise)return;this.setASRState("failed"),this.microphone=null;const n=this.finishFailedASR(t).finally(()=>{this.stopASRPromise===n&&(this.stopASRPromise=null)});this.stopASRPromise=n}applySessionFeatures(e){var t,n,i,s,r,a,o,l,h;const c=null!=(t=null==(n=e.e2e_resp)||null==(n=n.features)?void 0:n.auto_send_asr_to_llm)?t:null==(i=e.features)?void 0:i.auto_send_asr_to_llm;"boolean"==typeof c&&(this.autoSendASRToLLM=c);const u=null!=(s=null==(r=e.e2e_resp)||null==(r=r.features)||null==(r=r.speech_frontend)?void 0:r.enable_aec)?s:null==(a=e.features)||null==(a=a.speech_frontend)?void 0:a.enable_aec;if(this.farendAudioEnabled="boolean"==typeof u?u:null!=(o=null!=(l=this.agentOptions.requestedEnableAec)?l:null==(h=this.agentOptions.audio)?void 0:h.echoCancellationEnabled)&&o,this.farendAudioEnabled||this.audioUplink.clearFarend(),this.farendAudioEnabled||this.originalOnAudioPlaybackData)return this.setAudioPlaybackDataHandler(this.playbackDataHandler),void(this.farendAudioEnabled&&this.enableAudioPlaybackCapture());this.setAudioPlaybackDataHandler(void 0)}forwardFarendAudio(e){var t,n;const i=Boolean(null==(t=this.microphone)?void 0:t.isRecording),s=Boolean(null==(n=this.e2eClient)?void 0:n.isOpen);var r;this.farendAudioEnabled&&i&&s?(this.agentOptions.debugAudioChunks&&d("[Agent][E2E] farend AU chunk",e.data,{timestamp:e.timestamp,stream_id:2,codec:e.codec,sampleRate:e.sampleRate,channels:1,samples:e.samples,speech_id:e.speech_id,isFirstChunk:e.isFirstChunk}),this.audioUplink.offerFarend({data:e.data,timestamp:e.timestamp,isFirstChunk:e.isFirstChunk})):null==(r=globalThis.avatarSDKLogger)||null==r.log||r.log("[Agent][AudioUplink]","farend.drop",{reason:this.farendAudioEnabled?i?"e2e_not_open":"microphone_not_recording":"aec_disabled",timestamp:e.timestamp,isFirstChunk:e.isFirstChunk})}handleMicrophoneError(e,t){var n;const i=t,s="MediaRecorderError"===t.name,r="AudioEncoderError"===t.name,a=s||r,o="NotSupportedError"===t.name,l=Boolean(i.agentCode);if(a&&this.microphone===e&&"stopping"!==this.asrState){this.microphone=null;const t=this.finishFailedASR(e).finally(()=>{this.stopASRPromise===t&&(this.stopASRPromise=null)});this.stopASRPromise=t}this.emitError(a?"asr":l||o?"sdk":"permission",i.agentCode||(s?"MEDIA_RECORDER_ERROR":r?"AUDIO_ENCODER_ERROR":"MICROPHONE_ERROR"),t.message,null!=(n=i.retryable)?n:!o,t)}async finishFailedASR(e){try{await e.stop(),await this.audioUplink.drain()}catch(e){this.emitError("sdk","MICROPHONE_CLEANUP_FAILED","麦克风异常后的资源清理失败",!1,e)}try{this.disableAudioInput()}catch(e){}}enableAudioInput(){var e;this.audioInputEnabled||(this.sendControl({type:"event",message:"audio_input_on"}),this.audioInputEnabled=!0,null==(e=this.e2eClient)||e.setAudioInputEnabled(!0))}disableAudioInput(){var e;this.audioInputEnabled&&(this.sendControl({type:"event",message:"audio_input_off"}),this.audioInputEnabled=!1,null==(e=this.e2eClient)||e.setAudioInputEnabled(!1))}handleServerEvent(e){var t;if(!this.agentDestroyed&&!this.agentDestroying)switch(e.type){case"service_update":return void this.handleServiceUpdate(e);case"pong":default:return;case"asr_result":return void this.handleASRResult(e);case"llm_response":return void this.handleLLMResponse(e);case"semantic_judge_round_result":return void this.handleSemanticJudgeResult(e);case"volume_and_repetition_text_response":return void(!0===(null==(t=e.message)?void 0:t.skip)&&(this.pendingASR=!1,this.checkServiceMigration()));case"error":return void this.handleBackendError(e)}}handleASRResult(e){this.autoSendASRToLLM&&(this.pendingASR="string"==typeof e.text&&Boolean(e.text.trim()));const t={text:"string"==typeof e.text?e.text:"",isFinal:!0===e.is_final,raw:e};this.safeEmit(()=>{var e;return null==(e=this.agentOptions.callbacks)||null==e.onASRResult?void 0:e.onASRResult(t)}),this.checkServiceMigration()}handleLLMResponse(e){if("chunk"!==e.event&&"done"!==e.event)return;this.llmPending="done"!==e.event,this.pendingASR=!1,"chunk"===e.event&&e.text&&(this.interactionPending=!0,this.llmProducedText=!0),"done"===e.event&&(this.llmProducedText||(this.interactionPending=!1),this.llmProducedText=!1);const n=e.usage,i=t({event:e.event},"string"==typeof e.text?{text:e.text}:{},!0===e.is_first?{isFirst:!0}:{},n?{usage:{promptTokens:this.readFiniteNumber(n.prompt_tokens),completionTokens:this.readFiniteNumber(n.completion_tokens),totalTokens:this.readFiniteNumber(n.total_tokens),cachedTokens:this.readFiniteNumber(n.cached_tokens)}}:{},{raw:e});this.safeEmit(()=>{var e;return null==(e=this.agentOptions.callbacks)||null==e.onLLMResponse?void 0:e.onLLMResponse(i)}),this.checkServiceMigration()}handleSemanticJudgeResult(e){if(!e.message||"object"!=typeof e.message)return;const t=e.message,n={query:"string"==typeof t.query?t.query:"",meaningful:!0===t.meaningful,action:"string"==typeof t.action?t.action:"",raw:e};this.safeEmit(()=>{var e;return null==(e=this.agentOptions.callbacks)||null==e.onSemanticJudgeResult?void 0:e.onSemanticJudgeResult(n)}),!1===t.meaningful&&(this.pendingASR=!1,this.checkServiceMigration())}forwardVoiceEnd(){var e,t;if(this.suppressNextVoiceEnd)return this.suppressNextVoiceEnd=!1,this.interactionPending=!1,void this.checkServiceMigration();if(!(this.agentDestroyed||this.agentDestroying||"running"!==this.agentState&&"draining"!==(null==(e=this.serviceMigration)?void 0:e.phase)||null==(t=this.e2eClient))&&t.isOpen)try{this.sendControl({type:"event",message:"voice_end"}),this.interactionPending=!1,this.checkServiceMigration()}catch(e){}}readFiniteNumber(e){return"number"==typeof e&&Number.isFinite(e)?e:0}handleBackendError(e){var t;const n=["permission","network","asr","brain","ttsa","quota","sdk"].includes(e.domain)?e.domain:"sdk",i=String(null!=(t=e.code)?t:"BACKEND_ERROR"),s="string"==typeof e.message?e.message:"E2E 后端错误";"asr"===n&&(this.stopASRAfterSocketClose(),this.pendingASR=!1),"brain"===n&&(this.pendingASR=!1,this.llmPending=!1,this.llmProducedText||(this.interactionPending=!1)),this.emitConversation({state:"failed"}),this.emitError(n,i,s,"quota"!==n,e),this.checkServiceMigration()}handleServiceUpdate(e){var t;if(this.serviceMigration&&("connecting"!==this.serviceMigration.phase||null==(t=this.e2eClient)||!t.isOpen))return;let n=null;const i=e.grace_period_ms;try{const t=e.reconnect;if(!t||"string"!=typeof t.ws_url||"string"!=typeof t.e2e_token||!t.e2e_token||t.session_id!==this.e2eSessionId||"number"!=typeof i||!Number.isSafeInteger(i)||i<0||i>F||"number"!=typeof t.expires_in_ms||!Number.isSafeInteger(t.expires_in_ms)||t.expires_in_ms<=0||t.expires_in_ms>F)throw new Error("Invalid migration payload");const s=new URL(t.ws_url);if(!["ws:","wss:"].includes(s.protocol)||s.username||s.password||s.hash||this.currentE2EUrl.startsWith("wss:")&&"wss:"!==s.protocol||1!==s.searchParams.getAll("session_id").length||s.searchParams.get("session_id")!==this.e2eSessionId||1!==s.searchParams.getAll("token").length||s.searchParams.get("token")!==t.e2e_token)throw new Error("Invalid migration URL");n={session_id:this.e2eSessionId,ws_url:t.ws_url,e2e_token:t.e2e_token,expiresAt:Date.now()+t.expires_in_ms,attempts:0}}catch(e){return void(this.serviceMigration?this.failServiceMigration("E2E_SERVICE_UPDATE_INVALID","服务更新缺少有效的同房重连凭据"):this.startServiceMigration(null,0))}this.serviceMigration&&(this.stateBeforeReconnect=this.serviceMigration.previousState,this.cancelServiceMigration()),this.serviceReconnect=n,this.startServiceMigration(n,i)}startServiceMigration(e,t){var n;if(this.serviceMigration||this.agentDestroyed||this.agentDestroying)return;const i="reconnecting"===this.agentState?this.stateBeforeReconnect:this.agentState;this.cancelReconnect();let s=()=>{},r=e=>{};const a=new Promise((e,t)=>{s=e,r=t});a.catch(()=>{});const o={reconnect:e,previousState:i,phase:"draining",departAt:Date.now()+Math.max(0,t-3e3),quietUntil:0,audioStopped:!1,timer:null,expiryTimer:null,completion:a,resolve:s,reject:r};if(this.serviceMigration=o,this.stateBeforeReconnect=this.readRestorableState(),!e)return void this.failServiceMigration("E2E_SERVICE_UPDATE_INVALID","服务更新缺少有效的同房重连凭据");if(o.expiryTimer=setTimeout(()=>{this.failServiceMigration("E2E_MIGRATION_TOKEN_EXPIRED","E2E 迁移凭据已过期,请结束会话后重新初始化")},Math.max(0,e.expiresAt-Date.now())),this.setMigrationState("reconnecting"),this.serviceMigration!==o||this.agentDestroying)return;const l=Boolean((null==(n=this.microphone)?void 0:n.isRecording)||this.stopASRPromise||this.audioInputEnabled);o.timer=setTimeout(()=>{this.closeForServiceMigration(o)},Math.max(0,o.departAt-Date.now())),this.stopASR().then(()=>{this.serviceMigration===o&&"draining"===o.phase&&(o.audioStopped=!0,o.quietUntil=Date.now()+(l?2e3:0),this.checkServiceMigration())}).catch(()=>{this.serviceMigration===o&&this.failServiceMigration("E2E_MIGRATION_AUDIO_STOP_FAILED","E2E 迁移时停止音频失败")})}checkServiceMigration(){const e=this.serviceMigration;if(!e||"draining"!==e.phase||!e.audioStopped)return;const t=e.quietUntil-Date.now();Date.now()>=e.departAt||t<=0&&!this.interactionPending&&!this.llmPending&&!this.directSpeakOpen&&!this.pendingASR&&!this.voicePlaying?this.closeForServiceMigration(e):t>0?(null!==e.timer&&clearTimeout(e.timer),e.timer=setTimeout(()=>this.checkServiceMigration(),Math.max(0,Math.min(t,e.departAt-Date.now())))):(null!==e.timer&&clearTimeout(e.timer),e.timer=setTimeout(()=>{this.closeForServiceMigration(e)},Math.max(0,e.departAt-Date.now())))}async closeForServiceMigration(e){var t;if(this.serviceMigration!==e||"draining"!==e.phase||this.agentDestroying||this.agentDestroyed)return;e.phase="closing",null!==e.timer&&clearTimeout(e.timer),e.timer=null,this.audioUplink.reset(),this.audioInputEnabled=!1;const n=this.e2eClient,i=await(null==n?void 0:n.disconnectAndWait(1e3,"service_update",3e3));if(this.serviceMigration===e&&"closing"===e.phase&&!this.agentDestroying&&!this.agentDestroyed)if(!1!==i){this.e2eClient===n&&(this.e2eClient=null);try{await this.replaceE2EConnection(e.reconnect,!1)}catch(t){return void(this.serviceMigration===e&&this.failServiceMigration("E2E_MIGRATION_FAILED","创建迁移连接失败"))}this.serviceMigration!==e||this.agentDestroying||this.agentDestroyed||(e.phase="connecting",this.interactionPending=!1,this.llmPending=!1,this.llmProducedText=!1,this.directSpeakOpen=!1,this.pendingASR=!1,this.voicePlaying=!1,this.scheduleServiceReconnect(e,0===(null==(t=e.reconnect)?void 0:t.attempts)))}else this.failServiceMigration("E2E_MIGRATION_CLOSE_TIMEOUT","旧 E2E 未确认关闭,已停止迁移以避免双连接")}scheduleServiceReconnect(e,t=!1){var n,i;if(this.serviceMigration!==e||"connecting"!==e.phase||this.agentDestroying||this.agentDestroyed||this.reconnectInFlight)return;null!==e.timer&&clearTimeout(e.timer);const s=t?0:Math.max(100,this.getReconnectDelay((null!=(n=null==(i=e.reconnect)?void 0:i.attempts)?n:1)-1));e.timer=setTimeout(()=>{this.attemptServiceReconnect(e)},s)}async attemptServiceReconnect(e){if(this.serviceMigration!==e||"connecting"!==e.phase||this.agentDestroying||this.agentDestroyed)return;e.timer=null;const t=this.e2eClient;if(!t||!e.reconnect)return;if(Date.now()>=e.reconnect.expiresAt)return void this.failServiceMigration("E2E_MIGRATION_TOKEN_EXPIRED","E2E 迁移凭据已过期,请结束会话后重新初始化");this.reconnectInFlight=!0,e.reconnect.attempts+=1;const n=setTimeout(()=>t.disconnect(1e3,"migration_connect_timeout"),1e4);try{if(await t.connect(),clearTimeout(n),this.serviceMigration!==e||!t.isOpen||this.agentDestroying||this.agentDestroyed)return;if(await this.stopASRPromise,this.serviceMigration!==e||!t.isOpen||this.agentDestroying||this.agentDestroyed)return;try{this.setASRState("idle")}catch(e){}if(this.serviceMigration===e&&!this.agentDestroyed&&!this.agentDestroying){const t=e.previousState;this.cancelServiceMigration(),this.setMigrationState(t)}}catch(e){}finally{clearTimeout(n),this.serviceMigration===e&&(this.reconnectInFlight=!1,this.scheduleServiceReconnect(e))}}failServiceMigration(e,t){var n;const i=this.serviceMigration;i&&"failed"!==i.phase&&(i.phase="failed",null!==i.timer&&clearTimeout(i.timer),null!==i.expiryTimer&&clearTimeout(i.expiryTimer),i.timer=null,i.expiryTimer=null,this.cancelReconnect(),this.stopASRAfterSocketClose(),null==(n=this.e2eClient)||n.disconnect(1e3,"migration_failed"),i.reject(new Error(t)),this.abortInitialization(new Error(t)),this.setMigrationState("failed"),this.emitError("E2E_CLOSE_4009"===e?"quota":"network",e,t,!1))}setMigrationState(e){try{this.setAgentState(e)}catch(e){}}cancelServiceMigration(){const e=this.serviceMigration;e&&(this.serviceMigration=null,null!==e.timer&&clearTimeout(e.timer),null!==e.expiryTimer&&clearTimeout(e.expiryTimer),this.reconnectInFlight=!1,e.resolve())}handleSocketClose(e){if(this.agentDestroyed||this.agentDestroying)return;if(this.serviceMigration){const t=this.serviceMigration;return void("draining"===t.phase?4011===e.code||x.has(e.code)?this.closeForServiceMigration(t):this.failServiceMigration(`E2E_CLOSE_${e.code}`,"E2E 更新迁移被服务端终止"):"connecting"===t.phase&&(4011===e.code||x.has(e.code)?this.scheduleServiceReconnect(t):this.failServiceMigration(`E2E_CLOSE_${e.code}`,"E2E 更新迁移被服务端终止")))}if(4011===e.code)return void this.startServiceMigration(this.serviceReconnect,0);if(this.rejectInitialization)return void this.abortInitialization(new Error(`Agent 初始化期间 E2E WebSocket 已关闭: ${e.code} ${e.reason||""}`.trim()));if(this.audioUplink.reset(),this.stopASRAfterSocketClose(),this.shouldReconnect(e))return void this.beginReconnect(e);this.setAgentState("failed");const t=4009===e.code?"quota":"network";this.emitError(t,`E2E_CLOSE_${e.code}`,e.reason||B[e.code]||"E2E WebSocket 已关闭","quota"!==t,{code:e.code,reason:e.reason,wasClean:e.wasClean})}shouldReconnect(e){return this.reconnectOptions.enabled&&"idle"!==this.agentState&&"initializing"!==this.agentState&&"destroyed"!==this.agentState&&x.has(e.code)}beginReconnect(e){var t;"reconnecting"!==this.agentState&&(this.stateBeforeReconnect=this.readRestorableState(),this.reconnectAttempt=0,this.sessionReloadFallback=!1,this.setAgentState("reconnecting"),null==globalThis.addEventListener||globalThis.addEventListener("online",this.handleOnlineForReconnect),this.emitError("network","E2E_RECONNECTING","E2E WebSocket 已断开,正在重连",!0,{code:e.code,reason:e.reason,wasClean:e.wasClean})),this.audioInputEnabled=!1,null==(t=this.e2eClient)||t.setAudioInputEnabled(!1),this.scheduleE2EReconnect(!1)}scheduleE2EReconnect(e){if(this.agentDestroyed||this.agentDestroying||this.serviceMigration||"reconnecting"!==this.agentState||null!==this.reconnectTimer||this.reconnectInFlight||this.sessionReloadFallback)return;if(this.reconnectAttempt>=this.reconnectOptions.maxAttempts)return void this.fallbackToSessionReload();const t=e?0:this.getReconnectDelay(this.reconnectAttempt);this.reconnectTimer=globalThis.setTimeout(()=>{this.reconnectTimer=null,this.attemptE2EReconnect()},t)}async attemptE2EReconnect(){const e=this.e2eClient;if(e&&!this.serviceMigration&&!this.agentDestroyed&&!this.agentDestroying&&"reconnecting"===this.agentState){this.reconnectInFlight=!0,this.reconnectAttempt+=1;try{await e.connect(),this.e2eClient!==e||this.serviceMigration||this.agentDestroyed||this.agentDestroying||this.waitingForTtsaReload||this.finishReconnect()}catch(t){this.e2eClient!==e||this.serviceMigration||this.agentDestroyed||this.agentDestroying||this.scheduleE2EReconnect(!1)}finally{this.e2eClient!==e||this.serviceMigration||(this.reconnectInFlight=!1),this.e2eClient!==e||this.serviceMigration||"reconnecting"!==this.agentState||null!==this.reconnectTimer||this.sessionReloadFallback||this.scheduleE2EReconnect(!1)}}}async fallbackToSessionReload(){if(!(this.serviceMigration||this.sessionReloadFallback||this.agentDestroyed||this.agentDestroying)){this.sessionReloadFallback=!0,this.clearReconnectTimer();try{await this.restartSessionForTransport("e2e_reconnect_exhausted")}catch(e){this.failReconnect(e)}}}finishReconnect(){const e=this.stateBeforeReconnect;this.cancelReconnect(),"idle"!==this.asrState&&this.setASRState("idle"),this.setAgentState(e)}failReconnect(e){this.cancelReconnect(),this.setAgentState("failed"),this.emitConversation({state:"failed"}),this.emitError("network","E2E_RECONNECT_EXHAUSTED","E2E WebSocket 重连失败,请重新初始化 Agent",!1,e)}cancelReconnect(){this.clearReconnectTimer(),null==globalThis.removeEventListener||globalThis.removeEventListener("online",this.handleOnlineForReconnect),this.reconnectAttempt=0,this.reconnectInFlight=!1,this.sessionReloadFallback=!1,this.waitingForTtsaReload=!1}clearReconnectTimer(){null!==this.reconnectTimer&&(globalThis.clearTimeout(this.reconnectTimer),this.reconnectTimer=null)}getReconnectDelay(e){if(0===this.reconnectOptions.initialDelayMs)return 0;const t=Math.min(this.reconnectOptions.maxDelayMs,this.reconnectOptions.initialDelayMs*2**e);return 0===t?0:Math.round(t*(.8+.4*Math.random()))}readRestorableState(){return"running"===this.agentState||"stopped"===this.agentState||"ready"===this.agentState?this.agentState:"ready"}normalizeReconnectOptions(e){var t;const n=this.readNonNegativeNumber(null==e?void 0:e.initialDelayMs,500);return{enabled:null==(t=null==e?void 0:e.enabled)||t,maxAttempts:Math.max(1,Math.floor(this.readNonNegativeNumber(null==e?void 0:e.maxAttempts,6))),initialDelayMs:n,maxDelayMs:Math.max(n,this.readNonNegativeNumber(null==e?void 0:e.maxDelayMs,8e3))}}readNonNegativeNumber(e,t){return"number"==typeof e&&Number.isFinite(e)&&e>=0?e:t}stopASRAfterSocketClose(){if(this.asrStartGeneration+=1,this.stopASRPromise)return;const e=this.microphone;null!=e&&e.isRecording&&(this.microphone=null,this.stopASRPromise=this.finishStopASR(e,!1).catch(e=>{this.emitError("sdk","MICROPHONE_CLEANUP_FAILED","WebSocket 断开后清理麦克风失败",!1,e)}).finally(()=>{this.stopASRPromise=null}))}setAgentState(e){var t;this.agentState=e,this.agentDestroyed&&"destroyed"!==e||null==(t=this.agentOptions.callbacks)||null==t.onAgentStateChange||t.onAgentStateChange(e)}setASRState(e){this.asrState=e,this.safeEmit(()=>{var t;return null==(t=this.agentOptions.callbacks)||null==t.onASRStateChange?void 0:t.onASRStateChange(e)})}emitConversation(e){this.safeEmit(()=>{var t;return null==(t=this.agentOptions.callbacks)||null==t.onConversationChange?void 0:t.onConversationChange(e)})}emitError(e,t,n,i,s){const r={domain:e,code:t,message:n,retryable:i,cause:s};"asr"===e&&this.setASRState("failed"),this.safeEmit(()=>{var e;return null==(e=this.agentOptions.callbacks)||null==e.onError?void 0:e.onError(r)})}safeEmit(e){this.agentDestroyed||this.agentDestroying||e()}assertRunning(){if(this.assertE2EOpen(),this.serviceMigration)throw new Error("Agent 正在迁移 E2E,请等待恢复后再开始新交互");if("running"!==this.agentState)throw new Error("Agent 尚未启动,请先调用 start()")}assertASRStartAllowed(){if(this.assertRunning(),this.getStatus()===i.invisible)throw new Error("数字人处于隐身状态,无法启动 ASR,请先退出隐身模式")}assertE2EOpen(){var e;if(this.assertNotDestroyed(),null==(e=this.e2eClient)||!e.isOpen)throw new Error("Agent 尚未连接 E2E WebSocket")}assertNotDestroyed(){if(this.agentDestroyed||this.agentDestroying)throw new Error("Agent 已销毁")}}export{G as default};
1
+ import e from"@xmov/avatar";function t(){return t=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var i in n)({}).hasOwnProperty.call(n,i)&&(e[i]=n[i])}return e},t.apply(null,arguments)}var n,i,s,r;!function(e){e[e.CONTAINER_NOT_FOUND=10001]="CONTAINER_NOT_FOUND",e[e.CONNECT_SOCKET_ERROR=10002]="CONNECT_SOCKET_ERROR",e[e.START_SESSION_ERROR=10003]="START_SESSION_ERROR",e[e.STOP_SESSION_ERROR=10004]="STOP_SESSION_ERROR",e[e.INIT_ERROR=10005]="INIT_ERROR",e[e.SOCKET_QUIT=10006]="SOCKET_QUIT",e[e.SOCKET_DISCONNECT=10007]="SOCKET_DISCONNECT",e[e.VIDEO_FRAME_EXTRACT_ERROR=20001]="VIDEO_FRAME_EXTRACT_ERROR",e[e.INIT_WORKER_ERROR=20002]="INIT_WORKER_ERROR",e[e.PROCESS_VIDEO_STREAM_ERROR=20003]="PROCESS_VIDEO_STREAM_ERROR",e[e.FACE_PROCESSING_ERROR=20004]="FACE_PROCESSING_ERROR",e[e.RENDER_BODY_ERROR=20005]="RENDER_BODY_ERROR",e[e.RENDER_FACE_ERROR=20006]="RENDER_FACE_ERROR",e[e.WebGL_RENDER_ERROR=20007]="WebGL_RENDER_ERROR",e[e.RENDER_DATA_MISSING=20008]="RENDER_DATA_MISSING",e[e.RENDER_FACE_DATA_MISSING=20009]="RENDER_FACE_DATA_MISSING",e[e.EVENT_WITHOUT_SPEECH_ID=20010]="EVENT_WITHOUT_SPEECH_ID",e[e.EVENT_TRIGGERED=20011]="EVENT_TRIGGERED",e[e.BACKGROUND_IMAGE_LOAD_ERROR=30001]="BACKGROUND_IMAGE_LOAD_ERROR",e[e.FACE_BIN_LOAD_ERROR=30002]="FACE_BIN_LOAD_ERROR",e[e.INVALID_BODY_NAME=30003]="INVALID_BODY_NAME",e[e.VIDEO_DOWNLOAD_ERROR=30004]="VIDEO_DOWNLOAD_ERROR",e[e.BODY_DATA_EXPIRED=30005]="BODY_DATA_EXPIRED",e[e.EVENT_SPEAK_START_EXPIRED=30006]="EVENT_SPEAK_START_EXPIRED",e[e.EVENT_VOICE_START_EXPIRED=30007]="EVENT_VOICE_START_EXPIRED",e[e.AUDIO_DECODE_ERROR=40001]="AUDIO_DECODE_ERROR",e[e.FACE_DECODE_ERROR=40002]="FACE_DECODE_ERROR",e[e.VIDEO_DECODE_ERROR=40003]="VIDEO_DECODE_ERROR",e[e.EVENT_DECODE_ERROR=40004]="EVENT_DECODE_ERROR",e[e.INVALID_DATA_STRUCTURE=40005]="INVALID_DATA_STRUCTURE",e[e.TTSA_ERROR=40006]="TTSA_ERROR",e[e.AUDIO_DATA_EXPIRED=40007]="AUDIO_DATA_EXPIRED",e[e.NETWORK_DOWN=50001]="NETWORK_DOWN",e[e.NETWORK_UP=50002]="NETWORK_UP",e[e.NETWORK_RETRY=50003]="NETWORK_RETRY",e[e.NETWORK_BREAK=50004]="NETWORK_BREAK",e[e.WEBGL_CONTEXT_LOST=60001]="WEBGL_CONTEXT_LOST",e[e.WEBGL_CONTEXT_RESTORED=60002]="WEBGL_CONTEXT_RESTORED",e[e.WEBGL_CONTEXT_RESTORE_TIMEOUT=60003]="WEBGL_CONTEXT_RESTORE_TIMEOUT",e[e.WEBGL_CONTEXT_RECOVERED=60004]="WEBGL_CONTEXT_RECOVERED",e[e.WEBGL_CONTEXT_RECOVERY_FAILED=60005]="WEBGL_CONTEXT_RECOVERY_FAILED",e[e.WEBGL_CONTEXT_RECOVERY_MAX_ATTEMPTS=60006]="WEBGL_CONTEXT_RECOVERY_MAX_ATTEMPTS",e[e.WEBGL_RESOLUTION_RESTORED=60007]="WEBGL_RESOLUTION_RESTORED",e[e.WEBGL_CONTEXT_MANUAL_RESTARTED=60008]="WEBGL_CONTEXT_MANUAL_RESTARTED"}(n||(n={})),function(e){e[e.online=0]="online",e[e.offline=1]="offline",e[e.network_on=2]="network_on",e[e.network_off=3]="network_off",e[e.close=4]="close",e[e.visible=5]="visible",e[e.invisible=6]="invisible",e[e.stopped=7]="stopped"}(i||(i={})),function(e){e.init="init",e.rendering="rendering",e.pausing="pausing",e.paused="paused",e.resumed="resumed",e.stopped="stopped"}(s||(s={})),function(e){e.normal="normal",e.invisible="invisible"}(r||(r={}));const a=2097152;class o{constructor(e){var t;this.ws=null,this.connectPromise=null,this.rejectConnect=null,this.options=e,this.audioInputEnabled=null==(t=e.audioInputEnabled)||t;const n=e.WebSocketCtor||globalThis.WebSocket;if(!n)throw new Error("当前环境不支持 WebSocket");this.WebSocketCtor=n}get isOpen(){var e;return(null==(e=this.ws)?void 0:e.readyState)===this.WebSocketCtor.OPEN}connect(){var e,t;if(this.isOpen)return Promise.resolve();if(this.connectPromise)return this.connectPromise;let n;return null==(e=(t=this.options).onStateChange)||e.call(t,"connecting"),n=new Promise((e,t)=>{this.rejectConnect=t;let i=!1;const s=new this.WebSocketCtor(this.buildWsUrl(this.options.wsUrl,this.options.token));this.ws=s,s.onopen=()=>{var t,r;this.ws===s&&(i=!0,this.connectPromise===n&&(this.connectPromise=null),this.rejectConnect=null,null==(t=(r=this.options).onStateChange)||t.call(r,"open"),e())},s.onmessage=e=>{if(this.ws!==s)return;const t=this.parseMessage(e.data);var n,i;t&&(null==(n=(i=this.options).onEvent)||n.call(i,t))},s.onerror=()=>{var e,r,a,o;if(this.ws!==s)return;const l=new Error("E2E WebSocket 连接异常");i||(i=!0,this.connectPromise===n&&(this.connectPromise=null),this.rejectConnect=null,t(l)),null==(e=(r=this.options).onStateChange)||e.call(r,"error"),null==(a=(o=this.options).onError)||a.call(o,l)},s.onclose=e=>{var r,a,o,l;this.ws===s&&(this.ws=null,i||(i=!0,this.connectPromise===n&&(this.connectPromise=null),this.rejectConnect=null,t(new Error(`E2E WebSocket 已关闭: ${e.code} ${e.reason||""}`.trim()))),null==(r=(a=this.options).onStateChange)||r.call(a,"closed"),null==(o=(l=this.options).onClose)||o.call(l,e))}}),this.connectPromise=n,this.connectPromise}disconnect(e=1e3,t="client_close"){const n=this.detachSocket();n&&this.closeSocket(n,e,t)}disconnectAndWait(e=1e3,t="client_close",n=3e3){const i=this.ws;return i?(this.cancelPendingConnection(),new Promise(s=>{const r=i.onclose;let a,o=!1;const l=e=>{o||(o=!0,void 0!==a&&globalThis.clearTimeout(a),this.ws===i&&(this.ws=null),i.onclose=r,s(e))};i.onclose=e=>{try{null==r||r.call(i,e)}finally{l(!0)}},a=globalThis.setTimeout(()=>l(!1),Math.max(0,n));try{this.closeSocket(i,e,t)}catch(e){l(!1)}})):Promise.resolve(!0)}detachSocket(){if(!this.ws)return null;const e=this.ws;return this.ws=null,this.cancelPendingConnection(),e}cancelPendingConnection(){this.connectPromise=null;const e=this.rejectConnect;this.rejectConnect=null,null==e||e(new Error("E2E WebSocket 连接已取消"))}closeSocket(e,t,n){e.readyState!==this.WebSocketCtor.OPEN&&e.readyState!==this.WebSocketCtor.CONNECTING||e.close(t,n)}send(e){var t;this.assertOpen(),null==(t=this.ws)||t.send(JSON.stringify(e))}trySend(e){var t;return this.assertOpen(),!(this.ws&&this.ws.bufferedAmount>a||(null==(t=this.ws)||t.send(JSON.stringify(e)),0))}sendBinary(e){var t;if(this.assertOpen(),this.ws&&this.ws.bufferedAmount>a){const e=new Error("E2E WebSocket 发送缓冲区超限");throw e.name="WebSocketBackpressureError",e}null==(t=this.ws)||t.send(e)}ping(){this.send({type:"ping"})}setAudioInputEnabled(e){this.audioInputEnabled=e}assertOpen(){if(!this.isOpen)throw new Error("E2E WebSocket 未连接")}parseMessage(e){if("string"!=typeof e)return null;try{const t=JSON.parse(e);return t&&"string"==typeof t.type?t:null}catch(e){var t,n;return null==(t=(n=this.options).onError)||t.call(n,new Error("E2E WebSocket 消息不是合法 JSON")),null}}buildWsUrl(e,t){try{const n=new URL(e);return!t||n.searchParams.has("token")||n.searchParams.has("e2e_token")||n.searchParams.set("token",t),this.updateAudioInputQuery(n.searchParams),n.toString()}catch(n){const[i,s=""]=e.split("?",2),r=new URLSearchParams(s);!t||r.has("token")||r.has("e2e_token")||r.set("token",t),this.updateAudioInputQuery(r);const a=r.toString();return a?`${i}?${a}`:i}}updateAudioInputQuery(e){this.audioInputEnabled?e.delete("audio_input_off"):e.set("audio_input_off","1")}}const l={format:"webm_opus",mimeType:"audio/webm;codecs=opus",sampleRate:16e3,channels:1,chunkMs:100},h=16e3;class c{constructor(e={}){this.inputTrack=null,this.reader=null,this.writer=null,this.generator=null,this.audioContext=null,this.audioSource=null,this.audioDestination=null,this.pumpPromise=null,this.pumpFinished=!1,this.outputClosed=!1,this.draining=!1,this.inputSampleRate=null,this.resampleBuffer=[],this.resamplePosition=0,this.outputBuffer=[],this.outputTimestamp=null,this.startGeneration=0,this.options=e}async start(e){const t=++this.startGeneration;this.inputTrack=e,this.inputSampleRate=null,this.resampleBuffer=[],this.resamplePosition=0,this.outputBuffer=[],this.outputTimestamp=null,this.pumpFinished=!1,this.outputClosed=!1,this.draining=!1;const n=globalThis,i=n.MediaStreamTrackProcessor,s=n.MediaStreamTrackGenerator,r=n.AudioData;if(!i||!s||!r)return this.startWebAudio(e,n,t);const a=new i({track:e}),o=new s({kind:"audio"}),l=new MediaStream([o]),c=l.getAudioTracks()[0],u=null==c?void 0:c.getSettings();if(void 0!==(null==u?void 0:u.sampleRate)&&u.sampleRate!==h||void 0!==(null==u?void 0:u.channelCount)&&1!==u.channelCount)throw o.stop(),this.unsupported(`浏览器音频输出格式不符合要求: ${(null==u?void 0:u.sampleRate)||"unknown"} Hz / ${(null==u?void 0:u.channelCount)||"unknown"} channel`);try{this.reader=a.readable.getReader(),this.writer=o.writable.getWriter()}catch(t){throw o.stop(),"ended"!==e.readyState&&e.stop(),this.inputTrack=null,t}return this.generator=o,this.pumpPromise=this.pump(r),l}async startWebAudio(e,t,n){const i=globalThis.AudioContext||t.webkitAudioContext;if(!i)throw this.unsupported("当前浏览器不支持 16 kHz mono 音频处理");let s=null,r=null,a=null;try{if(s=new i({sampleRate:h}),this.audioContext=s,s.sampleRate!==h)throw this.unsupported(`浏览器无法创建 16 kHz AudioContext: ${s.sampleRate} Hz`);if("suspended"===s.state)try{await s.resume()}catch(e){throw this.contextSuspended(e)}if(this.assertStartActive(n),"running"!==s.state)throw this.contextSuspended();r=s.createMediaStreamSource(new MediaStream([e])),this.audioSource=r,a=s.createMediaStreamDestination(),this.audioDestination=a,a.channelCount=1,a.channelCountMode="explicit",a.channelInterpretation="speakers",r.connect(a);const t=a.stream.getAudioTracks()[0];if(!t)throw new Error("Web Audio 未返回音频输出轨道");return this.validateOutputTrack(t),this.assertStartActive(n),a.stream}catch(t){var o,l;throw null==(o=r)||o.disconnect(),null==(l=a)||l.stream.getTracks().forEach(e=>e.stop()),s&&"closed"!==s.state&&await s.close().catch(()=>{}),"ended"!==e.readyState&&e.stop(),this.inputTrack=null,this.audioContext===s&&(this.audioContext=null,this.audioSource=null,this.audioDestination=null),this.normalizeWebAudioError(t)}}async stop(){await this.drain(),this.audioContext?await this.closeWebAudioOutput():await this.closeOutput()}async drain(){var e;if(this.startGeneration+=1,this.draining=!0,null==(e=this.inputTrack)||e.stop(),this.inputTrack=null,this.audioContext)return;const t=this.pumpPromise;if(t){const e=t.then(()=>!0,()=>!0);var n,i;await Promise.race([e,this.delay(1e3)])||(await Promise.race([null==(n=this.reader)?void 0:n.cancel().catch(()=>{}),this.delay(100)]),await Promise.race([null==(i=this.writer)?void 0:i.abort(new Error("16 kHz mono 音频处理停止超时")).catch(()=>{}),this.delay(100)]),this.stopGenerator())}}async closeOutput(){const e=this.writer;this.outputClosed=!0,e&&this.pumpFinished?await Promise.race([e.close().catch(()=>{}),this.delay(100)]):await Promise.race([null==e?void 0:e.abort(new Error("16 kHz mono 音频处理停止超时")).catch(()=>{}),this.delay(100)]),this.stopGenerator(),e&&this.pumpFinished&&e.releaseLock(),this.reader=null,this.writer=null,this.generator=null,this.pumpPromise=null}async closeWebAudioOutput(){const e=this.audioContext,t=this.audioSource,n=this.audioDestination;this.audioContext=null,this.audioSource=null,this.audioDestination=null,null==t||t.disconnect(),null==n||n.disconnect(),null==n||n.stream.getTracks().forEach(e=>e.stop()),e&&"closed"!==e.state&&await e.close().catch(()=>{})}async pump(e){const t=this.reader,n=this.writer;if(t&&n)try{var i;let a=!1;for(;;){const i=await t.read();if(i.done){a=!0;break}if(!i.value)continue;const s=i.value;try{this.validateAudioData(s);const t=this.toMono(s),i=this.resample(t,s.sampleRate,!1);null===this.outputTimestamp&&(this.outputTimestamp=Number.isFinite(s.timestamp)?s.timestamp:0);for(const t of i)await this.writeChunk(n,e,t)}finally{s.close()}}for(const t of this.resample(new Float32Array,this.inputSampleRate||h,!0))await this.writeChunk(n,e,t);if(a&&!this.draining&&"ended"===(null==(i=this.inputTrack)?void 0:i.readyState)){var s,r;const e=new Error("麦克风音频轨道已结束");e.name="AudioTransformError",null==(s=(r=this.options).onError)||s.call(r,e),this.stopGenerator()}}catch(e){var a,o;const t=e instanceof Error?e:new Error("16 kHz mono 音频处理失败");t.name="AudioTransformError",null==(a=(o=this.options).onError)||a.call(o,t)}finally{t.releaseLock(),this.pumpFinished=!0,this.outputClosed&&n.releaseLock()}}toMono(e){const t=Math.max(1,e.numberOfChannels),n=new Float32Array(e.numberOfFrames);for(let i=0;i<t;i+=1){const s=new Float32Array(e.numberOfFrames);e.copyTo(s,{format:"f32-planar",planeIndex:i});for(let e=0;e<s.length;e+=1)n[e]+=s[e]/t}return n}resample(e,t,n){if(null===this.inputSampleRate)this.inputSampleRate=t;else if(this.inputSampleRate!==t)throw new Error(`输入音频采样率发生变化: ${this.inputSampleRate} -> ${t}`);for(const t of e)this.resampleBuffer.push(t);if(this.resampleBuffer.length>192e3)throw new Error("输入音频重采样缓冲区超限");n&&this.resampleBuffer.length>0&&this.resampleBuffer.push(this.resampleBuffer[this.resampleBuffer.length-1]);const i=t/h,s=[];for(;this.resamplePosition+1<this.resampleBuffer.length;){const e=Math.floor(this.resamplePosition),t=Math.min(e+1,this.resampleBuffer.length-1),n=this.resamplePosition-e;this.outputBuffer.push(this.resampleBuffer[e]*(1-n)+this.resampleBuffer[t]*n),this.resamplePosition+=i;const r=Math.floor(this.resamplePosition);r>0&&(this.resampleBuffer.splice(0,r),this.resamplePosition-=r),this.outputBuffer.length>=320&&s.push(new Float32Array(this.outputBuffer.splice(0,320)))}return n&&this.outputBuffer.length>0&&s.push(new Float32Array(this.outputBuffer.splice(0))),s}async writeChunk(e,t,n){var i;if(!n.length)return;const s=this.outputTimestamp||0,r=new t({format:"f32-planar",sampleRate:h,numberOfFrames:n.length,numberOfChannels:1,timestamp:s,data:n});this.outputTimestamp=s+Math.round(1e6*n.length/h),await e.write(r),r.close();const a=null==(i=this.generator)?void 0:i.getSettings();if(a&&(void 0!==a.sampleRate&&a.sampleRate!==h||void 0!==a.channelCount&&1!==a.channelCount))throw this.unsupported(`浏览器音频输出格式不符合要求: ${a.sampleRate||"unknown"} Hz / ${a.channelCount||"unknown"} channel`)}validateAudioData(e){if(!Number.isFinite(e.sampleRate)||!Number.isInteger(e.sampleRate)||e.sampleRate<8e3||e.sampleRate>192e3)throw new Error(`输入音频采样率无效: ${e.sampleRate}`);if(!Number.isInteger(e.numberOfChannels)||e.numberOfChannels<1||e.numberOfChannels>8)throw new Error(`输入音频声道数无效: ${e.numberOfChannels}`);if(!Number.isInteger(e.numberOfFrames)||e.numberOfFrames<1||e.numberOfFrames>96e3)throw new Error(`输入音频帧数无效: ${e.numberOfFrames}`);if(!Number.isFinite(e.timestamp)||Math.abs(e.timestamp)>1e15)throw new Error(`输入音频时间戳无效: ${e.timestamp}`)}delay(e){return new Promise(t=>{globalThis.setTimeout(()=>t(!1),e)})}stopGenerator(){this.generator&&"ended"!==this.generator.readyState&&this.generator.stop()}unsupported(e,t){const n=new Error(e);return n.name="NotSupportedError",n.agentCode="AUDIO_FIXED_TRACK_UNSUPPORTED",n.retryable=!1,n.cause=t,n}contextSuspended(e){const t=new Error("AudioContext 未运行,请在用户操作后重试");return t.name="NotAllowedError",t.agentCode="AUDIO_CONTEXT_SUSPENDED",t.retryable=!0,t.cause=e,t}assertStartActive(e){if(e!==this.startGeneration){const e=new Error("16 kHz mono 音频处理启动已取消");throw e.name="AbortError",e.agentCode="AUDIO_FIXED_TRACK_CANCELLED",e.retryable=!0,e}}normalizeWebAudioError(e){const t=e instanceof Error?e:new Error(String(e));if(t.agentCode)return t;if("NotSupportedError"===t.name)return this.unsupported(`浏览器无法初始化 16 kHz mono Web Audio: ${t.message}`,t);const n=new Error(`16 kHz mono Web Audio 初始化失败: ${t.message}`);return n.name=t.name,n.agentCode="AUDIO_FIXED_TRACK_FAILED",n.retryable=!0,n.cause=t,n}validateOutputTrack(e){const t=e.getSettings();if(void 0!==t.sampleRate&&t.sampleRate!==h||void 0!==t.channelCount&&1!==t.channelCount)throw this.unsupported(`浏览器音频输出格式不符合要求: ${t.sampleRate||"unknown"} Hz / ${t.channelCount||"unknown"} channel`)}}function u(e){return e.toString(16).padStart(2,"0")}async function d(e,n,i={},s=64){try{const r=n instanceof Blob?await n.arrayBuffer():ArrayBuffer.isView(n)?n.buffer.slice(n.byteOffset,n.byteOffset+n.byteLength):n,a=new Uint8Array(r),o=Array.from(a.slice(0,s)),l=n instanceof Blob?n.type:i.type;console.log(e,t({},i,{size:a.byteLength,type:l,previewLength:o.length,bytes:o,hex:o.map(u).join(" ")}))}catch(t){console.warn(e,"failed to read audio chunk",t)}}const p=new WeakMap,m=new WeakMap;function g(e){return e.audioSession}function E(e,t){try{return e.type=t,e.type===t}catch(e){return!1}}function f(){const e=globalThis.performance;return e&&Number.isFinite(e.timeOrigin)&&"function"==typeof e.now?e.timeOrigin+e.now():Date.now()}class S{constructor(e,t=f){this.timecodeScale=null,this.firstTimecode=null,this.lastTimecode=null,this.lastObservedAt=null,this.lastTimestamp=null,this.chunkMs=e,this.now=t,this.startedAt=t()}resolve(e){const t=this.now(),n=Number.isFinite(e)&&e>=0;if(n&&null===this.firstTimecode&&(this.firstTimecode=e),n&&null===this.timecodeScale&&null!==this.lastTimecode&&e>this.lastTimecode){const n=e-this.lastTimecode,i=null===this.lastObservedAt?0:t-this.lastObservedAt,s=i>=this.chunkMs/4?i:this.chunkMs,r=Math.abs(n-s),a=Math.abs(1e3*n-s);this.timecodeScale=a<r?1e3:1}let i=t-this.chunkMs;const s=n&&null!==this.firstTimecode?e-this.firstTimecode:null;var r;null!==s&&s>=0&&(null!==this.timecodeScale||0===s)&&(i=this.startedAt+s*(null!=(r=this.timecodeScale)?r:1)),null!==this.lastTimestamp&&(i=Math.max(i,this.lastTimestamp+this.chunkMs));const a=Math.max(Math.round(this.startedAt),Math.round(i));return this.lastTimecode=n?e:this.lastTimecode,this.lastObservedAt=t,this.lastTimestamp=a,a}}const R=2e4;function A(e){return{codec:"opus",sampleRate:e.sampleRate,numberOfChannels:e.channels,bitrate:e.bitrate,opus:{format:"opus",frameDuration:R}}}class y{constructor(e={}){this.encoder=null,this.isInitialized=!1,this.pendingEncode=null,this.timestamp=0,this.config=t({sampleRate:16e3,channels:1,bitrate:24e3,frameSize:1600},e)}static async isSupported(){if("undefined"==typeof AudioEncoder)return!1;try{var e;const t=await AudioEncoder.isConfigSupported(A({sampleRate:16e3,channels:1,bitrate:24e3,frameSize:1600}));return!0===t.supported&&(null==(e=t.config)||null==(e=e.opus)?void 0:e.frameDuration)===R}catch(e){return!1}}async init(){this.isInitialized||(this.encoder=this.createEncoder(),this.isInitialized=!0)}async encode(e){if(!this.isInitialized||!this.encoder)return Promise.reject(new Error("WebCodecOpusEncoder 未初始化"));if(this.pendingEncode)return Promise.reject(new Error("WebCodecs Opus 编码器仍在处理上一音频块"));const t=new Int16Array(e),n=this.timestamp,i=Math.round(1e6*e.length/this.config.sampleRate),s=n+i,r=i/R;if(!Number.isInteger(r))return Promise.reject(new Error("PCM 音频块无法按 20ms Opus packet 完整分包"));const a=new AudioData({format:"s16",sampleRate:this.config.sampleRate,numberOfFrames:t.length,numberOfChannels:this.config.channels,timestamp:n,data:t}),o=new Promise((e,t)=>{const i=globalThis.setTimeout(()=>{this.failPending(new Error("WebCodecs Opus 编码输出超过 2 秒"))},2e3);this.pendingEncode={startTimestampUs:n,endTimestampUs:s,expectedFrameCount:r,chunks:[],resolve:e,reject:t,timeoutId:i}});try{this.encoder.encode(a),this.timestamp=s}catch(e){this.failPending(e instanceof Error?e:new Error("WebCodecs Opus 编码失败"))}finally{a.close()}return o}reset(){if(this.timestamp=0,this.failPending(new Error("WebCodecs Opus 编码已重置")),this.encoder){try{this.encoder.close()}catch(e){}this.encoder=this.createEncoder()}}async destroy(){const e=this.encoder;if(e){try{await e.flush()}catch(e){}this.failPending(new Error("WebCodecs Opus 编码器已销毁"));try{e.close()}catch(e){}}this.encoder=null,this.isInitialized=!1}createEncoder(){let e;return e=new AudioEncoder({output:t=>{this.encoder===e&&this.handleOutput(t)},error:t=>{if(this.encoder!==e)return;const n=t instanceof Error?t:new Error("WebCodecs Opus 编码失败");(globalThis.avatarSDKLogger||console).error("[WebCodecOpusEncoder] 编码错误:",n),this.failPending(n)}}),e.configure(A(this.config)),e}handleOutput(e){const t=this.pendingEncode;if(!t)return;if(e.timestamp<t.startTimestampUs||e.timestamp>=t.endTimestampUs||null!==e.duration&&e.duration!==R)return void this.failPending(new Error("WebCodecs Opus 未按 20ms 分包,无法生成固定 100ms AU 音频"));if(t.chunks.push(e),t.chunks.length>t.expectedFrameCount)return void this.failPending(new Error("WebCodecs Opus 未按 20ms 分包,无法生成固定 100ms AU 音频"));if(t.chunks.length<t.expectedFrameCount)return;const n=t.chunks.map((e,n)=>{var i,s;const r=new Uint8Array(e.byteLength);e.copyTo(r);const a=null!=(i=null==(s=t.chunks[n+1])?void 0:s.timestamp)?i:t.endTimestampUs;return{data:r,durationMs:(e.duration&&e.duration>0?e.duration:Math.max(1,a-e.timestamp))/1e3}}),i=n.reduce((e,t)=>e+t.durationMs,0);n.some(e=>20!==e.durationMs)||i!==(t.endTimestampUs-t.startTimestampUs)/1e3?this.failPending(new Error("WebCodecs Opus 未按 20ms 分包,无法生成固定 100ms AU 音频")):(globalThis.clearTimeout(t.timeoutId),this.pendingEncode=null,t.resolve(n))}failPending(e){const t=this.pendingEncode;t&&(globalThis.clearTimeout(t.timeoutId),this.pendingEncode=null,t.reject(e))}}class v{constructor(e=16e3){this.chunks=[],this.hasHeader=!1,this.clusterTimestamp=0,this.frameDuration=100,this.sampleRate=e}writeHeader(){if(this.hasHeader)return;const e=this.buildEBMLHeader(),t=this.buildSegmentInfo(),n=this.buildOpusTracks();this.chunks.push(e),this.chunks.push(new Uint8Array([24,83,128,103,1,255,255,255,255,255,255,255])),this.chunks.push(t),this.chunks.push(n),this.hasHeader=!0,this.clusterTimestamp=0}feedOpusFrame(e,t=this.frameDuration){this.hasHeader||this.writeHeader();const n=this.buildCluster(Math.round(this.clusterTimestamp),e);this.chunks.push(n),this.clusterTimestamp+=t}flush(){if(0===this.chunks.length)return new ArrayBuffer(0);const e=this.chunks.reduce((e,t)=>e+t.length,0),t=new Uint8Array(e);let n=0;for(const e of this.chunks)t.set(e,n),n+=e.length;return this.chunks=[],t.buffer}reset(){this.chunks=[],this.hasHeader=!1,this.clusterTimestamp=0}encodeVINT(e,t){if(void 0!==t){const n=new Uint8Array(t);for(let i=t-1;i>=0;i--)n[i]=255&e,e>>>=8;return n[0]|=1<<8-t,n}if(e<127)return new Uint8Array([128|e]);if(e<16383)return new Uint8Array([64|e>>8,255&e]);if(e<2097151)return new Uint8Array([32|e>>16,e>>8&255,255&e]);if(e<268435455)return new Uint8Array([16|e>>24,e>>16&255,e>>8&255,255&e]);throw new Error(`VINT value too large: ${e}`)}encodeID(e){return e<=255?new Uint8Array([e]):e<=65535?new Uint8Array([e>>8,255&e]):e<=16777215?new Uint8Array([e>>16,e>>8&255,255&e]):new Uint8Array([e>>>24,e>>16&255,e>>8&255,255&e])}encodeUint(e){if(0===e)return new Uint8Array([0]);const t=[];let n=e;for(;n>0;)t.unshift(255&n),n=Math.floor(n/256);return new Uint8Array(t)}encodeString(e){return(new TextEncoder).encode(e)}encodeFloat32(e){const t=new ArrayBuffer(4);return new DataView(t).setFloat32(0,e,!1),new Uint8Array(t)}encodeElement(e,t){const n=this.encodeID(e),i=this.encodeVINT(t.length),s=new Uint8Array(n.length+i.length+t.length);return s.set(n,0),s.set(i,n.length),s.set(t,n.length+i.length),s}encodeUintElement(e,t){return this.encodeElement(e,this.encodeUint(t))}encodeStringElement(e,t){return this.encodeElement(e,this.encodeString(t))}encodeFloat32Element(e,t){return this.encodeElement(e,this.encodeFloat32(t))}concatArrays(e){const t=e.reduce((e,t)=>e+t.length,0),n=new Uint8Array(t);let i=0;for(const t of e)n.set(t,i),i+=t.length;return n}buildEBMLHeader(){const e=this.concatArrays([this.encodeUintElement(17030,1),this.encodeUintElement(17143,1),this.encodeUintElement(17138,4),this.encodeUintElement(17139,8),this.encodeStringElement(17026,"webm"),this.encodeUintElement(17031,4),this.encodeUintElement(17029,2)]);return this.encodeElement(440786851,e)}buildSegmentInfo(){const e=this.concatArrays([this.encodeUintElement(2807729,1e6),this.encodeStringElement(19840,"xmov-avatar-sdk"),this.encodeStringElement(22337,"xmov-avatar-sdk")]);return this.encodeElement(357149030,e)}buildOpusTracks(){const e=new Uint8Array([79,112,117,115,72,101,97,100,1,1,0,0,128,62,0,0,0,0,0]),t=this.concatArrays([this.encodeFloat32Element(181,this.sampleRate),this.encodeUintElement(159,1)]),n=this.concatArrays([this.encodeUintElement(215,1),this.encodeUintElement(29637,1),this.encodeUintElement(131,2),this.encodeUintElement(156,0),this.encodeStringElement(2274716,"und"),this.encodeStringElement(134,"A_OPUS"),this.encodeElement(25506,e),this.encodeUintElement(22186,0),this.encodeUintElement(22203,8e7),this.encodeElement(225,t)]),i=this.encodeElement(174,n);return this.encodeElement(374648427,i)}buildCluster(e,t){const n=this.encodeUintElement(231,e),i=this.encodeVINT(1),s=new Uint8Array([0,0]),r=new Uint8Array([128]),a=this.concatArrays([i,s,r,t]),o=this.encodeElement(163,a),l=this.concatArrays([n,o]);return this.encodeElement(524531317,l)}}class w{constructor(){this.opusEncoder=new y({sampleRate:16e3,channels:1,bitrate:24e3,frameSize:1600}),this.muxer=new v(16e3),this.queue=Promise.resolve(),this.generation=0,this.destroyed=!1,this.pendingBlocks=0}static isSupported(){return y.isSupported()}async init(){await this.opusEncoder.init()}async encode(e){var t=this;if(1600!==e.length)throw new Error(`PCM 音频块必须为 1600 samples,实际为 ${e.length}`);if(this.pendingBlocks>=20)throw new Error("PCM/Opus 编码队列超过 2 秒");this.pendingBlocks+=1;const n=this.generation,i=new Int16Array(e),s=this.queue.then(async function(){if(t.destroyed||n!==t.generation)return new ArrayBuffer(0);const e=await t.opusEncoder.encode(i);if(t.destroyed||n!==t.generation)return new ArrayBuffer(0);for(const n of e)t.muxer.feedOpusFrame(n.data,n.durationMs);return t.muxer.flush()}).finally(()=>{this.pendingBlocks-=1});return this.queue=s.then(()=>{},()=>{}),s}reset(){const e=++this.generation;this.queue=this.queue.then(()=>{this.destroyed||e!==this.generation||(this.opusEncoder.reset(),this.muxer.reset())})}async destroy(){this.destroyed=!0,this.generation+=1,await this.opusEncoder.destroy(),await this.queue,this.muxer.reset()}}const _="audio/webm;codecs=opus",b=16e3,T="agent-pcm-capture-processor",k=`\nclass AgentPcmCaptureProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.buffer = new Float32Array(1600);\n this.bufferIndex = 0;\n this.sequence = 0;\n this.port.onmessage = (event) => {\n if (event.data?.type === 'flush') {\n this.emitBlock(true);\n this.port.postMessage({ type: 'flushed' });\n }\n };\n }\n\n emitBlock(padPartial = false) {\n if (this.bufferIndex === 0 || (!padPartial && this.bufferIndex < 1600)) return;\n const pcm = new Int16Array(1600);\n for (let index = 0; index < this.bufferIndex; index += 1) {\n const sample = Math.max(-1, Math.min(1, this.buffer[index]));\n pcm[index] = sample < 0 ? sample * 0x8000 : sample * 0x7fff;\n }\n this.port.postMessage(\n { type: 'frame', pcm: pcm.buffer, sequence: this.sequence++ },\n [pcm.buffer],\n );\n this.buffer.fill(0);\n this.bufferIndex = 0;\n }\n\n process(inputs, outputs) {\n for (const output of outputs) {\n for (const channel of output) channel.fill(0);\n }\n const channels = inputs[0];\n if (!channels?.length || !channels[0]?.length) return true;\n for (let frame = 0; frame < channels[0].length; frame += 1) {\n let sample = 0;\n for (let channel = 0; channel < channels.length; channel += 1) {\n sample += channels[channel][frame] || 0;\n }\n this.buffer[this.bufferIndex++] = sample / channels.length;\n if (this.bufferIndex === 1600) this.emitBlock();\n }\n return true;\n }\n}\nregisterProcessor('${T}', AgentPcmCaptureProcessor);\n`;class C{constructor(e){var n,i;this.stream=null,this.recordingStream=null,this.audioTransformer=null,this.mediaRecorder=null,this.pcmAudioContext=null,this.pcmSource=null,this.pcmWorklet=null,this.pcmEncoder=null,this.pcmEncodeQueue=Promise.resolve(),this.pcmStartedAt=0,this.pcmGeneration=0,this.resolvePcmFlush=null,this.startGeneration=0,this.starting=!1,this.hasAudioSessionCapture=!1,this.options=e,this.pendingInputStream=null!=(n=e.inputStream)?n:null,this.usesProvidedInput=Boolean(e.inputStream),this.metadata=t({},l,{chunkMs:(null==(i=e.audio)?void 0:i.chunkMs)||l.chunkMs})}get isRecording(){return this.starting||Boolean(this.stream)||Boolean(this.pendingInputStream)}getAudioMetadata(){return this.metadata}async start(){if(this.stream||this.starting)return;const e=++this.startGeneration;this.starting=!0;try{var n,i;const a=this.pendingInputStream;this.stream=null!=a?a:null,this.pendingInputStream=null;const o=navigator.mediaDevices;if(!(this.stream||null!=o&&o.getUserMedia))throw new Error("当前浏览器不支持麦克风采集");const l=globalThis.MediaRecorder;if(this.options.usePcmWebCodecs){if(!await w.isSupported())throw this.webCodecsOpusUnsupported()}else if(null==l||null==l.isTypeSupported||!l.isTypeSupported(_))throw this.webMOpusUnsupported();if(this.assertStartActive(e),!this.stream){if(this.usesProvidedInput)throw a?this.cancelled():new Error("ASR 输入流已释放");this.hasAudioSessionCapture=function(e=navigator){var t,n;const i=g(e);if(!i)return!1;const s=p.get(i),r=null!=(t=null==s?void 0:s.previousType)?t:i.type;return!!E(i,"play-and-record")&&(p.set(i,{count:(null!=(n=null==s?void 0:s.count)?n:0)+1,previousType:r}),!0)}(),this.stream=await o.getUserMedia({audio:{sampleRate:{ideal:b},channelCount:{ideal:1},echoCancellation:!0,noiseSuppression:!0,autoGainControl:!0}})}this.assertStartActive(e);const h=this.stream.getAudioTracks()[0];if(!h)throw new Error("麦克风未返回音频轨道");var s,r;if(this.options.usePcmWebCodecs)return await this.startPcmWebMRecorder(this.stream),this.assertStartActive(e),this.metadata=t({},this.metadata,{sampleRate:b,channels:1,chunkMs:100}),void(null==(s=(r=this.options).onMetadata)||s.call(r,this.metadata));const u=new c({onError:e=>{var t,n;const i=e instanceof Error?e:new Error("16 kHz mono 音频处理失败");i.name="MediaRecorderError",null==(t=(n=this.options).onError)||t.call(n,i)}});this.audioTransformer=u,this.recordingStream=await u.start(h),this.assertStartActive(e);const d=this.recordingStream.getAudioTracks()[0],m=null==d?void 0:d.getSettings();this.metadata=t({},this.metadata,{sampleRate:(null==m?void 0:m.sampleRate)||b,channels:(null==m?void 0:m.channelCount)||1}),this.startWebMRecorder(l),null==(n=(i=this.options).onMetadata)||n.call(i,this.metadata)}catch(t){const n=e!==this.startGeneration;await this.stop();const i=n?this.cancelled():t instanceof Error?t:new Error("麦克风启动失败");var a,o;throw"AUDIO_FIXED_TRACK_CANCELLED"!==i.agentCode&&(null==(a=(o=this.options).onError)||a.call(o,i)),i}finally{this.startGeneration===e&&(this.starting=!1)}}async stop(){this.startGeneration+=1,this.starting=!1;try{var e;const t=this.audioTransformer;try{await(null==t?void 0:t.drain())}finally{await this.stopPcmWebMRecorder(),await this.stopMediaRecorder(),await(null==t?void 0:t.stop()),this.audioTransformer=null}this.recordingStream=null,this.stream&&!t&&this.stream.getTracks().forEach(e=>e.stop()),this.stream=null,null==(e=this.pendingInputStream)||e.getTracks().forEach(e=>e.stop()),this.pendingInputStream=null}finally{this.hasAudioSessionCapture&&(this.hasAudioSessionCapture=!1,function(e=navigator){const t=g(e);if(!t)return!1;const n=p.get(t);if(!n)return function(e=navigator){const t=g(e);if(!t)return!1;const n=p.get(t);return E(t,null!=n&&n.count||"play-and-record"===t.type?"play-and-record":"playback")}(e);if(n.count-=1,n.count>0)return E(t,"play-and-record");p.delete(t);const i="play-and-record"===n.previousType?"play-and-record":"playback";E(t,i)&&"playback"===i&&function(e){const t=m.get(e);if(t)for(const e of[...t])try{e()}catch(e){}}(t)}())}}startWebMRecorder(e){if(!this.recordingStream)throw new Error("16 kHz mono 音频流未就绪");let t;try{t=new e(this.recordingStream,{mimeType:_,audioBitsPerSecond:24e3})}catch(e){if(e instanceof Error&&"NotSupportedError"===e.name)throw this.webMOpusUnsupported(e);throw e}const n=new S(this.metadata.chunkMs);t.ondataavailable=e=>{if(e.data.size>0){var i;const s=n.resolve(e.timecode);null==(i=globalThis.avatarSDKLogger)||null==i.log||i.log("[Agent][Microphone]","audio chunk",{size:e.data.size,type:e.data.type,chunkMs:this.metadata.chunkMs,sampleRate:this.metadata.sampleRate,channels:this.metadata.channels,recorderState:t.state,timecode:e.timecode}),this.options.debugAudioChunks&&d("[Agent][Microphone] nearend WebM/Opus chunk",e.data,{chunkMs:this.metadata.chunkMs,sampleRate:this.metadata.sampleRate,channels:this.metadata.channels,recorderState:t.state,timecode:e.timecode}),this.options.onFrame(e.data,s)}},t.onerror=e=>{var t,n;const i=e.error,s=new Error(i instanceof Error?i.message:"WebM/Opus 麦克风录制失败");s.name="MediaRecorderError",null==(t=(n=this.options).onError)||t.call(n,s)},this.mediaRecorder=t;try{t.start(this.metadata.chunkMs)}catch(e){if(e instanceof Error&&"NotSupportedError"===e.name)throw this.webMOpusUnsupported(e);throw e}}assertStartActive(e){if(e!==this.startGeneration)throw this.cancelled()}cancelled(){const e=new Error("麦克风启动已取消");return e.name="AbortError",e.agentCode="AUDIO_FIXED_TRACK_CANCELLED",e.retryable=!0,e}webMOpusUnsupported(e){const t=new Error("当前浏览器不支持 audio/webm;codecs=opus");return t.name="NotSupportedError",t.agentCode="AUDIO_WEBM_OPUS_UNSUPPORTED",t.retryable=!1,t.cause=e,t}webCodecsOpusUnsupported(e){const t=new Error("当前浏览器不支持 WebCodecs AudioEncoder Opus");return t.name="NotSupportedError",t.agentCode="AUDIO_WEBCODECS_OPUS_UNSUPPORTED",t.retryable=!1,t.cause=e,t}async startPcmWebMRecorder(e){var t=this;const n=globalThis,i=globalThis.AudioContext||n.webkitAudioContext;if(!i||void 0===globalThis.AudioWorkletNode)throw this.webCodecsOpusUnsupported(new Error("AudioWorklet 不可用"));const s=new w;await s.init();const r=new i({sampleRate:b});if(r.sampleRate!==b)throw await s.destroy(),await r.close().catch(()=>{}),this.webCodecsOpusUnsupported(new Error("浏览器无法创建 16000 Hz AudioContext"));this.pcmAudioContext=r,this.pcmEncoder=s;const a=URL.createObjectURL(new Blob([k],{type:"application/javascript"}));try{await r.audioWorklet.addModule(a)}finally{URL.revokeObjectURL(a)}if("suspended"===r.state&&await r.resume(),"running"!==r.state)throw this.audioContextSuspended();const o=r.createMediaStreamSource(e),l=new AudioWorkletNode(r,T,{numberOfInputs:1,numberOfOutputs:1,outputChannelCount:[1]}),h=++this.pcmGeneration;this.pcmSource=o,this.pcmWorklet=l,this.pcmEncodeQueue=Promise.resolve(),this.pcmStartedAt=this.monotonicEpochNow(),l.port.onmessage=e=>{var n,i,r;if("flushed"===(null==(n=e.data)?void 0:n.type))return null==(r=this.resolvePcmFlush)||r.call(this),void(this.resolvePcmFlush=null);if("frame"!==(null==(i=e.data)?void 0:i.type)||!(e.data.pcm instanceof ArrayBuffer))return;const a=this.pcmStartedAt+100*Number(e.data.sequence||0),o=new Int16Array(e.data.pcm);this.pcmEncodeQueue=this.pcmEncodeQueue.then(async function(){const n=await s.encode(o);if(h!==t.pcmGeneration||0===n.byteLength)return;const i=new Blob([n],{type:_});t.options.debugAudioChunks&&d("[Agent][Microphone] nearend PCM/WebCodecs chunk",i,{chunkMs:100,sampleRate:b,channels:1,sequence:e.data.sequence}),t.options.onFrame(i,a)}).catch(e=>{var t,n;const i=e instanceof Error?e:new Error("PCM/Opus 麦克风编码失败");i.name="AudioEncoderError",null==(t=(n=this.options).onError)||t.call(n,i)})},o.connect(l),l.connect(r.destination)}async stopPcmWebMRecorder(){const e=this.pcmWorklet,t=this.pcmSource,n=this.pcmAudioContext,i=this.pcmEncoder;if(!e&&!n&&!i)return;null==t||t.disconnect();let s=!0;e&&(s=await Promise.race([new Promise(t=>{this.resolvePcmFlush=()=>t(!0),e.port.postMessage({type:"flush"})}),new Promise(e=>globalThis.setTimeout(()=>e(!1),100))]),s||(this.resolvePcmFlush=null,e.port.onmessage=null)),await this.pcmEncodeQueue,this.pcmGeneration+=1,this.resolvePcmFlush=null,e&&(e.port.onmessage=null,e.disconnect(),e.port.close()),n&&"closed"!==n.state&&await n.close().catch(()=>{}),await(null==i?void 0:i.destroy()),this.pcmWorklet=null,this.pcmSource=null,this.pcmAudioContext=null,this.pcmEncoder=null,this.pcmEncodeQueue=Promise.resolve()}monotonicEpochNow(){const e=globalThis.performance;return e&&Number.isFinite(e.timeOrigin)?Math.round(e.timeOrigin+e.now()):Date.now()}audioContextSuspended(){const e=new Error("AudioContext 未运行,请在用户操作后重试");return e.name="NotAllowedError",e.agentCode="AUDIO_CONTEXT_SUSPENDED",e.retryable=!0,e}async stopMediaRecorder(){const e=this.mediaRecorder;if(this.mediaRecorder=null,!e)return;const t="inactive"!==e.state;await new Promise(n=>{let i=!1;const s=()=>{i||(i=!0,globalThis.clearTimeout(a),n())},r=e.onerror,a=globalThis.setTimeout(s,1e3);if(e.onstop=s,e.onerror=t=>{null==r||r.call(e,t),s()},t)try{e.stop()}catch(e){s()}}),e.ondataavailable=null,e.onerror=null,e.onstop=null}}const O=new Uint8Array([26,69,223,163]);function D(e,t){const n=globalThis.avatarSDKLogger;null==n||null==n.log||n.log("[Agent][AudioUplink]",e,t)}function I(e,t,n){if(1!==e&&2!==e)throw new Error(`AU stream_id 无效: ${e}`);if(!Number.isFinite(t)||t<0||t>4294967295)throw new Error(`AU ts_ms 无效: ${t}`);const i=n instanceof ArrayBuffer?new Uint8Array(n):new Uint8Array(n.buffer,n.byteOffset,n.byteLength),s=new ArrayBuffer(8+i.byteLength),r=new DataView(s);return r.setUint8(0,65),r.setUint8(1,85),r.setUint8(2,1),r.setUint8(3,e),r.setUint32(4,Math.floor(t),!1),new Uint8Array(s,8).set(i),s}async function M(e){if(Array.isArray(e)){const t=await Promise.all(e.map(M)),n=new Uint8Array(t.reduce((e,t)=>e+t.byteLength,0));let i=0;for(const e of t)n.set(e,i),i+=e.byteLength;return n}return e instanceof Blob?new Uint8Array(await e.arrayBuffer()):e instanceof ArrayBuffer?new Uint8Array(e):new Uint8Array(e.buffer,e.byteOffset,e.byteLength)}class P{constructor(e){this.connectionStartedAt=null,this.connectionGeneration=0,this.sendChain=Promise.resolve(),this.failed=!1,this.pendingFarend=new Map,this.pendingNearend=new Map,this.closedNearendBuckets=new Set,this.lastTimestampByStream=new Map,this.lastSentTimestampByStream=new Map,this.pendingWebmPrefixByStream=new Map,this.farendStreamState="unknown",this.options=e}reset(e=null){this.connectionGeneration+=1,this.connectionStartedAt=e,this.pendingFarend.clear(),this.pendingNearend.clear(),this.closedNearendBuckets.clear(),this.lastTimestampByStream.clear(),this.lastSentTimestampByStream.clear(),this.pendingWebmPrefixByStream.clear(),this.farendStreamState="unknown",this.failed=!1,this.sendChain=Promise.resolve()}startInput(){this.failed=!1,this.pendingFarend.clear(),this.pendingNearend.clear(),this.closedNearendBuckets.clear(),this.pendingWebmPrefixByStream.clear(),this.farendStreamState="unknown"}clearFarend(){this.pendingFarend.clear(),this.pendingWebmPrefixByStream.delete(2),this.farendStreamState="unknown"}offerFarend(e){var t;if(!1===e.isFirstChunk&&"ready"!==this.farendStreamState)return void D("farend.drop",{reason:"awaiting_webm_header",timestamp:e.timestamp,isFirstChunk:!1});const n=this.normalizeBlock(2,e);if(!n)return void(e.isFirstChunk&&this.abandonFarendStream());e.isFirstChunk&&(this.pendingFarend.clear(),this.pendingWebmPrefixByStream.delete(2),this.farendStreamState="ready");const i=null!=(t=this.pendingFarend.get(n.frameKey))?t:[];i.push(n),this.pendingFarend.set(n.frameKey,i),D("farend.offer",{tsMs:n.tsMs,frameKey:n.frameKey,isFirstChunk:Boolean(n.isFirstChunk),pendingNearend:[...this.pendingNearend.keys()]}),this.flushOlderNearend(n.frameKey),this.prune(n.frameKey)}offerNearend(e){var t;const n=this.normalizeBlock(1,e);if(!n||this.closedNearendBuckets.has(n.frameKey))return;const i=null!=(t=this.pendingNearend.get(n.frameKey))?t:[];i.push(n),this.pendingNearend.set(n.frameKey,i),this.flushExpiredNearend(n.frameKey),this.prune(n.frameKey)}drain(){for(const e of[...this.pendingNearend.keys()].sort((e,t)=>e-t))this.flushBucket(e,!0);return this.sendChain}normalizeBlock(e,n){const i=this.connectionStartedAt;if(null===i||!Number.isFinite(n.timestamp))return null;const s=Math.max(0,Math.floor(n.timestamp-i)),r=Math.floor(s/100),a=this.lastTimestampByStream.get(e);return void 0!==a&&s<=a?null:(this.lastTimestampByStream.set(e,s),t({},n,{streamId:e,tsMs:s,frameKey:r}))}enqueue(e){var t=this;if(this.failed)return;const n=this.connectionGeneration;this.sendChain=this.sendChain.then(async function(){if(!t.failed)for(const s of e){var i;const e=await M(null!=(i=s.dataParts)?i:s.data);if(t.failed||n!==t.connectionGeneration||null===t.connectionStartedAt||0===e.byteLength)continue;2===s.streamId&&s.isFirstChunk&&t.pendingWebmPrefixByStream.delete(2);const r=t.coalesceSplitWebmMagic(s.streamId,e);if(!r)continue;const a=t.lastSentTimestampByStream.get(s.streamId);void 0!==a&&s.tsMs<=a||(t.options.sendBinary(I(s.streamId,s.tsMs,r)),t.lastSentTimestampByStream.set(s.streamId,s.tsMs),D("frame.send",{streamId:s.streamId,tsMs:s.tsMs,bytes:r.byteLength}))}}).catch(e=>{var t,i;if(n!==this.connectionGeneration)return;this.failed=!0,this.pendingFarend.clear(),this.pendingNearend.clear();const s=e instanceof Error?e:new Error("AU 音频上行失败");null==(t=(i=this.options).onError)||t.call(i,s)})}coalesceSplitWebmMagic(e,t){const n=this.pendingWebmPrefixByStream.get(e);if(n){const i=new Uint8Array(n.byteLength+t.byteLength);i.set(n),i.set(t,n.byteLength),this.pendingWebmPrefixByStream.delete(e),t=i}return t.byteLength<O.byteLength&&t.every((e,t)=>e===O[t])?(this.pendingWebmPrefixByStream.set(e,t.slice()),null):t}flushOlderNearend(e){for(const t of[...this.pendingNearend.keys()].sort((e,t)=>e-t)){if(t>=e)break;this.flushBucket(t,!0)}}flushExpiredNearend(e){this.flushOlderNearend(e-1)}flushBucket(e,t=!1){const n=this.takeNearend(e);if(!n)return;const i=e-1,s=this.pendingFarend.has(e)||t&&this.pendingFarend.has(i)?this.takeFarendThrough(e):null;this.closedNearendBuckets.add(e),D("bucket.flush",{nearendFrameKey:e,pairedFarend:Boolean(s),pendingFarend:[...this.pendingFarend.keys()]}),this.enqueue(s?[s,n]:[n])}takeNearend(e){const n=this.pendingNearend.get(e);return this.pendingNearend.delete(e),null!=n&&n.length?t({},n[0],{dataParts:n.flatMap(e=>{var t;return null!=(t=e.dataParts)?t:[e.data]})}):null}takeFarendThrough(e){const n=[];for(const t of[...this.pendingFarend.keys()].sort((e,t)=>e-t)){if(t>e)break;n.push(...this.pendingFarend.get(t)),this.pendingFarend.delete(t)}return 0===n.length?null:t({},n[n.length-1],{dataParts:n.map(e=>e.data),isFirstChunk:n.some(e=>e.isFirstChunk)})}abandonFarendStream(){D("farend.drop",{reason:"stale_bucket_abandons_webm_stream",pendingFarend:[...this.pendingFarend.keys()]}),this.pendingFarend.clear(),this.pendingWebmPrefixByStream.delete(2),this.farendStreamState="awaiting-header"}prune(e){const t=e-3;[...this.pendingFarend.keys()].some(e=>e<t)&&this.abandonFarendStream();for(const e of this.pendingNearend.keys())e<t&&this.flushBucket(e,!0);for(const e of this.closedNearendBuckets)e<t&&this.closedNearendBuckets.delete(e)}}const N=["e2eServer","authToken","audio","reconnect","agentCallbacks","webSocketCtor","asr_id","asr_config","tts_config","features","extras","llm_id","brain_config","session_speak_req_id"],F=2147483647;function U(e){if(void 0===e)return!1;const t=JSON.stringify(e);return void 0!==t&&"{}"!==t}const B={1006:"E2E WebSocket 异常断开:未收到 Close frame,请检查 E2EMPServer 日志或中间网络",4e3:"E2E WebSocket 路径未知",4001:"E2E Token 无效",4002:"E2E Session 不存在",4009:"E2E 配额超限",4010:"E2E 服务端主动断开"},x=new Set([1001,1006,1011,1012,1013,1014]);function W(e,t,n){if(void 0!==n&&(!n||Array.isArray(n)||"object"!=typeof n))throw new Error(`Agent ${e} JSON Config 必须是对象`);if(void 0!==t&&U(n))throw new Error(`Agent ${e} 配置不能同时提供 ID 和 JSON Config`)}function L(e,t){if(void 0!==t&&(!t||Array.isArray(t)||"object"!=typeof t))throw new Error(`Agent ${e} 必须是对象`)}class G extends e{constructor(e){var n,i,s;W("ASR",e.asr_id,e.asr_config),W("TTS",void 0,e.tts_config),W("Brain",e.llm_id,e.brain_config),L("features",e.features),L("extras",e.extras);const{e2eServer:r,authToken:a,audio:o,reconnect:l,agentCallbacks:h,webSocketCtor:c,asr_id:u,asr_config:d,tts_config:p,features:m,extras:g,llm_id:E,brain_config:f,session_speak_req_id:S}=e,R=function(e,t){if(null==e)return{};var n={};for(var i in e)if({}.hasOwnProperty.call(e,i)){if(-1!==t.indexOf(i))continue;n[i]=e[i]}return n}(e,N),A=R.onMessage,y=R.onRenderChange,v=R.onNetworkInfo,w=R.onSpeakStateChange,_=R.onVoiceStateChange,b=R.onAudioPlaybackData,T=e=>{var t;null==(t=O)||t.forwardFarendAudio(e),null==b||b(e)},k=null!=S?S:1,C=function(e){if(!e||!U(e))return;const n=e.extra_body;if(void 0!==n&&(!n||Array.isArray(n)||"object"!=typeof n))throw new Error("Agent Brain extra_body 必须是 JSON 对象");return t({},e)}(f);let O;super(t({},R,{session_speak_req_id:k,sessionRequestData:t({},void 0!==u?{asr_id:u}:{},U(d)?{asr_config:d}:{},U(p)?{tts_config:p}:{},U(m)?{features:m}:{},U(g)?{extras:g}:{},void 0!==E?{llm_id:E}:{},void 0!==C?{brain_config:C}:{},{session_speak_req_id:k}),enableClientInterrupt:null==(n=R.enableClientInterrupt)||n,onMessage:e=>{var t,n;A(e),null!=(t=O)&&t.reportingInitializationFailure||null==(n=O)||n.emitError("sdk",String(e.code||"AVATAR_ERROR"),e.message||"Avatar SDK 错误",!0,e)},onRenderChange:e=>{var t;null==y||y(e),null==(t=O)||t.safeEmit(()=>null==h||null==h.onRenderChange?void 0:h.onRenderChange(e))},onNetworkInfo:e=>{var t;null==v||v(e),null==(t=O)||t.safeEmit(()=>null==h||null==h.onNetworkInfo?void 0:h.onNetworkInfo(e))},onSpeakStateChange:(e,t)=>{var n,i,s;null==w||w(e,t),null==(n=O)||n.safeEmit(()=>null==h||null==h.onSpeakStateChange?void 0:h.onSpeakStateChange({state:e,clientSpeakId:t})),["speak_start","start","started"].includes(e)&&(O&&(O.interactionPending=!0,O.voicePlaying=!0),null==(i=O)||i.emitConversation({state:"speaking"})),["speak_end","end","ended","completed","finish","finished"].includes(e)&&(null==(s=O)||s.emitConversation({state:"completed"}))},onVoiceStateChange:(e,t,n)=>{var i;O&&"start"===e&&(O.voicePlaying=!0),"end"===e&&(O&&(O.voicePlaying=!1),null==(i=O)||i.forwardVoiceEnd()),null==_||_(e,t,n)}},b?{onAudioPlaybackData:T}:{})),this.e2eClient=null,this.microphone=null,this.stopASRPromise=null,this.initializationPromise=null,this.rejectInitialization=null,this.initialSessionAttempt=null,this.initialSessionGeneration=0,this.initialTtsaReadySessionIds=new Set,this.initialTtsaReadyBeforeSession=!1,this.asrStartGeneration=0,this.agentState="idle",this.asrState="idle",this.agentDestroyed=!1,this.agentDestroying=!1,this.destroyPromise=null,this.suppressNextVoiceEnd=!1,this.replaySending=!1,this.farendAudioEnabled=!1,this.reconnectTimer=null,this.reconnectAttempt=0,this.reconnectInFlight=!1,this.sessionReloadFallback=!1,this.waitingForTtsaReload=!1,this.stateBeforeReconnect="ready",this.e2eSessionId="",this.currentE2EUrl="",this.serviceReconnect=null,this.serviceMigration=null,this.interactionPending=!1,this.llmPending=!1,this.llmProducedText=!1,this.directSpeakOpen=!1,this.pendingASR=!1,this.autoSendASRToLLM=!0,this.voicePlaying=!1,this.reportingInitializationFailure=!1,this.handleOnlineForReconnect=()=>{this.serviceMigration||"reconnecting"!==this.agentState||this.reconnectInFlight||this.sessionReloadFallback||(this.clearReconnectTimer(),this.scheduleE2EReconnect(!0))},O=this,this.agentOptions={e2eServer:r,authToken:a,audio:o,requestedEnableAec:"boolean"==typeof(null==m||null==(i=m.speech_frontend)?void 0:i.enable_aec)?m.speech_frontend.enable_aec:void 0,callbacks:h,webSocketCtor:c,debugAudioChunks:Boolean(R.enableDebugger)},this.audioInputEnabled=null!=(s=null==o?void 0:o.inputEnabled)&&s,this.autoSendASRToLLM=!1!==(null==m?void 0:m.auto_send_asr_to_llm),this.originalOnAudioPlaybackData=b,this.playbackDataHandler=T,this.reconnectOptions=this.normalizeReconnectOptions(l),this.audioUplink=new P({sendBinary:e=>{var t;return null==(t=this.e2eClient)?void 0:t.sendBinary(e)},onError:e=>this.handleAudioUplinkError(e)})}getAgentState(){return this.agentState}getState(){return this.getAgentState()}getASRState(){return this.asrState}init(e={}){if(this.initializationPromise)return this.initializationPromise;const t=this.initialize(e).finally(()=>{this.initializationPromise===t&&(this.initializationPromise=null)});return this.initializationPromise=t,t}async initialize(e){this.assertNotDestroyed(),this.setAgentState("initializing");const n=new Promise((e,t)=>{this.rejectInitialization=t});n.catch(()=>{});try{const i=t({},e,{onDownloadProgress:e.onDownloadProgress||(()=>{})}),s=await super.initializeSession(i);if(this.assertNotDestroyed(),!s)throw new Error("XmovAvatar 初始化未返回 sessionInfo");for(this.applySessionFeatures(s),this.assertNotDestroyed(),this.beginInitialSession(s);;){const e=this.initialSessionAttempt;if(!e)throw new Error("Agent 初始化缺少会话连接任务");if(await Promise.race([Promise.all([e.e2ePromise,e.ttsaReadyPromise]),n]),e===this.initialSessionAttempt)break}for(this.assertNotDestroyed();this.serviceMigration;)await this.serviceMigration.completion;this.assertE2EOpen(),this.setAgentState("ready"),this.start()}catch(e){if(this.serviceMigration&&!this.agentDestroying&&!this.agentDestroyed)throw this.failServiceMigration("E2E_MIGRATION_FAILED","E2E 更新迁移失败"),e;if(!this.agentDestroyed&&!this.agentDestroying){var i;null==(i=this.e2eClient)||i.disconnect(1e3,"agent_init_failed"),this.e2eClient=null,await this.cleanupBeforeInitComplete("agent_init_failed");try{this.setAgentState("failed")}catch(e){}this.reportingInitializationFailure=!0;try{this.reportInitializationFailure("Agent 初始化失败",e)}catch(e){}finally{this.reportingInitializationFailure=!1}try{this.emitError("sdk","AGENT_INIT_FAILED","Agent 初始化失败",!0,e)}catch(e){}this.agentDestroyed=!0}throw e}finally{var s;this.rejectInitialization=null,this.initialSessionGeneration+=1,null==(s=this.initialSessionAttempt)||s.resolveTtsaReady(),this.initialSessionAttempt=null,this.initialTtsaReadySessionIds.clear(),this.initialTtsaReadyBeforeSession=!1}}beginInitialSession(e){this.e2eSessionId=e.session_id;const t=this.initialSessionAttempt,n=++this.initialSessionGeneration;null==t||t.resolveTtsaReady();let i=()=>{};const s=new Promise(e=>{i=e}),r=this.replaceE2EConnection(e.e2e_resp).catch(e=>{if(n===this.initialSessionGeneration)throw e}),a={sessionInfo:e,e2ePromise:r,ttsaReadyPromise:s,resolveTtsaReady:i};return this.initialSessionAttempt=a,this.initialTtsaReadySessionIds.delete(e.session_id)?a.resolveTtsaReady():!t&&this.initialTtsaReadyBeforeSession&&(this.initialTtsaReadyBeforeSession=!1,a.resolveTtsaReady()),r.catch(()=>{}),r}start(){var e;"initializing"!==this.agentState&&"running"!==this.agentState&&("reconnecting"!==this.agentState?(this.assertE2EOpen(),super.start(),this.setAgentState("running")):this.waitingForTtsaReload&&null!=(e=this.e2eClient)&&e.isOpen&&("running"===this.stateBeforeReconnect&&super.start(),this.finishReconnect()))}onTtsaReady(e){if(this.rejectInitialization){const t=this.initialSessionAttempt,n=null==e?void 0:e.session_id;!t||n&&t.sessionInfo.session_id!==n?n?this.initialTtsaReadySessionIds.add(n):this.initialTtsaReadyBeforeSession=!0:t.resolveTtsaReady()}else"reconnecting"!==this.agentState?"running"===this.agentState&&super.start():this.start()}abortInitialization(e){var t;null==(t=this.rejectInitialization)||t.call(this,e)}async stop(){if(this.agentDestroyed)return;const e="reconnecting"===this.agentState;e&&(this.stateBeforeReconnect="stopped",this.serviceMigration&&(this.serviceMigration.previousState="stopped")),await this.stopASR(),await super.stop(),e||this.setAgentState("stopped")}async switchInvisibleMode(){const e=this.getStatus(),t=e===i.online||e===i.visible;super.switchInvisibleMode(),t&&await this.stopASR()}destroy(e="user"){return this.destroyPromise?this.destroyPromise:this.agentDestroyed?("destroyed"!==this.agentState&&this.setAgentState("destroyed"),Promise.resolve()):(this.agentDestroying=!0,this.destroyPromise=Promise.resolve().then(()=>this.finishDestroy(e)),this.destroyPromise)}async finishDestroy(e){this.abortInitialization(new Error("Agent 初始化已取消")),this.cancelServiceMigration(),this.serviceReconnect=null,this.cancelReconnect(),this.cancelSessionRestart();try{this.stopASR().catch(e=>{this.emitError("sdk","AGENT_ASR_STOP_FAILED","Agent 销毁时停止 ASR 失败",!1,e)})}catch(e){this.emitError("sdk","AGENT_ASR_STOP_FAILED","Agent 销毁时停止 ASR 失败",!1,e)}const t=this.e2eClient;try{await(null==t?void 0:t.disconnectAndWait(1e3,e))}finally{this.e2eClient===t&&(this.e2eClient=null),this.audioUplink.reset()}try{await this.disconnectClientForDestroy(e)}finally{this.agentDestroyed=!0,this.agentDestroying=!1,this.setAgentState("destroyed")}}async ask(e){this.assertRunning(),this.interactionPending=!0,this.llmPending=!0,this.llmProducedText=!1,this.suppressNextVoiceEnd=!1,this.emitConversation({state:"asking",text:e}),this.sendControl({type:"ask",message:{text:e}})}speak(e,t=!0,n=!0){var i;if(this.replaySending)return n&&(this.suppressNextVoiceEnd=!0),super.speak(e,t,n,arguments[3]);"draining"===(null==(i=this.serviceMigration)?void 0:i.phase)&&this.directSpeakOpen&&!t?this.assertE2EOpen():this.assertRunning(),this.interactionPending=!0,this.directSpeakOpen=!n,this.suppressNextVoiceEnd=!1,t&&this.emitConversation({state:"speaking-directly",text:e}),this.sendControl({type:"speak",message:{text:e,is_start:t,is_end:n}})}handleReplaySend(e){if(this.serviceMigration)return;const t=this.replaySending;this.replaySending=!0;try{super.handleReplaySend(e)}finally{this.replaySending=t}}async speakByE2E(e){this.speak(e)}async startASR(e={}){var t;if(this.assertASRStartAllowed(),this.suppressNextVoiceEnd=!1,this.stopASRPromise&&(await this.stopASRPromise,this.assertASRStartAllowed()),null!=(t=this.microphone)&&t.isRecording)return;this.assertAudioChunkDuration();const n=e.inputStream?this.cloneASRInputStream(e.inputStream):void 0,i=++this.asrStartGeneration,s=!this.audioInputEnabled,r=new C({audio:this.agentOptions.audio,debugAudioChunks:this.agentOptions.debugAudioChunks,inputStream:n,usePcmWebCodecs:this.farendAudioEnabled,onFrame:(e,t)=>{var n,i;if(("running"===this.agentState||"draining"===(null==(n=this.serviceMigration)?void 0:n.phase))&&null!=(i=this.e2eClient)&&i.isOpen)try{var s;this.sendAudioFrame({data:e,timestamp:null!=t?t:Date.now()-((null==(s=this.agentOptions.audio)?void 0:s.chunkMs)||l.chunkMs)})}catch(e){this.stopASRAfterSocketClose()}},onError:e=>this.handleMicrophoneError(r,e)});this.microphone=r;try{this.audioUplink.startInput(),s&&this.enableAudioInput(),this.emitConversation({state:"asking"}),this.assertASRStartActive(i),this.setASRState("requesting-permission"),this.assertASRStartActive(i),this.setASRState("starting"),this.assertASRStartActive(i),await r.start(),this.assertRunning(),this.farendAudioEnabled&&this.restartAudioPlaybackCapture(),this.setASRState("listening")}catch(e){var a,o;await r.stop();const t="AUDIO_FIXED_TRACK_CANCELLED"===(null==e?void 0:e.agentCode),n=i===this.asrStartGeneration,l=!this.agentDestroyed&&!this.agentDestroying&&"running"===this.agentState&&Boolean(null==(a=this.e2eClient)?void 0:a.isOpen);if(this.microphone===r&&(this.microphone=null),n&&!t&&s&&null!=(o=this.e2eClient)&&o.isOpen)try{this.disableAudioInput()}catch(e){}throw!n||this.agentDestroyed||this.agentDestroying||(t&&l?this.setASRState("idle"):t||this.setASRState("failed")),t&&!l&&this.assertRunning(),e}}cloneASRInputStream(e){const t=e.getAudioTracks().find(e=>"ended"!==e.readyState);if(!t)throw new Error("ASR 输入流不包含有效音频轨道");return new MediaStream([t.clone()])}assertASRStartActive(e){if(e===this.asrStartGeneration)return;const t=new Error("麦克风启动已取消");throw t.name="AbortError",t.agentCode="AUDIO_FIXED_TRACK_CANCELLED",t.retryable=!0,t}stopASR(){if(this.asrStartGeneration+=1,this.stopASRPromise)return this.stopASRPromise;const e=this.microphone;var t;return null!=e&&e.isRecording?(this.microphone=null,this.stopASRPromise=this.finishStopASR(e).finally(()=>{this.stopASRPromise=null}),this.stopASRPromise):(null!=(t=this.e2eClient)&&t.isOpen&&this.disableAudioInput(),Promise.resolve())}async finishStopASR(e,t=!0){this.setASRState("stopping"),await e.stop(),await this.audioUplink.drain(),t?(this.disableAudioInput(),this.setASRState("idle")):this.setASRState("failed")}interrupt(){if(this.agentDestroyed||this.agentDestroying||this.serviceMigration&&"draining"!==this.serviceMigration.phase)return 0;this.sendControl({type:"interrupt"});const e=super.interrupt("speak");return this.interactionPending=!1,this.llmPending=!1,this.llmProducedText=!1,this.directSpeakOpen=!1,this.pendingASR=!1,this.voicePlaying=!1,this.emitConversation({state:"interrupted"}),this.checkServiceMigration(),e}async interruptConversation(e="user"){this.interrupt()}async onSessionReloaded(e){if(!this.agentDestroyed&&!this.agentDestroying){if(this.cancelServiceMigration(),this.serviceReconnect=null,this.e2eSessionId=e.session_id,this.rejectInitialization)return this.applySessionFeatures(e),void await this.beginInitialSession(e);"reconnecting"!==this.agentState&&(this.stateBeforeReconnect=this.readRestorableState(),this.setAgentState("reconnecting")),this.clearReconnectTimer(),this.sessionReloadFallback=!0,this.applySessionFeatures(e),await this.replaceE2EConnection(e.e2e_resp),this.waitingForTtsaReload=!0,this.sessionReloadFallback=!1}}onSessionReloadExhausted(e){this.rejectInitialization?this.abortInitialization(e instanceof Error?e:new Error("Agent 初始化期间 TTSA Session 恢复失败")):"reconnecting"===this.agentState&&this.failReconnect(e)}async replaceE2EConnection(e,t=!0){const{wsUrl:n,token:i}=this.resolveE2EConnection(e);this.currentE2EUrl=n;const s=this.e2eClient;let r;this.audioUplink.reset(),r=new o({wsUrl:n,token:i,audioInputEnabled:this.audioInputEnabled,WebSocketCtor:this.agentOptions.webSocketCtor,onStateChange:e=>{this.e2eClient===r&&("open"===e&&this.audioUplink.reset(Date.now()),this.safeEmit(()=>{var t;return null==(t=this.agentOptions.callbacks)||null==t.onSocketStateChange?void 0:t.onSocketStateChange(e)}))},onEvent:e=>{var t,n;this.e2eClient===r&&"closing"!==(null==(t=this.serviceMigration)?void 0:t.phase)&&"failed"!==(null==(n=this.serviceMigration)?void 0:n.phase)&&this.handleServerEvent(e)},onError:e=>{this.e2eClient===r&&"initializing"!==this.agentState&&"reconnecting"!==this.agentState&&this.emitError("network","E2E_SOCKET_ERROR",e.message,!0,e)},onClose:e=>{this.e2eClient===r&&this.handleSocketClose(e)}}),this.e2eClient=r,null==s||s.disconnect(1e3,"e2e_connection_replaced"),t&&await r.connect()}resolveE2EConnection(e){const t=Boolean(null==e?void 0:e.ws_url),n=Boolean(null==e?void 0:e.e2e_token);if(t!==n)throw new Error("统一 session 响应的 e2e_resp 必须同时返回 ws_url 和 e2e_token");if(t&&n)return{wsUrl:null==e?void 0:e.ws_url,token:null==e?void 0:e.e2e_token};const i=Boolean(this.agentOptions.e2eServer);if(i!==Boolean(this.agentOptions.authToken))throw new Error("e2eServer 和 authToken 必须同时配置");if(!i)throw new Error("统一 session 响应未返回 e2e_resp.ws_url,且未配置 e2eServer");return{wsUrl:this.agentOptions.e2eServer,token:this.agentOptions.authToken}}sendControl(e){try{var t;null==(t=this.e2eClient)||t.send(e)}catch(e){throw this.emitError("network","E2E_SEND_FAILED","E2E WebSocket 发送失败",!0,e),e}}assertAudioChunkDuration(){var e;const t=(null==(e=this.agentOptions.audio)?void 0:e.chunkMs)||l.chunkMs;if(!this.farendAudioEnabled||t===l.chunkMs)return;const n=new Error("AU v1 音频分片固定为 100ms");throw n.name="NotSupportedError",n.agentCode="AUDIO_CHUNK_DURATION_UNSUPPORTED",n.retryable=!1,this.emitError("sdk",n.agentCode,n.message,n.retryable,n),n}sendAudioFrame(e){if(this.farendAudioEnabled)this.agentOptions.debugAudioChunks&&d("[Agent][E2E] nearend AU chunk",e.data,{timestamp:e.timestamp,stream_id:1}),this.audioUplink.offerNearend(e);else{this.agentOptions.debugAudioChunks&&d("[Agent][E2E] nearend raw WebM/Opus chunk",e.data,{timestamp:e.timestamp});try{var t;null==(t=this.e2eClient)||t.sendBinary(e.data)}catch(e){this.handleAudioUplinkError(e instanceof Error?e:new Error("E2E nearend 音频帧发送失败"))}}}handleAudioUplinkError(e){this.emitError("network","E2E_AUDIO_SEND_FAILED",this.farendAudioEnabled?"E2E AU 音频帧发送失败":"E2E 音频帧发送失败",!0,e);const t=this.microphone;if(null==t||!t.isRecording||this.stopASRPromise)return;this.setASRState("failed"),this.microphone=null;const n=this.finishFailedASR(t).finally(()=>{this.stopASRPromise===n&&(this.stopASRPromise=null)});this.stopASRPromise=n}applySessionFeatures(e){var t,n,i,s,r,a,o,l,h;const c=null!=(t=null==(n=e.e2e_resp)||null==(n=n.features)?void 0:n.auto_send_asr_to_llm)?t:null==(i=e.features)?void 0:i.auto_send_asr_to_llm;"boolean"==typeof c&&(this.autoSendASRToLLM=c);const u=null!=(s=null==(r=e.e2e_resp)||null==(r=r.features)||null==(r=r.speech_frontend)?void 0:r.enable_aec)?s:null==(a=e.features)||null==(a=a.speech_frontend)?void 0:a.enable_aec;if(this.farendAudioEnabled="boolean"==typeof u?u:null!=(o=null!=(l=this.agentOptions.requestedEnableAec)?l:null==(h=this.agentOptions.audio)?void 0:h.echoCancellationEnabled)&&o,this.farendAudioEnabled||this.audioUplink.clearFarend(),this.farendAudioEnabled||this.originalOnAudioPlaybackData)return this.setAudioPlaybackDataHandler(this.playbackDataHandler),void(this.farendAudioEnabled&&this.enableAudioPlaybackCapture());this.setAudioPlaybackDataHandler(void 0)}forwardFarendAudio(e){var t,n;const i=Boolean(null==(t=this.microphone)?void 0:t.isRecording),s=Boolean(null==(n=this.e2eClient)?void 0:n.isOpen);var r;this.farendAudioEnabled&&i&&s?(this.agentOptions.debugAudioChunks&&d("[Agent][E2E] farend AU chunk",e.data,{timestamp:e.timestamp,stream_id:2,codec:e.codec,sampleRate:e.sampleRate,channels:1,samples:e.samples,speech_id:e.speech_id,isFirstChunk:e.isFirstChunk}),this.audioUplink.offerFarend({data:e.data,timestamp:e.timestamp,isFirstChunk:e.isFirstChunk})):null==(r=globalThis.avatarSDKLogger)||null==r.log||r.log("[Agent][AudioUplink]","farend.drop",{reason:this.farendAudioEnabled?i?"e2e_not_open":"microphone_not_recording":"aec_disabled",timestamp:e.timestamp,isFirstChunk:e.isFirstChunk})}handleMicrophoneError(e,t){var n;const i=t,s="MediaRecorderError"===t.name,r="AudioEncoderError"===t.name,a=s||r,o="NotSupportedError"===t.name,l=Boolean(i.agentCode);if(a&&this.microphone===e&&"stopping"!==this.asrState){this.microphone=null;const t=this.finishFailedASR(e).finally(()=>{this.stopASRPromise===t&&(this.stopASRPromise=null)});this.stopASRPromise=t}this.emitError(a?"asr":l||o?"sdk":"permission",i.agentCode||(s?"MEDIA_RECORDER_ERROR":r?"AUDIO_ENCODER_ERROR":"MICROPHONE_ERROR"),t.message,null!=(n=i.retryable)?n:!o,t)}async finishFailedASR(e){try{await e.stop(),await this.audioUplink.drain()}catch(e){this.emitError("sdk","MICROPHONE_CLEANUP_FAILED","麦克风异常后的资源清理失败",!1,e)}try{this.disableAudioInput()}catch(e){}}enableAudioInput(){var e;this.audioInputEnabled||(this.sendControl({type:"event",message:"audio_input_on"}),this.audioInputEnabled=!0,null==(e=this.e2eClient)||e.setAudioInputEnabled(!0))}disableAudioInput(){var e;this.audioInputEnabled&&(this.sendControl({type:"event",message:"audio_input_off"}),this.audioInputEnabled=!1,null==(e=this.e2eClient)||e.setAudioInputEnabled(!1))}handleServerEvent(e){var t;if(!this.agentDestroyed&&!this.agentDestroying)switch(e.type){case"service_update":return void this.handleServiceUpdate(e);case"pong":default:return;case"asr_result":return void this.handleASRResult(e);case"llm_response":return void this.handleLLMResponse(e);case"semantic_judge_round_result":return void this.handleSemanticJudgeResult(e);case"volume_and_repetition_text_response":return void(!0===(null==(t=e.message)?void 0:t.skip)&&(this.pendingASR=!1,this.checkServiceMigration()));case"error":return void this.handleBackendError(e)}}handleASRResult(e){this.autoSendASRToLLM&&(this.pendingASR="string"==typeof e.text&&Boolean(e.text.trim()));const t={text:"string"==typeof e.text?e.text:"",isFinal:!0===e.is_final,raw:e};this.safeEmit(()=>{var e;return null==(e=this.agentOptions.callbacks)||null==e.onASRResult?void 0:e.onASRResult(t)}),this.checkServiceMigration()}handleLLMResponse(e){if("chunk"!==e.event&&"done"!==e.event)return;this.llmPending="done"!==e.event,this.pendingASR=!1,"chunk"===e.event&&e.text&&(this.interactionPending=!0,this.llmProducedText=!0),"done"===e.event&&(this.llmProducedText||(this.interactionPending=!1),this.llmProducedText=!1);const n=e.usage,i=t({event:e.event},"string"==typeof e.text?{text:e.text}:{},!0===e.is_first?{isFirst:!0}:{},n?{usage:{promptTokens:this.readFiniteNumber(n.prompt_tokens),completionTokens:this.readFiniteNumber(n.completion_tokens),totalTokens:this.readFiniteNumber(n.total_tokens),cachedTokens:this.readFiniteNumber(n.cached_tokens)}}:{},{raw:e});this.safeEmit(()=>{var e;return null==(e=this.agentOptions.callbacks)||null==e.onLLMResponse?void 0:e.onLLMResponse(i)}),this.checkServiceMigration()}handleSemanticJudgeResult(e){if(!e.message||"object"!=typeof e.message)return;const t=e.message,n={query:"string"==typeof t.query?t.query:"",meaningful:!0===t.meaningful,action:"string"==typeof t.action?t.action:"",raw:e};this.safeEmit(()=>{var e;return null==(e=this.agentOptions.callbacks)||null==e.onSemanticJudgeResult?void 0:e.onSemanticJudgeResult(n)}),!1===t.meaningful&&(this.pendingASR=!1,this.checkServiceMigration())}forwardVoiceEnd(){var e,t;if(this.suppressNextVoiceEnd)return this.suppressNextVoiceEnd=!1,this.interactionPending=!1,void this.checkServiceMigration();if(!(this.agentDestroyed||this.agentDestroying||"running"!==this.agentState&&"draining"!==(null==(e=this.serviceMigration)?void 0:e.phase)||null==(t=this.e2eClient))&&t.isOpen)try{this.sendControl({type:"event",message:"voice_end"}),this.interactionPending=!1,this.checkServiceMigration()}catch(e){}}readFiniteNumber(e){return"number"==typeof e&&Number.isFinite(e)?e:0}handleBackendError(e){var t;const n=["permission","network","asr","brain","ttsa","quota","sdk"].includes(e.domain)?e.domain:"sdk",i=String(null!=(t=e.code)?t:"BACKEND_ERROR"),s="string"==typeof e.message?e.message:"E2E 后端错误";"asr"===n&&(this.stopASRAfterSocketClose(),this.pendingASR=!1),"brain"===n&&(this.pendingASR=!1,this.llmPending=!1,this.llmProducedText||(this.interactionPending=!1)),this.emitConversation({state:"failed"}),this.emitError(n,i,s,"quota"!==n,e),this.checkServiceMigration()}handleServiceUpdate(e){var t;if(this.serviceMigration&&("connecting"!==this.serviceMigration.phase||null==(t=this.e2eClient)||!t.isOpen))return;let n=null;const i=e.grace_period_ms;try{const t=e.reconnect;if(!t||"string"!=typeof t.ws_url||"string"!=typeof t.e2e_token||!t.e2e_token||t.session_id!==this.e2eSessionId||"number"!=typeof i||!Number.isSafeInteger(i)||i<0||i>F||"number"!=typeof t.expires_in_ms||!Number.isSafeInteger(t.expires_in_ms)||t.expires_in_ms<=0||t.expires_in_ms>F)throw new Error("Invalid migration payload");const s=new URL(t.ws_url);if(!["ws:","wss:"].includes(s.protocol)||s.username||s.password||s.hash||this.currentE2EUrl.startsWith("wss:")&&"wss:"!==s.protocol||1!==s.searchParams.getAll("session_id").length||s.searchParams.get("session_id")!==this.e2eSessionId||1!==s.searchParams.getAll("token").length||s.searchParams.get("token")!==t.e2e_token)throw new Error("Invalid migration URL");n={session_id:this.e2eSessionId,ws_url:t.ws_url,e2e_token:t.e2e_token,expiresAt:Date.now()+t.expires_in_ms,attempts:0}}catch(e){return void(this.serviceMigration?this.failServiceMigration("E2E_SERVICE_UPDATE_INVALID","服务更新缺少有效的同房重连凭据"):this.startServiceMigration(null,0))}this.serviceMigration&&(this.stateBeforeReconnect=this.serviceMigration.previousState,this.cancelServiceMigration()),this.serviceReconnect=n,this.startServiceMigration(n,i)}startServiceMigration(e,t){var n;if(this.serviceMigration||this.agentDestroyed||this.agentDestroying)return;const i="reconnecting"===this.agentState?this.stateBeforeReconnect:this.agentState;this.cancelReconnect();let s=()=>{},r=e=>{};const a=new Promise((e,t)=>{s=e,r=t});a.catch(()=>{});const o={reconnect:e,previousState:i,phase:"draining",departAt:Date.now()+Math.max(0,t-3e3),quietUntil:0,audioStopped:!1,timer:null,expiryTimer:null,completion:a,resolve:s,reject:r};if(this.serviceMigration=o,this.stateBeforeReconnect=this.readRestorableState(),!e)return void this.failServiceMigration("E2E_SERVICE_UPDATE_INVALID","服务更新缺少有效的同房重连凭据");if(o.expiryTimer=setTimeout(()=>{this.failServiceMigration("E2E_MIGRATION_TOKEN_EXPIRED","E2E 迁移凭据已过期,请结束会话后重新初始化")},Math.max(0,e.expiresAt-Date.now())),this.setMigrationState("reconnecting"),this.serviceMigration!==o||this.agentDestroying)return;const l=Boolean((null==(n=this.microphone)?void 0:n.isRecording)||this.stopASRPromise||this.audioInputEnabled);o.timer=setTimeout(()=>{this.closeForServiceMigration(o)},Math.max(0,o.departAt-Date.now())),this.stopASR().then(()=>{this.serviceMigration===o&&"draining"===o.phase&&(o.audioStopped=!0,o.quietUntil=Date.now()+(l?2e3:0),this.checkServiceMigration())}).catch(()=>{this.serviceMigration===o&&this.failServiceMigration("E2E_MIGRATION_AUDIO_STOP_FAILED","E2E 迁移时停止音频失败")})}checkServiceMigration(){const e=this.serviceMigration;if(!e||"draining"!==e.phase||!e.audioStopped)return;const t=e.quietUntil-Date.now();Date.now()>=e.departAt||t<=0&&!this.interactionPending&&!this.llmPending&&!this.directSpeakOpen&&!this.pendingASR&&!this.voicePlaying?this.closeForServiceMigration(e):t>0?(null!==e.timer&&clearTimeout(e.timer),e.timer=setTimeout(()=>this.checkServiceMigration(),Math.max(0,Math.min(t,e.departAt-Date.now())))):(null!==e.timer&&clearTimeout(e.timer),e.timer=setTimeout(()=>{this.closeForServiceMigration(e)},Math.max(0,e.departAt-Date.now())))}async closeForServiceMigration(e){var t;if(this.serviceMigration!==e||"draining"!==e.phase||this.agentDestroying||this.agentDestroyed)return;e.phase="closing",null!==e.timer&&clearTimeout(e.timer),e.timer=null,this.audioUplink.reset(),this.audioInputEnabled=!1;const n=this.e2eClient,i=await(null==n?void 0:n.disconnectAndWait(1e3,"service_update",3e3));if(this.serviceMigration===e&&"closing"===e.phase&&!this.agentDestroying&&!this.agentDestroyed)if(!1!==i){this.e2eClient===n&&(this.e2eClient=null);try{await this.replaceE2EConnection(e.reconnect,!1)}catch(t){return void(this.serviceMigration===e&&this.failServiceMigration("E2E_MIGRATION_FAILED","创建迁移连接失败"))}this.serviceMigration!==e||this.agentDestroying||this.agentDestroyed||(e.phase="connecting",this.interactionPending=!1,this.llmPending=!1,this.llmProducedText=!1,this.directSpeakOpen=!1,this.pendingASR=!1,this.voicePlaying=!1,this.scheduleServiceReconnect(e,0===(null==(t=e.reconnect)?void 0:t.attempts)))}else this.failServiceMigration("E2E_MIGRATION_CLOSE_TIMEOUT","旧 E2E 未确认关闭,已停止迁移以避免双连接")}scheduleServiceReconnect(e,t=!1){var n,i;if(this.serviceMigration!==e||"connecting"!==e.phase||this.agentDestroying||this.agentDestroyed||this.reconnectInFlight)return;null!==e.timer&&clearTimeout(e.timer);const s=t?0:Math.max(100,this.getReconnectDelay((null!=(n=null==(i=e.reconnect)?void 0:i.attempts)?n:1)-1));e.timer=setTimeout(()=>{this.attemptServiceReconnect(e)},s)}async attemptServiceReconnect(e){if(this.serviceMigration!==e||"connecting"!==e.phase||this.agentDestroying||this.agentDestroyed)return;e.timer=null;const t=this.e2eClient;if(!t||!e.reconnect)return;if(Date.now()>=e.reconnect.expiresAt)return void this.failServiceMigration("E2E_MIGRATION_TOKEN_EXPIRED","E2E 迁移凭据已过期,请结束会话后重新初始化");this.reconnectInFlight=!0,e.reconnect.attempts+=1;const n=setTimeout(()=>t.disconnect(1e3,"migration_connect_timeout"),1e4);try{if(await t.connect(),clearTimeout(n),this.serviceMigration!==e||!t.isOpen||this.agentDestroying||this.agentDestroyed)return;if(await this.stopASRPromise,this.serviceMigration!==e||!t.isOpen||this.agentDestroying||this.agentDestroyed)return;try{this.setASRState("idle")}catch(e){}if(this.serviceMigration===e&&!this.agentDestroyed&&!this.agentDestroying){const t=e.previousState;this.cancelServiceMigration(),this.setMigrationState(t)}}catch(e){}finally{clearTimeout(n),this.serviceMigration===e&&(this.reconnectInFlight=!1,this.scheduleServiceReconnect(e))}}failServiceMigration(e,t){var n;const i=this.serviceMigration;i&&"failed"!==i.phase&&(i.phase="failed",null!==i.timer&&clearTimeout(i.timer),null!==i.expiryTimer&&clearTimeout(i.expiryTimer),i.timer=null,i.expiryTimer=null,this.cancelReconnect(),this.stopASRAfterSocketClose(),null==(n=this.e2eClient)||n.disconnect(1e3,"migration_failed"),i.reject(new Error(t)),this.abortInitialization(new Error(t)),this.setMigrationState("failed"),this.emitError("E2E_CLOSE_4009"===e?"quota":"network",e,t,!1))}setMigrationState(e){try{this.setAgentState(e)}catch(e){}}cancelServiceMigration(){const e=this.serviceMigration;e&&(this.serviceMigration=null,null!==e.timer&&clearTimeout(e.timer),null!==e.expiryTimer&&clearTimeout(e.expiryTimer),this.reconnectInFlight=!1,e.resolve())}handleSocketClose(e){if(this.agentDestroyed||this.agentDestroying)return;if(this.serviceMigration){const t=this.serviceMigration;return void("draining"===t.phase?4011===e.code||x.has(e.code)?this.closeForServiceMigration(t):this.failServiceMigration(`E2E_CLOSE_${e.code}`,"E2E 更新迁移被服务端终止"):"connecting"===t.phase&&(4011===e.code||x.has(e.code)?this.scheduleServiceReconnect(t):this.failServiceMigration(`E2E_CLOSE_${e.code}`,"E2E 更新迁移被服务端终止")))}if(4011===e.code)return void this.startServiceMigration(this.serviceReconnect,0);if(this.rejectInitialization)return void this.abortInitialization(new Error(`Agent 初始化期间 E2E WebSocket 已关闭: ${e.code} ${e.reason||""}`.trim()));if(this.audioUplink.reset(),this.stopASRAfterSocketClose(),this.shouldReconnect(e))return void this.beginReconnect(e);this.setAgentState("failed");const t=4009===e.code?"quota":"network";this.emitError(t,`E2E_CLOSE_${e.code}`,e.reason||B[e.code]||"E2E WebSocket 已关闭","quota"!==t,{code:e.code,reason:e.reason,wasClean:e.wasClean})}shouldReconnect(e){return this.reconnectOptions.enabled&&"idle"!==this.agentState&&"initializing"!==this.agentState&&"destroyed"!==this.agentState&&x.has(e.code)}beginReconnect(e){var t;"reconnecting"!==this.agentState&&(this.stateBeforeReconnect=this.readRestorableState(),this.reconnectAttempt=0,this.sessionReloadFallback=!1,this.setAgentState("reconnecting"),null==globalThis.addEventListener||globalThis.addEventListener("online",this.handleOnlineForReconnect),this.emitError("network","E2E_RECONNECTING","E2E WebSocket 已断开,正在重连",!0,{code:e.code,reason:e.reason,wasClean:e.wasClean})),this.audioInputEnabled=!1,null==(t=this.e2eClient)||t.setAudioInputEnabled(!1),this.scheduleE2EReconnect(!1)}scheduleE2EReconnect(e){if(this.agentDestroyed||this.agentDestroying||this.serviceMigration||"reconnecting"!==this.agentState||null!==this.reconnectTimer||this.reconnectInFlight||this.sessionReloadFallback)return;if(this.reconnectAttempt>=this.reconnectOptions.maxAttempts)return void this.fallbackToSessionReload();const t=e?0:this.getReconnectDelay(this.reconnectAttempt);this.reconnectTimer=globalThis.setTimeout(()=>{this.reconnectTimer=null,this.attemptE2EReconnect()},t)}async attemptE2EReconnect(){const e=this.e2eClient;if(e&&!this.serviceMigration&&!this.agentDestroyed&&!this.agentDestroying&&"reconnecting"===this.agentState){this.reconnectInFlight=!0,this.reconnectAttempt+=1;try{await e.connect(),this.e2eClient!==e||this.serviceMigration||this.agentDestroyed||this.agentDestroying||this.waitingForTtsaReload||this.finishReconnect()}catch(t){this.e2eClient!==e||this.serviceMigration||this.agentDestroyed||this.agentDestroying||this.scheduleE2EReconnect(!1)}finally{this.e2eClient!==e||this.serviceMigration||(this.reconnectInFlight=!1),this.e2eClient!==e||this.serviceMigration||"reconnecting"!==this.agentState||null!==this.reconnectTimer||this.sessionReloadFallback||this.scheduleE2EReconnect(!1)}}}async fallbackToSessionReload(){if(!(this.serviceMigration||this.sessionReloadFallback||this.agentDestroyed||this.agentDestroying)){this.sessionReloadFallback=!0,this.clearReconnectTimer();try{await this.restartSessionForTransport("e2e_reconnect_exhausted")}catch(e){this.failReconnect(e)}}}finishReconnect(){const e=this.stateBeforeReconnect;this.cancelReconnect(),"idle"!==this.asrState&&this.setASRState("idle"),this.setAgentState(e)}failReconnect(e){this.cancelReconnect(),this.setAgentState("failed"),this.emitConversation({state:"failed"}),this.emitError("network","E2E_RECONNECT_EXHAUSTED","E2E WebSocket 重连失败,请重新初始化 Agent",!1,e)}cancelReconnect(){this.clearReconnectTimer(),null==globalThis.removeEventListener||globalThis.removeEventListener("online",this.handleOnlineForReconnect),this.reconnectAttempt=0,this.reconnectInFlight=!1,this.sessionReloadFallback=!1,this.waitingForTtsaReload=!1}clearReconnectTimer(){null!==this.reconnectTimer&&(globalThis.clearTimeout(this.reconnectTimer),this.reconnectTimer=null)}getReconnectDelay(e){if(0===this.reconnectOptions.initialDelayMs)return 0;const t=Math.min(this.reconnectOptions.maxDelayMs,this.reconnectOptions.initialDelayMs*2**e);return 0===t?0:Math.round(t*(.8+.4*Math.random()))}readRestorableState(){return"running"===this.agentState||"stopped"===this.agentState||"ready"===this.agentState?this.agentState:"ready"}normalizeReconnectOptions(e){var t;const n=this.readNonNegativeNumber(null==e?void 0:e.initialDelayMs,500);return{enabled:null==(t=null==e?void 0:e.enabled)||t,maxAttempts:Math.max(1,Math.floor(this.readNonNegativeNumber(null==e?void 0:e.maxAttempts,6))),initialDelayMs:n,maxDelayMs:Math.max(n,this.readNonNegativeNumber(null==e?void 0:e.maxDelayMs,8e3))}}readNonNegativeNumber(e,t){return"number"==typeof e&&Number.isFinite(e)&&e>=0?e:t}stopASRAfterSocketClose(){if(this.asrStartGeneration+=1,this.stopASRPromise)return;const e=this.microphone;null!=e&&e.isRecording&&(this.microphone=null,this.stopASRPromise=this.finishStopASR(e,!1).catch(e=>{this.emitError("sdk","MICROPHONE_CLEANUP_FAILED","WebSocket 断开后清理麦克风失败",!1,e)}).finally(()=>{this.stopASRPromise=null}))}setAgentState(e){var t;this.agentState=e,this.agentDestroyed&&"destroyed"!==e||null==(t=this.agentOptions.callbacks)||null==t.onAgentStateChange||t.onAgentStateChange(e)}setASRState(e){this.asrState=e,this.safeEmit(()=>{var t;return null==(t=this.agentOptions.callbacks)||null==t.onASRStateChange?void 0:t.onASRStateChange(e)})}emitConversation(e){this.safeEmit(()=>{var t;return null==(t=this.agentOptions.callbacks)||null==t.onConversationChange?void 0:t.onConversationChange(e)})}emitError(e,t,n,i,s){const r={domain:e,code:t,message:n,retryable:i,cause:s};"asr"===e&&this.setASRState("failed"),this.safeEmit(()=>{var e;return null==(e=this.agentOptions.callbacks)||null==e.onError?void 0:e.onError(r)})}safeEmit(e){this.agentDestroyed||this.agentDestroying||e()}assertRunning(){if(this.assertE2EOpen(),this.serviceMigration)throw new Error("Agent 正在迁移 E2E,请等待恢复后再开始新交互");if("running"!==this.agentState)throw new Error("Agent 尚未启动,请先调用 start()")}assertASRStartAllowed(){if(this.assertRunning(),this.getStatus()===i.invisible)throw new Error("数字人处于隐身状态,无法启动 ASR,请先退出隐身模式")}assertE2EOpen(){var e;if(this.assertNotDestroyed(),null==(e=this.e2eClient)||!e.isOpen)throw new Error("Agent 尚未连接 E2E WebSocket")}assertNotDestroyed(){if(this.agentDestroyed||this.agentDestroying)throw new Error("Agent 已销毁")}}export{G as default};