@absolutejs/voice 0.0.22-beta.464 → 0.0.22-beta.465

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5186,7 +5186,7 @@ var createVoiceSession = (options) => {
5186
5186
  };
5187
5187
 
5188
5188
  // src/generated/htmxBootstrapBundle.ts
5189
- var HTMX_BOOTSTRAP_BUNDLE = 'var We=(e)=>{if(typeof e!=="string")return e;return document.querySelector(e)},$e=(e,n,c,o)=>{let s=n??e.getAttribute("hx-get")??"";if(!s)return"";let t=new URL(s,window.location.origin);if(o)t.searchParams.set(c,o);else t.searchParams.delete(c);return`${t.pathname}${t.search}${t.hash}`},fe=(e,n)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let c=We(n.element);if(!c)return()=>{};let o=n.eventName??"voice-refresh",s=n.sessionQueryParam??"sessionId",t=()=>{let r=window,d=$e(c,n.route,s,e.sessionId);if(d)c.setAttribute("hx-get",d);r.htmx?.process?.(c),r.htmx?.trigger?.(c,o)},i=e.subscribe(t);return t(),()=>{i()}};var ke=(e)=>Math.max(-1,Math.min(1,e)),qe=(e)=>{let n=new Int16Array(e.length);for(let c=0;c<e.length;c+=1){let o=ke(e[c]??0);n[c]=o<0?o*32768:o*32767}return new Uint8Array(n.buffer)},Xe=(e)=>{let n=e instanceof Uint8Array?e:new Uint8Array(e);if(n.byteLength<2)return 0;let c=new Int16Array(n.buffer,n.byteOffset,Math.floor(n.byteLength/2));if(c.length===0)return 0;let o=0;for(let s of c){let t=s/32768;o+=t*t}return Math.min(1,Math.max(0,Math.sqrt(o/c.length)*5.5))},ze=(e,n,c)=>{if(n===c)return e;let o=n/c,s=Math.round(e.length/o),t=new Float32Array(s),i=0,r=0;while(i<t.length){let d=Math.round((i+1)*o),f=0,a=0;for(let A=r;A<d&&A<e.length;A+=1)f+=e[A]??0,a+=1;t[i]=a>0?f/a:0,i+=1,r=d}return t},Ae=(e)=>{let n=null,c=null,o=null,s=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.");s=await navigator.mediaDevices.getUserMedia({audio:{channelCount:e.channelCount??1}}),n=new r,c=n.createMediaStreamSource(s),o=n.createScriptProcessor(4096,1,1),o.onaudioprocess=(d)=>{let f=d.inputBuffer.getChannelData(0),a=ze(f,n?.sampleRate??48000,e.sampleRateHz??16000),A=qe(a);e.onLevel?.(Xe(A)),e.onAudio(A)},c.connect(o),o.connect(n.destination)},stop:()=>{o?.disconnect(),c?.disconnect(),s?.getTracks().forEach((r)=>r.stop()),n?.close(),e.onLevel?.(0),n=null,s=null,o=null,c=null}}};var ne=(e)=>{if(typeof e==="string"&&e.trim())return e;if(e instanceof Error&&e.message.trim())return e.message;if(e&&typeof e==="object"){let n=e;for(let c of["message","reason","description"]){let o=n[c];if(typeof o==="string"&&o.trim())return o}if("error"in n)return ne(n.error);if("cause"in n)return ne(n.cause);try{return JSON.stringify(e)}catch{}}return"Unexpected error"},he=(e)=>{switch(e.type){case"audio":return{chunk:Uint8Array.from(atob(e.chunkBase64),(n)=>n.charCodeAt(0)),format:e.format,receivedAt:e.receivedAt,turnId:e.turnId,type:"audio"};case"assistant":return{text:e.text,type:"assistant"};case"complete":return{sessionId:e.sessionId,type:"complete"};case"connection":return{reconnect:e.reconnect,type:"connection"};case"call_lifecycle":return{event:e.event,sessionId:e.sessionId,type:"call_lifecycle"};case"error":return{message:ne(e.message),type:"error"};case"final":return{transcript:e.transcript,type:"final"};case"partial":return{transcript:e.transcript,type:"partial"};case"replay":return{assistantTexts:e.assistantTexts,call:e.call,partial:e.partial,scenarioId:e.scenarioId,sessionId:e.sessionId,sessionMetadata:e.sessionMetadata,status:e.status,turns:e.turns,type:"replay"};case"session":return{sessionId:e.sessionId,sessionMetadata:e.sessionMetadata,scenarioId:e.scenarioId,status:e.status,type:"session"};case"turn":return{turn:e.turn,type:"turn"};default:return null}};var{mkdir:pn,writeFile:ec}=(()=>({}));function H(e){if(typeof e!=="string")throw TypeError("Path must be a string. Received "+JSON.stringify(e))}function Ce(e,n){var c="",o=0,s=-1,t=0,i;for(var r=0;r<=e.length;++r){if(r<e.length)i=e.charCodeAt(r);else if(i===47)break;else i=47;if(i===47){if(s===r-1||t===1);else if(s!==r-1&&t===2){if(c.length<2||o!==2||c.charCodeAt(c.length-1)!==46||c.charCodeAt(c.length-2)!==46){if(c.length>2){var d=c.lastIndexOf("/");if(d!==c.length-1){if(d===-1)c="",o=0;else c=c.slice(0,d),o=c.length-1-c.lastIndexOf("/");s=r,t=0;continue}}else if(c.length===2||c.length===1){c="",o=0,s=r,t=0;continue}}if(n){if(c.length>0)c+="/..";else c="..";o=2}}else{if(c.length>0)c+="/"+e.slice(s+1,r);else c=e.slice(s+1,r);o=r-s-1}s=r,t=0}else if(i===46&&t!==-1)++t;else t=-1}return c}function Ye(e,n){var c=n.dir||n.root,o=n.base||(n.name||"")+(n.ext||"");if(!c)return o;if(c===n.root)return c+o;return c+e+o}function ce(){var e="",n=!1,c;for(var o=arguments.length-1;o>=-1&&!n;o--){var s;if(o>=0)s=arguments[o];else{if(c===void 0)c=process.cwd();s=c}if(H(s),s.length===0)continue;e=s+"/"+e,n=s.charCodeAt(0)===47}if(e=Ce(e,!n),n)if(e.length>0)return"/"+e;else return"/";else if(e.length>0)return e;else return"."}function ye(e){if(H(e),e.length===0)return".";var n=e.charCodeAt(0)===47,c=e.charCodeAt(e.length-1)===47;if(e=Ce(e,!n),e.length===0&&!n)e=".";if(e.length>0&&c)e+="/";if(n)return"/"+e;return e}function Je(e){return H(e),e.length>0&&e.charCodeAt(0)===47}function Te(){if(arguments.length===0)return".";var e;for(var n=0;n<arguments.length;++n){var c=arguments[n];if(H(c),c.length>0)if(e===void 0)e=c;else e+="/"+c}if(e===void 0)return".";return ye(e)}function Qe(e,n){if(H(e),H(n),e===n)return"";if(e=ce(e),n=ce(n),e===n)return"";var c=1;for(;c<e.length;++c)if(e.charCodeAt(c)!==47)break;var o=e.length,s=o-c,t=1;for(;t<n.length;++t)if(n.charCodeAt(t)!==47)break;var i=n.length,r=i-t,d=s<r?s:r,f=-1,a=0;for(;a<=d;++a){if(a===d){if(r>d){if(n.charCodeAt(t+a)===47)return n.slice(t+a+1);else if(a===0)return n.slice(t+a)}else if(s>d){if(e.charCodeAt(c+a)===47)f=a;else if(a===0)f=0}break}var A=e.charCodeAt(c+a),g=n.charCodeAt(t+a);if(A!==g)break;else if(A===47)f=a}var y="";for(a=c+f+1;a<=o;++a)if(a===o||e.charCodeAt(a)===47)if(y.length===0)y+="..";else y+="/..";if(y.length>0)return y+n.slice(t+f);else{if(t+=f,n.charCodeAt(t)===47)++t;return n.slice(t)}}function Ze(e){return e}function Ke(e){if(H(e),e.length===0)return".";var n=e.charCodeAt(0),c=n===47,o=-1,s=!0;for(var t=e.length-1;t>=1;--t)if(n=e.charCodeAt(t),n===47){if(!s){o=t;break}}else s=!1;if(o===-1)return c?"/":".";if(c&&o===1)return"//";return e.slice(0,o)}function me(e,n){if(n!==void 0&&typeof n!=="string")throw TypeError(\'"ext" argument must be a string\');H(e);var c=0,o=-1,s=!0,t;if(n!==void 0&&n.length>0&&n.length<=e.length){if(n.length===e.length&&n===e)return"";var i=n.length-1,r=-1;for(t=e.length-1;t>=0;--t){var d=e.charCodeAt(t);if(d===47){if(!s){c=t+1;break}}else{if(r===-1)s=!1,r=t+1;if(i>=0)if(d===n.charCodeAt(i)){if(--i===-1)o=t}else i=-1,o=r}}if(c===o)o=r;else if(o===-1)o=e.length;return e.slice(c,o)}else{for(t=e.length-1;t>=0;--t)if(e.charCodeAt(t)===47){if(!s){c=t+1;break}}else if(o===-1)s=!1,o=t+1;if(o===-1)return"";return e.slice(c,o)}}function je(e){H(e);var n=-1,c=0,o=-1,s=!0,t=0;for(var i=e.length-1;i>=0;--i){var r=e.charCodeAt(i);if(r===47){if(!s){c=i+1;break}continue}if(o===-1)s=!1,o=i+1;if(r===46){if(n===-1)n=i;else if(t!==1)t=1}else if(n!==-1)t=-1}if(n===-1||o===-1||t===0||t===1&&n===o-1&&n===c+1)return"";return e.slice(n,o)}function Fe(e){if(e===null||typeof e!=="object")throw TypeError(\'The "pathObject" argument must be of type Object. Received type \'+typeof e);return Ye("/",e)}function ve(e){H(e);var n={root:"",dir:"",base:"",ext:"",name:""};if(e.length===0)return n;var c=e.charCodeAt(0),o=c===47,s;if(o)n.root="/",s=1;else s=0;var t=-1,i=0,r=-1,d=!0,f=e.length-1,a=0;for(;f>=s;--f){if(c=e.charCodeAt(f),c===47){if(!d){i=f+1;break}continue}if(r===-1)d=!1,r=f+1;if(c===46){if(t===-1)t=f;else if(a!==1)a=1}else if(t!==-1)a=-1}if(t===-1||r===-1||a===0||a===1&&t===r-1&&t===i+1){if(r!==-1)if(i===0&&o)n.base=n.name=e.slice(1,r);else n.base=n.name=e.slice(i,r)}else{if(i===0&&o)n.name=e.slice(1,t),n.base=e.slice(1,r);else n.name=e.slice(i,t),n.base=e.slice(i,r);n.ext=e.slice(t,r)}if(i>0)n.dir=e.slice(0,i-1);else if(o)n.dir="/";return n}var pe="/",en=":",jn=((e)=>(e.posix=e,e))({resolve:ce,normalize:ye,isAbsolute:Je,join:Te,relative:Qe,_makeLong:Ze,dirname:Ke,basename:me,extname:je,format:Fe,parse:ve,sep:pe,delimiter:en,win32:null,posix:null});var G=(e,n,c,o)=>{e.push({code:c,message:o,severity:n})};var nn=(e)=>e.length===0?void 0:e.reduce((n,c)=>n+c,0)/e.length,m=(e)=>e.length===0?void 0:Math.max(...e);var u=(e,n)=>{let c=e[n];return typeof c==="number"&&Number.isFinite(c)?c:void 0},K=(e,n)=>{let c=e[n];return typeof c==="boolean"?c:void 0},D=(e,n)=>{let c=e[n];return typeof c==="string"?c:void 0},oe=(e)=>String(e.id??D(e,"ssrc")??u(e,"ssrc")??D(e,"trackIdentifier")??D(e,"mid")??"unknown"),Se=(e)=>e===void 0?void 0:e*1000;var cn=(e)=>{let n={};for(let[c,o]of Object.entries(e))if(o===null||typeof o==="boolean"||typeof o==="number"||typeof o==="string")n[c]=o;return n};var Me=(e={})=>{let n=e.stats??[],c=[],o=n.filter((l)=>l.type==="inbound-rtp"&&D(l,"kind")!=="video"),s=n.filter((l)=>l.type==="outbound-rtp"&&D(l,"kind")!=="video"),t=n.filter((l)=>l.type==="candidate-pair"),i=n.filter((l)=>(l.type==="track"||l.type==="media-source")&&D(l,"kind")==="audio"),r=t.filter((l)=>K(l,"selected")===!0||K(l,"nominated")===!0||D(l,"state")==="succeeded").length,d=i.filter((l)=>D(l,"readyState")!=="ended"&&D(l,"trackState")!=="ended"&&K(l,"ended")!==!0).length,f=i.filter((l)=>D(l,"readyState")==="ended"||D(l,"trackState")==="ended"||K(l,"ended")===!0).length,a=o.reduce((l,M)=>l+(u(M,"packetsReceived")??0),0),A=s.reduce((l,M)=>l+(u(M,"packetsSent")??0),0),g=[...o,...s].reduce((l,M)=>l+Math.max(0,u(M,"packetsLost")??0),0),y=a+g,C=y===0?0:g/y,I=o.reduce((l,M)=>l+(u(M,"bytesReceived")??0),0),L=s.reduce((l,M)=>l+(u(M,"bytesSent")??0),0),R=m(t.map((l)=>Se(u(l,"currentRoundTripTime")??u(l,"roundTripTime"))).filter((l)=>l!==void 0)),w=m([...o,...s].map((l)=>Se(u(l,"jitter"))).filter((l)=>l!==void 0)),x=m(o.map((l)=>{let M=u(l,"jitterBufferDelay"),b=u(l,"jitterBufferEmittedCount");return M!==void 0&&b!==void 0&&b>0?M/b*1000:void 0}).filter((l)=>l!==void 0)),U=i.map((l)=>u(l,"audioLevel")).filter((l)=>l!==void 0);if(e.requireConnectedCandidatePair&&t.length>0&&r===0)G(c,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(e.requireLiveAudioTrack&&d===0)G(c,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(e.maxPacketLossRatio!==void 0&&C>e.maxPacketLossRatio)G(c,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(C)} above ${String(e.maxPacketLossRatio)}.`);if(e.maxRoundTripTimeMs!==void 0&&R!==void 0&&R>e.maxRoundTripTimeMs)G(c,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(R)}ms above ${String(e.maxRoundTripTimeMs)}ms.`);if(e.maxJitterMs!==void 0&&w!==void 0&&w>e.maxJitterMs)G(c,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(w)}ms above ${String(e.maxJitterMs)}ms.`);return{activeCandidatePairs:r,audioLevelAverage:nn(U),bytesReceived:I,bytesSent:L,checkedAt:Date.now(),endedAudioTracks:f,inboundPackets:a,issues:c,jitterBufferDelayMs:x,jitterMs:w,liveAudioTracks:d,outboundPackets:A,packetLossRatio:C,packetsLost:g,roundTripTimeMs:R,status:c.some((l)=>l.severity==="error")?"fail":c.length>0?"warn":"pass",totalStats:n.length}},Ie=async(e)=>{return[...(await e.peerConnection.getStats(e.selector??null)).values()].map(cn)};var Ve=(e={})=>{let n=e.stats??[],c=e.previousStats??[],o=[],s=new Map(c.map((g)=>[oe(g),g])),i=n.filter((g)=>(g.type==="inbound-rtp"||g.type==="outbound-rtp")&&D(g,"kind")!=="video"&&D(g,"mediaType")!=="video").map((g)=>{let y=g.type==="outbound-rtp"?"outbound":"inbound",C=y==="outbound"?"packetsSent":"packetsReceived",I=y==="outbound"?"bytesSent":"bytesReceived",L=s.get(oe(g)),R=u(g,C),w=L?u(L,C):void 0,x=u(g,I),U=L?u(L,I):void 0,l=g.timestamp!==void 0&&L?.timestamp!==void 0?g.timestamp-L.timestamp:void 0;return{bytesDelta:x!==void 0&&U!==void 0?x-U:void 0,currentPackets:R,direction:y,id:oe(g),packetDelta:R!==void 0&&w!==void 0?R-w:void 0,previousPackets:w,timeDeltaMs:l}}),r=i.filter((g)=>g.direction==="inbound"),d=i.filter((g)=>g.direction==="outbound"),f=m(i.map((g)=>g.timeDeltaMs).filter((g)=>g!==void 0)),a=r.filter((g)=>e.maxInboundPacketStallMs!==void 0&&g.timeDeltaMs!==void 0&&g.timeDeltaMs>=e.maxInboundPacketStallMs&&g.packetDelta!==void 0&&g.packetDelta<=0).length,A=d.filter((g)=>e.maxOutboundPacketStallMs!==void 0&&g.timeDeltaMs!==void 0&&g.timeDeltaMs>=e.maxOutboundPacketStallMs&&g.packetDelta!==void 0&&g.packetDelta<=0).length;if(e.requireInboundAudio&&r.length===0)G(o,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(e.requireOutboundAudio&&d.length===0)G(o,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(e.maxGapMs!==void 0&&f!==void 0&&f>e.maxGapMs)G(o,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(f)}ms above ${String(e.maxGapMs)}ms.`);if(a>0)G(o,"error","media.webrtc_inbound_stalled",`${String(a)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(A>0)G(o,"error","media.webrtc_outbound_stalled",`${String(A)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:r.length,issues:o,maxObservedGapMs:f,outboundAudioStreams:d.length,stalledInboundStreams:a,stalledOutboundStreams:A,status:o.some((g)=>g.severity==="error")?"fail":o.length>0?"warn":"pass",streams:i,totalStats:n.length}};var on="/api/voice/browser-media",tn=5000,sn=async(e)=>e.peerConnection??await e.getPeerConnection?.()??null,rn=async(e,n)=>{let c=n.fetch??globalThis.fetch;if(!c)return;await c(n.path??on,{body:JSON.stringify(e),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},Le=(e)=>{let n=null,c=[],o=async()=>{let i=await sn(e);if(!i)return;let r=await Ie({peerConnection:i}),d=Me({...e,stats:r}),f=e.continuity===!1?void 0:Ve({...e.continuity,previousStats:c,stats:r}),a={at:Date.now(),continuity:f,report:d,scenarioId:e.getScenarioId?.()??null,sessionId:e.getSessionId?.()??null};return c=r,e.onReport?.(a),await rn(a,e),a},s=()=>{o().catch((i)=>{e.onError?.(i)})},t=()=>{if(n)clearInterval(n),n=null};return{close:t,reportOnce:o,start:()=>{if(n)return;s(),n=setInterval(s,e.intervalMs??tn)},stop:t}};var W=()=>{},ln=()=>W,an={callControl:W,close:W,endTurn:W,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",send:W,sendAudio:W,simulateDisconnect:W,start:()=>{},subscribe:ln},dn=()=>crypto.randomUUID(),gn=(e,n,c)=>{let{hostname:o,port:s,protocol:t}=window.location,i=t==="https:"?"wss:":"ws:",r=s?`:${s}`:"",d=new URL(`${i}//${o}${r}${e}`);if(d.searchParams.set("sessionId",n),c)d.searchParams.set("scenarioId",c);return d.toString()},fn=(e)=>{if(!e||typeof e!=="object"||!("type"in e))return!1;switch(e.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}},An=(e)=>{if(typeof e.data!=="string")return null;try{let n=JSON.parse(e.data);return fn(n)?n:null}catch{return null}},be=(e,n={})=>{if(typeof window>"u")return an;let c=new Set,o=n.reconnect!==!1,s=n.maxReconnectAttempts??10,t=n.pingInterval??30000,i={isConnected:!1,pendingMessages:[],scenarioId:n.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:n.sessionId??dn(),ws:null},r=(l)=>{c.forEach((M)=>M(l))},d=()=>{if(i.pingInterval)clearInterval(i.pingInterval),i.pingInterval=null;if(i.reconnectTimeout)clearTimeout(i.reconnectTimeout),i.reconnectTimeout=null},f=()=>{if(i.ws?.readyState!==1)return;while(i.pendingMessages.length>0){let l=i.pendingMessages.shift();if(l!==void 0)i.ws.send(l)}},a=()=>{let l=Date.now()+500;i.reconnectAttempts+=1,r({reconnect:{attempts:i.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:s,nextAttemptAt:l,status:"reconnecting"},type:"connection"}),i.reconnectTimeout=setTimeout(()=>{if(i.reconnectAttempts>s){r({reconnect:{attempts:i.reconnectAttempts,maxAttempts:s,status:"exhausted"},type:"connection"});return}A()},500)},A=()=>{let l=new WebSocket(gn(e,i.sessionId,i.scenarioId));l.binaryType="arraybuffer",l.onopen=()=>{let M=i.reconnectAttempts>0;if(i.isConnected=!0,f(),M)r({reconnect:{attempts:i.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:s,status:"resumed"},type:"connection"}),i.reconnectAttempts=0;c.forEach((b)=>b({scenarioId:i.scenarioId??void 0,sessionId:i.sessionId,status:"active",type:"session"})),i.pingInterval=setInterval(()=>{if(l.readyState===1)l.send(JSON.stringify({type:"ping"}))},t)},l.onmessage=(M)=>{let b=An(M);if(!b)return;if(b.type==="session")i.sessionId=b.sessionId,i.scenarioId=b.scenarioId??i.scenarioId;c.forEach((z)=>z(b))},l.onclose=(M)=>{if(i.isConnected=!1,d(),o&&M.code!==1000&&i.reconnectAttempts<s)a();else if(o&&M.code!==1000)r({reconnect:{attempts:i.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:s,status:"exhausted"},type:"connection"})},i.ws=l},g=(l)=>{if(i.ws?.readyState===1){i.ws.send(l);return}i.pendingMessages.push(l)},y=(l)=>{g(JSON.stringify(l))},C=(l={})=>{if(l.sessionId)i.sessionId=l.sessionId;if(l.scenarioId)i.scenarioId=l.scenarioId;y({type:"start",sessionId:i.sessionId,scenarioId:i.scenarioId??void 0})},I=(l)=>{g(l)},L=()=>{y({type:"end_turn"})},R=(l)=>{y({...l,type:"call_control"})},w=()=>{if(d(),i.ws)i.ws.close(1000),i.ws=null;i.isConnected=!1,c.clear()},x=()=>{if(i.ws?.readyState===1)i.ws.close(4000,"absolutejs-voice-reconnect-proof")},U=(l)=>{return c.add(l),()=>{c.delete(l)}};return A(),{callControl:R,close:w,endTurn:L,getReadyState:()=>i.ws?.readyState??3,getScenarioId:()=>i.scenarioId??"",getSessionId:()=>i.sessionId,send:y,sendAudio:I,simulateDisconnect:x,start:C,subscribe:U}};var hn=()=>({attempts:0,maxAttempts:0,status:"idle"}),Cn=()=>({assistantAudio:[],assistantTexts:[],call:null,error:null,isConnected:!1,sessionMetadata:null,scenarioId:null,partial:"",reconnect:hn(),sessionId:null,status:"idle",turns:[]}),we=()=>{let e=Cn(),n=new Set,c=()=>{n.forEach((s)=>s())};return{dispatch:(s)=>{switch(s.type){case"audio":e={...e,assistantAudio:[...e.assistantAudio,{chunk:s.chunk,format:s.format,receivedAt:s.receivedAt,turnId:s.turnId}]};break;case"assistant":e={...e,assistantTexts:[...e.assistantTexts,s.text]};break;case"complete":e={...e,sessionId:s.sessionId,status:"completed"};break;case"call_lifecycle":e={...e,call:{...e.call,disposition:s.event.type==="end"?s.event.disposition:e.call?.disposition,endedAt:s.event.type==="end"?s.event.at:e.call?.endedAt,events:[...e.call?.events??[],s.event],lastEventAt:s.event.at,startedAt:e.call?.startedAt??s.event.at},sessionId:s.sessionId};break;case"connected":e={...e,isConnected:!0,reconnect:e.reconnect.status==="reconnecting"?{...e.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:e.reconnect};break;case"connection":e={...e,reconnect:s.reconnect};break;case"disconnected":e={...e,isConnected:!1};break;case"error":e={...e,error:s.message};break;case"final":e={...e,partial:s.transcript.text,turns:e.turns.map((t)=>t)};break;case"partial":e={...e,partial:s.transcript.text};break;case"replay":e={...e,assistantTexts:[...s.assistantTexts],call:s.call??null,error:null,isConnected:s.status==="active",partial:s.partial,reconnect:e.reconnect.status==="reconnecting"?{...e.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:e.reconnect,scenarioId:s.scenarioId??e.scenarioId,sessionId:s.sessionId,sessionMetadata:s.sessionMetadata??e.sessionMetadata,status:s.status,turns:[...s.turns]};break;case"session":e={...e,error:null,scenarioId:s.scenarioId??e.scenarioId,isConnected:s.status==="active",sessionId:s.sessionId,sessionMetadata:s.sessionMetadata??e.sessionMetadata,status:s.status};break;case"turn":e={...e,partial:"",turns:[...e.turns,s.turn]};break}c()},getServerSnapshot:()=>e,getSnapshot:()=>e,subscribe:(s)=>{return n.add(s),()=>{n.delete(s)}}}};var Re=(e,n={})=>{let c=be(e,n),o=we(),s=n.browserMedia&&typeof window<"u"?Le({...n.browserMedia,getScenarioId:()=>n.browserMedia?n.browserMedia.getScenarioId?.()??c.getScenarioId():c.getScenarioId(),getSessionId:()=>n.browserMedia?n.browserMedia.getSessionId?.()??c.getSessionId():c.getSessionId()}):null,t=new Set,i=(a)=>Promise.resolve().then(()=>{if(!a?.sessionId&&!a?.scenarioId)return;c.start(a),s?.start()}),r=()=>{t.forEach((a)=>a())},d=()=>{if(!n.reconnectReportPath||typeof fetch>"u")return;let a=o.getSnapshot(),A=JSON.stringify({at:Date.now(),reconnect:a.reconnect,scenarioId:a.scenarioId,sessionId:c.getSessionId(),turnIds:a.turns.map((g)=>g.id)});fetch(n.reconnectReportPath,{body:A,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},f=c.subscribe((a)=>{let A=he(a);if(A){if(o.dispatch(A),a.type==="connection")d();r()}});return{callControl(a){c.callControl(a)},close(){f(),s?.close(),c.close(),o.dispatch({type:"disconnected"}),r()},endTurn(){c.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:i,get partial(){return o.getSnapshot().partial},get reconnect(){return o.getSnapshot().reconnect},get sessionId(){return c.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(a){c.sendAudio(a)},simulateDisconnect(){c.simulateDisconnect()},subscribe(a){return t.add(a),()=>{t.delete(a)}}}};var ue=(e)=>{if(!e||e.enabled===!1)return;return{enabled:!0,maxGain:e.maxGain??3,noiseGateAttenuation:e.noiseGateAttenuation??0.15,noiseGateThreshold:e.noiseGateThreshold??0.006,targetLevel:e.targetLevel??0.08}};var yn={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}},Tn={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 _e=(e)=>{let n=e?.profile??"fast",c=e?.qualityProfile??"general",o=yn[n],s=Tn[c];return{profile:n,qualityProfile:c,silenceMs:e?.silenceMs??s.silenceMs??o.silenceMs,speechThreshold:e?.speechThreshold??s.speechThreshold??o.speechThreshold,transcriptStabilityMs:e?.transcriptStabilityMs??s.transcriptStabilityMs??o.transcriptStabilityMs}};var Sn={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"}}},Ee=(e="default")=>{let n=Sn[e];return{audioConditioning:ue(n.audioConditioning),capture:{channelCount:n.capture?.channelCount??1,sampleRateHz:n.capture?.sampleRateHz??16000},connection:{...n.connection},name:e,sttLifecycle:n.sttLifecycle??"continuous",turnDetection:_e(n.turnDetection)}};var Mn=(e)=>({assistantAudio:[...e.assistantAudio],assistantTexts:[...e.assistantTexts],call:e.call,error:e.error,isConnected:e.isConnected,isRecording:!1,partial:e.partial,reconnect:e.reconnect,recordingError:null,sessionId:e.sessionId,sessionMetadata:e.sessionMetadata,scenarioId:e.scenarioId,status:e.status,turns:[...e.turns]}),j=(e,n={})=>{let c=Ee(n.preset),o=Re(e,{...c.connection,...n.connection}),s=null,t=Mn(o),i=new Set,r=()=>{for(let C of i)C()},d=()=>{if(t={...t,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&&t.status==="completed"&&t.isRecording)s?.stop(),s=null,t={...t,isRecording:!1};r()},f=o.subscribe(d);d();let a=()=>{if(s)return s;return s=Ae({channelCount:n.capture?.channelCount??c.capture.channelCount,onLevel:n.capture?.onLevel,onAudio:(C)=>{if(n.capture?.onAudio){n.capture.onAudio(C,o.sendAudio);return}o.sendAudio(C)},sampleRateHz:n.capture?.sampleRateHz??c.capture.sampleRateHz}),s},A=()=>{s?.stop(),s=null,t={...t,isRecording:!1},r()},g=async()=>{if(t.isRecording)return;try{t={...t,recordingError:null},r(),await a().start(),t={...t,isRecording:!0},r()}catch(C){throw s=null,t={...t,isRecording:!1,recordingError:C instanceof Error?C.message:String(C)},r(),C}};return{bindHTMX(C){return fe(o,C)},callControl:(C)=>o.callControl(C),close:()=>{f(),A(),o.close()},endTurn:()=>o.endTurn(),get error(){return t.error},getServerSnapshot:()=>t,getSnapshot:()=>t,get isConnected(){return t.isConnected},get isRecording(){return t.isRecording},get partial(){return t.partial},get recordingError(){return t.recordingError},get reconnect(){return t.reconnect},sendAudio:(C)=>o.sendAudio(C),simulateDisconnect:()=>o.simulateDisconnect(),get sessionId(){return t.sessionId},get sessionMetadata(){return t.sessionMetadata},get scenarioId(){return t.scenarioId},startRecording:g,get status(){return t.status},stopRecording:A,subscribe:(C)=>{return i.add(C),()=>{i.delete(C)}},toggleRecording:async()=>{if(t.isRecording){A();return}await g()},get turns(){return t.turns},get assistantTexts(){return t.assistantTexts},get assistantAudio(){return t.assistantAudio},get call(){return t.call}}};var In=()=>({activeSourceCount:0,error:null,isActive:!1,isPlaying:!1,lastInterruptLatencyMs:void 0,lastPlaybackStopLatencyMs:void 0,processedChunkCount:0,queuedChunkCount:0}),Vn=()=>{if(typeof window>"u")return typeof AudioContext>"u"?void 0:AudioContext;return window.AudioContext??window.webkitAudioContext},Ln=(e,n)=>{let c=n.format;if(c.container!=="raw"||c.encoding!=="pcm_s16le")throw Error(`Unsupported assistant audio format: ${c.container}/${c.encoding}`);let o=n.chunk,s=Math.max(1,c.channels),t=Math.floor(o.byteLength/2),i=Math.max(1,Math.floor(t/s)),r=e.createBuffer(s,i,c.sampleRateHz),d=new DataView(o.buffer,o.byteOffset,o.byteLength);for(let f=0;f<s;f+=1){let a=r.getChannelData(f);for(let A=0;A<i;A+=1){let y=(A*s+f)*2;if(y+1>=o.byteLength){a[A]=0;continue}a[A]=d.getInt16(y,!0)/32768}}return r},F=(e,n={})=>{let c=new Set,o=new Set,s=(n.lookaheadMs??15)/1000,t=In(),i=null,r=null,d=0,f=Promise.resolve(),a=null,A=null,g=null,y=null,C=()=>{for(let h of c)h()},I=(h)=>{t={...t,...h},C()},L=()=>{if(t.error!==null)I({error:null})},R=()=>{if(y!==null)clearTimeout(y),y=null},w=(h)=>{R(),a=null,I({activeSourceCount:o.size,isPlaying:!1,lastInterruptLatencyMs:h,lastPlaybackStopLatencyMs:t.lastPlaybackStopLatencyMs??h}),g?.(),g=null,A=null},x=(h)=>{if(!h)return 0;return Math.max(0,((h.baseLatency??0)+(h.outputLatency??0))*1000)},U=(h)=>{if(!r)return;let T=1;if(r.gain.setValueAtTime){r.gain.setValueAtTime(T,h?.currentTime??0);return}r.gain.value=T},l=(h)=>{if(!r)return;let T=0;if(r.gain.setValueAtTime){r.gain.setValueAtTime(T,h?.currentTime??0);return}r.gain.value=T},M=()=>{if(a===null||o.size>0)return;w(Date.now()-a)},b=async()=>{if(i)return i;if(n.createAudioContext)i=n.createAudioContext();else{let h=Vn();if(!h)throw Error("Assistant audio playback requires AudioContext support.");i=new h}if(i.createGain)r=i.createGain(),r.connect?.(i.destination);return d=i.currentTime,i},z=async(h)=>{let T=await b(),E=Ln(T,h),V=T.createBufferSource();V.buffer=E,V.connect(r??T.destination),V.onended=()=>{o.delete(V),V.disconnect?.(),I({activeSourceCount:o.size,isPlaying:o.size>0&&t.isActive}),M()};let J=Math.max(T.currentTime+s,d);d=J+E.duration,o.add(V),I({activeSourceCount:o.size,isPlaying:!0}),V.start(J)},_=(h)=>{for(let T of[...o])T.stop?.();if(d=i?i.currentTime:0,h?.forceClear){for(let T of o)T.disconnect?.();o.clear(),M()}},$=async()=>{if(!t.isActive)return;let h=e.assistantAudio.slice(t.processedChunkCount);if(h.length===0)return;try{L();for(let T of h)await z(T);I({processedChunkCount:e.assistantAudio.length,queuedChunkCount:t.queuedChunkCount+h.length})}catch(T){I({error:T instanceof Error?T.message:String(T)})}},P=()=>{return f=f.then(()=>$(),()=>$()),f},k=e.subscribe(()=>{if(n.autoStart&&!t.isActive&&e.assistantAudio.length>0){N.start();return}if(t.isActive)P()}),N={close:async()=>{if(k(),_({forceClear:!0}),R(),g?.(),g=null,A=null,a=null,i&&i.state!=="closed")await i.close();i=null,r?.disconnect?.(),r=null,d=0,I({activeSourceCount:0,isActive:!1,isPlaying:!1})},get activeSourceCount(){return t.activeSourceCount},get error(){return t.error},getSnapshot:()=>t,get isActive(){return t.isActive},get isPlaying(){return t.isPlaying},interrupt:async()=>{let h=Date.now(),T=await b();a=h,l(T);let E=Date.now()-h+x(T);if(I({isActive:!1,isPlaying:o.size>0,lastPlaybackStopLatencyMs:E}),o.size===0){w(E);return}if(!A)A=new Promise((V)=>{g=V});R(),y=setTimeout(()=>{for(let V of o)V.disconnect?.();o.clear(),w(Date.now()-h)},250),_(),await A},get lastInterruptLatencyMs(){return t.lastInterruptLatencyMs},get lastPlaybackStopLatencyMs(){return t.lastPlaybackStopLatencyMs},pause:async()=>{if(!i){I({activeSourceCount:0,isActive:!1,isPlaying:!1});return}await i.suspend(),I({activeSourceCount:o.size,isActive:!1,isPlaying:!1})},get processedChunkCount(){return t.processedChunkCount},get queuedChunkCount(){return t.queuedChunkCount},start:async()=>{try{L();let h=await b();if(U(h),h.state==="suspended")await h.resume();I({activeSourceCount:o.size,isActive:!0,isPlaying:h.state==="running"}),await P()}catch(h){throw I({error:h instanceof Error?h.message:String(h),isActive:!1,isPlaying:!1}),h}},subscribe:(h)=>{return c.add(h),()=>{c.delete(h)}}};return N};var bn=()=>`barge-in:${Date.now()}:${crypto.randomUUID?.()??Math.random().toString(36).slice(2)}`,wn=(e,n)=>{let c=e.filter((i)=>i.status==="stopped"),o=c.map((i)=>i.latencyMs).filter((i)=>typeof i==="number"),s=c.filter((i)=>typeof i.latencyMs==="number"&&i.latencyMs>n).length,t=c.length-s;return{averageLatencyMs:o.length>0?Math.round(o.reduce((i,r)=>i+r,0)/o.length):void 0,events:[...e],failed:s,lastEvent:e.at(-1),passed:t,status:e.length===0?"empty":s>0?"fail":c.length===0?"warn":"pass",thresholdMs:n,total:c.length}},Pe=(e={})=>{let n=new Set,c=e.thresholdMs??250,o=e.fetch??globalThis.fetch,s=[],t=()=>{for(let d of n)d()},i=(d)=>{if(!e.path||typeof o!=="function")return;o(e.path,{body:JSON.stringify(d),headers:{"Content-Type":"application/json"},method:"POST"}).catch(()=>{})},r=(d,f)=>{let a={at:Date.now(),id:bn(),latencyMs:f.latencyMs,playbackStopLatencyMs:f.playbackStopLatencyMs,reason:f.reason,sessionId:f.sessionId,status:d,thresholdMs:c};return s.push(a),i(a),t(),a};return{getSnapshot:()=>wn(s,c),recordRequested:(d)=>r("requested",d),recordSkipped:(d)=>r("skipped",d),recordStopped:(d)=>r("stopped",d),subscribe:(d)=>{return n.add(d),()=>{n.delete(d)}}}};var Rn=0.08,un=(e,n={})=>(n.enabled??!0)&&e>=(n.interruptThreshold??Rn),te=(e,n,c={})=>{let o=e.partial,s=(i)=>{if(!n.isPlaying||c.enabled===!1){c.monitor?.recordSkipped({reason:i,sessionId:e.sessionId});return}c.monitor?.recordRequested({reason:i,sessionId:e.sessionId}),n.interrupt().then(()=>{c.monitor?.recordStopped({latencyMs:n.lastInterruptLatencyMs,playbackStopLatencyMs:n.lastPlaybackStopLatencyMs,reason:i,sessionId:e.sessionId})})},t=e.subscribe(()=>{if(c.interruptOnPartial===!1){o=e.partial;return}if(!o&&e.partial)s("partial-transcript");o=e.partial});return{close:()=>{t()},handleLevel:(i)=>{if(un(i,c))s("input-level")},sendAudio:(i)=>{s("manual-audio"),e.sendAudio(i)}}};var ae=48,_n=320,En=88,Pn="Guided test",Dn="General recording",On="Pick a scenario to begin the demo.",xn="I can walk you through a short guided voice test.",Un="I can capture one freeform recording and confirm that it landed.",Nn="Choose a scenario to begin. Guided test asks follow-up prompts. General recording just captures what you say.",Hn="Click Start general recording to capture one freeform answer.",xe="Speak freely. When you pause, the recording will be captured.",re="Recording saved. Start again if you want another capture.",Ue="Guided test complete. Review the saved summary below.",Ne="All prompts are covered. You can stop the microphone or keep speaking for extra detail.",Gn="Ready. Start guided test or general recording to begin.",Bn="Live. Answer the prompt, then click Stop microphone when finished.",De=["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."],He=(e,n,c)=>Math.min(c,Math.max(n,e)),X=(e)=>e.replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll(\'"\',"&quot;").replaceAll("\'","&#39;"),ie=(e,n)=>{let c=e[n];if(typeof c==="string"&&c.trim())return c;return null},le=(e)=>{if(typeof e==="string"&&e.trim())return e;if(e instanceof Error&&e.message.trim())return e.message;if(e&&typeof e==="object"){let n=e,c=ie(n,"message")??ie(n,"reason")??ie(n,"description");if(c)return c;if("error"in n)return le(n.error);if("cause"in n)return le(n.cause);try{return JSON.stringify(e)}catch{}}return"Unexpected error"},Wn=(e)=>{let n=[e.status];if(e.attempts>0||e.maxAttempts>0)n.push(`${e.attempts}/${e.maxAttempts} attempts`);if(e.nextAttemptAt){let c=Math.max(0,e.nextAttemptAt-Date.now());n.push(`retry in ${Math.ceil(c/100)/10}s`)}return n.join(" \xB7 ")},v=(e=ae)=>Array.from({length:e},()=>0),Oe=(e,n,c=ae)=>{let o=e.slice(-(c-1));o.push(He(n,0,1));while(o.length<c)o.unshift(0);return o},$n=(e,n=_n,c=En)=>{let o=e.length>1?e:v(ae),s=n/(o.length-1),t=c/2,i=c*0.34;if(Math.max(...o,0)<=0.015)return`M 0 ${t} L ${n} ${t}`;let d=o.map((a,A)=>{let g=A*0.76,y=Math.sin(g)*0.78+Math.sin(g*0.41)*0.22,C=a*i,I=s*A,L=He(t+y*C,8,c-8);return{x:I,y:L}});if(d.length===0)return`M 0 ${t} L ${n} ${t}`;let f=`M ${d[0]?.x??0} ${d[0]?.y??t}`;for(let a=1;a<d.length;a+=1){let A=d[a-1],g=d[a];if(!A||!g)continue;let y=(A.x+g.x)/2;f+=` Q ${y} ${A.y} ${g.x} ${g.y}`}return f},kn=(e)=>{if(!e)return De;try{let n=JSON.parse(e);if(Array.isArray(n)){let c=n.filter((o)=>typeof o==="string").map((o)=>o.trim()).filter(Boolean);if(c.length>0)return c}}catch{}return De},se=(e)=>{if(!e)return;let n=Number(e);return Number.isFinite(n)?n:void 0},qn=(e,n,c)=>{if(!n)return null;let o=document.querySelector(n);return o instanceof c?o:null},O=(e,n,c,o)=>{let s=n?document.querySelector(n):null;if(s instanceof c)return s;let t=e.querySelector(`#${o}`);if(t instanceof c)return t;throw Error(`Voice HTMX bootstrap could not find the required element "${o}".`)},Xn=(e)=>{if(!e.mode)return On;if(!e.hasStarted)return e.mode==="guided"?xn:Un;if(e.status==="completed")return e.mode==="guided"?Ue:re;if(e.mode==="general")return xe;return e.guidedPrompts[e.turnCount]??Ne},zn=(e)=>{if(!e.mode)return Nn;if(e.status==="completed")return e.mode==="guided"?Ue:re;if(!e.hasStarted)return e.mode==="guided"?`Click Start guided test to begin. First prompt: ${e.guidedPrompts[0]??"Answer the first prompt."}`:Hn;if(e.mode==="general")return e.turnCount===0?xe:re;return e.guidedPrompts[e.turnCount]??Ne},Yn=(e)=>{let n=e.dataset.voiceGuidedPath,c=e.dataset.voiceGeneralPath;if(!n||!c)throw Error("Voice HTMX bootstrap requires data-voice-guided-path and data-voice-general-path.");let o=kn(e.dataset.voiceGuidedPrompts),s=e.dataset.voiceGuidedLabel??Pn,t=e.dataset.voiceGeneralLabel??Dn,i=e.dataset.voiceReconnectReportPath,r=e.dataset.voiceBargeInPath,d=r?Pe({path:r,thresholdMs:se(e.dataset.voiceBargeInThresholdMs)}):null,f=se(e.dataset.voiceBargeInRecentWindowMs)??4000,a=se(e.dataset.voiceBargeInSpeechThreshold)??0.04,A=O(document,e.dataset.voiceSync,HTMLElement,"voice-htmx-sync"),g=O(e,e.dataset.voiceConnection,HTMLElement,"metric-connection"),y=O(e,e.dataset.voiceError,HTMLElement,"status-error"),C=O(e,e.dataset.voiceMicrophone,HTMLElement,"status-mic"),I=O(e,e.dataset.voicePrompt,HTMLElement,"status-prompt"),L=qn(e,e.dataset.voiceReconnect,HTMLElement),R=O(e,e.dataset.voiceChat,HTMLElement,"chat-list"),w=O(e,e.dataset.voiceStartGuided,HTMLButtonElement,"start-guided"),x=O(e,e.dataset.voiceStartGeneral,HTMLButtonElement,"start-general"),U=O(e,e.dataset.voiceStop,HTMLButtonElement,"stop-mic"),l=O(e,e.dataset.voiceMonitor,HTMLElement,"voice-monitor"),M=O(e,e.dataset.voiceMonitorCopy,HTMLElement,"voice-monitor-copy"),b=O(e,e.dataset.voiceWaveGlow,SVGPathElement,"voice-wave-glow"),z=O(e,e.dataset.voiceWavePath,SVGPathElement,"voice-wave-path"),_=null,$={general:!1,guided:!1},P=!1,k=null,N=v(),h=null,T=null,E=j(n,{capture:{onAudio:(S,B)=>{if(h){h.sendAudio(S);return}B(S)},onLevel:(S)=>{h?.handleLevel(S),N=Oe(N,S),p()}},connection:{reconnectReportPath:i},preset:"guided-intake"}),V=j(c,{capture:{onAudio:(S,B)=>{if(T){T.sendAudio(S);return}B(S)},onLevel:(S)=>{T?.handleLevel(S),N=Oe(N,S),p()}},connection:{reconnectReportPath:i},preset:"dictation"}),J=E.bindHTMX({element:A}),Ge=V.bindHTMX({element:A}),Q=F(E),Z=F(V);h=te(E,Q,{interruptThreshold:a,monitor:d??void 0}),T=te(V,Z,{interruptThreshold:a,monitor:d??void 0});let Y=()=>_==="general"?V:E,Qn=()=>_==="general"?Z:Q,p=()=>{let S=$n(N);b.setAttribute("d",S),z.setAttribute("d",S),M.innerHTML=`<span class="voice-live-dot"></span>${P?"Microphone live":"Microphone idle"}`,M.classList.toggle("is-live",P),l.classList.toggle("is-live",P)},q=()=>{let S=Y(),B=(_?$[_]:!1)||S.turns.length>0,ge=S.status;if(g.textContent=S.isConnected?"Connected":"Waiting",y.textContent=k||S.error||"None",L)L.textContent=Wn(S.reconnect);C.textContent=P?Bn:Gn,I.textContent=zn({guidedPrompts:o,hasStarted:B,mode:_,status:ge,turnCount:S.turns.length}),w.hidden=P,x.hidden=P,U.hidden=!P,R.innerHTML=`<article class="voice-chat-message assistant">\n <div class="voice-chat-role">${X(_==="general"?t:_==="guided"?s:"Voice demo")}</div>\n <p class="voice-turn-text">${X(Xn({generalLabel:t,guidedLabel:s,guidedPrompts:o,hasStarted:B,mode:_,status:ge,turnCount:S.turns.length}))}</p>\n</article>${S.turns.map((ee)=>`<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(ee.text)}</p>\n </article>\n ${ee.assistantText?`<article class="voice-chat-message assistant">\n <div class="voice-chat-role">${X(_==="general"?t:_==="guided"?s:"Guide")}</div>\n <p class="voice-turn-text">${X(ee.assistantText)}</p>\n </article>`:""}\n</div>`).join("")}${S.partial?`<article class="voice-chat-message user pending">\n <div class="voice-chat-role">Speaking</div>\n <p class="voice-turn-text">${X(S.partial)}</p>\n</article>`:""}`,p()},Be=()=>{Y().stopRecording(),P=!1,k=null,N=v(),q()},de=async(S)=>{_=S,$={...$,[S]:!0};try{await Y().startRecording(),k=null,P=!0,q()}catch(B){Y().stopRecording(),P=!1,N=v(),k=le(B),q()}};E.subscribe(()=>{if(E.assistantAudio.length>0)Q.start().catch(()=>{});q()}),V.subscribe(()=>{if(V.assistantAudio.length>0)Z.start().catch(()=>{});q()}),w.addEventListener("click",()=>{de("guided")}),x.addEventListener("click",()=>{de("general")}),U.addEventListener("click",()=>{Be()}),e.addEventListener("absolute-voice-simulate-disconnect",()=>{Y().simulateDisconnect()}),window.addEventListener("beforeunload",()=>{E.stopRecording(),V.stopRecording(),h?.close(),T?.close(),Q.close(),Z.close(),J(),Ge(),E.close(),V.close()}),q()},Jn=()=>{if(typeof window>"u"||typeof document>"u")return;let e=Array.from(document.querySelectorAll("[data-voice-htmx]"));for(let n of e)if(n instanceof HTMLElement)Yn(n)};Jn();export{Jn as initVoiceHTMX};\n';
5189
+ 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';
5190
5190
 
