@absolutejs/voice 0.0.22-beta.664 → 0.0.22-beta.666

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,10 +1,10 @@
1
- (()=>{var{defineProperty:k,getOwnPropertyNames:S0,getOwnPropertyDescriptor:w0}=Object,J0=Object.prototype.hasOwnProperty;function h0(V){return this[V]}var Y0=(V)=>{var A=(l??=new WeakMap).get(V),C;if(A)return A;if(A=k({},"__esModule",{value:!0}),V&&typeof V==="object"||typeof V==="function"){for(var g of S0(V))if(!J0.call(A,g))k(A,g,{get:h0.bind(V,g),enumerable:!(C=w0(V,g))||C.enumerable})}return l.set(V,A),A},l;var Z0=(V)=>V;function y0(V,A){this[V]=Z0.bind(null,A)}var Q0=(V,A)=>{for(var C in A)k(V,C,{get:A[C],enumerable:!0,configurable:!0,set:y0.bind(A,C)})};var _V={};Q0(_V,{mount:()=>$0,default:()=>TV,VOICE_EMBED_VERSION:()=>R0});var E0=(V)=>{if(typeof V!=="string")return V;return document.querySelector(V)},K0=(V,A,C,g)=>{let H=A??V.getAttribute("hx-get")??"";if(!H)return"";let _=new URL(H,window.location.origin);if(g)_.searchParams.set(C,g);else _.searchParams.delete(C);return`${_.pathname}${_.search}${_.hash}`},d=(V,A)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let C=E0(A.element);if(!C)return()=>{};let g=A.eventName??"voice-refresh",H=A.sessionQueryParam??"sessionId",_=()=>{let X=window,R=K0(C,A.route,H,V.sessionId);if(R)C.setAttribute("hx-get",R);X.htmx?.process?.(C),X.htmx?.trigger?.(C,g)},D=V.subscribe(_);return _(),()=>{D()}};var c0=(V)=>Math.max(-1,Math.min(1,V)),q0=(V)=>{let A=new Int16Array(V.length);for(let C=0;C<V.length;C+=1){let g=c0(V[C]??0);A[C]=g<0?g*32768:g*32767}return new Uint8Array(A.buffer)},N0=(V)=>{let A=V instanceof Uint8Array?V:new Uint8Array(V);if(A.byteLength<2)return 0;let C=new Int16Array(A.buffer,A.byteOffset,Math.floor(A.byteLength/2));if(C.length===0)return 0;let g=0;for(let H of C){let _=H/32768;g+=_*_}return Math.min(1,Math.max(0,Math.sqrt(g/C.length)*5.5))},z0=(V,A,C)=>{if(A===C)return V;let g=A/C,H=Math.round(V.length/g),_=new Float32Array(H),D=0,X=0;while(D<_.length){let R=Math.round((D+1)*g),O=0,L=0;for(let W=X;W<R&&W<V.length;W+=1)O+=V[W]??0,L+=1;_[D]=L>0?O/L:0,D+=1,X=R}return _},v=(V)=>{let A=null,C=null,g=null,H=null,_=0,D=null,X=(L)=>{L.processorNode?.disconnect(),L.sourceNode?.disconnect(),L.mediaStream?.getTracks().forEach((W)=>W.stop()),L.audioContext?.close()};return{start:()=>{if(H)return Promise.resolve();if(D)return D;let L=++_,U=(async()=>{if(!V.stream&&(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia))throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let T=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!T)throw Error("Browser microphone capture requires AudioContext support.");let w=null,Z=null,Y=null,Q=null;try{if(Z=V.stream??await navigator.mediaDevices.getUserMedia({audio:{autoGainControl:!0,channelCount:V.channelCount??1,echoCancellation:!0,noiseSuppression:!0}}),L!==_)throw Error("Microphone capture stopped during startup.");if(w=new T,w.state==="suspended")await w.resume();if(L!==_)throw Error("Microphone capture stopped during startup.");if(Q=w.createMediaStreamSource(Z),Y=w.createScriptProcessor(4096,1,1),Y.onaudioprocess=(G)=>{let K=G.inputBuffer.getChannelData(0),q=z0(K,w?.sampleRate??48000,V.sampleRateHz??16000),$=q0(q);V.onLevel?.(N0($)),V.onAudio($)},Q.connect(Y),Y.connect(w.destination),L!==_)throw Error("Microphone capture stopped during startup.");A=w,H=Z,g=Y,C=Q}catch(G){throw X({audioContext:w,mediaStream:Z,processorNode:Y,sourceNode:Q}),G}})().finally(()=>{if(D===U)D=null});return D=U,U},stop:()=>{_+=1,X({audioContext:A,mediaStream:H,processorNode:g,sourceNode:C}),V.onLevel?.(0),A=null,H=null,g=null,C=null}}};var F=(V)=>{if(typeof V==="string"&&V.trim())return V;if(V instanceof Error&&V.message.trim())return V.message;if(V&&typeof V==="object"){let A=V;for(let C of["message","reason","description"]){let g=A[C];if(typeof g==="string"&&g.trim())return g}if("error"in A)return F(A.error);if("cause"in A)return F(A.cause);try{return JSON.stringify(V)}catch{}}return"Unexpected error"},i=(V)=>{switch(V.type){case"audio":return{chunk:Uint8Array.from(atob(V.chunkBase64),(A)=>A.charCodeAt(0)),format:V.format,receivedAt:V.receivedAt,turnId:V.turnId,type:"audio"};case"assistant":return{text:V.text,turnId:V.turnId,type:"assistant"};case"assistant_delta":return{delta:V.delta,turnId:V.turnId,type:"assistant_delta"};case"complete":return{sessionId:V.sessionId,type:"complete"};case"connection":return{reconnect:V.reconnect,type:"connection"};case"call_lifecycle":return{event:V.event,sessionId:V.sessionId,type:"call_lifecycle"};case"error":return{message:F(V.message),recoverable:V.recoverable,type:"error"};case"call_control_ack":return null;case"final":return{transcript:V.transcript,type:"final"};case"partial":return{transcript:V.transcript,type:"partial"};case"replay":return{assistantTexts:V.assistantTexts,call:V.call,partial:V.partial,scenarioId:V.scenarioId,sessionId:V.sessionId,sessionMetadata:V.sessionMetadata,status:V.status,turns:V.turns,type:"replay"};case"session":return{paused:V.paused,pauseExpiresAt:V.pauseExpiresAt,sessionId:V.sessionId,sessionMetadata:V.sessionMetadata,scenarioId:V.scenarioId,status:V.status,type:"session"};case"turn":return{turn:V.turn,type:"turn"};default:return null}};var UV=Math.PI*2;var N=(V,A,C,g)=>{V.push({code:C,message:g,severity:A})};var P0=(V)=>V.length===0?void 0:V.reduce((A,C)=>A+C,0)/V.length,x=(V)=>V.length===0?void 0:Math.max(...V);var E=(V,A)=>{let C=V[A];return typeof C==="number"&&Number.isFinite(C)?C:void 0},j=(V,A)=>{let C=V[A];return typeof C==="boolean"?C:void 0},c=(V,A)=>{let C=V[A];return typeof C==="string"?C:void 0},f=(V)=>String(V.id??c(V,"ssrc")??E(V,"ssrc")??c(V,"trackIdentifier")??c(V,"mid")??"unknown"),o=(V)=>V===void 0?void 0:V*1000;var B0=(V)=>{let A={};for(let[C,g]of Object.entries(V))if(g===null||typeof g==="boolean"||typeof g==="number"||typeof g==="string")A[C]=g;return A};var n=(V={})=>{let A=V.stats??[],C=[],g=A.filter(($)=>$.type==="inbound-rtp"&&c($,"kind")!=="video"),H=A.filter(($)=>$.type==="outbound-rtp"&&c($,"kind")!=="video"),_=A.filter(($)=>$.type==="candidate-pair"),D=A.filter(($)=>($.type==="track"||$.type==="media-source")&&c($,"kind")==="audio"),X=_.filter(($)=>j($,"selected")===!0||j($,"nominated")===!0||c($,"state")==="succeeded").length,R=D.filter(($)=>c($,"readyState")!=="ended"&&c($,"trackState")!=="ended"&&j($,"ended")!==!0).length,O=D.filter(($)=>c($,"readyState")==="ended"||c($,"trackState")==="ended"||j($,"ended")===!0).length,L=g.reduce(($,J)=>$+(E(J,"packetsReceived")??0),0),W=H.reduce(($,J)=>$+(E(J,"packetsSent")??0),0),U=[...g,...H].reduce(($,J)=>$+Math.max(0,E(J,"packetsLost")??0),0),T=L+U,w=T===0?0:U/T,Z=g.reduce(($,J)=>$+(E(J,"bytesReceived")??0),0),Y=H.reduce(($,J)=>$+(E(J,"bytesSent")??0),0),Q=x(_.map(($)=>o(E($,"currentRoundTripTime")??E($,"roundTripTime"))).filter(($)=>$!==void 0)),G=x([...g,...H].map(($)=>o(E($,"jitter"))).filter(($)=>$!==void 0)),K=x(g.map(($)=>{let J=E($,"jitterBufferDelay"),b=E($,"jitterBufferEmittedCount");return J!==void 0&&b!==void 0&&b>0?J/b*1000:void 0}).filter(($)=>$!==void 0)),q=D.map(($)=>E($,"audioLevel")).filter(($)=>$!==void 0);if(V.requireConnectedCandidatePair&&_.length>0&&X===0)N(C,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(V.requireLiveAudioTrack&&R===0)N(C,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(V.maxPacketLossRatio!==void 0&&w>V.maxPacketLossRatio)N(C,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(w)} above ${String(V.maxPacketLossRatio)}.`);if(V.maxRoundTripTimeMs!==void 0&&Q!==void 0&&Q>V.maxRoundTripTimeMs)N(C,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(Q)}ms above ${String(V.maxRoundTripTimeMs)}ms.`);if(V.maxJitterMs!==void 0&&G!==void 0&&G>V.maxJitterMs)N(C,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(G)}ms above ${String(V.maxJitterMs)}ms.`);return{activeCandidatePairs:X,audioLevelAverage:P0(q),bytesReceived:Z,bytesSent:Y,checkedAt:Date.now(),endedAudioTracks:O,inboundPackets:L,issues:C,jitterBufferDelayMs:K,jitterMs:G,liveAudioTracks:R,outboundPackets:W,packetLossRatio:w,packetsLost:U,roundTripTimeMs:Q,status:C.some(($)=>$.severity==="error")?"fail":C.length>0?"warn":"pass",totalStats:A.length}},m=async(V)=>{return[...(await V.peerConnection.getStats(V.selector??null)).values()].map(B0)};var u=(V={})=>{let A=V.stats??[],C=V.previousStats??[],g=[],H=new Map(C.map((U)=>[f(U),U])),D=A.filter((U)=>(U.type==="inbound-rtp"||U.type==="outbound-rtp")&&c(U,"kind")!=="video"&&c(U,"mediaType")!=="video").map((U)=>{let T=U.type==="outbound-rtp"?"outbound":"inbound",w=T==="outbound"?"packetsSent":"packetsReceived",Z=T==="outbound"?"bytesSent":"bytesReceived",Y=H.get(f(U)),Q=E(U,w),G=Y?E(Y,w):void 0,K=E(U,Z),q=Y?E(Y,Z):void 0,$=U.timestamp!==void 0&&Y?.timestamp!==void 0?U.timestamp-Y.timestamp:void 0;return{bytesDelta:K!==void 0&&q!==void 0?K-q:void 0,currentPackets:Q,direction:T,id:f(U),packetDelta:Q!==void 0&&G!==void 0?Q-G:void 0,previousPackets:G,timeDeltaMs:$}}),X=D.filter((U)=>U.direction==="inbound"),R=D.filter((U)=>U.direction==="outbound"),O=x(D.map((U)=>U.timeDeltaMs).filter((U)=>U!==void 0)),L=X.filter((U)=>V.maxInboundPacketStallMs!==void 0&&U.timeDeltaMs!==void 0&&U.timeDeltaMs>=V.maxInboundPacketStallMs&&U.packetDelta!==void 0&&U.packetDelta<=0).length,W=R.filter((U)=>V.maxOutboundPacketStallMs!==void 0&&U.timeDeltaMs!==void 0&&U.timeDeltaMs>=V.maxOutboundPacketStallMs&&U.packetDelta!==void 0&&U.packetDelta<=0).length;if(V.requireInboundAudio&&X.length===0)N(g,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(V.requireOutboundAudio&&R.length===0)N(g,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(V.maxGapMs!==void 0&&O!==void 0&&O>V.maxGapMs)N(g,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(O)}ms above ${String(V.maxGapMs)}ms.`);if(L>0)N(g,"error","media.webrtc_inbound_stalled",`${String(L)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(W>0)N(g,"error","media.webrtc_outbound_stalled",`${String(W)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:X.length,issues:g,maxObservedGapMs:O,outboundAudioStreams:R.length,stalledInboundStreams:L,stalledOutboundStreams:W,status:g.some((U)=>U.severity==="error")?"fail":g.length>0?"warn":"pass",streams:D,totalStats:A.length}};var M0="/api/voice/browser-media",b0=5000,j0=async(V)=>V.peerConnection??await V.getPeerConnection?.()??null,x0=async(V,A)=>{let C=A.fetch??globalThis.fetch;if(!C)return;await C(A.path??M0,{body:JSON.stringify(V),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},r=(V)=>{let A=null,C=[],g=async()=>{let D=await j0(V);if(!D)return;let X=await m({peerConnection:D}),R=n({...V,stats:X}),O=V.continuity===!1?void 0:u({...V.continuity,previousStats:C,stats:X}),L={at:Date.now(),continuity:O,report:R,scenarioId:V.getScenarioId?.()??null,sessionId:V.getSessionId?.()??null};return C=X,V.onReport?.(L),await x0(L,V),L},H=()=>{g().catch((D)=>{V.onError?.(D)})},_=()=>{if(A)clearInterval(A),A=null};return{close:_,reportOnce:g,stop:_,start:()=>{if(A)return;H(),A=setInterval(H,V.intervalMs??b0)}}};var k0=(V,A,C)=>Math.min(C,A*2**(Math.max(1,V)-1));class s extends Error{constructor(V="Voice authorization could not be renewed."){super(V);this.name="VoiceReconnectRejectedError"}}var P=()=>{},F0=()=>Promise.resolve(),f0=()=>P,l0={callControl:F0,close:P,disconnect:P,endTurn:P,send:P,sendAudio:P,simulateDisconnect:P,subscribe:f0,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",start:()=>{}},d0=()=>crypto.randomUUID(),v0=(V,A,C,g)=>{let{hostname:H,port:_,protocol:D}=window.location,X=D==="https:"?"wss:":"ws:",R=_?`:${_}`:"",O=new URL(`${X}//${H}${R}${V}`);if(O.searchParams.set("sessionId",A),C)O.searchParams.set("scenarioId",C);for(let[L,W]of Object.entries(g))if(L!=="sessionId"&&L!=="scenarioId"&&W)O.searchParams.set(L,W);return O.toString()},i0=(V)=>{if(!V||typeof V!=="object"||!("type"in V))return!1;switch(V.type){case"audio":case"assistant":case"call_lifecycle":case"call_control_ack":case"complete":case"connection":case"error":case"final":case"partial":case"pong":case"replay":case"session":case"turn":return!0;default:return!1}},o0=(V)=>{if(typeof V.data!=="string")return null;try{let A=JSON.parse(V.data);return i0(A)?A:null}catch{return null}},a=(V,A={})=>{if(typeof window>"u")return l0;let C=new Set,g=new Map,H=A.reconnect!==!1,_=A.maxReconnectAttempts??15,D=A.reconnectMaxDelayMs??8000,X=A.pingInterval??30000,R=A.reconnectResetAfterMs??30000,O=0,L=!1,W=null,U=(I)=>k0(I,500,D),T={isConnected:!1,pendingMessages:[],scenarioId:A.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectResetTimeout:null,reconnectTimeout:null,sessionId:A.sessionId??d0(),ws:null},w=(I)=>{C.forEach((S)=>S(I))},Z=(I)=>{C.forEach((S)=>S({message:I,recoverable:!1,type:"error"})),w({reconnect:{attempts:T.reconnectAttempts,maxAttempts:_,status:"exhausted"},type:"connection"})},Y=()=>{if(T.pingInterval)clearInterval(T.pingInterval),T.pingInterval=null;if(T.reconnectTimeout)clearTimeout(T.reconnectTimeout),T.reconnectTimeout=null;if(T.reconnectResetTimeout)clearTimeout(T.reconnectResetTimeout),T.reconnectResetTimeout=null},Q=()=>{if(T.ws?.readyState!==1)return;while(T.pendingMessages.length>0){let I=T.pendingMessages.shift();if(I!==void 0)T.ws.send(I)}},G=(I)=>{for(let S of g.values())clearTimeout(S.timeout),S.reject(Error(I));g.clear()},K=()=>{if(L)return;let I=O;T.reconnectAttempts+=1;let S=U(T.reconnectAttempts),y=Date.now()+S;w({reconnect:{attempts:T.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:_,nextAttemptAt:y,status:"reconnecting"},type:"connection"}),T.reconnectTimeout=setTimeout(async()=>{if(L||O!==I)return;if(T.reconnectAttempts>_){Z("Voice connection could not be restored.");return}try{if(A.prepareReconnect){let h=new AbortController;W=h;let B=setTimeout(()=>h.abort("Voice reconnect preparation timed out"),A.prepareReconnectTimeoutMs??1e4);try{await Promise.race([A.prepareReconnect({attempt:T.reconnectAttempts,path:V,scenarioId:T.scenarioId,sessionId:T.sessionId,signal:h.signal}),new Promise((M,O0)=>{h.signal.addEventListener("abort",()=>O0(Error("Voice reconnect preparation timed out.")),{once:!0})})])}finally{if(clearTimeout(B),W===h)W=null}}}catch(h){if(L||O!==I)return;if(h instanceof s){Z(h.message);return}if(T.reconnectAttempts<_)K();else Z("Voice authorization could not be renewed for reconnect.");return}if(L||O!==I)return;q()},S)},q=()=>{if(L)return;let I=new WebSocket(v0(V,T.sessionId,T.scenarioId,A.query??{}));I.binaryType="arraybuffer",I.onopen=()=>{if(T.ws!==I){I.close(1000);return}let S=T.reconnectAttempts>0;if(T.isConnected=!0,Q(),S)w({reconnect:{attempts:T.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:_,status:"resumed"},type:"connection"}),T.reconnectResetTimeout=setTimeout(()=>{if(T.ws===I&&I.readyState===1)T.reconnectAttempts=0;T.reconnectResetTimeout=null},R);T.pingInterval=setInterval(()=>{if(I.readyState===1)I.send(JSON.stringify({type:"ping"}))},X)},I.onmessage=(S)=>{if(T.ws!==I)return;let y=o0(S);if(!y)return;if(y.type==="session")T.sessionId=y.sessionId,T.scenarioId=y.scenarioId??T.scenarioId;if(y.type==="call_control_ack"){let h=g.get(y.requestId);if(h)if(clearTimeout(h.timeout),g.delete(y.requestId),y.ok)h.resolve();else h.reject(Error(y.message??`${y.action} was not applied`))}C.forEach((h)=>h(y))},I.onclose=(S)=>{if(T.ws!==I)return;T.ws=null,T.isConnected=!1,Y(),G("Voice connection interrupted");let y=H&&S.code!==1000&&S.code!==4401&&S.code!==4403&&S.code!==4500&&T.reconnectAttempts<_,h=S.code===4401||S.code===4403,B=S.code===4500;if(h)C.forEach((M)=>M({message:S.code===4401?"Voice authorization expired or was rejected.":"Voice authorization was forbidden.",recoverable:!1,type:"error"}));if(B)C.forEach((M)=>M({message:"Voice provider initialization failed.",recoverable:!1,type:"error"}));if(y)K();else if(H&&S.code!==1000){if(h)w({reconnect:{attempts:T.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:_,status:"exhausted"},type:"connection"});else if(!B)Z("Voice connection could not be restored.")}},T.ws=I},$=(I)=>{if(L)return;if(T.ws?.readyState===1){T.ws.send(I);return}if(typeof I!=="string")return;if(T.pendingMessages.length>=100)T.pendingMessages.shift();T.pendingMessages.push(I)},J=(I)=>{$(JSON.stringify(I))},b=(I={})=>{if(I.sessionId&&I.sessionId!==T.sessionId)throw Error("Voice session switching requires a new connection instance.");if(I.sessionId)T.sessionId=I.sessionId;if(I.scenarioId)T.scenarioId=I.scenarioId;J({scenarioId:T.scenarioId??void 0,sessionId:T.sessionId,type:"start"})},I0=(I)=>{$(I)},U0=()=>{J({type:"end_turn"})},L0=(I)=>{if(T.ws?.readyState!==1)return Promise.reject(Error(`${I.action} is unavailable while disconnected`));let S=crypto.randomUUID(),{promise:y,reject:h,resolve:B}=Promise.withResolvers(),M=setTimeout(()=>{g.delete(S),h(Error(`${I.action} acknowledgement timed out`))},5000);return g.set(S,{reject:h,resolve:B,timeout:M}),T.ws.send(JSON.stringify({...I,requestId:S,type:"call_control"})),y},W0=(I="client-close")=>{if(L=!0,W?.abort(),W=null,O+=1,Y(),G("Voice connection closed"),T.pendingMessages.length=0,T.ws){let S=T.ws;if(T.ws=null,S.readyState===1)S.send(JSON.stringify({reason:I,type:"close"}));S.close(1000)}T.isConnected=!1,C.clear()},D0=()=>{if(L=!0,W?.abort(),W=null,O+=1,Y(),G("Voice connection disconnected"),T.pendingMessages.length=0,T.ws){let I=T.ws;T.ws=null,I.close(1000)}T.isConnected=!1,C.clear()},X0=()=>{if(T.ws?.readyState===1)T.ws.close(4000,"absolutejs-voice-reconnect-proof")},G0=(I)=>{return C.add(I),()=>{C.delete(I)}};return q(),{callControl:L0,close:W0,disconnect:D0,endTurn:U0,send:J,sendAudio:I0,simulateDisconnect:X0,start:b,subscribe:G0,getReadyState:()=>T.ws?.readyState??3,getScenarioId:()=>T.scenarioId??"",getSessionId:()=>T.sessionId}};var n0=()=>({attempts:0,maxAttempts:0,status:"idle"}),m0=(V,A)=>{let C=A.trim().replace(/\s+/g," ");if(!C)return V;if(!V)return C;if(V===C||V.endsWith(C))return V;if(C.includes(V))return C;return`${V} ${C}`},u0=(V,A)=>{let C=A.trim().replace(/\s+/g," ");if(!V)return C;if(!C||V.endsWith(C))return V;return`${V} ${C}`},r0=()=>({assistantAudio:[],assistantStreamingText:"",assistantTexts:[],call:null,error:null,isConnected:!1,paused:!1,partial:"",reconnect:n0(),scenarioId:null,sessionId:null,sessionMetadata:null,status:"idle",turns:[]}),p=()=>{let V=r0(),A="",C=new Set,g=()=>{C.forEach((_)=>_())};return{dispatch:(_)=>{switch(_.type){case"audio":V={...V,assistantAudio:[...V.assistantAudio,{chunk:_.chunk,format:_.format,receivedAt:_.receivedAt,turnId:_.turnId}]};break;case"assistant":V={...V,assistantStreamingText:"",assistantTexts:[...V.assistantTexts,_.text]};break;case"assistant_delta":V={...V,assistantStreamingText:`${V.assistantStreamingText}${_.delta}`};break;case"complete":V={...V,sessionId:_.sessionId,status:"completed"};break;case"call_lifecycle":V={...V,call:{...V.call,disposition:_.event.type==="end"?_.event.disposition:V.call?.disposition,endedAt:_.event.type==="end"?_.event.at:V.call?.endedAt,events:[...V.call?.events??[],_.event],lastEventAt:_.event.at,startedAt:V.call?.startedAt??_.event.at},sessionId:_.sessionId};break;case"connected":V={...V,isConnected:!0,reconnect:V.reconnect.status==="reconnecting"?{...V.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:V.reconnect};break;case"connection":V={...V,reconnect:_.reconnect};break;case"disconnected":V={...V,isConnected:!1};break;case"error":V={...V,error:_.message,..._.recoverable===!1?{isConnected:!1,status:"failed"}:{}};break;case"final":A=m0(A,_.transcript.text),V={...V,partial:A};break;case"partial":V={...V,partial:u0(A,_.transcript.text)};break;case"replay":A=_.partial,V={...V,assistantStreamingText:"",assistantTexts:[..._.assistantTexts],call:_.call??null,error:null,isConnected:_.status==="active",partial:_.partial,reconnect:V.reconnect.status==="reconnecting"?{...V.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:V.reconnect,scenarioId:_.scenarioId??V.scenarioId,sessionId:_.sessionId,sessionMetadata:_.sessionMetadata??V.sessionMetadata,status:_.status,turns:[..._.turns]};break;case"session":V={...V,error:null,paused:_.paused??!1,pauseExpiresAt:_.pauseExpiresAt,scenarioId:_.scenarioId??V.scenarioId,isConnected:_.status==="active",sessionId:_.sessionId,sessionMetadata:_.sessionMetadata??V.sessionMetadata,status:_.status};break;case"turn":A="",V={...V,partial:"",turns:[...V.turns,_.turn]};break}g()},getServerSnapshot:()=>V,getSnapshot:()=>V,subscribe:(_)=>{return C.add(_),()=>{C.delete(_)}}}};var t=(V,A={})=>{let C=a(V,A),g=p(),H=A.browserMedia&&typeof window<"u"?r({...A.browserMedia,getScenarioId:()=>A.browserMedia?A.browserMedia.getScenarioId?.()??C.getScenarioId():C.getScenarioId(),getSessionId:()=>A.browserMedia?A.browserMedia.getSessionId?.()??C.getSessionId():C.getSessionId()}):null,_=new Set,D=!1,X=(W)=>Promise.resolve().then(()=>{if(D)return;if(!W?.sessionId&&!W?.scenarioId)return;C.start(W),H?.start()}),R=()=>{_.forEach((W)=>W())},O=()=>{if(!A.reconnectReportPath||typeof fetch>"u")return;let W=g.getSnapshot(),U=JSON.stringify({at:Date.now(),reconnect:W.reconnect,scenarioId:W.scenarioId,sessionId:C.getSessionId(),turnIds:W.turns.map((T)=>T.id)});fetch(A.reconnectReportPath,{body:U,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},L=C.subscribe((W)=>{let U=i(W);if(U){if(g.dispatch(U),W.type==="connection")O();R()}});return{start:X,get assistantAudio(){return g.getSnapshot().assistantAudio},get assistantTexts(){return g.getSnapshot().assistantTexts},get assistantStreamingText(){return g.getSnapshot().assistantStreamingText},get call(){return g.getSnapshot().call},callControl(W){return C.callControl(W)},close(W){D=!0,L(),H?.close(),C.close(W),g.dispatch({type:"disconnected"}),R()},disconnect(){D=!0,L(),H?.close(),C.disconnect(),g.dispatch({type:"disconnected"}),R()},endTurn(){C.endTurn()},get error(){return g.getSnapshot().error},getServerSnapshot(){return g.getServerSnapshot()},getSnapshot(){return g.getSnapshot()},get isConnected(){return g.getSnapshot().isConnected},get partial(){return g.getSnapshot().partial},get paused(){return g.getSnapshot().paused},get pauseExpiresAt(){return g.getSnapshot().pauseExpiresAt},get reconnect(){return g.getSnapshot().reconnect},get scenarioId(){return g.getSnapshot().scenarioId},sendAudio(W){C.sendAudio(W)},get sessionId(){return C.getSessionId()},get sessionMetadata(){return g.getSnapshot().sessionMetadata},simulateDisconnect(){C.simulateDisconnect()},get status(){return g.getSnapshot().status},subscribe(W){return _.add(W),()=>{_.delete(W)}},get turns(){return g.getSnapshot().turns}}};var e=(V)=>{if(!V||V.enabled===!1)return;return{enabled:!0,maxGain:V.maxGain??3,noiseGateAttenuation:V.noiseGateAttenuation??0.15,noiseGateThreshold:V.noiseGateThreshold??0.006,targetLevel:V.targetLevel??0.08}};var s0={balanced:{qualityProfile:"general",minSilenceMs:400,silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:"general",minSilenceMs:300,silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},"long-form":{qualityProfile:"general",minSilenceMs:600,silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},a0={"accent-heavy":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},general:{},"noisy-room":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},"short-command":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var V0=(V)=>{let A=V?.profile??"fast",C=V?.qualityProfile??"general",g=s0[A],H=a0[C],_=V?.silenceMs??H.silenceMs??g.silenceMs;return{profile:A,qualityProfile:C,minSilenceMs:Math.min(_,V?.minSilenceMs??H.minSilenceMs??g.minSilenceMs),silenceMs:_,speechThreshold:V?.speechThreshold??H.speechThreshold??g.speechThreshold,transcriptStabilityMs:V?.transcriptStabilityMs??H.transcriptStabilityMs??g.transcriptStabilityMs}};var p0={chat:{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"balanced",qualityProfile:"short-command"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"fast",qualityProfile:"general"}},dictation:{audioConditioning:{enabled:!0,maxGain:2.25,noiseGateAttenuation:0.05,noiseGateThreshold:0.003,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"guided-intake":{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"turn-scoped",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"noisy-room":{audioConditioning:{enabled:!0,maxGain:3,noiseGateAttenuation:0.12,noiseGateThreshold:0.006,targetLevel:0.085},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:2100,speechThreshold:0.02,transcriptStabilityMs:1650}},"pstn-balanced":{audioConditioning:{enabled:!0,maxGain:2.8,noiseGateAttenuation:0.07,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:660,speechThreshold:0.012,transcriptStabilityMs:300}},"pstn-fast":{audioConditioning:{enabled:!0,maxGain:2.75,noiseGateAttenuation:0.06,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:620,speechThreshold:0.012,transcriptStabilityMs:280}},reliability:{audioConditioning:{enabled:!0,maxGain:2.9,noiseGateAttenuation:0.08,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room"}}},A0=(V="default")=>{let A=p0[V];return{audioConditioning:e(A.audioConditioning),capture:{channelCount:A.capture?.channelCount??1,sampleRateHz:A.capture?.sampleRateHz??16000},connection:{...A.connection},name:V,sttLifecycle:A.sttLifecycle??"continuous",turnDetection:V0(A.turnDetection)}};var t0=(V)=>({assistantAudio:[...V.assistantAudio],assistantStreamingText:V.assistantStreamingText,assistantTexts:[...V.assistantTexts],call:V.call,error:V.error,isConnected:V.isConnected,isRecording:!1,paused:V.paused,pauseExpiresAt:V.pauseExpiresAt,partial:V.partial,reconnect:V.reconnect,recordingError:null,sessionId:V.sessionId,sessionMetadata:V.sessionMetadata,scenarioId:V.scenarioId,status:V.status,turns:[...V.turns]}),g0=(V,A={})=>{let C=A0(A.preset),g=t(V,{...C.connection,...A.connection}),H=null,_=0,D=!1,X=null,R=t0(g),O=new Set,L=()=>{for(let G of O)G()},W=()=>{if(R={...R,assistantAudio:[...g.assistantAudio],assistantStreamingText:g.assistantStreamingText,assistantTexts:[...g.assistantTexts],call:g.call,error:g.error,isConnected:g.isConnected,paused:g.paused,pauseExpiresAt:g.pauseExpiresAt,partial:g.partial,reconnect:g.reconnect,sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,scenarioId:g.scenarioId,status:g.status,turns:[...g.turns]},A.autoStopOnComplete!==!1&&R.status==="completed"&&R.isRecording)H?.stop(),H=null,R={...R,isRecording:!1};L()},U=g.subscribe(W);W();let T=()=>{if(H)return H;return H=v({channelCount:A.capture?.channelCount??C.capture.channelCount,onLevel:A.capture?.onLevel,onAudio:(G)=>{if(A.capture?.onAudio){A.capture.onAudio(G,g.sendAudio);return}g.sendAudio(G)},sampleRateHz:A.capture?.sampleRateHz??C.capture.sampleRateHz,...A.capture?.stream?{stream:A.capture.stream}:{}}),H},w=()=>{_+=1,H?.stop(),H=null,R={...R,isRecording:!1},L()},Z=async()=>{if(D)throw Error("Voice controller is closed.");if(R.isRecording)return;if(X)return X;let G=++_,K=T(),$=(async()=>{try{if(R={...R,recordingError:null},L(),await K.start(),D||G!==_||H!==K){K.stop();return}R={...R,isRecording:!0},L()}catch(J){if(K.stop(),G!==_||H!==K)throw J;throw H=null,R={...R,isRecording:!1,recordingError:J instanceof Error?J.message:String(J)},L(),J}})().finally(()=>{if(X===$)X=null});return X=$,X};return{close:(G)=>{D=!0,U(),w(),g.close(G)},disconnect:()=>{D=!0,U(),w(),g.disconnect()},startRecording:Z,stopRecording:w,get assistantAudio(){return R.assistantAudio},get assistantTexts(){return R.assistantTexts},get assistantStreamingText(){return R.assistantStreamingText},bindHTMX(G){return d(g,G)},get call(){return R.call},callControl:(G)=>g.callControl(G),endTurn:()=>g.endTurn(),get error(){return R.error},getServerSnapshot:()=>R,getSnapshot:()=>R,get isConnected(){return R.isConnected},get isRecording(){return R.isRecording},get partial(){return R.partial},get paused(){return R.paused},get pauseExpiresAt(){return R.pauseExpiresAt},get reconnect(){return R.reconnect},get recordingError(){return R.recordingError},get scenarioId(){return R.scenarioId},sendAudio:(G)=>g.sendAudio(G),get sessionId(){return R.sessionId},get sessionMetadata(){return R.sessionMetadata},simulateDisconnect:()=>g.simulateDisconnect(),get status(){return R.status},subscribe:(G)=>{return O.add(G),()=>{O.delete(G)}},toggleRecording:async()=>{if(R.isRecording){w();return}await Z()},get turns(){return R.turns}}};var z=(V)=>String(V).replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;");var C0=(V)=>{if(!V.isConnected)return"idle";if(V.isPlaying)return"speaking";if(V.isRecording&&V.hasActivePartial)return"listening";if(V.isRecording)return"listening";if(V.lastTranscriptAt&&!V.lastAssistantAt)return"thinking";if(V.lastTranscriptAt&&V.lastAssistantAt&&V.lastTranscriptAt>V.lastAssistantAt)return"thinking";return"idle"};var e0={accent:"#3b82f6",background:"#0f172a",errorAccent:"#ef4444",fontFamily:'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',foreground:"#f8fafc",radius:16},VV={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},AV=(V,A)=>{switch(V){case"listening":return A.listening;case"speaking":return A.speaking;case"thinking":return A.thinking;case"idle":return A.idle}},_0=(V)=>{let A={...e0,...V.theme},C={...VV,...V.labels},g=V.state.assistantAudio.at(-1)?.receivedAt,H=V.state.turns.at(-1)?.committedAt,_=C0({hasActivePartial:V.state.partial.length>0,isConnected:V.state.isConnected,isPlaying:!1,isRecording:V.state.isRecording,lastAssistantAt:g,lastTranscriptAt:H}),D=!V.state.isConnected&&V.state.status!=="idle"&&!V.state.error,X=V.state.error?"Error":D?C.connecting:V.state.status==="completed"?C.callEnded:AV(_,C);return{agentState:_,classes:{container:`absolute-voice-widget absolute-voice-widget--${_}`,dot:`absolute-voice-widget__dot${V.state.error?" absolute-voice-widget__dot--error":""}`},controls:{canEnd:V.state.isConnected,canMute:V.state.isRecording,canStart:!V.state.isRecording&&V.state.status!=="completed"},errorMessage:V.state.error??void 0,labels:C,partial:V.state.partial||void 0,statusLabel:X,theme:A,title:V.title??"Voice"}},gV=(V)=>typeof V==="number"?`${V}px`:V,T0=(V)=>{let A=V.theme,C=`background:${A.background};border-radius:${gV(A.radius)};color:${A.foreground};font-family:${A.fontFamily};min-width:240px;padding:20px 22px;`,g=`background:${V.errorMessage?A.errorAccent:V.agentState==="idle"?"rgba(148,163,184,0.6)":A.accent};border-radius:50%;height:10px;width:10px;`,H=[];if(V.controls.canStart)H.push(`<button type="button" data-action="start" style="background:${A.accent};border:none;border-radius:12px;color:${A.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${z(V.labels.startCall)}</button>`);if(V.controls.canMute)H.push(`<button type="button" data-action="mute" style="background:transparent;border:1px solid rgba(255,255,255,0.18);border-radius:12px;color:${A.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${z(V.labels.mute)}</button>`);if(V.controls.canEnd)H.push(`<button type="button" data-action="end" style="background:${A.errorAccent};border:none;border-radius:12px;color:${A.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${z(V.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${V.agentState}" class="${z(V.classes.container)}" style="${C}">
1
+ (()=>{var{defineProperty:k,getOwnPropertyNames:S0,getOwnPropertyDescriptor:O0}=Object,w0=Object.prototype.hasOwnProperty;function h0(V){return this[V]}var J0=(V)=>{var g=(l??=new WeakMap).get(V),C;if(g)return g;if(g=k({},"__esModule",{value:!0}),V&&typeof V==="object"||typeof V==="function"){for(var A of S0(V))if(!w0.call(g,A))k(g,A,{get:h0.bind(V,A),enumerable:!(C=O0(V,A))||C.enumerable})}return l.set(V,g),g},l;var Y0=(V)=>V;function Z0(V,g){this[V]=Y0.bind(null,g)}var Q0=(V,g)=>{for(var C in g)k(V,C,{get:g[C],enumerable:!0,configurable:!0,set:Z0.bind(g,C)})};var _V={};Q0(_V,{mount:()=>T0,default:()=>RV,VOICE_EMBED_VERSION:()=>$0});var E0=(V)=>{if(typeof V!=="string")return V;return document.querySelector(V)},c0=(V,g,C,A)=>{let H=g??V.getAttribute("hx-get")??"";if(!H)return"";let _=new URL(H,window.location.origin);if(A)_.searchParams.set(C,A);else _.searchParams.delete(C);return`${_.pathname}${_.search}${_.hash}`},d=(V,g)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let C=E0(g.element);if(!C)return()=>{};let A=g.eventName??"voice-refresh",H=g.sessionQueryParam??"sessionId",_=()=>{let D=window,$=c0(C,g.route,H,V.sessionId);if($)C.setAttribute("hx-get",$);D.htmx?.process?.(C),D.htmx?.trigger?.(C,A)},y=V.subscribe(_);return _(),()=>{y()}};var K0=(V)=>Math.max(-1,Math.min(1,V)),q0=(V)=>{let g=new Int16Array(V.length);for(let C=0;C<V.length;C+=1){let A=K0(V[C]??0);g[C]=A<0?A*32768:A*32767}return new Uint8Array(g.buffer)},N0=(V)=>{let g=V instanceof Uint8Array?V:new Uint8Array(V);if(g.byteLength<2)return 0;let C=new Int16Array(g.buffer,g.byteOffset,Math.floor(g.byteLength/2));if(C.length===0)return 0;let A=0;for(let H of C){let _=H/32768;A+=_*_}return Math.min(1,Math.max(0,Math.sqrt(A/C.length)*5.5))},z0=(V,g,C)=>{if(g===C)return V;let A=g/C,H=Math.round(V.length/A),_=new Float32Array(H),y=0,D=0;while(y<_.length){let $=Math.round((y+1)*A),G=0,L=0;for(let W=D;W<$&&W<V.length;W+=1)G+=V[W]??0,L+=1;_[y]=L>0?G/L:0,y+=1,D=$}return _},v=(V)=>{let g=null,C=null,A=null,H=null,_=0,y=null,D=(L)=>{L.processorNode?.disconnect(),L.sourceNode?.disconnect(),L.mediaStream?.getTracks().forEach((W)=>W.stop()),L.audioContext?.close()};return{start:()=>{if(H)return Promise.resolve();if(y)return y;let L=++_,U=(async()=>{if(!V.stream&&(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia))throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let R=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!R)throw Error("Browser microphone capture requires AudioContext support.");let O=null,Y=null,J=null,Q=null;try{if(Y=V.stream??await navigator.mediaDevices.getUserMedia({audio:{autoGainControl:!0,channelCount:V.channelCount??1,echoCancellation:!0,noiseSuppression:!0}}),L!==_)throw Error("Microphone capture stopped during startup.");if(O=new R,O.state==="suspended")await O.resume();if(L!==_)throw Error("Microphone capture stopped during startup.");if(Q=O.createMediaStreamSource(Y),J=O.createScriptProcessor(4096,1,1),J.onaudioprocess=(X)=>{let c=X.inputBuffer.getChannelData(0),q=z0(c,O?.sampleRate??48000,V.sampleRateHz??16000),T=q0(q);V.onLevel?.(N0(T)),V.onAudio(T)},Q.connect(J),J.connect(O.destination),L!==_)throw Error("Microphone capture stopped during startup.");g=O,H=Y,A=J,C=Q}catch(X){throw D({audioContext:O,mediaStream:Y,processorNode:J,sourceNode:Q}),X}})().finally(()=>{if(y===U)y=null});return y=U,U},stop:()=>{_+=1,D({audioContext:g,mediaStream:H,processorNode:A,sourceNode:C}),V.onLevel?.(0),g=null,H=null,A=null,C=null}}};var F=(V)=>{if(typeof V==="string"&&V.trim())return V;if(V instanceof Error&&V.message.trim())return V.message;if(V&&typeof V==="object"){let g=V;for(let C of["message","reason","description"]){let A=g[C];if(typeof A==="string"&&A.trim())return A}if("error"in g)return F(g.error);if("cause"in g)return F(g.cause);try{return JSON.stringify(V)}catch{}}return"Unexpected error"},i=(V)=>{switch(V.type){case"audio":return{chunk:Uint8Array.from(atob(V.chunkBase64),(g)=>g.charCodeAt(0)),format:V.format,receivedAt:V.receivedAt,turnId:V.turnId,type:"audio"};case"assistant":return{text:V.text,turnId:V.turnId,type:"assistant"};case"assistant_delta":return{delta:V.delta,turnId:V.turnId,type:"assistant_delta"};case"complete":return{sessionId:V.sessionId,type:"complete"};case"connection":return{reconnect:V.reconnect,type:"connection"};case"call_lifecycle":return{event:V.event,sessionId:V.sessionId,type:"call_lifecycle"};case"error":return{message:F(V.message),recoverable:V.recoverable,type:"error"};case"call_control_ack":return null;case"final":return{transcript:V.transcript,type:"final"};case"partial":return{transcript:V.transcript,type:"partial"};case"playback_rate":return{rate:V.rate,type:"playback_rate"};case"replay":return{assistantTexts:V.assistantTexts,call:V.call,partial:V.partial,scenarioId:V.scenarioId,sessionId:V.sessionId,sessionMetadata:V.sessionMetadata,status:V.status,turns:V.turns,type:"replay"};case"session":return{paused:V.paused,pauseExpiresAt:V.pauseExpiresAt,sessionId:V.sessionId,sessionMetadata:V.sessionMetadata,scenarioId:V.scenarioId,status:V.status,type:"session"};case"turn":return{turn:V.turn,type:"turn"};default:return null}};var UV=Math.PI*2;var N=(V,g,C,A)=>{V.push({code:C,message:A,severity:g})};var b0=(V)=>V.length===0?void 0:V.reduce((g,C)=>g+C,0)/V.length,x=(V)=>V.length===0?void 0:Math.max(...V);var E=(V,g)=>{let C=V[g];return typeof C==="number"&&Number.isFinite(C)?C:void 0},j=(V,g)=>{let C=V[g];return typeof C==="boolean"?C:void 0},K=(V,g)=>{let C=V[g];return typeof C==="string"?C:void 0},f=(V)=>String(V.id??K(V,"ssrc")??E(V,"ssrc")??K(V,"trackIdentifier")??K(V,"mid")??"unknown"),o=(V)=>V===void 0?void 0:V*1000;var P0=(V)=>{let g={};for(let[C,A]of Object.entries(V))if(A===null||typeof A==="boolean"||typeof A==="number"||typeof A==="string")g[C]=A;return g};var n=(V={})=>{let g=V.stats??[],C=[],A=g.filter((T)=>T.type==="inbound-rtp"&&K(T,"kind")!=="video"),H=g.filter((T)=>T.type==="outbound-rtp"&&K(T,"kind")!=="video"),_=g.filter((T)=>T.type==="candidate-pair"),y=g.filter((T)=>(T.type==="track"||T.type==="media-source")&&K(T,"kind")==="audio"),D=_.filter((T)=>j(T,"selected")===!0||j(T,"nominated")===!0||K(T,"state")==="succeeded").length,$=y.filter((T)=>K(T,"readyState")!=="ended"&&K(T,"trackState")!=="ended"&&j(T,"ended")!==!0).length,G=y.filter((T)=>K(T,"readyState")==="ended"||K(T,"trackState")==="ended"||j(T,"ended")===!0).length,L=A.reduce((T,w)=>T+(E(w,"packetsReceived")??0),0),W=H.reduce((T,w)=>T+(E(w,"packetsSent")??0),0),U=[...A,...H].reduce((T,w)=>T+Math.max(0,E(w,"packetsLost")??0),0),R=L+U,O=R===0?0:U/R,Y=A.reduce((T,w)=>T+(E(w,"bytesReceived")??0),0),J=H.reduce((T,w)=>T+(E(w,"bytesSent")??0),0),Q=x(_.map((T)=>o(E(T,"currentRoundTripTime")??E(T,"roundTripTime"))).filter((T)=>T!==void 0)),X=x([...A,...H].map((T)=>o(E(T,"jitter"))).filter((T)=>T!==void 0)),c=x(A.map((T)=>{let w=E(T,"jitterBufferDelay"),M=E(T,"jitterBufferEmittedCount");return w!==void 0&&M!==void 0&&M>0?w/M*1000:void 0}).filter((T)=>T!==void 0)),q=y.map((T)=>E(T,"audioLevel")).filter((T)=>T!==void 0);if(V.requireConnectedCandidatePair&&_.length>0&&D===0)N(C,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(V.requireLiveAudioTrack&&$===0)N(C,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(V.maxPacketLossRatio!==void 0&&O>V.maxPacketLossRatio)N(C,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(O)} above ${String(V.maxPacketLossRatio)}.`);if(V.maxRoundTripTimeMs!==void 0&&Q!==void 0&&Q>V.maxRoundTripTimeMs)N(C,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(Q)}ms above ${String(V.maxRoundTripTimeMs)}ms.`);if(V.maxJitterMs!==void 0&&X!==void 0&&X>V.maxJitterMs)N(C,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(X)}ms above ${String(V.maxJitterMs)}ms.`);return{activeCandidatePairs:D,audioLevelAverage:b0(q),bytesReceived:Y,bytesSent:J,checkedAt:Date.now(),endedAudioTracks:G,inboundPackets:L,issues:C,jitterBufferDelayMs:c,jitterMs:X,liveAudioTracks:$,outboundPackets:W,packetLossRatio:O,packetsLost:U,roundTripTimeMs:Q,status:C.some((T)=>T.severity==="error")?"fail":C.length>0?"warn":"pass",totalStats:g.length}},m=async(V)=>{return[...(await V.peerConnection.getStats(V.selector??null)).values()].map(P0)};var u=(V={})=>{let g=V.stats??[],C=V.previousStats??[],A=[],H=new Map(C.map((U)=>[f(U),U])),y=g.filter((U)=>(U.type==="inbound-rtp"||U.type==="outbound-rtp")&&K(U,"kind")!=="video"&&K(U,"mediaType")!=="video").map((U)=>{let R=U.type==="outbound-rtp"?"outbound":"inbound",O=R==="outbound"?"packetsSent":"packetsReceived",Y=R==="outbound"?"bytesSent":"bytesReceived",J=H.get(f(U)),Q=E(U,O),X=J?E(J,O):void 0,c=E(U,Y),q=J?E(J,Y):void 0,T=U.timestamp!==void 0&&J?.timestamp!==void 0?U.timestamp-J.timestamp:void 0;return{bytesDelta:c!==void 0&&q!==void 0?c-q:void 0,currentPackets:Q,direction:R,id:f(U),packetDelta:Q!==void 0&&X!==void 0?Q-X:void 0,previousPackets:X,timeDeltaMs:T}}),D=y.filter((U)=>U.direction==="inbound"),$=y.filter((U)=>U.direction==="outbound"),G=x(y.map((U)=>U.timeDeltaMs).filter((U)=>U!==void 0)),L=D.filter((U)=>V.maxInboundPacketStallMs!==void 0&&U.timeDeltaMs!==void 0&&U.timeDeltaMs>=V.maxInboundPacketStallMs&&U.packetDelta!==void 0&&U.packetDelta<=0).length,W=$.filter((U)=>V.maxOutboundPacketStallMs!==void 0&&U.timeDeltaMs!==void 0&&U.timeDeltaMs>=V.maxOutboundPacketStallMs&&U.packetDelta!==void 0&&U.packetDelta<=0).length;if(V.requireInboundAudio&&D.length===0)N(A,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(V.requireOutboundAudio&&$.length===0)N(A,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(V.maxGapMs!==void 0&&G!==void 0&&G>V.maxGapMs)N(A,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(G)}ms above ${String(V.maxGapMs)}ms.`);if(L>0)N(A,"error","media.webrtc_inbound_stalled",`${String(L)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(W>0)N(A,"error","media.webrtc_outbound_stalled",`${String(W)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:D.length,issues:A,maxObservedGapMs:G,outboundAudioStreams:$.length,stalledInboundStreams:L,stalledOutboundStreams:W,status:A.some((U)=>U.severity==="error")?"fail":A.length>0?"warn":"pass",streams:y,totalStats:g.length}};var B0="/api/voice/browser-media",M0=5000,j0=async(V)=>V.peerConnection??await V.getPeerConnection?.()??null,x0=async(V,g)=>{let C=g.fetch??globalThis.fetch;if(!C)return;await C(g.path??B0,{body:JSON.stringify(V),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},r=(V)=>{let g=null,C=[],A=async()=>{let y=await j0(V);if(!y)return;let D=await m({peerConnection:y}),$=n({...V,stats:D}),G=V.continuity===!1?void 0:u({...V.continuity,previousStats:C,stats:D}),L={at:Date.now(),continuity:G,report:$,scenarioId:V.getScenarioId?.()??null,sessionId:V.getSessionId?.()??null};return C=D,V.onReport?.(L),await x0(L,V),L},H=()=>{A().catch((y)=>{V.onError?.(y)})},_=()=>{if(g)clearInterval(g),g=null};return{close:_,reportOnce:A,stop:_,start:()=>{if(g)return;H(),g=setInterval(H,V.intervalMs??M0)}}};var k0=(V,g,C)=>Math.min(C,g*2**(Math.max(1,V)-1));class s extends Error{constructor(V="Voice authorization could not be renewed."){super(V);this.name="VoiceReconnectRejectedError"}}var b=()=>{},F0=()=>Promise.resolve(),f0=()=>b,l0={callControl:F0,close:b,disconnect:b,endTurn:b,send:b,sendAudio:b,simulateDisconnect:b,subscribe:f0,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",start:()=>{}},d0=()=>crypto.randomUUID(),v0=(V,g,C,A)=>{let{hostname:H,port:_,protocol:y}=window.location,D=y==="https:"?"wss:":"ws:",$=_?`:${_}`:"",G=new URL(`${D}//${H}${$}${V}`);if(G.searchParams.set("sessionId",g),C)G.searchParams.set("scenarioId",C);for(let[L,W]of Object.entries(A))if(L!=="sessionId"&&L!=="scenarioId"&&W)G.searchParams.set(L,W);return G.toString()},i0=(V)=>{if(!V||typeof V!=="object"||!("type"in V))return!1;switch(V.type){case"audio":case"assistant":case"call_lifecycle":case"call_control_ack":case"complete":case"connection":case"error":case"final":case"partial":case"playback_rate":case"pong":case"replay":case"session":case"turn":return!0;default:return!1}},o0=(V)=>{if(typeof V.data!=="string")return null;try{let g=JSON.parse(V.data);return i0(g)?g:null}catch{return null}},a=(V,g={})=>{if(typeof window>"u")return l0;let C=new Set,A=new Map,H=g.reconnect!==!1,_=g.maxReconnectAttempts??15,y=g.reconnectMaxDelayMs??8000,D=g.pingInterval??30000,$=g.reconnectResetAfterMs??30000,G=0,L=!1,W=null,U=(I)=>k0(I,500,y),R={isConnected:!1,pendingMessages:[],scenarioId:g.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectResetTimeout:null,reconnectTimeout:null,sessionId:g.sessionId??d0(),ws:null},O=(I)=>{C.forEach((S)=>S(I))},Y=(I)=>{C.forEach((S)=>S({message:I,recoverable:!1,type:"error"})),O({reconnect:{attempts:R.reconnectAttempts,maxAttempts:_,status:"exhausted"},type:"connection"})},J=()=>{if(R.pingInterval)clearInterval(R.pingInterval),R.pingInterval=null;if(R.reconnectTimeout)clearTimeout(R.reconnectTimeout),R.reconnectTimeout=null;if(R.reconnectResetTimeout)clearTimeout(R.reconnectResetTimeout),R.reconnectResetTimeout=null},Q=()=>{if(R.ws?.readyState!==1)return;while(R.pendingMessages.length>0){let I=R.pendingMessages.shift();if(I!==void 0)R.ws.send(I)}},X=(I)=>{for(let S of A.values())clearTimeout(S.timeout),S.reject(Error(I));A.clear()},c=()=>{if(L)return;let I=G;R.reconnectAttempts+=1;let S=U(R.reconnectAttempts),Z=Date.now()+S;O({reconnect:{attempts:R.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:_,nextAttemptAt:Z,status:"reconnecting"},type:"connection"}),R.reconnectTimeout=setTimeout(async()=>{if(L||G!==I)return;if(R.reconnectAttempts>_){Y("Voice connection could not be restored.");return}try{if(g.prepareReconnect){let h=new AbortController;W=h;let P=setTimeout(()=>h.abort("Voice reconnect preparation timed out"),g.prepareReconnectTimeoutMs??1e4);try{await Promise.race([g.prepareReconnect({attempt:R.reconnectAttempts,path:V,scenarioId:R.scenarioId,sessionId:R.sessionId,signal:h.signal}),new Promise((B,G0)=>{h.signal.addEventListener("abort",()=>G0(Error("Voice reconnect preparation timed out.")),{once:!0})})])}finally{if(clearTimeout(P),W===h)W=null}}}catch(h){if(L||G!==I)return;if(h instanceof s){Y(h.message);return}if(R.reconnectAttempts<_)c();else Y("Voice authorization could not be renewed for reconnect.");return}if(L||G!==I)return;q()},S)},q=()=>{if(L)return;let I=new WebSocket(v0(V,R.sessionId,R.scenarioId,g.query??{}));I.binaryType="arraybuffer",I.onopen=()=>{if(R.ws!==I){I.close(1000);return}let S=R.reconnectAttempts>0;if(R.isConnected=!0,Q(),S)O({reconnect:{attempts:R.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:_,status:"resumed"},type:"connection"}),R.reconnectResetTimeout=setTimeout(()=>{if(R.ws===I&&I.readyState===1)R.reconnectAttempts=0;R.reconnectResetTimeout=null},$);R.pingInterval=setInterval(()=>{if(I.readyState===1)I.send(JSON.stringify({type:"ping"}))},D)},I.onmessage=(S)=>{if(R.ws!==I)return;let Z=o0(S);if(!Z)return;if(Z.type==="session")R.sessionId=Z.sessionId,R.scenarioId=Z.scenarioId??R.scenarioId;if(Z.type==="call_control_ack"){let h=A.get(Z.requestId);if(h)if(clearTimeout(h.timeout),A.delete(Z.requestId),Z.ok)h.resolve();else h.reject(Error(Z.message??`${Z.action} was not applied`))}C.forEach((h)=>h(Z))},I.onclose=(S)=>{if(R.ws!==I)return;R.ws=null,R.isConnected=!1,J(),X("Voice connection interrupted");let Z=H&&S.code!==1000&&S.code!==4401&&S.code!==4403&&S.code!==4500&&R.reconnectAttempts<_,h=S.code===4401||S.code===4403,P=S.code===4500;if(h)C.forEach((B)=>B({message:S.code===4401?"Voice authorization expired or was rejected.":"Voice authorization was forbidden.",recoverable:!1,type:"error"}));if(P)C.forEach((B)=>B({message:"Voice provider initialization failed.",recoverable:!1,type:"error"}));if(Z)c();else if(H&&S.code!==1000){if(h)O({reconnect:{attempts:R.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:_,status:"exhausted"},type:"connection"});else if(!P)Y("Voice connection could not be restored.")}},R.ws=I},T=(I)=>{if(L)return;if(R.ws?.readyState===1){R.ws.send(I);return}if(typeof I!=="string")return;if(R.pendingMessages.length>=100)R.pendingMessages.shift();R.pendingMessages.push(I)},w=(I)=>{T(JSON.stringify(I))},M=(I={})=>{if(I.sessionId&&I.sessionId!==R.sessionId)throw Error("Voice session switching requires a new connection instance.");if(I.sessionId)R.sessionId=I.sessionId;if(I.scenarioId)R.scenarioId=I.scenarioId;w({scenarioId:R.scenarioId??void 0,sessionId:R.sessionId,type:"start"})},I0=(I)=>{T(I)},U0=()=>{w({type:"end_turn"})},L0=(I)=>{if(R.ws?.readyState!==1)return Promise.reject(Error(`${I.action} is unavailable while disconnected`));let S=crypto.randomUUID(),{promise:Z,reject:h,resolve:P}=Promise.withResolvers(),B=setTimeout(()=>{A.delete(S),h(Error(`${I.action} acknowledgement timed out`))},5000);return A.set(S,{reject:h,resolve:P,timeout:B}),R.ws.send(JSON.stringify({...I,requestId:S,type:"call_control"})),Z},W0=(I="client-close")=>{if(L=!0,W?.abort(),W=null,G+=1,J(),X("Voice connection closed"),R.pendingMessages.length=0,R.ws){let S=R.ws;if(R.ws=null,S.readyState===1)S.send(JSON.stringify({reason:I,type:"close"}));S.close(1000)}R.isConnected=!1,C.clear()},y0=()=>{if(L=!0,W?.abort(),W=null,G+=1,J(),X("Voice connection disconnected"),R.pendingMessages.length=0,R.ws){let I=R.ws;R.ws=null,I.close(1000)}R.isConnected=!1,C.clear()},D0=()=>{if(R.ws?.readyState===1)R.ws.close(4000,"absolutejs-voice-reconnect-proof")},X0=(I)=>{return C.add(I),()=>{C.delete(I)}};return q(),{callControl:L0,close:W0,disconnect:y0,endTurn:U0,send:w,sendAudio:I0,simulateDisconnect:D0,start:M,subscribe:X0,getReadyState:()=>R.ws?.readyState??3,getScenarioId:()=>R.scenarioId??"",getSessionId:()=>R.sessionId}};var n0=()=>({attempts:0,maxAttempts:0,status:"idle"}),m0=(V,g)=>{let C=g.trim().replace(/\s+/g," ");if(!C)return V;if(!V)return C;if(V===C||V.endsWith(C))return V;if(C.includes(V))return C;return`${V} ${C}`},u0=(V,g)=>{let C=g.trim().replace(/\s+/g," ");if(!V)return C;if(!C||V.endsWith(C))return V;return`${V} ${C}`},r0=()=>({assistantAudio:[],assistantStreamingText:"",assistantTexts:[],call:null,error:null,isConnected:!1,paused:!1,partial:"",playbackRate:null,reconnect:n0(),scenarioId:null,sessionId:null,sessionMetadata:null,status:"idle",turns:[]}),p=()=>{let V=r0(),g="",C=new Set,A=()=>{C.forEach((_)=>_())};return{dispatch:(_)=>{switch(_.type){case"audio":V={...V,assistantAudio:[...V.assistantAudio,{chunk:_.chunk,format:_.format,receivedAt:_.receivedAt,turnId:_.turnId}]};break;case"assistant":V={...V,assistantStreamingText:"",assistantTexts:[...V.assistantTexts,_.text]};break;case"assistant_delta":V={...V,assistantStreamingText:`${V.assistantStreamingText}${_.delta}`};break;case"complete":V={...V,sessionId:_.sessionId,status:"completed"};break;case"call_lifecycle":V={...V,call:{...V.call,disposition:_.event.type==="end"?_.event.disposition:V.call?.disposition,endedAt:_.event.type==="end"?_.event.at:V.call?.endedAt,events:[...V.call?.events??[],_.event],lastEventAt:_.event.at,startedAt:V.call?.startedAt??_.event.at},sessionId:_.sessionId};break;case"connected":V={...V,isConnected:!0,reconnect:V.reconnect.status==="reconnecting"?{...V.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:V.reconnect};break;case"connection":V={...V,reconnect:_.reconnect};break;case"disconnected":V={...V,isConnected:!1};break;case"error":V={...V,error:_.message,..._.recoverable===!1?{isConnected:!1,status:"failed"}:{}};break;case"final":g=m0(g,_.transcript.text),V={...V,partial:g};break;case"partial":V={...V,partial:u0(g,_.transcript.text)};break;case"playback_rate":V={...V,playbackRate:Math.max(0.5,Math.min(2,_.rate))};break;case"replay":g=_.partial,V={...V,assistantStreamingText:"",assistantTexts:[..._.assistantTexts],call:_.call??null,error:null,isConnected:_.status==="active",partial:_.partial,reconnect:V.reconnect.status==="reconnecting"?{...V.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:V.reconnect,scenarioId:_.scenarioId??V.scenarioId,sessionId:_.sessionId,sessionMetadata:_.sessionMetadata??V.sessionMetadata,status:_.status,turns:[..._.turns]};break;case"session":V={...V,error:null,paused:_.paused??!1,pauseExpiresAt:_.pauseExpiresAt,scenarioId:_.scenarioId??V.scenarioId,isConnected:_.status==="active",sessionId:_.sessionId,sessionMetadata:_.sessionMetadata??V.sessionMetadata,status:_.status};break;case"turn":g="",V={...V,partial:"",turns:[...V.turns,_.turn]};break}A()},getServerSnapshot:()=>V,getSnapshot:()=>V,subscribe:(_)=>{return C.add(_),()=>{C.delete(_)}}}};var t=(V,g={})=>{let C=a(V,g),A=p(),H=g.browserMedia&&typeof window<"u"?r({...g.browserMedia,getScenarioId:()=>g.browserMedia?g.browserMedia.getScenarioId?.()??C.getScenarioId():C.getScenarioId(),getSessionId:()=>g.browserMedia?g.browserMedia.getSessionId?.()??C.getSessionId():C.getSessionId()}):null,_=new Set,y=!1,D=(W)=>Promise.resolve().then(()=>{if(y)return;if(!W?.sessionId&&!W?.scenarioId)return;C.start(W),H?.start()}),$=()=>{_.forEach((W)=>W())},G=()=>{if(!g.reconnectReportPath||typeof fetch>"u")return;let W=A.getSnapshot(),U=JSON.stringify({at:Date.now(),reconnect:W.reconnect,scenarioId:W.scenarioId,sessionId:C.getSessionId(),turnIds:W.turns.map((R)=>R.id)});fetch(g.reconnectReportPath,{body:U,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},L=C.subscribe((W)=>{let U=i(W);if(U){if(A.dispatch(U),W.type==="connection")G();$()}});return{start:D,get assistantAudio(){return A.getSnapshot().assistantAudio},get assistantTexts(){return A.getSnapshot().assistantTexts},get assistantStreamingText(){return A.getSnapshot().assistantStreamingText},get call(){return A.getSnapshot().call},callControl(W){return C.callControl(W)},close(W){y=!0,L(),H?.close(),C.close(W),A.dispatch({type:"disconnected"}),$()},disconnect(){y=!0,L(),H?.close(),C.disconnect(),A.dispatch({type:"disconnected"}),$()},endTurn(){C.endTurn()},get error(){return A.getSnapshot().error},getServerSnapshot(){return A.getServerSnapshot()},getSnapshot(){return A.getSnapshot()},get isConnected(){return A.getSnapshot().isConnected},get partial(){return A.getSnapshot().partial},get playbackRate(){return A.getSnapshot().playbackRate},get paused(){return A.getSnapshot().paused},get pauseExpiresAt(){return A.getSnapshot().pauseExpiresAt},get reconnect(){return A.getSnapshot().reconnect},get scenarioId(){return A.getSnapshot().scenarioId},sendAudio(W){C.sendAudio(W)},get sessionId(){return C.getSessionId()},get sessionMetadata(){return A.getSnapshot().sessionMetadata},simulateDisconnect(){C.simulateDisconnect()},get status(){return A.getSnapshot().status},subscribe(W){return _.add(W),()=>{_.delete(W)}},get turns(){return A.getSnapshot().turns}}};var e=(V)=>{if(!V||V.enabled===!1)return;return{enabled:!0,maxGain:V.maxGain??3,noiseGateAttenuation:V.noiseGateAttenuation??0.15,noiseGateThreshold:V.noiseGateThreshold??0.006,targetLevel:V.targetLevel??0.08}};var s0={balanced:{qualityProfile:"general",minSilenceMs:400,silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:"general",minSilenceMs:300,silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},"long-form":{qualityProfile:"general",minSilenceMs:600,silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},a0={"accent-heavy":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},general:{},"noisy-room":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},"short-command":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var V0=(V)=>{let g=V?.profile??"fast",C=V?.qualityProfile??"general",A=s0[g],H=a0[C],_=V?.silenceMs??H.silenceMs??A.silenceMs;return{profile:g,qualityProfile:C,minSilenceMs:Math.min(_,V?.minSilenceMs??H.minSilenceMs??A.minSilenceMs),silenceMs:_,speechThreshold:V?.speechThreshold??H.speechThreshold??A.speechThreshold,transcriptStabilityMs:V?.transcriptStabilityMs??H.transcriptStabilityMs??A.transcriptStabilityMs}};var p0={chat:{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"balanced",qualityProfile:"short-command"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"fast",qualityProfile:"general"}},dictation:{audioConditioning:{enabled:!0,maxGain:2.25,noiseGateAttenuation:0.05,noiseGateThreshold:0.003,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"guided-intake":{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"turn-scoped",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"noisy-room":{audioConditioning:{enabled:!0,maxGain:3,noiseGateAttenuation:0.12,noiseGateThreshold:0.006,targetLevel:0.085},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:2100,speechThreshold:0.02,transcriptStabilityMs:1650}},"pstn-balanced":{audioConditioning:{enabled:!0,maxGain:2.8,noiseGateAttenuation:0.07,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:660,speechThreshold:0.012,transcriptStabilityMs:300}},"pstn-fast":{audioConditioning:{enabled:!0,maxGain:2.75,noiseGateAttenuation:0.06,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:620,speechThreshold:0.012,transcriptStabilityMs:280}},reliability:{audioConditioning:{enabled:!0,maxGain:2.9,noiseGateAttenuation:0.08,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room"}}},g0=(V="default")=>{let g=p0[V];return{audioConditioning:e(g.audioConditioning),capture:{channelCount:g.capture?.channelCount??1,sampleRateHz:g.capture?.sampleRateHz??16000},connection:{...g.connection},name:V,sttLifecycle:g.sttLifecycle??"continuous",turnDetection:V0(g.turnDetection)}};var t0=(V)=>({assistantAudio:[...V.assistantAudio],assistantStreamingText:V.assistantStreamingText,assistantTexts:[...V.assistantTexts],call:V.call,error:V.error,isConnected:V.isConnected,isRecording:!1,paused:V.paused,pauseExpiresAt:V.pauseExpiresAt,partial:V.partial,playbackRate:V.playbackRate,reconnect:V.reconnect,recordingError:null,sessionId:V.sessionId,sessionMetadata:V.sessionMetadata,scenarioId:V.scenarioId,status:V.status,turns:[...V.turns]}),A0=(V,g={})=>{let C=g0(g.preset),A=t(V,{...C.connection,...g.connection}),H=null,_=0,y=!1,D=null,$=t0(A),G=new Set,L=()=>{for(let X of G)X()},W=()=>{if($={...$,assistantAudio:[...A.assistantAudio],assistantStreamingText:A.assistantStreamingText,assistantTexts:[...A.assistantTexts],call:A.call,error:A.error,isConnected:A.isConnected,paused:A.paused,pauseExpiresAt:A.pauseExpiresAt,partial:A.partial,playbackRate:A.playbackRate,reconnect:A.reconnect,sessionId:A.sessionId,sessionMetadata:A.sessionMetadata,scenarioId:A.scenarioId,status:A.status,turns:[...A.turns]},g.autoStopOnComplete!==!1&&$.status==="completed"&&$.isRecording)H?.stop(),H=null,$={...$,isRecording:!1};L()},U=A.subscribe(W);W();let R=()=>{if(H)return H;return H=v({channelCount:g.capture?.channelCount??C.capture.channelCount,onLevel:g.capture?.onLevel,onAudio:(X)=>{if(g.capture?.onAudio){g.capture.onAudio(X,A.sendAudio);return}A.sendAudio(X)},sampleRateHz:g.capture?.sampleRateHz??C.capture.sampleRateHz,...g.capture?.stream?{stream:g.capture.stream}:{}}),H},O=()=>{_+=1,H?.stop(),H=null,$={...$,isRecording:!1},L()},Y=async()=>{if(y)throw Error("Voice controller is closed.");if($.isRecording)return;if(D)return D;let X=++_,c=R(),T=(async()=>{try{if($={...$,recordingError:null},L(),await c.start(),y||X!==_||H!==c){c.stop();return}$={...$,isRecording:!0},L()}catch(w){if(c.stop(),X!==_||H!==c)throw w;throw H=null,$={...$,isRecording:!1,recordingError:w instanceof Error?w.message:String(w)},L(),w}})().finally(()=>{if(D===T)D=null});return D=T,D};return{close:(X)=>{y=!0,U(),O(),A.close(X)},disconnect:()=>{y=!0,U(),O(),A.disconnect()},startRecording:Y,stopRecording:O,get assistantAudio(){return $.assistantAudio},get assistantTexts(){return $.assistantTexts},get assistantStreamingText(){return $.assistantStreamingText},bindHTMX(X){return d(A,X)},get call(){return $.call},callControl:(X)=>A.callControl(X),endTurn:()=>A.endTurn(),get error(){return $.error},getServerSnapshot:()=>$,getSnapshot:()=>$,get isConnected(){return $.isConnected},get isRecording(){return $.isRecording},get partial(){return $.partial},get playbackRate(){return $.playbackRate},get paused(){return $.paused},get pauseExpiresAt(){return $.pauseExpiresAt},get reconnect(){return $.reconnect},get recordingError(){return $.recordingError},get scenarioId(){return $.scenarioId},sendAudio:(X)=>A.sendAudio(X),get sessionId(){return $.sessionId},get sessionMetadata(){return $.sessionMetadata},simulateDisconnect:()=>A.simulateDisconnect(),get status(){return $.status},subscribe:(X)=>{return G.add(X),()=>{G.delete(X)}},toggleRecording:async()=>{if($.isRecording){O();return}await Y()},get turns(){return $.turns}}};var z=(V)=>String(V).replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;");var C0=(V)=>{if(!V.isConnected)return"idle";if(V.isPlaying)return"speaking";if(V.isRecording&&V.hasActivePartial)return"listening";if(V.isRecording)return"listening";if(V.lastTranscriptAt&&!V.lastAssistantAt)return"thinking";if(V.lastTranscriptAt&&V.lastAssistantAt&&V.lastTranscriptAt>V.lastAssistantAt)return"thinking";return"idle"};var e0={accent:"#3b82f6",background:"#0f172a",errorAccent:"#ef4444",fontFamily:'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',foreground:"#f8fafc",radius:16},VV={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},gV=(V,g)=>{switch(V){case"listening":return g.listening;case"speaking":return g.speaking;case"thinking":return g.thinking;case"idle":return g.idle}},_0=(V)=>{let g={...e0,...V.theme},C={...VV,...V.labels},A=V.state.assistantAudio.at(-1)?.receivedAt,H=V.state.turns.at(-1)?.committedAt,_=C0({hasActivePartial:V.state.partial.length>0,isConnected:V.state.isConnected,isPlaying:!1,isRecording:V.state.isRecording,lastAssistantAt:A,lastTranscriptAt:H}),y=!V.state.isConnected&&V.state.status!=="idle"&&!V.state.error,D=V.state.error?"Error":y?C.connecting:V.state.status==="completed"?C.callEnded:gV(_,C);return{agentState:_,classes:{container:`absolute-voice-widget absolute-voice-widget--${_}`,dot:`absolute-voice-widget__dot${V.state.error?" absolute-voice-widget__dot--error":""}`},controls:{canEnd:V.state.isConnected,canMute:V.state.isRecording,canStart:!V.state.isRecording&&V.state.status!=="completed"},errorMessage:V.state.error??void 0,labels:C,partial:V.state.partial||void 0,statusLabel:D,theme:g,title:V.title??"Voice"}},AV=(V)=>typeof V==="number"?`${V}px`:V,R0=(V)=>{let g=V.theme,C=`background:${g.background};border-radius:${AV(g.radius)};color:${g.foreground};font-family:${g.fontFamily};min-width:240px;padding:20px 22px;`,A=`background:${V.errorMessage?g.errorAccent:V.agentState==="idle"?"rgba(148,163,184,0.6)":g.accent};border-radius:50%;height:10px;width:10px;`,H=[];if(V.controls.canStart)H.push(`<button type="button" data-action="start" style="background:${g.accent};border:none;border-radius:12px;color:${g.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${z(V.labels.startCall)}</button>`);if(V.controls.canMute)H.push(`<button type="button" data-action="mute" style="background:transparent;border:1px solid rgba(255,255,255,0.18);border-radius:12px;color:${g.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${z(V.labels.mute)}</button>`);if(V.controls.canEnd)H.push(`<button type="button" data-action="end" style="background:${g.errorAccent};border:none;border-radius:12px;color:${g.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${z(V.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${V.agentState}" class="${z(V.classes.container)}" style="${C}">
2
2
  <div style="align-items:center;display:flex;gap:10px;margin-bottom:12px;">
3
- <span aria-hidden="true" class="${z(V.classes.dot)}" style="${g}"></span>
3
+ <span aria-hidden="true" class="${z(V.classes.dot)}" style="${A}"></span>
4
4
  <strong style="font-size:15px;">${z(V.title)}</strong>
5
5
  <span style="font-size:13px;margin-left:auto;opacity:0.7;">${z(V.statusLabel)}</span>
6
6
  </div>
7
7
  ${V.partial?`<p style="font-size:13px;margin:8px 0 12px;opacity:0.85;word-break:break-word;">“${z(V.partial)}”</p>`:""}
8
8
  <div style="display:flex;gap:10px;">${H.join("")}</div>
9
- ${V.errorMessage?`<p style="color:${A.errorAccent};font-size:12px;margin-top:12px;">${z(V.errorMessage)}</p>`:""}
10
- </div>`};var CV=(V)=>{if(typeof V!=="string")return V;let A=document.querySelector(V);if(!A)throw Error(`AbsoluteVoice.mount: no element matches "${V}"`);return A},$0=(V,A={})=>{let C=CV(V),g=g0(A.path??"/voice",A.controllerOptions),H=null,_=null,D=()=>{let R=_0({...A.labels!==void 0?{labels:A.labels}:{},state:{assistantAudio:g.assistantAudio,error:g.error,isConnected:g.isConnected,isRecording:g.isRecording,partial:g.partial,status:g.status,turns:g.turns},...A.theme!==void 0?{theme:A.theme}:{},...A.title!==void 0?{title:A.title}:{}});C.innerHTML=T0(R);for(let O of C.querySelectorAll("button[data-action]")){let{action:L}=O.dataset;O.addEventListener("click",()=>{if(L==="start")g.startRecording();else if(L==="mute")g.stopRecording();else if(L==="end")g.close()})}if(g.error&&g.error!==H)H=g.error,A.onError?.(g.error);if(g.status!==_)_=g.status,A.onStatusChange?.(g.status)},X=g.subscribe(D);if(D(),A.autoStart)g.startRecording();return{controller:g,async end(){await g.close()},mute(){g.stopRecording()},async start(){await g.startRecording()},unmount(){X(),g.close(),C.innerHTML=""}}},R0="0.0.22-beta.516",H0={mount:$0,version:R0};if(typeof globalThis<"u")globalThis.AbsoluteVoice=H0;var TV=H0;})();
9
+ ${V.errorMessage?`<p style="color:${g.errorAccent};font-size:12px;margin-top:12px;">${z(V.errorMessage)}</p>`:""}
10
+ </div>`};var CV=(V)=>{if(typeof V!=="string")return V;let g=document.querySelector(V);if(!g)throw Error(`AbsoluteVoice.mount: no element matches "${V}"`);return g},T0=(V,g={})=>{let C=CV(V),A=A0(g.path??"/voice",g.controllerOptions),H=null,_=null,y=()=>{let $=_0({...g.labels!==void 0?{labels:g.labels}:{},state:{assistantAudio:A.assistantAudio,error:A.error,isConnected:A.isConnected,isRecording:A.isRecording,partial:A.partial,status:A.status,turns:A.turns},...g.theme!==void 0?{theme:g.theme}:{},...g.title!==void 0?{title:g.title}:{}});C.innerHTML=R0($);for(let G of C.querySelectorAll("button[data-action]")){let{action:L}=G.dataset;G.addEventListener("click",()=>{if(L==="start")A.startRecording();else if(L==="mute")A.stopRecording();else if(L==="end")A.close()})}if(A.error&&A.error!==H)H=A.error,g.onError?.(A.error);if(A.status!==_)_=A.status,g.onStatusChange?.(A.status)},D=A.subscribe(y);if(y(),g.autoStart)A.startRecording();return{controller:A,async end(){await A.close()},mute(){A.stopRecording()},async start(){await A.startRecording()},unmount(){D(),A.close(),C.innerHTML=""}}},$0="0.0.22-beta.516",H0={mount:T0,version:$0};if(typeof globalThis<"u")globalThis.AbsoluteVoice=H0;var RV=H0;})();
package/dist/index.js CHANGED
@@ -4298,6 +4298,11 @@ var createVoiceSession = (options) => {
4298
4298
  let lastSpeechEnergyAt = 0;
4299
4299
  let sttHealthPhaseStart = 0;
4300
4300
  let sttDeafConfirmedAt = 0;
4301
+ const resetSttHealthWindow = () => {
4302
+ lastSpeechEnergyAt = 0;
4303
+ sttHealthPhaseStart = 0;
4304
+ sttDeafConfirmedAt = 0;
4305
+ };
4301
4306
  let lastSttRecoverySpokenAt = 0;
4302
4307
  let activeTTSTurnId;
4303
4308
  let assistantSpeechEndsAt = 0;
@@ -6729,6 +6734,7 @@ var createVoiceSession = (options) => {
6729
6734
  markTurnCommitted(currentSession, finalText, transcripts);
6730
6735
  });
