@absolutejs/voice 0.0.22-beta.597 → 0.0.22-beta.599

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:E,getOwnPropertyNames:I0,getOwnPropertyDescriptor:H0}=Object,_0=Object.prototype.hasOwnProperty;function G0(g){return this[g]}var L0=(g)=>{var A=(x??=new WeakMap).get(g),C;if(A)return A;if(A=E({},"__esModule",{value:!0}),g&&typeof g==="object"||typeof g==="function"){for(var V of I0(g))if(!_0.call(A,V))E(A,V,{get:G0.bind(g,V),enumerable:!(C=H0(g,V))||C.enumerable})}return x.set(g,A),A},x;var U0=(g)=>g;function w0(g,A){this[g]=U0.bind(null,A)}var y0=(g,A)=>{for(var C in A)E(g,C,{get:A[C],enumerable:!0,configurable:!0,set:w0.bind(A,C)})};var r0={};y0(r0,{mount:()=>V0,default:()=>s0,VOICE_EMBED_VERSION:()=>C0});var X0=(g)=>{if(typeof g!=="string")return g;return document.querySelector(g)},h0=(g,A,C,V)=>{let I=A??g.getAttribute("hx-get")??"";if(!I)return"";let R=new URL(I,window.location.origin);if(V)R.searchParams.set(C,V);else R.searchParams.delete(C);return`${R.pathname}${R.search}${R.hash}`},F=(g,A)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let C=X0(A.element);if(!C)return()=>{};let V=A.eventName??"voice-refresh",I=A.sessionQueryParam??"sessionId",R=()=>{let L=window,$=h0(C,A.route,I,g.sessionId);if($)C.setAttribute("hx-get",$);L.htmx?.process?.(C),L.htmx?.trigger?.(C,V)},w=g.subscribe(R);return R(),()=>{w()}};var D0=(g)=>Math.max(-1,Math.min(1,g)),J0=(g)=>{let A=new Int16Array(g.length);for(let C=0;C<g.length;C+=1){let V=D0(g[C]??0);A[C]=V<0?V*32768:V*32767}return new Uint8Array(A.buffer)},Y0=(g)=>{let A=g instanceof Uint8Array?g:new Uint8Array(g);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 V=0;for(let I of C){let R=I/32768;V+=R*R}return Math.min(1,Math.max(0,Math.sqrt(V/C.length)*5.5))},Z0=(g,A,C)=>{if(A===C)return g;let V=A/C,I=Math.round(g.length/V),R=new Float32Array(I),w=0,L=0;while(w<R.length){let $=Math.round((w+1)*V),X=0,G=0;for(let h=L;h<$&&h<g.length;h+=1)X+=g[h]??0,G+=1;R[w]=G>0?X/G:0,w+=1,L=$}return R},f=(g)=>{let A=null,C=null,V=null,I=null;return{start:async()=>{if(!g.stream&&(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia))throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let L=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!L)throw Error("Browser microphone capture requires AudioContext support.");if(I=g.stream??await navigator.mediaDevices.getUserMedia({audio:{autoGainControl:!0,channelCount:g.channelCount??1,echoCancellation:!0,noiseSuppression:!0}}),A=new L,A.state==="suspended")await A.resume();C=A.createMediaStreamSource(I),V=A.createScriptProcessor(4096,1,1),V.onaudioprocess=($)=>{let X=$.inputBuffer.getChannelData(0),G=Z0(X,A?.sampleRate??48000,g.sampleRateHz??16000),h=J0(G);g.onLevel?.(Y0(h)),g.onAudio(h)},C.connect(V),V.connect(A.destination)},stop:()=>{V?.disconnect(),C?.disconnect(),I?.getTracks().forEach((L)=>L.stop()),A?.close(),g.onLevel?.(0),A=null,I=null,V=null,C=null}}};var M=(g)=>{if(typeof g==="string"&&g.trim())return g;if(g instanceof Error&&g.message.trim())return g.message;if(g&&typeof g==="object"){let A=g;for(let C of["message","reason","description"]){let V=A[C];if(typeof V==="string"&&V.trim())return V}if("error"in A)return M(A.error);if("cause"in A)return M(A.cause);try{return JSON.stringify(g)}catch{}}return"Unexpected error"},l=(g)=>{switch(g.type){case"audio":return{chunk:Uint8Array.from(atob(g.chunkBase64),(A)=>A.charCodeAt(0)),format:g.format,receivedAt:g.receivedAt,turnId:g.turnId,type:"audio"};case"assistant":return{text:g.text,turnId:g.turnId,type:"assistant"};case"assistant_delta":return{delta:g.delta,turnId:g.turnId,type:"assistant_delta"};case"complete":return{sessionId:g.sessionId,type:"complete"};case"connection":return{reconnect:g.reconnect,type:"connection"};case"call_lifecycle":return{event:g.event,sessionId:g.sessionId,type:"call_lifecycle"};case"error":return{message:M(g.message),type:"error"};case"final":return{transcript:g.transcript,type:"final"};case"partial":return{transcript:g.transcript,type:"partial"};case"replay":return{assistantTexts:g.assistantTexts,call:g.call,partial:g.partial,scenarioId:g.scenarioId,sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,status:g.status,turns:g.turns,type:"replay"};case"session":return{sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,scenarioId:g.scenarioId,status:g.status,type:"session"};case"turn":return{turn:g.turn,type:"turn"};default:return null}};var gg=Math.PI*2;var T=(g,A,C,V)=>{g.push({code:C,message:V,severity:A})};var O0=(g)=>g.length===0?void 0:g.reduce((A,C)=>A+C,0)/g.length,z=(g)=>g.length===0?void 0:Math.max(...g);var D=(g,A)=>{let C=g[A];return typeof C==="number"&&Number.isFinite(C)?C:void 0},B=(g,A)=>{let C=g[A];return typeof C==="boolean"?C:void 0},Y=(g,A)=>{let C=g[A];return typeof C==="string"?C:void 0},k=(g)=>String(g.id??Y(g,"ssrc")??D(g,"ssrc")??Y(g,"trackIdentifier")??Y(g,"mid")??"unknown"),d=(g)=>g===void 0?void 0:g*1000;var S0=(g)=>{let A={};for(let[C,V]of Object.entries(g))if(V===null||typeof V==="boolean"||typeof V==="number"||typeof V==="string")A[C]=V;return A};var v=(g={})=>{let A=g.stats??[],C=[],V=A.filter((H)=>H.type==="inbound-rtp"&&Y(H,"kind")!=="video"),I=A.filter((H)=>H.type==="outbound-rtp"&&Y(H,"kind")!=="video"),R=A.filter((H)=>H.type==="candidate-pair"),w=A.filter((H)=>(H.type==="track"||H.type==="media-source")&&Y(H,"kind")==="audio"),L=R.filter((H)=>B(H,"selected")===!0||B(H,"nominated")===!0||Y(H,"state")==="succeeded").length,$=w.filter((H)=>Y(H,"readyState")!=="ended"&&Y(H,"trackState")!=="ended"&&B(H,"ended")!==!0).length,X=w.filter((H)=>Y(H,"readyState")==="ended"||Y(H,"trackState")==="ended"||B(H,"ended")===!0).length,G=V.reduce((H,J)=>H+(D(J,"packetsReceived")??0),0),h=I.reduce((H,J)=>H+(D(J,"packetsSent")??0),0),_=[...V,...I].reduce((H,J)=>H+Math.max(0,D(J,"packetsLost")??0),0),Z=G+_,y=Z===0?0:_/Z,K=V.reduce((H,J)=>H+(D(J,"bytesReceived")??0),0),O=I.reduce((H,J)=>H+(D(J,"bytesSent")??0),0),S=z(R.map((H)=>d(D(H,"currentRoundTripTime")??D(H,"roundTripTime"))).filter((H)=>H!==void 0)),W=z([...V,...I].map((H)=>d(D(H,"jitter"))).filter((H)=>H!==void 0)),N=z(V.map((H)=>{let J=D(H,"jitterBufferDelay"),U=D(H,"jitterBufferEmittedCount");return J!==void 0&&U!==void 0&&U>0?J/U*1000:void 0}).filter((H)=>H!==void 0)),j=w.map((H)=>D(H,"audioLevel")).filter((H)=>H!==void 0);if(g.requireConnectedCandidatePair&&R.length>0&&L===0)T(C,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(g.requireLiveAudioTrack&&$===0)T(C,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(g.maxPacketLossRatio!==void 0&&y>g.maxPacketLossRatio)T(C,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(y)} above ${String(g.maxPacketLossRatio)}.`);if(g.maxRoundTripTimeMs!==void 0&&S!==void 0&&S>g.maxRoundTripTimeMs)T(C,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(S)}ms above ${String(g.maxRoundTripTimeMs)}ms.`);if(g.maxJitterMs!==void 0&&W!==void 0&&W>g.maxJitterMs)T(C,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(W)}ms above ${String(g.maxJitterMs)}ms.`);return{activeCandidatePairs:L,audioLevelAverage:O0(j),bytesReceived:K,bytesSent:O,checkedAt:Date.now(),endedAudioTracks:X,inboundPackets:G,issues:C,jitterBufferDelayMs:N,jitterMs:W,liveAudioTracks:$,outboundPackets:h,packetLossRatio:y,packetsLost:_,roundTripTimeMs:S,status:C.some((H)=>H.severity==="error")?"fail":C.length>0?"warn":"pass",totalStats:A.length}},i=async(g)=>{return[...(await g.peerConnection.getStats(g.selector??null)).values()].map(S0)};var o=(g={})=>{let A=g.stats??[],C=g.previousStats??[],V=[],I=new Map(C.map((_)=>[k(_),_])),w=A.filter((_)=>(_.type==="inbound-rtp"||_.type==="outbound-rtp")&&Y(_,"kind")!=="video"&&Y(_,"mediaType")!=="video").map((_)=>{let Z=_.type==="outbound-rtp"?"outbound":"inbound",y=Z==="outbound"?"packetsSent":"packetsReceived",K=Z==="outbound"?"bytesSent":"bytesReceived",O=I.get(k(_)),S=D(_,y),W=O?D(O,y):void 0,N=D(_,K),j=O?D(O,K):void 0,H=_.timestamp!==void 0&&O?.timestamp!==void 0?_.timestamp-O.timestamp:void 0;return{bytesDelta:N!==void 0&&j!==void 0?N-j:void 0,currentPackets:S,direction:Z,id:k(_),packetDelta:S!==void 0&&W!==void 0?S-W:void 0,previousPackets:W,timeDeltaMs:H}}),L=w.filter((_)=>_.direction==="inbound"),$=w.filter((_)=>_.direction==="outbound"),X=z(w.map((_)=>_.timeDeltaMs).filter((_)=>_!==void 0)),G=L.filter((_)=>g.maxInboundPacketStallMs!==void 0&&_.timeDeltaMs!==void 0&&_.timeDeltaMs>=g.maxInboundPacketStallMs&&_.packetDelta!==void 0&&_.packetDelta<=0).length,h=$.filter((_)=>g.maxOutboundPacketStallMs!==void 0&&_.timeDeltaMs!==void 0&&_.timeDeltaMs>=g.maxOutboundPacketStallMs&&_.packetDelta!==void 0&&_.packetDelta<=0).length;if(g.requireInboundAudio&&L.length===0)T(V,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(g.requireOutboundAudio&&$.length===0)T(V,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(g.maxGapMs!==void 0&&X!==void 0&&X>g.maxGapMs)T(V,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(X)}ms above ${String(g.maxGapMs)}ms.`);if(G>0)T(V,"error","media.webrtc_inbound_stalled",`${String(G)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(h>0)T(V,"error","media.webrtc_outbound_stalled",`${String(h)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:L.length,issues:V,maxObservedGapMs:X,outboundAudioStreams:$.length,stalledInboundStreams:G,stalledOutboundStreams:h,status:V.some((_)=>_.severity==="error")?"fail":V.length>0?"warn":"pass",streams:w,totalStats:A.length}};var W0="/api/voice/browser-media",Q0=5000,T0=async(g)=>g.peerConnection??await g.getPeerConnection?.()??null,c0=async(g,A)=>{let C=A.fetch??globalThis.fetch;if(!C)return;await C(A.path??W0,{body:JSON.stringify(g),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},n=(g)=>{let A=null,C=[],V=async()=>{let w=await T0(g);if(!w)return;let L=await i({peerConnection:w}),$=v({...g,stats:L}),X=g.continuity===!1?void 0:o({...g.continuity,previousStats:C,stats:L}),G={at:Date.now(),continuity:X,report:$,scenarioId:g.getScenarioId?.()??null,sessionId:g.getSessionId?.()??null};return C=L,g.onReport?.(G),await c0(G,g),G},I=()=>{V().catch((w)=>{g.onError?.(w)})},R=()=>{if(A)clearInterval(A),A=null};return{close:R,reportOnce:V,stop:R,start:()=>{if(A)return;I(),A=setInterval(I,g.intervalMs??Q0)}}};var K0=(g,A,C)=>Math.min(C,A*2**(Math.max(1,g)-1));var q=()=>{},P0=()=>q,q0={callControl:q,close:q,endTurn:q,send:q,sendAudio:q,simulateDisconnect:q,subscribe:P0,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",start:()=>{}},N0=()=>crypto.randomUUID(),j0=(g,A,C)=>{let{hostname:V,port:I,protocol:R}=window.location,w=R==="https:"?"wss:":"ws:",L=I?`:${I}`:"",$=new URL(`${w}//${V}${L}${g}`);if($.searchParams.set("sessionId",A),C)$.searchParams.set("scenarioId",C);return $.toString()},B0=(g)=>{if(!g||typeof g!=="object"||!("type"in g))return!1;switch(g.type){case"audio":case"assistant":case"call_lifecycle":case"complete":case"connection":case"error":case"final":case"partial":case"pong":case"replay":case"session":case"turn":return!0;default:return!1}},z0=(g)=>{if(typeof g.data!=="string")return null;try{let A=JSON.parse(g.data);return B0(A)?A:null}catch{return null}},m=(g,A={})=>{if(typeof window>"u")return q0;let C=new Set,V=A.reconnect!==!1,I=A.maxReconnectAttempts??15,R=A.reconnectMaxDelayMs??8000,w=A.pingInterval??30000,L=(U)=>K0(U,500,R),$={isConnected:!1,pendingMessages:[],scenarioId:A.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:A.sessionId??N0(),ws:null},X=(U)=>{C.forEach((Q)=>Q(U))},G=()=>{if($.pingInterval)clearInterval($.pingInterval),$.pingInterval=null;if($.reconnectTimeout)clearTimeout($.reconnectTimeout),$.reconnectTimeout=null},h=()=>{if($.ws?.readyState!==1)return;while($.pendingMessages.length>0){let U=$.pendingMessages.shift();if(U!==void 0)$.ws.send(U)}},_=()=>{$.reconnectAttempts+=1;let U=L($.reconnectAttempts),Q=Date.now()+U;X({reconnect:{attempts:$.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:I,nextAttemptAt:Q,status:"reconnecting"},type:"connection"}),$.reconnectTimeout=setTimeout(()=>{if($.reconnectAttempts>I){X({reconnect:{attempts:$.reconnectAttempts,maxAttempts:I,status:"exhausted"},type:"connection"});return}Z()},U)},Z=()=>{let U=new WebSocket(j0(g,$.sessionId,$.scenarioId));U.binaryType="arraybuffer",U.onopen=()=>{let Q=$.reconnectAttempts>0;if($.isConnected=!0,h(),Q)X({reconnect:{attempts:$.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:I,status:"resumed"},type:"connection"}),$.reconnectAttempts=0;C.forEach((P)=>P({scenarioId:$.scenarioId??void 0,sessionId:$.sessionId,status:"active",type:"session"})),$.pingInterval=setInterval(()=>{if(U.readyState===1)U.send(JSON.stringify({type:"ping"}))},w)},U.onmessage=(Q)=>{let P=z0(Q);if(!P)return;if(P.type==="session")$.sessionId=P.sessionId,$.scenarioId=P.scenarioId??$.scenarioId;C.forEach(($0)=>$0(P))},U.onclose=(Q)=>{if($.isConnected=!1,G(),V&&Q.code!==1000&&$.reconnectAttempts<I)_();else if(V&&Q.code!==1000)X({reconnect:{attempts:$.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:I,status:"exhausted"},type:"connection"})},$.ws=U},y=(U)=>{if($.ws?.readyState===1){$.ws.send(U);return}$.pendingMessages.push(U)},K=(U)=>{y(JSON.stringify(U))},O=(U={})=>{if(U.sessionId)$.sessionId=U.sessionId;if(U.scenarioId)$.scenarioId=U.scenarioId;K({scenarioId:$.scenarioId??void 0,sessionId:$.sessionId,type:"start"})},S=(U)=>{y(U)},W=()=>{K({type:"end_turn"})},N=(U)=>{K({...U,type:"call_control"})},j=()=>{if(G(),$.ws)$.ws.close(1000),$.ws=null;$.isConnected=!1,C.clear()},H=()=>{if($.ws?.readyState===1)$.ws.close(4000,"absolutejs-voice-reconnect-proof")},J=(U)=>{return C.add(U),()=>{C.delete(U)}};return Z(),{callControl:N,close:j,endTurn:W,send:K,sendAudio:S,simulateDisconnect:H,start:O,subscribe:J,getReadyState:()=>$.ws?.readyState??3,getScenarioId:()=>$.scenarioId??"",getSessionId:()=>$.sessionId}};var b0=()=>({attempts:0,maxAttempts:0,status:"idle"}),E0=(g,A)=>{let C=A.trim().replace(/\s+/g," ");if(!C)return g;if(!g)return C;if(g===C||g.endsWith(C))return g;if(C.includes(g))return C;return`${g} ${C}`},M0=(g,A)=>{let C=A.trim().replace(/\s+/g," ");if(!g)return C;if(!C||g.endsWith(C))return g;return`${g} ${C}`},k0=()=>({assistantAudio:[],assistantStreamingText:"",assistantTexts:[],call:null,error:null,isConnected:!1,partial:"",reconnect:b0(),scenarioId:null,sessionId:null,sessionMetadata:null,status:"idle",turns:[]}),u=()=>{let g=k0(),A="",C=new Set,V=()=>{C.forEach((R)=>R())};return{dispatch:(R)=>{switch(R.type){case"audio":g={...g,assistantAudio:[...g.assistantAudio,{chunk:R.chunk,format:R.format,receivedAt:R.receivedAt,turnId:R.turnId}]};break;case"assistant":g={...g,assistantStreamingText:"",assistantTexts:[...g.assistantTexts,R.text]};break;case"assistant_delta":g={...g,assistantStreamingText:`${g.assistantStreamingText}${R.delta}`};break;case"complete":g={...g,sessionId:R.sessionId,status:"completed"};break;case"call_lifecycle":g={...g,call:{...g.call,disposition:R.event.type==="end"?R.event.disposition:g.call?.disposition,endedAt:R.event.type==="end"?R.event.at:g.call?.endedAt,events:[...g.call?.events??[],R.event],lastEventAt:R.event.at,startedAt:g.call?.startedAt??R.event.at},sessionId:R.sessionId};break;case"connected":g={...g,isConnected:!0,reconnect:g.reconnect.status==="reconnecting"?{...g.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:g.reconnect};break;case"connection":g={...g,reconnect:R.reconnect};break;case"disconnected":g={...g,isConnected:!1};break;case"error":g={...g,error:R.message};break;case"final":A=E0(A,R.transcript.text),g={...g,partial:A};break;case"partial":g={...g,partial:M0(A,R.transcript.text)};break;case"replay":A=R.partial,g={...g,assistantStreamingText:"",assistantTexts:[...R.assistantTexts],call:R.call??null,error:null,isConnected:R.status==="active",partial:R.partial,reconnect:g.reconnect.status==="reconnecting"?{...g.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:g.reconnect,scenarioId:R.scenarioId??g.scenarioId,sessionId:R.sessionId,sessionMetadata:R.sessionMetadata??g.sessionMetadata,status:R.status,turns:[...R.turns]};break;case"session":g={...g,error:null,scenarioId:R.scenarioId??g.scenarioId,isConnected:R.status==="active",sessionId:R.sessionId,sessionMetadata:R.sessionMetadata??g.sessionMetadata,status:R.status};break;case"turn":A="",g={...g,partial:"",turns:[...g.turns,R.turn]};break}V()},getServerSnapshot:()=>g,getSnapshot:()=>g,subscribe:(R)=>{return C.add(R),()=>{C.delete(R)}}}};var r=(g,A={})=>{let C=m(g,A),V=u(),I=A.browserMedia&&typeof window<"u"?n({...A.browserMedia,getScenarioId:()=>A.browserMedia?A.browserMedia.getScenarioId?.()??C.getScenarioId():C.getScenarioId(),getSessionId:()=>A.browserMedia?A.browserMedia.getSessionId?.()??C.getSessionId():C.getSessionId()}):null,R=new Set,w=(G)=>Promise.resolve().then(()=>{if(!G?.sessionId&&!G?.scenarioId)return;C.start(G),I?.start()}),L=()=>{R.forEach((G)=>G())},$=()=>{if(!A.reconnectReportPath||typeof fetch>"u")return;let G=V.getSnapshot(),h=JSON.stringify({at:Date.now(),reconnect:G.reconnect,scenarioId:G.scenarioId,sessionId:C.getSessionId(),turnIds:G.turns.map((_)=>_.id)});fetch(A.reconnectReportPath,{body:h,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},X=C.subscribe((G)=>{let h=l(G);if(h){if(V.dispatch(h),G.type==="connection")$();L()}});return{start:w,get assistantAudio(){return V.getSnapshot().assistantAudio},get assistantTexts(){return V.getSnapshot().assistantTexts},get assistantStreamingText(){return V.getSnapshot().assistantStreamingText},get call(){return V.getSnapshot().call},callControl(G){C.callControl(G)},close(){X(),I?.close(),C.close(),V.dispatch({type:"disconnected"}),L()},endTurn(){C.endTurn()},get error(){return V.getSnapshot().error},getServerSnapshot(){return V.getServerSnapshot()},getSnapshot(){return V.getSnapshot()},get isConnected(){return V.getSnapshot().isConnected},get partial(){return V.getSnapshot().partial},get reconnect(){return V.getSnapshot().reconnect},get scenarioId(){return V.getSnapshot().scenarioId},sendAudio(G){C.sendAudio(G)},get sessionId(){return C.getSessionId()},get sessionMetadata(){return V.getSnapshot().sessionMetadata},simulateDisconnect(){C.simulateDisconnect()},get status(){return V.getSnapshot().status},subscribe(G){return R.add(G),()=>{R.delete(G)}},get turns(){return V.getSnapshot().turns}}};var s=(g)=>{if(!g||g.enabled===!1)return;return{enabled:!0,maxGain:g.maxGain??3,noiseGateAttenuation:g.noiseGateAttenuation??0.15,noiseGateThreshold:g.noiseGateThreshold??0.006,targetLevel:g.targetLevel??0.08}};var b=1200;var x0={balanced:{qualityProfile:"general",semanticVetoMaxMs:0,semanticVetoRecheckMs:b,silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:"general",semanticVetoMaxMs:0,semanticVetoRecheckMs:b,silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},"long-form":{qualityProfile:"general",semanticVetoMaxMs:0,semanticVetoRecheckMs:b,silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},F0={"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}},f0="fast",l0="general",a=(g)=>{let A=g?.profile??f0,C=g?.qualityProfile??l0,V=x0[A],I=F0[C];return{profile:A,qualityProfile:C,semanticVetoMaxMs:g?.semanticVetoMaxMs??V.semanticVetoMaxMs,semanticVetoRecheckMs:g?.semanticVetoRecheckMs??V.semanticVetoRecheckMs,silenceMs:g?.silenceMs??I.silenceMs??V.silenceMs,speechThreshold:g?.speechThreshold??I.speechThreshold??V.speechThreshold,transcriptStabilityMs:g?.transcriptStabilityMs??I.transcriptStabilityMs??V.transcriptStabilityMs}};var d0={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"}}},p=(g="default")=>{let A=d0[g];return{audioConditioning:s(A.audioConditioning),capture:{channelCount:A.capture?.channelCount??1,sampleRateHz:A.capture?.sampleRateHz??16000},connection:{...A.connection},name:g,sttLifecycle:A.sttLifecycle??"continuous",turnDetection:a(A.turnDetection)}};var v0=(g)=>({assistantAudio:[...g.assistantAudio],assistantStreamingText:g.assistantStreamingText,assistantTexts:[...g.assistantTexts],call:g.call,error:g.error,isConnected:g.isConnected,isRecording:!1,partial:g.partial,reconnect:g.reconnect,recordingError:null,sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,scenarioId:g.scenarioId,status:g.status,turns:[...g.turns]}),t=(g,A={})=>{let C=p(A.preset),V=r(g,{...C.connection,...A.connection}),I=null,R=v0(V),w=new Set,L=()=>{for(let y of w)y()},$=()=>{if(R={...R,assistantAudio:[...V.assistantAudio],assistantStreamingText:V.assistantStreamingText,assistantTexts:[...V.assistantTexts],call:V.call,error:V.error,isConnected:V.isConnected,partial:V.partial,reconnect:V.reconnect,sessionId:V.sessionId,sessionMetadata:V.sessionMetadata,scenarioId:V.scenarioId,status:V.status,turns:[...V.turns]},A.autoStopOnComplete!==!1&&R.status==="completed"&&R.isRecording)I?.stop(),I=null,R={...R,isRecording:!1};L()},X=V.subscribe($);$();let G=()=>{if(I)return I;return I=f({channelCount:A.capture?.channelCount??C.capture.channelCount,onLevel:A.capture?.onLevel,onAudio:(y)=>{if(A.capture?.onAudio){A.capture.onAudio(y,V.sendAudio);return}V.sendAudio(y)},sampleRateHz:A.capture?.sampleRateHz??C.capture.sampleRateHz,...A.capture?.stream?{stream:A.capture.stream}:{}}),I},h=()=>{I?.stop(),I=null,R={...R,isRecording:!1},L()},_=async()=>{if(R.isRecording)return;try{R={...R,recordingError:null},L(),await G().start(),R={...R,isRecording:!0},L()}catch(y){throw I=null,R={...R,isRecording:!1,recordingError:y instanceof Error?y.message:String(y)},L(),y}};return{close:()=>{X(),h(),V.close()},startRecording:_,stopRecording:h,get assistantAudio(){return R.assistantAudio},get assistantTexts(){return R.assistantTexts},get assistantStreamingText(){return R.assistantStreamingText},bindHTMX(y){return F(V,y)},get call(){return R.call},callControl:(y)=>V.callControl(y),endTurn:()=>V.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 reconnect(){return R.reconnect},get recordingError(){return R.recordingError},get scenarioId(){return R.scenarioId},sendAudio:(y)=>V.sendAudio(y),get sessionId(){return R.sessionId},get sessionMetadata(){return R.sessionMetadata},simulateDisconnect:()=>V.simulateDisconnect(),get status(){return R.status},subscribe:(y)=>{return w.add(y),()=>{w.delete(y)}},toggleRecording:async()=>{if(R.isRecording){h();return}await _()},get turns(){return R.turns}}};var c=(g)=>String(g).replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;");var e=(g)=>{if(!g.isConnected)return"idle";if(g.isPlaying)return"speaking";if(g.isRecording&&g.hasActivePartial)return"listening";if(g.isRecording)return"listening";if(g.lastTranscriptAt&&!g.lastAssistantAt)return"thinking";if(g.lastTranscriptAt&&g.lastAssistantAt&&g.lastTranscriptAt>g.lastAssistantAt)return"thinking";return"idle"};var i0={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},o0={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},n0=(g,A)=>{switch(g){case"listening":return A.listening;case"speaking":return A.speaking;case"thinking":return A.thinking;case"idle":return A.idle}},g0=(g)=>{let A={...i0,...g.theme},C={...o0,...g.labels},V=g.state.assistantAudio.at(-1)?.receivedAt,I=g.state.turns.at(-1)?.committedAt,R=e({hasActivePartial:g.state.partial.length>0,isConnected:g.state.isConnected,isPlaying:!1,isRecording:g.state.isRecording,lastAssistantAt:V,lastTranscriptAt:I}),w=!g.state.isConnected&&g.state.status!=="idle"&&!g.state.error,L=g.state.error?"Error":w?C.connecting:g.state.status==="completed"?C.callEnded:n0(R,C);return{agentState:R,classes:{container:`absolute-voice-widget absolute-voice-widget--${R}`,dot:`absolute-voice-widget__dot${g.state.error?" absolute-voice-widget__dot--error":""}`},controls:{canEnd:g.state.isConnected,canMute:g.state.isRecording,canStart:!g.state.isRecording&&g.state.status!=="completed"},errorMessage:g.state.error??void 0,labels:C,partial:g.state.partial||void 0,statusLabel:L,theme:A,title:g.title??"Voice"}},m0=(g)=>typeof g==="number"?`${g}px`:g,A0=(g)=>{let A=g.theme,C=`background:${A.background};border-radius:${m0(A.radius)};color:${A.foreground};font-family:${A.fontFamily};min-width:240px;padding:20px 22px;`,V=`background:${g.errorMessage?A.errorAccent:g.agentState==="idle"?"rgba(148,163,184,0.6)":A.accent};border-radius:50%;height:10px;width:10px;`,I=[];if(g.controls.canStart)I.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;">${c(g.labels.startCall)}</button>`);if(g.controls.canMute)I.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;">${c(g.labels.mute)}</button>`);if(g.controls.canEnd)I.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;">${c(g.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${g.agentState}" class="${c(g.classes.container)}" style="${C}">
1
+ (()=>{var{defineProperty:j,getOwnPropertyNames:_0,getOwnPropertyDescriptor:c0}=Object,R0=Object.prototype.hasOwnProperty;function $0(g){return this[g]}var I0=(g)=>{var A=(x??=new WeakMap).get(g),C;if(A)return A;if(A=j({},"__esModule",{value:!0}),g&&typeof g==="object"||typeof g==="function"){for(var V of _0(g))if(!R0.call(A,V))j(A,V,{get:$0.bind(g,V),enumerable:!(C=c0(g,V))||C.enumerable})}return x.set(g,A),A},x;var H0=(g)=>g;function U0(g,A){this[g]=H0.bind(null,A)}var L0=(g,A)=>{for(var C in A)j(g,C,{get:A[C],enumerable:!0,configurable:!0,set:U0.bind(A,C)})};var o0={};L0(o0,{mount:()=>A0,default:()=>m0,VOICE_EMBED_VERSION:()=>V0});var G0=(g)=>{if(typeof g!=="string")return g;return document.querySelector(g)},W0=(g,A,C,V)=>{let c=A??g.getAttribute("hx-get")??"";if(!c)return"";let T=new URL(c,window.location.origin);if(V)T.searchParams.set(C,V);else T.searchParams.delete(C);return`${T.pathname}${T.search}${T.hash}`},k=(g,A)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let C=G0(A.element);if(!C)return()=>{};let V=A.eventName??"voice-refresh",c=A.sessionQueryParam??"sessionId",T=()=>{let H=window,_=W0(C,A.route,c,g.sessionId);if(_)C.setAttribute("hx-get",_);H.htmx?.process?.(C),H.htmx?.trigger?.(C,V)},L=g.subscribe(T);return T(),()=>{L()}};var y0=(g)=>Math.max(-1,Math.min(1,g)),w0=(g)=>{let A=new Int16Array(g.length);for(let C=0;C<g.length;C+=1){let V=y0(g[C]??0);A[C]=V<0?V*32768:V*32767}return new Uint8Array(A.buffer)},D0=(g)=>{let A=g instanceof Uint8Array?g:new Uint8Array(g);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 V=0;for(let c of C){let T=c/32768;V+=T*T}return Math.min(1,Math.max(0,Math.sqrt(V/C.length)*5.5))},S0=(g,A,C)=>{if(A===C)return g;let V=A/C,c=Math.round(g.length/V),T=new Float32Array(c),L=0,H=0;while(L<T.length){let _=Math.round((L+1)*V),W=0,I=0;for(let y=H;y<_&&y<g.length;y+=1)W+=g[y]??0,I+=1;T[L]=I>0?W/I:0,L+=1,H=_}return T},F=(g)=>{let A=null,C=null,V=null,c=null;return{start:async()=>{if(!g.stream&&(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia))throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let H=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!H)throw Error("Browser microphone capture requires AudioContext support.");if(c=g.stream??await navigator.mediaDevices.getUserMedia({audio:{autoGainControl:!0,channelCount:g.channelCount??1,echoCancellation:!0,noiseSuppression:!0}}),A=new H,A.state==="suspended")await A.resume();C=A.createMediaStreamSource(c),V=A.createScriptProcessor(4096,1,1),V.onaudioprocess=(_)=>{let W=_.inputBuffer.getChannelData(0),I=S0(W,A?.sampleRate??48000,g.sampleRateHz??16000),y=w0(I);g.onLevel?.(D0(y)),g.onAudio(y)},C.connect(V),V.connect(A.destination)},stop:()=>{V?.disconnect(),C?.disconnect(),c?.getTracks().forEach((H)=>H.stop()),A?.close(),g.onLevel?.(0),A=null,c=null,V=null,C=null}}};var B=(g)=>{if(typeof g==="string"&&g.trim())return g;if(g instanceof Error&&g.message.trim())return g.message;if(g&&typeof g==="object"){let A=g;for(let C of["message","reason","description"]){let V=A[C];if(typeof V==="string"&&V.trim())return V}if("error"in A)return B(A.error);if("cause"in A)return B(A.cause);try{return JSON.stringify(g)}catch{}}return"Unexpected error"},l=(g)=>{switch(g.type){case"audio":return{chunk:Uint8Array.from(atob(g.chunkBase64),(A)=>A.charCodeAt(0)),format:g.format,receivedAt:g.receivedAt,turnId:g.turnId,type:"audio"};case"assistant":return{text:g.text,turnId:g.turnId,type:"assistant"};case"assistant_delta":return{delta:g.delta,turnId:g.turnId,type:"assistant_delta"};case"complete":return{sessionId:g.sessionId,type:"complete"};case"connection":return{reconnect:g.reconnect,type:"connection"};case"call_lifecycle":return{event:g.event,sessionId:g.sessionId,type:"call_lifecycle"};case"error":return{message:B(g.message),type:"error"};case"final":return{transcript:g.transcript,type:"final"};case"partial":return{transcript:g.transcript,type:"partial"};case"replay":return{assistantTexts:g.assistantTexts,call:g.call,partial:g.partial,scenarioId:g.scenarioId,sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,status:g.status,turns:g.turns,type:"replay"};case"session":return{sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,scenarioId:g.scenarioId,status:g.status,type:"session"};case"turn":return{turn:g.turn,type:"turn"};default:return null}};var p0=Math.PI*2;var Z=(g,A,C,V)=>{g.push({code:C,message:V,severity:A})};var X0=(g)=>g.length===0?void 0:g.reduce((A,C)=>A+C,0)/g.length,b=(g)=>g.length===0?void 0:Math.max(...g);var w=(g,A)=>{let C=g[A];return typeof C==="number"&&Number.isFinite(C)?C:void 0},z=(g,A)=>{let C=g[A];return typeof C==="boolean"?C:void 0},S=(g,A)=>{let C=g[A];return typeof C==="string"?C:void 0},M=(g)=>String(g.id??S(g,"ssrc")??w(g,"ssrc")??S(g,"trackIdentifier")??S(g,"mid")??"unknown"),f=(g)=>g===void 0?void 0:g*1000;var h0=(g)=>{let A={};for(let[C,V]of Object.entries(g))if(V===null||typeof V==="boolean"||typeof V==="number"||typeof V==="string")A[C]=V;return A};var d=(g={})=>{let A=g.stats??[],C=[],V=A.filter((R)=>R.type==="inbound-rtp"&&S(R,"kind")!=="video"),c=A.filter((R)=>R.type==="outbound-rtp"&&S(R,"kind")!=="video"),T=A.filter((R)=>R.type==="candidate-pair"),L=A.filter((R)=>(R.type==="track"||R.type==="media-source")&&S(R,"kind")==="audio"),H=T.filter((R)=>z(R,"selected")===!0||z(R,"nominated")===!0||S(R,"state")==="succeeded").length,_=L.filter((R)=>S(R,"readyState")!=="ended"&&S(R,"trackState")!=="ended"&&z(R,"ended")!==!0).length,W=L.filter((R)=>S(R,"readyState")==="ended"||S(R,"trackState")==="ended"||z(R,"ended")===!0).length,I=V.reduce((R,D)=>R+(w(D,"packetsReceived")??0),0),y=c.reduce((R,D)=>R+(w(D,"packetsSent")??0),0),$=[...V,...c].reduce((R,D)=>R+Math.max(0,w(D,"packetsLost")??0),0),X=I+$,G=X===0?0:$/X,P=V.reduce((R,D)=>R+(w(D,"bytesReceived")??0),0),h=c.reduce((R,D)=>R+(w(D,"bytesSent")??0),0),O=b(T.map((R)=>f(w(R,"currentRoundTripTime")??w(R,"roundTripTime"))).filter((R)=>R!==void 0)),J=b([...V,...c].map((R)=>f(w(R,"jitter"))).filter((R)=>R!==void 0)),E=b(V.map((R)=>{let D=w(R,"jitterBufferDelay"),U=w(R,"jitterBufferEmittedCount");return D!==void 0&&U!==void 0&&U>0?D/U*1000:void 0}).filter((R)=>R!==void 0)),N=L.map((R)=>w(R,"audioLevel")).filter((R)=>R!==void 0);if(g.requireConnectedCandidatePair&&T.length>0&&H===0)Z(C,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(g.requireLiveAudioTrack&&_===0)Z(C,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(g.maxPacketLossRatio!==void 0&&G>g.maxPacketLossRatio)Z(C,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(G)} above ${String(g.maxPacketLossRatio)}.`);if(g.maxRoundTripTimeMs!==void 0&&O!==void 0&&O>g.maxRoundTripTimeMs)Z(C,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(O)}ms above ${String(g.maxRoundTripTimeMs)}ms.`);if(g.maxJitterMs!==void 0&&J!==void 0&&J>g.maxJitterMs)Z(C,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(J)}ms above ${String(g.maxJitterMs)}ms.`);return{activeCandidatePairs:H,audioLevelAverage:X0(N),bytesReceived:P,bytesSent:h,checkedAt:Date.now(),endedAudioTracks:W,inboundPackets:I,issues:C,jitterBufferDelayMs:E,jitterMs:J,liveAudioTracks:_,outboundPackets:y,packetLossRatio:G,packetsLost:$,roundTripTimeMs:O,status:C.some((R)=>R.severity==="error")?"fail":C.length>0?"warn":"pass",totalStats:A.length}},v=async(g)=>{return[...(await g.peerConnection.getStats(g.selector??null)).values()].map(h0)};var i=(g={})=>{let A=g.stats??[],C=g.previousStats??[],V=[],c=new Map(C.map(($)=>[M($),$])),L=A.filter(($)=>($.type==="inbound-rtp"||$.type==="outbound-rtp")&&S($,"kind")!=="video"&&S($,"mediaType")!=="video").map(($)=>{let X=$.type==="outbound-rtp"?"outbound":"inbound",G=X==="outbound"?"packetsSent":"packetsReceived",P=X==="outbound"?"bytesSent":"bytesReceived",h=c.get(M($)),O=w($,G),J=h?w(h,G):void 0,E=w($,P),N=h?w(h,P):void 0,R=$.timestamp!==void 0&&h?.timestamp!==void 0?$.timestamp-h.timestamp:void 0;return{bytesDelta:E!==void 0&&N!==void 0?E-N:void 0,currentPackets:O,direction:X,id:M($),packetDelta:O!==void 0&&J!==void 0?O-J:void 0,previousPackets:J,timeDeltaMs:R}}),H=L.filter(($)=>$.direction==="inbound"),_=L.filter(($)=>$.direction==="outbound"),W=b(L.map(($)=>$.timeDeltaMs).filter(($)=>$!==void 0)),I=H.filter(($)=>g.maxInboundPacketStallMs!==void 0&&$.timeDeltaMs!==void 0&&$.timeDeltaMs>=g.maxInboundPacketStallMs&&$.packetDelta!==void 0&&$.packetDelta<=0).length,y=_.filter(($)=>g.maxOutboundPacketStallMs!==void 0&&$.timeDeltaMs!==void 0&&$.timeDeltaMs>=g.maxOutboundPacketStallMs&&$.packetDelta!==void 0&&$.packetDelta<=0).length;if(g.requireInboundAudio&&H.length===0)Z(V,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(g.requireOutboundAudio&&_.length===0)Z(V,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(g.maxGapMs!==void 0&&W!==void 0&&W>g.maxGapMs)Z(V,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(W)}ms above ${String(g.maxGapMs)}ms.`);if(I>0)Z(V,"error","media.webrtc_inbound_stalled",`${String(I)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(y>0)Z(V,"error","media.webrtc_outbound_stalled",`${String(y)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:H.length,issues:V,maxObservedGapMs:W,outboundAudioStreams:_.length,stalledInboundStreams:I,stalledOutboundStreams:y,status:V.some(($)=>$.severity==="error")?"fail":V.length>0?"warn":"pass",streams:L,totalStats:A.length}};var O0="/api/voice/browser-media",J0=5000,Y0=async(g)=>g.peerConnection??await g.getPeerConnection?.()??null,Z0=async(g,A)=>{let C=A.fetch??globalThis.fetch;if(!C)return;await C(A.path??O0,{body:JSON.stringify(g),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},n=(g)=>{let A=null,C=[],V=async()=>{let L=await Y0(g);if(!L)return;let H=await v({peerConnection:L}),_=d({...g,stats:H}),W=g.continuity===!1?void 0:i({...g.continuity,previousStats:C,stats:H}),I={at:Date.now(),continuity:W,report:_,scenarioId:g.getScenarioId?.()??null,sessionId:g.getSessionId?.()??null};return C=H,g.onReport?.(I),await Z0(I,g),I},c=()=>{V().catch((L)=>{g.onError?.(L)})},T=()=>{if(A)clearInterval(A),A=null};return{close:T,reportOnce:V,stop:T,start:()=>{if(A)return;c(),A=setInterval(c,g.intervalMs??J0)}}};var Q0=(g,A,C)=>Math.min(C,A*2**(Math.max(1,g)-1));var q=()=>{},P0=()=>q,K0={callControl:q,close:q,endTurn:q,send:q,sendAudio:q,simulateDisconnect:q,subscribe:P0,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",start:()=>{}},q0=()=>crypto.randomUUID(),E0=(g,A,C)=>{let{hostname:V,port:c,protocol:T}=window.location,L=T==="https:"?"wss:":"ws:",H=c?`:${c}`:"",_=new URL(`${L}//${V}${H}${g}`);if(_.searchParams.set("sessionId",A),C)_.searchParams.set("scenarioId",C);return _.toString()},N0=(g)=>{if(!g||typeof g!=="object"||!("type"in g))return!1;switch(g.type){case"audio":case"assistant":case"call_lifecycle":case"complete":case"connection":case"error":case"final":case"partial":case"pong":case"replay":case"session":case"turn":return!0;default:return!1}},z0=(g)=>{if(typeof g.data!=="string")return null;try{let A=JSON.parse(g.data);return N0(A)?A:null}catch{return null}},o=(g,A={})=>{if(typeof window>"u")return K0;let C=new Set,V=A.reconnect!==!1,c=A.maxReconnectAttempts??15,T=A.reconnectMaxDelayMs??8000,L=A.pingInterval??30000,H=(U)=>Q0(U,500,T),_={isConnected:!1,pendingMessages:[],scenarioId:A.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:A.sessionId??q0(),ws:null},W=(U)=>{C.forEach((Y)=>Y(U))},I=()=>{if(_.pingInterval)clearInterval(_.pingInterval),_.pingInterval=null;if(_.reconnectTimeout)clearTimeout(_.reconnectTimeout),_.reconnectTimeout=null},y=()=>{if(_.ws?.readyState!==1)return;while(_.pendingMessages.length>0){let U=_.pendingMessages.shift();if(U!==void 0)_.ws.send(U)}},$=()=>{_.reconnectAttempts+=1;let U=H(_.reconnectAttempts),Y=Date.now()+U;W({reconnect:{attempts:_.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:c,nextAttemptAt:Y,status:"reconnecting"},type:"connection"}),_.reconnectTimeout=setTimeout(()=>{if(_.reconnectAttempts>c){W({reconnect:{attempts:_.reconnectAttempts,maxAttempts:c,status:"exhausted"},type:"connection"});return}X()},U)},X=()=>{let U=new WebSocket(E0(g,_.sessionId,_.scenarioId));U.binaryType="arraybuffer",U.onopen=()=>{let Y=_.reconnectAttempts>0;if(_.isConnected=!0,y(),Y)W({reconnect:{attempts:_.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:c,status:"resumed"},type:"connection"}),_.reconnectAttempts=0;C.forEach((K)=>K({scenarioId:_.scenarioId??void 0,sessionId:_.sessionId,status:"active",type:"session"})),_.pingInterval=setInterval(()=>{if(U.readyState===1)U.send(JSON.stringify({type:"ping"}))},L)},U.onmessage=(Y)=>{let K=z0(Y);if(!K)return;if(K.type==="session")_.sessionId=K.sessionId,_.scenarioId=K.scenarioId??_.scenarioId;C.forEach((T0)=>T0(K))},U.onclose=(Y)=>{if(_.isConnected=!1,I(),V&&Y.code!==1000&&_.reconnectAttempts<c)$();else if(V&&Y.code!==1000)W({reconnect:{attempts:_.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:c,status:"exhausted"},type:"connection"})},_.ws=U},G=(U)=>{if(_.ws?.readyState===1){_.ws.send(U);return}_.pendingMessages.push(U)},P=(U)=>{G(JSON.stringify(U))},h=(U={})=>{if(U.sessionId)_.sessionId=U.sessionId;if(U.scenarioId)_.scenarioId=U.scenarioId;P({scenarioId:_.scenarioId??void 0,sessionId:_.sessionId,type:"start"})},O=(U)=>{G(U)},J=()=>{P({type:"end_turn"})},E=(U)=>{P({...U,type:"call_control"})},N=()=>{if(I(),_.ws)_.ws.close(1000),_.ws=null;_.isConnected=!1,C.clear()},R=()=>{if(_.ws?.readyState===1)_.ws.close(4000,"absolutejs-voice-reconnect-proof")},D=(U)=>{return C.add(U),()=>{C.delete(U)}};return X(),{callControl:E,close:N,endTurn:J,send:P,sendAudio:O,simulateDisconnect:R,start:h,subscribe:D,getReadyState:()=>_.ws?.readyState??3,getScenarioId:()=>_.scenarioId??"",getSessionId:()=>_.sessionId}};var b0=()=>({attempts:0,maxAttempts:0,status:"idle"}),j0=(g,A)=>{let C=A.trim().replace(/\s+/g," ");if(!C)return g;if(!g)return C;if(g===C||g.endsWith(C))return g;if(C.includes(g))return C;return`${g} ${C}`},B0=(g,A)=>{let C=A.trim().replace(/\s+/g," ");if(!g)return C;if(!C||g.endsWith(C))return g;return`${g} ${C}`},M0=()=>({assistantAudio:[],assistantStreamingText:"",assistantTexts:[],call:null,error:null,isConnected:!1,partial:"",reconnect:b0(),scenarioId:null,sessionId:null,sessionMetadata:null,status:"idle",turns:[]}),m=()=>{let g=M0(),A="",C=new Set,V=()=>{C.forEach((T)=>T())};return{dispatch:(T)=>{switch(T.type){case"audio":g={...g,assistantAudio:[...g.assistantAudio,{chunk:T.chunk,format:T.format,receivedAt:T.receivedAt,turnId:T.turnId}]};break;case"assistant":g={...g,assistantStreamingText:"",assistantTexts:[...g.assistantTexts,T.text]};break;case"assistant_delta":g={...g,assistantStreamingText:`${g.assistantStreamingText}${T.delta}`};break;case"complete":g={...g,sessionId:T.sessionId,status:"completed"};break;case"call_lifecycle":g={...g,call:{...g.call,disposition:T.event.type==="end"?T.event.disposition:g.call?.disposition,endedAt:T.event.type==="end"?T.event.at:g.call?.endedAt,events:[...g.call?.events??[],T.event],lastEventAt:T.event.at,startedAt:g.call?.startedAt??T.event.at},sessionId:T.sessionId};break;case"connected":g={...g,isConnected:!0,reconnect:g.reconnect.status==="reconnecting"?{...g.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:g.reconnect};break;case"connection":g={...g,reconnect:T.reconnect};break;case"disconnected":g={...g,isConnected:!1};break;case"error":g={...g,error:T.message};break;case"final":A=j0(A,T.transcript.text),g={...g,partial:A};break;case"partial":g={...g,partial:B0(A,T.transcript.text)};break;case"replay":A=T.partial,g={...g,assistantStreamingText:"",assistantTexts:[...T.assistantTexts],call:T.call??null,error:null,isConnected:T.status==="active",partial:T.partial,reconnect:g.reconnect.status==="reconnecting"?{...g.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:g.reconnect,scenarioId:T.scenarioId??g.scenarioId,sessionId:T.sessionId,sessionMetadata:T.sessionMetadata??g.sessionMetadata,status:T.status,turns:[...T.turns]};break;case"session":g={...g,error:null,scenarioId:T.scenarioId??g.scenarioId,isConnected:T.status==="active",sessionId:T.sessionId,sessionMetadata:T.sessionMetadata??g.sessionMetadata,status:T.status};break;case"turn":A="",g={...g,partial:"",turns:[...g.turns,T.turn]};break}V()},getServerSnapshot:()=>g,getSnapshot:()=>g,subscribe:(T)=>{return C.add(T),()=>{C.delete(T)}}}};var r=(g,A={})=>{let C=o(g,A),V=m(),c=A.browserMedia&&typeof window<"u"?n({...A.browserMedia,getScenarioId:()=>A.browserMedia?A.browserMedia.getScenarioId?.()??C.getScenarioId():C.getScenarioId(),getSessionId:()=>A.browserMedia?A.browserMedia.getSessionId?.()??C.getSessionId():C.getSessionId()}):null,T=new Set,L=(I)=>Promise.resolve().then(()=>{if(!I?.sessionId&&!I?.scenarioId)return;C.start(I),c?.start()}),H=()=>{T.forEach((I)=>I())},_=()=>{if(!A.reconnectReportPath||typeof fetch>"u")return;let I=V.getSnapshot(),y=JSON.stringify({at:Date.now(),reconnect:I.reconnect,scenarioId:I.scenarioId,sessionId:C.getSessionId(),turnIds:I.turns.map(($)=>$.id)});fetch(A.reconnectReportPath,{body:y,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},W=C.subscribe((I)=>{let y=l(I);if(y){if(V.dispatch(y),I.type==="connection")_();H()}});return{start:L,get assistantAudio(){return V.getSnapshot().assistantAudio},get assistantTexts(){return V.getSnapshot().assistantTexts},get assistantStreamingText(){return V.getSnapshot().assistantStreamingText},get call(){return V.getSnapshot().call},callControl(I){C.callControl(I)},close(){W(),c?.close(),C.close(),V.dispatch({type:"disconnected"}),H()},endTurn(){C.endTurn()},get error(){return V.getSnapshot().error},getServerSnapshot(){return V.getServerSnapshot()},getSnapshot(){return V.getSnapshot()},get isConnected(){return V.getSnapshot().isConnected},get partial(){return V.getSnapshot().partial},get reconnect(){return V.getSnapshot().reconnect},get scenarioId(){return V.getSnapshot().scenarioId},sendAudio(I){C.sendAudio(I)},get sessionId(){return C.getSessionId()},get sessionMetadata(){return V.getSnapshot().sessionMetadata},simulateDisconnect(){C.simulateDisconnect()},get status(){return V.getSnapshot().status},subscribe(I){return T.add(I),()=>{T.delete(I)}},get turns(){return V.getSnapshot().turns}}};var u=(g)=>{if(!g||g.enabled===!1)return;return{enabled:!0,maxGain:g.maxGain??3,noiseGateAttenuation:g.noiseGateAttenuation??0.15,noiseGateThreshold:g.noiseGateThreshold??0.006,targetLevel:g.targetLevel??0.08}};var x0={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}},k0={"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 s=(g)=>{let A=g?.profile??"fast",C=g?.qualityProfile??"general",V=x0[A],c=k0[C],T=g?.silenceMs??c.silenceMs??V.silenceMs;return{profile:A,qualityProfile:C,minSilenceMs:Math.min(T,g?.minSilenceMs??c.minSilenceMs??V.minSilenceMs),silenceMs:T,speechThreshold:g?.speechThreshold??c.speechThreshold??V.speechThreshold,transcriptStabilityMs:g?.transcriptStabilityMs??c.transcriptStabilityMs??V.transcriptStabilityMs}};var F0={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"}}},a=(g="default")=>{let A=F0[g];return{audioConditioning:u(A.audioConditioning),capture:{channelCount:A.capture?.channelCount??1,sampleRateHz:A.capture?.sampleRateHz??16000},connection:{...A.connection},name:g,sttLifecycle:A.sttLifecycle??"continuous",turnDetection:s(A.turnDetection)}};var l0=(g)=>({assistantAudio:[...g.assistantAudio],assistantStreamingText:g.assistantStreamingText,assistantTexts:[...g.assistantTexts],call:g.call,error:g.error,isConnected:g.isConnected,isRecording:!1,partial:g.partial,reconnect:g.reconnect,recordingError:null,sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,scenarioId:g.scenarioId,status:g.status,turns:[...g.turns]}),p=(g,A={})=>{let C=a(A.preset),V=r(g,{...C.connection,...A.connection}),c=null,T=l0(V),L=new Set,H=()=>{for(let G of L)G()},_=()=>{if(T={...T,assistantAudio:[...V.assistantAudio],assistantStreamingText:V.assistantStreamingText,assistantTexts:[...V.assistantTexts],call:V.call,error:V.error,isConnected:V.isConnected,partial:V.partial,reconnect:V.reconnect,sessionId:V.sessionId,sessionMetadata:V.sessionMetadata,scenarioId:V.scenarioId,status:V.status,turns:[...V.turns]},A.autoStopOnComplete!==!1&&T.status==="completed"&&T.isRecording)c?.stop(),c=null,T={...T,isRecording:!1};H()},W=V.subscribe(_);_();let I=()=>{if(c)return c;return c=F({channelCount:A.capture?.channelCount??C.capture.channelCount,onLevel:A.capture?.onLevel,onAudio:(G)=>{if(A.capture?.onAudio){A.capture.onAudio(G,V.sendAudio);return}V.sendAudio(G)},sampleRateHz:A.capture?.sampleRateHz??C.capture.sampleRateHz,...A.capture?.stream?{stream:A.capture.stream}:{}}),c},y=()=>{c?.stop(),c=null,T={...T,isRecording:!1},H()},$=async()=>{if(T.isRecording)return;try{T={...T,recordingError:null},H(),await I().start(),T={...T,isRecording:!0},H()}catch(G){throw c=null,T={...T,isRecording:!1,recordingError:G instanceof Error?G.message:String(G)},H(),G}};return{close:()=>{W(),y(),V.close()},startRecording:$,stopRecording:y,get assistantAudio(){return T.assistantAudio},get assistantTexts(){return T.assistantTexts},get assistantStreamingText(){return T.assistantStreamingText},bindHTMX(G){return k(V,G)},get call(){return T.call},callControl:(G)=>V.callControl(G),endTurn:()=>V.endTurn(),get error(){return T.error},getServerSnapshot:()=>T,getSnapshot:()=>T,get isConnected(){return T.isConnected},get isRecording(){return T.isRecording},get partial(){return T.partial},get reconnect(){return T.reconnect},get recordingError(){return T.recordingError},get scenarioId(){return T.scenarioId},sendAudio:(G)=>V.sendAudio(G),get sessionId(){return T.sessionId},get sessionMetadata(){return T.sessionMetadata},simulateDisconnect:()=>V.simulateDisconnect(),get status(){return T.status},subscribe:(G)=>{return L.add(G),()=>{L.delete(G)}},toggleRecording:async()=>{if(T.isRecording){y();return}await $()},get turns(){return T.turns}}};var Q=(g)=>String(g).replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;");var t=(g)=>{if(!g.isConnected)return"idle";if(g.isPlaying)return"speaking";if(g.isRecording&&g.hasActivePartial)return"listening";if(g.isRecording)return"listening";if(g.lastTranscriptAt&&!g.lastAssistantAt)return"thinking";if(g.lastTranscriptAt&&g.lastAssistantAt&&g.lastTranscriptAt>g.lastAssistantAt)return"thinking";return"idle"};var f0={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},d0={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},v0=(g,A)=>{switch(g){case"listening":return A.listening;case"speaking":return A.speaking;case"thinking":return A.thinking;case"idle":return A.idle}},e=(g)=>{let A={...f0,...g.theme},C={...d0,...g.labels},V=g.state.assistantAudio.at(-1)?.receivedAt,c=g.state.turns.at(-1)?.committedAt,T=t({hasActivePartial:g.state.partial.length>0,isConnected:g.state.isConnected,isPlaying:!1,isRecording:g.state.isRecording,lastAssistantAt:V,lastTranscriptAt:c}),L=!g.state.isConnected&&g.state.status!=="idle"&&!g.state.error,H=g.state.error?"Error":L?C.connecting:g.state.status==="completed"?C.callEnded:v0(T,C);return{agentState:T,classes:{container:`absolute-voice-widget absolute-voice-widget--${T}`,dot:`absolute-voice-widget__dot${g.state.error?" absolute-voice-widget__dot--error":""}`},controls:{canEnd:g.state.isConnected,canMute:g.state.isRecording,canStart:!g.state.isRecording&&g.state.status!=="completed"},errorMessage:g.state.error??void 0,labels:C,partial:g.state.partial||void 0,statusLabel:H,theme:A,title:g.title??"Voice"}},i0=(g)=>typeof g==="number"?`${g}px`:g,g0=(g)=>{let A=g.theme,C=`background:${A.background};border-radius:${i0(A.radius)};color:${A.foreground};font-family:${A.fontFamily};min-width:240px;padding:20px 22px;`,V=`background:${g.errorMessage?A.errorAccent:g.agentState==="idle"?"rgba(148,163,184,0.6)":A.accent};border-radius:50%;height:10px;width:10px;`,c=[];if(g.controls.canStart)c.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;">${Q(g.labels.startCall)}</button>`);if(g.controls.canMute)c.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;">${Q(g.labels.mute)}</button>`);if(g.controls.canEnd)c.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;">${Q(g.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${g.agentState}" class="${Q(g.classes.container)}" style="${C}">
2
2
  <div style="align-items:center;display:flex;gap:10px;margin-bottom:12px;">
3
- <span aria-hidden="true" class="${c(g.classes.dot)}" style="${V}"></span>
4
- <strong style="font-size:15px;">${c(g.title)}</strong>
5
- <span style="font-size:13px;margin-left:auto;opacity:0.7;">${c(g.statusLabel)}</span>
3
+ <span aria-hidden="true" class="${Q(g.classes.dot)}" style="${V}"></span>
4
+ <strong style="font-size:15px;">${Q(g.title)}</strong>
5
+ <span style="font-size:13px;margin-left:auto;opacity:0.7;">${Q(g.statusLabel)}</span>
6
6
  </div>
7
- ${g.partial?`<p style="font-size:13px;margin:8px 0 12px;opacity:0.85;word-break:break-word;">“${c(g.partial)}”</p>`:""}
8
- <div style="display:flex;gap:10px;">${I.join("")}</div>
9
- ${g.errorMessage?`<p style="color:${A.errorAccent};font-size:12px;margin-top:12px;">${c(g.errorMessage)}</p>`:""}
10
- </div>`};var u0=(g)=>{if(typeof g!=="string")return g;let A=document.querySelector(g);if(!A)throw Error(`AbsoluteVoice.mount: no element matches "${g}"`);return A},V0=(g,A={})=>{let C=u0(g),V=t(A.path??"/voice",A.controllerOptions),I=null,R=null,w=()=>{let $=g0({...A.labels!==void 0?{labels:A.labels}:{},state:{assistantAudio:V.assistantAudio,error:V.error,isConnected:V.isConnected,isRecording:V.isRecording,partial:V.partial,status:V.status,turns:V.turns},...A.theme!==void 0?{theme:A.theme}:{},...A.title!==void 0?{title:A.title}:{}});C.innerHTML=A0($);for(let X of C.querySelectorAll("button[data-action]")){let{action:G}=X.dataset;X.addEventListener("click",()=>{if(G==="start")V.startRecording();else if(G==="mute")V.stopRecording();else if(G==="end")V.close()})}if(V.error&&V.error!==I)I=V.error,A.onError?.(V.error);if(V.status!==R)R=V.status,A.onStatusChange?.(V.status)},L=V.subscribe(w);if(w(),A.autoStart)V.startRecording();return{controller:V,async end(){await V.close()},mute(){V.stopRecording()},async start(){await V.startRecording()},unmount(){L(),V.close(),C.innerHTML=""}}},C0="0.0.22-beta.516",R0={mount:V0,version:C0};if(typeof globalThis<"u")globalThis.AbsoluteVoice=R0;var s0=R0;})();
7
+ ${g.partial?`<p style="font-size:13px;margin:8px 0 12px;opacity:0.85;word-break:break-word;">“${Q(g.partial)}”</p>`:""}
8
+ <div style="display:flex;gap:10px;">${c.join("")}</div>
9
+ ${g.errorMessage?`<p style="color:${A.errorAccent};font-size:12px;margin-top:12px;">${Q(g.errorMessage)}</p>`:""}
10
+ </div>`};var n0=(g)=>{if(typeof g!=="string")return g;let A=document.querySelector(g);if(!A)throw Error(`AbsoluteVoice.mount: no element matches "${g}"`);return A},A0=(g,A={})=>{let C=n0(g),V=p(A.path??"/voice",A.controllerOptions),c=null,T=null,L=()=>{let _=e({...A.labels!==void 0?{labels:A.labels}:{},state:{assistantAudio:V.assistantAudio,error:V.error,isConnected:V.isConnected,isRecording:V.isRecording,partial:V.partial,status:V.status,turns:V.turns},...A.theme!==void 0?{theme:A.theme}:{},...A.title!==void 0?{title:A.title}:{}});C.innerHTML=g0(_);for(let W of C.querySelectorAll("button[data-action]")){let{action:I}=W.dataset;W.addEventListener("click",()=>{if(I==="start")V.startRecording();else if(I==="mute")V.stopRecording();else if(I==="end")V.close()})}if(V.error&&V.error!==c)c=V.error,A.onError?.(V.error);if(V.status!==T)T=V.status,A.onStatusChange?.(V.status)},H=V.subscribe(L);if(L(),A.autoStart)V.startRecording();return{controller:V,async end(){await V.close()},mute(){V.stopRecording()},async start(){await V.startRecording()},unmount(){H(),V.close(),C.innerHTML=""}}},V0="0.0.22-beta.516",C0={mount:A0,version:V0};if(typeof globalThis<"u")globalThis.AbsoluteVoice=C0;var m0=C0;})();
package/dist/index.js CHANGED
@@ -391,146 +391,25 @@ var resolveLogger = (logger) => ({
391
391
  ...logger
392
392
  });
393
393
 
394
- // src/core/turnDetection.ts
395
- var DEFAULT_SILENCE_MS = 700;
396
- var DEFAULT_SPEECH_THRESHOLD = 0.015;
397
- var DEFAULT_SEMANTIC_VETO_RECHECK_MS = 1200;
398
- var toUint8Array = (audio) => {
399
- if (audio instanceof ArrayBuffer) {
400
- return new Uint8Array(audio);
401
- }
402
- return new Uint8Array(audio.buffer, audio.byteOffset, audio.byteLength);
403
- };
404
- var measureAudioLevel = (audio) => {
405
- const bytes = toUint8Array(audio);
406
- if (bytes.byteLength < 2) {
407
- return 0;
408
- }
409
- const samples = new Int16Array(bytes.buffer, bytes.byteOffset, Math.floor(bytes.byteLength / 2));
410
- if (samples.length === 0) {
411
- return 0;
412
- }
413
- let sumSquares = 0;
414
- for (const sample of samples) {
415
- const normalized = sample / 32768;
416
- sumSquares += normalized * normalized;
417
- }
418
- return Math.sqrt(sumSquares / samples.length);
419
- };
420
- var normalizeText = (value) => value.trim().replace(/\s+/g, " ");
421
- var countWords = (value) => value.length > 0 ? value.split(" ").length : 0;
422
- var selectPreferredTranscriptText = (currentText, nextText) => {
423
- const current = normalizeText(currentText);
424
- const next = normalizeText(nextText);
425
- if (!current) {
426
- return next;
427
- }
428
- if (!next) {
429
- return current;
430
- }
431
- if (current === next || current.includes(next)) {
432
- return current;
433
- }
434
- if (next.includes(current)) {
435
- return next;
436
- }
437
- if (countWords(next) > countWords(current)) {
438
- return next;
439
- }
440
- if (countWords(next) === countWords(current) && next.length > current.length) {
441
- return next;
442
- }
443
- return current;
444
- };
445
- var mergeSequentialTranscriptText = (currentText, nextText) => {
446
- const current = normalizeText(currentText);
447
- const next = normalizeText(nextText);
448
- if (!current) {
449
- return next;
450
- }
451
- if (!next) {
452
- return current;
453
- }
454
- const currentWords = current.split(" ");
455
- const nextWords = next.split(" ");
456
- const maxOverlap = Math.min(currentWords.length, nextWords.length);
457
- for (let overlap = maxOverlap;overlap > 0; overlap -= 1) {
458
- const currentSuffix = currentWords.slice(-overlap).join(" ");
459
- const nextPrefix = nextWords.slice(0, overlap).join(" ");
460
- if (currentSuffix === nextPrefix) {
461
- return [...currentWords, ...nextWords.slice(overlap)].join(" ");
462
- }
463
- }
464
- return `${current} ${next}`.trim();
465
- };
466
- var countCommonPrefixWords = (currentText, nextText) => {
467
- const currentWords = normalizeText(currentText).split(" ").filter(Boolean);
468
- const nextWords = normalizeText(nextText).split(" ").filter(Boolean);
469
- const maxWords = Math.min(currentWords.length, nextWords.length);
470
- let count = 0;
471
- for (let index = 0;index < maxWords; index += 1) {
472
- if (currentWords[index] !== nextWords[index]) {
473
- break;
474
- }
475
- count += 1;
476
- }
477
- return count;
478
- };
479
- var mergeTranscriptTexts = (transcripts) => {
480
- const merged = [];
481
- for (const transcript of transcripts) {
482
- const nextText = normalizeText(transcript.text);
483
- if (!nextText) {
484
- continue;
485
- }
486
- const previous = merged.at(-1);
487
- if (!previous) {
488
- merged.push(nextText);
489
- continue;
490
- }
491
- if (nextText === previous || previous.includes(nextText)) {
492
- continue;
493
- }
494
- if (nextText.includes(previous)) {
495
- merged[merged.length - 1] = nextText;
496
- continue;
497
- }
498
- merged.push(nextText);
499
- }
500
- return merged.join(" ").trim();
501
- };
502
- var buildTurnText = (transcripts, partialText, options = {}) => {
503
- const finalText = mergeTranscriptTexts(transcripts);
504
- const nextPartial = normalizeText(partialText);
505
- const lastFinalEndedAtMs = [...transcripts].reverse().find((transcript) => typeof transcript.endedAtMs === "number")?.endedAtMs;
506
- if (finalText && nextPartial && typeof lastFinalEndedAtMs === "number" && typeof options.partialStartedAtMs === "number" && options.partialStartedAtMs - lastFinalEndedAtMs >= 250 && countCommonPrefixWords(finalText, nextPartial) === 0) {
507
- return mergeSequentialTranscriptText(finalText, nextPartial);
508
- }
509
- return selectPreferredTranscriptText(finalText, nextPartial);
510
- };
511
-
512
394
  // src/core/turnProfiles.ts
513
395
  var TURN_PROFILE_DEFAULTS = {
514
396
  balanced: {
515
397
  qualityProfile: "general",
516
- semanticVetoMaxMs: 0,
517
- semanticVetoRecheckMs: DEFAULT_SEMANTIC_VETO_RECHECK_MS,
398
+ minSilenceMs: 400,
518
399
  silenceMs: 1400,
519
400
  speechThreshold: 0.012,
520
401
  transcriptStabilityMs: 1000
521
402
  },
522
403
  fast: {
523
404
  qualityProfile: "general",
524
- semanticVetoMaxMs: 0,
525
- semanticVetoRecheckMs: DEFAULT_SEMANTIC_VETO_RECHECK_MS,
405
+ minSilenceMs: 300,
526
406
  silenceMs: 700,
527
407
  speechThreshold: 0.015,
528
408
  transcriptStabilityMs: 450
529
409
  },
530
410
  "long-form": {
531
411
  qualityProfile: "general",
532
- semanticVetoMaxMs: 0,
533
- semanticVetoRecheckMs: DEFAULT_SEMANTIC_VETO_RECHECK_MS,
412
+ minSilenceMs: 600,
534
413
  silenceMs: 2200,
535
414
  speechThreshold: 0.01,
536
415
  transcriptStabilityMs: 1500
@@ -561,12 +440,12 @@ var resolveTurnDetectionConfig = (config) => {
561
440
  const qualityProfile = config?.qualityProfile ?? DEFAULT_QUALITY_PROFILE;
562
441
  const preset = TURN_PROFILE_DEFAULTS[profile];
563
442
  const quality = QUALITY_PROFILE_DEFAULTS[qualityProfile];
443
+ const silenceMs = config?.silenceMs ?? quality.silenceMs ?? preset.silenceMs;
564
444
  return {
565
445
  profile,
566
446
  qualityProfile,
567
- semanticVetoMaxMs: config?.semanticVetoMaxMs ?? preset.semanticVetoMaxMs,
568
- semanticVetoRecheckMs: config?.semanticVetoRecheckMs ?? preset.semanticVetoRecheckMs,
569
- silenceMs: config?.silenceMs ?? quality.silenceMs ?? preset.silenceMs,
447
+ minSilenceMs: Math.min(silenceMs, config?.minSilenceMs ?? quality.minSilenceMs ?? preset.minSilenceMs),
448
+ silenceMs,
570
449
  speechThreshold: config?.speechThreshold ?? quality.speechThreshold ?? preset.speechThreshold,
571
450
  transcriptStabilityMs: config?.transcriptStabilityMs ?? quality.transcriptStabilityMs ?? preset.transcriptStabilityMs
572
451
  };
@@ -3580,6 +3459,124 @@ var createVoiceTwilioRedirectHandoffAdapter = (options) => ({
3580
3459
  }
3581
3460
  });
3582
3461
 
3462
+ // src/core/turnDetection.ts
3463
+ var DEFAULT_SILENCE_MS = 700;
3464
+ var DEFAULT_SPEECH_THRESHOLD = 0.015;
3465
+ var DEFAULT_MIN_SILENCE_MS = 400;
3466
+ var toUint8Array = (audio) => {
3467
+ if (audio instanceof ArrayBuffer) {
3468
+ return new Uint8Array(audio);
3469
+ }
3470
+ return new Uint8Array(audio.buffer, audio.byteOffset, audio.byteLength);
3471
+ };
3472
+ var measureAudioLevel = (audio) => {
3473
+ const bytes = toUint8Array(audio);
3474
+ if (bytes.byteLength < 2) {
3475
+ return 0;
3476
+ }
3477
+ const samples = new Int16Array(bytes.buffer, bytes.byteOffset, Math.floor(bytes.byteLength / 2));
3478
+ if (samples.length === 0) {
3479
+ return 0;
3480
+ }
3481
+ let sumSquares = 0;
3482
+ for (const sample of samples) {
3483
+ const normalized = sample / 32768;
3484
+ sumSquares += normalized * normalized;
3485
+ }
3486
+ return Math.sqrt(sumSquares / samples.length);
3487
+ };
3488
+ var normalizeText = (value) => value.trim().replace(/\s+/g, " ");
3489
+ var countWords = (value) => value.length > 0 ? value.split(" ").length : 0;
3490
+ var selectPreferredTranscriptText = (currentText, nextText) => {
3491
+ const current = normalizeText(currentText);
3492
+ const next = normalizeText(nextText);
3493
+ if (!current) {
3494
+ return next;
3495
+ }
3496
+ if (!next) {
3497
+ return current;
3498
+ }
3499
+ if (current === next || current.includes(next)) {
3500
+ return current;
3501
+ }
3502
+ if (next.includes(current)) {
3503
+ return next;
3504
+ }
3505
+ if (countWords(next) > countWords(current)) {
3506
+ return next;
3507
+ }
3508
+ if (countWords(next) === countWords(current) && next.length > current.length) {
3509
+ return next;
3510
+ }
3511
+ return current;
3512
+ };
3513
+ var mergeSequentialTranscriptText = (currentText, nextText) => {
3514
+ const current = normalizeText(currentText);
3515
+ const next = normalizeText(nextText);
3516
+ if (!current) {
3517
+ return next;
3518
+ }
3519
+ if (!next) {
3520
+ return current;
3521
+ }
3522
+ const currentWords = current.split(" ");
3523
+ const nextWords = next.split(" ");
3524
+ const maxOverlap = Math.min(currentWords.length, nextWords.length);
3525
+ for (let overlap = maxOverlap;overlap > 0; overlap -= 1) {
3526
+ const currentSuffix = currentWords.slice(-overlap).join(" ");
3527
+ const nextPrefix = nextWords.slice(0, overlap).join(" ");
3528
+ if (currentSuffix === nextPrefix) {
3529
+ return [...currentWords, ...nextWords.slice(overlap)].join(" ");
3530
+ }
3531
+ }
3532
+ return `${current} ${next}`.trim();
3533
+ };
3534
+ var countCommonPrefixWords = (currentText, nextText) => {
3535
+ const currentWords = normalizeText(currentText).split(" ").filter(Boolean);
3536
+ const nextWords = normalizeText(nextText).split(" ").filter(Boolean);
3537
+ const maxWords = Math.min(currentWords.length, nextWords.length);
3538
+ let count = 0;
3539
+ for (let index = 0;index < maxWords; index += 1) {
3540
+ if (currentWords[index] !== nextWords[index]) {
3541
+ break;
3542
+ }
3543
+ count += 1;
3544
+ }
3545
+ return count;
3546
+ };
3547
+ var mergeTranscriptTexts = (transcripts) => {
3548
+ const merged = [];
3549
+ for (const transcript of transcripts) {
3550
+ const nextText = normalizeText(transcript.text);
3551
+ if (!nextText) {
3552
+ continue;
3553
+ }
3554
+ const previous = merged.at(-1);
3555
+ if (!previous) {
3556
+ merged.push(nextText);
3557
+ continue;
3558
+ }
3559
+ if (nextText === previous || previous.includes(nextText)) {
3560
+ continue;
3561
+ }
3562
+ if (nextText.includes(previous)) {
3563
+ merged[merged.length - 1] = nextText;
3564
+ continue;
3565
+ }
3566
+ merged.push(nextText);
3567
+ }
3568
+ return merged.join(" ").trim();
3569
+ };
3570
+ var buildTurnText = (transcripts, partialText, options = {}) => {
3571
+ const finalText = mergeTranscriptTexts(transcripts);
3572
+ const nextPartial = normalizeText(partialText);
3573
+ const lastFinalEndedAtMs = [...transcripts].reverse().find((transcript) => typeof transcript.endedAtMs === "number")?.endedAtMs;
3574
+ if (finalText && nextPartial && typeof lastFinalEndedAtMs === "number" && typeof options.partialStartedAtMs === "number" && options.partialStartedAtMs - lastFinalEndedAtMs >= 250 && countCommonPrefixWords(finalText, nextPartial) === 0) {
3575
+ return mergeSequentialTranscriptText(finalText, nextPartial);
3576
+ }
3577
+ return selectPreferredTranscriptText(finalText, nextPartial);
3578
+ };
3579
+
3583
3580
  // src/core/types.ts
3584
3581
  var ttsAdapterSessionCanCancel = (session) => typeof session.cancel === "function";
3585
3582
 
@@ -3926,14 +3923,22 @@ var createVoiceSession = (options) => {
3926
3923
  strategy: options.reconnect.strategy ?? "resume-last-turn",
3927
3924
  timeout: options.reconnect.timeout ?? DEFAULT_RECONNECT_TIMEOUT
3928
3925
  };
3926
+ const resolvedSilenceMs = options.turnDetection.silenceMs ?? DEFAULT_SILENCE_MS;
3929
3927
  const turnDetection = {
3930
- silenceMs: options.turnDetection.silenceMs ?? DEFAULT_SILENCE_MS,
3928
+ silenceMs: resolvedSilenceMs,
3929
+ minSilenceMs: Math.min(resolvedSilenceMs, options.turnDetection.minSilenceMs ?? DEFAULT_MIN_SILENCE_MS),
3931
3930
  speechThreshold: options.turnDetection.speechThreshold ?? DEFAULT_SPEECH_THRESHOLD,
3932
- transcriptStabilityMs: options.turnDetection.transcriptStabilityMs ?? DEFAULT_TRANSCRIPT_STABILITY_MS,
3933
- semanticVetoMaxMs: options.turnDetection.semanticVetoMaxMs ?? 0,
3934
- semanticVetoRecheckMs: options.turnDetection.semanticVetoRecheckMs ?? DEFAULT_SEMANTIC_VETO_RECHECK_MS
3931
+ transcriptStabilityMs: options.turnDetection.transcriptStabilityMs ?? DEFAULT_TRANSCRIPT_STABILITY_MS
3932
+ };
3933
+ let lastTurnCompleteConfidence = null;
3934
+ const adaptiveSilenceMs = () => {
3935
+ const { minSilenceMs, silenceMs } = turnDetection;
3936
+ if (lastTurnCompleteConfidence === null || silenceMs <= minSilenceMs) {
3937
+ return silenceMs;
3938
+ }
3939
+ const complete = Math.max(0, Math.min(1, lastTurnCompleteConfidence));
3940
+ return Math.round(minSilenceMs + (silenceMs - minSilenceMs) * (1 - complete));
3935
3941
  };
3936
- let semanticVetoElapsedMs = 0;
3937
3942
  const sttFallback = options.sttFallback ? {
3938
3943
  adapter: options.sttFallback.adapter,
3939
3944
  completionTimeoutMs: options.sttFallback.completionTimeoutMs ?? DEFAULT_FALLBACK_COMPLETION_TIMEOUT_MS,
@@ -4137,6 +4142,17 @@ var createVoiceSession = (options) => {
4137
4142
  pruneTurnAudio();
4138
4143
  return currentTurnAudio.map((audio) => audio.chunk);
4139
4144
  };
4145
+ const turnAudioInputFormat = recordingConfig?.userInputFormat ?? options.realtimeInputFormat ?? DEFAULT_REALTIME_FORMAT;
4146
+ const getTurnAudioForDetector = () => {
4147
+ if (!options.semanticTurnDetector || currentTurnAudio.length === 0) {
4148
+ return { turnAudio: undefined, turnAudioFormat: undefined };
4149
+ }
4150
+ const turnAudio = currentTurnAudio.map((audio) => {
4151
+ const c = audio.chunk;
4152
+ return c instanceof ArrayBuffer ? new Uint8Array(c) : new Uint8Array(c.buffer, c.byteOffset, c.byteLength);
4153
+ });
4154
+ return { turnAudio, turnAudioFormat: turnAudioInputFormat };
4155
+ };
4140
4156
  const clearSilenceTimer = () => {
4141
4157
  if (!silenceTimer) {
4142
4158
  return;
@@ -4455,46 +4471,8 @@ var createVoiceSession = (options) => {
4455
4471
  runScheduledCommit(reason);
4456
4472
  }, delayMs);
4457
4473
  };
4458
- const scheduleSilenceCommit = (delayMs = turnDetection.silenceMs, reset = true) => scheduleTurnCommit(delayMs, "silence", reset);
4459
- const shouldDeferSilenceCommit = async (reason) => {
4460
- if (reason !== "silence" || turnDetection.semanticVetoMaxMs <= 0 || !options.semanticTurnDetector || semanticVetoElapsedMs >= turnDetection.semanticVetoMaxMs) {
4461
- return false;
4462
- }
4463
- const session = await readSession();
4464
- const { partialText, transcripts } = session.currentTurn;
4465
- const userText = buildTurnText(transcripts, partialText, {
4466
- partialEndedAtMs: session.currentTurn.partialEndedAt,
4467
- partialStartedAtMs: session.currentTurn.partialStartedAt
4468
- });
4469
- if (!userText) {
4470
- return false;
4471
- }
4472
- const silenceMs = session.currentTurn.silenceStartedAt !== undefined ? Date.now() - session.currentTurn.silenceStartedAt : turnDetection.silenceMs;
4473
- let endOfTurn = true;
4474
- try {
4475
- const verdict = await Promise.resolve(options.semanticTurnDetector.evaluate({
4476
- lastFinalTranscript: transcripts.at(-1),
4477
- partialText,
4478
- silenceMs,
4479
- transcripts
4480
- }));
4481
- endOfTurn = verdict.endOfTurn;
4482
- } catch {
4483
- return false;
4484
- }
4485
- if (endOfTurn !== false) {
4486
- return false;
4487
- }
4488
- const remaining = turnDetection.semanticVetoMaxMs - semanticVetoElapsedMs;
4489
- const extendMs = Math.max(1, Math.min(turnDetection.semanticVetoRecheckMs, remaining));
4490
- semanticVetoElapsedMs += extendMs;
4491
- scheduleTurnCommit(extendMs, reason);
4492
- return true;
4493
- };
4474
+ const scheduleSilenceCommit = (delayMs = adaptiveSilenceMs(), reset = true) => scheduleTurnCommit(delayMs, "silence", reset);
4494
4475
  const runScheduledCommit = async (reason) => {
4495
- if (await shouldDeferSilenceCommit(reason)) {
4496
- return;
4497
- }
4498
4476
  await api.commitTurn(reason);
4499
4477
  };
4500
4478
  const requestTurnCommit = async (reason) => {
@@ -5234,7 +5212,7 @@ var createVoiceSession = (options) => {
5234
5212
  session2.lastActivityAt = Date.now();
5235
5213
  session2.status = "active";
5236
5214
  });
5237
- semanticVetoElapsedMs = 0;
5215
+ lastTurnCompleteConfidence = null;
5238
5216
  if (silenceTimer && pendingCommitReason === "vendor") {
5239
5217
  scheduleTurnCommit(getVendorCommitDelayMs(), "vendor");
5240
5218
  }
@@ -5261,8 +5239,15 @@ var createVoiceSession = (options) => {
5261
5239
  lastFinalTranscript: transcript,
5262
5240
  partialText: session.currentTurn.partialText,
5263
5241
  silenceMs: session.currentTurn.silenceStartedAt !== undefined ? Date.now() - session.currentTurn.silenceStartedAt : 0,
5264
- transcripts: session.currentTurn.transcripts
5242
+ transcripts: session.currentTurn.transcripts,
5243
+ ...getTurnAudioForDetector()
5265
5244
  }));
5245
+ if (typeof verdict.confidence === "number") {
5246
+ lastTurnCompleteConfidence = verdict.confidence;
5247
+ if (silenceTimer && pendingCommitReason === "silence") {
5248
+ scheduleSilenceCommit();
5249
+ }
5250
+ }
5266
5251
  if (verdict.endOfTurn) {
5267
5252
  clearSilenceTimer();
5268
5253
  await requestTurnCommit("vendor");
@@ -5958,7 +5943,7 @@ var createVoiceSession = (options) => {
5958
5943
  };
5959
5944
  const commitTurnInternal = async (reason = "manual") => {
5960
5945
  clearSilenceTimer();
5961
- semanticVetoElapsedMs = 0;
5946
+ lastTurnCompleteConfidence = null;
5962
5947
  backchannelDriver?.reset();
5963
5948
  amdLastTurnCommitAt = Date.now();
5964
5949
  const session = await readSession();
@@ -42561,16 +42546,18 @@ var createVoiceConfiguration = (configuration) => configuration;
42561
42546
  var DEFAULT_SPEECH_THRESHOLD2 = 0.015;
42562
42547
  var DEFAULT_SILENCE_MS2 = 700;
42563
42548
  var DEFAULT_TRANSCRIPT_STABILITY_MS2 = 200;
42564
- var DEFAULT_SEMANTIC_VETO_RECHECK_MS2 = 1200;
42565
- var resolveTurnDetection = (input) => ({
42566
- profile: input?.profile ?? "balanced",
42567
- qualityProfile: input?.qualityProfile ?? "general",
42568
- semanticVetoMaxMs: input?.semanticVetoMaxMs ?? 0,
42569
- semanticVetoRecheckMs: input?.semanticVetoRecheckMs ?? DEFAULT_SEMANTIC_VETO_RECHECK_MS2,
42570
- silenceMs: input?.silenceMs ?? DEFAULT_SILENCE_MS2,
42571
- speechThreshold: input?.speechThreshold ?? DEFAULT_SPEECH_THRESHOLD2,
42572
- transcriptStabilityMs: input?.transcriptStabilityMs ?? DEFAULT_TRANSCRIPT_STABILITY_MS2
42573
- });
42549
+ var DEFAULT_MIN_SILENCE_MS2 = 400;
42550
+ var resolveTurnDetection = (input) => {
42551
+ const silenceMs = input?.silenceMs ?? DEFAULT_SILENCE_MS2;
42552
+ return {
42553
+ profile: input?.profile ?? "balanced",
42554
+ qualityProfile: input?.qualityProfile ?? "general",
42555
+ minSilenceMs: Math.min(silenceMs, input?.minSilenceMs ?? DEFAULT_MIN_SILENCE_MS2),
42556
+ silenceMs,
42557
+ speechThreshold: input?.speechThreshold ?? DEFAULT_SPEECH_THRESHOLD2,
42558
+ transcriptStabilityMs: input?.transcriptStabilityMs ?? DEFAULT_TRANSCRIPT_STABILITY_MS2
42559
+ };
42560
+ };
42574
42561
  var resolveReconnect = (input) => ({
42575
42562
  maxAttempts: input?.maxAttempts ?? 3,
42576
42563
  strategy: input?.strategy ?? "resume-last-turn",