5191
5191
  // src/profileSwitchRecommendation.ts
5192
5192
  import { Elysia } from "elysia";
@@ -12555,935 +12555,16 @@ var createVoiceDiagnosticsRoutes = (options) => {
12555
12555
  };
12556
12556
  // src/mediaPipelineRoutes.ts
12557
12557
  import { Elysia as Elysia13 } from "elysia";
12558
-
12559
- // node_modules/@absolutejs/media/dist/index.js
12560
- import { mkdir, writeFile } from "fs/promises";
12561
- import { join } from "path";
12562
- var formatLabel2 = (format) => `${format.container}/${format.encoding}/${String(format.sampleRateHz)}hz/${String(format.channels)}ch`;
12563
- var formatMatches2 = (actual, expected) => actual.container === expected.container && actual.encoding === expected.encoding && actual.sampleRateHz === expected.sampleRateHz && actual.channels === expected.channels;
12564
- var pushIssue = (issues, severity, code, message) => {
12565
- issues.push({ code, message, severity });
12566
- };
12567
- var numericMetadata = (frame, key) => {
12568
- const value = frame.metadata?.[key];
12569
- return typeof value === "number" && Number.isFinite(value) ? value : undefined;
12570
- };
12571
- var average = (values) => values.length === 0 ? undefined : values.reduce((total, value) => total + value, 0) / values.length;
12572
- var max = (values) => values.length === 0 ? undefined : Math.max(...values);
12573
- var min = (values) => values.length === 0 ? undefined : Math.min(...values);
12574
- var numericStat = (stat, key) => {
12575
- const value = stat[key];
12576
- return typeof value === "number" && Number.isFinite(value) ? value : undefined;
12577
- };
12578
- var booleanStat = (stat, key) => {
12579
- const value = stat[key];
12580
- return typeof value === "boolean" ? value : undefined;
12581
- };
12582
- var stringStat = (stat, key) => {
12583
- const value = stat[key];
12584
- return typeof value === "string" ? value : undefined;
12585
- };
12586
- var statKey = (stat) => String(stat.id ?? stringStat(stat, "ssrc") ?? numericStat(stat, "ssrc") ?? stringStat(stat, "trackIdentifier") ?? stringStat(stat, "mid") ?? "unknown");
12587
- var secondsToMs = (value) => value === undefined ? undefined : value * 1000;
12588
- var DEFAULT_TELEPHONY_FORMAT = {
12589
- channels: 1,
12590
- container: "raw",
12591
- encoding: "mulaw",
12592
- sampleRateHz: 8000
12593
- };
12594
- var bytesToBase64 = (audio) => {
12595
- const bytes = audio instanceof ArrayBuffer ? new Uint8Array(audio) : new Uint8Array(audio.buffer, audio.byteOffset, audio.byteLength);
12596
- return Buffer.from(bytes).toString("base64");
12597
- };
12598
- var base64ToBytes = (value) => new Uint8Array(Buffer.from(value, "base64"));
12599
- var unknownRecord = (value) => value && typeof value === "object" ? value : {};
12600
- var firstString2 = (records, keys) => {
12601
- for (const record of records) {
12602
- for (const key of keys) {
12603
- const value = record[key];
12604
- if (typeof value === "string" && value.length > 0) {
12605
- return value;
12606
- }
12607
- if (typeof value === "number" && Number.isFinite(value)) {
12608
- return String(value);
12609
- }
12610
- }
12611
- }
12612
- return;
12613
- };
12614
- var firstNumber = (records, keys) => {
12615
- for (const record of records) {
12616
- for (const key of keys) {
12617
- const value = record[key];
12618
- if (typeof value === "number" && Number.isFinite(value)) {
12619
- return value;
12620
- }
12621
- if (typeof value === "string") {
12622
- const parsed = Number(value);
12623
- if (Number.isFinite(parsed)) {
12624
- return parsed;
12625
- }
12626
- }
12627
- }
12628
- }
12629
- return;
12630
- };
12631
- var telephonyDirection = (track) => {
12632
- const normalized = track?.toLowerCase();
12633
- if (!normalized) {
12634
- return "unknown";
12635
- }
12636
- if (normalized.includes("inbound") || normalized.includes("caller") || normalized.includes("in")) {
12637
- return "inbound";
12638
- }
12639
- if (normalized.includes("outbound") || normalized.includes("assistant") || normalized.includes("out")) {
12640
- return "outbound";
12641
- }
12642
- return "unknown";
12643
- };
12644
- var telephonyFrameKind = (direction) => direction === "outbound" ? "assistant-audio" : "input-audio";
12645
- var telephonyEventKind = (envelope) => {
12646
- const raw = firstString2([envelope], ["event", "type", "eventType"]) ?? firstString2([unknownRecord(envelope.message)], ["event", "type"]);
12647
- const normalized = raw?.toLowerCase().replace(/[_\s-]+/g, "-");
12648
- if (!normalized) {
12649
- return "unknown";
12650
- }
12651
- if (normalized.includes("connected")) {
12652
- return "connected";
12653
- }
12654
- if (normalized.includes("start")) {
12655
- return "start";
12656
- }
12657
- if (normalized.includes("media")) {
12658
- return "media";
12659
- }
12660
- if (normalized.includes("stop") || normalized.includes("closed")) {
12661
- return "stop";
12662
- }
12663
- if (normalized.includes("error") || normalized.includes("failed")) {
12664
- return "error";
12665
- }
12666
- return "unknown";
12667
- };
12668
- var normalizeWebRTCStat = (stat) => {
12669
- const sample = {};
12670
- for (const [key, value] of Object.entries(stat)) {
12671
- if (value === null || typeof value === "boolean" || typeof value === "number" || typeof value === "string") {
12672
- sample[key] = value;
12673
- }
12674
- }
12675
- return sample;
12676
- };
12677
- var parseTelephonyMediaFrame = (input) => {
12678
- const envelope = input.envelope;
12679
- const media = unknownRecord(envelope.media);
12680
- const payload = firstString2([media, envelope], ["payload", "audio", "data"]) ?? firstString2([unknownRecord(envelope.message)], ["payload"]);
12681
- if (!payload) {
12682
- return;
12683
- }
12684
- const carrier = input.carrier ?? firstString2([envelope], ["provider"]) ?? "telephony";
12685
- const streamId = firstString2([media, envelope], ["streamSid", "stream_id", "streamId", "streamId", "callSid", "call_id"]);
12686
- const sequenceNumber = firstString2([media, envelope], ["sequenceNumber", "sequence_number", "chunk"]);
12687
- const track = firstString2([media, envelope], ["track", "direction"]);
12688
- const direction = telephonyDirection(track);
12689
- const timestamp = firstNumber([media, envelope], ["timestamp", "time", "startedAt"]);
12690
- return {
12691
- at: timestamp,
12692
- audio: base64ToBytes(payload),
12693
- format: input.format ?? DEFAULT_TELEPHONY_FORMAT,
12694
- id: [
12695
- carrier,
12696
- streamId ?? input.sessionId ?? "stream",
12697
- sequenceNumber ?? timestamp ?? Date.now()
12698
- ].join(":"),
12699
- kind: telephonyFrameKind(direction),
12700
- metadata: {
12701
- carrier,
12702
- direction,
12703
- event: firstString2([envelope], ["event", "type"]),
12704
- sequenceNumber,
12705
- streamId,
12706
- track
12707
- },
12708
- sessionId: input.sessionId ?? streamId,
12709
- source: "telephony"
12710
- };
12711
- };
12712
- var serializeTelephonyMediaFrame = (input) => {
12713
- const carrier = input.carrier ?? input.frame.metadata?.carrier ?? "telephony";
12714
- const streamId = input.streamId ?? (typeof input.frame.metadata?.streamId === "string" ? input.frame.metadata.streamId : input.frame.sessionId);
12715
- const sequenceNumber = input.sequenceNumber ?? (typeof input.frame.metadata?.sequenceNumber === "string" || typeof input.frame.metadata?.sequenceNumber === "number" ? input.frame.metadata.sequenceNumber : undefined);
12716
- const direction = input.frame.kind === "assistant-audio" ? "outbound" : "inbound";
12717
- const payload = input.frame.audio ? bytesToBase64(input.frame.audio) : "";
12718
- if (carrier === "twilio") {
12719
- return {
12720
- event: "media",
12721
- sequenceNumber,
12722
- streamSid: streamId,
12723
- media: {
12724
- payload,
12725
- timestamp: input.frame.at,
12726
- track: direction
12727
- }
12728
- };
12729
- }
12730
- if (carrier === "telnyx") {
12731
- return {
12732
- event: "media",
12733
- stream_id: streamId,
12734
- sequence_number: sequenceNumber,
12735
- media: {
12736
- payload,
12737
- timestamp: input.frame.at,
12738
- track: direction
12739
- }
12740
- };
12741
- }
12742
- if (carrier === "plivo") {
12743
- return {
12744
- event: "media",
12745
- streamId,
12746
- sequenceNumber,
12747
- media: {
12748
- payload,
12749
- timestamp: input.frame.at,
12750
- track: direction
12751
- }
12752
- };
12753
- }
12754
- return {
12755
- event: "media",
12756
- provider: carrier,
12757
- sequenceNumber,
12758
- streamId,
12759
- media: {
12760
- payload,
12761
- timestamp: input.frame.at,
12762
- track: direction
12763
- }
12764
- };
12765
- };
12766
- var createTelephonyMediaSerializer = (input) => {
12767
- const format = input.format ?? DEFAULT_TELEPHONY_FORMAT;
12768
- return {
12769
- carrier: input.carrier,
12770
- format,
12771
- parse: (envelope) => parseTelephonyMediaFrame({
12772
- carrier: input.carrier,
12773
- envelope,
12774
- format,
12775
- sessionId: input.sessionId ?? input.streamId
12776
- }),
12777
- serialize: (frame) => serializeTelephonyMediaFrame({
12778
- carrier: input.carrier,
12779
- frame,
12780
- streamId: input.streamId
12781
- })
12782
- };
12783
- };
12784
- var parseTelephonyStreamEvent = (input) => {
12785
- const envelope = input.envelope;
12786
- const media = unknownRecord(envelope.media);
12787
- const start = unknownRecord(envelope.start);
12788
- const stop = unknownRecord(envelope.stop);
12789
- const errorRecord = unknownRecord(envelope.error);
12790
- const kind = telephonyEventKind(envelope);
12791
- const carrier = input.carrier ?? firstString2([envelope], ["provider", "carrier"]) ?? "telephony";
12792
- const frame = kind === "media" ? parseTelephonyMediaFrame({
12793
- carrier,
12794
- envelope,
12795
- format: input.format,
12796
- sessionId: input.sessionId
12797
- }) : undefined;
12798
- const streamId = firstString2([media, start, stop, envelope], ["streamSid", "stream_id", "streamId", "callSid", "call_id"]) ?? input.sessionId;
12799
- const sequenceNumber = firstString2([media, envelope], ["sequenceNumber", "sequence_number", "chunk"]);
12800
- const track = firstString2([media, envelope], ["track", "direction"]);
12801
- return {
12802
- audioBytes: frame?.audio ? frame.audio instanceof ArrayBuffer ? frame.audio.byteLength : frame.audio.byteLength : 0,
12803
- at: frame?.at ?? firstNumber([media, start, stop, envelope], ["timestamp", "time", "startedAt"]),
12804
- carrier,
12805
- direction: telephonyDirection(track),
12806
- error: firstString2([errorRecord, envelope], ["message", "error", "reason"]),
12807
- kind,
12808
- sequenceNumber,
12809
- streamId
12810
- };
12811
- };
12812
- var buildMediaTelephonyStreamLifecycleReport = (input = {}) => {
12813
- const envelopes = input.envelopes ?? [];
12814
- const events = envelopes.map((envelope) => parseTelephonyStreamEvent({
12815
- carrier: input.carrier,
12816
- envelope
12817
- }));
12818
- const issues = [];
12819
- const startedIndex = events.findIndex((event) => event.kind === "start");
12820
- const firstMediaIndex = events.findIndex((event) => event.kind === "media");
12821
- const stoppedIndex = events.findIndex((event) => event.kind === "stop");
12822
- const started = startedIndex >= 0;
12823
- const stopped = stoppedIndex >= 0;
12824
- const mediaEvents = events.filter((event) => event.kind === "media");
12825
- const audioBytes = events.reduce((total, event) => total + event.audioBytes, 0);
12826
- const minAudioBytes = input.minAudioBytes ?? 1;
12827
- const streamIds = Array.from(new Set(events.map((event) => event.streamId).filter(Boolean)));
12828
- if ((input.requireStart ?? true) && !started) {
12829
- pushIssue(issues, "error", "media.telephony_missing_start", "Telephony media stream did not include a start event.");
12830
- }
12831
- if ((input.requireMedia ?? true) && mediaEvents.length === 0) {
12832
- pushIssue(issues, "error", "media.telephony_missing_media", "Telephony media stream did not include media payload events.");
12833
- }
12834
- if ((input.requireStop ?? true) && !stopped) {
12835
- pushIssue(issues, input.maxMissingStop === false ? "warning" : "error", "media.telephony_missing_stop", "Telephony media stream did not include a stop event.");
12836
- }
12837
- if (started && firstMediaIndex >= 0 && firstMediaIndex < startedIndex) {
12838
- pushIssue(issues, "error", "media.telephony_media_before_start", "Telephony media payload arrived before the stream start event.");
12839
- }
12840
- if (stopped && firstMediaIndex >= 0 && stoppedIndex < firstMediaIndex) {
12841
- pushIssue(issues, "error", "media.telephony_stop_before_media", "Telephony media stream stopped before any media payload arrived.");
12842
- }
12843
- if (mediaEvents.length > 0 && audioBytes < minAudioBytes) {
12844
- pushIssue(issues, "error", "media.telephony_no_audio_bytes", `Telephony media stream parsed ${String(audioBytes)} audio byte(s), below required ${String(minAudioBytes)}.`);
12845
- }
12846
- for (const event of events) {
12847
- if (event.kind === "error") {
12848
- pushIssue(issues, "error", "media.telephony_stream_error", event.error ?? "Telephony media stream emitted an error event.");
12849
- }
12850
- }
12851
- return {
12852
- audioBytes,
12853
- carrier: input.carrier,
12854
- checkedAt: Date.now(),
12855
- events,
12856
- issues,
12857
- mediaEvents: mediaEvents.length,
12858
- started,
12859
- status: issues.some((issue) => issue.severity === "error") ? "fail" : issues.length > 0 ? "warn" : "pass",
12860
- stopped,
12861
- streamIds
12862
- };
12863
- };
12864
- var buildMediaResamplingPlan = (input) => {
12865
- const required = !formatMatches2(input.inputFormat, input.outputFormat);
12866
- return {
12867
- inputFormat: input.inputFormat,
12868
- outputFormat: input.outputFormat,
12869
- ratio: input.outputFormat.sampleRateHz / input.inputFormat.sampleRateHz,
12870
- required,
12871
- status: input.inputFormat.container === input.outputFormat.container && input.inputFormat.encoding === input.outputFormat.encoding && input.inputFormat.channels === input.outputFormat.channels ? "pass" : "warn"
12872
- };
12873
- };
12874
- var speechProbability = (frame) => {
12875
- if (frame.metadata?.isSpeech === true) {
12876
- return 1;
12877
- }
12878
- if (frame.metadata?.isSpeech === false) {
12879
- return 0;
12880
- }
12881
- for (const key of ["speechProbability", "voiceProbability", "rms", "energy"]) {
12882
- const value = numericMetadata(frame, key);
12883
- if (value !== undefined) {
12884
- return value;
12885
- }
12886
- }
12887
- return 0;
12888
- };
12889
- var buildMediaVadReport = (input = {}) => {
12890
- const frames = (input.frames ?? []).filter((frame) => frame.kind === "input-audio");
12891
- const speechStartThreshold = input.speechStartThreshold ?? 0.6;
12892
- const speechEndThreshold = input.speechEndThreshold ?? 0.35;
12893
- const minSpeechFrames = input.minSpeechFrames ?? 1;
12894
- const maxSilenceFrames = input.maxSilenceFrames ?? 1;
12895
- const segments = [];
12896
- let activeFrames = [];
12897
- let silenceFrames = 0;
12898
- const closeSegment = () => {
12899
- if (activeFrames.length < minSpeechFrames) {
12900
- activeFrames = [];
12901
- silenceFrames = 0;
12902
- return;
12903
- }
12904
- const first = activeFrames[0];
12905
- const last = activeFrames.at(-1);
12906
- if (!first) {
12907
- return;
12908
- }
12909
- segments.push({
12910
- durationMs: first.at !== undefined && last?.at !== undefined ? last.at - first.at + (last.durationMs ?? 0) : undefined,
12911
- endAt: last?.at !== undefined ? last.at + (last.durationMs ?? 0) : undefined,
12912
- frameCount: activeFrames.length,
12913
- segmentId: `vad:${String(segments.length + 1)}`,
12914
- sessionId: first.sessionId,
12915
- startAt: first.at,
12916
- turnId: first.turnId
12917
- });
12918
- activeFrames = [];
12919
- silenceFrames = 0;
12920
- };
12921
- for (const frame of frames) {
12922
- const probability = speechProbability(frame);
12923
- if (activeFrames.length === 0) {
12924
- if (probability >= speechStartThreshold) {
12925
- activeFrames.push(frame);
12926
- }
12927
- continue;
12928
- }
12929
- activeFrames.push(frame);
12930
- if (probability <= speechEndThreshold) {
12931
- silenceFrames += 1;
12932
- } else {
12933
- silenceFrames = 0;
12934
- }
12935
- if (silenceFrames > maxSilenceFrames) {
12936
- closeSegment();
12937
- }
12938
- }
12939
- closeSegment();
12940
- return {
12941
- checkedAt: Date.now(),
12942
- inputAudioFrames: frames.length,
12943
- segments,
12944
- status: frames.length === 0 ? "warn" : "pass"
12945
- };
12946
- };
12947
- var buildMediaInterruptionReport = (input = {}) => {
12948
- const issues = [];
12949
- const interruptionFrames = (input.frames ?? []).filter((frame) => frame.kind === "interruption");
12950
- const latenciesMs = interruptionFrames.map((frame) => frame.latencyMs).filter((latency) => typeof latency === "number");
12951
- const maxInterruptionLatencyMs = input.maxInterruptionLatencyMs;
12952
- if (interruptionFrames.length === 0) {
12953
- pushIssue(issues, "warning", "media.interruption_missing", "No interruption frame was observed.");
12954
- }
12955
- if (maxInterruptionLatencyMs !== undefined && latenciesMs.some((latency) => latency > maxInterruptionLatencyMs)) {
12956
- pushIssue(issues, "error", "media.interruption_latency", `Interruption latency exceeded ${String(maxInterruptionLatencyMs)}ms.`);
12957
- }
12958
- return {
12959
- checkedAt: Date.now(),
12960
- interruptionFrames: interruptionFrames.length,
12961
- issues,
12962
- latenciesMs,
12963
- status: issues.some((issue) => issue.severity === "error") ? "fail" : issues.length > 0 ? "warn" : "pass"
12964
- };
12965
- };
12966
- var buildMediaQualityReport = (input = {}) => {
12967
- const frames = [...input.frames ?? []].sort((a, b) => (a.at ?? 0) - (b.at ?? 0));
12968
- const audioFrames = frames.filter((frame) => frame.kind === "input-audio" || frame.kind === "assistant-audio");
12969
- const inputAudioFrames = frames.filter((frame) => frame.kind === "input-audio");
12970
- const assistantAudioFrames = frames.filter((frame) => frame.kind === "assistant-audio");
12971
- const issues = [];
12972
- const gapsMs = [];
12973
- for (const [index, frame] of audioFrames.entries()) {
12974
- const previous = audioFrames[index - 1];
12975
- if (previous?.at === undefined || frame.at === undefined || previous.durationMs === undefined) {
12976
- continue;
12977
- }
12978
- const gap = frame.at - (previous.at + previous.durationMs);
12979
- if (gap > 0) {
12980
- gapsMs.push(gap);
12981
- }
12982
- }
12983
- const jitterMs = audioFrames.map((frame) => numericMetadata(frame, "jitterMs")).filter((value) => value !== undefined).at(-1) ?? max(gapsMs);
12984
- const first = audioFrames.find((frame) => frame.at !== undefined);
12985
- const last = audioFrames.toReversed().find((frame) => frame.at !== undefined);
12986
- const durationMs = first?.at !== undefined && last?.at !== undefined ? last.at - first.at + (last.durationMs ?? 0) : undefined;
12987
- const expectedDurationMs = audioFrames.length > 0 ? audioFrames.reduce((total, frame) => total + (frame.durationMs ?? 0), 0) : undefined;
12988
- const timestampDriftMs = durationMs !== undefined && expectedDurationMs !== undefined ? Math.max(0, durationMs - expectedDurationMs) : undefined;
12989
- const speechScores = inputAudioFrames.map(speechProbability);
12990
- const speechFrames = speechScores.filter((score) => score >= 0.6).length;
12991
- const silenceFrames = speechScores.filter((score) => score <= 0.35).length;
12992
- const unknownSpeechFrames = Math.max(0, inputAudioFrames.length - speechFrames - silenceFrames);
12993
- const speechRatio = inputAudioFrames.length === 0 ? 0 : speechFrames / inputAudioFrames.length;
12994
- const silenceRatio = inputAudioFrames.length === 0 ? 0 : silenceFrames / inputAudioFrames.length;
12995
- const levels = audioFrames.map((frame) => numericMetadata(frame, "level") ?? numericMetadata(frame, "rms") ?? numericMetadata(frame, "energy")).filter((value) => value !== undefined);
12996
- const backpressureEvents = input.transport?.backpressureEvents ?? 0;
12997
- const maxGapMs = input.maxGapMs;
12998
- if (maxGapMs !== undefined && gapsMs.some((gap) => gap > maxGapMs)) {
12999
- pushIssue(issues, "warning", "media.quality_gap", `Observed media gap above ${String(maxGapMs)}ms.`);
13000
- }
13001
- if (input.maxJitterMs !== undefined && jitterMs !== undefined && jitterMs > input.maxJitterMs) {
13002
- pushIssue(issues, "warning", "media.quality_jitter", `Observed jitter ${String(jitterMs)}ms above ${String(input.maxJitterMs)}ms.`);
13003
- }
13004
- if (input.maxTimestampDriftMs !== undefined && timestampDriftMs !== undefined && timestampDriftMs > input.maxTimestampDriftMs) {
13005
- pushIssue(issues, "warning", "media.quality_timestamp_drift", `Observed timestamp drift ${String(timestampDriftMs)}ms above ${String(input.maxTimestampDriftMs)}ms.`);
13006
- }
13007
- if (input.minSpeechRatio !== undefined && inputAudioFrames.length > 0 && speechRatio < input.minSpeechRatio) {
13008
- pushIssue(issues, "warning", "media.quality_speech_ratio", `Observed speech ratio ${String(speechRatio)} below ${String(input.minSpeechRatio)}.`);
13009
- }
13010
- if (input.maxBackpressureEvents !== undefined && backpressureEvents > input.maxBackpressureEvents) {
13011
- pushIssue(issues, "warning", "media.quality_backpressure", `Observed ${String(backpressureEvents)} backpressure event(s), above ${String(input.maxBackpressureEvents)}.`);
13012
- }
13013
- return {
13014
- assistantAudioFrames: assistantAudioFrames.length,
13015
- backpressureEvents,
13016
- checkedAt: Date.now(),
13017
- durationMs,
13018
- gapCount: gapsMs.length,
13019
- gapsMs,
13020
- inputAudioFrames: inputAudioFrames.length,
13021
- issues,
13022
- jitterMs,
13023
- levelAverage: average(levels),
13024
- levelMax: max(levels),
13025
- levelMin: min(levels),
13026
- silenceFrames,
13027
- silenceRatio,
13028
- speechFrames,
13029
- speechRatio,
13030
- status: issues.some((issue) => issue.severity === "error") ? "fail" : issues.length > 0 ? "warn" : "pass",
13031
- timestampDriftMs,
13032
- totalFrames: frames.length,
13033
- unknownSpeechFrames
13034
- };
13035
- };
13036
- var buildMediaWebRTCStatsReport = (input = {}) => {
13037
- const stats = input.stats ?? [];
13038
- const issues = [];
13039
- const inbound = stats.filter((stat) => stat.type === "inbound-rtp" && stringStat(stat, "kind") !== "video");
13040
- const outbound = stats.filter((stat) => stat.type === "outbound-rtp" && stringStat(stat, "kind") !== "video");
13041
- const candidatePairs = stats.filter((stat) => stat.type === "candidate-pair");
13042
- const audioTracks = stats.filter((stat) => (stat.type === "track" || stat.type === "media-source") && stringStat(stat, "kind") === "audio");
13043
- const activeCandidatePairs = candidatePairs.filter((stat) => booleanStat(stat, "selected") === true || booleanStat(stat, "nominated") === true || stringStat(stat, "state") === "succeeded").length;
13044
- const liveAudioTracks = audioTracks.filter((stat) => stringStat(stat, "readyState") !== "ended" && stringStat(stat, "trackState") !== "ended" && booleanStat(stat, "ended") !== true).length;
13045
- const endedAudioTracks = audioTracks.filter((stat) => stringStat(stat, "readyState") === "ended" || stringStat(stat, "trackState") === "ended" || booleanStat(stat, "ended") === true).length;
13046
- const inboundPackets = inbound.reduce((total, stat) => total + (numericStat(stat, "packetsReceived") ?? 0), 0);
13047
- const outboundPackets = outbound.reduce((total, stat) => total + (numericStat(stat, "packetsSent") ?? 0), 0);
13048
- const packetsLost = [...inbound, ...outbound].reduce((total, stat) => total + Math.max(0, numericStat(stat, "packetsLost") ?? 0), 0);
13049
- const packetLossDenominator = inboundPackets + packetsLost;
13050
- const packetLossRatio = packetLossDenominator === 0 ? 0 : packetsLost / packetLossDenominator;
13051
- const bytesReceived = inbound.reduce((total, stat) => total + (numericStat(stat, "bytesReceived") ?? 0), 0);
13052
- const bytesSent = outbound.reduce((total, stat) => total + (numericStat(stat, "bytesSent") ?? 0), 0);
13053
- const roundTripTimeMs = max(candidatePairs.map((stat) => secondsToMs(numericStat(stat, "currentRoundTripTime") ?? numericStat(stat, "roundTripTime"))).filter((value) => value !== undefined));
13054
- const jitterMs = max([...inbound, ...outbound].map((stat) => secondsToMs(numericStat(stat, "jitter"))).filter((value) => value !== undefined));
13055
- const jitterBufferDelayMs = max(inbound.map((stat) => {
13056
- const delay = numericStat(stat, "jitterBufferDelay");
13057
- const emitted = numericStat(stat, "jitterBufferEmittedCount");
13058
- return delay !== undefined && emitted !== undefined && emitted > 0 ? delay / emitted * 1000 : undefined;
13059
- }).filter((value) => value !== undefined));
13060
- const audioLevels = audioTracks.map((stat) => numericStat(stat, "audioLevel")).filter((value) => value !== undefined);
13061
- if (input.requireConnectedCandidatePair && candidatePairs.length > 0 && activeCandidatePairs === 0) {
13062
- pushIssue(issues, "error", "media.webrtc_candidate_pair_missing", "No active WebRTC candidate pair was observed.");
13063
- }
13064
- if (input.requireLiveAudioTrack && liveAudioTracks === 0) {
13065
- pushIssue(issues, "error", "media.webrtc_audio_track_missing", "No live WebRTC audio track was observed.");
13066
- }
13067
- if (input.maxPacketLossRatio !== undefined && packetLossRatio > input.maxPacketLossRatio) {
13068
- pushIssue(issues, "warning", "media.webrtc_packet_loss", `Observed WebRTC packet loss ratio ${String(packetLossRatio)} above ${String(input.maxPacketLossRatio)}.`);
13069
- }
13070
- if (input.maxRoundTripTimeMs !== undefined && roundTripTimeMs !== undefined && roundTripTimeMs > input.maxRoundTripTimeMs) {
13071
- pushIssue(issues, "warning", "media.webrtc_round_trip_time", `Observed WebRTC RTT ${String(roundTripTimeMs)}ms above ${String(input.maxRoundTripTimeMs)}ms.`);
13072
- }
13073
- if (input.maxJitterMs !== undefined && jitterMs !== undefined && jitterMs > input.maxJitterMs) {
13074
- pushIssue(issues, "warning", "media.webrtc_jitter", `Observed WebRTC jitter ${String(jitterMs)}ms above ${String(input.maxJitterMs)}ms.`);
13075
- }
13076
- return {
13077
- activeCandidatePairs,
13078
- audioLevelAverage: average(audioLevels),
13079
- bytesReceived,
13080
- bytesSent,
13081
- checkedAt: Date.now(),
13082
- endedAudioTracks,
13083
- inboundPackets,
13084
- issues,
13085
- jitterBufferDelayMs,
13086
- jitterMs,
13087
- liveAudioTracks,
13088
- outboundPackets,
13089
- packetLossRatio,
13090
- packetsLost,
13091
- roundTripTimeMs,
13092
- status: issues.some((issue) => issue.severity === "error") ? "fail" : issues.length > 0 ? "warn" : "pass",
13093
- totalStats: stats.length
13094
- };
13095
- };
13096
- var collectMediaWebRTCStats = async (input) => {
13097
- const report = await input.peerConnection.getStats(input.selector ?? null);
13098
- return [...report.values()].map(normalizeWebRTCStat);
13099
- };
13100
- var buildMediaWebRTCStreamContinuityReport = (input = {}) => {
13101
- const stats = input.stats ?? [];
13102
- const previousStats = input.previousStats ?? [];
13103
- const issues = [];
13104
- const previousByKey = new Map(previousStats.map((stat) => [statKey(stat), stat]));
13105
- const audioRtp = stats.filter((stat) => (stat.type === "inbound-rtp" || stat.type === "outbound-rtp") && stringStat(stat, "kind") !== "video" && stringStat(stat, "mediaType") !== "video");
13106
- const streams = audioRtp.map((stat) => {
13107
- const direction = stat.type === "outbound-rtp" ? "outbound" : "inbound";
13108
- const packetsKey = direction === "outbound" ? "packetsSent" : "packetsReceived";
13109
- const bytesKey = direction === "outbound" ? "bytesSent" : "bytesReceived";
13110
- const previous = previousByKey.get(statKey(stat));
13111
- const currentPackets = numericStat(stat, packetsKey);
13112
- const previousPackets = previous ? numericStat(previous, packetsKey) : undefined;
13113
- const currentBytes = numericStat(stat, bytesKey);
13114
- const previousBytes = previous ? numericStat(previous, bytesKey) : undefined;
13115
- const timeDeltaMs = stat.timestamp !== undefined && previous?.timestamp !== undefined ? stat.timestamp - previous.timestamp : undefined;
13116
- return {
13117
- bytesDelta: currentBytes !== undefined && previousBytes !== undefined ? currentBytes - previousBytes : undefined,
13118
- currentPackets,
13119
- direction,
13120
- id: statKey(stat),
13121
- packetDelta: currentPackets !== undefined && previousPackets !== undefined ? currentPackets - previousPackets : undefined,
13122
- previousPackets,
13123
- timeDeltaMs
13124
- };
13125
- });
13126
- const inbound = streams.filter((stream) => stream.direction === "inbound");
13127
- const outbound = streams.filter((stream) => stream.direction === "outbound");
13128
- const maxObservedGapMs = max(streams.map((stream) => stream.timeDeltaMs).filter((value) => value !== undefined));
13129
- const stalledInboundStreams = inbound.filter((stream) => input.maxInboundPacketStallMs !== undefined && stream.timeDeltaMs !== undefined && stream.timeDeltaMs >= input.maxInboundPacketStallMs && stream.packetDelta !== undefined && stream.packetDelta <= 0).length;
13130
- const stalledOutboundStreams = outbound.filter((stream) => input.maxOutboundPacketStallMs !== undefined && stream.timeDeltaMs !== undefined && stream.timeDeltaMs >= input.maxOutboundPacketStallMs && stream.packetDelta !== undefined && stream.packetDelta <= 0).length;
13131
- if (input.requireInboundAudio && inbound.length === 0) {
13132
- pushIssue(issues, "error", "media.webrtc_inbound_audio_missing", "No inbound WebRTC audio RTP stream was observed.");
13133
- }
13134
- if (input.requireOutboundAudio && outbound.length === 0) {
13135
- pushIssue(issues, "error", "media.webrtc_outbound_audio_missing", "No outbound WebRTC audio RTP stream was observed.");
13136
- }
13137
- if (input.maxGapMs !== undefined && maxObservedGapMs !== undefined && maxObservedGapMs > input.maxGapMs) {
13138
- pushIssue(issues, "warning", "media.webrtc_stream_gap", `Observed WebRTC stream sample gap ${String(maxObservedGapMs)}ms above ${String(input.maxGapMs)}ms.`);
13139
- }
13140
- if (stalledInboundStreams > 0) {
13141
- pushIssue(issues, "error", "media.webrtc_inbound_stalled", `${String(stalledInboundStreams)} inbound WebRTC audio stream(s) stopped receiving packets.`);
13142
- }
13143
- if (stalledOutboundStreams > 0) {
13144
- pushIssue(issues, "error", "media.webrtc_outbound_stalled", `${String(stalledOutboundStreams)} outbound WebRTC audio stream(s) stopped sending packets.`);
13145
- }
13146
- return {
13147
- checkedAt: Date.now(),
13148
- inboundAudioStreams: inbound.length,
13149
- issues,
13150
- maxObservedGapMs,
13151
- outboundAudioStreams: outbound.length,
13152
- stalledInboundStreams,
13153
- stalledOutboundStreams,
13154
- status: issues.some((issue) => issue.severity === "error") ? "fail" : issues.length > 0 ? "warn" : "pass",
13155
- streams,
13156
- totalStats: stats.length
13157
- };
13158
- };
13159
- var buildMediaPipelineCalibrationReport = (input = {}) => {
13160
- const frames = input.frames ?? [];
13161
- const issues = [];
13162
- const inputFrames = frames.filter((frame) => frame.kind === "input-audio");
13163
- const assistantFrames = frames.filter((frame) => frame.kind === "assistant-audio");
13164
- const turnCommitFrames = frames.filter((frame) => frame.kind === "turn-commit");
13165
- const interruptionFrameRecords = frames.filter((frame) => frame.kind === "interruption");
13166
- const traceLinkedFrames = frames.filter((frame) => frame.traceEventId).length;
13167
- const backpressureFrames = frames.filter((frame) => Boolean(frame.metadata?.backpressure)).length;
13168
- const audioLatencies = assistantFrames.map((frame) => frame.latencyMs).filter((latency) => typeof latency === "number");
13169
- const firstAudioLatencyMs = audioLatencies.length > 0 ? Math.min(...audioLatencies) : undefined;
13170
- const jitterValues = frames.map((frame) => numericMetadata(frame, "jitterMs")).filter((value) => value !== undefined);
13171
- const jitterMs = jitterValues.length > 0 ? Math.max(...jitterValues) : undefined;
13172
- const inputFormat = input.inputFormat ?? inputFrames.find((frame) => frame.format)?.format;
13173
- const outputFormat = input.outputFormat ?? assistantFrames.find((frame) => frame.format)?.format;
13174
- const resamplingRequired = Boolean(input.expectedInputFormat && inputFormat && inputFormat.sampleRateHz !== input.expectedInputFormat.sampleRateHz) || Boolean(input.expectedOutputFormat && outputFormat && outputFormat.sampleRateHz !== input.expectedOutputFormat.sampleRateHz);
13175
- const resamplingTargetHz = resamplingRequired && input.expectedInputFormat ? input.expectedInputFormat.sampleRateHz : resamplingRequired ? input.expectedOutputFormat?.sampleRateHz : undefined;
13176
- if (inputFrames.length === 0) {
13177
- pushIssue(issues, "warning", "media.input_audio_missing", "No input audio frames were observed.");
13178
- }
13179
- if (assistantFrames.length === 0) {
13180
- pushIssue(issues, "warning", "media.assistant_audio_missing", "No assistant audio frames were observed.");
13181
- }
13182
- if (input.expectedInputFormat && inputFormat && !formatMatches2(inputFormat, input.expectedInputFormat)) {
13183
- pushIssue(issues, inputFormat.sampleRateHz === input.expectedInputFormat.sampleRateHz ? "warning" : "error", "media.input_format_mismatch", `Input format ${formatLabel2(inputFormat)} does not match expected ${formatLabel2(input.expectedInputFormat)}.`);
13184
- }
13185
- if (input.expectedOutputFormat && outputFormat && !formatMatches2(outputFormat, input.expectedOutputFormat)) {
13186
- pushIssue(issues, outputFormat.sampleRateHz === input.expectedOutputFormat.sampleRateHz ? "warning" : "error", "media.output_format_mismatch", `Output format ${formatLabel2(outputFormat)} does not match expected ${formatLabel2(input.expectedOutputFormat)}.`);
13187
- }
13188
- if (firstAudioLatencyMs !== undefined && input.maxFirstAudioLatencyMs !== undefined && firstAudioLatencyMs > input.maxFirstAudioLatencyMs) {
13189
- pushIssue(issues, "error", "media.first_audio_latency", `First audio latency ${String(firstAudioLatencyMs)}ms exceeds budget ${String(input.maxFirstAudioLatencyMs)}ms.`);
13190
- }
13191
- if (jitterMs !== undefined && input.maxJitterMs !== undefined && jitterMs > input.maxJitterMs) {
13192
- pushIssue(issues, "warning", "media.jitter", `Media jitter ${String(jitterMs)}ms exceeds budget ${String(input.maxJitterMs)}ms.`);
13193
- }
13194
- if (input.maxBackpressureFrames !== undefined && backpressureFrames > input.maxBackpressureFrames) {
13195
- pushIssue(issues, "warning", "media.backpressure", `Backpressure frame count ${String(backpressureFrames)} exceeds budget ${String(input.maxBackpressureFrames)}.`);
13196
- }
13197
- if (input.requireInterruptionFrame && interruptionFrameRecords.length === 0) {
13198
- pushIssue(issues, "warning", "media.interruption_missing", "No interruption frame was observed.");
13199
- }
13200
- if (input.requireTraceEvidence && traceLinkedFrames === 0) {
13201
- pushIssue(issues, "warning", "media.trace_evidence_missing", "No media frames were linked to trace evidence.");
13202
- }
13203
- return {
13204
- assistantAudioFrames: assistantFrames.length,
13205
- backpressureFrames,
13206
- checkedAt: Date.now(),
13207
- firstAudioLatencyMs,
13208
- inputAudioFrames: inputFrames.length,
13209
- inputFormat,
13210
- interruptionFrames: interruptionFrameRecords.length,
13211
- issues,
13212
- jitterMs,
13213
- outputFormat,
13214
- resamplingRequired,
13215
- resamplingTargetHz,
13216
- status: issues.some((issue) => issue.severity === "error") ? "fail" : issues.length > 0 ? "warn" : "pass",
13217
- surface: input.surface ?? "media-pipeline",
13218
- traceLinkedFrames,
13219
- turnCommitFrames: turnCommitFrames.length
13220
- };
13221
- };
13222
- var DEFAULT_METADATA_DENY = [
13223
- "audioPayload",
13224
- "auth",
13225
- "authorization",
13226
- "cookie",
13227
- "email",
13228
- "phone",
13229
- "phoneNumber",
13230
- "rawPayload",
13231
- "secret",
13232
- "token",
13233
- "transcript",
13234
- "utterance"
13235
- ];
13236
- var DEFAULT_TRUNCATE = 8;
13237
- var issueCodes = (issues) => Array.from(new Set(issues.map((issue) => issue.code)));
13238
- var lastEventKind = (events) => events[events.length - 1]?.kind;
13239
- var formatOptionalMs = (value) => value === undefined ? "n/a" : `${String(Math.round(value))}ms`;
13240
- var formatRatio = (value) => `${(value * 100).toFixed(1)}%`;
13241
- var escapeMarkdownCell = (value) => value.replace(/\|/g, "\\|").replace(/\n/g, " ");
13242
- var renderIssuesTable = (issues) => {
13243
- if (issues.length === 0) {
13244
- return `- No issues.
13245
- `;
13246
- }
13247
- const rows = issues.map((issue) => `| ${issue.severity} | ${escapeMarkdownCell(issue.code)} | ${escapeMarkdownCell(issue.message)} |`).join(`
13248
- `);
13249
- return `| Severity | Code | Message |
13250
- | --- | --- | --- |
13251
- ${rows}
13252
- `;
13253
- };
13254
- var summarizeMediaQualityReport = (report) => ({
13255
- backpressureEvents: report.backpressureEvents,
13256
- description: `${report.totalFrames} frame(s), ${report.gapCount} gap(s), speech ${formatRatio(report.speechRatio)}, status ${report.status}.`,
13257
- driftMs: report.timestampDriftMs,
13258
- frameCount: report.totalFrames,
13259
- gapCount: report.gapCount,
13260
- issueCodes: issueCodes(report.issues),
13261
- issueCount: report.issues.length,
13262
- jitterMs: report.jitterMs,
13263
- silenceRatio: report.silenceRatio,
13264
- speechRatio: report.speechRatio,
13265
- status: report.status
13266
- });
13267
- var summarizeMediaTransportReport = (report) => ({
13268
- backpressureEvents: report.backpressureEvents,
13269
- description: `${report.name}: ${report.state}, in ${report.inputFrames}, out ${report.outputFrames}, backpressure ${report.backpressureEvents}.`,
13270
- errors: report.events.filter((event) => event.kind === "error").length,
13271
- inputFrames: report.inputFrames,
13272
- lastEventKind: lastEventKind(report.events),
13273
- name: report.name,
13274
- outputFrames: report.outputFrames,
13275
- state: report.state,
13276
- status: report.status
13277
- });
13278
- var summarizeMediaProcessorGraphReport = (report) => {
13279
- const errorIssueCodes = Array.from(new Set(report.errors.map((event) => event.kind)));
13280
- return {
13281
- backpressureEvents: report.backpressure.events.length,
13282
- description: `${report.name}: ${report.state}, ${report.nodes.length} node(s), in ${report.inputFrames}, out ${report.emittedFrames}, dropped ${report.droppedFrames}.`,
13283
- droppedFrames: report.droppedFrames,
13284
- edgeCount: report.edges.length,
13285
- edgeEventCount: report.edgeEvents.length,
13286
- emittedFrames: report.emittedFrames,
13287
- errorCount: report.errors.length,
13288
- inputFrames: report.inputFrames,
13289
- issueCodes: errorIssueCodes,
13290
- lifecycleEventCount: report.lifecycleEvents.length,
13291
- name: report.name,
13292
- nodeCount: report.nodes.length,
13293
- state: report.state,
13294
- status: report.status,
13295
- timingMaxMs: report.timing.maxNodeMs
13296
- };
13297
- };
13298
- var renderMediaQualityMarkdown = (report, options = {}) => {
13299
- const title = options.title ?? "Media Quality Report";
13300
- const lines = [
13301
- `# ${title}`,
13302
- "",
13303
- `Status: **${report.status}**`,
13304
- "",
13305
- "| Metric | Value |",
13306
- "| --- | ---: |",
13307
- `| Total frames | ${report.totalFrames} |`,
13308
- `| Input audio | ${report.inputAudioFrames} |`,
13309
- `| Assistant audio | ${report.assistantAudioFrames} |`,
13310
- `| Gaps | ${report.gapCount} |`,
13311
- `| Jitter | ${formatOptionalMs(report.jitterMs)} |`,
13312
- `| Timestamp drift | ${formatOptionalMs(report.timestampDriftMs)} |`,
13313
- `| Speech ratio | ${formatRatio(report.speechRatio)} |`,
13314
- `| Silence ratio | ${formatRatio(report.silenceRatio)} |`,
13315
- `| Backpressure events | ${report.backpressureEvents} |`,
13316
- "",
13317
- "## Issues",
13318
- "",
13319
- renderIssuesTable(report.issues).trimEnd()
13320
- ];
13321
- return `${lines.join(`
13322
- `)}
13323
- `;
13324
- };
13325
- var renderMediaTransportMarkdown = (report, options = {}) => {
13326
- const title = options.title ?? `Media Transport: ${report.name}`;
13327
- const limit = options.redact?.truncateArraysAt ?? DEFAULT_TRUNCATE;
13328
- const events = report.events.slice(-limit);
13329
- const eventRows = events.length === 0 ? "- No transport events recorded." : ["| At | Kind | State | Buffered | Error |", "| --- | --- | --- | ---: | --- |"].concat(events.map((event) => `| ${event.at} | ${event.kind} | ${event.state} | ${event.bufferedFrames ?? ""} | ${escapeMarkdownCell(event.error ?? "")} |`)).join(`
13330
- `);
13331
- const lines = [
13332
- `# ${title}`,
13333
- "",
13334
- `Status: **${report.status}** \xB7 State: **${report.state}**`,
13335
- "",
13336
- "| Metric | Value |",
13337
- "| --- | ---: |",
13338
- `| Input frames | ${report.inputFrames} |`,
13339
- `| Output frames | ${report.outputFrames} |`,
13340
- `| Backpressure events | ${report.backpressureEvents} |`,
13341
- `| Connected | ${report.connected ? "yes" : "no"} |`,
13342
- `| Closed | ${report.closed ? "yes" : "no"} |`,
13343
- `| Failed | ${report.failed ? "yes" : "no"} |`,
13344
- "",
13345
- `## Last ${events.length} event(s)`,
13346
- "",
13347
- eventRows
13348
- ];
13349
- return `${lines.join(`
13350
- `)}
13351
- `;
13352
- };
13353
- var renderMediaProcessorGraphMarkdown = (report, options = {}) => {
13354
- const title = options.title ?? `Media Processor Graph: ${report.name}`;
13355
- const limit = options.redact?.truncateArraysAt ?? DEFAULT_TRUNCATE;
13356
- const nodeRows = report.nodes.map((node) => `| ${escapeMarkdownCell(node.name)} | ${node.kind} | ${node.status} | ${node.inputFrames} | ${node.emittedFrames} | ${node.droppedFrames} | ${node.errors.length} |`).join(`
13357
- `);
13358
- const edgeRows = report.edges.slice(0, limit).map((edge) => `| ${escapeMarkdownCell(edge.from)} | ${escapeMarkdownCell(edge.to)} | ${edge.status} | ${edge.emittedFrames} |`).join(`
13359
- `);
13360
- const issues = report.errors.map((event) => ({
13361
- code: event.kind,
13362
- message: event.error ?? `Processor graph ${event.kind} (state ${event.state}).`,
13363
- severity: "error"
13364
- }));
13365
- const lines = [
13366
- `# ${title}`,
13367
- "",
13368
- `Status: **${report.status}** \xB7 State: **${report.state}**`,
13369
- "",
13370
- "| Metric | Value |",
13371
- "| --- | ---: |",
13372
- `| Nodes | ${report.nodes.length} |`,
13373
- `| Input frames | ${report.inputFrames} |`,
13374
- `| Emitted frames | ${report.emittedFrames} |`,
13375
- `| Dropped frames | ${report.droppedFrames} |`,
13376
- `| Lifecycle events | ${report.lifecycleEvents.length} |`,
13377
- `| Edge events | ${report.edgeEvents.length} |`,
13378
- `| Backpressure events | ${report.backpressure.events.length} |`,
13379
- `| Timing max | ${formatOptionalMs(report.timing.maxNodeMs)} |`,
13380
- `| Timing average | ${formatOptionalMs(report.timing.averageNodeMs)} |`,
13381
- "",
13382
- "## Nodes",
13383
- "",
13384
- nodeRows ? `| Node | Kind | Status | In | Out | Dropped | Errors |
13385
- | --- | --- | --- | ---: | ---: | ---: | ---: |
13386
- ${nodeRows}` : "- No nodes.",
13387
- "",
13388
- `## Edges (showing up to ${limit})`,
13389
- "",
13390
- edgeRows ? `| From | To | Status | Frames |
13391
- | --- | --- | --- | ---: |
13392
- ${edgeRows}` : "- No edges.",
13393
- "",
13394
- "## Errors",
13395
- "",
13396
- renderIssuesTable(issues).trimEnd()
13397
- ];
13398
- return `${lines.join(`
13399
- `)}
13400
- `;
13401
- };
13402
- var truncateArrays = (value, limit, seen) => {
13403
- if (Array.isArray(value)) {
13404
- const head = value.slice(0, limit).map((entry) => truncateArrays(entry, limit, seen));
13405
- if (value.length > limit) {
13406
- return [...head, { truncated: value.length - limit }];
13407
- }
13408
- return head;
13409
- }
13410
- if (value && typeof value === "object") {
13411
- if (seen.has(value))
13412
- return value;
13413
- seen.add(value);
13414
- const next = {};
13415
- for (const [key, entry] of Object.entries(value)) {
13416
- next[key] = truncateArrays(entry, limit, seen);
13417
- }
13418
- return next;
13419
- }
13420
- return value;
13421
- };
13422
- var applyRedaction = (value, options, seen) => {
13423
- const mode = options.mode ?? "omit";
13424
- const maskValue = options.maskValue ?? "[redacted]";
13425
- const allow = new Set(options.metadataAllow ?? []);
13426
- const deny = new Set(options.metadataDeny ?? DEFAULT_METADATA_DENY);
13427
- const walk = (input) => {
13428
- if (Array.isArray(input)) {
13429
- return input.map((entry) => walk(entry));
13430
- }
13431
- if (input && typeof input === "object") {
13432
- if (seen.has(input))
13433
- return input;
13434
- seen.add(input);
13435
- const next = {};
13436
- for (const [key, entry] of Object.entries(input)) {
13437
- if (allow.has(key)) {
13438
- next[key] = entry;
13439
- continue;
13440
- }
13441
- if (deny.has(key)) {
13442
- if (mode === "mask")
13443
- next[key] = maskValue;
13444
- continue;
13445
- }
13446
- next[key] = walk(entry);
13447
- }
13448
- return next;
13449
- }
13450
- return input;
13451
- };
13452
- return walk(value);
13453
- };
13454
- var redactMediaReport = (report, options = {}) => {
13455
- const limit = options.truncateArraysAt ?? DEFAULT_TRUNCATE;
13456
- const truncated = truncateArrays(report, limit, new WeakSet);
13457
- return applyRedaction(truncated, options, new WeakSet);
13458
- };
13459
- var buildArtifactPair = (report, summary, markdown, options) => {
13460
- const jsonValue = options.redact ? redactMediaReport(report, options.redact) : report;
13461
- return {
13462
- json: JSON.stringify(jsonValue, null, 2),
13463
- jsonValue,
13464
- markdown,
13465
- summary
13466
- };
13467
- };
13468
- var buildMediaQualityArtifact = (report, options = {}) => buildArtifactPair(report, summarizeMediaQualityReport(report), renderMediaQualityMarkdown(report, options), options);
13469
- var buildMediaTransportArtifact = (report, options = {}) => buildArtifactPair(report, summarizeMediaTransportReport(report), renderMediaTransportMarkdown(report, options), options);
13470
- var buildMediaProcessorGraphArtifact = (report, options = {}) => buildArtifactPair(report, summarizeMediaProcessorGraphReport(report), renderMediaProcessorGraphMarkdown(report, options), options);
13471
- var writeMediaArtifact = async (input) => {
13472
- await mkdir(input.dir, { recursive: true });
13473
- const jsonPath = join(input.dir, `${input.slug}.json`);
13474
- const markdownPath = join(input.dir, `${input.slug}.md`);
13475
- await Promise.all([
13476
- writeFile(jsonPath, input.json, "utf8"),
13477
- writeFile(markdownPath, input.markdown, "utf8")
13478
- ]);
13479
- return {
13480
- jsonPath,
13481
- markdownPath,
13482
- summary: input.summary
13483
- };
13484
- };
13485
-
13486
- // src/mediaPipelineRoutes.ts
12558
+ import {
12559
+ buildMediaInterruptionReport,
12560
+ buildMediaPipelineCalibrationReport,
12561
+ buildMediaQualityReport,
12562
+ buildMediaResamplingPlan,
12563
+ buildMediaVadReport,
12564
+ summarizeMediaProcessorGraphReport,
12565
+ summarizeMediaQualityReport,
12566
+ summarizeMediaTransportReport
12567
+ } from "@absolutejs/media";
13487
12568
  var escapeHtml16 = (value) => String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