6731
6736
  speechDetected = false;
6737
+ resetSttHealthWindow();
6732
6738
  rewindFallbackTurnAudio();
6733
6739
  logger.info("voice turn committed", {
6734
6740
  reason,
@@ -7056,30 +7062,35 @@ var createVoiceSession = (options) => {
7056
7062
  clearSilenceTimer();
7057
7063
  }
7058
7064
  const nowMs = Date.now();
7059
- if (nowMs - lastSpeechEnergyAt > STT_HEALTH_SPEECH_GAP_MS) {
7060
- sttHealthPhaseStart = nowMs;
7061
- }
7062
- lastSpeechEnergyAt = nowMs;
7063
- const lastTranscriptAt = latest.currentTurn.lastTranscriptAt ?? 0;
7064
- if (!options.realtime && sttSession && lastTranscriptAt < sttHealthPhaseStart && nowMs - sttHealthPhaseStart >= STT_HEALTH_STALE_MS) {
7065
- sttDeafConfirmedAt = nowMs;
7066
- sttReconnectCount = nowMs - lastSttReconnectAt < STT_RECONNECT_FLAP_WINDOW_MS ? sttReconnectCount + 1 : 1;
7067
- lastSttReconnectAt = nowMs;
7068
- sttHealthPhaseStart = nowMs;
7069
- maybeSpeakSttRecovery(nowMs, latest);
7070
- if (sttReconnectCount <= MAX_STT_RECONNECTS_IN_FLAP_WINDOW) {
7071
- await appendTrace({
7072
- payload: {
7073
- action: "stt-health-reconnect",
7074
- attempt: sttReconnectCount,
7075
- classification: "stt-deaf",
7076
- reason: `no transcript for ${STT_HEALTH_STALE_MS}ms of continuous speech`
7077
- },
7078
- session: latest,
7079
- type: "session.error"
7080
- });
7081
- await closeAdapter("stt stale; health-reconnect");
7082
- return;
7065
+ if (assistantTurnInProgress) {
7066
+ resetSttHealthWindow();
7067
+ } else {
7068
+ if (nowMs - lastSpeechEnergyAt > STT_HEALTH_SPEECH_GAP_MS) {
7069
+ sttHealthPhaseStart = nowMs;
7070
+ }
7071
+ lastSpeechEnergyAt = nowMs;
7072
+ const lastTranscriptAt = latest.currentTurn.lastTranscriptAt ?? 0;
7073
+ if (!options.realtime && sttSession && lastTranscriptAt < sttHealthPhaseStart && nowMs - sttHealthPhaseStart >= STT_HEALTH_STALE_MS) {
7074
+ sttDeafConfirmedAt = nowMs;
7075
+ sttReconnectCount = nowMs - lastSttReconnectAt < STT_RECONNECT_FLAP_WINDOW_MS ? sttReconnectCount + 1 : 1;
7076
+ lastSttReconnectAt = nowMs;
7077
+ sttHealthPhaseStart = nowMs;
7078
+ maybeSpeakSttRecovery(nowMs, latest);
7079
+ if (sttReconnectCount <= MAX_STT_RECONNECTS_IN_FLAP_WINDOW) {
7080
+ await appendTrace({
7081
+ payload: {
7082
+ action: "stt-health-reconnect",
7083
+ attempt: sttReconnectCount,
7084
+ classification: "stt-deaf",
7085
+ recoverable: true,
7086
+ reason: `no transcript for ${STT_HEALTH_STALE_MS}ms of continuous speech`
7087
+ },
7088
+ session: latest,
7089
+ type: "session.error"
7090
+ });
7091
+ await closeAdapter("stt stale; health-reconnect");
7092
+ return;
7093
+ }
7083
7094
  }
7084
7095
  }
7085
7096
  } else if (speechDetected) {
@@ -7209,6 +7220,11 @@ var createVoiceSession = (options) => {
7209
7220
  speechThreshold: turnDetection.speechThreshold,
7210
7221
  transcriptStabilityMs: turnDetection.transcriptStabilityMs
7211
7222
  };
7223
+ }),
7224
+ setPlaybackRate: async (rate) => runSerial("api.setPlaybackRate", async () => {
7225
+ const clamped = Math.max(0.5, Math.min(2, Number(rate) || 1));
7226
+ await send({ rate: clamped, type: "playback_rate" });
7227
+ return clamped;
7212
7228
  })
7213
7229
  };
7214
7230
  return api;
@@ -26493,10 +26509,6 @@ var createMemoryVoicePlivoWebhookNonceStore = () => {
26493
26509
  }
26494
26510
  nonces.add(nonce);
26495
26511
  return true;
26496
- },
26497
- has: (nonce) => nonces.has(nonce),
26498
- set: (nonce) => {
26499
- nonces.add(nonce);
26500
26512
  }
