@absolutejs/voice 0.0.22-beta.516 → 0.0.22-beta.517

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.
@@ -245,6 +245,7 @@ var serverMessageToAction = (message) => {
245
245
  };
246
246
 
247
247
  // node_modules/@absolutejs/media/dist/index.js
248
+ var TAU = Math.PI * 2;
248
249
  var pushIssue = (issues, severity, code, message) => {
249
250
  issues.push({ code, message, severity });
250
251
  };
@@ -245,6 +245,7 @@ var serverMessageToAction = (message) => {
245
245
  };
246
246
 
247
247
  // node_modules/@absolutejs/media/dist/index.js
248
+ var TAU = Math.PI * 2;
248
249
  var pushIssue = (issues, severity, code, message) => {
249
250
  issues.push({ code, message, severity });
250
251
  };
@@ -1,10 +1,10 @@
1
- (()=>{var{defineProperty:a,getOwnPropertyNames:lc,getOwnPropertyDescriptor:ic}=Object,gc=Object.prototype.hasOwnProperty;function sc(c){return this[c]}var ec=(c)=>{var n=(W??=new WeakMap).get(c),l;if(n)return n;if(n=a({},"__esModule",{value:!0}),c&&typeof c==="object"||typeof c==="function"){for(var o of lc(c))if(!gc.call(n,o))a(n,o,{get:sc.bind(c,o),enumerable:!(l=ic(c,o))||l.enumerable})}return W.set(c,n),n},W;var Ac=(c)=>c;function dc(c,n){this[c]=Ac.bind(null,n)}var Cc=(c,n)=>{for(var l in n)a(c,l,{get:n[l],enumerable:!0,configurable:!0,set:dc.bind(n,l)})};var Yc={};Cc(Yc,{mount:()=>p,default:()=>qc,VOICE_EMBED_VERSION:()=>cc});var Tc=(c)=>{if(typeof c!=="string")return c;return document.querySelector(c)},tc=(c,n,l,o)=>{let i=n??c.getAttribute("hx-get")??"";if(!i)return"";let e=new URL(i,window.location.origin);if(o)e.searchParams.set(l,o);else e.searchParams.delete(l);return`${e.pathname}${e.search}${e.hash}`},$=(c,n)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let l=Tc(n.element);if(!l)return()=>{};let o=n.eventName??"voice-refresh",i=n.sessionQueryParam??"sessionId",e=()=>{let C=window,T=tc(l,n.route,i,c.sessionId);if(T)l.setAttribute("hx-get",T);C.htmx?.process?.(l),C.htmx?.trigger?.(l,o)},s=c.subscribe(e);return e(),()=>{s()}};var hc=(c)=>Math.max(-1,Math.min(1,c)),Sc=(c)=>{let n=new Int16Array(c.length);for(let l=0;l<c.length;l+=1){let o=hc(c[l]??0);n[l]=o<0?o*32768:o*32767}return new Uint8Array(n.buffer)},yc=(c)=>{let n=c instanceof Uint8Array?c:new Uint8Array(c);if(n.byteLength<2)return 0;let l=new Int16Array(n.buffer,n.byteOffset,Math.floor(n.byteLength/2));if(l.length===0)return 0;let o=0;for(let i of l){let e=i/32768;o+=e*e}return Math.min(1,Math.max(0,Math.sqrt(o/l.length)*5.5))},Rc=(c,n,l)=>{if(n===l)return c;let o=n/l,i=Math.round(c.length/o),e=new Float32Array(i),s=0,C=0;while(s<e.length){let T=Math.round((s+1)*o),S=0,d=0;for(let h=C;h<T&&h<c.length;h+=1)S+=c[h]??0,d+=1;e[s]=d>0?S/d:0,s+=1,C=T}return e},H=(c)=>{let n=null,l=null,o=null,i=null;return{start:async()=>{if(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia)throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let C=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!C)throw Error("Browser microphone capture requires AudioContext support.");i=await navigator.mediaDevices.getUserMedia({audio:{channelCount:c.channelCount??1}}),n=new C,l=n.createMediaStreamSource(i),o=n.createScriptProcessor(4096,1,1),o.onaudioprocess=(T)=>{let S=T.inputBuffer.getChannelData(0),d=Rc(S,n?.sampleRate??48000,c.sampleRateHz??16000),h=Sc(d);c.onLevel?.(yc(h)),c.onAudio(h)},l.connect(o),o.connect(n.destination)},stop:()=>{o?.disconnect(),l?.disconnect(),i?.getTracks().forEach((C)=>C.stop()),n?.close(),c.onLevel?.(0),n=null,i=null,o=null,l=null}}};var P=(c)=>{if(typeof c==="string"&&c.trim())return c;if(c instanceof Error&&c.message.trim())return c.message;if(c&&typeof c==="object"){let n=c;for(let l of["message","reason","description"]){let o=n[l];if(typeof o==="string"&&o.trim())return o}if("error"in n)return P(n.error);if("cause"in n)return P(n.cause);try{return JSON.stringify(c)}catch{}}return"Unexpected error"},G=(c)=>{switch(c.type){case"audio":return{chunk:Uint8Array.from(atob(c.chunkBase64),(n)=>n.charCodeAt(0)),format:c.format,receivedAt:c.receivedAt,turnId:c.turnId,type:"audio"};case"assistant":return{text:c.text,type:"assistant"};case"complete":return{sessionId:c.sessionId,type:"complete"};case"connection":return{reconnect:c.reconnect,type:"connection"};case"call_lifecycle":return{event:c.event,sessionId:c.sessionId,type:"call_lifecycle"};case"error":return{message:P(c.message),type:"error"};case"final":return{transcript:c.transcript,type:"final"};case"partial":return{transcript:c.transcript,type:"partial"};case"replay":return{assistantTexts:c.assistantTexts,call:c.call,partial:c.partial,scenarioId:c.scenarioId,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,status:c.status,turns:c.turns,type:"replay"};case"session":return{sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,scenarioId:c.scenarioId,status:c.status,type:"session"};case"turn":return{turn:c.turn,type:"turn"};default:return null}};var M=(c,n,l,o)=>{c.push({code:l,message:o,severity:n})};var Vc=(c)=>c.length===0?void 0:c.reduce((n,l)=>n+l,0)/c.length,E=(c)=>c.length===0?void 0:Math.max(...c);var R=(c,n)=>{let l=c[n];return typeof l==="number"&&Number.isFinite(l)?l:void 0},O=(c,n)=>{let l=c[n];return typeof l==="boolean"?l:void 0},V=(c,n)=>{let l=c[n];return typeof l==="string"?l:void 0},N=(c)=>String(c.id??V(c,"ssrc")??R(c,"ssrc")??V(c,"trackIdentifier")??V(c,"mid")??"unknown"),z=(c)=>c===void 0?void 0:c*1000;var Ic=(c)=>{let n={};for(let[l,o]of Object.entries(c))if(o===null||typeof o==="boolean"||typeof o==="number"||typeof o==="string")n[l]=o;return n};var X=(c={})=>{let n=c.stats??[],l=[],o=n.filter((g)=>g.type==="inbound-rtp"&&V(g,"kind")!=="video"),i=n.filter((g)=>g.type==="outbound-rtp"&&V(g,"kind")!=="video"),e=n.filter((g)=>g.type==="candidate-pair"),s=n.filter((g)=>(g.type==="track"||g.type==="media-source")&&V(g,"kind")==="audio"),C=e.filter((g)=>O(g,"selected")===!0||O(g,"nominated")===!0||V(g,"state")==="succeeded").length,T=s.filter((g)=>V(g,"readyState")!=="ended"&&V(g,"trackState")!=="ended"&&O(g,"ended")!==!0).length,S=s.filter((g)=>V(g,"readyState")==="ended"||V(g,"trackState")==="ended"||O(g,"ended")===!0).length,d=o.reduce((g,y)=>g+(R(y,"packetsReceived")??0),0),h=i.reduce((g,y)=>g+(R(y,"packetsSent")??0),0),A=[...o,...i].reduce((g,y)=>g+Math.max(0,R(y,"packetsLost")??0),0),I=d+A,t=I===0?0:A/I,x=o.reduce((g,y)=>g+(R(y,"bytesReceived")??0),0),w=i.reduce((g,y)=>g+(R(y,"bytesSent")??0),0),b=E(e.map((g)=>z(R(g,"currentRoundTripTime")??R(g,"roundTripTime"))).filter((g)=>g!==void 0)),f=E([...o,...i].map((g)=>z(R(g,"jitter"))).filter((g)=>g!==void 0)),D=E(o.map((g)=>{let y=R(g,"jitterBufferDelay"),_=R(g,"jitterBufferEmittedCount");return y!==void 0&&_!==void 0&&_>0?y/_*1000:void 0}).filter((g)=>g!==void 0)),r=s.map((g)=>R(g,"audioLevel")).filter((g)=>g!==void 0);if(c.requireConnectedCandidatePair&&e.length>0&&C===0)M(l,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(c.requireLiveAudioTrack&&T===0)M(l,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(c.maxPacketLossRatio!==void 0&&t>c.maxPacketLossRatio)M(l,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(t)} above ${String(c.maxPacketLossRatio)}.`);if(c.maxRoundTripTimeMs!==void 0&&b!==void 0&&b>c.maxRoundTripTimeMs)M(l,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(b)}ms above ${String(c.maxRoundTripTimeMs)}ms.`);if(c.maxJitterMs!==void 0&&f!==void 0&&f>c.maxJitterMs)M(l,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(f)}ms above ${String(c.maxJitterMs)}ms.`);return{activeCandidatePairs:C,audioLevelAverage:Vc(r),bytesReceived:x,bytesSent:w,checkedAt:Date.now(),endedAudioTracks:S,inboundPackets:d,issues:l,jitterBufferDelayMs:D,jitterMs:f,liveAudioTracks:T,outboundPackets:h,packetLossRatio:t,packetsLost:A,roundTripTimeMs:b,status:l.some((g)=>g.severity==="error")?"fail":l.length>0?"warn":"pass",totalStats:n.length}},Y=async(c)=>{return[...(await c.peerConnection.getStats(c.selector??null)).values()].map(Ic)};var q=(c={})=>{let n=c.stats??[],l=c.previousStats??[],o=[],i=new Map(l.map((A)=>[N(A),A])),s=n.filter((A)=>(A.type==="inbound-rtp"||A.type==="outbound-rtp")&&V(A,"kind")!=="video"&&V(A,"mediaType")!=="video").map((A)=>{let I=A.type==="outbound-rtp"?"outbound":"inbound",t=I==="outbound"?"packetsSent":"packetsReceived",x=I==="outbound"?"bytesSent":"bytesReceived",w=i.get(N(A)),b=R(A,t),f=w?R(w,t):void 0,D=R(A,x),r=w?R(w,x):void 0,g=A.timestamp!==void 0&&w?.timestamp!==void 0?A.timestamp-w.timestamp:void 0;return{bytesDelta:D!==void 0&&r!==void 0?D-r:void 0,currentPackets:b,direction:I,id:N(A),packetDelta:b!==void 0&&f!==void 0?b-f:void 0,previousPackets:f,timeDeltaMs:g}}),C=s.filter((A)=>A.direction==="inbound"),T=s.filter((A)=>A.direction==="outbound"),S=E(s.map((A)=>A.timeDeltaMs).filter((A)=>A!==void 0)),d=C.filter((A)=>c.maxInboundPacketStallMs!==void 0&&A.timeDeltaMs!==void 0&&A.timeDeltaMs>=c.maxInboundPacketStallMs&&A.packetDelta!==void 0&&A.packetDelta<=0).length,h=T.filter((A)=>c.maxOutboundPacketStallMs!==void 0&&A.timeDeltaMs!==void 0&&A.timeDeltaMs>=c.maxOutboundPacketStallMs&&A.packetDelta!==void 0&&A.packetDelta<=0).length;if(c.requireInboundAudio&&C.length===0)M(o,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(c.requireOutboundAudio&&T.length===0)M(o,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(c.maxGapMs!==void 0&&S!==void 0&&S>c.maxGapMs)M(o,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(S)}ms above ${String(c.maxGapMs)}ms.`);if(d>0)M(o,"error","media.webrtc_inbound_stalled",`${String(d)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(h>0)M(o,"error","media.webrtc_outbound_stalled",`${String(h)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:C.length,issues:o,maxObservedGapMs:S,outboundAudioStreams:T.length,stalledInboundStreams:d,stalledOutboundStreams:h,status:o.some((A)=>A.severity==="error")?"fail":o.length>0?"warn":"pass",streams:s,totalStats:n.length}};var _c="/api/voice/browser-media",wc=5000,bc=async(c)=>c.peerConnection??await c.getPeerConnection?.()??null,fc=async(c,n)=>{let l=n.fetch??globalThis.fetch;if(!l)return;await l(n.path??_c,{body:JSON.stringify(c),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},Q=(c)=>{let n=null,l=[],o=async()=>{let s=await bc(c);if(!s)return;let C=await Y({peerConnection:s}),T=X({...c,stats:C}),S=c.continuity===!1?void 0:q({...c.continuity,previousStats:l,stats:C}),d={at:Date.now(),continuity:S,report:T,scenarioId:c.getScenarioId?.()??null,sessionId:c.getSessionId?.()??null};return l=C,c.onReport?.(d),await fc(d,c),d},i=()=>{o().catch((s)=>{c.onError?.(s)})},e=()=>{if(n)clearInterval(n),n=null};return{close:e,reportOnce:o,start:()=>{if(n)return;i(),n=setInterval(i,c.intervalMs??wc)},stop:e}};var U=()=>{},Mc=()=>U,Lc={callControl:U,close:U,endTurn:U,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",send:U,sendAudio:U,simulateDisconnect:U,start:()=>{},subscribe:Mc},Uc=()=>crypto.randomUUID(),xc=(c,n,l)=>{let{hostname:o,port:i,protocol:e}=window.location,s=e==="https:"?"wss:":"ws:",C=i?`:${i}`:"",T=new URL(`${s}//${o}${C}${c}`);if(T.searchParams.set("sessionId",n),l)T.searchParams.set("scenarioId",l);return T.toString()},Dc=(c)=>{if(!c||typeof c!=="object"||!("type"in c))return!1;switch(c.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}},rc=(c)=>{if(typeof c.data!=="string")return null;try{let n=JSON.parse(c.data);return Dc(n)?n:null}catch{return null}},J=(c,n={})=>{if(typeof window>"u")return Lc;let l=new Set,o=n.reconnect!==!1,i=n.maxReconnectAttempts??10,e=n.pingInterval??30000,s={isConnected:!1,pendingMessages:[],scenarioId:n.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:n.sessionId??Uc(),ws:null},C=(g)=>{l.forEach((y)=>y(g))},T=()=>{if(s.pingInterval)clearInterval(s.pingInterval),s.pingInterval=null;if(s.reconnectTimeout)clearTimeout(s.reconnectTimeout),s.reconnectTimeout=null},S=()=>{if(s.ws?.readyState!==1)return;while(s.pendingMessages.length>0){let g=s.pendingMessages.shift();if(g!==void 0)s.ws.send(g)}},d=()=>{let g=Date.now()+500;s.reconnectAttempts+=1,C({reconnect:{attempts:s.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:i,nextAttemptAt:g,status:"reconnecting"},type:"connection"}),s.reconnectTimeout=setTimeout(()=>{if(s.reconnectAttempts>i){C({reconnect:{attempts:s.reconnectAttempts,maxAttempts:i,status:"exhausted"},type:"connection"});return}h()},500)},h=()=>{let g=new WebSocket(xc(c,s.sessionId,s.scenarioId));g.binaryType="arraybuffer",g.onopen=()=>{let y=s.reconnectAttempts>0;if(s.isConnected=!0,S(),y)C({reconnect:{attempts:s.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:i,status:"resumed"},type:"connection"}),s.reconnectAttempts=0;l.forEach((_)=>_({scenarioId:s.scenarioId??void 0,sessionId:s.sessionId,status:"active",type:"session"})),s.pingInterval=setInterval(()=>{if(g.readyState===1)g.send(JSON.stringify({type:"ping"}))},e)},g.onmessage=(y)=>{let _=rc(y);if(!_)return;if(_.type==="session")s.sessionId=_.sessionId,s.scenarioId=_.scenarioId??s.scenarioId;l.forEach((oc)=>oc(_))},g.onclose=(y)=>{if(s.isConnected=!1,T(),o&&y.code!==1000&&s.reconnectAttempts<i)d();else if(o&&y.code!==1000)C({reconnect:{attempts:s.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:i,status:"exhausted"},type:"connection"})},s.ws=g},A=(g)=>{if(s.ws?.readyState===1){s.ws.send(g);return}s.pendingMessages.push(g)},I=(g)=>{A(JSON.stringify(g))},t=(g={})=>{if(g.sessionId)s.sessionId=g.sessionId;if(g.scenarioId)s.scenarioId=g.scenarioId;I({type:"start",sessionId:s.sessionId,scenarioId:s.scenarioId??void 0})},x=(g)=>{A(g)},w=()=>{I({type:"end_turn"})},b=(g)=>{I({...g,type:"call_control"})},f=()=>{if(T(),s.ws)s.ws.close(1000),s.ws=null;s.isConnected=!1,l.clear()},D=()=>{if(s.ws?.readyState===1)s.ws.close(4000,"absolutejs-voice-reconnect-proof")},r=(g)=>{return l.add(g),()=>{l.delete(g)}};return h(),{callControl:b,close:f,endTurn:w,getReadyState:()=>s.ws?.readyState??3,getScenarioId:()=>s.scenarioId??"",getSessionId:()=>s.sessionId,send:I,sendAudio:x,simulateDisconnect:D,start:t,subscribe:r}};var Oc=()=>({attempts:0,maxAttempts:0,status:"idle"}),Ec=()=>({assistantAudio:[],assistantTexts:[],call:null,error:null,isConnected:!1,sessionMetadata:null,scenarioId:null,partial:"",reconnect:Oc(),sessionId:null,status:"idle",turns:[]}),Z=()=>{let c=Ec(),n=new Set,l=()=>{n.forEach((i)=>i())};return{dispatch:(i)=>{switch(i.type){case"audio":c={...c,assistantAudio:[...c.assistantAudio,{chunk:i.chunk,format:i.format,receivedAt:i.receivedAt,turnId:i.turnId}]};break;case"assistant":c={...c,assistantTexts:[...c.assistantTexts,i.text]};break;case"complete":c={...c,sessionId:i.sessionId,status:"completed"};break;case"call_lifecycle":c={...c,call:{...c.call,disposition:i.event.type==="end"?i.event.disposition:c.call?.disposition,endedAt:i.event.type==="end"?i.event.at:c.call?.endedAt,events:[...c.call?.events??[],i.event],lastEventAt:i.event.at,startedAt:c.call?.startedAt??i.event.at},sessionId:i.sessionId};break;case"connected":c={...c,isConnected:!0,reconnect:c.reconnect.status==="reconnecting"?{...c.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:c.reconnect};break;case"connection":c={...c,reconnect:i.reconnect};break;case"disconnected":c={...c,isConnected:!1};break;case"error":c={...c,error:i.message};break;case"final":c={...c,partial:i.transcript.text,turns:c.turns.map((e)=>e)};break;case"partial":c={...c,partial:i.transcript.text};break;case"replay":c={...c,assistantTexts:[...i.assistantTexts],call:i.call??null,error:null,isConnected:i.status==="active",partial:i.partial,reconnect:c.reconnect.status==="reconnecting"?{...c.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:c.reconnect,scenarioId:i.scenarioId??c.scenarioId,sessionId:i.sessionId,sessionMetadata:i.sessionMetadata??c.sessionMetadata,status:i.status,turns:[...i.turns]};break;case"session":c={...c,error:null,scenarioId:i.scenarioId??c.scenarioId,isConnected:i.status==="active",sessionId:i.sessionId,sessionMetadata:i.sessionMetadata??c.sessionMetadata,status:i.status};break;case"turn":c={...c,partial:"",turns:[...c.turns,i.turn]};break}l()},getServerSnapshot:()=>c,getSnapshot:()=>c,subscribe:(i)=>{return n.add(i),()=>{n.delete(i)}}}};var B=(c,n={})=>{let l=J(c,n),o=Z(),i=n.browserMedia&&typeof window<"u"?Q({...n.browserMedia,getScenarioId:()=>n.browserMedia?n.browserMedia.getScenarioId?.()??l.getScenarioId():l.getScenarioId(),getSessionId:()=>n.browserMedia?n.browserMedia.getSessionId?.()??l.getSessionId():l.getSessionId()}):null,e=new Set,s=(d)=>Promise.resolve().then(()=>{if(!d?.sessionId&&!d?.scenarioId)return;l.start(d),i?.start()}),C=()=>{e.forEach((d)=>d())},T=()=>{if(!n.reconnectReportPath||typeof fetch>"u")return;let d=o.getSnapshot(),h=JSON.stringify({at:Date.now(),reconnect:d.reconnect,scenarioId:d.scenarioId,sessionId:l.getSessionId(),turnIds:d.turns.map((A)=>A.id)});fetch(n.reconnectReportPath,{body:h,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},S=l.subscribe((d)=>{let h=G(d);if(h){if(o.dispatch(h),d.type==="connection")T();C()}});return{callControl(d){l.callControl(d)},close(){S(),i?.close(),l.close(),o.dispatch({type:"disconnected"}),C()},endTurn(){l.endTurn()},get error(){return o.getSnapshot().error},getServerSnapshot(){return o.getServerSnapshot()},getSnapshot(){return o.getSnapshot()},get isConnected(){return o.getSnapshot().isConnected},get scenarioId(){return o.getSnapshot().scenarioId},get sessionMetadata(){return o.getSnapshot().sessionMetadata},start:s,get partial(){return o.getSnapshot().partial},get reconnect(){return o.getSnapshot().reconnect},get sessionId(){return l.getSessionId()},get status(){return o.getSnapshot().status},get turns(){return o.getSnapshot().turns},get assistantTexts(){return o.getSnapshot().assistantTexts},get assistantAudio(){return o.getSnapshot().assistantAudio},get call(){return o.getSnapshot().call},sendAudio(d){l.sendAudio(d)},simulateDisconnect(){l.simulateDisconnect()},subscribe(d){return e.add(d),()=>{e.delete(d)}}}};var K=(c)=>{if(!c||c.enabled===!1)return;return{enabled:!0,maxGain:c.maxGain??3,noiseGateAttenuation:c.noiseGateAttenuation??0.15,noiseGateThreshold:c.noiseGateThreshold??0.006,targetLevel:c.targetLevel??0.08}};var ac={balanced:{qualityProfile:"general",silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:"general",silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},"long-form":{qualityProfile:"general",silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},Pc={general:{},"accent-heavy":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},"noisy-room":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},"short-command":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var j=(c)=>{let n=c?.profile??"fast",l=c?.qualityProfile??"general",o=ac[n],i=Pc[l];return{profile:n,qualityProfile:l,silenceMs:c?.silenceMs??i.silenceMs??o.silenceMs,speechThreshold:c?.speechThreshold??i.speechThreshold??o.speechThreshold,transcriptStabilityMs:c?.transcriptStabilityMs??i.transcriptStabilityMs??o.transcriptStabilityMs}};var Nc={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:{qualityProfile:"short-command",profile:"balanced"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"general",profile:"fast"}},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:{qualityProfile:"accent-heavy",profile:"long-form"}},"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:{qualityProfile:"accent-heavy",profile:"long-form"}},"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:{qualityProfile:"noisy-room",profile:"long-form",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:{qualityProfile:"noisy-room",profile:"long-form",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:{qualityProfile:"noisy-room",profile:"long-form",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:{qualityProfile:"noisy-room",profile:"long-form"}}},k=(c="default")=>{let n=Nc[c];return{audioConditioning:K(n.audioConditioning),capture:{channelCount:n.capture?.channelCount??1,sampleRateHz:n.capture?.sampleRateHz??16000},connection:{...n.connection},name:c,sttLifecycle:n.sttLifecycle??"continuous",turnDetection:j(n.turnDetection)}};var Wc=(c)=>({assistantAudio:[...c.assistantAudio],assistantTexts:[...c.assistantTexts],call:c.call,error:c.error,isConnected:c.isConnected,isRecording:!1,partial:c.partial,reconnect:c.reconnect,recordingError:null,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,scenarioId:c.scenarioId,status:c.status,turns:[...c.turns]}),F=(c,n={})=>{let l=k(n.preset),o=B(c,{...l.connection,...n.connection}),i=null,e=Wc(o),s=new Set,C=()=>{for(let t of s)t()},T=()=>{if(e={...e,assistantAudio:[...o.assistantAudio],assistantTexts:[...o.assistantTexts],call:o.call,error:o.error,isConnected:o.isConnected,partial:o.partial,reconnect:o.reconnect,sessionId:o.sessionId,sessionMetadata:o.sessionMetadata,scenarioId:o.scenarioId,status:o.status,turns:[...o.turns]},n.autoStopOnComplete!==!1&&e.status==="completed"&&e.isRecording)i?.stop(),i=null,e={...e,isRecording:!1};C()},S=o.subscribe(T);T();let d=()=>{if(i)return i;return i=H({channelCount:n.capture?.channelCount??l.capture.channelCount,onLevel:n.capture?.onLevel,onAudio:(t)=>{if(n.capture?.onAudio){n.capture.onAudio(t,o.sendAudio);return}o.sendAudio(t)},sampleRateHz:n.capture?.sampleRateHz??l.capture.sampleRateHz}),i},h=()=>{i?.stop(),i=null,e={...e,isRecording:!1},C()},A=async()=>{if(e.isRecording)return;try{e={...e,recordingError:null},C(),await d().start(),e={...e,isRecording:!0},C()}catch(t){throw i=null,e={...e,isRecording:!1,recordingError:t instanceof Error?t.message:String(t)},C(),t}};return{bindHTMX(t){return $(o,t)},callControl:(t)=>o.callControl(t),close:()=>{S(),h(),o.close()},endTurn:()=>o.endTurn(),get error(){return e.error},getServerSnapshot:()=>e,getSnapshot:()=>e,get isConnected(){return e.isConnected},get isRecording(){return e.isRecording},get partial(){return e.partial},get recordingError(){return e.recordingError},get reconnect(){return e.reconnect},sendAudio:(t)=>o.sendAudio(t),simulateDisconnect:()=>o.simulateDisconnect(),get sessionId(){return e.sessionId},get sessionMetadata(){return e.sessionMetadata},get scenarioId(){return e.scenarioId},startRecording:A,get status(){return e.status},stopRecording:h,subscribe:(t)=>{return s.add(t),()=>{s.delete(t)}},toggleRecording:async()=>{if(e.isRecording){h();return}await A()},get turns(){return e.turns},get assistantTexts(){return e.assistantTexts},get assistantAudio(){return e.assistantAudio},get call(){return e.call}}};var m=(c)=>{if(!c.isConnected)return"idle";if(c.isPlaying)return"speaking";if(c.isRecording&&c.hasActivePartial)return"listening";if(c.isRecording)return"listening";if(c.lastTranscriptAt&&!c.lastAssistantAt)return"thinking";if(c.lastTranscriptAt&&c.lastAssistantAt&&c.lastTranscriptAt>c.lastAssistantAt)return"thinking";return"idle"};var $c={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},Hc={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},Gc=(c,n)=>{switch(c){case"listening":return n.listening;case"speaking":return n.speaking;case"thinking":return n.thinking;case"idle":return n.idle}},v=(c)=>{let n={...$c,...c.theme},l={...Hc,...c.labels},o=c.state.assistantAudio.at(-1)?.receivedAt,i=c.state.turns.at(-1)?.committedAt,e=m({hasActivePartial:c.state.partial.length>0,isConnected:c.state.isConnected,isPlaying:!1,isRecording:c.state.isRecording,lastAssistantAt:o,lastTranscriptAt:i}),s=!c.state.isConnected&&c.state.status!=="idle"&&!c.state.error,C=c.state.error?"Error":s?l.connecting:c.state.status==="completed"?l.callEnded:Gc(e,l);return{agentState:e,classes:{container:`absolute-voice-widget absolute-voice-widget--${e}`,dot:`absolute-voice-widget__dot${c.state.error?" absolute-voice-widget__dot--error":""}`},controls:{canEnd:c.state.isConnected,canMute:c.state.isRecording,canStart:!c.state.isRecording&&c.state.status!=="completed"},errorMessage:c.state.error??void 0,labels:l,partial:c.state.partial||void 0,statusLabel:C,theme:n,title:c.title??"Voice"}},L=(c)=>c.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;"),zc=(c)=>typeof c==="number"?`${c}px`:c,u=(c)=>{let n=c.theme,l=`background:${n.background};border-radius:${zc(n.radius)};color:${n.foreground};font-family:${n.fontFamily};min-width:240px;padding:20px 22px;`,o=`background:${c.errorMessage?n.errorAccent:c.agentState==="idle"?"rgba(148,163,184,0.6)":n.accent};border-radius:50%;height:10px;width:10px;`,i=[];if(c.controls.canStart)i.push(`<button type="button" data-action="start" style="background:${n.accent};border:none;border-radius:12px;color:${n.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${L(c.labels.startCall)}</button>`);if(c.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:${n.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${L(c.labels.mute)}</button>`);if(c.controls.canEnd)i.push(`<button type="button" data-action="end" style="background:${n.errorAccent};border:none;border-radius:12px;color:${n.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${L(c.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${c.agentState}" class="${L(c.classes.container)}" style="${l}">
1
+ (()=>{var{defineProperty:H,getOwnPropertyNames:oc,getOwnPropertyDescriptor:lc}=Object,Ac=Object.prototype.hasOwnProperty;function Cc(c){return this[c]}var ic=(c)=>{var n=(Y??=new WeakMap).get(c),o;if(n)return n;if(n=H({},"__esModule",{value:!0}),c&&typeof c==="object"||typeof c==="function"){for(var g of oc(c))if(!Ac.call(n,g))H(n,g,{get:Cc.bind(c,g),enumerable:!(o=lc(c,g))||o.enumerable})}return Y.set(c,n),n},Y;var yc=(c)=>c;function Tc(c,n){this[c]=yc.bind(null,n)}var Vc=(c,n)=>{for(var o in n)H(c,o,{get:n[o],enumerable:!0,configurable:!0,set:Tc.bind(n,o)})};var Bc={};Vc(Bc,{mount:()=>p,default:()=>Kc,VOICE_EMBED_VERSION:()=>cc});var Ic=(c)=>{if(typeof c!=="string")return c;return document.querySelector(c)},Rc=(c,n,o,g)=>{let l=n??c.getAttribute("hx-get")??"";if(!l)return"";let i=new URL(l,window.location.origin);if(g)i.searchParams.set(o,g);else i.searchParams.delete(o);return`${i.pathname}${i.search}${i.hash}`},Q=(c,n)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let o=Ic(n.element);if(!o)return()=>{};let g=n.eventName??"voice-refresh",l=n.sessionQueryParam??"sessionId",i=()=>{let V=window,I=Rc(o,n.route,l,c.sessionId);if(I)o.setAttribute("hx-get",I);V.htmx?.process?.(o),V.htmx?.trigger?.(o,g)},C=c.subscribe(i);return i(),()=>{C()}};var hc=(c)=>Math.max(-1,Math.min(1,c)),Sc=(c)=>{let n=new Int16Array(c.length);for(let o=0;o<c.length;o+=1){let g=hc(c[o]??0);n[o]=g<0?g*32768:g*32767}return new Uint8Array(n.buffer)},dc=(c)=>{let n=c instanceof Uint8Array?c:new Uint8Array(c);if(n.byteLength<2)return 0;let o=new Int16Array(n.buffer,n.byteOffset,Math.floor(n.byteLength/2));if(o.length===0)return 0;let g=0;for(let l of o){let i=l/32768;g+=i*i}return Math.min(1,Math.max(0,Math.sqrt(g/o.length)*5.5))},_c=(c,n,o)=>{if(n===o)return c;let g=n/o,l=Math.round(c.length/g),i=new Float32Array(l),C=0,V=0;while(C<i.length){let I=Math.round((C+1)*g),S=0,T=0;for(let h=V;h<I&&h<c.length;h+=1)S+=c[h]??0,T+=1;i[C]=T>0?S/T:0,C+=1,V=I}return i},J=(c)=>{let n=null,o=null,g=null,l=null;return{start:async()=>{if(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia)throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let V=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!V)throw Error("Browser microphone capture requires AudioContext support.");l=await navigator.mediaDevices.getUserMedia({audio:{channelCount:c.channelCount??1}}),n=new V,o=n.createMediaStreamSource(l),g=n.createScriptProcessor(4096,1,1),g.onaudioprocess=(I)=>{let S=I.inputBuffer.getChannelData(0),T=_c(S,n?.sampleRate??48000,c.sampleRateHz??16000),h=Sc(T);c.onLevel?.(dc(h)),c.onAudio(h)},o.connect(g),g.connect(n.destination)},stop:()=>{g?.disconnect(),o?.disconnect(),l?.getTracks().forEach((V)=>V.stop()),n?.close(),c.onLevel?.(0),n=null,l=null,g=null,o=null}}};var G=(c)=>{if(typeof c==="string"&&c.trim())return c;if(c instanceof Error&&c.message.trim())return c.message;if(c&&typeof c==="object"){let n=c;for(let o of["message","reason","description"]){let g=n[o];if(typeof g==="string"&&g.trim())return g}if("error"in n)return G(n.error);if("cause"in n)return G(n.cause);try{return JSON.stringify(c)}catch{}}return"Unexpected error"},q=(c)=>{switch(c.type){case"audio":return{chunk:Uint8Array.from(atob(c.chunkBase64),(n)=>n.charCodeAt(0)),format:c.format,receivedAt:c.receivedAt,turnId:c.turnId,type:"audio"};case"assistant":return{text:c.text,type:"assistant"};case"complete":return{sessionId:c.sessionId,type:"complete"};case"connection":return{reconnect:c.reconnect,type:"connection"};case"call_lifecycle":return{event:c.event,sessionId:c.sessionId,type:"call_lifecycle"};case"error":return{message:G(c.message),type:"error"};case"final":return{transcript:c.transcript,type:"final"};case"partial":return{transcript:c.transcript,type:"partial"};case"replay":return{assistantTexts:c.assistantTexts,call:c.call,partial:c.partial,scenarioId:c.scenarioId,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,status:c.status,turns:c.turns,type:"replay"};case"session":return{sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,scenarioId:c.scenarioId,status:c.status,type:"session"};case"turn":return{turn:c.turn,type:"turn"};default:return null}};var tc=Math.PI*2;var D=(c,n,o,g)=>{c.push({code:o,message:g,severity:n})};var wc=(c)=>c.length===0?void 0:c.reduce((n,o)=>n+o,0)/c.length,$=(c)=>c.length===0?void 0:Math.max(...c);var _=(c,n)=>{let o=c[n];return typeof o==="number"&&Number.isFinite(o)?o:void 0},N=(c,n)=>{let o=c[n];return typeof o==="boolean"?o:void 0},w=(c,n)=>{let o=c[n];return typeof o==="string"?o:void 0},X=(c)=>String(c.id??w(c,"ssrc")??_(c,"ssrc")??w(c,"trackIdentifier")??w(c,"mid")??"unknown"),Z=(c)=>c===void 0?void 0:c*1000;var Lc=(c)=>{let n={};for(let[o,g]of Object.entries(c))if(g===null||typeof g==="boolean"||typeof g==="number"||typeof g==="string")n[o]=g;return n};var z=(c={})=>{let n=c.stats??[],o=[],g=n.filter((A)=>A.type==="inbound-rtp"&&w(A,"kind")!=="video"),l=n.filter((A)=>A.type==="outbound-rtp"&&w(A,"kind")!=="video"),i=n.filter((A)=>A.type==="candidate-pair"),C=n.filter((A)=>(A.type==="track"||A.type==="media-source")&&w(A,"kind")==="audio"),V=i.filter((A)=>N(A,"selected")===!0||N(A,"nominated")===!0||w(A,"state")==="succeeded").length,I=C.filter((A)=>w(A,"readyState")!=="ended"&&w(A,"trackState")!=="ended"&&N(A,"ended")!==!0).length,S=C.filter((A)=>w(A,"readyState")==="ended"||w(A,"trackState")==="ended"||N(A,"ended")===!0).length,T=g.reduce((A,d)=>A+(_(d,"packetsReceived")??0),0),h=l.reduce((A,d)=>A+(_(d,"packetsSent")??0),0),y=[...g,...l].reduce((A,d)=>A+Math.max(0,_(d,"packetsLost")??0),0),L=T+y,R=L===0?0:y/L,W=g.reduce((A,d)=>A+(_(d,"bytesReceived")??0),0),b=l.reduce((A,d)=>A+(_(d,"bytesSent")??0),0),M=$(i.map((A)=>Z(_(A,"currentRoundTripTime")??_(A,"roundTripTime"))).filter((A)=>A!==void 0)),O=$([...g,...l].map((A)=>Z(_(A,"jitter"))).filter((A)=>A!==void 0)),s=$(g.map((A)=>{let d=_(A,"jitterBufferDelay"),U=_(A,"jitterBufferEmittedCount");return d!==void 0&&U!==void 0&&U>0?d/U*1000:void 0}).filter((A)=>A!==void 0)),P=C.map((A)=>_(A,"audioLevel")).filter((A)=>A!==void 0);if(c.requireConnectedCandidatePair&&i.length>0&&V===0)D(o,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(c.requireLiveAudioTrack&&I===0)D(o,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(c.maxPacketLossRatio!==void 0&&R>c.maxPacketLossRatio)D(o,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(R)} above ${String(c.maxPacketLossRatio)}.`);if(c.maxRoundTripTimeMs!==void 0&&M!==void 0&&M>c.maxRoundTripTimeMs)D(o,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(M)}ms above ${String(c.maxRoundTripTimeMs)}ms.`);if(c.maxJitterMs!==void 0&&O!==void 0&&O>c.maxJitterMs)D(o,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(O)}ms above ${String(c.maxJitterMs)}ms.`);return{activeCandidatePairs:V,audioLevelAverage:wc(P),bytesReceived:W,bytesSent:b,checkedAt:Date.now(),endedAudioTracks:S,inboundPackets:T,issues:o,jitterBufferDelayMs:s,jitterMs:O,liveAudioTracks:I,outboundPackets:h,packetLossRatio:R,packetsLost:y,roundTripTimeMs:M,status:o.some((A)=>A.severity==="error")?"fail":o.length>0?"warn":"pass",totalStats:n.length}},B=async(c)=>{return[...(await c.peerConnection.getStats(c.selector??null)).values()].map(Lc)};var K=(c={})=>{let n=c.stats??[],o=c.previousStats??[],g=[],l=new Map(o.map((y)=>[X(y),y])),C=n.filter((y)=>(y.type==="inbound-rtp"||y.type==="outbound-rtp")&&w(y,"kind")!=="video"&&w(y,"mediaType")!=="video").map((y)=>{let L=y.type==="outbound-rtp"?"outbound":"inbound",R=L==="outbound"?"packetsSent":"packetsReceived",W=L==="outbound"?"bytesSent":"bytesReceived",b=l.get(X(y)),M=_(y,R),O=b?_(b,R):void 0,s=_(y,W),P=b?_(b,W):void 0,A=y.timestamp!==void 0&&b?.timestamp!==void 0?y.timestamp-b.timestamp:void 0;return{bytesDelta:s!==void 0&&P!==void 0?s-P:void 0,currentPackets:M,direction:L,id:X(y),packetDelta:M!==void 0&&O!==void 0?M-O:void 0,previousPackets:O,timeDeltaMs:A}}),V=C.filter((y)=>y.direction==="inbound"),I=C.filter((y)=>y.direction==="outbound"),S=$(C.map((y)=>y.timeDeltaMs).filter((y)=>y!==void 0)),T=V.filter((y)=>c.maxInboundPacketStallMs!==void 0&&y.timeDeltaMs!==void 0&&y.timeDeltaMs>=c.maxInboundPacketStallMs&&y.packetDelta!==void 0&&y.packetDelta<=0).length,h=I.filter((y)=>c.maxOutboundPacketStallMs!==void 0&&y.timeDeltaMs!==void 0&&y.timeDeltaMs>=c.maxOutboundPacketStallMs&&y.packetDelta!==void 0&&y.packetDelta<=0).length;if(c.requireInboundAudio&&V.length===0)D(g,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(c.requireOutboundAudio&&I.length===0)D(g,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(c.maxGapMs!==void 0&&S!==void 0&&S>c.maxGapMs)D(g,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(S)}ms above ${String(c.maxGapMs)}ms.`);if(T>0)D(g,"error","media.webrtc_inbound_stalled",`${String(T)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(h>0)D(g,"error","media.webrtc_outbound_stalled",`${String(h)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:V.length,issues:g,maxObservedGapMs:S,outboundAudioStreams:I.length,stalledInboundStreams:T,stalledOutboundStreams:h,status:g.some((y)=>y.severity==="error")?"fail":g.length>0?"warn":"pass",streams:C,totalStats:n.length}};var Uc="/api/voice/browser-media",bc=5000,Mc=async(c)=>c.peerConnection??await c.getPeerConnection?.()??null,Oc=async(c,n)=>{let o=n.fetch??globalThis.fetch;if(!o)return;await o(n.path??Uc,{body:JSON.stringify(c),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},j=(c)=>{let n=null,o=[],g=async()=>{let C=await Mc(c);if(!C)return;let V=await B({peerConnection:C}),I=z({...c,stats:V}),S=c.continuity===!1?void 0:K({...c.continuity,previousStats:o,stats:V}),T={at:Date.now(),continuity:S,report:I,scenarioId:c.getScenarioId?.()??null,sessionId:c.getSessionId?.()??null};return o=V,c.onReport?.(T),await Oc(T,c),T},l=()=>{g().catch((C)=>{c.onError?.(C)})},i=()=>{if(n)clearInterval(n),n=null};return{close:i,reportOnce:g,start:()=>{if(n)return;l(),n=setInterval(l,c.intervalMs??bc)},stop:i}};var E=()=>{},Dc=()=>E,xc={callControl:E,close:E,endTurn:E,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",send:E,sendAudio:E,simulateDisconnect:E,start:()=>{},subscribe:Dc},Ec=()=>crypto.randomUUID(),Wc=(c,n,o)=>{let{hostname:g,port:l,protocol:i}=window.location,C=i==="https:"?"wss:":"ws:",V=l?`:${l}`:"",I=new URL(`${C}//${g}${V}${c}`);if(I.searchParams.set("sessionId",n),o)I.searchParams.set("scenarioId",o);return I.toString()},sc=(c)=>{if(!c||typeof c!=="object"||!("type"in c))return!1;switch(c.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}},Pc=(c)=>{if(typeof c.data!=="string")return null;try{let n=JSON.parse(c.data);return sc(n)?n:null}catch{return null}},f=(c,n={})=>{if(typeof window>"u")return xc;let o=new Set,g=n.reconnect!==!1,l=n.maxReconnectAttempts??10,i=n.pingInterval??30000,C={isConnected:!1,pendingMessages:[],scenarioId:n.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:n.sessionId??Ec(),ws:null},V=(A)=>{o.forEach((d)=>d(A))},I=()=>{if(C.pingInterval)clearInterval(C.pingInterval),C.pingInterval=null;if(C.reconnectTimeout)clearTimeout(C.reconnectTimeout),C.reconnectTimeout=null},S=()=>{if(C.ws?.readyState!==1)return;while(C.pendingMessages.length>0){let A=C.pendingMessages.shift();if(A!==void 0)C.ws.send(A)}},T=()=>{let A=Date.now()+500;C.reconnectAttempts+=1,V({reconnect:{attempts:C.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:l,nextAttemptAt:A,status:"reconnecting"},type:"connection"}),C.reconnectTimeout=setTimeout(()=>{if(C.reconnectAttempts>l){V({reconnect:{attempts:C.reconnectAttempts,maxAttempts:l,status:"exhausted"},type:"connection"});return}h()},500)},h=()=>{let A=new WebSocket(Wc(c,C.sessionId,C.scenarioId));A.binaryType="arraybuffer",A.onopen=()=>{let d=C.reconnectAttempts>0;if(C.isConnected=!0,S(),d)V({reconnect:{attempts:C.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:l,status:"resumed"},type:"connection"}),C.reconnectAttempts=0;o.forEach((U)=>U({scenarioId:C.scenarioId??void 0,sessionId:C.sessionId,status:"active",type:"session"})),C.pingInterval=setInterval(()=>{if(A.readyState===1)A.send(JSON.stringify({type:"ping"}))},i)},A.onmessage=(d)=>{let U=Pc(d);if(!U)return;if(U.type==="session")C.sessionId=U.sessionId,C.scenarioId=U.scenarioId??C.scenarioId;o.forEach((gc)=>gc(U))},A.onclose=(d)=>{if(C.isConnected=!1,I(),g&&d.code!==1000&&C.reconnectAttempts<l)T();else if(g&&d.code!==1000)V({reconnect:{attempts:C.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:l,status:"exhausted"},type:"connection"})},C.ws=A},y=(A)=>{if(C.ws?.readyState===1){C.ws.send(A);return}C.pendingMessages.push(A)},L=(A)=>{y(JSON.stringify(A))},R=(A={})=>{if(A.sessionId)C.sessionId=A.sessionId;if(A.scenarioId)C.scenarioId=A.scenarioId;L({type:"start",sessionId:C.sessionId,scenarioId:C.scenarioId??void 0})},W=(A)=>{y(A)},b=()=>{L({type:"end_turn"})},M=(A)=>{L({...A,type:"call_control"})},O=()=>{if(I(),C.ws)C.ws.close(1000),C.ws=null;C.isConnected=!1,o.clear()},s=()=>{if(C.ws?.readyState===1)C.ws.close(4000,"absolutejs-voice-reconnect-proof")},P=(A)=>{return o.add(A),()=>{o.delete(A)}};return h(),{callControl:M,close:O,endTurn:b,getReadyState:()=>C.ws?.readyState??3,getScenarioId:()=>C.scenarioId??"",getSessionId:()=>C.sessionId,send:L,sendAudio:W,simulateDisconnect:s,start:R,subscribe:P}};var Nc=()=>({attempts:0,maxAttempts:0,status:"idle"}),$c=()=>({assistantAudio:[],assistantTexts:[],call:null,error:null,isConnected:!1,sessionMetadata:null,scenarioId:null,partial:"",reconnect:Nc(),sessionId:null,status:"idle",turns:[]}),k=()=>{let c=$c(),n=new Set,o=()=>{n.forEach((l)=>l())};return{dispatch:(l)=>{switch(l.type){case"audio":c={...c,assistantAudio:[...c.assistantAudio,{chunk:l.chunk,format:l.format,receivedAt:l.receivedAt,turnId:l.turnId}]};break;case"assistant":c={...c,assistantTexts:[...c.assistantTexts,l.text]};break;case"complete":c={...c,sessionId:l.sessionId,status:"completed"};break;case"call_lifecycle":c={...c,call:{...c.call,disposition:l.event.type==="end"?l.event.disposition:c.call?.disposition,endedAt:l.event.type==="end"?l.event.at:c.call?.endedAt,events:[...c.call?.events??[],l.event],lastEventAt:l.event.at,startedAt:c.call?.startedAt??l.event.at},sessionId:l.sessionId};break;case"connected":c={...c,isConnected:!0,reconnect:c.reconnect.status==="reconnecting"?{...c.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:c.reconnect};break;case"connection":c={...c,reconnect:l.reconnect};break;case"disconnected":c={...c,isConnected:!1};break;case"error":c={...c,error:l.message};break;case"final":c={...c,partial:l.transcript.text,turns:c.turns.map((i)=>i)};break;case"partial":c={...c,partial:l.transcript.text};break;case"replay":c={...c,assistantTexts:[...l.assistantTexts],call:l.call??null,error:null,isConnected:l.status==="active",partial:l.partial,reconnect:c.reconnect.status==="reconnecting"?{...c.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:c.reconnect,scenarioId:l.scenarioId??c.scenarioId,sessionId:l.sessionId,sessionMetadata:l.sessionMetadata??c.sessionMetadata,status:l.status,turns:[...l.turns]};break;case"session":c={...c,error:null,scenarioId:l.scenarioId??c.scenarioId,isConnected:l.status==="active",sessionId:l.sessionId,sessionMetadata:l.sessionMetadata??c.sessionMetadata,status:l.status};break;case"turn":c={...c,partial:"",turns:[...c.turns,l.turn]};break}o()},getServerSnapshot:()=>c,getSnapshot:()=>c,subscribe:(l)=>{return n.add(l),()=>{n.delete(l)}}}};var e=(c,n={})=>{let o=f(c,n),g=k(),l=n.browserMedia&&typeof window<"u"?j({...n.browserMedia,getScenarioId:()=>n.browserMedia?n.browserMedia.getScenarioId?.()??o.getScenarioId():o.getScenarioId(),getSessionId:()=>n.browserMedia?n.browserMedia.getSessionId?.()??o.getSessionId():o.getSessionId()}):null,i=new Set,C=(T)=>Promise.resolve().then(()=>{if(!T?.sessionId&&!T?.scenarioId)return;o.start(T),l?.start()}),V=()=>{i.forEach((T)=>T())},I=()=>{if(!n.reconnectReportPath||typeof fetch>"u")return;let T=g.getSnapshot(),h=JSON.stringify({at:Date.now(),reconnect:T.reconnect,scenarioId:T.scenarioId,sessionId:o.getSessionId(),turnIds:T.turns.map((y)=>y.id)});fetch(n.reconnectReportPath,{body:h,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},S=o.subscribe((T)=>{let h=q(T);if(h){if(g.dispatch(h),T.type==="connection")I();V()}});return{callControl(T){o.callControl(T)},close(){S(),l?.close(),o.close(),g.dispatch({type:"disconnected"}),V()},endTurn(){o.endTurn()},get error(){return g.getSnapshot().error},getServerSnapshot(){return g.getServerSnapshot()},getSnapshot(){return g.getSnapshot()},get isConnected(){return g.getSnapshot().isConnected},get scenarioId(){return g.getSnapshot().scenarioId},get sessionMetadata(){return g.getSnapshot().sessionMetadata},start:C,get partial(){return g.getSnapshot().partial},get reconnect(){return g.getSnapshot().reconnect},get sessionId(){return o.getSessionId()},get status(){return g.getSnapshot().status},get turns(){return g.getSnapshot().turns},get assistantTexts(){return g.getSnapshot().assistantTexts},get assistantAudio(){return g.getSnapshot().assistantAudio},get call(){return g.getSnapshot().call},sendAudio(T){o.sendAudio(T)},simulateDisconnect(){o.simulateDisconnect()},subscribe(T){return i.add(T),()=>{i.delete(T)}}}};var t=(c)=>{if(!c||c.enabled===!1)return;return{enabled:!0,maxGain:c.maxGain??3,noiseGateAttenuation:c.noiseGateAttenuation??0.15,noiseGateThreshold:c.noiseGateThreshold??0.006,targetLevel:c.targetLevel??0.08}};var Hc={balanced:{qualityProfile:"general",silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:"general",silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},"long-form":{qualityProfile:"general",silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},Gc={general:{},"accent-heavy":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},"noisy-room":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},"short-command":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var F=(c)=>{let n=c?.profile??"fast",o=c?.qualityProfile??"general",g=Hc[n],l=Gc[o];return{profile:n,qualityProfile:o,silenceMs:c?.silenceMs??l.silenceMs??g.silenceMs,speechThreshold:c?.speechThreshold??l.speechThreshold??g.speechThreshold,transcriptStabilityMs:c?.transcriptStabilityMs??l.transcriptStabilityMs??g.transcriptStabilityMs}};var Xc={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:{qualityProfile:"short-command",profile:"balanced"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"general",profile:"fast"}},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:{qualityProfile:"accent-heavy",profile:"long-form"}},"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:{qualityProfile:"accent-heavy",profile:"long-form"}},"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:{qualityProfile:"noisy-room",profile:"long-form",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:{qualityProfile:"noisy-room",profile:"long-form",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:{qualityProfile:"noisy-room",profile:"long-form",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:{qualityProfile:"noisy-room",profile:"long-form"}}},r=(c="default")=>{let n=Xc[c];return{audioConditioning:t(n.audioConditioning),capture:{channelCount:n.capture?.channelCount??1,sampleRateHz:n.capture?.sampleRateHz??16000},connection:{...n.connection},name:c,sttLifecycle:n.sttLifecycle??"continuous",turnDetection:F(n.turnDetection)}};var Yc=(c)=>({assistantAudio:[...c.assistantAudio],assistantTexts:[...c.assistantTexts],call:c.call,error:c.error,isConnected:c.isConnected,isRecording:!1,partial:c.partial,reconnect:c.reconnect,recordingError:null,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,scenarioId:c.scenarioId,status:c.status,turns:[...c.turns]}),a=(c,n={})=>{let o=r(n.preset),g=e(c,{...o.connection,...n.connection}),l=null,i=Yc(g),C=new Set,V=()=>{for(let R of C)R()},I=()=>{if(i={...i,assistantAudio:[...g.assistantAudio],assistantTexts:[...g.assistantTexts],call:g.call,error:g.error,isConnected:g.isConnected,partial:g.partial,reconnect:g.reconnect,sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,scenarioId:g.scenarioId,status:g.status,turns:[...g.turns]},n.autoStopOnComplete!==!1&&i.status==="completed"&&i.isRecording)l?.stop(),l=null,i={...i,isRecording:!1};V()},S=g.subscribe(I);I();let T=()=>{if(l)return l;return l=J({channelCount:n.capture?.channelCount??o.capture.channelCount,onLevel:n.capture?.onLevel,onAudio:(R)=>{if(n.capture?.onAudio){n.capture.onAudio(R,g.sendAudio);return}g.sendAudio(R)},sampleRateHz:n.capture?.sampleRateHz??o.capture.sampleRateHz}),l},h=()=>{l?.stop(),l=null,i={...i,isRecording:!1},V()},y=async()=>{if(i.isRecording)return;try{i={...i,recordingError:null},V(),await T().start(),i={...i,isRecording:!0},V()}catch(R){throw l=null,i={...i,isRecording:!1,recordingError:R instanceof Error?R.message:String(R)},V(),R}};return{bindHTMX(R){return Q(g,R)},callControl:(R)=>g.callControl(R),close:()=>{S(),h(),g.close()},endTurn:()=>g.endTurn(),get error(){return i.error},getServerSnapshot:()=>i,getSnapshot:()=>i,get isConnected(){return i.isConnected},get isRecording(){return i.isRecording},get partial(){return i.partial},get recordingError(){return i.recordingError},get reconnect(){return i.reconnect},sendAudio:(R)=>g.sendAudio(R),simulateDisconnect:()=>g.simulateDisconnect(),get sessionId(){return i.sessionId},get sessionMetadata(){return i.sessionMetadata},get scenarioId(){return i.scenarioId},startRecording:y,get status(){return i.status},stopRecording:h,subscribe:(R)=>{return C.add(R),()=>{C.delete(R)}},toggleRecording:async()=>{if(i.isRecording){h();return}await y()},get turns(){return i.turns},get assistantTexts(){return i.assistantTexts},get assistantAudio(){return i.assistantAudio},get call(){return i.call}}};var v=(c)=>{if(!c.isConnected)return"idle";if(c.isPlaying)return"speaking";if(c.isRecording&&c.hasActivePartial)return"listening";if(c.isRecording)return"listening";if(c.lastTranscriptAt&&!c.lastAssistantAt)return"thinking";if(c.lastTranscriptAt&&c.lastAssistantAt&&c.lastTranscriptAt>c.lastAssistantAt)return"thinking";return"idle"};var Qc={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},Jc={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},qc=(c,n)=>{switch(c){case"listening":return n.listening;case"speaking":return n.speaking;case"thinking":return n.thinking;case"idle":return n.idle}},u=(c)=>{let n={...Qc,...c.theme},o={...Jc,...c.labels},g=c.state.assistantAudio.at(-1)?.receivedAt,l=c.state.turns.at(-1)?.committedAt,i=v({hasActivePartial:c.state.partial.length>0,isConnected:c.state.isConnected,isPlaying:!1,isRecording:c.state.isRecording,lastAssistantAt:g,lastTranscriptAt:l}),C=!c.state.isConnected&&c.state.status!=="idle"&&!c.state.error,V=c.state.error?"Error":C?o.connecting:c.state.status==="completed"?o.callEnded:qc(i,o);return{agentState:i,classes:{container:`absolute-voice-widget absolute-voice-widget--${i}`,dot:`absolute-voice-widget__dot${c.state.error?" absolute-voice-widget__dot--error":""}`},controls:{canEnd:c.state.isConnected,canMute:c.state.isRecording,canStart:!c.state.isRecording&&c.state.status!=="completed"},errorMessage:c.state.error??void 0,labels:o,partial:c.state.partial||void 0,statusLabel:V,theme:n,title:c.title??"Voice"}},x=(c)=>c.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;"),Zc=(c)=>typeof c==="number"?`${c}px`:c,m=(c)=>{let n=c.theme,o=`background:${n.background};border-radius:${Zc(n.radius)};color:${n.foreground};font-family:${n.fontFamily};min-width:240px;padding:20px 22px;`,g=`background:${c.errorMessage?n.errorAccent:c.agentState==="idle"?"rgba(148,163,184,0.6)":n.accent};border-radius:50%;height:10px;width:10px;`,l=[];if(c.controls.canStart)l.push(`<button type="button" data-action="start" style="background:${n.accent};border:none;border-radius:12px;color:${n.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${x(c.labels.startCall)}</button>`);if(c.controls.canMute)l.push(`<button type="button" data-action="mute" style="background:transparent;border:1px solid rgba(255,255,255,0.18);border-radius:12px;color:${n.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${x(c.labels.mute)}</button>`);if(c.controls.canEnd)l.push(`<button type="button" data-action="end" style="background:${n.errorAccent};border:none;border-radius:12px;color:${n.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${x(c.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${c.agentState}" class="${x(c.classes.container)}" style="${o}">
2
2
  <div style="align-items:center;display:flex;gap:10px;margin-bottom:12px;">
3
- <span aria-hidden="true" class="${L(c.classes.dot)}" style="${o}"></span>
4
- <strong style="font-size:15px;">${L(c.title)}</strong>
5
- <span style="font-size:13px;margin-left:auto;opacity:0.7;">${L(c.statusLabel)}</span>
3
+ <span aria-hidden="true" class="${x(c.classes.dot)}" style="${g}"></span>
4
+ <strong style="font-size:15px;">${x(c.title)}</strong>
5
+ <span style="font-size:13px;margin-left:auto;opacity:0.7;">${x(c.statusLabel)}</span>
6
6
  </div>
7
- ${c.partial?`<p style="font-size:13px;margin:8px 0 12px;opacity:0.85;word-break:break-word;">“${L(c.partial)}”</p>`:""}
8
- <div style="display:flex;gap:10px;">${i.join("")}</div>
9
- ${c.errorMessage?`<p style="color:${n.errorAccent};font-size:12px;margin-top:12px;">${L(c.errorMessage)}</p>`:""}
10
- </div>`};var Xc=(c)=>{if(typeof c!=="string")return c;let n=document.querySelector(c);if(!n)throw Error(`AbsoluteVoice.mount: no element matches "${c}"`);return n},p=(c,n={})=>{let l=Xc(c),o=F(n.path??"/voice",n.controllerOptions),i=null,e=null,s=()=>{let T=v({...n.labels!==void 0?{labels:n.labels}:{},state:{assistantAudio:o.assistantAudio,error:o.error,isConnected:o.isConnected,isRecording:o.isRecording,partial:o.partial,status:o.status,turns:o.turns},...n.theme!==void 0?{theme:n.theme}:{},...n.title!==void 0?{title:n.title}:{}});l.innerHTML=u(T);for(let S of l.querySelectorAll("button[data-action]")){let d=S.dataset.action;S.addEventListener("click",()=>{if(d==="start")o.startRecording();else if(d==="mute")o.stopRecording();else if(d==="end")o.close()})}if(o.error&&o.error!==i)i=o.error,n.onError?.(o.error);if(o.status!==e)e=o.status,n.onStatusChange?.(o.status)},C=o.subscribe(s);if(s(),n.autoStart)o.startRecording();return{controller:o,async end(){await o.close()},mute(){o.stopRecording()},async start(){await o.startRecording()},unmount(){C(),o.close(),l.innerHTML=""}}},cc="0.0.22-beta.516",nc={mount:p,version:cc};if(typeof globalThis<"u")globalThis.AbsoluteVoice=nc;var qc=nc;})();
7
+ ${c.partial?`<p style="font-size:13px;margin:8px 0 12px;opacity:0.85;word-break:break-word;">“${x(c.partial)}”</p>`:""}
8
+ <div style="display:flex;gap:10px;">${l.join("")}</div>
9
+ ${c.errorMessage?`<p style="color:${n.errorAccent};font-size:12px;margin-top:12px;">${x(c.errorMessage)}</p>`:""}
10
+ </div>`};var zc=(c)=>{if(typeof c!=="string")return c;let n=document.querySelector(c);if(!n)throw Error(`AbsoluteVoice.mount: no element matches "${c}"`);return n},p=(c,n={})=>{let o=zc(c),g=a(n.path??"/voice",n.controllerOptions),l=null,i=null,C=()=>{let I=u({...n.labels!==void 0?{labels:n.labels}:{},state:{assistantAudio:g.assistantAudio,error:g.error,isConnected:g.isConnected,isRecording:g.isRecording,partial:g.partial,status:g.status,turns:g.turns},...n.theme!==void 0?{theme:n.theme}:{},...n.title!==void 0?{title:n.title}:{}});o.innerHTML=m(I);for(let S of o.querySelectorAll("button[data-action]")){let T=S.dataset.action;S.addEventListener("click",()=>{if(T==="start")g.startRecording();else if(T==="mute")g.stopRecording();else if(T==="end")g.close()})}if(g.error&&g.error!==l)l=g.error,n.onError?.(g.error);if(g.status!==i)i=g.status,n.onStatusChange?.(g.status)},V=g.subscribe(C);if(C(),n.autoStart)g.startRecording();return{controller:g,async end(){await g.close()},mute(){g.stopRecording()},async start(){await g.startRecording()},unmount(){V(),g.close(),o.innerHTML=""}}},cc="0.0.22-beta.516",nc={mount:p,version:cc};if(typeof globalThis<"u")globalThis.AbsoluteVoice=nc;var Kc=nc;})();
@@ -1 +1 @@
1
- export declare const HTMX_BOOTSTRAP_BUNDLE = "var Nn=(n)=>{if(typeof n!==\"string\")return n;return document.querySelector(n)},Hn=(n,e,o,c)=>{let i=e??n.getAttribute(\"hx-get\")??\"\";if(!i)return\"\";let s=new URL(i,window.location.origin);if(c)s.searchParams.set(o,c);else s.searchParams.delete(o);return`${s.pathname}${s.search}${s.hash}`},gn=(n,e)=>{if(typeof window>\"u\"||typeof document>\"u\")return()=>{};let o=Nn(e.element);if(!o)return()=>{};let c=e.eventName??\"voice-refresh\",i=e.sessionQueryParam??\"sessionId\",s=()=>{let r=window,g=Hn(o,e.route,i,n.sessionId);if(g)o.setAttribute(\"hx-get\",g);r.htmx?.process?.(o),r.htmx?.trigger?.(o,c)},t=n.subscribe(s);return s(),()=>{t()}};var Gn=(n)=>Math.max(-1,Math.min(1,n)),Bn=(n)=>{let e=new Int16Array(n.length);for(let o=0;o<n.length;o+=1){let c=Gn(n[o]??0);e[o]=c<0?c*32768:c*32767}return new Uint8Array(e.buffer)},Wn=(n)=>{let e=n instanceof Uint8Array?n:new Uint8Array(n);if(e.byteLength<2)return 0;let o=new Int16Array(e.buffer,e.byteOffset,Math.floor(e.byteLength/2));if(o.length===0)return 0;let c=0;for(let i of o){let s=i/32768;c+=s*s}return Math.min(1,Math.max(0,Math.sqrt(c/o.length)*5.5))},$n=(n,e,o)=>{if(e===o)return n;let c=e/o,i=Math.round(n.length/c),s=new Float32Array(i),t=0,r=0;while(t<s.length){let g=Math.round((t+1)*c),y=0,d=0;for(let h=r;h<g&&h<n.length;h+=1)y+=n[h]??0,d+=1;s[t]=d>0?y/d:0,t+=1,r=g}return s},An=(n)=>{let e=null,o=null,c=null,i=null;return{start:async()=>{if(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.\");i=await navigator.mediaDevices.getUserMedia({audio:{channelCount:n.channelCount??1}}),e=new r,o=e.createMediaStreamSource(i),c=e.createScriptProcessor(4096,1,1),c.onaudioprocess=(g)=>{let y=g.inputBuffer.getChannelData(0),d=$n(y,e?.sampleRate??48000,n.sampleRateHz??16000),h=Bn(d);n.onLevel?.(Wn(h)),n.onAudio(h)},o.connect(c),c.connect(e.destination)},stop:()=>{c?.disconnect(),o?.disconnect(),i?.getTracks().forEach((r)=>r.stop()),e?.close(),n.onLevel?.(0),e=null,i=null,c=null,o=null}}};var nn=(n)=>{if(typeof n===\"string\"&&n.trim())return n;if(n instanceof Error&&n.message.trim())return n.message;if(n&&typeof n===\"object\"){let e=n;for(let o of[\"message\",\"reason\",\"description\"]){let c=e[o];if(typeof c===\"string\"&&c.trim())return c}if(\"error\"in e)return nn(e.error);if(\"cause\"in e)return nn(e.cause);try{return JSON.stringify(n)}catch{}}return\"Unexpected error\"},hn=(n)=>{switch(n.type){case\"audio\":return{chunk:Uint8Array.from(atob(n.chunkBase64),(e)=>e.charCodeAt(0)),format:n.format,receivedAt:n.receivedAt,turnId:n.turnId,type:\"audio\"};case\"assistant\":return{text:n.text,type:\"assistant\"};case\"complete\":return{sessionId:n.sessionId,type:\"complete\"};case\"connection\":return{reconnect:n.reconnect,type:\"connection\"};case\"call_lifecycle\":return{event:n.event,sessionId:n.sessionId,type:\"call_lifecycle\"};case\"error\":return{message:nn(n.message),type:\"error\"};case\"final\":return{transcript:n.transcript,type:\"final\"};case\"partial\":return{transcript:n.transcript,type:\"partial\"};case\"replay\":return{assistantTexts:n.assistantTexts,call:n.call,partial:n.partial,scenarioId:n.scenarioId,sessionId:n.sessionId,sessionMetadata:n.sessionMetadata,status:n.status,turns:n.turns,type:\"replay\"};case\"session\":return{sessionId:n.sessionId,sessionMetadata:n.sessionMetadata,scenarioId:n.scenarioId,status:n.status,type:\"session\"};case\"turn\":return{turn:n.turn,type:\"turn\"};default:return null}};var H=(n,e,o,c)=>{n.push({code:o,message:c,severity:e})};var qn=(n)=>n.length===0?void 0:n.reduce((e,o)=>e+o,0)/n.length,K=(n)=>n.length===0?void 0:Math.max(...n);var _=(n,e)=>{let o=n[e];return typeof o===\"number\"&&Number.isFinite(o)?o:void 0},Z=(n,e)=>{let o=n[e];return typeof o===\"boolean\"?o:void 0},O=(n,e)=>{let o=n[e];return typeof o===\"string\"?o:void 0},en=(n)=>String(n.id??O(n,\"ssrc\")??_(n,\"ssrc\")??O(n,\"trackIdentifier\")??O(n,\"mid\")??\"unknown\"),yn=(n)=>n===void 0?void 0:n*1000;var Xn=(n)=>{let e={};for(let[o,c]of Object.entries(n))if(c===null||typeof c===\"boolean\"||typeof c===\"number\"||typeof c===\"string\")e[o]=c;return e};var Cn=(n={})=>{let e=n.stats??[],o=[],c=e.filter((l)=>l.type===\"inbound-rtp\"&&O(l,\"kind\")!==\"video\"),i=e.filter((l)=>l.type===\"outbound-rtp\"&&O(l,\"kind\")!==\"video\"),s=e.filter((l)=>l.type===\"candidate-pair\"),t=e.filter((l)=>(l.type===\"track\"||l.type===\"media-source\")&&O(l,\"kind\")===\"audio\"),r=s.filter((l)=>Z(l,\"selected\")===!0||Z(l,\"nominated\")===!0||O(l,\"state\")===\"succeeded\").length,g=t.filter((l)=>O(l,\"readyState\")!==\"ended\"&&O(l,\"trackState\")!==\"ended\"&&Z(l,\"ended\")!==!0).length,y=t.filter((l)=>O(l,\"readyState\")===\"ended\"||O(l,\"trackState\")===\"ended\"||Z(l,\"ended\")===!0).length,d=c.reduce((l,S)=>l+(_(S,\"packetsReceived\")??0),0),h=i.reduce((l,S)=>l+(_(S,\"packetsSent\")??0),0),a=[...c,...i].reduce((l,S)=>l+Math.max(0,_(S,\"packetsLost\")??0),0),M=d+a,C=M===0?0:a/M,I=c.reduce((l,S)=>l+(_(S,\"bytesReceived\")??0),0),L=i.reduce((l,S)=>l+(_(S,\"bytesSent\")??0),0),b=K(s.map((l)=>yn(_(l,\"currentRoundTripTime\")??_(l,\"roundTripTime\"))).filter((l)=>l!==void 0)),w=K([...c,...i].map((l)=>yn(_(l,\"jitter\"))).filter((l)=>l!==void 0)),u=K(c.map((l)=>{let S=_(l,\"jitterBufferDelay\"),R=_(l,\"jitterBufferEmittedCount\");return S!==void 0&&R!==void 0&&R>0?S/R*1000:void 0}).filter((l)=>l!==void 0)),U=t.map((l)=>_(l,\"audioLevel\")).filter((l)=>l!==void 0);if(n.requireConnectedCandidatePair&&s.length>0&&r===0)H(o,\"error\",\"media.webrtc_candidate_pair_missing\",\"No active WebRTC candidate pair was observed.\");if(n.requireLiveAudioTrack&&g===0)H(o,\"error\",\"media.webrtc_audio_track_missing\",\"No live WebRTC audio track was observed.\");if(n.maxPacketLossRatio!==void 0&&C>n.maxPacketLossRatio)H(o,\"warning\",\"media.webrtc_packet_loss\",`Observed WebRTC packet loss ratio ${String(C)} above ${String(n.maxPacketLossRatio)}.`);if(n.maxRoundTripTimeMs!==void 0&&b!==void 0&&b>n.maxRoundTripTimeMs)H(o,\"warning\",\"media.webrtc_round_trip_time\",`Observed WebRTC RTT ${String(b)}ms above ${String(n.maxRoundTripTimeMs)}ms.`);if(n.maxJitterMs!==void 0&&w!==void 0&&w>n.maxJitterMs)H(o,\"warning\",\"media.webrtc_jitter\",`Observed WebRTC jitter ${String(w)}ms above ${String(n.maxJitterMs)}ms.`);return{activeCandidatePairs:r,audioLevelAverage:qn(U),bytesReceived:I,bytesSent:L,checkedAt:Date.now(),endedAudioTracks:y,inboundPackets:d,issues:o,jitterBufferDelayMs:u,jitterMs:w,liveAudioTracks:g,outboundPackets:h,packetLossRatio:C,packetsLost:a,roundTripTimeMs:b,status:o.some((l)=>l.severity===\"error\")?\"fail\":o.length>0?\"warn\":\"pass\",totalStats:e.length}},fn=async(n)=>{return[...(await n.peerConnection.getStats(n.selector??null)).values()].map(Xn)};var Tn=(n={})=>{let e=n.stats??[],o=n.previousStats??[],c=[],i=new Map(o.map((a)=>[en(a),a])),t=e.filter((a)=>(a.type===\"inbound-rtp\"||a.type===\"outbound-rtp\")&&O(a,\"kind\")!==\"video\"&&O(a,\"mediaType\")!==\"video\").map((a)=>{let M=a.type===\"outbound-rtp\"?\"outbound\":\"inbound\",C=M===\"outbound\"?\"packetsSent\":\"packetsReceived\",I=M===\"outbound\"?\"bytesSent\":\"bytesReceived\",L=i.get(en(a)),b=_(a,C),w=L?_(L,C):void 0,u=_(a,I),U=L?_(L,I):void 0,l=a.timestamp!==void 0&&L?.timestamp!==void 0?a.timestamp-L.timestamp:void 0;return{bytesDelta:u!==void 0&&U!==void 0?u-U:void 0,currentPackets:b,direction:M,id:en(a),packetDelta:b!==void 0&&w!==void 0?b-w:void 0,previousPackets:w,timeDeltaMs:l}}),r=t.filter((a)=>a.direction===\"inbound\"),g=t.filter((a)=>a.direction===\"outbound\"),y=K(t.map((a)=>a.timeDeltaMs).filter((a)=>a!==void 0)),d=r.filter((a)=>n.maxInboundPacketStallMs!==void 0&&a.timeDeltaMs!==void 0&&a.timeDeltaMs>=n.maxInboundPacketStallMs&&a.packetDelta!==void 0&&a.packetDelta<=0).length,h=g.filter((a)=>n.maxOutboundPacketStallMs!==void 0&&a.timeDeltaMs!==void 0&&a.timeDeltaMs>=n.maxOutboundPacketStallMs&&a.packetDelta!==void 0&&a.packetDelta<=0).length;if(n.requireInboundAudio&&r.length===0)H(c,\"error\",\"media.webrtc_inbound_audio_missing\",\"No inbound WebRTC audio RTP stream was observed.\");if(n.requireOutboundAudio&&g.length===0)H(c,\"error\",\"media.webrtc_outbound_audio_missing\",\"No outbound WebRTC audio RTP stream was observed.\");if(n.maxGapMs!==void 0&&y!==void 0&&y>n.maxGapMs)H(c,\"warning\",\"media.webrtc_stream_gap\",`Observed WebRTC stream sample gap ${String(y)}ms above ${String(n.maxGapMs)}ms.`);if(d>0)H(c,\"error\",\"media.webrtc_inbound_stalled\",`${String(d)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(h>0)H(c,\"error\",\"media.webrtc_outbound_stalled\",`${String(h)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:r.length,issues:c,maxObservedGapMs:y,outboundAudioStreams:g.length,stalledInboundStreams:d,stalledOutboundStreams:h,status:c.some((a)=>a.severity===\"error\")?\"fail\":c.length>0?\"warn\":\"pass\",streams:t,totalStats:e.length}};var zn=\"/api/voice/browser-media\",kn=5000,Yn=async(n)=>n.peerConnection??await n.getPeerConnection?.()??null,Jn=async(n,e)=>{let o=e.fetch??globalThis.fetch;if(!o)return;await o(e.path??zn,{body:JSON.stringify(n),headers:{\"Content-Type\":\"application/json\"},keepalive:!0,method:\"POST\"})},Sn=(n)=>{let e=null,o=[],c=async()=>{let t=await Yn(n);if(!t)return;let r=await fn({peerConnection:t}),g=Cn({...n,stats:r}),y=n.continuity===!1?void 0:Tn({...n.continuity,previousStats:o,stats:r}),d={at:Date.now(),continuity:y,report:g,scenarioId:n.getScenarioId?.()??null,sessionId:n.getSessionId?.()??null};return o=r,n.onReport?.(d),await Jn(d,n),d},i=()=>{c().catch((t)=>{n.onError?.(t)})},s=()=>{if(e)clearInterval(e),e=null};return{close:s,reportOnce:c,start:()=>{if(e)return;i(),e=setInterval(i,n.intervalMs??kn)},stop:s}};var B=()=>{},Qn=()=>B,Zn={callControl:B,close:B,endTurn:B,getReadyState:()=>3,getScenarioId:()=>\"\",getSessionId:()=>\"\",send:B,sendAudio:B,simulateDisconnect:B,start:()=>{},subscribe:Qn},Kn=()=>crypto.randomUUID(),jn=(n,e,o)=>{let{hostname:c,port:i,protocol:s}=window.location,t=s===\"https:\"?\"wss:\":\"ws:\",r=i?`:${i}`:\"\",g=new URL(`${t}//${c}${r}${n}`);if(g.searchParams.set(\"sessionId\",e),o)g.searchParams.set(\"scenarioId\",o);return g.toString()},mn=(n)=>{if(!n||typeof n!==\"object\"||!(\"type\"in n))return!1;switch(n.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}},Fn=(n)=>{if(typeof n.data!==\"string\")return null;try{let e=JSON.parse(n.data);return mn(e)?e:null}catch{return null}},Mn=(n,e={})=>{if(typeof window>\"u\")return Zn;let o=new Set,c=e.reconnect!==!1,i=e.maxReconnectAttempts??10,s=e.pingInterval??30000,t={isConnected:!1,pendingMessages:[],scenarioId:e.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:e.sessionId??Kn(),ws:null},r=(l)=>{o.forEach((S)=>S(l))},g=()=>{if(t.pingInterval)clearInterval(t.pingInterval),t.pingInterval=null;if(t.reconnectTimeout)clearTimeout(t.reconnectTimeout),t.reconnectTimeout=null},y=()=>{if(t.ws?.readyState!==1)return;while(t.pendingMessages.length>0){let l=t.pendingMessages.shift();if(l!==void 0)t.ws.send(l)}},d=()=>{let l=Date.now()+500;t.reconnectAttempts+=1,r({reconnect:{attempts:t.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:i,nextAttemptAt:l,status:\"reconnecting\"},type:\"connection\"}),t.reconnectTimeout=setTimeout(()=>{if(t.reconnectAttempts>i){r({reconnect:{attempts:t.reconnectAttempts,maxAttempts:i,status:\"exhausted\"},type:\"connection\"});return}h()},500)},h=()=>{let l=new WebSocket(jn(n,t.sessionId,t.scenarioId));l.binaryType=\"arraybuffer\",l.onopen=()=>{let S=t.reconnectAttempts>0;if(t.isConnected=!0,y(),S)r({reconnect:{attempts:t.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:i,status:\"resumed\"},type:\"connection\"}),t.reconnectAttempts=0;o.forEach((R)=>R({scenarioId:t.scenarioId??void 0,sessionId:t.sessionId,status:\"active\",type:\"session\"})),t.pingInterval=setInterval(()=>{if(l.readyState===1)l.send(JSON.stringify({type:\"ping\"}))},s)},l.onmessage=(S)=>{let R=Fn(S);if(!R)return;if(R.type===\"session\")t.sessionId=R.sessionId,t.scenarioId=R.scenarioId??t.scenarioId;o.forEach((z)=>z(R))},l.onclose=(S)=>{if(t.isConnected=!1,g(),c&&S.code!==1000&&t.reconnectAttempts<i)d();else if(c&&S.code!==1000)r({reconnect:{attempts:t.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:i,status:\"exhausted\"},type:\"connection\"})},t.ws=l},a=(l)=>{if(t.ws?.readyState===1){t.ws.send(l);return}t.pendingMessages.push(l)},M=(l)=>{a(JSON.stringify(l))},C=(l={})=>{if(l.sessionId)t.sessionId=l.sessionId;if(l.scenarioId)t.scenarioId=l.scenarioId;M({type:\"start\",sessionId:t.sessionId,scenarioId:t.scenarioId??void 0})},I=(l)=>{a(l)},L=()=>{M({type:\"end_turn\"})},b=(l)=>{M({...l,type:\"call_control\"})},w=()=>{if(g(),t.ws)t.ws.close(1000),t.ws=null;t.isConnected=!1,o.clear()},u=()=>{if(t.ws?.readyState===1)t.ws.close(4000,\"absolutejs-voice-reconnect-proof\")},U=(l)=>{return o.add(l),()=>{o.delete(l)}};return h(),{callControl:b,close:w,endTurn:L,getReadyState:()=>t.ws?.readyState??3,getScenarioId:()=>t.scenarioId??\"\",getSessionId:()=>t.sessionId,send:M,sendAudio:I,simulateDisconnect:u,start:C,subscribe:U}};var vn=()=>({attempts:0,maxAttempts:0,status:\"idle\"}),pn=()=>({assistantAudio:[],assistantTexts:[],call:null,error:null,isConnected:!1,sessionMetadata:null,scenarioId:null,partial:\"\",reconnect:vn(),sessionId:null,status:\"idle\",turns:[]}),In=()=>{let n=pn(),e=new Set,o=()=>{e.forEach((i)=>i())};return{dispatch:(i)=>{switch(i.type){case\"audio\":n={...n,assistantAudio:[...n.assistantAudio,{chunk:i.chunk,format:i.format,receivedAt:i.receivedAt,turnId:i.turnId}]};break;case\"assistant\":n={...n,assistantTexts:[...n.assistantTexts,i.text]};break;case\"complete\":n={...n,sessionId:i.sessionId,status:\"completed\"};break;case\"call_lifecycle\":n={...n,call:{...n.call,disposition:i.event.type===\"end\"?i.event.disposition:n.call?.disposition,endedAt:i.event.type===\"end\"?i.event.at:n.call?.endedAt,events:[...n.call?.events??[],i.event],lastEventAt:i.event.at,startedAt:n.call?.startedAt??i.event.at},sessionId:i.sessionId};break;case\"connected\":n={...n,isConnected:!0,reconnect:n.reconnect.status===\"reconnecting\"?{...n.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:\"resumed\"}:n.reconnect};break;case\"connection\":n={...n,reconnect:i.reconnect};break;case\"disconnected\":n={...n,isConnected:!1};break;case\"error\":n={...n,error:i.message};break;case\"final\":n={...n,partial:i.transcript.text,turns:n.turns.map((s)=>s)};break;case\"partial\":n={...n,partial:i.transcript.text};break;case\"replay\":n={...n,assistantTexts:[...i.assistantTexts],call:i.call??null,error:null,isConnected:i.status===\"active\",partial:i.partial,reconnect:n.reconnect.status===\"reconnecting\"?{...n.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:\"resumed\"}:n.reconnect,scenarioId:i.scenarioId??n.scenarioId,sessionId:i.sessionId,sessionMetadata:i.sessionMetadata??n.sessionMetadata,status:i.status,turns:[...i.turns]};break;case\"session\":n={...n,error:null,scenarioId:i.scenarioId??n.scenarioId,isConnected:i.status===\"active\",sessionId:i.sessionId,sessionMetadata:i.sessionMetadata??n.sessionMetadata,status:i.status};break;case\"turn\":n={...n,partial:\"\",turns:[...n.turns,i.turn]};break}o()},getServerSnapshot:()=>n,getSnapshot:()=>n,subscribe:(i)=>{return e.add(i),()=>{e.delete(i)}}}};var Vn=(n,e={})=>{let o=Mn(n,e),c=In(),i=e.browserMedia&&typeof window<\"u\"?Sn({...e.browserMedia,getScenarioId:()=>e.browserMedia?e.browserMedia.getScenarioId?.()??o.getScenarioId():o.getScenarioId(),getSessionId:()=>e.browserMedia?e.browserMedia.getSessionId?.()??o.getSessionId():o.getSessionId()}):null,s=new Set,t=(d)=>Promise.resolve().then(()=>{if(!d?.sessionId&&!d?.scenarioId)return;o.start(d),i?.start()}),r=()=>{s.forEach((d)=>d())},g=()=>{if(!e.reconnectReportPath||typeof fetch>\"u\")return;let d=c.getSnapshot(),h=JSON.stringify({at:Date.now(),reconnect:d.reconnect,scenarioId:d.scenarioId,sessionId:o.getSessionId(),turnIds:d.turns.map((a)=>a.id)});fetch(e.reconnectReportPath,{body:h,headers:{\"Content-Type\":\"application/json\"},keepalive:!0,method:\"POST\"}).catch(()=>{})},y=o.subscribe((d)=>{let h=hn(d);if(h){if(c.dispatch(h),d.type===\"connection\")g();r()}});return{callControl(d){o.callControl(d)},close(){y(),i?.close(),o.close(),c.dispatch({type:\"disconnected\"}),r()},endTurn(){o.endTurn()},get error(){return c.getSnapshot().error},getServerSnapshot(){return c.getServerSnapshot()},getSnapshot(){return c.getSnapshot()},get isConnected(){return c.getSnapshot().isConnected},get scenarioId(){return c.getSnapshot().scenarioId},get sessionMetadata(){return c.getSnapshot().sessionMetadata},start:t,get partial(){return c.getSnapshot().partial},get reconnect(){return c.getSnapshot().reconnect},get sessionId(){return o.getSessionId()},get status(){return c.getSnapshot().status},get turns(){return c.getSnapshot().turns},get assistantTexts(){return c.getSnapshot().assistantTexts},get assistantAudio(){return c.getSnapshot().assistantAudio},get call(){return c.getSnapshot().call},sendAudio(d){o.sendAudio(d)},simulateDisconnect(){o.simulateDisconnect()},subscribe(d){return s.add(d),()=>{s.delete(d)}}}};var Ln=(n)=>{if(!n||n.enabled===!1)return;return{enabled:!0,maxGain:n.maxGain??3,noiseGateAttenuation:n.noiseGateAttenuation??0.15,noiseGateThreshold:n.noiseGateThreshold??0.006,targetLevel:n.targetLevel??0.08}};var ne={balanced:{qualityProfile:\"general\",silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:\"general\",silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},\"long-form\":{qualityProfile:\"general\",silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},ee={general:{},\"accent-heavy\":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},\"noisy-room\":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},\"short-command\":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var Rn=(n)=>{let e=n?.profile??\"fast\",o=n?.qualityProfile??\"general\",c=ne[e],i=ee[o];return{profile:e,qualityProfile:o,silenceMs:n?.silenceMs??i.silenceMs??c.silenceMs,speechThreshold:n?.speechThreshold??i.speechThreshold??c.speechThreshold,transcriptStabilityMs:n?.transcriptStabilityMs??i.transcriptStabilityMs??c.transcriptStabilityMs}};var ce={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:{qualityProfile:\"short-command\",profile:\"balanced\"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:\"continuous\",turnDetection:{qualityProfile:\"general\",profile:\"fast\"}},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:{qualityProfile:\"accent-heavy\",profile:\"long-form\"}},\"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:{qualityProfile:\"accent-heavy\",profile:\"long-form\"}},\"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:{qualityProfile:\"noisy-room\",profile:\"long-form\",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:{qualityProfile:\"noisy-room\",profile:\"long-form\",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:{qualityProfile:\"noisy-room\",profile:\"long-form\",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:{qualityProfile:\"noisy-room\",profile:\"long-form\"}}},wn=(n=\"default\")=>{let e=ce[n];return{audioConditioning:Ln(e.audioConditioning),capture:{channelCount:e.capture?.channelCount??1,sampleRateHz:e.capture?.sampleRateHz??16000},connection:{...e.connection},name:n,sttLifecycle:e.sttLifecycle??\"continuous\",turnDetection:Rn(e.turnDetection)}};var oe=(n)=>({assistantAudio:[...n.assistantAudio],assistantTexts:[...n.assistantTexts],call:n.call,error:n.error,isConnected:n.isConnected,isRecording:!1,partial:n.partial,reconnect:n.reconnect,recordingError:null,sessionId:n.sessionId,sessionMetadata:n.sessionMetadata,scenarioId:n.scenarioId,status:n.status,turns:[...n.turns]}),j=(n,e={})=>{let o=wn(e.preset),c=Vn(n,{...o.connection,...e.connection}),i=null,s=oe(c),t=new Set,r=()=>{for(let C of t)C()},g=()=>{if(s={...s,assistantAudio:[...c.assistantAudio],assistantTexts:[...c.assistantTexts],call:c.call,error:c.error,isConnected:c.isConnected,partial:c.partial,reconnect:c.reconnect,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,scenarioId:c.scenarioId,status:c.status,turns:[...c.turns]},e.autoStopOnComplete!==!1&&s.status===\"completed\"&&s.isRecording)i?.stop(),i=null,s={...s,isRecording:!1};r()},y=c.subscribe(g);g();let d=()=>{if(i)return i;return i=An({channelCount:e.capture?.channelCount??o.capture.channelCount,onLevel:e.capture?.onLevel,onAudio:(C)=>{if(e.capture?.onAudio){e.capture.onAudio(C,c.sendAudio);return}c.sendAudio(C)},sampleRateHz:e.capture?.sampleRateHz??o.capture.sampleRateHz}),i},h=()=>{i?.stop(),i=null,s={...s,isRecording:!1},r()},a=async()=>{if(s.isRecording)return;try{s={...s,recordingError:null},r(),await d().start(),s={...s,isRecording:!0},r()}catch(C){throw i=null,s={...s,isRecording:!1,recordingError:C instanceof Error?C.message:String(C)},r(),C}};return{bindHTMX(C){return gn(c,C)},callControl:(C)=>c.callControl(C),close:()=>{y(),h(),c.close()},endTurn:()=>c.endTurn(),get error(){return s.error},getServerSnapshot:()=>s,getSnapshot:()=>s,get isConnected(){return s.isConnected},get isRecording(){return s.isRecording},get partial(){return s.partial},get recordingError(){return s.recordingError},get reconnect(){return s.reconnect},sendAudio:(C)=>c.sendAudio(C),simulateDisconnect:()=>c.simulateDisconnect(),get sessionId(){return s.sessionId},get sessionMetadata(){return s.sessionMetadata},get scenarioId(){return s.scenarioId},startRecording:a,get status(){return s.status},stopRecording:h,subscribe:(C)=>{return t.add(C),()=>{t.delete(C)}},toggleRecording:async()=>{if(s.isRecording){h();return}await a()},get turns(){return s.turns},get assistantTexts(){return s.assistantTexts},get assistantAudio(){return s.assistantAudio},get call(){return s.call}}};var te=()=>({activeSourceCount:0,error:null,isActive:!1,isPlaying:!1,lastInterruptLatencyMs:void 0,lastPlaybackStopLatencyMs:void 0,processedChunkCount:0,queuedChunkCount:0}),ie=()=>{if(typeof window>\"u\")return typeof AudioContext>\"u\"?void 0:AudioContext;return window.AudioContext??window.webkitAudioContext},se=(n,e)=>{let o=e.format;if(o.container!==\"raw\"||o.encoding!==\"pcm_s16le\")throw Error(`Unsupported assistant audio format: ${o.container}/${o.encoding}`);let c=e.chunk,i=Math.max(1,o.channels),s=Math.floor(c.byteLength/2),t=Math.max(1,Math.floor(s/i)),r=n.createBuffer(i,t,o.sampleRateHz),g=new DataView(c.buffer,c.byteOffset,c.byteLength);for(let y=0;y<i;y+=1){let d=r.getChannelData(y);for(let h=0;h<t;h+=1){let M=(h*i+y)*2;if(M+1>=c.byteLength){d[h]=0;continue}d[h]=g.getInt16(M,!0)/32768}}return r},m=(n,e={})=>{let o=new Set,c=new Set,i=(e.lookaheadMs??15)/1000,s=te(),t=null,r=null,g=0,y=Promise.resolve(),d=null,h=null,a=null,M=null,C=()=>{for(let A of o)A()},I=(A)=>{s={...s,...A},C()},L=()=>{if(s.error!==null)I({error:null})},b=()=>{if(M!==null)clearTimeout(M),M=null},w=(A)=>{b(),d=null,I({activeSourceCount:c.size,isPlaying:!1,lastInterruptLatencyMs:A,lastPlaybackStopLatencyMs:s.lastPlaybackStopLatencyMs??A}),a?.(),a=null,h=null},u=(A)=>{if(!A)return 0;return Math.max(0,((A.baseLatency??0)+(A.outputLatency??0))*1000)},U=(A)=>{if(!r)return;let f=1;if(r.gain.setValueAtTime){r.gain.setValueAtTime(f,A?.currentTime??0);return}r.gain.value=f},l=(A)=>{if(!r)return;let f=0;if(r.gain.setValueAtTime){r.gain.setValueAtTime(f,A?.currentTime??0);return}r.gain.value=f},S=()=>{if(d===null||c.size>0)return;w(Date.now()-d)},R=async()=>{if(t)return t;if(e.createAudioContext)t=e.createAudioContext();else{let A=ie();if(!A)throw Error(\"Assistant audio playback requires AudioContext support.\");t=new A}if(t.createGain)r=t.createGain(),r.connect?.(t.destination);return g=t.currentTime,t},z=async(A)=>{let f=await R(),P=se(f,A),V=f.createBufferSource();V.buffer=P,V.connect(r??f.destination),V.onended=()=>{c.delete(V),V.disconnect?.(),I({activeSourceCount:c.size,isPlaying:c.size>0&&s.isActive}),S()};let Y=Math.max(f.currentTime+i,g);g=Y+P.duration,c.add(V),I({activeSourceCount:c.size,isPlaying:!0}),V.start(Y)},E=(A)=>{for(let f of[...c])f.stop?.();if(g=t?t.currentTime:0,A?.forceClear){for(let f of c)f.disconnect?.();c.clear(),S()}},W=async()=>{if(!s.isActive)return;let A=n.assistantAudio.slice(s.processedChunkCount);if(A.length===0)return;try{L();for(let f of A)await z(f);I({processedChunkCount:n.assistantAudio.length,queuedChunkCount:s.queuedChunkCount+A.length})}catch(f){I({error:f instanceof Error?f.message:String(f)})}},D=()=>{return y=y.then(()=>W(),()=>W()),y},$=n.subscribe(()=>{if(e.autoStart&&!s.isActive&&n.assistantAudio.length>0){N.start();return}if(s.isActive)D()}),N={close:async()=>{if($(),E({forceClear:!0}),b(),a?.(),a=null,h=null,d=null,t&&t.state!==\"closed\")await t.close();t=null,r?.disconnect?.(),r=null,g=0,I({activeSourceCount:0,isActive:!1,isPlaying:!1})},get activeSourceCount(){return s.activeSourceCount},get error(){return s.error},getSnapshot:()=>s,get isActive(){return s.isActive},get isPlaying(){return s.isPlaying},interrupt:async()=>{let A=Date.now(),f=await R();d=A,l(f);let P=Date.now()-A+u(f);if(I({isActive:!1,isPlaying:c.size>0,lastPlaybackStopLatencyMs:P}),c.size===0){w(P);return}if(!h)h=new Promise((V)=>{a=V});b(),M=setTimeout(()=>{for(let V of c)V.disconnect?.();c.clear(),w(Date.now()-A)},250),E(),await h},get lastInterruptLatencyMs(){return s.lastInterruptLatencyMs},get lastPlaybackStopLatencyMs(){return s.lastPlaybackStopLatencyMs},pause:async()=>{if(!t){I({activeSourceCount:0,isActive:!1,isPlaying:!1});return}await t.suspend(),I({activeSourceCount:c.size,isActive:!1,isPlaying:!1})},get processedChunkCount(){return s.processedChunkCount},get queuedChunkCount(){return s.queuedChunkCount},start:async()=>{try{L();let A=await R();if(U(A),A.state===\"suspended\")await A.resume();I({activeSourceCount:c.size,isActive:!0,isPlaying:A.state===\"running\"}),await D()}catch(A){throw I({error:A instanceof Error?A.message:String(A),isActive:!1,isPlaying:!1}),A}},subscribe:(A)=>{return o.add(A),()=>{o.delete(A)}}};return N};var le=()=>`barge-in:${Date.now()}:${crypto.randomUUID?.()??Math.random().toString(36).slice(2)}`,re=(n,e)=>{let o=n.filter((t)=>t.status===\"stopped\"),c=o.map((t)=>t.latencyMs).filter((t)=>typeof t===\"number\"),i=o.filter((t)=>typeof t.latencyMs===\"number\"&&t.latencyMs>e).length,s=o.length-i;return{averageLatencyMs:c.length>0?Math.round(c.reduce((t,r)=>t+r,0)/c.length):void 0,events:[...n],failed:i,lastEvent:n.at(-1),passed:s,status:n.length===0?\"empty\":i>0?\"fail\":o.length===0?\"warn\":\"pass\",thresholdMs:e,total:o.length}},bn=(n={})=>{let e=new Set,o=n.thresholdMs??250,c=n.fetch??globalThis.fetch,i=[],s=()=>{for(let g of e)g()},t=(g)=>{if(!n.path||typeof c!==\"function\")return;c(n.path,{body:JSON.stringify(g),headers:{\"Content-Type\":\"application/json\"},method:\"POST\"}).catch(()=>{})},r=(g,y)=>{let d={at:Date.now(),id:le(),latencyMs:y.latencyMs,playbackStopLatencyMs:y.playbackStopLatencyMs,reason:y.reason,sessionId:y.sessionId,status:g,thresholdMs:o};return i.push(d),t(d),s(),d};return{getSnapshot:()=>re(i,o),recordRequested:(g)=>r(\"requested\",g),recordSkipped:(g)=>r(\"skipped\",g),recordStopped:(g)=>r(\"stopped\",g),subscribe:(g)=>{return e.add(g),()=>{e.delete(g)}}}};var ae=0.08,de=(n,e={})=>(e.enabled??!0)&&n>=(e.interruptThreshold??ae),cn=(n,e,o={})=>{let c=n.partial,i=(t)=>{if(!e.isPlaying||o.enabled===!1){o.monitor?.recordSkipped({reason:t,sessionId:n.sessionId});return}o.monitor?.recordRequested({reason:t,sessionId:n.sessionId}),e.interrupt().then(()=>{o.monitor?.recordStopped({latencyMs:e.lastInterruptLatencyMs,playbackStopLatencyMs:e.lastPlaybackStopLatencyMs,reason:t,sessionId:n.sessionId})})},s=n.subscribe(()=>{if(o.interruptOnPartial===!1){c=n.partial;return}if(!c&&n.partial)i(\"partial-transcript\");c=n.partial});return{close:()=>{s()},handleLevel:(t)=>{if(de(t,o))i(\"input-level\")},sendAudio:(t)=>{i(\"manual-audio\"),n.sendAudio(t)}}};var rn=48,ge=320,Ae=88,he=\"Guided test\",ye=\"General recording\",Ce=\"Pick a scenario to begin the demo.\",fe=\"I can walk you through a short guided voice test.\",Te=\"I can capture one freeform recording and confirm that it landed.\",Se=\"Choose a scenario to begin. Guided test asks follow-up prompts. General recording just captures what you say.\",Me=\"Click Start general recording to capture one freeform answer.\",Pn=\"Speak freely. When you pause, the recording will be captured.\",sn=\"Recording saved. Start again if you want another capture.\",Dn=\"Guided test complete. Review the saved summary below.\",On=\"All prompts are covered. You can stop the microphone or keep speaking for extra detail.\",Ie=\"Ready. Start guided test or general recording to begin.\",Ve=\"Live. Answer the prompt, then click Stop microphone when finished.\",_n=[\"Start with a quick introduction about who you are.\",\"Now describe what you are trying to do or test.\",\"Finish with any detail that feels blocked, risky, or unclear.\"],xn=(n,e,o)=>Math.min(o,Math.max(e,n)),X=(n)=>n.replaceAll(\"&\",\"&amp;\").replaceAll(\"<\",\"&lt;\").replaceAll(\">\",\"&gt;\").replaceAll('\"',\"&quot;\").replaceAll(\"'\",\"&#39;\"),on=(n,e)=>{let o=n[e];if(typeof o===\"string\"&&o.trim())return o;return null},ln=(n)=>{if(typeof n===\"string\"&&n.trim())return n;if(n instanceof Error&&n.message.trim())return n.message;if(n&&typeof n===\"object\"){let e=n,o=on(e,\"message\")??on(e,\"reason\")??on(e,\"description\");if(o)return o;if(\"error\"in e)return ln(e.error);if(\"cause\"in e)return ln(e.cause);try{return JSON.stringify(n)}catch{}}return\"Unexpected error\"},Le=(n)=>{let e=[n.status];if(n.attempts>0||n.maxAttempts>0)e.push(`${n.attempts}/${n.maxAttempts} attempts`);if(n.nextAttemptAt){let o=Math.max(0,n.nextAttemptAt-Date.now());e.push(`retry in ${Math.ceil(o/100)/10}s`)}return e.join(\" \u00B7 \")},F=(n=rn)=>Array.from({length:n},()=>0),En=(n,e,o=rn)=>{let c=n.slice(-(o-1));c.push(xn(e,0,1));while(c.length<o)c.unshift(0);return c},Re=(n,e=ge,o=Ae)=>{let c=n.length>1?n:F(rn),i=e/(c.length-1),s=o/2,t=o*0.34;if(Math.max(...c,0)<=0.015)return`M 0 ${s} L ${e} ${s}`;let g=c.map((d,h)=>{let a=h*0.76,M=Math.sin(a)*0.78+Math.sin(a*0.41)*0.22,C=d*t,I=i*h,L=xn(s+M*C,8,o-8);return{x:I,y:L}});if(g.length===0)return`M 0 ${s} L ${e} ${s}`;let y=`M ${g[0]?.x??0} ${g[0]?.y??s}`;for(let d=1;d<g.length;d+=1){let h=g[d-1],a=g[d];if(!h||!a)continue;let M=(h.x+a.x)/2;y+=` Q ${M} ${h.y} ${a.x} ${a.y}`}return y},we=(n)=>{if(!n)return _n;try{let e=JSON.parse(n);if(Array.isArray(e)){let o=e.filter((c)=>typeof c===\"string\").map((c)=>c.trim()).filter(Boolean);if(o.length>0)return o}}catch{}return _n},tn=(n)=>{if(!n)return;let e=Number(n);return Number.isFinite(e)?e:void 0},be=(n,e,o)=>{if(!e)return null;let c=document.querySelector(e);return c instanceof o?c:null},x=(n,e,o,c)=>{let i=e?document.querySelector(e):null;if(i instanceof o)return i;let s=n.querySelector(`#${c}`);if(s instanceof o)return s;throw Error(`Voice HTMX bootstrap could not find the required element \"${c}\".`)},_e=(n)=>{if(!n.mode)return Ce;if(!n.hasStarted)return n.mode===\"guided\"?fe:Te;if(n.status===\"completed\")return n.mode===\"guided\"?Dn:sn;if(n.mode===\"general\")return Pn;return n.guidedPrompts[n.turnCount]??On},Ee=(n)=>{if(!n.mode)return Se;if(n.status===\"completed\")return n.mode===\"guided\"?Dn:sn;if(!n.hasStarted)return n.mode===\"guided\"?`Click Start guided test to begin. First prompt: ${n.guidedPrompts[0]??\"Answer the first prompt.\"}`:Me;if(n.mode===\"general\")return n.turnCount===0?Pn:sn;return n.guidedPrompts[n.turnCount]??On},Pe=(n)=>{let e=n.dataset.voiceGuidedPath,o=n.dataset.voiceGeneralPath;if(!e||!o)throw Error(\"Voice HTMX bootstrap requires data-voice-guided-path and data-voice-general-path.\");let c=we(n.dataset.voiceGuidedPrompts),i=n.dataset.voiceGuidedLabel??he,s=n.dataset.voiceGeneralLabel??ye,t=n.dataset.voiceReconnectReportPath,r=n.dataset.voiceBargeInPath,g=r?bn({path:r,thresholdMs:tn(n.dataset.voiceBargeInThresholdMs)}):null,y=tn(n.dataset.voiceBargeInRecentWindowMs)??4000,d=tn(n.dataset.voiceBargeInSpeechThreshold)??0.04,h=x(document,n.dataset.voiceSync,HTMLElement,\"voice-htmx-sync\"),a=x(n,n.dataset.voiceConnection,HTMLElement,\"metric-connection\"),M=x(n,n.dataset.voiceError,HTMLElement,\"status-error\"),C=x(n,n.dataset.voiceMicrophone,HTMLElement,\"status-mic\"),I=x(n,n.dataset.voicePrompt,HTMLElement,\"status-prompt\"),L=be(n,n.dataset.voiceReconnect,HTMLElement),b=x(n,n.dataset.voiceChat,HTMLElement,\"chat-list\"),w=x(n,n.dataset.voiceStartGuided,HTMLButtonElement,\"start-guided\"),u=x(n,n.dataset.voiceStartGeneral,HTMLButtonElement,\"start-general\"),U=x(n,n.dataset.voiceStop,HTMLButtonElement,\"stop-mic\"),l=x(n,n.dataset.voiceMonitor,HTMLElement,\"voice-monitor\"),S=x(n,n.dataset.voiceMonitorCopy,HTMLElement,\"voice-monitor-copy\"),R=x(n,n.dataset.voiceWaveGlow,SVGPathElement,\"voice-wave-glow\"),z=x(n,n.dataset.voiceWavePath,SVGPathElement,\"voice-wave-path\"),E=null,W={general:!1,guided:!1},D=!1,$=null,N=F(),A=null,f=null,P=j(e,{capture:{onAudio:(T,G)=>{if(A){A.sendAudio(T);return}G(T)},onLevel:(T)=>{A?.handleLevel(T),N=En(N,T),v()}},connection:{reconnectReportPath:t},preset:\"guided-intake\"}),V=j(o,{capture:{onAudio:(T,G)=>{if(f){f.sendAudio(T);return}G(T)},onLevel:(T)=>{f?.handleLevel(T),N=En(N,T),v()}},connection:{reconnectReportPath:t},preset:\"dictation\"}),Y=P.bindHTMX({element:h}),un=V.bindHTMX({element:h}),J=m(P),Q=m(V);A=cn(P,J,{interruptThreshold:d,monitor:g??void 0}),f=cn(V,Q,{interruptThreshold:d,monitor:g??void 0});let k=()=>E===\"general\"?V:P,Oe=()=>E===\"general\"?Q:J,v=()=>{let T=Re(N);R.setAttribute(\"d\",T),z.setAttribute(\"d\",T),S.innerHTML=`<span class=\"voice-live-dot\"></span>${D?\"Microphone live\":\"Microphone idle\"}`,S.classList.toggle(\"is-live\",D),l.classList.toggle(\"is-live\",D)},q=()=>{let T=k(),G=(E?W[E]:!1)||T.turns.length>0,dn=T.status;if(a.textContent=T.isConnected?\"Connected\":\"Waiting\",M.textContent=$||T.error||\"None\",L)L.textContent=Le(T.reconnect);C.textContent=D?Ve:Ie,I.textContent=Ee({guidedPrompts:c,hasStarted:G,mode:E,status:dn,turnCount:T.turns.length}),w.hidden=D,u.hidden=D,U.hidden=!D,b.innerHTML=`<article class=\"voice-chat-message assistant\">\n <div class=\"voice-chat-role\">${X(E===\"general\"?s:E===\"guided\"?i:\"Voice demo\")}</div>\n <p class=\"voice-turn-text\">${X(_e({generalLabel:s,guidedLabel:i,guidedPrompts:c,hasStarted:G,mode:E,status:dn,turnCount:T.turns.length}))}</p>\n</article>${T.turns.map((p)=>`<div class=\"voice-chat-stack\">\n <article class=\"voice-chat-message user\">\n <div class=\"voice-chat-role\">You</div>\n <p class=\"voice-turn-text\">${X(p.text)}</p>\n </article>\n ${p.assistantText?`<article class=\"voice-chat-message assistant\">\n <div class=\"voice-chat-role\">${X(E===\"general\"?s:E===\"guided\"?i:\"Guide\")}</div>\n <p class=\"voice-turn-text\">${X(p.assistantText)}</p>\n </article>`:\"\"}\n</div>`).join(\"\")}${T.partial?`<article class=\"voice-chat-message user pending\">\n <div class=\"voice-chat-role\">Speaking</div>\n <p class=\"voice-turn-text\">${X(T.partial)}</p>\n</article>`:\"\"}`,v()},Un=()=>{k().stopRecording(),D=!1,$=null,N=F(),q()},an=async(T)=>{E=T,W={...W,[T]:!0};try{await k().startRecording(),$=null,D=!0,q()}catch(G){k().stopRecording(),D=!1,N=F(),$=ln(G),q()}};P.subscribe(()=>{if(P.assistantAudio.length>0)J.start().catch(()=>{});q()}),V.subscribe(()=>{if(V.assistantAudio.length>0)Q.start().catch(()=>{});q()}),w.addEventListener(\"click\",()=>{an(\"guided\")}),u.addEventListener(\"click\",()=>{an(\"general\")}),U.addEventListener(\"click\",()=>{Un()}),n.addEventListener(\"absolute-voice-simulate-disconnect\",()=>{k().simulateDisconnect()}),window.addEventListener(\"beforeunload\",()=>{P.stopRecording(),V.stopRecording(),A?.close(),f?.close(),J.close(),Q.close(),Y(),un(),P.close(),V.close()}),q()},De=()=>{if(typeof window>\"u\"||typeof document>\"u\")return;let n=Array.from(document.querySelectorAll(\"[data-voice-htmx]\"));for(let e of n)if(e instanceof HTMLElement)Pe(e)};De();export{De as initVoiceHTMX};\n";
1
+ export declare const HTMX_BOOTSTRAP_BUNDLE = "var Hn=(n)=>{if(typeof n!==\"string\")return n;return document.querySelector(n)},Gn=(n,c,o,e)=>{let i=c??n.getAttribute(\"hx-get\")??\"\";if(!i)return\"\";let l=new URL(i,window.location.origin);if(e)l.searchParams.set(o,e);else l.searchParams.delete(o);return`${l.pathname}${l.search}${l.hash}`},gn=(n,c)=>{if(typeof window>\"u\"||typeof document>\"u\")return()=>{};let o=Hn(c.element);if(!o)return()=>{};let e=c.eventName??\"voice-refresh\",i=c.sessionQueryParam??\"sessionId\",l=()=>{let d=window,g=Gn(o,c.route,i,n.sessionId);if(g)o.setAttribute(\"hx-get\",g);d.htmx?.process?.(o),d.htmx?.trigger?.(o,e)},t=n.subscribe(l);return l(),()=>{t()}};var Bn=(n)=>Math.max(-1,Math.min(1,n)),Wn=(n)=>{let c=new Int16Array(n.length);for(let o=0;o<n.length;o+=1){let e=Bn(n[o]??0);c[o]=e<0?e*32768:e*32767}return new Uint8Array(c.buffer)},$n=(n)=>{let c=n instanceof Uint8Array?n:new Uint8Array(n);if(c.byteLength<2)return 0;let o=new Int16Array(c.buffer,c.byteOffset,Math.floor(c.byteLength/2));if(o.length===0)return 0;let e=0;for(let i of o){let l=i/32768;e+=l*l}return Math.min(1,Math.max(0,Math.sqrt(e/o.length)*5.5))},qn=(n,c,o)=>{if(c===o)return n;let e=c/o,i=Math.round(n.length/e),l=new Float32Array(i),t=0,d=0;while(t<l.length){let g=Math.round((t+1)*e),y=0,a=0;for(let h=d;h<g&&h<n.length;h+=1)y+=n[h]??0,a+=1;l[t]=a>0?y/a:0,t+=1,d=g}return l},An=(n)=>{let c=null,o=null,e=null,i=null;return{start:async()=>{if(typeof navigator>\"u\"||!navigator.mediaDevices?.getUserMedia)throw Error(\"Browser microphone capture requires navigator.mediaDevices.getUserMedia.\");let d=(typeof window<\"u\"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!d)throw Error(\"Browser microphone capture requires AudioContext support.\");i=await navigator.mediaDevices.getUserMedia({audio:{channelCount:n.channelCount??1}}),c=new d,o=c.createMediaStreamSource(i),e=c.createScriptProcessor(4096,1,1),e.onaudioprocess=(g)=>{let y=g.inputBuffer.getChannelData(0),a=qn(y,c?.sampleRate??48000,n.sampleRateHz??16000),h=Wn(a);n.onLevel?.($n(h)),n.onAudio(h)},o.connect(e),e.connect(c.destination)},stop:()=>{e?.disconnect(),o?.disconnect(),i?.getTracks().forEach((d)=>d.stop()),c?.close(),n.onLevel?.(0),c=null,i=null,e=null,o=null}}};var nn=(n)=>{if(typeof n===\"string\"&&n.trim())return n;if(n instanceof Error&&n.message.trim())return n.message;if(n&&typeof n===\"object\"){let c=n;for(let o of[\"message\",\"reason\",\"description\"]){let e=c[o];if(typeof e===\"string\"&&e.trim())return e}if(\"error\"in c)return nn(c.error);if(\"cause\"in c)return nn(c.cause);try{return JSON.stringify(n)}catch{}}return\"Unexpected error\"},hn=(n)=>{switch(n.type){case\"audio\":return{chunk:Uint8Array.from(atob(n.chunkBase64),(c)=>c.charCodeAt(0)),format:n.format,receivedAt:n.receivedAt,turnId:n.turnId,type:\"audio\"};case\"assistant\":return{text:n.text,type:\"assistant\"};case\"complete\":return{sessionId:n.sessionId,type:\"complete\"};case\"connection\":return{reconnect:n.reconnect,type:\"connection\"};case\"call_lifecycle\":return{event:n.event,sessionId:n.sessionId,type:\"call_lifecycle\"};case\"error\":return{message:nn(n.message),type:\"error\"};case\"final\":return{transcript:n.transcript,type:\"final\"};case\"partial\":return{transcript:n.transcript,type:\"partial\"};case\"replay\":return{assistantTexts:n.assistantTexts,call:n.call,partial:n.partial,scenarioId:n.scenarioId,sessionId:n.sessionId,sessionMetadata:n.sessionMetadata,status:n.status,turns:n.turns,type:\"replay\"};case\"session\":return{sessionId:n.sessionId,sessionMetadata:n.sessionMetadata,scenarioId:n.scenarioId,status:n.status,type:\"session\"};case\"turn\":return{turn:n.turn,type:\"turn\"};default:return null}};var Hc=Math.PI*2;var G=(n,c,o,e)=>{n.push({code:o,message:e,severity:c})};var Xn=(n)=>n.length===0?void 0:n.reduce((c,o)=>c+o,0)/n.length,K=(n)=>n.length===0?void 0:Math.max(...n);var b=(n,c)=>{let o=n[c];return typeof o===\"number\"&&Number.isFinite(o)?o:void 0},Z=(n,c)=>{let o=n[c];return typeof o===\"boolean\"?o:void 0},O=(n,c)=>{let o=n[c];return typeof o===\"string\"?o:void 0},cn=(n)=>String(n.id??O(n,\"ssrc\")??b(n,\"ssrc\")??O(n,\"trackIdentifier\")??O(n,\"mid\")??\"unknown\"),yn=(n)=>n===void 0?void 0:n*1000;var un=(n)=>{let c={};for(let[o,e]of Object.entries(n))if(e===null||typeof e===\"boolean\"||typeof e===\"number\"||typeof e===\"string\")c[o]=e;return c};var Cn=(n={})=>{let c=n.stats??[],o=[],e=c.filter((s)=>s.type===\"inbound-rtp\"&&O(s,\"kind\")!==\"video\"),i=c.filter((s)=>s.type===\"outbound-rtp\"&&O(s,\"kind\")!==\"video\"),l=c.filter((s)=>s.type===\"candidate-pair\"),t=c.filter((s)=>(s.type===\"track\"||s.type===\"media-source\")&&O(s,\"kind\")===\"audio\"),d=l.filter((s)=>Z(s,\"selected\")===!0||Z(s,\"nominated\")===!0||O(s,\"state\")===\"succeeded\").length,g=t.filter((s)=>O(s,\"readyState\")!==\"ended\"&&O(s,\"trackState\")!==\"ended\"&&Z(s,\"ended\")!==!0).length,y=t.filter((s)=>O(s,\"readyState\")===\"ended\"||O(s,\"trackState\")===\"ended\"||Z(s,\"ended\")===!0).length,a=e.reduce((s,V)=>s+(b(V,\"packetsReceived\")??0),0),h=i.reduce((s,V)=>s+(b(V,\"packetsSent\")??0),0),r=[...e,...i].reduce((s,V)=>s+Math.max(0,b(V,\"packetsLost\")??0),0),M=a+r,C=M===0?0:r/M,S=e.reduce((s,V)=>s+(b(V,\"bytesReceived\")??0),0),w=i.reduce((s,V)=>s+(b(V,\"bytesSent\")??0),0),E=K(l.map((s)=>yn(b(s,\"currentRoundTripTime\")??b(s,\"roundTripTime\"))).filter((s)=>s!==void 0)),_=K([...e,...i].map((s)=>yn(b(s,\"jitter\"))).filter((s)=>s!==void 0)),U=K(e.map((s)=>{let V=b(s,\"jitterBufferDelay\"),R=b(s,\"jitterBufferEmittedCount\");return V!==void 0&&R!==void 0&&R>0?V/R*1000:void 0}).filter((s)=>s!==void 0)),N=t.map((s)=>b(s,\"audioLevel\")).filter((s)=>s!==void 0);if(n.requireConnectedCandidatePair&&l.length>0&&d===0)G(o,\"error\",\"media.webrtc_candidate_pair_missing\",\"No active WebRTC candidate pair was observed.\");if(n.requireLiveAudioTrack&&g===0)G(o,\"error\",\"media.webrtc_audio_track_missing\",\"No live WebRTC audio track was observed.\");if(n.maxPacketLossRatio!==void 0&&C>n.maxPacketLossRatio)G(o,\"warning\",\"media.webrtc_packet_loss\",`Observed WebRTC packet loss ratio ${String(C)} above ${String(n.maxPacketLossRatio)}.`);if(n.maxRoundTripTimeMs!==void 0&&E!==void 0&&E>n.maxRoundTripTimeMs)G(o,\"warning\",\"media.webrtc_round_trip_time\",`Observed WebRTC RTT ${String(E)}ms above ${String(n.maxRoundTripTimeMs)}ms.`);if(n.maxJitterMs!==void 0&&_!==void 0&&_>n.maxJitterMs)G(o,\"warning\",\"media.webrtc_jitter\",`Observed WebRTC jitter ${String(_)}ms above ${String(n.maxJitterMs)}ms.`);return{activeCandidatePairs:d,audioLevelAverage:Xn(N),bytesReceived:S,bytesSent:w,checkedAt:Date.now(),endedAudioTracks:y,inboundPackets:a,issues:o,jitterBufferDelayMs:U,jitterMs:_,liveAudioTracks:g,outboundPackets:h,packetLossRatio:C,packetsLost:r,roundTripTimeMs:E,status:o.some((s)=>s.severity===\"error\")?\"fail\":o.length>0?\"warn\":\"pass\",totalStats:c.length}},Tn=async(n)=>{return[...(await n.peerConnection.getStats(n.selector??null)).values()].map(un)};var In=(n={})=>{let c=n.stats??[],o=n.previousStats??[],e=[],i=new Map(o.map((r)=>[cn(r),r])),t=c.filter((r)=>(r.type===\"inbound-rtp\"||r.type===\"outbound-rtp\")&&O(r,\"kind\")!==\"video\"&&O(r,\"mediaType\")!==\"video\").map((r)=>{let M=r.type===\"outbound-rtp\"?\"outbound\":\"inbound\",C=M===\"outbound\"?\"packetsSent\":\"packetsReceived\",S=M===\"outbound\"?\"bytesSent\":\"bytesReceived\",w=i.get(cn(r)),E=b(r,C),_=w?b(w,C):void 0,U=b(r,S),N=w?b(w,S):void 0,s=r.timestamp!==void 0&&w?.timestamp!==void 0?r.timestamp-w.timestamp:void 0;return{bytesDelta:U!==void 0&&N!==void 0?U-N:void 0,currentPackets:E,direction:M,id:cn(r),packetDelta:E!==void 0&&_!==void 0?E-_:void 0,previousPackets:_,timeDeltaMs:s}}),d=t.filter((r)=>r.direction===\"inbound\"),g=t.filter((r)=>r.direction===\"outbound\"),y=K(t.map((r)=>r.timeDeltaMs).filter((r)=>r!==void 0)),a=d.filter((r)=>n.maxInboundPacketStallMs!==void 0&&r.timeDeltaMs!==void 0&&r.timeDeltaMs>=n.maxInboundPacketStallMs&&r.packetDelta!==void 0&&r.packetDelta<=0).length,h=g.filter((r)=>n.maxOutboundPacketStallMs!==void 0&&r.timeDeltaMs!==void 0&&r.timeDeltaMs>=n.maxOutboundPacketStallMs&&r.packetDelta!==void 0&&r.packetDelta<=0).length;if(n.requireInboundAudio&&d.length===0)G(e,\"error\",\"media.webrtc_inbound_audio_missing\",\"No inbound WebRTC audio RTP stream was observed.\");if(n.requireOutboundAudio&&g.length===0)G(e,\"error\",\"media.webrtc_outbound_audio_missing\",\"No outbound WebRTC audio RTP stream was observed.\");if(n.maxGapMs!==void 0&&y!==void 0&&y>n.maxGapMs)G(e,\"warning\",\"media.webrtc_stream_gap\",`Observed WebRTC stream sample gap ${String(y)}ms above ${String(n.maxGapMs)}ms.`);if(a>0)G(e,\"error\",\"media.webrtc_inbound_stalled\",`${String(a)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(h>0)G(e,\"error\",\"media.webrtc_outbound_stalled\",`${String(h)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:d.length,issues:e,maxObservedGapMs:y,outboundAudioStreams:g.length,stalledInboundStreams:a,stalledOutboundStreams:h,status:e.some((r)=>r.severity===\"error\")?\"fail\":e.length>0?\"warn\":\"pass\",streams:t,totalStats:c.length}};var Yn=\"/api/voice/browser-media\",Jn=5000,Qn=async(n)=>n.peerConnection??await n.getPeerConnection?.()??null,kn=async(n,c)=>{let o=c.fetch??globalThis.fetch;if(!o)return;await o(c.path??Yn,{body:JSON.stringify(n),headers:{\"Content-Type\":\"application/json\"},keepalive:!0,method:\"POST\"})},Vn=(n)=>{let c=null,o=[],e=async()=>{let t=await Qn(n);if(!t)return;let d=await Tn({peerConnection:t}),g=Cn({...n,stats:d}),y=n.continuity===!1?void 0:In({...n.continuity,previousStats:o,stats:d}),a={at:Date.now(),continuity:y,report:g,scenarioId:n.getScenarioId?.()??null,sessionId:n.getSessionId?.()??null};return o=d,n.onReport?.(a),await kn(a,n),a},i=()=>{e().catch((t)=>{n.onError?.(t)})},l=()=>{if(c)clearInterval(c),c=null};return{close:l,reportOnce:e,start:()=>{if(c)return;i(),c=setInterval(i,n.intervalMs??Jn)},stop:l}};var W=()=>{},zn=()=>W,Zn={callControl:W,close:W,endTurn:W,getReadyState:()=>3,getScenarioId:()=>\"\",getSessionId:()=>\"\",send:W,sendAudio:W,simulateDisconnect:W,start:()=>{},subscribe:zn},Kn=()=>crypto.randomUUID(),jn=(n,c,o)=>{let{hostname:e,port:i,protocol:l}=window.location,t=l===\"https:\"?\"wss:\":\"ws:\",d=i?`:${i}`:\"\",g=new URL(`${t}//${e}${d}${n}`);if(g.searchParams.set(\"sessionId\",c),o)g.searchParams.set(\"scenarioId\",o);return g.toString()},Fn=(n)=>{if(!n||typeof n!==\"object\"||!(\"type\"in n))return!1;switch(n.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}},vn=(n)=>{if(typeof n.data!==\"string\")return null;try{let c=JSON.parse(n.data);return Fn(c)?c:null}catch{return null}},Mn=(n,c={})=>{if(typeof window>\"u\")return Zn;let o=new Set,e=c.reconnect!==!1,i=c.maxReconnectAttempts??10,l=c.pingInterval??30000,t={isConnected:!1,pendingMessages:[],scenarioId:c.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:c.sessionId??Kn(),ws:null},d=(s)=>{o.forEach((V)=>V(s))},g=()=>{if(t.pingInterval)clearInterval(t.pingInterval),t.pingInterval=null;if(t.reconnectTimeout)clearTimeout(t.reconnectTimeout),t.reconnectTimeout=null},y=()=>{if(t.ws?.readyState!==1)return;while(t.pendingMessages.length>0){let s=t.pendingMessages.shift();if(s!==void 0)t.ws.send(s)}},a=()=>{let s=Date.now()+500;t.reconnectAttempts+=1,d({reconnect:{attempts:t.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:i,nextAttemptAt:s,status:\"reconnecting\"},type:\"connection\"}),t.reconnectTimeout=setTimeout(()=>{if(t.reconnectAttempts>i){d({reconnect:{attempts:t.reconnectAttempts,maxAttempts:i,status:\"exhausted\"},type:\"connection\"});return}h()},500)},h=()=>{let s=new WebSocket(jn(n,t.sessionId,t.scenarioId));s.binaryType=\"arraybuffer\",s.onopen=()=>{let V=t.reconnectAttempts>0;if(t.isConnected=!0,y(),V)d({reconnect:{attempts:t.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:i,status:\"resumed\"},type:\"connection\"}),t.reconnectAttempts=0;o.forEach((R)=>R({scenarioId:t.scenarioId??void 0,sessionId:t.sessionId,status:\"active\",type:\"session\"})),t.pingInterval=setInterval(()=>{if(s.readyState===1)s.send(JSON.stringify({type:\"ping\"}))},l)},s.onmessage=(V)=>{let R=vn(V);if(!R)return;if(R.type===\"session\")t.sessionId=R.sessionId,t.scenarioId=R.scenarioId??t.scenarioId;o.forEach((Y)=>Y(R))},s.onclose=(V)=>{if(t.isConnected=!1,g(),e&&V.code!==1000&&t.reconnectAttempts<i)a();else if(e&&V.code!==1000)d({reconnect:{attempts:t.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:i,status:\"exhausted\"},type:\"connection\"})},t.ws=s},r=(s)=>{if(t.ws?.readyState===1){t.ws.send(s);return}t.pendingMessages.push(s)},M=(s)=>{r(JSON.stringify(s))},C=(s={})=>{if(s.sessionId)t.sessionId=s.sessionId;if(s.scenarioId)t.scenarioId=s.scenarioId;M({type:\"start\",sessionId:t.sessionId,scenarioId:t.scenarioId??void 0})},S=(s)=>{r(s)},w=()=>{M({type:\"end_turn\"})},E=(s)=>{M({...s,type:\"call_control\"})},_=()=>{if(g(),t.ws)t.ws.close(1000),t.ws=null;t.isConnected=!1,o.clear()},U=()=>{if(t.ws?.readyState===1)t.ws.close(4000,\"absolutejs-voice-reconnect-proof\")},N=(s)=>{return o.add(s),()=>{o.delete(s)}};return h(),{callControl:E,close:_,endTurn:w,getReadyState:()=>t.ws?.readyState??3,getScenarioId:()=>t.scenarioId??\"\",getSessionId:()=>t.sessionId,send:M,sendAudio:S,simulateDisconnect:U,start:C,subscribe:N}};var mn=()=>({attempts:0,maxAttempts:0,status:\"idle\"}),pn=()=>({assistantAudio:[],assistantTexts:[],call:null,error:null,isConnected:!1,sessionMetadata:null,scenarioId:null,partial:\"\",reconnect:mn(),sessionId:null,status:\"idle\",turns:[]}),Sn=()=>{let n=pn(),c=new Set,o=()=>{c.forEach((i)=>i())};return{dispatch:(i)=>{switch(i.type){case\"audio\":n={...n,assistantAudio:[...n.assistantAudio,{chunk:i.chunk,format:i.format,receivedAt:i.receivedAt,turnId:i.turnId}]};break;case\"assistant\":n={...n,assistantTexts:[...n.assistantTexts,i.text]};break;case\"complete\":n={...n,sessionId:i.sessionId,status:\"completed\"};break;case\"call_lifecycle\":n={...n,call:{...n.call,disposition:i.event.type===\"end\"?i.event.disposition:n.call?.disposition,endedAt:i.event.type===\"end\"?i.event.at:n.call?.endedAt,events:[...n.call?.events??[],i.event],lastEventAt:i.event.at,startedAt:n.call?.startedAt??i.event.at},sessionId:i.sessionId};break;case\"connected\":n={...n,isConnected:!0,reconnect:n.reconnect.status===\"reconnecting\"?{...n.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:\"resumed\"}:n.reconnect};break;case\"connection\":n={...n,reconnect:i.reconnect};break;case\"disconnected\":n={...n,isConnected:!1};break;case\"error\":n={...n,error:i.message};break;case\"final\":n={...n,partial:i.transcript.text,turns:n.turns.map((l)=>l)};break;case\"partial\":n={...n,partial:i.transcript.text};break;case\"replay\":n={...n,assistantTexts:[...i.assistantTexts],call:i.call??null,error:null,isConnected:i.status===\"active\",partial:i.partial,reconnect:n.reconnect.status===\"reconnecting\"?{...n.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:\"resumed\"}:n.reconnect,scenarioId:i.scenarioId??n.scenarioId,sessionId:i.sessionId,sessionMetadata:i.sessionMetadata??n.sessionMetadata,status:i.status,turns:[...i.turns]};break;case\"session\":n={...n,error:null,scenarioId:i.scenarioId??n.scenarioId,isConnected:i.status===\"active\",sessionId:i.sessionId,sessionMetadata:i.sessionMetadata??n.sessionMetadata,status:i.status};break;case\"turn\":n={...n,partial:\"\",turns:[...n.turns,i.turn]};break}o()},getServerSnapshot:()=>n,getSnapshot:()=>n,subscribe:(i)=>{return c.add(i),()=>{c.delete(i)}}}};var Ln=(n,c={})=>{let o=Mn(n,c),e=Sn(),i=c.browserMedia&&typeof window<\"u\"?Vn({...c.browserMedia,getScenarioId:()=>c.browserMedia?c.browserMedia.getScenarioId?.()??o.getScenarioId():o.getScenarioId(),getSessionId:()=>c.browserMedia?c.browserMedia.getSessionId?.()??o.getSessionId():o.getSessionId()}):null,l=new Set,t=(a)=>Promise.resolve().then(()=>{if(!a?.sessionId&&!a?.scenarioId)return;o.start(a),i?.start()}),d=()=>{l.forEach((a)=>a())},g=()=>{if(!c.reconnectReportPath||typeof fetch>\"u\")return;let a=e.getSnapshot(),h=JSON.stringify({at:Date.now(),reconnect:a.reconnect,scenarioId:a.scenarioId,sessionId:o.getSessionId(),turnIds:a.turns.map((r)=>r.id)});fetch(c.reconnectReportPath,{body:h,headers:{\"Content-Type\":\"application/json\"},keepalive:!0,method:\"POST\"}).catch(()=>{})},y=o.subscribe((a)=>{let h=hn(a);if(h){if(e.dispatch(h),a.type===\"connection\")g();d()}});return{callControl(a){o.callControl(a)},close(){y(),i?.close(),o.close(),e.dispatch({type:\"disconnected\"}),d()},endTurn(){o.endTurn()},get error(){return e.getSnapshot().error},getServerSnapshot(){return e.getServerSnapshot()},getSnapshot(){return e.getSnapshot()},get isConnected(){return e.getSnapshot().isConnected},get scenarioId(){return e.getSnapshot().scenarioId},get sessionMetadata(){return e.getSnapshot().sessionMetadata},start:t,get partial(){return e.getSnapshot().partial},get reconnect(){return e.getSnapshot().reconnect},get sessionId(){return o.getSessionId()},get status(){return e.getSnapshot().status},get turns(){return e.getSnapshot().turns},get assistantTexts(){return e.getSnapshot().assistantTexts},get assistantAudio(){return e.getSnapshot().assistantAudio},get call(){return e.getSnapshot().call},sendAudio(a){o.sendAudio(a)},simulateDisconnect(){o.simulateDisconnect()},subscribe(a){return l.add(a),()=>{l.delete(a)}}}};var wn=(n)=>{if(!n||n.enabled===!1)return;return{enabled:!0,maxGain:n.maxGain??3,noiseGateAttenuation:n.noiseGateAttenuation??0.15,noiseGateThreshold:n.noiseGateThreshold??0.006,targetLevel:n.targetLevel??0.08}};var nc={balanced:{qualityProfile:\"general\",silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:\"general\",silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},\"long-form\":{qualityProfile:\"general\",silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},cc={general:{},\"accent-heavy\":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},\"noisy-room\":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},\"short-command\":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var Rn=(n)=>{let c=n?.profile??\"fast\",o=n?.qualityProfile??\"general\",e=nc[c],i=cc[o];return{profile:c,qualityProfile:o,silenceMs:n?.silenceMs??i.silenceMs??e.silenceMs,speechThreshold:n?.speechThreshold??i.speechThreshold??e.speechThreshold,transcriptStabilityMs:n?.transcriptStabilityMs??i.transcriptStabilityMs??e.transcriptStabilityMs}};var ec={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:{qualityProfile:\"short-command\",profile:\"balanced\"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:\"continuous\",turnDetection:{qualityProfile:\"general\",profile:\"fast\"}},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:{qualityProfile:\"accent-heavy\",profile:\"long-form\"}},\"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:{qualityProfile:\"accent-heavy\",profile:\"long-form\"}},\"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:{qualityProfile:\"noisy-room\",profile:\"long-form\",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:{qualityProfile:\"noisy-room\",profile:\"long-form\",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:{qualityProfile:\"noisy-room\",profile:\"long-form\",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:{qualityProfile:\"noisy-room\",profile:\"long-form\"}}},_n=(n=\"default\")=>{let c=ec[n];return{audioConditioning:wn(c.audioConditioning),capture:{channelCount:c.capture?.channelCount??1,sampleRateHz:c.capture?.sampleRateHz??16000},connection:{...c.connection},name:n,sttLifecycle:c.sttLifecycle??\"continuous\",turnDetection:Rn(c.turnDetection)}};var oc=(n)=>({assistantAudio:[...n.assistantAudio],assistantTexts:[...n.assistantTexts],call:n.call,error:n.error,isConnected:n.isConnected,isRecording:!1,partial:n.partial,reconnect:n.reconnect,recordingError:null,sessionId:n.sessionId,sessionMetadata:n.sessionMetadata,scenarioId:n.scenarioId,status:n.status,turns:[...n.turns]}),j=(n,c={})=>{let o=_n(c.preset),e=Ln(n,{...o.connection,...c.connection}),i=null,l=oc(e),t=new Set,d=()=>{for(let C of t)C()},g=()=>{if(l={...l,assistantAudio:[...e.assistantAudio],assistantTexts:[...e.assistantTexts],call:e.call,error:e.error,isConnected:e.isConnected,partial:e.partial,reconnect:e.reconnect,sessionId:e.sessionId,sessionMetadata:e.sessionMetadata,scenarioId:e.scenarioId,status:e.status,turns:[...e.turns]},c.autoStopOnComplete!==!1&&l.status===\"completed\"&&l.isRecording)i?.stop(),i=null,l={...l,isRecording:!1};d()},y=e.subscribe(g);g();let a=()=>{if(i)return i;return i=An({channelCount:c.capture?.channelCount??o.capture.channelCount,onLevel:c.capture?.onLevel,onAudio:(C)=>{if(c.capture?.onAudio){c.capture.onAudio(C,e.sendAudio);return}e.sendAudio(C)},sampleRateHz:c.capture?.sampleRateHz??o.capture.sampleRateHz}),i},h=()=>{i?.stop(),i=null,l={...l,isRecording:!1},d()},r=async()=>{if(l.isRecording)return;try{l={...l,recordingError:null},d(),await a().start(),l={...l,isRecording:!0},d()}catch(C){throw i=null,l={...l,isRecording:!1,recordingError:C instanceof Error?C.message:String(C)},d(),C}};return{bindHTMX(C){return gn(e,C)},callControl:(C)=>e.callControl(C),close:()=>{y(),h(),e.close()},endTurn:()=>e.endTurn(),get error(){return l.error},getServerSnapshot:()=>l,getSnapshot:()=>l,get isConnected(){return l.isConnected},get isRecording(){return l.isRecording},get partial(){return l.partial},get recordingError(){return l.recordingError},get reconnect(){return l.reconnect},sendAudio:(C)=>e.sendAudio(C),simulateDisconnect:()=>e.simulateDisconnect(),get sessionId(){return l.sessionId},get sessionMetadata(){return l.sessionMetadata},get scenarioId(){return l.scenarioId},startRecording:r,get status(){return l.status},stopRecording:h,subscribe:(C)=>{return t.add(C),()=>{t.delete(C)}},toggleRecording:async()=>{if(l.isRecording){h();return}await r()},get turns(){return l.turns},get assistantTexts(){return l.assistantTexts},get assistantAudio(){return l.assistantAudio},get call(){return l.call}}};var tc=()=>({activeSourceCount:0,error:null,isActive:!1,isPlaying:!1,lastInterruptLatencyMs:void 0,lastPlaybackStopLatencyMs:void 0,processedChunkCount:0,queuedChunkCount:0}),ic=()=>{if(typeof window>\"u\")return typeof AudioContext>\"u\"?void 0:AudioContext;return window.AudioContext??window.webkitAudioContext},lc=(n,c)=>{let o=c.format;if(o.container!==\"raw\"||o.encoding!==\"pcm_s16le\")throw Error(`Unsupported assistant audio format: ${o.container}/${o.encoding}`);let e=c.chunk,i=Math.max(1,o.channels),l=Math.floor(e.byteLength/2),t=Math.max(1,Math.floor(l/i)),d=n.createBuffer(i,t,o.sampleRateHz),g=new DataView(e.buffer,e.byteOffset,e.byteLength);for(let y=0;y<i;y+=1){let a=d.getChannelData(y);for(let h=0;h<t;h+=1){let M=(h*i+y)*2;if(M+1>=e.byteLength){a[h]=0;continue}a[h]=g.getInt16(M,!0)/32768}}return d},F=(n,c={})=>{let o=new Set,e=new Set,i=(c.lookaheadMs??15)/1000,l=tc(),t=null,d=null,g=0,y=Promise.resolve(),a=null,h=null,r=null,M=null,C=()=>{for(let A of o)A()},S=(A)=>{l={...l,...A},C()},w=()=>{if(l.error!==null)S({error:null})},E=()=>{if(M!==null)clearTimeout(M),M=null},_=(A)=>{E(),a=null,S({activeSourceCount:e.size,isPlaying:!1,lastInterruptLatencyMs:A,lastPlaybackStopLatencyMs:l.lastPlaybackStopLatencyMs??A}),r?.(),r=null,h=null},U=(A)=>{if(!A)return 0;return Math.max(0,((A.baseLatency??0)+(A.outputLatency??0))*1000)},N=(A)=>{if(!d)return;let T=1;if(d.gain.setValueAtTime){d.gain.setValueAtTime(T,A?.currentTime??0);return}d.gain.value=T},s=(A)=>{if(!d)return;let T=0;if(d.gain.setValueAtTime){d.gain.setValueAtTime(T,A?.currentTime??0);return}d.gain.value=T},V=()=>{if(a===null||e.size>0)return;_(Date.now()-a)},R=async()=>{if(t)return t;if(c.createAudioContext)t=c.createAudioContext();else{let A=ic();if(!A)throw Error(\"Assistant audio playback requires AudioContext support.\");t=new A}if(t.createGain)d=t.createGain(),d.connect?.(t.destination);return g=t.currentTime,t},Y=async(A)=>{let T=await R(),P=lc(T,A),L=T.createBufferSource();L.buffer=P,L.connect(d??T.destination),L.onended=()=>{e.delete(L),L.disconnect?.(),S({activeSourceCount:e.size,isPlaying:e.size>0&&l.isActive}),V()};let Q=Math.max(T.currentTime+i,g);g=Q+P.duration,e.add(L),S({activeSourceCount:e.size,isPlaying:!0}),L.start(Q)},f=(A)=>{for(let T of[...e])T.stop?.();if(g=t?t.currentTime:0,A?.forceClear){for(let T of e)T.disconnect?.();e.clear(),V()}},$=async()=>{if(!l.isActive)return;let A=n.assistantAudio.slice(l.processedChunkCount);if(A.length===0)return;try{w();for(let T of A)await Y(T);S({processedChunkCount:n.assistantAudio.length,queuedChunkCount:l.queuedChunkCount+A.length})}catch(T){S({error:T instanceof Error?T.message:String(T)})}},D=()=>{return y=y.then(()=>$(),()=>$()),y},q=n.subscribe(()=>{if(c.autoStart&&!l.isActive&&n.assistantAudio.length>0){H.start();return}if(l.isActive)D()}),H={close:async()=>{if(q(),f({forceClear:!0}),E(),r?.(),r=null,h=null,a=null,t&&t.state!==\"closed\")await t.close();t=null,d?.disconnect?.(),d=null,g=0,S({activeSourceCount:0,isActive:!1,isPlaying:!1})},get activeSourceCount(){return l.activeSourceCount},get error(){return l.error},getSnapshot:()=>l,get isActive(){return l.isActive},get isPlaying(){return l.isPlaying},interrupt:async()=>{let A=Date.now(),T=await R();a=A,s(T);let P=Date.now()-A+U(T);if(S({isActive:!1,isPlaying:e.size>0,lastPlaybackStopLatencyMs:P}),e.size===0){_(P);return}if(!h)h=new Promise((L)=>{r=L});E(),M=setTimeout(()=>{for(let L of e)L.disconnect?.();e.clear(),_(Date.now()-A)},250),f(),await h},get lastInterruptLatencyMs(){return l.lastInterruptLatencyMs},get lastPlaybackStopLatencyMs(){return l.lastPlaybackStopLatencyMs},pause:async()=>{if(!t){S({activeSourceCount:0,isActive:!1,isPlaying:!1});return}await t.suspend(),S({activeSourceCount:e.size,isActive:!1,isPlaying:!1})},get processedChunkCount(){return l.processedChunkCount},get queuedChunkCount(){return l.queuedChunkCount},start:async()=>{try{w();let A=await R();if(N(A),A.state===\"suspended\")await A.resume();S({activeSourceCount:e.size,isActive:!0,isPlaying:A.state===\"running\"}),await D()}catch(A){throw S({error:A instanceof Error?A.message:String(A),isActive:!1,isPlaying:!1}),A}},subscribe:(A)=>{return o.add(A),()=>{o.delete(A)}}};return H};var sc=()=>`barge-in:${Date.now()}:${crypto.randomUUID?.()??Math.random().toString(36).slice(2)}`,dc=(n,c)=>{let o=n.filter((t)=>t.status===\"stopped\"),e=o.map((t)=>t.latencyMs).filter((t)=>typeof t===\"number\"),i=o.filter((t)=>typeof t.latencyMs===\"number\"&&t.latencyMs>c).length,l=o.length-i;return{averageLatencyMs:e.length>0?Math.round(e.reduce((t,d)=>t+d,0)/e.length):void 0,events:[...n],failed:i,lastEvent:n.at(-1),passed:l,status:n.length===0?\"empty\":i>0?\"fail\":o.length===0?\"warn\":\"pass\",thresholdMs:c,total:o.length}},En=(n={})=>{let c=new Set,o=n.thresholdMs??250,e=n.fetch??globalThis.fetch,i=[],l=()=>{for(let g of c)g()},t=(g)=>{if(!n.path||typeof e!==\"function\")return;e(n.path,{body:JSON.stringify(g),headers:{\"Content-Type\":\"application/json\"},method:\"POST\"}).catch(()=>{})},d=(g,y)=>{let a={at:Date.now(),id:sc(),latencyMs:y.latencyMs,playbackStopLatencyMs:y.playbackStopLatencyMs,reason:y.reason,sessionId:y.sessionId,status:g,thresholdMs:o};return i.push(a),t(a),l(),a};return{getSnapshot:()=>dc(i,o),recordRequested:(g)=>d(\"requested\",g),recordSkipped:(g)=>d(\"skipped\",g),recordStopped:(g)=>d(\"stopped\",g),subscribe:(g)=>{return c.add(g),()=>{c.delete(g)}}}};var rc=0.08,ac=(n,c={})=>(c.enabled??!0)&&n>=(c.interruptThreshold??rc),en=(n,c,o={})=>{let e=n.partial,i=(t)=>{if(!c.isPlaying||o.enabled===!1){o.monitor?.recordSkipped({reason:t,sessionId:n.sessionId});return}o.monitor?.recordRequested({reason:t,sessionId:n.sessionId}),c.interrupt().then(()=>{o.monitor?.recordStopped({latencyMs:c.lastInterruptLatencyMs,playbackStopLatencyMs:c.lastPlaybackStopLatencyMs,reason:t,sessionId:n.sessionId})})},l=n.subscribe(()=>{if(o.interruptOnPartial===!1){e=n.partial;return}if(!e&&n.partial)i(\"partial-transcript\");e=n.partial});return{close:()=>{l()},handleLevel:(t)=>{if(ac(t,o))i(\"input-level\")},sendAudio:(t)=>{i(\"manual-audio\"),n.sendAudio(t)}}};var dn=48,gc=320,Ac=88,hc=\"Guided test\",yc=\"General recording\",Cc=\"Pick a scenario to begin the demo.\",Tc=\"I can walk you through a short guided voice test.\",Ic=\"I can capture one freeform recording and confirm that it landed.\",Vc=\"Choose a scenario to begin. Guided test asks follow-up prompts. General recording just captures what you say.\",Mc=\"Click Start general recording to capture one freeform answer.\",Pn=\"Speak freely. When you pause, the recording will be captured.\",ln=\"Recording saved. Start again if you want another capture.\",Dn=\"Guided test complete. Review the saved summary below.\",On=\"All prompts are covered. You can stop the microphone or keep speaking for extra detail.\",Sc=\"Ready. Start guided test or general recording to begin.\",Lc=\"Live. Answer the prompt, then click Stop microphone when finished.\",bn=[\"Start with a quick introduction about who you are.\",\"Now describe what you are trying to do or test.\",\"Finish with any detail that feels blocked, risky, or unclear.\"],xn=(n,c,o)=>Math.min(o,Math.max(c,n)),u=(n)=>n.replaceAll(\"&\",\"&amp;\").replaceAll(\"<\",\"&lt;\").replaceAll(\">\",\"&gt;\").replaceAll('\"',\"&quot;\").replaceAll(\"'\",\"&#39;\"),on=(n,c)=>{let o=n[c];if(typeof o===\"string\"&&o.trim())return o;return null},sn=(n)=>{if(typeof n===\"string\"&&n.trim())return n;if(n instanceof Error&&n.message.trim())return n.message;if(n&&typeof n===\"object\"){let c=n,o=on(c,\"message\")??on(c,\"reason\")??on(c,\"description\");if(o)return o;if(\"error\"in c)return sn(c.error);if(\"cause\"in c)return sn(c.cause);try{return JSON.stringify(n)}catch{}}return\"Unexpected error\"},wc=(n)=>{let c=[n.status];if(n.attempts>0||n.maxAttempts>0)c.push(`${n.attempts}/${n.maxAttempts} attempts`);if(n.nextAttemptAt){let o=Math.max(0,n.nextAttemptAt-Date.now());c.push(`retry in ${Math.ceil(o/100)/10}s`)}return c.join(\" \u00B7 \")},v=(n=dn)=>Array.from({length:n},()=>0),fn=(n,c,o=dn)=>{let e=n.slice(-(o-1));e.push(xn(c,0,1));while(e.length<o)e.unshift(0);return e},Rc=(n,c=gc,o=Ac)=>{let e=n.length>1?n:v(dn),i=c/(e.length-1),l=o/2,t=o*0.34;if(Math.max(...e,0)<=0.015)return`M 0 ${l} L ${c} ${l}`;let g=e.map((a,h)=>{let r=h*0.76,M=Math.sin(r)*0.78+Math.sin(r*0.41)*0.22,C=a*t,S=i*h,w=xn(l+M*C,8,o-8);return{x:S,y:w}});if(g.length===0)return`M 0 ${l} L ${c} ${l}`;let y=`M ${g[0]?.x??0} ${g[0]?.y??l}`;for(let a=1;a<g.length;a+=1){let h=g[a-1],r=g[a];if(!h||!r)continue;let M=(h.x+r.x)/2;y+=` Q ${M} ${h.y} ${r.x} ${r.y}`}return y},_c=(n)=>{if(!n)return bn;try{let c=JSON.parse(n);if(Array.isArray(c)){let o=c.filter((e)=>typeof e===\"string\").map((e)=>e.trim()).filter(Boolean);if(o.length>0)return o}}catch{}return bn},tn=(n)=>{if(!n)return;let c=Number(n);return Number.isFinite(c)?c:void 0},Ec=(n,c,o)=>{if(!c)return null;let e=document.querySelector(c);return e instanceof o?e:null},x=(n,c,o,e)=>{let i=c?document.querySelector(c):null;if(i instanceof o)return i;let l=n.querySelector(`#${e}`);if(l instanceof o)return l;throw Error(`Voice HTMX bootstrap could not find the required element \"${e}\".`)},bc=(n)=>{if(!n.mode)return Cc;if(!n.hasStarted)return n.mode===\"guided\"?Tc:Ic;if(n.status===\"completed\")return n.mode===\"guided\"?Dn:ln;if(n.mode===\"general\")return Pn;return n.guidedPrompts[n.turnCount]??On},fc=(n)=>{if(!n.mode)return Vc;if(n.status===\"completed\")return n.mode===\"guided\"?Dn:ln;if(!n.hasStarted)return n.mode===\"guided\"?`Click Start guided test to begin. First prompt: ${n.guidedPrompts[0]??\"Answer the first prompt.\"}`:Mc;if(n.mode===\"general\")return n.turnCount===0?Pn:ln;return n.guidedPrompts[n.turnCount]??On},Pc=(n)=>{let c=n.dataset.voiceGuidedPath,o=n.dataset.voiceGeneralPath;if(!c||!o)throw Error(\"Voice HTMX bootstrap requires data-voice-guided-path and data-voice-general-path.\");let e=_c(n.dataset.voiceGuidedPrompts),i=n.dataset.voiceGuidedLabel??hc,l=n.dataset.voiceGeneralLabel??yc,t=n.dataset.voiceReconnectReportPath,d=n.dataset.voiceBargeInPath,g=d?En({path:d,thresholdMs:tn(n.dataset.voiceBargeInThresholdMs)}):null,y=tn(n.dataset.voiceBargeInRecentWindowMs)??4000,a=tn(n.dataset.voiceBargeInSpeechThreshold)??0.04,h=x(document,n.dataset.voiceSync,HTMLElement,\"voice-htmx-sync\"),r=x(n,n.dataset.voiceConnection,HTMLElement,\"metric-connection\"),M=x(n,n.dataset.voiceError,HTMLElement,\"status-error\"),C=x(n,n.dataset.voiceMicrophone,HTMLElement,\"status-mic\"),S=x(n,n.dataset.voicePrompt,HTMLElement,\"status-prompt\"),w=Ec(n,n.dataset.voiceReconnect,HTMLElement),E=x(n,n.dataset.voiceChat,HTMLElement,\"chat-list\"),_=x(n,n.dataset.voiceStartGuided,HTMLButtonElement,\"start-guided\"),U=x(n,n.dataset.voiceStartGeneral,HTMLButtonElement,\"start-general\"),N=x(n,n.dataset.voiceStop,HTMLButtonElement,\"stop-mic\"),s=x(n,n.dataset.voiceMonitor,HTMLElement,\"voice-monitor\"),V=x(n,n.dataset.voiceMonitorCopy,HTMLElement,\"voice-monitor-copy\"),R=x(n,n.dataset.voiceWaveGlow,SVGPathElement,\"voice-wave-glow\"),Y=x(n,n.dataset.voiceWavePath,SVGPathElement,\"voice-wave-path\"),f=null,$={general:!1,guided:!1},D=!1,q=null,H=v(),A=null,T=null,P=j(c,{capture:{onAudio:(I,B)=>{if(A){A.sendAudio(I);return}B(I)},onLevel:(I)=>{A?.handleLevel(I),H=fn(H,I),m()}},connection:{reconnectReportPath:t},preset:\"guided-intake\"}),L=j(o,{capture:{onAudio:(I,B)=>{if(T){T.sendAudio(I);return}B(I)},onLevel:(I)=>{T?.handleLevel(I),H=fn(H,I),m()}},connection:{reconnectReportPath:t},preset:\"dictation\"}),Q=P.bindHTMX({element:h}),Un=L.bindHTMX({element:h}),k=F(P),z=F(L);A=en(P,k,{interruptThreshold:a,monitor:g??void 0}),T=en(L,z,{interruptThreshold:a,monitor:g??void 0});let J=()=>f===\"general\"?L:P,Oc=()=>f===\"general\"?z:k,m=()=>{let I=Rc(H);R.setAttribute(\"d\",I),Y.setAttribute(\"d\",I),V.innerHTML=`<span class=\"voice-live-dot\"></span>${D?\"Microphone live\":\"Microphone idle\"}`,V.classList.toggle(\"is-live\",D),s.classList.toggle(\"is-live\",D)},X=()=>{let I=J(),B=(f?$[f]:!1)||I.turns.length>0,an=I.status;if(r.textContent=I.isConnected?\"Connected\":\"Waiting\",M.textContent=q||I.error||\"None\",w)w.textContent=wc(I.reconnect);C.textContent=D?Lc:Sc,S.textContent=fc({guidedPrompts:e,hasStarted:B,mode:f,status:an,turnCount:I.turns.length}),_.hidden=D,U.hidden=D,N.hidden=!D,E.innerHTML=`<article class=\"voice-chat-message assistant\">\n <div class=\"voice-chat-role\">${u(f===\"general\"?l:f===\"guided\"?i:\"Voice demo\")}</div>\n <p class=\"voice-turn-text\">${u(bc({generalLabel:l,guidedLabel:i,guidedPrompts:e,hasStarted:B,mode:f,status:an,turnCount:I.turns.length}))}</p>\n</article>${I.turns.map((p)=>`<div class=\"voice-chat-stack\">\n <article class=\"voice-chat-message user\">\n <div class=\"voice-chat-role\">You</div>\n <p class=\"voice-turn-text\">${u(p.text)}</p>\n </article>\n ${p.assistantText?`<article class=\"voice-chat-message assistant\">\n <div class=\"voice-chat-role\">${u(f===\"general\"?l:f===\"guided\"?i:\"Guide\")}</div>\n <p class=\"voice-turn-text\">${u(p.assistantText)}</p>\n </article>`:\"\"}\n</div>`).join(\"\")}${I.partial?`<article class=\"voice-chat-message user pending\">\n <div class=\"voice-chat-role\">Speaking</div>\n <p class=\"voice-turn-text\">${u(I.partial)}</p>\n</article>`:\"\"}`,m()},Nn=()=>{J().stopRecording(),D=!1,q=null,H=v(),X()},rn=async(I)=>{f=I,$={...$,[I]:!0};try{await J().startRecording(),q=null,D=!0,X()}catch(B){J().stopRecording(),D=!1,H=v(),q=sn(B),X()}};P.subscribe(()=>{if(P.assistantAudio.length>0)k.start().catch(()=>{});X()}),L.subscribe(()=>{if(L.assistantAudio.length>0)z.start().catch(()=>{});X()}),_.addEventListener(\"click\",()=>{rn(\"guided\")}),U.addEventListener(\"click\",()=>{rn(\"general\")}),N.addEventListener(\"click\",()=>{Nn()}),n.addEventListener(\"absolute-voice-simulate-disconnect\",()=>{J().simulateDisconnect()}),window.addEventListener(\"beforeunload\",()=>{P.stopRecording(),L.stopRecording(),A?.close(),T?.close(),k.close(),z.close(),Q(),Un(),P.close(),L.close()}),X()},Dc=()=>{if(typeof window>\"u\"||typeof document>\"u\")return;let n=Array.from(document.querySelectorAll(\"[data-voice-htmx]\"));for(let c of n)if(c instanceof HTMLElement)Pc(c)};Dc();export{Dc as initVoiceHTMX};\n";
package/dist/index.js CHANGED
@@ -5594,7 +5594,19 @@ var createVoiceSession = (options) => {
5594
5594
  return;
5595
5595
  }
5596
5596
  const adapter = await ensureAdapter();
5597
- const conditionedAudio = conditionAudioChunk(audio, options.audioConditioning);
5597
+ let inboundAudio = audio;
5598
+ if (options.noiseSuppressor) {
5599
+ try {
5600
+ const suppressed = await options.noiseSuppressor.process({
5601
+ format: options.noiseSuppressorFormat ?? DEFAULT_FORMAT,
5602
+ pcm: audio
5603
+ });
5604
+ inboundAudio = suppressed.bytes;
5605
+ } catch (error) {
5606
+ options.logger?.warn?.(`noise suppression failed, passing raw audio through: ${error instanceof Error ? error.message : String(error)}`);
5607
+ }
5608
+ }
5609
+ const conditionedAudio = conditionAudioChunk(inboundAudio, options.audioConditioning);
5598
5610
  const audioLevel = measureAudioLevel(conditionedAudio);
5599
5611
  const shouldStoreAudio = speechDetected || audioLevel >= turnDetection.speechThreshold;
5600
5612
  await writeSession((currentSession) => {
@@ -5653,6 +5665,11 @@ var createVoiceSession = (options) => {
5653
5665
  clearSilenceTimer();
5654
5666
  clearCallSilenceWatchdog();
5655
5667
  clearAmdEvaluationTimer();
5668
+ if (options.noiseSuppressor?.close) {
5669
+ try {
5670
+ await options.noiseSuppressor.close();
5671
+ } catch {}
5672
+ }
5656
5673
  await closeTTSSession(reason);
5657
5674
  await closeAdapter(reason);
5658
5675
  await persistRecordings();
@@ -5724,7 +5741,7 @@ var createVoiceSession = (options) => {
5724
5741
  };
5725
5742
 
5726
5743
  // src/generated/htmxBootstrapBundle.ts
5727
- var HTMX_BOOTSTRAP_BUNDLE = 'var Nn=(n)=>{if(typeof n!=="string")return n;return document.querySelector(n)},Hn=(n,e,o,c)=>{let i=e??n.getAttribute("hx-get")??"";if(!i)return"";let s=new URL(i,window.location.origin);if(c)s.searchParams.set(o,c);else s.searchParams.delete(o);return`${s.pathname}${s.search}${s.hash}`},gn=(n,e)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let o=Nn(e.element);if(!o)return()=>{};let c=e.eventName??"voice-refresh",i=e.sessionQueryParam??"sessionId",s=()=>{let r=window,g=Hn(o,e.route,i,n.sessionId);if(g)o.setAttribute("hx-get",g);r.htmx?.process?.(o),r.htmx?.trigger?.(o,c)},t=n.subscribe(s);return s(),()=>{t()}};var Gn=(n)=>Math.max(-1,Math.min(1,n)),Bn=(n)=>{let e=new Int16Array(n.length);for(let o=0;o<n.length;o+=1){let c=Gn(n[o]??0);e[o]=c<0?c*32768:c*32767}return new Uint8Array(e.buffer)},Wn=(n)=>{let e=n instanceof Uint8Array?n:new Uint8Array(n);if(e.byteLength<2)return 0;let o=new Int16Array(e.buffer,e.byteOffset,Math.floor(e.byteLength/2));if(o.length===0)return 0;let c=0;for(let i of o){let s=i/32768;c+=s*s}return Math.min(1,Math.max(0,Math.sqrt(c/o.length)*5.5))},$n=(n,e,o)=>{if(e===o)return n;let c=e/o,i=Math.round(n.length/c),s=new Float32Array(i),t=0,r=0;while(t<s.length){let g=Math.round((t+1)*c),y=0,d=0;for(let h=r;h<g&&h<n.length;h+=1)y+=n[h]??0,d+=1;s[t]=d>0?y/d:0,t+=1,r=g}return s},An=(n)=>{let e=null,o=null,c=null,i=null;return{start:async()=>{if(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.");i=await navigator.mediaDevices.getUserMedia({audio:{channelCount:n.channelCount??1}}),e=new r,o=e.createMediaStreamSource(i),c=e.createScriptProcessor(4096,1,1),c.onaudioprocess=(g)=>{let y=g.inputBuffer.getChannelData(0),d=$n(y,e?.sampleRate??48000,n.sampleRateHz??16000),h=Bn(d);n.onLevel?.(Wn(h)),n.onAudio(h)},o.connect(c),c.connect(e.destination)},stop:()=>{c?.disconnect(),o?.disconnect(),i?.getTracks().forEach((r)=>r.stop()),e?.close(),n.onLevel?.(0),e=null,i=null,c=null,o=null}}};var nn=(n)=>{if(typeof n==="string"&&n.trim())return n;if(n instanceof Error&&n.message.trim())return n.message;if(n&&typeof n==="object"){let e=n;for(let o of["message","reason","description"]){let c=e[o];if(typeof c==="string"&&c.trim())return c}if("error"in e)return nn(e.error);if("cause"in e)return nn(e.cause);try{return JSON.stringify(n)}catch{}}return"Unexpected error"},hn=(n)=>{switch(n.type){case"audio":return{chunk:Uint8Array.from(atob(n.chunkBase64),(e)=>e.charCodeAt(0)),format:n.format,receivedAt:n.receivedAt,turnId:n.turnId,type:"audio"};case"assistant":return{text:n.text,type:"assistant"};case"complete":return{sessionId:n.sessionId,type:"complete"};case"connection":return{reconnect:n.reconnect,type:"connection"};case"call_lifecycle":return{event:n.event,sessionId:n.sessionId,type:"call_lifecycle"};case"error":return{message:nn(n.message),type:"error"};case"final":return{transcript:n.transcript,type:"final"};case"partial":return{transcript:n.transcript,type:"partial"};case"replay":return{assistantTexts:n.assistantTexts,call:n.call,partial:n.partial,scenarioId:n.scenarioId,sessionId:n.sessionId,sessionMetadata:n.sessionMetadata,status:n.status,turns:n.turns,type:"replay"};case"session":return{sessionId:n.sessionId,sessionMetadata:n.sessionMetadata,scenarioId:n.scenarioId,status:n.status,type:"session"};case"turn":return{turn:n.turn,type:"turn"};default:return null}};var H=(n,e,o,c)=>{n.push({code:o,message:c,severity:e})};var qn=(n)=>n.length===0?void 0:n.reduce((e,o)=>e+o,0)/n.length,K=(n)=>n.length===0?void 0:Math.max(...n);var _=(n,e)=>{let o=n[e];return typeof o==="number"&&Number.isFinite(o)?o:void 0},Z=(n,e)=>{let o=n[e];return typeof o==="boolean"?o:void 0},O=(n,e)=>{let o=n[e];return typeof o==="string"?o:void 0},en=(n)=>String(n.id??O(n,"ssrc")??_(n,"ssrc")??O(n,"trackIdentifier")??O(n,"mid")??"unknown"),yn=(n)=>n===void 0?void 0:n*1000;var Xn=(n)=>{let e={};for(let[o,c]of Object.entries(n))if(c===null||typeof c==="boolean"||typeof c==="number"||typeof c==="string")e[o]=c;return e};var Cn=(n={})=>{let e=n.stats??[],o=[],c=e.filter((l)=>l.type==="inbound-rtp"&&O(l,"kind")!=="video"),i=e.filter((l)=>l.type==="outbound-rtp"&&O(l,"kind")!=="video"),s=e.filter((l)=>l.type==="candidate-pair"),t=e.filter((l)=>(l.type==="track"||l.type==="media-source")&&O(l,"kind")==="audio"),r=s.filter((l)=>Z(l,"selected")===!0||Z(l,"nominated")===!0||O(l,"state")==="succeeded").length,g=t.filter((l)=>O(l,"readyState")!=="ended"&&O(l,"trackState")!=="ended"&&Z(l,"ended")!==!0).length,y=t.filter((l)=>O(l,"readyState")==="ended"||O(l,"trackState")==="ended"||Z(l,"ended")===!0).length,d=c.reduce((l,S)=>l+(_(S,"packetsReceived")??0),0),h=i.reduce((l,S)=>l+(_(S,"packetsSent")??0),0),a=[...c,...i].reduce((l,S)=>l+Math.max(0,_(S,"packetsLost")??0),0),M=d+a,C=M===0?0:a/M,I=c.reduce((l,S)=>l+(_(S,"bytesReceived")??0),0),L=i.reduce((l,S)=>l+(_(S,"bytesSent")??0),0),b=K(s.map((l)=>yn(_(l,"currentRoundTripTime")??_(l,"roundTripTime"))).filter((l)=>l!==void 0)),w=K([...c,...i].map((l)=>yn(_(l,"jitter"))).filter((l)=>l!==void 0)),u=K(c.map((l)=>{let S=_(l,"jitterBufferDelay"),R=_(l,"jitterBufferEmittedCount");return S!==void 0&&R!==void 0&&R>0?S/R*1000:void 0}).filter((l)=>l!==void 0)),U=t.map((l)=>_(l,"audioLevel")).filter((l)=>l!==void 0);if(n.requireConnectedCandidatePair&&s.length>0&&r===0)H(o,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(n.requireLiveAudioTrack&&g===0)H(o,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(n.maxPacketLossRatio!==void 0&&C>n.maxPacketLossRatio)H(o,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(C)} above ${String(n.maxPacketLossRatio)}.`);if(n.maxRoundTripTimeMs!==void 0&&b!==void 0&&b>n.maxRoundTripTimeMs)H(o,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(b)}ms above ${String(n.maxRoundTripTimeMs)}ms.`);if(n.maxJitterMs!==void 0&&w!==void 0&&w>n.maxJitterMs)H(o,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(w)}ms above ${String(n.maxJitterMs)}ms.`);return{activeCandidatePairs:r,audioLevelAverage:qn(U),bytesReceived:I,bytesSent:L,checkedAt:Date.now(),endedAudioTracks:y,inboundPackets:d,issues:o,jitterBufferDelayMs:u,jitterMs:w,liveAudioTracks:g,outboundPackets:h,packetLossRatio:C,packetsLost:a,roundTripTimeMs:b,status:o.some((l)=>l.severity==="error")?"fail":o.length>0?"warn":"pass",totalStats:e.length}},fn=async(n)=>{return[...(await n.peerConnection.getStats(n.selector??null)).values()].map(Xn)};var Tn=(n={})=>{let e=n.stats??[],o=n.previousStats??[],c=[],i=new Map(o.map((a)=>[en(a),a])),t=e.filter((a)=>(a.type==="inbound-rtp"||a.type==="outbound-rtp")&&O(a,"kind")!=="video"&&O(a,"mediaType")!=="video").map((a)=>{let M=a.type==="outbound-rtp"?"outbound":"inbound",C=M==="outbound"?"packetsSent":"packetsReceived",I=M==="outbound"?"bytesSent":"bytesReceived",L=i.get(en(a)),b=_(a,C),w=L?_(L,C):void 0,u=_(a,I),U=L?_(L,I):void 0,l=a.timestamp!==void 0&&L?.timestamp!==void 0?a.timestamp-L.timestamp:void 0;return{bytesDelta:u!==void 0&&U!==void 0?u-U:void 0,currentPackets:b,direction:M,id:en(a),packetDelta:b!==void 0&&w!==void 0?b-w:void 0,previousPackets:w,timeDeltaMs:l}}),r=t.filter((a)=>a.direction==="inbound"),g=t.filter((a)=>a.direction==="outbound"),y=K(t.map((a)=>a.timeDeltaMs).filter((a)=>a!==void 0)),d=r.filter((a)=>n.maxInboundPacketStallMs!==void 0&&a.timeDeltaMs!==void 0&&a.timeDeltaMs>=n.maxInboundPacketStallMs&&a.packetDelta!==void 0&&a.packetDelta<=0).length,h=g.filter((a)=>n.maxOutboundPacketStallMs!==void 0&&a.timeDeltaMs!==void 0&&a.timeDeltaMs>=n.maxOutboundPacketStallMs&&a.packetDelta!==void 0&&a.packetDelta<=0).length;if(n.requireInboundAudio&&r.length===0)H(c,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(n.requireOutboundAudio&&g.length===0)H(c,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(n.maxGapMs!==void 0&&y!==void 0&&y>n.maxGapMs)H(c,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(y)}ms above ${String(n.maxGapMs)}ms.`);if(d>0)H(c,"error","media.webrtc_inbound_stalled",`${String(d)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(h>0)H(c,"error","media.webrtc_outbound_stalled",`${String(h)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:r.length,issues:c,maxObservedGapMs:y,outboundAudioStreams:g.length,stalledInboundStreams:d,stalledOutboundStreams:h,status:c.some((a)=>a.severity==="error")?"fail":c.length>0?"warn":"pass",streams:t,totalStats:e.length}};var zn="/api/voice/browser-media",kn=5000,Yn=async(n)=>n.peerConnection??await n.getPeerConnection?.()??null,Jn=async(n,e)=>{let o=e.fetch??globalThis.fetch;if(!o)return;await o(e.path??zn,{body:JSON.stringify(n),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},Sn=(n)=>{let e=null,o=[],c=async()=>{let t=await Yn(n);if(!t)return;let r=await fn({peerConnection:t}),g=Cn({...n,stats:r}),y=n.continuity===!1?void 0:Tn({...n.continuity,previousStats:o,stats:r}),d={at:Date.now(),continuity:y,report:g,scenarioId:n.getScenarioId?.()??null,sessionId:n.getSessionId?.()??null};return o=r,n.onReport?.(d),await Jn(d,n),d},i=()=>{c().catch((t)=>{n.onError?.(t)})},s=()=>{if(e)clearInterval(e),e=null};return{close:s,reportOnce:c,start:()=>{if(e)return;i(),e=setInterval(i,n.intervalMs??kn)},stop:s}};var B=()=>{},Qn=()=>B,Zn={callControl:B,close:B,endTurn:B,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",send:B,sendAudio:B,simulateDisconnect:B,start:()=>{},subscribe:Qn},Kn=()=>crypto.randomUUID(),jn=(n,e,o)=>{let{hostname:c,port:i,protocol:s}=window.location,t=s==="https:"?"wss:":"ws:",r=i?`:${i}`:"",g=new URL(`${t}//${c}${r}${n}`);if(g.searchParams.set("sessionId",e),o)g.searchParams.set("scenarioId",o);return g.toString()},mn=(n)=>{if(!n||typeof n!=="object"||!("type"in n))return!1;switch(n.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}},Fn=(n)=>{if(typeof n.data!=="string")return null;try{let e=JSON.parse(n.data);return mn(e)?e:null}catch{return null}},Mn=(n,e={})=>{if(typeof window>"u")return Zn;let o=new Set,c=e.reconnect!==!1,i=e.maxReconnectAttempts??10,s=e.pingInterval??30000,t={isConnected:!1,pendingMessages:[],scenarioId:e.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:e.sessionId??Kn(),ws:null},r=(l)=>{o.forEach((S)=>S(l))},g=()=>{if(t.pingInterval)clearInterval(t.pingInterval),t.pingInterval=null;if(t.reconnectTimeout)clearTimeout(t.reconnectTimeout),t.reconnectTimeout=null},y=()=>{if(t.ws?.readyState!==1)return;while(t.pendingMessages.length>0){let l=t.pendingMessages.shift();if(l!==void 0)t.ws.send(l)}},d=()=>{let l=Date.now()+500;t.reconnectAttempts+=1,r({reconnect:{attempts:t.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:i,nextAttemptAt:l,status:"reconnecting"},type:"connection"}),t.reconnectTimeout=setTimeout(()=>{if(t.reconnectAttempts>i){r({reconnect:{attempts:t.reconnectAttempts,maxAttempts:i,status:"exhausted"},type:"connection"});return}h()},500)},h=()=>{let l=new WebSocket(jn(n,t.sessionId,t.scenarioId));l.binaryType="arraybuffer",l.onopen=()=>{let S=t.reconnectAttempts>0;if(t.isConnected=!0,y(),S)r({reconnect:{attempts:t.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:i,status:"resumed"},type:"connection"}),t.reconnectAttempts=0;o.forEach((R)=>R({scenarioId:t.scenarioId??void 0,sessionId:t.sessionId,status:"active",type:"session"})),t.pingInterval=setInterval(()=>{if(l.readyState===1)l.send(JSON.stringify({type:"ping"}))},s)},l.onmessage=(S)=>{let R=Fn(S);if(!R)return;if(R.type==="session")t.sessionId=R.sessionId,t.scenarioId=R.scenarioId??t.scenarioId;o.forEach((z)=>z(R))},l.onclose=(S)=>{if(t.isConnected=!1,g(),c&&S.code!==1000&&t.reconnectAttempts<i)d();else if(c&&S.code!==1000)r({reconnect:{attempts:t.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:i,status:"exhausted"},type:"connection"})},t.ws=l},a=(l)=>{if(t.ws?.readyState===1){t.ws.send(l);return}t.pendingMessages.push(l)},M=(l)=>{a(JSON.stringify(l))},C=(l={})=>{if(l.sessionId)t.sessionId=l.sessionId;if(l.scenarioId)t.scenarioId=l.scenarioId;M({type:"start",sessionId:t.sessionId,scenarioId:t.scenarioId??void 0})},I=(l)=>{a(l)},L=()=>{M({type:"end_turn"})},b=(l)=>{M({...l,type:"call_control"})},w=()=>{if(g(),t.ws)t.ws.close(1000),t.ws=null;t.isConnected=!1,o.clear()},u=()=>{if(t.ws?.readyState===1)t.ws.close(4000,"absolutejs-voice-reconnect-proof")},U=(l)=>{return o.add(l),()=>{o.delete(l)}};return h(),{callControl:b,close:w,endTurn:L,getReadyState:()=>t.ws?.readyState??3,getScenarioId:()=>t.scenarioId??"",getSessionId:()=>t.sessionId,send:M,sendAudio:I,simulateDisconnect:u,start:C,subscribe:U}};var vn=()=>({attempts:0,maxAttempts:0,status:"idle"}),pn=()=>({assistantAudio:[],assistantTexts:[],call:null,error:null,isConnected:!1,sessionMetadata:null,scenarioId:null,partial:"",reconnect:vn(),sessionId:null,status:"idle",turns:[]}),In=()=>{let n=pn(),e=new Set,o=()=>{e.forEach((i)=>i())};return{dispatch:(i)=>{switch(i.type){case"audio":n={...n,assistantAudio:[...n.assistantAudio,{chunk:i.chunk,format:i.format,receivedAt:i.receivedAt,turnId:i.turnId}]};break;case"assistant":n={...n,assistantTexts:[...n.assistantTexts,i.text]};break;case"complete":n={...n,sessionId:i.sessionId,status:"completed"};break;case"call_lifecycle":n={...n,call:{...n.call,disposition:i.event.type==="end"?i.event.disposition:n.call?.disposition,endedAt:i.event.type==="end"?i.event.at:n.call?.endedAt,events:[...n.call?.events??[],i.event],lastEventAt:i.event.at,startedAt:n.call?.startedAt??i.event.at},sessionId:i.sessionId};break;case"connected":n={...n,isConnected:!0,reconnect:n.reconnect.status==="reconnecting"?{...n.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:n.reconnect};break;case"connection":n={...n,reconnect:i.reconnect};break;case"disconnected":n={...n,isConnected:!1};break;case"error":n={...n,error:i.message};break;case"final":n={...n,partial:i.transcript.text,turns:n.turns.map((s)=>s)};break;case"partial":n={...n,partial:i.transcript.text};break;case"replay":n={...n,assistantTexts:[...i.assistantTexts],call:i.call??null,error:null,isConnected:i.status==="active",partial:i.partial,reconnect:n.reconnect.status==="reconnecting"?{...n.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:n.reconnect,scenarioId:i.scenarioId??n.scenarioId,sessionId:i.sessionId,sessionMetadata:i.sessionMetadata??n.sessionMetadata,status:i.status,turns:[...i.turns]};break;case"session":n={...n,error:null,scenarioId:i.scenarioId??n.scenarioId,isConnected:i.status==="active",sessionId:i.sessionId,sessionMetadata:i.sessionMetadata??n.sessionMetadata,status:i.status};break;case"turn":n={...n,partial:"",turns:[...n.turns,i.turn]};break}o()},getServerSnapshot:()=>n,getSnapshot:()=>n,subscribe:(i)=>{return e.add(i),()=>{e.delete(i)}}}};var Vn=(n,e={})=>{let o=Mn(n,e),c=In(),i=e.browserMedia&&typeof window<"u"?Sn({...e.browserMedia,getScenarioId:()=>e.browserMedia?e.browserMedia.getScenarioId?.()??o.getScenarioId():o.getScenarioId(),getSessionId:()=>e.browserMedia?e.browserMedia.getSessionId?.()??o.getSessionId():o.getSessionId()}):null,s=new Set,t=(d)=>Promise.resolve().then(()=>{if(!d?.sessionId&&!d?.scenarioId)return;o.start(d),i?.start()}),r=()=>{s.forEach((d)=>d())},g=()=>{if(!e.reconnectReportPath||typeof fetch>"u")return;let d=c.getSnapshot(),h=JSON.stringify({at:Date.now(),reconnect:d.reconnect,scenarioId:d.scenarioId,sessionId:o.getSessionId(),turnIds:d.turns.map((a)=>a.id)});fetch(e.reconnectReportPath,{body:h,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},y=o.subscribe((d)=>{let h=hn(d);if(h){if(c.dispatch(h),d.type==="connection")g();r()}});return{callControl(d){o.callControl(d)},close(){y(),i?.close(),o.close(),c.dispatch({type:"disconnected"}),r()},endTurn(){o.endTurn()},get error(){return c.getSnapshot().error},getServerSnapshot(){return c.getServerSnapshot()},getSnapshot(){return c.getSnapshot()},get isConnected(){return c.getSnapshot().isConnected},get scenarioId(){return c.getSnapshot().scenarioId},get sessionMetadata(){return c.getSnapshot().sessionMetadata},start:t,get partial(){return c.getSnapshot().partial},get reconnect(){return c.getSnapshot().reconnect},get sessionId(){return o.getSessionId()},get status(){return c.getSnapshot().status},get turns(){return c.getSnapshot().turns},get assistantTexts(){return c.getSnapshot().assistantTexts},get assistantAudio(){return c.getSnapshot().assistantAudio},get call(){return c.getSnapshot().call},sendAudio(d){o.sendAudio(d)},simulateDisconnect(){o.simulateDisconnect()},subscribe(d){return s.add(d),()=>{s.delete(d)}}}};var Ln=(n)=>{if(!n||n.enabled===!1)return;return{enabled:!0,maxGain:n.maxGain??3,noiseGateAttenuation:n.noiseGateAttenuation??0.15,noiseGateThreshold:n.noiseGateThreshold??0.006,targetLevel:n.targetLevel??0.08}};var ne={balanced:{qualityProfile:"general",silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:"general",silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},"long-form":{qualityProfile:"general",silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},ee={general:{},"accent-heavy":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},"noisy-room":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},"short-command":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var Rn=(n)=>{let e=n?.profile??"fast",o=n?.qualityProfile??"general",c=ne[e],i=ee[o];return{profile:e,qualityProfile:o,silenceMs:n?.silenceMs??i.silenceMs??c.silenceMs,speechThreshold:n?.speechThreshold??i.speechThreshold??c.speechThreshold,transcriptStabilityMs:n?.transcriptStabilityMs??i.transcriptStabilityMs??c.transcriptStabilityMs}};var ce={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:{qualityProfile:"short-command",profile:"balanced"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"general",profile:"fast"}},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:{qualityProfile:"accent-heavy",profile:"long-form"}},"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:{qualityProfile:"accent-heavy",profile:"long-form"}},"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:{qualityProfile:"noisy-room",profile:"long-form",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:{qualityProfile:"noisy-room",profile:"long-form",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:{qualityProfile:"noisy-room",profile:"long-form",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:{qualityProfile:"noisy-room",profile:"long-form"}}},wn=(n="default")=>{let e=ce[n];return{audioConditioning:Ln(e.audioConditioning),capture:{channelCount:e.capture?.channelCount??1,sampleRateHz:e.capture?.sampleRateHz??16000},connection:{...e.connection},name:n,sttLifecycle:e.sttLifecycle??"continuous",turnDetection:Rn(e.turnDetection)}};var oe=(n)=>({assistantAudio:[...n.assistantAudio],assistantTexts:[...n.assistantTexts],call:n.call,error:n.error,isConnected:n.isConnected,isRecording:!1,partial:n.partial,reconnect:n.reconnect,recordingError:null,sessionId:n.sessionId,sessionMetadata:n.sessionMetadata,scenarioId:n.scenarioId,status:n.status,turns:[...n.turns]}),j=(n,e={})=>{let o=wn(e.preset),c=Vn(n,{...o.connection,...e.connection}),i=null,s=oe(c),t=new Set,r=()=>{for(let C of t)C()},g=()=>{if(s={...s,assistantAudio:[...c.assistantAudio],assistantTexts:[...c.assistantTexts],call:c.call,error:c.error,isConnected:c.isConnected,partial:c.partial,reconnect:c.reconnect,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,scenarioId:c.scenarioId,status:c.status,turns:[...c.turns]},e.autoStopOnComplete!==!1&&s.status==="completed"&&s.isRecording)i?.stop(),i=null,s={...s,isRecording:!1};r()},y=c.subscribe(g);g();let d=()=>{if(i)return i;return i=An({channelCount:e.capture?.channelCount??o.capture.channelCount,onLevel:e.capture?.onLevel,onAudio:(C)=>{if(e.capture?.onAudio){e.capture.onAudio(C,c.sendAudio);return}c.sendAudio(C)},sampleRateHz:e.capture?.sampleRateHz??o.capture.sampleRateHz}),i},h=()=>{i?.stop(),i=null,s={...s,isRecording:!1},r()},a=async()=>{if(s.isRecording)return;try{s={...s,recordingError:null},r(),await d().start(),s={...s,isRecording:!0},r()}catch(C){throw i=null,s={...s,isRecording:!1,recordingError:C instanceof Error?C.message:String(C)},r(),C}};return{bindHTMX(C){return gn(c,C)},callControl:(C)=>c.callControl(C),close:()=>{y(),h(),c.close()},endTurn:()=>c.endTurn(),get error(){return s.error},getServerSnapshot:()=>s,getSnapshot:()=>s,get isConnected(){return s.isConnected},get isRecording(){return s.isRecording},get partial(){return s.partial},get recordingError(){return s.recordingError},get reconnect(){return s.reconnect},sendAudio:(C)=>c.sendAudio(C),simulateDisconnect:()=>c.simulateDisconnect(),get sessionId(){return s.sessionId},get sessionMetadata(){return s.sessionMetadata},get scenarioId(){return s.scenarioId},startRecording:a,get status(){return s.status},stopRecording:h,subscribe:(C)=>{return t.add(C),()=>{t.delete(C)}},toggleRecording:async()=>{if(s.isRecording){h();return}await a()},get turns(){return s.turns},get assistantTexts(){return s.assistantTexts},get assistantAudio(){return s.assistantAudio},get call(){return s.call}}};var te=()=>({activeSourceCount:0,error:null,isActive:!1,isPlaying:!1,lastInterruptLatencyMs:void 0,lastPlaybackStopLatencyMs:void 0,processedChunkCount:0,queuedChunkCount:0}),ie=()=>{if(typeof window>"u")return typeof AudioContext>"u"?void 0:AudioContext;return window.AudioContext??window.webkitAudioContext},se=(n,e)=>{let o=e.format;if(o.container!=="raw"||o.encoding!=="pcm_s16le")throw Error(`Unsupported assistant audio format: ${o.container}/${o.encoding}`);let c=e.chunk,i=Math.max(1,o.channels),s=Math.floor(c.byteLength/2),t=Math.max(1,Math.floor(s/i)),r=n.createBuffer(i,t,o.sampleRateHz),g=new DataView(c.buffer,c.byteOffset,c.byteLength);for(let y=0;y<i;y+=1){let d=r.getChannelData(y);for(let h=0;h<t;h+=1){let M=(h*i+y)*2;if(M+1>=c.byteLength){d[h]=0;continue}d[h]=g.getInt16(M,!0)/32768}}return r},m=(n,e={})=>{let o=new Set,c=new Set,i=(e.lookaheadMs??15)/1000,s=te(),t=null,r=null,g=0,y=Promise.resolve(),d=null,h=null,a=null,M=null,C=()=>{for(let A of o)A()},I=(A)=>{s={...s,...A},C()},L=()=>{if(s.error!==null)I({error:null})},b=()=>{if(M!==null)clearTimeout(M),M=null},w=(A)=>{b(),d=null,I({activeSourceCount:c.size,isPlaying:!1,lastInterruptLatencyMs:A,lastPlaybackStopLatencyMs:s.lastPlaybackStopLatencyMs??A}),a?.(),a=null,h=null},u=(A)=>{if(!A)return 0;return Math.max(0,((A.baseLatency??0)+(A.outputLatency??0))*1000)},U=(A)=>{if(!r)return;let f=1;if(r.gain.setValueAtTime){r.gain.setValueAtTime(f,A?.currentTime??0);return}r.gain.value=f},l=(A)=>{if(!r)return;let f=0;if(r.gain.setValueAtTime){r.gain.setValueAtTime(f,A?.currentTime??0);return}r.gain.value=f},S=()=>{if(d===null||c.size>0)return;w(Date.now()-d)},R=async()=>{if(t)return t;if(e.createAudioContext)t=e.createAudioContext();else{let A=ie();if(!A)throw Error("Assistant audio playback requires AudioContext support.");t=new A}if(t.createGain)r=t.createGain(),r.connect?.(t.destination);return g=t.currentTime,t},z=async(A)=>{let f=await R(),P=se(f,A),V=f.createBufferSource();V.buffer=P,V.connect(r??f.destination),V.onended=()=>{c.delete(V),V.disconnect?.(),I({activeSourceCount:c.size,isPlaying:c.size>0&&s.isActive}),S()};let Y=Math.max(f.currentTime+i,g);g=Y+P.duration,c.add(V),I({activeSourceCount:c.size,isPlaying:!0}),V.start(Y)},E=(A)=>{for(let f of[...c])f.stop?.();if(g=t?t.currentTime:0,A?.forceClear){for(let f of c)f.disconnect?.();c.clear(),S()}},W=async()=>{if(!s.isActive)return;let A=n.assistantAudio.slice(s.processedChunkCount);if(A.length===0)return;try{L();for(let f of A)await z(f);I({processedChunkCount:n.assistantAudio.length,queuedChunkCount:s.queuedChunkCount+A.length})}catch(f){I({error:f instanceof Error?f.message:String(f)})}},D=()=>{return y=y.then(()=>W(),()=>W()),y},$=n.subscribe(()=>{if(e.autoStart&&!s.isActive&&n.assistantAudio.length>0){N.start();return}if(s.isActive)D()}),N={close:async()=>{if($(),E({forceClear:!0}),b(),a?.(),a=null,h=null,d=null,t&&t.state!=="closed")await t.close();t=null,r?.disconnect?.(),r=null,g=0,I({activeSourceCount:0,isActive:!1,isPlaying:!1})},get activeSourceCount(){return s.activeSourceCount},get error(){return s.error},getSnapshot:()=>s,get isActive(){return s.isActive},get isPlaying(){return s.isPlaying},interrupt:async()=>{let A=Date.now(),f=await R();d=A,l(f);let P=Date.now()-A+u(f);if(I({isActive:!1,isPlaying:c.size>0,lastPlaybackStopLatencyMs:P}),c.size===0){w(P);return}if(!h)h=new Promise((V)=>{a=V});b(),M=setTimeout(()=>{for(let V of c)V.disconnect?.();c.clear(),w(Date.now()-A)},250),E(),await h},get lastInterruptLatencyMs(){return s.lastInterruptLatencyMs},get lastPlaybackStopLatencyMs(){return s.lastPlaybackStopLatencyMs},pause:async()=>{if(!t){I({activeSourceCount:0,isActive:!1,isPlaying:!1});return}await t.suspend(),I({activeSourceCount:c.size,isActive:!1,isPlaying:!1})},get processedChunkCount(){return s.processedChunkCount},get queuedChunkCount(){return s.queuedChunkCount},start:async()=>{try{L();let A=await R();if(U(A),A.state==="suspended")await A.resume();I({activeSourceCount:c.size,isActive:!0,isPlaying:A.state==="running"}),await D()}catch(A){throw I({error:A instanceof Error?A.message:String(A),isActive:!1,isPlaying:!1}),A}},subscribe:(A)=>{return o.add(A),()=>{o.delete(A)}}};return N};var le=()=>`barge-in:${Date.now()}:${crypto.randomUUID?.()??Math.random().toString(36).slice(2)}`,re=(n,e)=>{let o=n.filter((t)=>t.status==="stopped"),c=o.map((t)=>t.latencyMs).filter((t)=>typeof t==="number"),i=o.filter((t)=>typeof t.latencyMs==="number"&&t.latencyMs>e).length,s=o.length-i;return{averageLatencyMs:c.length>0?Math.round(c.reduce((t,r)=>t+r,0)/c.length):void 0,events:[...n],failed:i,lastEvent:n.at(-1),passed:s,status:n.length===0?"empty":i>0?"fail":o.length===0?"warn":"pass",thresholdMs:e,total:o.length}},bn=(n={})=>{let e=new Set,o=n.thresholdMs??250,c=n.fetch??globalThis.fetch,i=[],s=()=>{for(let g of e)g()},t=(g)=>{if(!n.path||typeof c!=="function")return;c(n.path,{body:JSON.stringify(g),headers:{"Content-Type":"application/json"},method:"POST"}).catch(()=>{})},r=(g,y)=>{let d={at:Date.now(),id:le(),latencyMs:y.latencyMs,playbackStopLatencyMs:y.playbackStopLatencyMs,reason:y.reason,sessionId:y.sessionId,status:g,thresholdMs:o};return i.push(d),t(d),s(),d};return{getSnapshot:()=>re(i,o),recordRequested:(g)=>r("requested",g),recordSkipped:(g)=>r("skipped",g),recordStopped:(g)=>r("stopped",g),subscribe:(g)=>{return e.add(g),()=>{e.delete(g)}}}};var ae=0.08,de=(n,e={})=>(e.enabled??!0)&&n>=(e.interruptThreshold??ae),cn=(n,e,o={})=>{let c=n.partial,i=(t)=>{if(!e.isPlaying||o.enabled===!1){o.monitor?.recordSkipped({reason:t,sessionId:n.sessionId});return}o.monitor?.recordRequested({reason:t,sessionId:n.sessionId}),e.interrupt().then(()=>{o.monitor?.recordStopped({latencyMs:e.lastInterruptLatencyMs,playbackStopLatencyMs:e.lastPlaybackStopLatencyMs,reason:t,sessionId:n.sessionId})})},s=n.subscribe(()=>{if(o.interruptOnPartial===!1){c=n.partial;return}if(!c&&n.partial)i("partial-transcript");c=n.partial});return{close:()=>{s()},handleLevel:(t)=>{if(de(t,o))i("input-level")},sendAudio:(t)=>{i("manual-audio"),n.sendAudio(t)}}};var rn=48,ge=320,Ae=88,he="Guided test",ye="General recording",Ce="Pick a scenario to begin the demo.",fe="I can walk you through a short guided voice test.",Te="I can capture one freeform recording and confirm that it landed.",Se="Choose a scenario to begin. Guided test asks follow-up prompts. General recording just captures what you say.",Me="Click Start general recording to capture one freeform answer.",Pn="Speak freely. When you pause, the recording will be captured.",sn="Recording saved. Start again if you want another capture.",Dn="Guided test complete. Review the saved summary below.",On="All prompts are covered. You can stop the microphone or keep speaking for extra detail.",Ie="Ready. Start guided test or general recording to begin.",Ve="Live. Answer the prompt, then click Stop microphone when finished.",_n=["Start with a quick introduction about who you are.","Now describe what you are trying to do or test.","Finish with any detail that feels blocked, risky, or unclear."],xn=(n,e,o)=>Math.min(o,Math.max(e,n)),X=(n)=>n.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll(\'"\',"&quot;").replaceAll("\'","&#39;"),on=(n,e)=>{let o=n[e];if(typeof o==="string"&&o.trim())return o;return null},ln=(n)=>{if(typeof n==="string"&&n.trim())return n;if(n instanceof Error&&n.message.trim())return n.message;if(n&&typeof n==="object"){let e=n,o=on(e,"message")??on(e,"reason")??on(e,"description");if(o)return o;if("error"in e)return ln(e.error);if("cause"in e)return ln(e.cause);try{return JSON.stringify(n)}catch{}}return"Unexpected error"},Le=(n)=>{let e=[n.status];if(n.attempts>0||n.maxAttempts>0)e.push(`${n.attempts}/${n.maxAttempts} attempts`);if(n.nextAttemptAt){let o=Math.max(0,n.nextAttemptAt-Date.now());e.push(`retry in ${Math.ceil(o/100)/10}s`)}return e.join(" \xB7 ")},F=(n=rn)=>Array.from({length:n},()=>0),En=(n,e,o=rn)=>{let c=n.slice(-(o-1));c.push(xn(e,0,1));while(c.length<o)c.unshift(0);return c},Re=(n,e=ge,o=Ae)=>{let c=n.length>1?n:F(rn),i=e/(c.length-1),s=o/2,t=o*0.34;if(Math.max(...c,0)<=0.015)return`M 0 ${s} L ${e} ${s}`;let g=c.map((d,h)=>{let a=h*0.76,M=Math.sin(a)*0.78+Math.sin(a*0.41)*0.22,C=d*t,I=i*h,L=xn(s+M*C,8,o-8);return{x:I,y:L}});if(g.length===0)return`M 0 ${s} L ${e} ${s}`;let y=`M ${g[0]?.x??0} ${g[0]?.y??s}`;for(let d=1;d<g.length;d+=1){let h=g[d-1],a=g[d];if(!h||!a)continue;let M=(h.x+a.x)/2;y+=` Q ${M} ${h.y} ${a.x} ${a.y}`}return y},we=(n)=>{if(!n)return _n;try{let e=JSON.parse(n);if(Array.isArray(e)){let o=e.filter((c)=>typeof c==="string").map((c)=>c.trim()).filter(Boolean);if(o.length>0)return o}}catch{}return _n},tn=(n)=>{if(!n)return;let e=Number(n);return Number.isFinite(e)?e:void 0},be=(n,e,o)=>{if(!e)return null;let c=document.querySelector(e);return c instanceof o?c:null},x=(n,e,o,c)=>{let i=e?document.querySelector(e):null;if(i instanceof o)return i;let s=n.querySelector(`#${c}`);if(s instanceof o)return s;throw Error(`Voice HTMX bootstrap could not find the required element "${c}".`)},_e=(n)=>{if(!n.mode)return Ce;if(!n.hasStarted)return n.mode==="guided"?fe:Te;if(n.status==="completed")return n.mode==="guided"?Dn:sn;if(n.mode==="general")return Pn;return n.guidedPrompts[n.turnCount]??On},Ee=(n)=>{if(!n.mode)return Se;if(n.status==="completed")return n.mode==="guided"?Dn:sn;if(!n.hasStarted)return n.mode==="guided"?`Click Start guided test to begin. First prompt: ${n.guidedPrompts[0]??"Answer the first prompt."}`:Me;if(n.mode==="general")return n.turnCount===0?Pn:sn;return n.guidedPrompts[n.turnCount]??On},Pe=(n)=>{let e=n.dataset.voiceGuidedPath,o=n.dataset.voiceGeneralPath;if(!e||!o)throw Error("Voice HTMX bootstrap requires data-voice-guided-path and data-voice-general-path.");let c=we(n.dataset.voiceGuidedPrompts),i=n.dataset.voiceGuidedLabel??he,s=n.dataset.voiceGeneralLabel??ye,t=n.dataset.voiceReconnectReportPath,r=n.dataset.voiceBargeInPath,g=r?bn({path:r,thresholdMs:tn(n.dataset.voiceBargeInThresholdMs)}):null,y=tn(n.dataset.voiceBargeInRecentWindowMs)??4000,d=tn(n.dataset.voiceBargeInSpeechThreshold)??0.04,h=x(document,n.dataset.voiceSync,HTMLElement,"voice-htmx-sync"),a=x(n,n.dataset.voiceConnection,HTMLElement,"metric-connection"),M=x(n,n.dataset.voiceError,HTMLElement,"status-error"),C=x(n,n.dataset.voiceMicrophone,HTMLElement,"status-mic"),I=x(n,n.dataset.voicePrompt,HTMLElement,"status-prompt"),L=be(n,n.dataset.voiceReconnect,HTMLElement),b=x(n,n.dataset.voiceChat,HTMLElement,"chat-list"),w=x(n,n.dataset.voiceStartGuided,HTMLButtonElement,"start-guided"),u=x(n,n.dataset.voiceStartGeneral,HTMLButtonElement,"start-general"),U=x(n,n.dataset.voiceStop,HTMLButtonElement,"stop-mic"),l=x(n,n.dataset.voiceMonitor,HTMLElement,"voice-monitor"),S=x(n,n.dataset.voiceMonitorCopy,HTMLElement,"voice-monitor-copy"),R=x(n,n.dataset.voiceWaveGlow,SVGPathElement,"voice-wave-glow"),z=x(n,n.dataset.voiceWavePath,SVGPathElement,"voice-wave-path"),E=null,W={general:!1,guided:!1},D=!1,$=null,N=F(),A=null,f=null,P=j(e,{capture:{onAudio:(T,G)=>{if(A){A.sendAudio(T);return}G(T)},onLevel:(T)=>{A?.handleLevel(T),N=En(N,T),v()}},connection:{reconnectReportPath:t},preset:"guided-intake"}),V=j(o,{capture:{onAudio:(T,G)=>{if(f){f.sendAudio(T);return}G(T)},onLevel:(T)=>{f?.handleLevel(T),N=En(N,T),v()}},connection:{reconnectReportPath:t},preset:"dictation"}),Y=P.bindHTMX({element:h}),un=V.bindHTMX({element:h}),J=m(P),Q=m(V);A=cn(P,J,{interruptThreshold:d,monitor:g??void 0}),f=cn(V,Q,{interruptThreshold:d,monitor:g??void 0});let k=()=>E==="general"?V:P,Oe=()=>E==="general"?Q:J,v=()=>{let T=Re(N);R.setAttribute("d",T),z.setAttribute("d",T),S.innerHTML=`<span class="voice-live-dot"></span>${D?"Microphone live":"Microphone idle"}`,S.classList.toggle("is-live",D),l.classList.toggle("is-live",D)},q=()=>{let T=k(),G=(E?W[E]:!1)||T.turns.length>0,dn=T.status;if(a.textContent=T.isConnected?"Connected":"Waiting",M.textContent=$||T.error||"None",L)L.textContent=Le(T.reconnect);C.textContent=D?Ve:Ie,I.textContent=Ee({guidedPrompts:c,hasStarted:G,mode:E,status:dn,turnCount:T.turns.length}),w.hidden=D,u.hidden=D,U.hidden=!D,b.innerHTML=`<article class="voice-chat-message assistant">\n <div class="voice-chat-role">${X(E==="general"?s:E==="guided"?i:"Voice demo")}</div>\n <p class="voice-turn-text">${X(_e({generalLabel:s,guidedLabel:i,guidedPrompts:c,hasStarted:G,mode:E,status:dn,turnCount:T.turns.length}))}</p>\n</article>${T.turns.map((p)=>`<div class="voice-chat-stack">\n <article class="voice-chat-message user">\n <div class="voice-chat-role">You</div>\n <p class="voice-turn-text">${X(p.text)}</p>\n </article>\n ${p.assistantText?`<article class="voice-chat-message assistant">\n <div class="voice-chat-role">${X(E==="general"?s:E==="guided"?i:"Guide")}</div>\n <p class="voice-turn-text">${X(p.assistantText)}</p>\n </article>`:""}\n</div>`).join("")}${T.partial?`<article class="voice-chat-message user pending">\n <div class="voice-chat-role">Speaking</div>\n <p class="voice-turn-text">${X(T.partial)}</p>\n</article>`:""}`,v()},Un=()=>{k().stopRecording(),D=!1,$=null,N=F(),q()},an=async(T)=>{E=T,W={...W,[T]:!0};try{await k().startRecording(),$=null,D=!0,q()}catch(G){k().stopRecording(),D=!1,N=F(),$=ln(G),q()}};P.subscribe(()=>{if(P.assistantAudio.length>0)J.start().catch(()=>{});q()}),V.subscribe(()=>{if(V.assistantAudio.length>0)Q.start().catch(()=>{});q()}),w.addEventListener("click",()=>{an("guided")}),u.addEventListener("click",()=>{an("general")}),U.addEventListener("click",()=>{Un()}),n.addEventListener("absolute-voice-simulate-disconnect",()=>{k().simulateDisconnect()}),window.addEventListener("beforeunload",()=>{P.stopRecording(),V.stopRecording(),A?.close(),f?.close(),J.close(),Q.close(),Y(),un(),P.close(),V.close()}),q()},De=()=>{if(typeof window>"u"||typeof document>"u")return;let n=Array.from(document.querySelectorAll("[data-voice-htmx]"));for(let e of n)if(e instanceof HTMLElement)Pe(e)};De();export{De as initVoiceHTMX};\n';
5744
+ var HTMX_BOOTSTRAP_BUNDLE = 'var Hn=(n)=>{if(typeof n!=="string")return n;return document.querySelector(n)},Gn=(n,c,o,e)=>{let i=c??n.getAttribute("hx-get")??"";if(!i)return"";let l=new URL(i,window.location.origin);if(e)l.searchParams.set(o,e);else l.searchParams.delete(o);return`${l.pathname}${l.search}${l.hash}`},gn=(n,c)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let o=Hn(c.element);if(!o)return()=>{};let e=c.eventName??"voice-refresh",i=c.sessionQueryParam??"sessionId",l=()=>{let d=window,g=Gn(o,c.route,i,n.sessionId);if(g)o.setAttribute("hx-get",g);d.htmx?.process?.(o),d.htmx?.trigger?.(o,e)},t=n.subscribe(l);return l(),()=>{t()}};var Bn=(n)=>Math.max(-1,Math.min(1,n)),Wn=(n)=>{let c=new Int16Array(n.length);for(let o=0;o<n.length;o+=1){let e=Bn(n[o]??0);c[o]=e<0?e*32768:e*32767}return new Uint8Array(c.buffer)},$n=(n)=>{let c=n instanceof Uint8Array?n:new Uint8Array(n);if(c.byteLength<2)return 0;let o=new Int16Array(c.buffer,c.byteOffset,Math.floor(c.byteLength/2));if(o.length===0)return 0;let e=0;for(let i of o){let l=i/32768;e+=l*l}return Math.min(1,Math.max(0,Math.sqrt(e/o.length)*5.5))},qn=(n,c,o)=>{if(c===o)return n;let e=c/o,i=Math.round(n.length/e),l=new Float32Array(i),t=0,d=0;while(t<l.length){let g=Math.round((t+1)*e),y=0,a=0;for(let h=d;h<g&&h<n.length;h+=1)y+=n[h]??0,a+=1;l[t]=a>0?y/a:0,t+=1,d=g}return l},An=(n)=>{let c=null,o=null,e=null,i=null;return{start:async()=>{if(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia)throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let d=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!d)throw Error("Browser microphone capture requires AudioContext support.");i=await navigator.mediaDevices.getUserMedia({audio:{channelCount:n.channelCount??1}}),c=new d,o=c.createMediaStreamSource(i),e=c.createScriptProcessor(4096,1,1),e.onaudioprocess=(g)=>{let y=g.inputBuffer.getChannelData(0),a=qn(y,c?.sampleRate??48000,n.sampleRateHz??16000),h=Wn(a);n.onLevel?.($n(h)),n.onAudio(h)},o.connect(e),e.connect(c.destination)},stop:()=>{e?.disconnect(),o?.disconnect(),i?.getTracks().forEach((d)=>d.stop()),c?.close(),n.onLevel?.(0),c=null,i=null,e=null,o=null}}};var nn=(n)=>{if(typeof n==="string"&&n.trim())return n;if(n instanceof Error&&n.message.trim())return n.message;if(n&&typeof n==="object"){let c=n;for(let o of["message","reason","description"]){let e=c[o];if(typeof e==="string"&&e.trim())return e}if("error"in c)return nn(c.error);if("cause"in c)return nn(c.cause);try{return JSON.stringify(n)}catch{}}return"Unexpected error"},hn=(n)=>{switch(n.type){case"audio":return{chunk:Uint8Array.from(atob(n.chunkBase64),(c)=>c.charCodeAt(0)),format:n.format,receivedAt:n.receivedAt,turnId:n.turnId,type:"audio"};case"assistant":return{text:n.text,type:"assistant"};case"complete":return{sessionId:n.sessionId,type:"complete"};case"connection":return{reconnect:n.reconnect,type:"connection"};case"call_lifecycle":return{event:n.event,sessionId:n.sessionId,type:"call_lifecycle"};case"error":return{message:nn(n.message),type:"error"};case"final":return{transcript:n.transcript,type:"final"};case"partial":return{transcript:n.transcript,type:"partial"};case"replay":return{assistantTexts:n.assistantTexts,call:n.call,partial:n.partial,scenarioId:n.scenarioId,sessionId:n.sessionId,sessionMetadata:n.sessionMetadata,status:n.status,turns:n.turns,type:"replay"};case"session":return{sessionId:n.sessionId,sessionMetadata:n.sessionMetadata,scenarioId:n.scenarioId,status:n.status,type:"session"};case"turn":return{turn:n.turn,type:"turn"};default:return null}};var Hc=Math.PI*2;var G=(n,c,o,e)=>{n.push({code:o,message:e,severity:c})};var Xn=(n)=>n.length===0?void 0:n.reduce((c,o)=>c+o,0)/n.length,K=(n)=>n.length===0?void 0:Math.max(...n);var b=(n,c)=>{let o=n[c];return typeof o==="number"&&Number.isFinite(o)?o:void 0},Z=(n,c)=>{let o=n[c];return typeof o==="boolean"?o:void 0},O=(n,c)=>{let o=n[c];return typeof o==="string"?o:void 0},cn=(n)=>String(n.id??O(n,"ssrc")??b(n,"ssrc")??O(n,"trackIdentifier")??O(n,"mid")??"unknown"),yn=(n)=>n===void 0?void 0:n*1000;var un=(n)=>{let c={};for(let[o,e]of Object.entries(n))if(e===null||typeof e==="boolean"||typeof e==="number"||typeof e==="string")c[o]=e;return c};var Cn=(n={})=>{let c=n.stats??[],o=[],e=c.filter((s)=>s.type==="inbound-rtp"&&O(s,"kind")!=="video"),i=c.filter((s)=>s.type==="outbound-rtp"&&O(s,"kind")!=="video"),l=c.filter((s)=>s.type==="candidate-pair"),t=c.filter((s)=>(s.type==="track"||s.type==="media-source")&&O(s,"kind")==="audio"),d=l.filter((s)=>Z(s,"selected")===!0||Z(s,"nominated")===!0||O(s,"state")==="succeeded").length,g=t.filter((s)=>O(s,"readyState")!=="ended"&&O(s,"trackState")!=="ended"&&Z(s,"ended")!==!0).length,y=t.filter((s)=>O(s,"readyState")==="ended"||O(s,"trackState")==="ended"||Z(s,"ended")===!0).length,a=e.reduce((s,V)=>s+(b(V,"packetsReceived")??0),0),h=i.reduce((s,V)=>s+(b(V,"packetsSent")??0),0),r=[...e,...i].reduce((s,V)=>s+Math.max(0,b(V,"packetsLost")??0),0),M=a+r,C=M===0?0:r/M,S=e.reduce((s,V)=>s+(b(V,"bytesReceived")??0),0),w=i.reduce((s,V)=>s+(b(V,"bytesSent")??0),0),E=K(l.map((s)=>yn(b(s,"currentRoundTripTime")??b(s,"roundTripTime"))).filter((s)=>s!==void 0)),_=K([...e,...i].map((s)=>yn(b(s,"jitter"))).filter((s)=>s!==void 0)),U=K(e.map((s)=>{let V=b(s,"jitterBufferDelay"),R=b(s,"jitterBufferEmittedCount");return V!==void 0&&R!==void 0&&R>0?V/R*1000:void 0}).filter((s)=>s!==void 0)),N=t.map((s)=>b(s,"audioLevel")).filter((s)=>s!==void 0);if(n.requireConnectedCandidatePair&&l.length>0&&d===0)G(o,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(n.requireLiveAudioTrack&&g===0)G(o,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(n.maxPacketLossRatio!==void 0&&C>n.maxPacketLossRatio)G(o,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(C)} above ${String(n.maxPacketLossRatio)}.`);if(n.maxRoundTripTimeMs!==void 0&&E!==void 0&&E>n.maxRoundTripTimeMs)G(o,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(E)}ms above ${String(n.maxRoundTripTimeMs)}ms.`);if(n.maxJitterMs!==void 0&&_!==void 0&&_>n.maxJitterMs)G(o,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(_)}ms above ${String(n.maxJitterMs)}ms.`);return{activeCandidatePairs:d,audioLevelAverage:Xn(N),bytesReceived:S,bytesSent:w,checkedAt:Date.now(),endedAudioTracks:y,inboundPackets:a,issues:o,jitterBufferDelayMs:U,jitterMs:_,liveAudioTracks:g,outboundPackets:h,packetLossRatio:C,packetsLost:r,roundTripTimeMs:E,status:o.some((s)=>s.severity==="error")?"fail":o.length>0?"warn":"pass",totalStats:c.length}},Tn=async(n)=>{return[...(await n.peerConnection.getStats(n.selector??null)).values()].map(un)};var In=(n={})=>{let c=n.stats??[],o=n.previousStats??[],e=[],i=new Map(o.map((r)=>[cn(r),r])),t=c.filter((r)=>(r.type==="inbound-rtp"||r.type==="outbound-rtp")&&O(r,"kind")!=="video"&&O(r,"mediaType")!=="video").map((r)=>{let M=r.type==="outbound-rtp"?"outbound":"inbound",C=M==="outbound"?"packetsSent":"packetsReceived",S=M==="outbound"?"bytesSent":"bytesReceived",w=i.get(cn(r)),E=b(r,C),_=w?b(w,C):void 0,U=b(r,S),N=w?b(w,S):void 0,s=r.timestamp!==void 0&&w?.timestamp!==void 0?r.timestamp-w.timestamp:void 0;return{bytesDelta:U!==void 0&&N!==void 0?U-N:void 0,currentPackets:E,direction:M,id:cn(r),packetDelta:E!==void 0&&_!==void 0?E-_:void 0,previousPackets:_,timeDeltaMs:s}}),d=t.filter((r)=>r.direction==="inbound"),g=t.filter((r)=>r.direction==="outbound"),y=K(t.map((r)=>r.timeDeltaMs).filter((r)=>r!==void 0)),a=d.filter((r)=>n.maxInboundPacketStallMs!==void 0&&r.timeDeltaMs!==void 0&&r.timeDeltaMs>=n.maxInboundPacketStallMs&&r.packetDelta!==void 0&&r.packetDelta<=0).length,h=g.filter((r)=>n.maxOutboundPacketStallMs!==void 0&&r.timeDeltaMs!==void 0&&r.timeDeltaMs>=n.maxOutboundPacketStallMs&&r.packetDelta!==void 0&&r.packetDelta<=0).length;if(n.requireInboundAudio&&d.length===0)G(e,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(n.requireOutboundAudio&&g.length===0)G(e,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(n.maxGapMs!==void 0&&y!==void 0&&y>n.maxGapMs)G(e,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(y)}ms above ${String(n.maxGapMs)}ms.`);if(a>0)G(e,"error","media.webrtc_inbound_stalled",`${String(a)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(h>0)G(e,"error","media.webrtc_outbound_stalled",`${String(h)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:d.length,issues:e,maxObservedGapMs:y,outboundAudioStreams:g.length,stalledInboundStreams:a,stalledOutboundStreams:h,status:e.some((r)=>r.severity==="error")?"fail":e.length>0?"warn":"pass",streams:t,totalStats:c.length}};var Yn="/api/voice/browser-media",Jn=5000,Qn=async(n)=>n.peerConnection??await n.getPeerConnection?.()??null,kn=async(n,c)=>{let o=c.fetch??globalThis.fetch;if(!o)return;await o(c.path??Yn,{body:JSON.stringify(n),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},Vn=(n)=>{let c=null,o=[],e=async()=>{let t=await Qn(n);if(!t)return;let d=await Tn({peerConnection:t}),g=Cn({...n,stats:d}),y=n.continuity===!1?void 0:In({...n.continuity,previousStats:o,stats:d}),a={at:Date.now(),continuity:y,report:g,scenarioId:n.getScenarioId?.()??null,sessionId:n.getSessionId?.()??null};return o=d,n.onReport?.(a),await kn(a,n),a},i=()=>{e().catch((t)=>{n.onError?.(t)})},l=()=>{if(c)clearInterval(c),c=null};return{close:l,reportOnce:e,start:()=>{if(c)return;i(),c=setInterval(i,n.intervalMs??Jn)},stop:l}};var W=()=>{},zn=()=>W,Zn={callControl:W,close:W,endTurn:W,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",send:W,sendAudio:W,simulateDisconnect:W,start:()=>{},subscribe:zn},Kn=()=>crypto.randomUUID(),jn=(n,c,o)=>{let{hostname:e,port:i,protocol:l}=window.location,t=l==="https:"?"wss:":"ws:",d=i?`:${i}`:"",g=new URL(`${t}//${e}${d}${n}`);if(g.searchParams.set("sessionId",c),o)g.searchParams.set("scenarioId",o);return g.toString()},Fn=(n)=>{if(!n||typeof n!=="object"||!("type"in n))return!1;switch(n.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}},vn=(n)=>{if(typeof n.data!=="string")return null;try{let c=JSON.parse(n.data);return Fn(c)?c:null}catch{return null}},Mn=(n,c={})=>{if(typeof window>"u")return Zn;let o=new Set,e=c.reconnect!==!1,i=c.maxReconnectAttempts??10,l=c.pingInterval??30000,t={isConnected:!1,pendingMessages:[],scenarioId:c.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:c.sessionId??Kn(),ws:null},d=(s)=>{o.forEach((V)=>V(s))},g=()=>{if(t.pingInterval)clearInterval(t.pingInterval),t.pingInterval=null;if(t.reconnectTimeout)clearTimeout(t.reconnectTimeout),t.reconnectTimeout=null},y=()=>{if(t.ws?.readyState!==1)return;while(t.pendingMessages.length>0){let s=t.pendingMessages.shift();if(s!==void 0)t.ws.send(s)}},a=()=>{let s=Date.now()+500;t.reconnectAttempts+=1,d({reconnect:{attempts:t.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:i,nextAttemptAt:s,status:"reconnecting"},type:"connection"}),t.reconnectTimeout=setTimeout(()=>{if(t.reconnectAttempts>i){d({reconnect:{attempts:t.reconnectAttempts,maxAttempts:i,status:"exhausted"},type:"connection"});return}h()},500)},h=()=>{let s=new WebSocket(jn(n,t.sessionId,t.scenarioId));s.binaryType="arraybuffer",s.onopen=()=>{let V=t.reconnectAttempts>0;if(t.isConnected=!0,y(),V)d({reconnect:{attempts:t.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:i,status:"resumed"},type:"connection"}),t.reconnectAttempts=0;o.forEach((R)=>R({scenarioId:t.scenarioId??void 0,sessionId:t.sessionId,status:"active",type:"session"})),t.pingInterval=setInterval(()=>{if(s.readyState===1)s.send(JSON.stringify({type:"ping"}))},l)},s.onmessage=(V)=>{let R=vn(V);if(!R)return;if(R.type==="session")t.sessionId=R.sessionId,t.scenarioId=R.scenarioId??t.scenarioId;o.forEach((Y)=>Y(R))},s.onclose=(V)=>{if(t.isConnected=!1,g(),e&&V.code!==1000&&t.reconnectAttempts<i)a();else if(e&&V.code!==1000)d({reconnect:{attempts:t.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:i,status:"exhausted"},type:"connection"})},t.ws=s},r=(s)=>{if(t.ws?.readyState===1){t.ws.send(s);return}t.pendingMessages.push(s)},M=(s)=>{r(JSON.stringify(s))},C=(s={})=>{if(s.sessionId)t.sessionId=s.sessionId;if(s.scenarioId)t.scenarioId=s.scenarioId;M({type:"start",sessionId:t.sessionId,scenarioId:t.scenarioId??void 0})},S=(s)=>{r(s)},w=()=>{M({type:"end_turn"})},E=(s)=>{M({...s,type:"call_control"})},_=()=>{if(g(),t.ws)t.ws.close(1000),t.ws=null;t.isConnected=!1,o.clear()},U=()=>{if(t.ws?.readyState===1)t.ws.close(4000,"absolutejs-voice-reconnect-proof")},N=(s)=>{return o.add(s),()=>{o.delete(s)}};return h(),{callControl:E,close:_,endTurn:w,getReadyState:()=>t.ws?.readyState??3,getScenarioId:()=>t.scenarioId??"",getSessionId:()=>t.sessionId,send:M,sendAudio:S,simulateDisconnect:U,start:C,subscribe:N}};var mn=()=>({attempts:0,maxAttempts:0,status:"idle"}),pn=()=>({assistantAudio:[],assistantTexts:[],call:null,error:null,isConnected:!1,sessionMetadata:null,scenarioId:null,partial:"",reconnect:mn(),sessionId:null,status:"idle",turns:[]}),Sn=()=>{let n=pn(),c=new Set,o=()=>{c.forEach((i)=>i())};return{dispatch:(i)=>{switch(i.type){case"audio":n={...n,assistantAudio:[...n.assistantAudio,{chunk:i.chunk,format:i.format,receivedAt:i.receivedAt,turnId:i.turnId}]};break;case"assistant":n={...n,assistantTexts:[...n.assistantTexts,i.text]};break;case"complete":n={...n,sessionId:i.sessionId,status:"completed"};break;case"call_lifecycle":n={...n,call:{...n.call,disposition:i.event.type==="end"?i.event.disposition:n.call?.disposition,endedAt:i.event.type==="end"?i.event.at:n.call?.endedAt,events:[...n.call?.events??[],i.event],lastEventAt:i.event.at,startedAt:n.call?.startedAt??i.event.at},sessionId:i.sessionId};break;case"connected":n={...n,isConnected:!0,reconnect:n.reconnect.status==="reconnecting"?{...n.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:n.reconnect};break;case"connection":n={...n,reconnect:i.reconnect};break;case"disconnected":n={...n,isConnected:!1};break;case"error":n={...n,error:i.message};break;case"final":n={...n,partial:i.transcript.text,turns:n.turns.map((l)=>l)};break;case"partial":n={...n,partial:i.transcript.text};break;case"replay":n={...n,assistantTexts:[...i.assistantTexts],call:i.call??null,error:null,isConnected:i.status==="active",partial:i.partial,reconnect:n.reconnect.status==="reconnecting"?{...n.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:n.reconnect,scenarioId:i.scenarioId??n.scenarioId,sessionId:i.sessionId,sessionMetadata:i.sessionMetadata??n.sessionMetadata,status:i.status,turns:[...i.turns]};break;case"session":n={...n,error:null,scenarioId:i.scenarioId??n.scenarioId,isConnected:i.status==="active",sessionId:i.sessionId,sessionMetadata:i.sessionMetadata??n.sessionMetadata,status:i.status};break;case"turn":n={...n,partial:"",turns:[...n.turns,i.turn]};break}o()},getServerSnapshot:()=>n,getSnapshot:()=>n,subscribe:(i)=>{return c.add(i),()=>{c.delete(i)}}}};var Ln=(n,c={})=>{let o=Mn(n,c),e=Sn(),i=c.browserMedia&&typeof window<"u"?Vn({...c.browserMedia,getScenarioId:()=>c.browserMedia?c.browserMedia.getScenarioId?.()??o.getScenarioId():o.getScenarioId(),getSessionId:()=>c.browserMedia?c.browserMedia.getSessionId?.()??o.getSessionId():o.getSessionId()}):null,l=new Set,t=(a)=>Promise.resolve().then(()=>{if(!a?.sessionId&&!a?.scenarioId)return;o.start(a),i?.start()}),d=()=>{l.forEach((a)=>a())},g=()=>{if(!c.reconnectReportPath||typeof fetch>"u")return;let a=e.getSnapshot(),h=JSON.stringify({at:Date.now(),reconnect:a.reconnect,scenarioId:a.scenarioId,sessionId:o.getSessionId(),turnIds:a.turns.map((r)=>r.id)});fetch(c.reconnectReportPath,{body:h,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},y=o.subscribe((a)=>{let h=hn(a);if(h){if(e.dispatch(h),a.type==="connection")g();d()}});return{callControl(a){o.callControl(a)},close(){y(),i?.close(),o.close(),e.dispatch({type:"disconnected"}),d()},endTurn(){o.endTurn()},get error(){return e.getSnapshot().error},getServerSnapshot(){return e.getServerSnapshot()},getSnapshot(){return e.getSnapshot()},get isConnected(){return e.getSnapshot().isConnected},get scenarioId(){return e.getSnapshot().scenarioId},get sessionMetadata(){return e.getSnapshot().sessionMetadata},start:t,get partial(){return e.getSnapshot().partial},get reconnect(){return e.getSnapshot().reconnect},get sessionId(){return o.getSessionId()},get status(){return e.getSnapshot().status},get turns(){return e.getSnapshot().turns},get assistantTexts(){return e.getSnapshot().assistantTexts},get assistantAudio(){return e.getSnapshot().assistantAudio},get call(){return e.getSnapshot().call},sendAudio(a){o.sendAudio(a)},simulateDisconnect(){o.simulateDisconnect()},subscribe(a){return l.add(a),()=>{l.delete(a)}}}};var wn=(n)=>{if(!n||n.enabled===!1)return;return{enabled:!0,maxGain:n.maxGain??3,noiseGateAttenuation:n.noiseGateAttenuation??0.15,noiseGateThreshold:n.noiseGateThreshold??0.006,targetLevel:n.targetLevel??0.08}};var nc={balanced:{qualityProfile:"general",silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:"general",silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},"long-form":{qualityProfile:"general",silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},cc={general:{},"accent-heavy":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},"noisy-room":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},"short-command":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var Rn=(n)=>{let c=n?.profile??"fast",o=n?.qualityProfile??"general",e=nc[c],i=cc[o];return{profile:c,qualityProfile:o,silenceMs:n?.silenceMs??i.silenceMs??e.silenceMs,speechThreshold:n?.speechThreshold??i.speechThreshold??e.speechThreshold,transcriptStabilityMs:n?.transcriptStabilityMs??i.transcriptStabilityMs??e.transcriptStabilityMs}};var ec={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:{qualityProfile:"short-command",profile:"balanced"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{qualityProfile:"general",profile:"fast"}},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:{qualityProfile:"accent-heavy",profile:"long-form"}},"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:{qualityProfile:"accent-heavy",profile:"long-form"}},"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:{qualityProfile:"noisy-room",profile:"long-form",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:{qualityProfile:"noisy-room",profile:"long-form",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:{qualityProfile:"noisy-room",profile:"long-form",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:{qualityProfile:"noisy-room",profile:"long-form"}}},_n=(n="default")=>{let c=ec[n];return{audioConditioning:wn(c.audioConditioning),capture:{channelCount:c.capture?.channelCount??1,sampleRateHz:c.capture?.sampleRateHz??16000},connection:{...c.connection},name:n,sttLifecycle:c.sttLifecycle??"continuous",turnDetection:Rn(c.turnDetection)}};var oc=(n)=>({assistantAudio:[...n.assistantAudio],assistantTexts:[...n.assistantTexts],call:n.call,error:n.error,isConnected:n.isConnected,isRecording:!1,partial:n.partial,reconnect:n.reconnect,recordingError:null,sessionId:n.sessionId,sessionMetadata:n.sessionMetadata,scenarioId:n.scenarioId,status:n.status,turns:[...n.turns]}),j=(n,c={})=>{let o=_n(c.preset),e=Ln(n,{...o.connection,...c.connection}),i=null,l=oc(e),t=new Set,d=()=>{for(let C of t)C()},g=()=>{if(l={...l,assistantAudio:[...e.assistantAudio],assistantTexts:[...e.assistantTexts],call:e.call,error:e.error,isConnected:e.isConnected,partial:e.partial,reconnect:e.reconnect,sessionId:e.sessionId,sessionMetadata:e.sessionMetadata,scenarioId:e.scenarioId,status:e.status,turns:[...e.turns]},c.autoStopOnComplete!==!1&&l.status==="completed"&&l.isRecording)i?.stop(),i=null,l={...l,isRecording:!1};d()},y=e.subscribe(g);g();let a=()=>{if(i)return i;return i=An({channelCount:c.capture?.channelCount??o.capture.channelCount,onLevel:c.capture?.onLevel,onAudio:(C)=>{if(c.capture?.onAudio){c.capture.onAudio(C,e.sendAudio);return}e.sendAudio(C)},sampleRateHz:c.capture?.sampleRateHz??o.capture.sampleRateHz}),i},h=()=>{i?.stop(),i=null,l={...l,isRecording:!1},d()},r=async()=>{if(l.isRecording)return;try{l={...l,recordingError:null},d(),await a().start(),l={...l,isRecording:!0},d()}catch(C){throw i=null,l={...l,isRecording:!1,recordingError:C instanceof Error?C.message:String(C)},d(),C}};return{bindHTMX(C){return gn(e,C)},callControl:(C)=>e.callControl(C),close:()=>{y(),h(),e.close()},endTurn:()=>e.endTurn(),get error(){return l.error},getServerSnapshot:()=>l,getSnapshot:()=>l,get isConnected(){return l.isConnected},get isRecording(){return l.isRecording},get partial(){return l.partial},get recordingError(){return l.recordingError},get reconnect(){return l.reconnect},sendAudio:(C)=>e.sendAudio(C),simulateDisconnect:()=>e.simulateDisconnect(),get sessionId(){return l.sessionId},get sessionMetadata(){return l.sessionMetadata},get scenarioId(){return l.scenarioId},startRecording:r,get status(){return l.status},stopRecording:h,subscribe:(C)=>{return t.add(C),()=>{t.delete(C)}},toggleRecording:async()=>{if(l.isRecording){h();return}await r()},get turns(){return l.turns},get assistantTexts(){return l.assistantTexts},get assistantAudio(){return l.assistantAudio},get call(){return l.call}}};var tc=()=>({activeSourceCount:0,error:null,isActive:!1,isPlaying:!1,lastInterruptLatencyMs:void 0,lastPlaybackStopLatencyMs:void 0,processedChunkCount:0,queuedChunkCount:0}),ic=()=>{if(typeof window>"u")return typeof AudioContext>"u"?void 0:AudioContext;return window.AudioContext??window.webkitAudioContext},lc=(n,c)=>{let o=c.format;if(o.container!=="raw"||o.encoding!=="pcm_s16le")throw Error(`Unsupported assistant audio format: ${o.container}/${o.encoding}`);let e=c.chunk,i=Math.max(1,o.channels),l=Math.floor(e.byteLength/2),t=Math.max(1,Math.floor(l/i)),d=n.createBuffer(i,t,o.sampleRateHz),g=new DataView(e.buffer,e.byteOffset,e.byteLength);for(let y=0;y<i;y+=1){let a=d.getChannelData(y);for(let h=0;h<t;h+=1){let M=(h*i+y)*2;if(M+1>=e.byteLength){a[h]=0;continue}a[h]=g.getInt16(M,!0)/32768}}return d},F=(n,c={})=>{let o=new Set,e=new Set,i=(c.lookaheadMs??15)/1000,l=tc(),t=null,d=null,g=0,y=Promise.resolve(),a=null,h=null,r=null,M=null,C=()=>{for(let A of o)A()},S=(A)=>{l={...l,...A},C()},w=()=>{if(l.error!==null)S({error:null})},E=()=>{if(M!==null)clearTimeout(M),M=null},_=(A)=>{E(),a=null,S({activeSourceCount:e.size,isPlaying:!1,lastInterruptLatencyMs:A,lastPlaybackStopLatencyMs:l.lastPlaybackStopLatencyMs??A}),r?.(),r=null,h=null},U=(A)=>{if(!A)return 0;return Math.max(0,((A.baseLatency??0)+(A.outputLatency??0))*1000)},N=(A)=>{if(!d)return;let T=1;if(d.gain.setValueAtTime){d.gain.setValueAtTime(T,A?.currentTime??0);return}d.gain.value=T},s=(A)=>{if(!d)return;let T=0;if(d.gain.setValueAtTime){d.gain.setValueAtTime(T,A?.currentTime??0);return}d.gain.value=T},V=()=>{if(a===null||e.size>0)return;_(Date.now()-a)},R=async()=>{if(t)return t;if(c.createAudioContext)t=c.createAudioContext();else{let A=ic();if(!A)throw Error("Assistant audio playback requires AudioContext support.");t=new A}if(t.createGain)d=t.createGain(),d.connect?.(t.destination);return g=t.currentTime,t},Y=async(A)=>{let T=await R(),P=lc(T,A),L=T.createBufferSource();L.buffer=P,L.connect(d??T.destination),L.onended=()=>{e.delete(L),L.disconnect?.(),S({activeSourceCount:e.size,isPlaying:e.size>0&&l.isActive}),V()};let Q=Math.max(T.currentTime+i,g);g=Q+P.duration,e.add(L),S({activeSourceCount:e.size,isPlaying:!0}),L.start(Q)},f=(A)=>{for(let T of[...e])T.stop?.();if(g=t?t.currentTime:0,A?.forceClear){for(let T of e)T.disconnect?.();e.clear(),V()}},$=async()=>{if(!l.isActive)return;let A=n.assistantAudio.slice(l.processedChunkCount);if(A.length===0)return;try{w();for(let T of A)await Y(T);S({processedChunkCount:n.assistantAudio.length,queuedChunkCount:l.queuedChunkCount+A.length})}catch(T){S({error:T instanceof Error?T.message:String(T)})}},D=()=>{return y=y.then(()=>$(),()=>$()),y},q=n.subscribe(()=>{if(c.autoStart&&!l.isActive&&n.assistantAudio.length>0){H.start();return}if(l.isActive)D()}),H={close:async()=>{if(q(),f({forceClear:!0}),E(),r?.(),r=null,h=null,a=null,t&&t.state!=="closed")await t.close();t=null,d?.disconnect?.(),d=null,g=0,S({activeSourceCount:0,isActive:!1,isPlaying:!1})},get activeSourceCount(){return l.activeSourceCount},get error(){return l.error},getSnapshot:()=>l,get isActive(){return l.isActive},get isPlaying(){return l.isPlaying},interrupt:async()=>{let A=Date.now(),T=await R();a=A,s(T);let P=Date.now()-A+U(T);if(S({isActive:!1,isPlaying:e.size>0,lastPlaybackStopLatencyMs:P}),e.size===0){_(P);return}if(!h)h=new Promise((L)=>{r=L});E(),M=setTimeout(()=>{for(let L of e)L.disconnect?.();e.clear(),_(Date.now()-A)},250),f(),await h},get lastInterruptLatencyMs(){return l.lastInterruptLatencyMs},get lastPlaybackStopLatencyMs(){return l.lastPlaybackStopLatencyMs},pause:async()=>{if(!t){S({activeSourceCount:0,isActive:!1,isPlaying:!1});return}await t.suspend(),S({activeSourceCount:e.size,isActive:!1,isPlaying:!1})},get processedChunkCount(){return l.processedChunkCount},get queuedChunkCount(){return l.queuedChunkCount},start:async()=>{try{w();let A=await R();if(N(A),A.state==="suspended")await A.resume();S({activeSourceCount:e.size,isActive:!0,isPlaying:A.state==="running"}),await D()}catch(A){throw S({error:A instanceof Error?A.message:String(A),isActive:!1,isPlaying:!1}),A}},subscribe:(A)=>{return o.add(A),()=>{o.delete(A)}}};return H};var sc=()=>`barge-in:${Date.now()}:${crypto.randomUUID?.()??Math.random().toString(36).slice(2)}`,dc=(n,c)=>{let o=n.filter((t)=>t.status==="stopped"),e=o.map((t)=>t.latencyMs).filter((t)=>typeof t==="number"),i=o.filter((t)=>typeof t.latencyMs==="number"&&t.latencyMs>c).length,l=o.length-i;return{averageLatencyMs:e.length>0?Math.round(e.reduce((t,d)=>t+d,0)/e.length):void 0,events:[...n],failed:i,lastEvent:n.at(-1),passed:l,status:n.length===0?"empty":i>0?"fail":o.length===0?"warn":"pass",thresholdMs:c,total:o.length}},En=(n={})=>{let c=new Set,o=n.thresholdMs??250,e=n.fetch??globalThis.fetch,i=[],l=()=>{for(let g of c)g()},t=(g)=>{if(!n.path||typeof e!=="function")return;e(n.path,{body:JSON.stringify(g),headers:{"Content-Type":"application/json"},method:"POST"}).catch(()=>{})},d=(g,y)=>{let a={at:Date.now(),id:sc(),latencyMs:y.latencyMs,playbackStopLatencyMs:y.playbackStopLatencyMs,reason:y.reason,sessionId:y.sessionId,status:g,thresholdMs:o};return i.push(a),t(a),l(),a};return{getSnapshot:()=>dc(i,o),recordRequested:(g)=>d("requested",g),recordSkipped:(g)=>d("skipped",g),recordStopped:(g)=>d("stopped",g),subscribe:(g)=>{return c.add(g),()=>{c.delete(g)}}}};var rc=0.08,ac=(n,c={})=>(c.enabled??!0)&&n>=(c.interruptThreshold??rc),en=(n,c,o={})=>{let e=n.partial,i=(t)=>{if(!c.isPlaying||o.enabled===!1){o.monitor?.recordSkipped({reason:t,sessionId:n.sessionId});return}o.monitor?.recordRequested({reason:t,sessionId:n.sessionId}),c.interrupt().then(()=>{o.monitor?.recordStopped({latencyMs:c.lastInterruptLatencyMs,playbackStopLatencyMs:c.lastPlaybackStopLatencyMs,reason:t,sessionId:n.sessionId})})},l=n.subscribe(()=>{if(o.interruptOnPartial===!1){e=n.partial;return}if(!e&&n.partial)i("partial-transcript");e=n.partial});return{close:()=>{l()},handleLevel:(t)=>{if(ac(t,o))i("input-level")},sendAudio:(t)=>{i("manual-audio"),n.sendAudio(t)}}};var dn=48,gc=320,Ac=88,hc="Guided test",yc="General recording",Cc="Pick a scenario to begin the demo.",Tc="I can walk you through a short guided voice test.",Ic="I can capture one freeform recording and confirm that it landed.",Vc="Choose a scenario to begin. Guided test asks follow-up prompts. General recording just captures what you say.",Mc="Click Start general recording to capture one freeform answer.",Pn="Speak freely. When you pause, the recording will be captured.",ln="Recording saved. Start again if you want another capture.",Dn="Guided test complete. Review the saved summary below.",On="All prompts are covered. You can stop the microphone or keep speaking for extra detail.",Sc="Ready. Start guided test or general recording to begin.",Lc="Live. Answer the prompt, then click Stop microphone when finished.",bn=["Start with a quick introduction about who you are.","Now describe what you are trying to do or test.","Finish with any detail that feels blocked, risky, or unclear."],xn=(n,c,o)=>Math.min(o,Math.max(c,n)),u=(n)=>n.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll(\'"\',"&quot;").replaceAll("\'","&#39;"),on=(n,c)=>{let o=n[c];if(typeof o==="string"&&o.trim())return o;return null},sn=(n)=>{if(typeof n==="string"&&n.trim())return n;if(n instanceof Error&&n.message.trim())return n.message;if(n&&typeof n==="object"){let c=n,o=on(c,"message")??on(c,"reason")??on(c,"description");if(o)return o;if("error"in c)return sn(c.error);if("cause"in c)return sn(c.cause);try{return JSON.stringify(n)}catch{}}return"Unexpected error"},wc=(n)=>{let c=[n.status];if(n.attempts>0||n.maxAttempts>0)c.push(`${n.attempts}/${n.maxAttempts} attempts`);if(n.nextAttemptAt){let o=Math.max(0,n.nextAttemptAt-Date.now());c.push(`retry in ${Math.ceil(o/100)/10}s`)}return c.join(" \xB7 ")},v=(n=dn)=>Array.from({length:n},()=>0),fn=(n,c,o=dn)=>{let e=n.slice(-(o-1));e.push(xn(c,0,1));while(e.length<o)e.unshift(0);return e},Rc=(n,c=gc,o=Ac)=>{let e=n.length>1?n:v(dn),i=c/(e.length-1),l=o/2,t=o*0.34;if(Math.max(...e,0)<=0.015)return`M 0 ${l} L ${c} ${l}`;let g=e.map((a,h)=>{let r=h*0.76,M=Math.sin(r)*0.78+Math.sin(r*0.41)*0.22,C=a*t,S=i*h,w=xn(l+M*C,8,o-8);return{x:S,y:w}});if(g.length===0)return`M 0 ${l} L ${c} ${l}`;let y=`M ${g[0]?.x??0} ${g[0]?.y??l}`;for(let a=1;a<g.length;a+=1){let h=g[a-1],r=g[a];if(!h||!r)continue;let M=(h.x+r.x)/2;y+=` Q ${M} ${h.y} ${r.x} ${r.y}`}return y},_c=(n)=>{if(!n)return bn;try{let c=JSON.parse(n);if(Array.isArray(c)){let o=c.filter((e)=>typeof e==="string").map((e)=>e.trim()).filter(Boolean);if(o.length>0)return o}}catch{}return bn},tn=(n)=>{if(!n)return;let c=Number(n);return Number.isFinite(c)?c:void 0},Ec=(n,c,o)=>{if(!c)return null;let e=document.querySelector(c);return e instanceof o?e:null},x=(n,c,o,e)=>{let i=c?document.querySelector(c):null;if(i instanceof o)return i;let l=n.querySelector(`#${e}`);if(l instanceof o)return l;throw Error(`Voice HTMX bootstrap could not find the required element "${e}".`)},bc=(n)=>{if(!n.mode)return Cc;if(!n.hasStarted)return n.mode==="guided"?Tc:Ic;if(n.status==="completed")return n.mode==="guided"?Dn:ln;if(n.mode==="general")return Pn;return n.guidedPrompts[n.turnCount]??On},fc=(n)=>{if(!n.mode)return Vc;if(n.status==="completed")return n.mode==="guided"?Dn:ln;if(!n.hasStarted)return n.mode==="guided"?`Click Start guided test to begin. First prompt: ${n.guidedPrompts[0]??"Answer the first prompt."}`:Mc;if(n.mode==="general")return n.turnCount===0?Pn:ln;return n.guidedPrompts[n.turnCount]??On},Pc=(n)=>{let c=n.dataset.voiceGuidedPath,o=n.dataset.voiceGeneralPath;if(!c||!o)throw Error("Voice HTMX bootstrap requires data-voice-guided-path and data-voice-general-path.");let e=_c(n.dataset.voiceGuidedPrompts),i=n.dataset.voiceGuidedLabel??hc,l=n.dataset.voiceGeneralLabel??yc,t=n.dataset.voiceReconnectReportPath,d=n.dataset.voiceBargeInPath,g=d?En({path:d,thresholdMs:tn(n.dataset.voiceBargeInThresholdMs)}):null,y=tn(n.dataset.voiceBargeInRecentWindowMs)??4000,a=tn(n.dataset.voiceBargeInSpeechThreshold)??0.04,h=x(document,n.dataset.voiceSync,HTMLElement,"voice-htmx-sync"),r=x(n,n.dataset.voiceConnection,HTMLElement,"metric-connection"),M=x(n,n.dataset.voiceError,HTMLElement,"status-error"),C=x(n,n.dataset.voiceMicrophone,HTMLElement,"status-mic"),S=x(n,n.dataset.voicePrompt,HTMLElement,"status-prompt"),w=Ec(n,n.dataset.voiceReconnect,HTMLElement),E=x(n,n.dataset.voiceChat,HTMLElement,"chat-list"),_=x(n,n.dataset.voiceStartGuided,HTMLButtonElement,"start-guided"),U=x(n,n.dataset.voiceStartGeneral,HTMLButtonElement,"start-general"),N=x(n,n.dataset.voiceStop,HTMLButtonElement,"stop-mic"),s=x(n,n.dataset.voiceMonitor,HTMLElement,"voice-monitor"),V=x(n,n.dataset.voiceMonitorCopy,HTMLElement,"voice-monitor-copy"),R=x(n,n.dataset.voiceWaveGlow,SVGPathElement,"voice-wave-glow"),Y=x(n,n.dataset.voiceWavePath,SVGPathElement,"voice-wave-path"),f=null,$={general:!1,guided:!1},D=!1,q=null,H=v(),A=null,T=null,P=j(c,{capture:{onAudio:(I,B)=>{if(A){A.sendAudio(I);return}B(I)},onLevel:(I)=>{A?.handleLevel(I),H=fn(H,I),m()}},connection:{reconnectReportPath:t},preset:"guided-intake"}),L=j(o,{capture:{onAudio:(I,B)=>{if(T){T.sendAudio(I);return}B(I)},onLevel:(I)=>{T?.handleLevel(I),H=fn(H,I),m()}},connection:{reconnectReportPath:t},preset:"dictation"}),Q=P.bindHTMX({element:h}),Un=L.bindHTMX({element:h}),k=F(P),z=F(L);A=en(P,k,{interruptThreshold:a,monitor:g??void 0}),T=en(L,z,{interruptThreshold:a,monitor:g??void 0});let J=()=>f==="general"?L:P,Oc=()=>f==="general"?z:k,m=()=>{let I=Rc(H);R.setAttribute("d",I),Y.setAttribute("d",I),V.innerHTML=`<span class="voice-live-dot"></span>${D?"Microphone live":"Microphone idle"}`,V.classList.toggle("is-live",D),s.classList.toggle("is-live",D)},X=()=>{let I=J(),B=(f?$[f]:!1)||I.turns.length>0,an=I.status;if(r.textContent=I.isConnected?"Connected":"Waiting",M.textContent=q||I.error||"None",w)w.textContent=wc(I.reconnect);C.textContent=D?Lc:Sc,S.textContent=fc({guidedPrompts:e,hasStarted:B,mode:f,status:an,turnCount:I.turns.length}),_.hidden=D,U.hidden=D,N.hidden=!D,E.innerHTML=`<article class="voice-chat-message assistant">\n <div class="voice-chat-role">${u(f==="general"?l:f==="guided"?i:"Voice demo")}</div>\n <p class="voice-turn-text">${u(bc({generalLabel:l,guidedLabel:i,guidedPrompts:e,hasStarted:B,mode:f,status:an,turnCount:I.turns.length}))}</p>\n</article>${I.turns.map((p)=>`<div class="voice-chat-stack">\n <article class="voice-chat-message user">\n <div class="voice-chat-role">You</div>\n <p class="voice-turn-text">${u(p.text)}</p>\n </article>\n ${p.assistantText?`<article class="voice-chat-message assistant">\n <div class="voice-chat-role">${u(f==="general"?l:f==="guided"?i:"Guide")}</div>\n <p class="voice-turn-text">${u(p.assistantText)}</p>\n </article>`:""}\n</div>`).join("")}${I.partial?`<article class="voice-chat-message user pending">\n <div class="voice-chat-role">Speaking</div>\n <p class="voice-turn-text">${u(I.partial)}</p>\n</article>`:""}`,m()},Nn=()=>{J().stopRecording(),D=!1,q=null,H=v(),X()},rn=async(I)=>{f=I,$={...$,[I]:!0};try{await J().startRecording(),q=null,D=!0,X()}catch(B){J().stopRecording(),D=!1,H=v(),q=sn(B),X()}};P.subscribe(()=>{if(P.assistantAudio.length>0)k.start().catch(()=>{});X()}),L.subscribe(()=>{if(L.assistantAudio.length>0)z.start().catch(()=>{});X()}),_.addEventListener("click",()=>{rn("guided")}),U.addEventListener("click",()=>{rn("general")}),N.addEventListener("click",()=>{Nn()}),n.addEventListener("absolute-voice-simulate-disconnect",()=>{J().simulateDisconnect()}),window.addEventListener("beforeunload",()=>{P.stopRecording(),L.stopRecording(),A?.close(),T?.close(),k.close(),z.close(),Q(),Un(),P.close(),L.close()}),X()},Dc=()=>{if(typeof window>"u"||typeof document>"u")return;let n=Array.from(document.querySelectorAll("[data-voice-htmx]"));for(let c of n)if(c instanceof HTMLElement)Pc(c)};Dc();export{Dc as initVoiceHTMX};\n';
5728
5745
 
5729
5746
  // src/profileSwitchRecommendation.ts
5730
5747
  import { Elysia } from "elysia";
@@ -6678,6 +6695,8 @@ var resolveSessionOptions = (config) => {
6678
6695
  const preset = resolveVoiceRuntimePreset(config.preset);
6679
6696
  return {
6680
6697
  audioConditioning: config.audioConditioning !== undefined ? resolveAudioConditioningConfig(config.audioConditioning) : preset.audioConditioning,
6698
+ noiseSuppressor: config.noiseSuppressor,
6699
+ noiseSuppressorFormat: config.noiseSuppressorFormat,
6681
6700
  costTelemetry: config.costTelemetry,
6682
6701
  sttFallback: resolveSTTFallbackConfig(config.sttFallback),
6683
6702
  logger: config.logger,
@@ -6888,6 +6907,8 @@ var voice = (config) => {
6888
6907
  });
6889
6908
  return createVoiceSession({
6890
6909
  audioConditioning: sessionOptions.audioConditioning,
6910
+ noiseSuppressor: sessionOptions.noiseSuppressor,
6911
+ noiseSuppressorFormat: sessionOptions.noiseSuppressorFormat,
6891
6912
  context,
6892
6913
  id: sessionId,
6893
6914
  handoff: config.handoff,
@@ -7453,7 +7453,19 @@ var createVoiceSession = (options) => {
7453
7453
  return;
7454
7454
  }
7455
7455
  const adapter = await ensureAdapter();
7456
- const conditionedAudio = conditionAudioChunk(audio, options.audioConditioning);
7456
+ let inboundAudio = audio;
7457
+ if (options.noiseSuppressor) {
7458
+ try {
7459
+ const suppressed = await options.noiseSuppressor.process({
7460
+ format: options.noiseSuppressorFormat ?? DEFAULT_FORMAT,
7461
+ pcm: audio
7462
+ });
7463
+ inboundAudio = suppressed.bytes;
7464
+ } catch (error) {
7465
+ options.logger?.warn?.(`noise suppression failed, passing raw audio through: ${error instanceof Error ? error.message : String(error)}`);
7466
+ }
7467
+ }
7468
+ const conditionedAudio = conditionAudioChunk(inboundAudio, options.audioConditioning);
7457
7469
  const audioLevel = measureAudioLevel(conditionedAudio);
7458
7470
  const shouldStoreAudio = speechDetected || audioLevel >= turnDetection.speechThreshold;
7459
7471
  await writeSession((currentSession) => {
@@ -7512,6 +7524,11 @@ var createVoiceSession = (options) => {
7512
7524
  clearSilenceTimer();
7513
7525
  clearCallSilenceWatchdog();
7514
7526
  clearAmdEvaluationTimer();
7527
+ if (options.noiseSuppressor?.close) {
7528
+ try {
7529
+ await options.noiseSuppressor.close();
7530
+ } catch {}
7531
+ }
7515
7532
  await closeTTSSession(reason);
7516
7533
  await closeAdapter(reason);
7517
7534
  await persistRecordings();
package/dist/types.d.ts CHANGED
@@ -15,6 +15,26 @@ export type AudioFormat = {
15
15
  channels: 1 | 2;
16
16
  };
17
17
  export type AudioChunk = ArrayBuffer | ArrayBufferView;
18
+ /**
19
+ * Structural contract for an inbound noise suppressor applied before STT.
20
+ * Matches `@absolutejs/media`'s `NoiseSuppressor`, so any media suppressor
21
+ * (`createFFmpegNoiseSuppressor`, `createEnergyGateNoiseSuppressor`, a Krisp
22
+ * adapter, etc.) satisfies it without a hard import.
23
+ */
24
+ export type VoiceNoiseSuppressor = {
25
+ readonly kind: string;
26
+ process: (input: {
27
+ format: AudioFormat;
28
+ pcm: ArrayBuffer | ArrayBufferView;
29
+ }) => Promise<{
30
+ bytes: Uint8Array;
31
+ format: AudioFormat;
32
+ }> | {
33
+ bytes: Uint8Array;
34
+ format: AudioFormat;
35
+ };
36
+ close?: () => Promise<void> | void;
37
+ };
18
38
  export type VoiceLanguageStrategy = {
19
39
  mode: "auto-detect";
20
40
  allowedLanguages?: string[];
@@ -712,6 +732,8 @@ export type VoicePluginConfig<TContext = unknown, TSession extends VoiceSessionR
712
732
  reconnect?: VoiceReconnectConfig;
713
733
  turnDetection?: VoiceTurnDetectionConfig;
714
734
  audioConditioning?: VoiceAudioConditioningConfig;
735
+ noiseSuppressor?: VoiceNoiseSuppressor;
736
+ noiseSuppressorFormat?: AudioFormat;
715
737
  logger?: VoiceLogger;
716
738
  htmx?: boolean | VoiceHTMXConfig<TSession, NoInfer<TResult>>;
717
739
  handoff?: VoiceHandoffConfig<TContext, TSession, TResult>;
@@ -760,6 +782,8 @@ export type CreateVoiceSessionOptions<TContext = unknown, TSession extends Voice
760
782
  sttLifecycle: VoiceSTTLifecycle;
761
783
  turnDetection: VoiceResolvedTurnDetectionConfig;
762
784
  audioConditioning?: VoiceResolvedAudioConditioningConfig;
785
+ noiseSuppressor?: VoiceNoiseSuppressor;
786
+ noiseSuppressorFormat?: AudioFormat;
763
787
  handoff?: VoiceHandoffConfig<TContext, TSession, TResult>;
764
788
  liveOps?: VoiceLiveOpsRuntimeConfig;
765
789
  route: VoiceNormalizedRouteConfig<TContext, TSession, TResult>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.516",
3
+ "version": "0.0.22-beta.517",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",
@@ -259,7 +259,7 @@
259
259
  }
260
260
  },
261
261
  "dependencies": {
262
- "@absolutejs/media": "0.0.1-beta.16"
262
+ "@absolutejs/media": "0.0.1-beta.19"
263
263
  },
264
264
  "devDependencies": {
265
265
  "@absolutejs/absolute": "0.19.0-beta.646",