13488
12569
  var statusRank2 = {
13489
12570
  pass: 0,
@@ -13733,7 +12814,7 @@ var summarizeVoiceMediaPipelineReport = (report, options = {}) => {
13733
12814
  const processorGraph = report.processorGraph ? summarizeMediaProcessorGraphReport(report.processorGraph) : undefined;
13734
12815
  const transport = report.transport ? summarizeMediaTransportReport(report.transport) : undefined;
13735
12816
  const interruptionIssueCodes = collectIssueCodes(report.interruption.issues);
13736
- const issueCodes2 = Array.from(new Set([
12817
+ const issueCodes = Array.from(new Set([
13737
12818
  ...collectIssueCodes(calibration.issues),
13738
12819
  ...quality.issueCodes,
13739
12820
  ...interruptionIssueCodes,
@@ -13768,7 +12849,7 @@ var summarizeVoiceMediaPipelineReport = (report, options = {}) => {
13768
12849
  latenciesMs: report.interruption.latenciesMs,
13769
12850
  status: report.interruption.status
13770
12851
  },
13771
- issueCodes: issueCodes2,
12852
+ issueCodes,
13772
12853
  issues,
13773
12854
  ok: report.ok,
13774
12855
  processorGraph,
@@ -13791,6 +12872,12 @@ var summarizeVoiceMediaPipelineReport = (report, options = {}) => {
13791
12872
  };
13792
12873
  // src/telephonyMediaRoutes.ts
13793
12874
  import { Elysia as Elysia14 } from "elysia";
12875
+ import {
12876
+ buildMediaTelephonyStreamLifecycleReport,
12877
+ createTelephonyMediaSerializer,
12878
+ parseTelephonyMediaFrame,
12879
+ serializeTelephonyMediaFrame
12880
+ } from "@absolutejs/media";
13794
12881
  var demoPayload = Buffer.from(new Uint8Array([1, 2, 3, 4])).toString("base64");
13795
12882
  var demoEnvelope = (carrier) => {
13796
12883
  if (carrier === "twilio") {
@@ -14294,6 +13381,12 @@ var createVoiceBrowserCallProfileRoutes = (options = {}) => {
14294
13381
  return routes;
14295
13382
  };
14296
13383
  // src/mediaPipelineSurfaces.ts
13384
+ import {
13385
+ buildMediaProcessorGraphArtifact,
13386
+ buildMediaQualityArtifact,
13387
+ buildMediaTransportArtifact,
13388
+ writeMediaArtifact
13389
+ } from "@absolutejs/media";
14297
13390
  var calibrationIssues = (report) => report.calibration.issues.map((issue) => ({
14298
13391
  code: issue.code,
14299
13392
  message: issue.message,
@@ -20219,7 +19312,7 @@ import { Elysia as Elysia27 } from "elysia";
20219
19312
  var escapeHtml28 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
20220
19313
  var getString10 = (value) => typeof value === "string" && value.trim() ? value : undefined;
20221
19314
  var getNumber7 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
20222
- var firstString3 = (payload, keys) => {
19315
+ var firstString2 = (payload, keys) => {
20223
19316
  for (const key of keys) {
20224
19317
  const value = getString10(payload[key]);
20225
19318
  if (value) {
@@ -20228,7 +19321,7 @@ var firstString3 = (payload, keys) => {
20228
19321
  }
20229
19322
  return;
20230
19323
  };
20231
- var firstNumber2 = (payload, keys) => {
19324
+ var firstNumber = (payload, keys) => {
20232
19325
  for (const key of keys) {
20233
19326
  const value = getNumber7(payload[key]);
20234
19327
  if (value !== undefined) {
@@ -20237,20 +19330,20 @@ var firstNumber2 = (payload, keys) => {
20237
19330
  }
20238
19331
  return;
20239
19332
  };
20240
- var eventProvider = (event) => firstString3(event.payload, [
19333
+ var eventProvider = (event) => firstString2(event.payload, [
20241
19334
  "provider",
20242
19335
  "selectedProvider",
20243
19336
  "fallbackProvider",
20244
19337
  "variantId"
20245
19338
  ]);
20246
- var eventStatus = (event) => firstString3(event.payload, [
19339
+ var eventStatus = (event) => firstString2(event.payload, [
20247
19340
  "providerStatus",
20248
19341
  "status",
20249
19342
  "disposition",
20250
19343
  "type",
20251
19344
  "reason"
20252
19345
  ]);
20253
- var eventElapsedMs = (event) => firstNumber2(event.payload, ["elapsedMs", "latencyMs", "durationMs"]);
19346
+ var eventElapsedMs = (event) => firstNumber(event.payload, ["elapsedMs", "latencyMs", "durationMs"]);
20254
19347
  var resolveSessionHref2 = (value, sessionId) => {
20255
19348
  if (value === false) {
20256
19349
  return;
@@ -22349,8 +21442,8 @@ var createVoiceLiveOpsRoutes = (options = {}) => {
22349
21442
  };
22350
21443
  // src/deliveryRuntime.ts
22351
21444
  import { Elysia as Elysia32 } from "elysia";
22352
- import { mkdir as mkdir2 } from "fs/promises";
22353
- import { dirname, join as join2 } from "path";
21445
+ import { mkdir } from "fs/promises";
21446
+ import { dirname, join } from "path";
22354
21447
  var escapeHtml32 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
22355
21448
  var renderSummaryCard = (label, summary) => {
22356
21449
  if (!summary) {
@@ -22375,8 +21468,8 @@ var createDeliveryRuntimeFileSink = (input) => ({
22375
21468
  deliver: async ({ events }) => {
22376
21469
  const firstEvent = events[0];
22377
21470
  const fileName = `${Date.now()}-${encodeURIComponent(firstEvent?.id ?? crypto.randomUUID())}.json`;
22378
- const path = join2(input.directory, input.kind, fileName);
22379
- await mkdir2(dirname(path), { recursive: true });
21471
+ const path = join(input.directory, input.kind, fileName);
21472
+ await mkdir(dirname(path), { recursive: true });
22380
21473
  await Bun.write(path, JSON.stringify({
22381
21474
  eventCount: events.length,
22382
21475
  events,
@@ -23347,7 +22440,7 @@ var assertVoiceTelephonyWebhookNormalizationEvidence = (input = {}) => {
23347
22440
  return assertion;
23348
22441
  };
23349
22442
  var normalizeToken = (value) => typeof value === "string" ? value.trim().toLowerCase().replace(/\s+/g, "-").replace(/_+/g, "-") : undefined;
23350
- var firstString4 = (source, keys) => {
22443
+ var firstString3 = (source, keys) => {
23351
22444
  for (const key of keys) {
23352
22445
  const value = source[key];
23353
22446
  if (typeof value === "string" && value.trim()) {
@@ -23358,7 +22451,7 @@ var firstString4 = (source, keys) => {
23358
22451
  }
23359
22452
  }
23360
22453
  };
23361
- var firstNumber3 = (source, keys) => {
22454
+ var firstNumber2 = (source, keys) => {
23362
22455
  for (const key of keys) {
23363
22456
  const value = source[key];
23364
22457
  if (typeof value === "number" && Number.isFinite(value)) {
@@ -23723,8 +22816,8 @@ var verifyVoiceTelephonyWebhook = async (input) => {
23723
22816
  var durationMsFromSeconds = (value) => typeof value === "number" ? value * 1000 : undefined;
23724
22817
  var parseVoiceTelephonyWebhookEvent = (input) => {
23725
22818
  const payload = flattenPayload(input.body);
23726
- const provider = firstString4(payload, ["provider", "Provider"]) ?? input.provider;
23727
- const status = firstString4(payload, [
22819
+ const provider = firstString3(payload, ["provider", "Provider"]) ?? input.provider;
22820
+ const status = firstString3(payload, [
23728
22821
  "CallStatus",
23729
22822
  "call_status",
23730
22823
  "callStatus",
@@ -23734,7 +22827,7 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
23734
22827
  "event_type",
23735
22828
  "type"
23736
22829
  ]);
23737
- const durationMs = firstNumber3(payload, ["durationMs", "duration_ms"]) ?? durationMsFromSeconds(firstNumber3(payload, [
22830
+ const durationMs = firstNumber2(payload, ["durationMs", "duration_ms"]) ?? durationMsFromSeconds(firstNumber2(payload, [
23738
22831
  "CallDuration",
23739
22832
  "call_duration",
23740
22833
  "callDuration",
@@ -23742,21 +22835,21 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
23742
22835
  "dial_call_duration",
23743
22836
  "duration"
23744
22837
  ]));
23745
- const sipCode = firstNumber3(payload, [
22838
+ const sipCode = firstNumber2(payload, [
23746
22839
  "SipResponseCode",
23747
22840
  "sip_response_code",
23748
22841
  "sipCode",
23749
22842
  "sip_code",
23750
22843
  "hangupCauseCode"
23751
22844
  ]);
23752
- const from = firstString4(payload, ["From", "from", "caller_id", "callerId"]);
23753
- const to = firstString4(payload, [
22845
+ const from = firstString3(payload, ["From", "from", "caller_id", "callerId"]);
22846
+ const to = firstString3(payload, [
23754
22847
  "To",
23755
22848
  "to",
23756
22849
  "called_number",
23757
22850
  "calledNumber"
23758
22851
  ]);
23759
- const target = firstString4(payload, [
22852
+ const target = firstString3(payload, [
23760
22853
  "transferTarget",
23761
22854
  "TransferTarget",
23762
22855
  "target",
@@ -23764,7 +22857,7 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
23764
22857
  "department"
23765
22858
  ]);
23766
22859
  return {
23767
- answeredBy: firstString4(payload, [
22860
+ answeredBy: firstString3(payload, [
23768
22861
  "AnsweredBy",
23769
22862
  "answered_by",
23770
22863
  "answeredBy",
@@ -23778,7 +22871,7 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
23778
22871
  ...payload
23779
22872
  },
23780
22873
  provider,
23781
- reason: firstString4(payload, [
22874
+ reason: firstString3(payload, [
23782
22875
  "Reason",
23783
22876
  "reason",
23784
22877
  "HangupCause",
@@ -23794,7 +22887,7 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
23794
22887
  var defaultSessionId = (input) => {
23795
22888
  const payload = flattenPayload(input.body);
23796
22889
  const metadataSessionId = input.event.metadata?.sessionId;
23797
- return firstString4(input.query, ["sessionId", "session_id"]) ?? firstString4(payload, [
22890
+ return firstString3(input.query, ["sessionId", "session_id"]) ?? firstString3(payload, [
23798
22891
  "sessionId",
23799
22892
  "session_id",
23800
22893
  "SessionId",
@@ -23809,7 +22902,7 @@ var defaultSessionId = (input) => {
23809
22902
  };
23810
22903
  var defaultIdempotencyKey = (input) => {
23811
22904
  const payload = flattenPayload(input.body);
23812
- const eventId = firstString4(payload, [
22905
+ const eventId = firstString3(payload, [
23813
22906
  "id",
23814
22907
  "event_id",
23815
22908
  "eventId",
@@ -27577,7 +26670,7 @@ var percentile5 = (values, percentileValue) => {
27577
26670
  const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(percentileValue / 100 * sorted.length) - 1));
27578
26671
  return Math.round(sorted[index] ?? 0);
27579
26672
  };
27580
- var average2 = (values) => values.length === 0 ? undefined : Math.round(values.reduce((total, value) => total + value, 0) / values.length);
26673
+ var average = (values) => values.length === 0 ? undefined : Math.round(values.reduce((total, value) => total + value, 0) / values.length);
27581
26674
  var resolveBudget = (stage, options) => ({
27582
26675
  failAfterMs: options.budgets?.[stage]?.failAfterMs ?? options.failAfterMs ?? DEFAULT_FAIL_AFTER_MS,
27583
26676
  warnAfterMs: options.budgets?.[stage]?.warnAfterMs ?? options.warnAfterMs ?? DEFAULT_WARN_AFTER_MS
@@ -27771,7 +26864,7 @@ var summarizeStage = (stage, measurements, options) => {
27771
26864
  const failed = stageMeasurements.filter((measurement) => measurement.status === "fail").length;
27772
26865
  const warnings = stageMeasurements.filter((measurement) => measurement.status === "warn").length;
27773
26866
  return {
27774
- averageMs: average2(latencies),
26867
+ averageMs: average(latencies),
27775
26868
  budget: resolveBudget(stage, options),
27776
26869
  failed,
27777
26870
  label: STAGE_LABELS[stage],
@@ -28881,8 +27974,8 @@ var createVoiceIncidentTimelineRoutes = (options) => {
28881
27974
  import { Elysia as Elysia45 } from "elysia";
28882
27975
  import { Database as Database4 } from "bun:sqlite";
28883
27976
  import { createHash } from "crypto";
28884
- import { mkdir as mkdir3, readFile, stat, unlink } from "fs/promises";
28885
- import { join as join3 } from "path";
27977
+ import { mkdir as mkdir2, readFile, stat, unlink } from "fs/promises";
27978
+ import { join as join2 } from "path";
28886
27979
  var voiceObservabilityExportSchemaVersion = "1.0.0";
28887
27980
  var voiceObservabilityExportSchemaId = "com.absolutejs.voice.observability-export";
28888
27981
  var createVoiceObservabilityExportSchema = () => ({
@@ -29457,15 +28550,15 @@ var loadVoiceObservabilityExportReplaySource = async (source) => {
29457
28550
  return source;
29458
28551
  }
29459
28552
  if (source.kind === "file") {
29460
- const root = join3(source.directory, source.runId);
29461
- const receiptPath = source.receiptDirectory ? join3(source.receiptDirectory, `${encodeURIComponent(deliveryReceiptId(source.runId))}.json`) : undefined;
28553
+ const root = join2(source.directory, source.runId);
28554
+ const receiptPath = source.receiptDirectory ? join2(source.receiptDirectory, `${encodeURIComponent(deliveryReceiptId(source.runId))}.json`) : undefined;
29462
28555
  const deliveryReceipt = receiptPath ? await Bun.file(receiptPath).text().then(JSON.parse).catch(() => {
29463
28556
  return;
29464
28557
  }) : undefined;
29465
28558
  return {
29466
- artifactIndex: JSON.parse(await Bun.file(join3(root, "artifact-index.json")).text()),
28559
+ artifactIndex: JSON.parse(await Bun.file(join2(root, "artifact-index.json")).text()),
29467
28560
  deliveryReceipt,
29468
- manifest: JSON.parse(await Bun.file(join3(root, "manifest.json")).text())
28561
+ manifest: JSON.parse(await Bun.file(join2(root, "manifest.json")).text())
29469
28562
  };
29470
28563
  }
29471
28564
  if (source.kind === "s3") {
@@ -29675,7 +28768,7 @@ var createVoiceMemoryObservabilityExportDeliveryReceiptStore = () => {
29675
28768
  };
29676
28769
  };
29677
28770
  var createVoiceFileObservabilityExportDeliveryReceiptStore = (options) => {
29678
- const receiptPath = (id) => join3(options.directory, `${encodeURIComponent(id)}.json`);
28771
+ const receiptPath = (id) => join2(options.directory, `${encodeURIComponent(id)}.json`);
29679
28772
  return {
29680
28773
  get: async (id) => {
29681
28774
  const file = Bun.file(receiptPath(id));
@@ -29685,10 +28778,10 @@ var createVoiceFileObservabilityExportDeliveryReceiptStore = (options) => {
29685
28778
  return JSON.parse(await file.text());
29686
28779
  },
29687
28780
  list: async () => {
29688
- await mkdir3(options.directory, { recursive: true });
28781
+ await mkdir2(options.directory, { recursive: true });
29689
28782
  const receipts = [];
29690
28783
  for (const entry of await Array.fromAsync(new Bun.Glob("*.json").scan(options.directory))) {
29691
- const file = Bun.file(join3(options.directory, entry));
28784
+ const file = Bun.file(join2(options.directory, entry));
29692
28785
  receipts.push(JSON.parse(await file.text()));
29693
28786
  }
29694
28787
  return receipts.sort((left, right) => right.checkedAt - left.checkedAt);
@@ -29699,7 +28792,7 @@ var createVoiceFileObservabilityExportDeliveryReceiptStore = (options) => {
29699
28792
  });
29700
28793
  },
29701
28794
  set: async (id, receipt) => {
29702
- await mkdir3(options.directory, { recursive: true });
28795
+ await mkdir2(options.directory, { recursive: true });
29703
28796
  await Bun.write(receiptPath(id), `${JSON.stringify(receipt, null, 2)}
29704
28797
  `);
29705
28798
  }
@@ -30205,16 +29298,16 @@ var deliverVoiceObservabilityExport = async (options) => {
30205
29298
  const label = destination.label ?? (destination.kind === "file" ? "File observability export" : destination.kind === "s3" ? "S3 observability export" : destination.kind === "sqlite" ? "SQLite observability export" : destination.kind === "postgres" ? "Postgres observability export" : "Webhook observability export");
30206
29299
  try {
30207
29300
  if (destination.kind === "file") {
30208
- const target = join3(destination.directory, runId);
30209
- await mkdir3(join3(target, "artifacts"), { recursive: true });
30210
- await Bun.write(join3(target, "manifest.json"), manifest);
30211
- await Bun.write(join3(target, "artifact-index.json"), index);
29301
+ const target = join2(destination.directory, runId);
29302
+ await mkdir2(join2(target, "artifacts"), { recursive: true });
29303
+ await Bun.write(join2(target, "manifest.json"), manifest);
29304
+ await Bun.write(join2(target, "artifact-index.json"), index);
30212
29305
  if (destination.includeArtifacts !== false) {
30213
29306
  for (const artifact of options.report.artifacts) {
30214
29307
  if (!artifact.path) {
30215
29308
  continue;
30216
29309
  }
30217
- await Bun.write(join3(target, "artifacts", safeArtifactFileName(artifact)), await readFile(stripArtifactPathAnchor(artifact.path)));
29310
+ await Bun.write(join2(target, "artifacts", safeArtifactFileName(artifact)), await readFile(stripArtifactPathAnchor(artifact.path)));
30218
29311
  }
30219
29312
  }
30220
29313
  return {
@@ -33446,7 +32539,7 @@ var createVoiceDataControlRoutes = (options) => {
33446
32539
  };
33447
32540
  // src/evalRoutes.ts
33448
32541
  import { Elysia as Elysia49 } from "elysia";
33449
- import { mkdir as mkdir4 } from "fs/promises";
32542
+ import { mkdir as mkdir3 } from "fs/promises";
33450
32543
  import { dirname as dirname2 } from "path";
33451
32544
  var escapeHtml46 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
33452
32545
  var rate4 = (count, total) => count / Math.max(1, total);
@@ -33758,7 +32851,7 @@ var createVoiceFileEvalBaselineStore = (filePath) => ({
33758
32851
  return text.trim() ? JSON.parse(text) : undefined;
33759
32852
  },
33760
32853
  set: async (report) => {
33761
- await mkdir4(dirname2(filePath), { recursive: true });
32854
+ await mkdir3(dirname2(filePath), { recursive: true });
33762
32855
  await Bun.write(filePath, JSON.stringify(report, null, 2));
33763
32856
  }
33764
32857
  });
@@ -35649,7 +34742,7 @@ import { Elysia as Elysia53 } from "elysia";
35649
34742
  var DEFAULT_WARN_AFTER_MS2 = 1800;
35650
34743
  var DEFAULT_FAIL_AFTER_MS2 = 3200;
35651
34744
  var escapeHtml50 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
35652
- var firstNumber4 = (values) => values.filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
34745
+ var firstNumber3 = (values) => values.filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
35653
34746
  var getString19 = (value) => typeof value === "string" && value.trim() ? value : undefined;
35654
34747
  var createTraceStageIndex = (events) => {
35655
34748
  const index = new Map;
@@ -35673,8 +34766,8 @@ var createTraceStageIndex = (events) => {
35673
34766
  };
35674
34767
  var summarizeTurn = (sessionId, turn, options) => {
35675
34768
  const traceStages = options.stageIndex?.get(`${sessionId}:${turn.id}`);
35676
- const firstTranscriptAt = traceStages?.get("speech_detected") ?? firstNumber4(turn.transcripts.map((transcript) => transcript.endedAtMs ?? transcript.startedAtMs));
35677
- const finalTranscriptAt = traceStages?.get("final_transcript") ?? firstNumber4(turn.transcripts.filter((transcript) => transcript.isFinal).map((transcript) => transcript.endedAtMs ?? transcript.startedAtMs));
34769
+ const firstTranscriptAt = traceStages?.get("speech_detected") ?? firstNumber3(turn.transcripts.map((transcript) => transcript.endedAtMs ?? transcript.startedAtMs));
34770
+ const finalTranscriptAt = traceStages?.get("final_transcript") ?? firstNumber3(turn.transcripts.filter((transcript) => transcript.isFinal).map((transcript) => transcript.endedAtMs ?? transcript.startedAtMs));
35678
34771
  const committedAt = traceStages?.get("turn_committed") ?? turn.committedAt;
35679
34772
  const assistantTextStartedAt = traceStages?.get("assistant_text_started") ?? (turn.assistantText ? committedAt : undefined);
35680
34773
  const ttsSendStartedAt = traceStages?.get("tts_send_started");
@@ -36589,21 +35682,21 @@ var createVoicePhoneAgent = (options) => {
36589
35682
  };
36590
35683
  // src/fileStore.ts
36591
35684
  import {
36592
- mkdir as mkdir5,
35685
+ mkdir as mkdir4,
36593
35686
  readFile as readFile2,
36594
35687
  readdir,
36595
35688
  rename,
36596
35689
  rm,
36597
35690
  stat as stat2,
36598
- writeFile as writeFile2
35691
+ writeFile
36599
35692
  } from "fs/promises";
36600
- import { join as join4 } from "path";
35693
+ import { join as join3 } from "path";
36601
35694
  var listJsonFiles = async (directory) => {
36602
35695
  try {
36603
35696
  const entries = await readdir(directory, {
36604
35697
  withFileTypes: true
36605
35698
  });
36606
- return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => join4(directory, entry.name));
35699
+ return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => join3(directory, entry.name));
36607
35700
  } catch (error) {
36608
35701
  if (error.code === "ENOENT") {
36609
35702
  return [];
@@ -36621,7 +35714,7 @@ var listRecentJsonFiles = async (directory, limit) => {
36621
35714
  }
36622
35715
  return (await rebuildRecentJsonFileIndex(directory)).slice(0, limit).map((entry) => entry.path);
36623
35716
  };
36624
- var recentJsonFileIndexPath = (directory) => join4(directory, ".recent-index");
35717
+ var recentJsonFileIndexPath = (directory) => join3(directory, ".recent-index");
36625
35718
  var sortRecentJsonFileIndexEntries = (entries) => entries.sort((left, right) => right.updatedAt - left.updatedAt);
36626
35719
  var readRecentJsonFileIndex = async (directory) => {
36627
35720
  try {
@@ -36635,12 +35728,12 @@ var readRecentJsonFileIndex = async (directory) => {
36635
35728
  }
36636
35729
  };
36637
35730
  var writeRecentJsonFileIndex = async (directory, files) => {
36638
- await mkdir5(directory, {
35731
+ await mkdir4(directory, {
36639
35732
  recursive: true
36640
35733
  });
36641
35734
  const path = recentJsonFileIndexPath(directory);
36642
35735
  const tempPath = `${path}.${crypto.randomUUID()}.tmp`;
36643
- await writeFile2(tempPath, JSON.stringify({
35736
+ await writeFile(tempPath, JSON.stringify({
36644
35737
  files: sortRecentJsonFileIndexEntries(files).slice(0, 5000),
36645
35738
  version: 1
36646
35739
  }));
@@ -36716,15 +35809,15 @@ var omitReadWindow = (filter) => {
36716
35809
  return next;
36717
35810
  };
36718
35811
  var encodeStoreId = (id) => `${encodeURIComponent(id)}.json`;
36719
- var resolveFilePath = (directory, id) => join4(directory, encodeStoreId(id));
35812
+ var resolveFilePath = (directory, id) => join3(directory, encodeStoreId(id));
36720
35813
  var createMemoryStoreId = (input) => `${input.assistantId}:${input.namespace}:${input.key}`;
36721
35814
  var readJsonFile = async (path) => JSON.parse(await readFile2(path, "utf8"));
36722
35815
  var writeJsonFile = async (path, value, options) => {
36723
- await mkdir5(options.directory, {
35816
+ await mkdir4(options.directory, {
36724
35817
  recursive: true
36725
35818
  });
36726
35819
  const tempPath = `${path}.${crypto.randomUUID()}.tmp`;
36727
- await writeFile2(tempPath, JSON.stringify(value, null, options.pretty === false ? undefined : 2));
35820
+ await writeFile(tempPath, JSON.stringify(value, null, options.pretty === false ? undefined : 2));
36728
35821
  await rename(tempPath, path);
36729
35822
  };
36730
35823
  var createVoiceFileSessionStore = (options) => {
@@ -37098,51 +36191,51 @@ var createVoiceFileIncidentBundleStore = (options) => {
37098
36191
  var createVoiceFileRuntimeStorage = (options) => ({
37099
36192
  audit: createVoiceFileAuditEventStore({
37100
36193
  ...options,
37101
- directory: join4(options.directory, "audit")
36194
+ directory: join3(options.directory, "audit")
37102
36195
  }),
37103
36196
  auditDeliveries: createVoiceFileAuditSinkDeliveryStore({
37104
36197
  ...options,
37105
- directory: join4(options.directory, "audit-deliveries")
36198
+ directory: join3(options.directory, "audit-deliveries")
37106
36199
  }),
37107
36200
  campaigns: createVoiceFileCampaignStore({
37108
36201
  ...options,
37109
- directory: join4(options.directory, "campaigns")
36202
+ directory: join3(options.directory, "campaigns")
37110
36203
  }),
37111
36204
  events: createVoiceFileIntegrationEventStore({
37112
36205
  ...options,
37113
- directory: join4(options.directory, "events")
36206
+ directory: join3(options.directory, "events")
37114
36207
  }),
37115
36208
  externalObjects: createVoiceFileExternalObjectMapStore({
37116
36209
  ...options,
37117
- directory: join4(options.directory, "external-objects")
36210
+ directory: join3(options.directory, "external-objects")
37118
36211
  }),
37119
36212
  incidentBundles: createVoiceFileIncidentBundleStore({
37120
36213
  ...options,
37121
- directory: join4(options.directory, "incident-bundles")
36214
+ directory: join3(options.directory, "incident-bundles")
37122
36215
  }),
37123
36216
  memories: createVoiceFileAssistantMemoryStore({
37124
36217
  ...options,
37125
- directory: join4(options.directory, "memories")
36218
+ directory: join3(options.directory, "memories")
37126
36219
  }),
37127
36220
  reviews: createVoiceFileReviewStore({
37128
36221
  ...options,
37129
- directory: join4(options.directory, "reviews")
36222
+ directory: join3(options.directory, "reviews")
37130
36223
  }),
37131
36224
  session: createVoiceFileSessionStore({
37132
36225
  ...options,
37133
- directory: join4(options.directory, "sessions")
36226
+ directory: join3(options.directory, "sessions")
37134
36227
  }),
37135
36228
  tasks: createVoiceFileTaskStore({
37136
36229
  ...options,
37137
- directory: join4(options.directory, "tasks")
36230
+ directory: join3(options.directory, "tasks")
37138
36231
  }),
37139
36232
  traceDeliveries: createVoiceFileTraceSinkDeliveryStore({
37140
36233
  ...options,
37141
- directory: join4(options.directory, "trace-deliveries")
36234
+ directory: join3(options.directory, "trace-deliveries")
37142
36235
  }),
37143
36236
  traces: createVoiceFileTraceEventStore({
37144
36237
  ...options,
37145
- directory: join4(options.directory, "traces")
36238
+ directory: join3(options.directory, "traces")
37146
36239
  })
37147
36240
  });
37148
36241
  var createStoredVoiceCallReviewArtifact = (id, artifact) => withVoiceCallReviewId(id, artifact);
@@ -39541,7 +38634,7 @@ var statusRank8 = {
39541
38634
  warn: 1,
39542
38635
  fail: 2
39543
38636
  };
39544
- var statusExceeds2 = (actual, max2) => statusRank8[actual] > statusRank8[max2];
38637
+ var statusExceeds2 = (actual, max) => statusRank8[actual] > statusRank8[max];
39545
38638
  var buildVoiceProviderContractMatrix = (input) => {
39546
38639
  const rows = input.contracts.map((contract) => {
39547
38640
  const configured = contract.configured !== false;
@@ -42981,8 +42074,8 @@ var shapeTelephonyAssistantText = (text, options = {}) => {
42981
42074
  };
42982
42075
  // src/proofPack.ts
42983
42076
  import { Elysia as Elysia69 } from "elysia";
42984
- import { mkdir as mkdir6 } from "fs/promises";
42985
- import { dirname as dirname3, join as join5 } from "path";
42077
+ import { mkdir as mkdir5 } from "fs/promises";
42078
+ import { dirname as dirname3, join as join4 } from "path";
42986
42079
  var toGeneratedAt = (value) => value === undefined ? new Date().toISOString() : typeof value === "number" ? new Date(value).toISOString() : value;
42987
42080
  var getProofPackMetadata = (proofPack) => {
42988
42081
  const built = buildVoiceProofPack(proofPack);
@@ -43511,10 +42604,10 @@ var writeVoiceProofPack = async (input, options = { outputDir: ".voice-runtime/p
43511
42604
  ...input,
43512
42605
  outputDir: options.outputDir
43513
42606
  });
43514
- const jsonPath = join5(options.outputDir, options.jsonFileName ?? "latest.json");
43515
- const markdownPath = join5(options.outputDir, options.markdownFileName ?? "latest.md");
43516
- await mkdir6(dirname3(jsonPath), { recursive: true });
43517
- await mkdir6(dirname3(markdownPath), { recursive: true });
42607
+ const jsonPath = join4(options.outputDir, options.jsonFileName ?? "latest.json");
42608
+ const markdownPath = join4(options.outputDir, options.markdownFileName ?? "latest.md");
42609
+ await mkdir5(dirname3(jsonPath), { recursive: true });
42610
+ await mkdir5(dirname3(markdownPath), { recursive: true });
43518
42611
  await Promise.all([
43519
42612
  Bun.write(jsonPath, JSON.stringify(proofPack, null, 2)),
43520
42613
  Bun.write(markdownPath, renderVoiceProofPackMarkdown(proofPack))