26501
26513
  };
26502
26514
  };
@@ -26544,20 +26556,13 @@ var createVoiceSQLitePlivoWebhookNonceStore = (options) => {
26544
26556
  expires_at INTEGER
26545
26557
  )`);
26546
26558
  const pruneExpired = database.query(`DELETE FROM "${tableName}" WHERE expires_at IS NOT NULL AND expires_at <= ?1`);
26547
- const select = database.query(`SELECT nonce FROM "${tableName}" WHERE nonce = ?1 AND (expires_at IS NULL OR expires_at > ?2) LIMIT 1`);
26548
26559
  const insert = database.query(`INSERT OR IGNORE INTO "${tableName}" (nonce, created_at, expires_at) VALUES (?1, ?2, ?3)`);
26549
- const upsert = database.query(`INSERT INTO "${tableName}" (nonce, created_at, expires_at) VALUES (?1, ?2, ?3)
26550
- ON CONFLICT(nonce) DO UPDATE SET expires_at = excluded.expires_at`);
26551
26560
  return {
26552
26561
  claim: (nonce) => {
26553
26562
  const now = Date.now();
26554
26563
  pruneExpired.run(now);
26555
26564
  const result = insert.run(nonce, now, getPlivoNonceExpiresAt(options.ttlSeconds));
26556
26565
  return result.changes > 0;
26557
- },
26558
- has: (nonce) => Boolean(select.get(nonce, Date.now())),
26559
- set: (nonce) => {
26560
- upsert.run(nonce, Date.now(), getPlivoNonceExpiresAt(options.ttlSeconds));
26561
26566
  }
26562
26567
  };
26563
26568
  };
@@ -26611,21 +26616,6 @@ var createVoicePostgresPlivoWebhookNonceStore = (options = {}) => {
26611
26616
  ON CONFLICT (nonce) DO NOTHING
26612
26617
  RETURNING nonce`, [nonce, Date.now(), getPlivoNonceExpiresAt(options.ttlSeconds)]);
26613
26618
  return rows.length > 0;
26614
- },
26615
- has: async (nonce) => {
26616
- await initialized;
26617
- const sql = await client;
26618
- const rows = await sql.unsafe(`SELECT nonce FROM ${qualifiedTableName}
26619
- WHERE nonce = $1 AND (expires_at IS NULL OR expires_at > $2)
26620
- LIMIT 1`, [nonce, Date.now()]);
26621
- return rows.length > 0;
26622
- },
26623
- set: async (nonce) => {
26624
- await initialized;
26625
- const sql = await client;
26626
- await sql.unsafe(`INSERT INTO ${qualifiedTableName} (nonce, created_at, expires_at)
26627
- VALUES ($1, $2, $3)
26628
- ON CONFLICT (nonce) DO UPDATE SET expires_at = EXCLUDED.expires_at`, [nonce, Date.now(), getPlivoNonceExpiresAt(options.ttlSeconds)]);
26629
26619
  }
26630
26620
  };
26631
26621
  };
@@ -26652,16 +26642,9 @@ var createVoicePlivoWebhookVerifier = (options) => async (input) => {
26652
26642
  if (!nonce) {
26653
26643
  return { ok: false, reason: "invalid-signature" };
26654
26644
  }
26655
- if (nonceStore.claim) {
26656
- if (!await nonceStore.claim(nonce)) {
26657
- return { ok: false, reason: "invalid-signature" };
26658
- }
26659
- return verification;
26660
- }
26661
- if (await nonceStore.has(nonce)) {
26645
+ if (!await nonceStore.claim(nonce)) {
26662
26646
  return { ok: false, reason: "invalid-signature" };
26663
26647
  }
26664
- await nonceStore.set(nonce);
26665
26648
  return verification;
26666
26649
  };
26667
26650
  var createVoiceRedisPlivoWebhookNonceStore = (options = {}) => {
@@ -26676,11 +26659,7 @@ var createVoiceRedisPlivoWebhookNonceStore = (options = {}) => {
26676
26659
  return client.set(key, "1", ...nx ? ["NX"] : []);
26677
26660
  };
26678
26661
  return {
26679
- claim: async (nonce) => await setNonce(nonce, true) === "OK",
26680
- has: async (nonce) => Boolean(await client.exists(getPlivoRedisNonceKey(keyPrefix, nonce))),
26681
- set: async (nonce) => {
26682
- await setNonce(nonce, false);
26683
- }
26662
+ claim: async (nonce) => await setNonce(nonce, true) === "OK"
26684
26663
  };
26685
26664
  };
26686
26665
  var buildPlivoVoiceSetupStatus = async (options, input) => {
@@ -27092,10 +27071,6 @@ var createMemoryVoiceTelnyxWebhookEventStore = () => {
27092
27071
  }
27093
27072
  eventIds.add(eventId);
27094
27073
  return true;
27095
- },
27096
- has: (eventId) => eventIds.has(eventId),
27097
- set: (eventId) => {
27098
- eventIds.add(eventId);
27099
27074
  }
27100
27075
  };
27101
27076
  };
@@ -27145,20 +27120,13 @@ var createVoiceSQLiteTelnyxWebhookEventStore = (options) => {
27145
27120
  expires_at INTEGER
27146
27121
  )`);
27147
27122
  const pruneExpired = database.query(`DELETE FROM "${tableName}" WHERE expires_at IS NOT NULL AND expires_at <= ?1`);
27148
- const select = database.query(`SELECT event_id FROM "${tableName}" WHERE event_id = ?1 AND (expires_at IS NULL OR expires_at > ?2) LIMIT 1`);
27149
27123
  const insert = database.query(`INSERT OR IGNORE INTO "${tableName}" (event_id, created_at, expires_at) VALUES (?1, ?2, ?3)`);
27150
- const upsert = database.query(`INSERT INTO "${tableName}" (event_id, created_at, expires_at) VALUES (?1, ?2, ?3)
27151
- ON CONFLICT(event_id) DO UPDATE SET expires_at = excluded.expires_at`);
27152
27124
  return {
27153
27125
  claim: (eventId) => {
27154
27126
  const now = Date.now();
27155
27127
  pruneExpired.run(now);
27156
27128
  const result = insert.run(eventId, now, getTelnyxEventExpiresAt(options.ttlSeconds));
27157
27129
  return result.changes > 0;
27158
- },
27159
- has: (eventId) => Boolean(select.get(eventId, Date.now())),
27160
- set: (eventId) => {
27161
- upsert.run(eventId, Date.now(), getTelnyxEventExpiresAt(options.ttlSeconds));
27162
27130
  }
27163
27131
  };
27164
27132
  };
@@ -27212,21 +27180,6 @@ var createVoicePostgresTelnyxWebhookEventStore = (options = {}) => {
27212
27180
  ON CONFLICT (event_id) DO NOTHING
27213
27181
  RETURNING event_id`, [eventId, Date.now(), getTelnyxEventExpiresAt(options.ttlSeconds)]);
27214
27182
  return rows.length > 0;
27215
- },
27216
- has: async (eventId) => {
27217
- await initialized;
27218
- const sql = await client;
27219
- const rows = await sql.unsafe(`SELECT event_id FROM ${qualifiedTableName}
27220
- WHERE event_id = $1 AND (expires_at IS NULL OR expires_at > $2)
27221
- LIMIT 1`, [eventId, Date.now()]);
27222
- return rows.length > 0;
27223
- },
27224
- set: async (eventId) => {
27225
- await initialized;
27226
- const sql = await client;
27227
- await sql.unsafe(`INSERT INTO ${qualifiedTableName} (event_id, created_at, expires_at)
27228
- VALUES ($1, $2, $3)
27229
- ON CONFLICT (event_id) DO UPDATE SET expires_at = EXCLUDED.expires_at`, [eventId, Date.now(), getTelnyxEventExpiresAt(options.ttlSeconds)]);
27230
27183
  }
27231
27184
  };
27232
27185
  };
@@ -27243,11 +27196,7 @@ var createVoiceRedisTelnyxWebhookEventStore = (options = {}) => {
27243
27196
  return client.set(key, "1", ...nx ? ["NX"] : []);
27244
27197
  };
27245
27198
  return {
27246
- claim: async (eventId) => await setEvent(eventId, true) === "OK",
27247
- has: async (eventId) => Boolean(await client.exists(getTelnyxRedisEventKey(keyPrefix, eventId))),
27248
- set: async (eventId) => {
27249
- await setEvent(eventId, false);
27250
- }
27199
+ claim: async (eventId) => await setEvent(eventId, true) === "OK"
27251
27200
  };
27252
27201
  };
27253
27202
  var readTelnyxWebhookEventId = (rawBody) => {
@@ -27288,14 +27237,7 @@ var createVoiceTelnyxWebhookVerifier = (options) => async (input) => {
27288
27237
  if (!eventId) {
27289
27238
  return { ok: false, reason: "invalid-signature" };
27290
27239
  }
27291
- if (eventStore.claim) {
27292
- return await eventStore.claim(eventId) ? verification : { ok: false, reason: "invalid-signature" };
27293
- }
27294
- if (await eventStore.has(eventId)) {
27295
- return { ok: false, reason: "invalid-signature" };
27296
- }
27297
- await eventStore.set(eventId);
27298
- return verification;
27240
+ return await eventStore.claim(eventId) ? verification : { ok: false, reason: "invalid-signature" };
27299
27241
  };
27300
27242
  var buildTelnyxVoiceSetupStatus = async (options, input) => {
27301
27243
  const origin = resolveRequestOrigin2(input.request);
@@ -44905,6 +44847,7 @@ var createContractApi = (session) => ({
44905
44847
  speechThreshold: 0,
44906
44848
  transcriptStabilityMs: 0
44907
44849
  }),
44850
+ setPlaybackRate: async (rate5) => rate5,
44908
44851
  snapshot: async () => session,
44909
44852
  transfer: async () => {}
44910
44853
  });
@@ -11324,6 +11324,11 @@ var serverMessageToAction = (message) => {
11324
11324
  transcript: message.transcript,
11325
11325
  type: "partial"
11326
11326
  };
11327
+ case "playback_rate":
11328
+ return {
11329
+ rate: message.rate,
11330
+ type: "playback_rate"
11331
+ };
11327
11332
  case "replay":
11328
11333
  return {
11329
11334
  assistantTexts: message.assistantTexts,
@@ -11506,6 +11511,7 @@ var isVoiceServerMessage = (value) => {
11506
11511
  case "error":
11507
11512
  case "final":
11508
11513
  case "partial":
11514
+ case "playback_rate":
11509
11515
  case "pong":
11510
11516
  case "replay":
11511
11517
  case "session":
@@ -11932,6 +11938,7 @@ var createInitialState = () => ({
11932
11938
  isConnected: false,
11933
11939
  paused: false,
11934
11940
  partial: "",
11941
+ playbackRate: null,
11935
11942
  reconnect: createInitialReconnectState(),
11936
11943
  scenarioId: null,
11937
11944
  sessionId: null,
@@ -12040,6 +12047,12 @@ var createVoiceStreamStore = () => {
12040
12047
  partial: joinPartial(turnFinalText, action.transcript.text)
12041
12048
  };
12042
12049
  break;
12050
+ case "playback_rate":
12051
+ state = {
12052
+ ...state,
12053
+ playbackRate: Math.max(0.5, Math.min(2, action.rate))
12054
+ };
12055
+ break;
12043
12056
  case "replay":
12044
12057
  turnFinalText = action.partial;
12045
12058
  state = {
@@ -12205,6 +12218,9 @@ var createVoiceStream = (path, options = {}) => {
12205
12218
  get partial() {
12206
12219
  return store.getSnapshot().partial;
12207
12220
  },
12221
+ get playbackRate() {
12222
+ return store.getSnapshot().playbackRate;
12223
+ },
12208
12224
  get paused() {
12209
12225
  return store.getSnapshot().paused;
12210
12226
  },
@@ -12809,6 +12825,7 @@ var createInitialState2 = (stream) => ({
12809
12825
  paused: stream.paused,
12810
12826
  pauseExpiresAt: stream.pauseExpiresAt,
12811
12827
  partial: stream.partial,
12828
+ playbackRate: stream.playbackRate,
12812
12829
  reconnect: stream.reconnect,
12813
12830
  recordingError: null,
12814
12831
  sessionId: stream.sessionId,
@@ -12846,6 +12863,7 @@ var createVoiceController = (path, options = {}) => {
12846
12863
  paused: stream.paused,
12847
12864
  pauseExpiresAt: stream.pauseExpiresAt,
12848
12865
  partial: stream.partial,
12866
+ playbackRate: stream.playbackRate,
12849
12867
  reconnect: stream.reconnect,
12850
12868
  sessionId: stream.sessionId,
12851
12869
  sessionMetadata: stream.sessionMetadata,
@@ -12992,6 +13010,9 @@ var createVoiceController = (path, options = {}) => {
12992
13010
  get partial() {
12993
13011
  return state.partial;
12994
13012
  },
13013
+ get playbackRate() {
13014
+ return state.playbackRate;
13015
+ },
12995
13016
  get paused() {
12996
13017
  return state.paused;
12997
13018
  },
@@ -29,6 +29,7 @@ export declare const useVoiceController: <TResult = unknown>(path: string, optio
29
29
  }>;
30
30
  error: string | null;
31
31
  isConnected: boolean;
32
+ playbackRate: number | null;
32
33
  isRecording: boolean;
33
34
  recordingError: string | null;
34
35
  };
@@ -25,4 +25,5 @@ export declare const useVoiceStream: <TResult = unknown>(path: string, options?:
25
25
  }>;
26
26
  error: string | null;
27
27
  isConnected: boolean;
28
+ playbackRate: number | null;
28
29
  };
@@ -693,6 +693,11 @@ var serverMessageToAction = (message) => {
693
693
  transcript: message.transcript,
694
694
  type: "partial"
695
695
  };
696
+ case "playback_rate":
697
+ return {
698
+ rate: message.rate,
699
+ type: "playback_rate"
700
+ };
696
701
  case "replay":
697
702
  return {
698
703
  assistantTexts: message.assistantTexts,
@@ -875,6 +880,7 @@ var isVoiceServerMessage = (value) => {
875
880
  case "error":
876
881
  case "final":
877
882
  case "partial":
883
+ case "playback_rate":
878
884
  case "pong":
879
885
  case "replay":
880
886
  case "session":
@@ -1301,6 +1307,7 @@ var createInitialState = () => ({
1301
1307
  isConnected: false,
1302
1308
  paused: false,
1303
1309
  partial: "",
1310
+ playbackRate: null,
1304
1311
  reconnect: createInitialReconnectState(),
1305
1312
  scenarioId: null,
1306
1313
  sessionId: null,
@@ -1409,6 +1416,12 @@ var createVoiceStreamStore = () => {
1409
1416
  partial: joinPartial(turnFinalText, action.transcript.text)
1410
1417
  };
1411
1418
  break;
1419
+ case "playback_rate":
1420
+ state = {
1421
+ ...state,
1422
+ playbackRate: Math.max(0.5, Math.min(2, action.rate))
1423
+ };
1424
+ break;
1412
1425
  case "replay":
1413
1426
  turnFinalText = action.partial;
1414
1427
  state = {
@@ -1574,6 +1587,9 @@ var createVoiceStream = (path, options = {}) => {
1574
1587
  get partial() {
1575
1588
  return store.getSnapshot().partial;
1576
1589
  },
1590
+ get playbackRate() {
1591
+ return store.getSnapshot().playbackRate;
1592
+ },
1577
1593
  get paused() {
1578
1594
  return store.getSnapshot().paused;
1579
1595
  },
@@ -1946,6 +1962,7 @@ var createInitialState2 = (stream) => ({
1946
1962
  paused: stream.paused,
1947
1963
  pauseExpiresAt: stream.pauseExpiresAt,
1948
1964
  partial: stream.partial,
1965
+ playbackRate: stream.playbackRate,
1949
1966
  reconnect: stream.reconnect,
1950
1967
  recordingError: null,
1951
1968
  sessionId: stream.sessionId,
@@ -1983,6 +2000,7 @@ var createVoiceController = (path, options = {}) => {
1983
2000
  paused: stream.paused,
1984
2001
  pauseExpiresAt: stream.pauseExpiresAt,
1985
2002
  partial: stream.partial,
2003
+ playbackRate: stream.playbackRate,
1986
2004
  reconnect: stream.reconnect,
1987
2005
  sessionId: stream.sessionId,
1988
2006
  sessionMetadata: stream.sessionMetadata,
@@ -2129,6 +2147,9 @@ var createVoiceController = (path, options = {}) => {
2129
2147
  get partial() {
2130
2148
  return state.partial;
2131
2149
  },
2150
+ get playbackRate() {
2151
+ return state.playbackRate;
2152
+ },
2132
2153
  get paused() {
2133
2154
  return state.paused;
2134
2155
  },
@@ -135,9 +135,8 @@ export type PlivoVoiceSmokeOptions = {
135
135
  title?: string;
136
136
  };
137
137
  export type VoicePlivoWebhookNonceStore = {
138
- claim?: (nonce: string) => Promise<boolean> | boolean;
139
- has: (nonce: string) => Promise<boolean> | boolean;
140
- set: (nonce: string) => Promise<void> | void;
138
+ /** Atomically accepts an unseen signature nonce and rejects every replay. */
139
+ claim: (nonce: string) => Promise<boolean> | boolean;
141
140
  };
142
141
  export type VoicePlivoWebhookNonceStoreOptions = {
143
142
  ttlSeconds?: number;
@@ -155,7 +154,7 @@ export type VoicePostgresPlivoWebhookNonceStoreOptions = VoicePlivoWebhookNonceS
155
154
  tableName?: string;
156
155
  tablePrefix?: string;
157
156
  };
158
- export type VoiceRedisPlivoWebhookNonceClient = Pick<RedisClient, "exists" | "set">;
157
+ export type VoiceRedisPlivoWebhookNonceClient = Pick<RedisClient, "set">;
159
158
  export type VoiceRedisPlivoWebhookNonceStoreOptions = VoicePlivoWebhookNonceStoreOptions & {
160
159
  client?: VoiceRedisPlivoWebhookNonceClient;
161
160
  keyPrefix?: string;