@absolutejs/voice 0.0.22-beta.450 → 0.0.22-beta.452
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/client/htmxBootstrap.js +4 -1
- package/dist/client/index.js +91 -4
- package/dist/generated/htmxBootstrapBundle.d.ts +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +94 -5
- package/dist/proofTrends.d.ts +29 -0
- package/dist/react/index.js +91 -4
- package/dist/vue/index.js +91 -4
- package/package.json +1 -1
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 Ue=(e)=>{if(typeof e!=="string")return e;return document.querySelector(e)},Ne=(e,n,t,o)=>{let r=n??e.getAttribute("hx-get")??"";if(!r)return"";let i=new URL(r,window.location.origin);if(o)i.searchParams.set(t,o);else i.searchParams.delete(t);return`${i.pathname}${i.search}${i.hash}`},de=(e,n)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let t=Ue(n.element);if(!t)return()=>{};let o=n.eventName??"voice-refresh",r=n.sessionQueryParam??"sessionId",i=()=>{let l=window,g=Ne(t,n.route,r,e.sessionId);if(g)t.setAttribute("hx-get",g);l.htmx?.process?.(t),l.htmx?.trigger?.(t,o)},c=e.subscribe(i);return i(),()=>{c()}};var He=(e)=>Math.max(-1,Math.min(1,e)),Ge=(e)=>{let n=new Int16Array(e.length);for(let t=0;t<e.length;t+=1){let o=He(e[t]??0);n[t]=o<0?o*32768:o*32767}return new Uint8Array(n.buffer)},Be=(e)=>{let n=e instanceof Uint8Array?e:new Uint8Array(e);if(n.byteLength<2)return 0;let t=new Int16Array(n.buffer,n.byteOffset,Math.floor(n.byteLength/2));if(t.length===0)return 0;let o=0;for(let r of t){let i=r/32768;o+=i*i}return Math.min(1,Math.max(0,Math.sqrt(o/t.length)*5.5))},We=(e,n,t)=>{if(n===t)return e;let o=n/t,r=Math.round(e.length/o),i=new Float32Array(r),c=0,l=0;while(c<i.length){let g=Math.round((c+1)*o),y=0,d=0;for(let h=l;h<g&&h<e.length;h+=1)y+=e[h]??0,d+=1;i[c]=d>0?y/d:0,c+=1,l=g}return i},ge=(e)=>{let n=null,t=null,o=null,r=null;return{start:async()=>{if(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia)throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let l=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!l)throw Error("Browser microphone capture requires AudioContext support.");r=await navigator.mediaDevices.getUserMedia({audio:{channelCount:e.channelCount??1}}),n=new l,t=n.createMediaStreamSource(r),o=n.createScriptProcessor(4096,1,1),o.onaudioprocess=(g)=>{let y=g.inputBuffer.getChannelData(0),d=We(y,n?.sampleRate??48000,e.sampleRateHz??16000),h=Ge(d);e.onLevel?.(Be(h)),e.onAudio(h)},t.connect(o),o.connect(n.destination)},stop:()=>{o?.disconnect(),t?.disconnect(),r?.getTracks().forEach((l)=>l.stop()),n?.close(),e.onLevel?.(0),n=null,r=null,o=null,t=null}}};var ee=(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 t of["message","reason","description"]){let o=n[t];if(typeof o==="string"&&o.trim())return o}if("error"in n)return ee(n.error);if("cause"in n)return ee(n.cause);try{return JSON.stringify(e)}catch{}}return"Unexpected error"},Ae=(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:ee(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 H=(e,n,t,o)=>{e.push({code:t,message:o,severity:n})};var $e=(e)=>e.length===0?void 0:e.reduce((n,t)=>n+t,0)/e.length,K=(e)=>e.length===0?void 0:Math.max(...e);var b=(e,n)=>{let t=e[n];return typeof t==="number"&&Number.isFinite(t)?t:void 0},Z=(e,n)=>{let t=e[n];return typeof t==="boolean"?t:void 0},D=(e,n)=>{let t=e[n];return typeof t==="string"?t:void 0},ne=(e)=>String(e.id??D(e,"ssrc")??b(e,"ssrc")??D(e,"trackIdentifier")??D(e,"mid")??"unknown"),he=(e)=>e===void 0?void 0:e*1000;var ke=(e)=>{let n={};for(let[t,o]of Object.entries(e))if(o===null||typeof o==="boolean"||typeof o==="number"||typeof o==="string")n[t]=o;return n};var ye=(e={})=>{let n=e.stats??[],t=[],o=n.filter((s)=>s.type==="inbound-rtp"&&D(s,"kind")!=="video"),r=n.filter((s)=>s.type==="outbound-rtp"&&D(s,"kind")!=="video"),i=n.filter((s)=>s.type==="candidate-pair"),c=n.filter((s)=>(s.type==="track"||s.type==="media-source")&&D(s,"kind")==="audio"),l=i.filter((s)=>Z(s,"selected")===!0||Z(s,"nominated")===!0||D(s,"state")==="succeeded").length,g=c.filter((s)=>D(s,"readyState")!=="ended"&&D(s,"trackState")!=="ended"&&Z(s,"ended")!==!0).length,y=c.filter((s)=>D(s,"readyState")==="ended"||D(s,"trackState")==="ended"||Z(s,"ended")===!0).length,d=o.reduce((s,S)=>s+(b(S,"packetsReceived")??0),0),h=r.reduce((s,S)=>s+(b(S,"packetsSent")??0),0),a=[...o,...r].reduce((s,S)=>s+Math.max(0,b(S,"packetsLost")??0),0),M=d+a,C=M===0?0:a/M,I=o.reduce((s,S)=>s+(b(S,"bytesReceived")??0),0),w=r.reduce((s,S)=>s+(b(S,"bytesSent")??0),0),R=K(i.map((s)=>he(b(s,"currentRoundTripTime")??b(s,"roundTripTime"))).filter((s)=>s!==void 0)),u=K([...o,...r].map((s)=>he(b(s,"jitter"))).filter((s)=>s!==void 0)),O=K(o.map((s)=>{let S=b(s,"jitterBufferDelay"),L=b(s,"jitterBufferEmittedCount");return S!==void 0&&L!==void 0&&L>0?S/L*1000:void 0}).filter((s)=>s!==void 0)),U=c.map((s)=>b(s,"audioLevel")).filter((s)=>s!==void 0);if(e.requireConnectedCandidatePair&&i.length>0&&l===0)H(t,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(e.requireLiveAudioTrack&&g===0)H(t,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(e.maxPacketLossRatio!==void 0&&C>e.maxPacketLossRatio)H(t,"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)H(t,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(R)}ms above ${String(e.maxRoundTripTimeMs)}ms.`);if(e.maxJitterMs!==void 0&&u!==void 0&&u>e.maxJitterMs)H(t,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(u)}ms above ${String(e.maxJitterMs)}ms.`);return{activeCandidatePairs:l,audioLevelAverage:$e(U),bytesReceived:I,bytesSent:w,checkedAt:Date.now(),endedAudioTracks:y,inboundPackets:d,issues:t,jitterBufferDelayMs:O,jitterMs:u,liveAudioTracks:g,outboundPackets:h,packetLossRatio:C,packetsLost:a,roundTripTimeMs:R,status:t.some((s)=>s.severity==="error")?"fail":t.length>0?"warn":"pass",totalStats:n.length}},Ce=async(e)=>{return[...(await e.peerConnection.getStats(e.selector??null)).values()].map(ke)};var fe=(e={})=>{let n=e.stats??[],t=e.previousStats??[],o=[],r=new Map(t.map((a)=>[ne(a),a])),c=n.filter((a)=>(a.type==="inbound-rtp"||a.type==="outbound-rtp")&&D(a,"kind")!=="video"&&D(a,"mediaType")!=="video").map((a)=>{let M=a.type==="outbound-rtp"?"outbound":"inbound",C=M==="outbound"?"packetsSent":"packetsReceived",I=M==="outbound"?"bytesSent":"bytesReceived",w=r.get(ne(a)),R=b(a,C),u=w?b(w,C):void 0,O=b(a,I),U=w?b(w,I):void 0,s=a.timestamp!==void 0&&w?.timestamp!==void 0?a.timestamp-w.timestamp:void 0;return{bytesDelta:O!==void 0&&U!==void 0?O-U:void 0,currentPackets:R,direction:M,id:ne(a),packetDelta:R!==void 0&&u!==void 0?R-u:void 0,previousPackets:u,timeDeltaMs:s}}),l=c.filter((a)=>a.direction==="inbound"),g=c.filter((a)=>a.direction==="outbound"),y=K(c.map((a)=>a.timeDeltaMs).filter((a)=>a!==void 0)),d=l.filter((a)=>e.maxInboundPacketStallMs!==void 0&&a.timeDeltaMs!==void 0&&a.timeDeltaMs>=e.maxInboundPacketStallMs&&a.packetDelta!==void 0&&a.packetDelta<=0).length,h=g.filter((a)=>e.maxOutboundPacketStallMs!==void 0&&a.timeDeltaMs!==void 0&&a.timeDeltaMs>=e.maxOutboundPacketStallMs&&a.packetDelta!==void 0&&a.packetDelta<=0).length;if(e.requireInboundAudio&&l.length===0)H(o,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(e.requireOutboundAudio&&g.length===0)H(o,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(e.maxGapMs!==void 0&&y!==void 0&&y>e.maxGapMs)H(o,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(y)}ms above ${String(e.maxGapMs)}ms.`);if(d>0)H(o,"error","media.webrtc_inbound_stalled",`${String(d)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(h>0)H(o,"error","media.webrtc_outbound_stalled",`${String(h)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:l.length,issues:o,maxObservedGapMs:y,outboundAudioStreams:g.length,stalledInboundStreams:d,stalledOutboundStreams:h,status:o.some((a)=>a.severity==="error")?"fail":o.length>0?"warn":"pass",streams:c,totalStats:n.length}};var qe="/api/voice/browser-media",Xe=5000,ze=async(e)=>e.peerConnection??await e.getPeerConnection?.()??null,Ye=async(e,n)=>{let t=n.fetch??globalThis.fetch;if(!t)return;await t(n.path??qe,{body:JSON.stringify(e),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},Te=(e)=>{let n=null,t=[],o=async()=>{let c=await ze(e);if(!c)return;let l=await Ce({peerConnection:c}),g=ye({...e,stats:l}),y=e.continuity===!1?void 0:fe({...e.continuity,previousStats:t,stats:l}),d={at:Date.now(),continuity:y,report:g,scenarioId:e.getScenarioId?.()??null,sessionId:e.getSessionId?.()??null};return t=l,e.onReport?.(d),await Ye(d,e),d},r=()=>{o().catch((c)=>{e.onError?.(c)})},i=()=>{if(n)clearInterval(n),n=null};return{close:i,reportOnce:o,start:()=>{if(n)return;r(),n=setInterval(r,e.intervalMs??Xe)},stop:i}};var B=()=>{},Je=()=>B,Qe={callControl:B,close:B,endTurn:B,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",send:B,sendAudio:B,simulateDisconnect:B,start:()=>{},subscribe:Je},Ze=()=>crypto.randomUUID(),Ke=(e,n,t)=>{let{hostname:o,port:r,protocol:i}=window.location,c=i==="https:"?"wss:":"ws:",l=r?`:${r}`:"",g=new URL(`${c}//${o}${l}${e}`);if(g.searchParams.set("sessionId",n),t)g.searchParams.set("scenarioId",t);return g.toString()},me=(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}},je=(e)=>{if(typeof e.data!=="string")return null;try{let n=JSON.parse(e.data);return me(n)?n:null}catch{return null}},Se=(e,n={})=>{if(typeof window>"u")return Qe;let t=new Set,o=n.reconnect!==!1,r=n.maxReconnectAttempts??10,i=n.pingInterval??30000,c={isConnected:!1,pendingMessages:[],scenarioId:n.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:n.sessionId??Ze(),ws:null},l=(s)=>{t.forEach((S)=>S(s))},g=()=>{if(c.pingInterval)clearInterval(c.pingInterval),c.pingInterval=null;if(c.reconnectTimeout)clearTimeout(c.reconnectTimeout),c.reconnectTimeout=null},y=()=>{if(c.ws?.readyState!==1)return;while(c.pendingMessages.length>0){let s=c.pendingMessages.shift();if(s!==void 0)c.ws.send(s)}},d=()=>{let s=Date.now()+500;c.reconnectAttempts+=1,l({reconnect:{attempts:c.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:r,nextAttemptAt:s,status:"reconnecting"},type:"connection"}),c.reconnectTimeout=setTimeout(()=>{if(c.reconnectAttempts>r){l({reconnect:{attempts:c.reconnectAttempts,maxAttempts:r,status:"exhausted"},type:"connection"});return}h()},500)},h=()=>{let s=new WebSocket(Ke(e,c.sessionId,c.scenarioId));s.binaryType="arraybuffer",s.onopen=()=>{let S=c.reconnectAttempts>0;if(c.isConnected=!0,y(),S)l({reconnect:{attempts:c.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:r,status:"resumed"},type:"connection"}),c.reconnectAttempts=0;t.forEach((L)=>L({scenarioId:c.scenarioId??void 0,sessionId:c.sessionId,status:"active",type:"session"})),c.pingInterval=setInterval(()=>{if(s.readyState===1)s.send(JSON.stringify({type:"ping"}))},i)},s.onmessage=(S)=>{let L=je(S);if(!L)return;if(L.type==="session")c.sessionId=L.sessionId,c.scenarioId=L.scenarioId??c.scenarioId;t.forEach((X)=>X(L))},s.onclose=(S)=>{if(c.isConnected=!1,g(),o&&S.code!==1000&&c.reconnectAttempts<r)d();else if(o&&S.code!==1000)l({reconnect:{attempts:c.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:r,status:"exhausted"},type:"connection"})},c.ws=s},a=(s)=>{if(c.ws?.readyState===1){c.ws.send(s);return}c.pendingMessages.push(s)},M=(s)=>{a(JSON.stringify(s))},C=(s={})=>{if(s.sessionId)c.sessionId=s.sessionId;if(s.scenarioId)c.scenarioId=s.scenarioId;M({type:"start",sessionId:c.sessionId,scenarioId:c.scenarioId??void 0})},I=(s)=>{a(s)},w=()=>{M({type:"end_turn"})},R=(s)=>{M({...s,type:"call_control"})},u=()=>{if(g(),c.ws)c.ws.close(1000),c.ws=null;c.isConnected=!1,t.clear()},O=()=>{if(c.ws?.readyState===1)c.ws.close(4000,"absolutejs-voice-reconnect-proof")},U=(s)=>{return t.add(s),()=>{t.delete(s)}};return h(),{callControl:R,close:u,endTurn:w,getReadyState:()=>c.ws?.readyState??3,getScenarioId:()=>c.scenarioId??"",getSessionId:()=>c.sessionId,send:M,sendAudio:I,simulateDisconnect:O,start:C,subscribe:U}};var ve=()=>({attempts:0,maxAttempts:0,status:"idle"}),Fe=()=>({assistantAudio:[],assistantTexts:[],call:null,error:null,isConnected:!1,sessionMetadata:null,scenarioId:null,partial:"",reconnect:ve(),sessionId:null,status:"idle",turns:[]}),Me=()=>{let e=Fe(),n=new Set,t=()=>{n.forEach((r)=>r())};return{dispatch:(r)=>{switch(r.type){case"audio":e={...e,assistantAudio:[...e.assistantAudio,{chunk:r.chunk,format:r.format,receivedAt:r.receivedAt,turnId:r.turnId}]};break;case"assistant":e={...e,assistantTexts:[...e.assistantTexts,r.text]};break;case"complete":e={...e,sessionId:r.sessionId,status:"completed"};break;case"call_lifecycle":e={...e,call:{...e.call,disposition:r.event.type==="end"?r.event.disposition:e.call?.disposition,endedAt:r.event.type==="end"?r.event.at:e.call?.endedAt,events:[...e.call?.events??[],r.event],lastEventAt:r.event.at,startedAt:e.call?.startedAt??r.event.at},sessionId:r.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:r.reconnect};break;case"disconnected":e={...e,isConnected:!1};break;case"error":e={...e,error:r.message};break;case"final":e={...e,partial:r.transcript.text,turns:e.turns.map((i)=>i)};break;case"partial":e={...e,partial:r.transcript.text};break;case"replay":e={...e,assistantTexts:[...r.assistantTexts],call:r.call??null,error:null,isConnected:r.status==="active",partial:r.partial,reconnect:e.reconnect.status==="reconnecting"?{...e.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:e.reconnect,scenarioId:r.scenarioId??e.scenarioId,sessionId:r.sessionId,sessionMetadata:r.sessionMetadata??e.sessionMetadata,status:r.status,turns:[...r.turns]};break;case"session":e={...e,error:null,scenarioId:r.scenarioId??e.scenarioId,isConnected:r.status==="active",sessionId:r.sessionId,sessionMetadata:r.sessionMetadata??e.sessionMetadata,status:r.status};break;case"turn":e={...e,partial:"",turns:[...e.turns,r.turn]};break}t()},getServerSnapshot:()=>e,getSnapshot:()=>e,subscribe:(r)=>{return n.add(r),()=>{n.delete(r)}}}};var Ie=(e,n={})=>{let t=Se(e,n),o=Me(),r=n.browserMedia&&typeof window<"u"?Te({...n.browserMedia,getScenarioId:()=>n.browserMedia?n.browserMedia.getScenarioId?.()??t.getScenarioId():t.getScenarioId(),getSessionId:()=>n.browserMedia?n.browserMedia.getSessionId?.()??t.getSessionId():t.getSessionId()}):null,i=new Set,c=(d)=>Promise.resolve().then(()=>{if(!d?.sessionId&&!d?.scenarioId)return;t.start(d),r?.start()}),l=()=>{i.forEach((d)=>d())},g=()=>{if(!n.reconnectReportPath||typeof fetch>"u")return;let d=o.getSnapshot(),h=JSON.stringify({at:Date.now(),reconnect:d.reconnect,scenarioId:d.scenarioId,sessionId:t.getSessionId(),turnIds:d.turns.map((a)=>a.id)});fetch(n.reconnectReportPath,{body:h,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},y=t.subscribe((d)=>{let h=Ae(d);if(h){if(o.dispatch(h),d.type==="connection")g();l()}});return{callControl(d){t.callControl(d)},close(){y(),r?.close(),t.close(),o.dispatch({type:"disconnected"}),l()},endTurn(){t.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:c,get partial(){return o.getSnapshot().partial},get reconnect(){return o.getSnapshot().reconnect},get sessionId(){return t.getSessionId()},get status(){return o.getSnapshot().status},get turns(){return o.getSnapshot().turns},get assistantTexts(){return o.getSnapshot().assistantTexts},get assistantAudio(){return o.getSnapshot().assistantAudio},get call(){return o.getSnapshot().call},sendAudio(d){t.sendAudio(d)},simulateDisconnect(){t.simulateDisconnect()},subscribe(d){return i.add(d),()=>{i.delete(d)}}}};var Ve=(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 pe={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}},en={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 we=(e)=>{let n=e?.profile??"fast",t=e?.qualityProfile??"general",o=pe[n],r=en[t];return{profile:n,qualityProfile:t,silenceMs:e?.silenceMs??r.silenceMs??o.silenceMs,speechThreshold:e?.speechThreshold??r.speechThreshold??o.speechThreshold,transcriptStabilityMs:e?.transcriptStabilityMs??r.transcriptStabilityMs??o.transcriptStabilityMs}};var nn={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"}}},Le=(e="default")=>{let n=nn[e];return{audioConditioning:Ve(n.audioConditioning),capture:{channelCount:n.capture?.channelCount??1,sampleRateHz:n.capture?.sampleRateHz??16000},connection:{...n.connection},name:e,sttLifecycle:n.sttLifecycle??"continuous",turnDetection:we(n.turnDetection)}};var on=(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]}),m=(e,n={})=>{let t=Le(n.preset),o=Ie(e,{...t.connection,...n.connection}),r=null,i=on(o),c=new Set,l=()=>{for(let C of c)C()},g=()=>{if(i={...i,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&&i.status==="completed"&&i.isRecording)r?.stop(),r=null,i={...i,isRecording:!1};l()},y=o.subscribe(g);g();let d=()=>{if(r)return r;return r=ge({channelCount:n.capture?.channelCount??t.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??t.capture.sampleRateHz}),r},h=()=>{r?.stop(),r=null,i={...i,isRecording:!1},l()},a=async()=>{if(i.isRecording)return;try{i={...i,recordingError:null},l(),await d().start(),i={...i,isRecording:!0},l()}catch(C){throw r=null,i={...i,isRecording:!1,recordingError:C instanceof Error?C.message:String(C)},l(),C}};return{bindHTMX(C){return de(o,C)},callControl:(C)=>o.callControl(C),close:()=>{y(),h(),o.close()},endTurn:()=>o.endTurn(),get error(){return i.error},getServerSnapshot:()=>i,getSnapshot:()=>i,get isConnected(){return i.isConnected},get isRecording(){return i.isRecording},get partial(){return i.partial},get recordingError(){return i.recordingError},get reconnect(){return i.reconnect},sendAudio:(C)=>o.sendAudio(C),simulateDisconnect:()=>o.simulateDisconnect(),get sessionId(){return i.sessionId},get sessionMetadata(){return i.sessionMetadata},get scenarioId(){return i.scenarioId},startRecording:a,get status(){return i.status},stopRecording:h,subscribe:(C)=>{return c.add(C),()=>{c.delete(C)}},toggleRecording:async()=>{if(i.isRecording){h();return}await a()},get turns(){return i.turns},get assistantTexts(){return i.assistantTexts},get assistantAudio(){return i.assistantAudio},get call(){return i.call}}};var tn=()=>({activeSourceCount:0,error:null,isActive:!1,isPlaying:!1,lastInterruptLatencyMs:void 0,lastPlaybackStopLatencyMs:void 0,processedChunkCount:0,queuedChunkCount:0}),cn=()=>{if(typeof window>"u")return typeof AudioContext>"u"?void 0:AudioContext;return window.AudioContext??window.webkitAudioContext},rn=(e,n)=>{let t=n.format;if(t.container!=="raw"||t.encoding!=="pcm_s16le")throw Error(`Unsupported assistant audio format: ${t.container}/${t.encoding}`);let o=n.chunk,r=Math.max(1,t.channels),i=Math.floor(o.byteLength/2),c=Math.max(1,Math.floor(i/r)),l=e.createBuffer(r,c,t.sampleRateHz),g=new DataView(o.buffer,o.byteOffset,o.byteLength);for(let y=0;y<r;y+=1){let d=l.getChannelData(y);for(let h=0;h<c;h+=1){let M=(h*r+y)*2;if(M+1>=o.byteLength){d[h]=0;continue}d[h]=g.getInt16(M,!0)/32768}}return l},j=(e,n={})=>{let t=new Set,o=new Set,r=(n.lookaheadMs??15)/1000,i=tn(),c=null,l=null,g=0,y=Promise.resolve(),d=null,h=null,a=null,M=null,C=()=>{for(let A of t)A()},I=(A)=>{i={...i,...A},C()},w=()=>{if(i.error!==null)I({error:null})},R=()=>{if(M!==null)clearTimeout(M),M=null},u=(A)=>{R(),d=null,I({activeSourceCount:o.size,isPlaying:!1,lastInterruptLatencyMs:A,lastPlaybackStopLatencyMs:i.lastPlaybackStopLatencyMs??A}),a?.(),a=null,h=null},O=(A)=>{if(!A)return 0;return Math.max(0,((A.baseLatency??0)+(A.outputLatency??0))*1000)},U=(A)=>{if(!l)return;let f=1;if(l.gain.setValueAtTime){l.gain.setValueAtTime(f,A?.currentTime??0);return}l.gain.value=f},s=(A)=>{if(!l)return;let f=0;if(l.gain.setValueAtTime){l.gain.setValueAtTime(f,A?.currentTime??0);return}l.gain.value=f},S=()=>{if(d===null||o.size>0)return;u(Date.now()-d)},L=async()=>{if(c)return c;if(n.createAudioContext)c=n.createAudioContext();else{let A=cn();if(!A)throw Error("Assistant audio playback requires AudioContext support.");c=new A}if(c.createGain)l=c.createGain(),l.connect?.(c.destination);return g=c.currentTime,c},X=async(A)=>{let f=await L(),E=rn(f,A),V=f.createBufferSource();V.buffer=E,V.connect(l??f.destination),V.onended=()=>{o.delete(V),V.disconnect?.(),I({activeSourceCount:o.size,isPlaying:o.size>0&&i.isActive}),S()};let Y=Math.max(f.currentTime+r,g);g=Y+E.duration,o.add(V),I({activeSourceCount:o.size,isPlaying:!0}),V.start(Y)},_=(A)=>{for(let f of[...o])f.stop?.();if(g=c?c.currentTime:0,A?.forceClear){for(let f of o)f.disconnect?.();o.clear(),S()}},W=async()=>{if(!i.isActive)return;let A=e.assistantAudio.slice(i.processedChunkCount);if(A.length===0)return;try{w();for(let f of A)await X(f);I({processedChunkCount:e.assistantAudio.length,queuedChunkCount:i.queuedChunkCount+A.length})}catch(f){I({error:f instanceof Error?f.message:String(f)})}},P=()=>{return y=y.then(()=>W(),()=>W()),y},$=e.subscribe(()=>{if(n.autoStart&&!i.isActive&&e.assistantAudio.length>0){N.start();return}if(i.isActive)P()}),N={close:async()=>{if($(),_({forceClear:!0}),R(),a?.(),a=null,h=null,d=null,c&&c.state!=="closed")await c.close();c=null,l?.disconnect?.(),l=null,g=0,I({activeSourceCount:0,isActive:!1,isPlaying:!1})},get activeSourceCount(){return i.activeSourceCount},get error(){return i.error},getSnapshot:()=>i,get isActive(){return i.isActive},get isPlaying(){return i.isPlaying},interrupt:async()=>{let A=Date.now(),f=await L();d=A,s(f);let E=Date.now()-A+O(f);if(I({isActive:!1,isPlaying:o.size>0,lastPlaybackStopLatencyMs:E}),o.size===0){u(E);return}if(!h)h=new Promise((V)=>{a=V});R(),M=setTimeout(()=>{for(let V of o)V.disconnect?.();o.clear(),u(Date.now()-A)},250),_(),await h},get lastInterruptLatencyMs(){return i.lastInterruptLatencyMs},get lastPlaybackStopLatencyMs(){return i.lastPlaybackStopLatencyMs},pause:async()=>{if(!c){I({activeSourceCount:0,isActive:!1,isPlaying:!1});return}await c.suspend(),I({activeSourceCount:o.size,isActive:!1,isPlaying:!1})},get processedChunkCount(){return i.processedChunkCount},get queuedChunkCount(){return i.queuedChunkCount},start:async()=>{try{w();let A=await L();if(U(A),A.state==="suspended")await A.resume();I({activeSourceCount:o.size,isActive:!0,isPlaying:A.state==="running"}),await P()}catch(A){throw I({error:A instanceof Error?A.message:String(A),isActive:!1,isPlaying:!1}),A}},subscribe:(A)=>{return t.add(A),()=>{t.delete(A)}}};return N};var sn=()=>`barge-in:${Date.now()}:${crypto.randomUUID?.()??Math.random().toString(36).slice(2)}`,ln=(e,n)=>{let t=e.filter((c)=>c.status==="stopped"),o=t.map((c)=>c.latencyMs).filter((c)=>typeof c==="number"),r=t.filter((c)=>typeof c.latencyMs==="number"&&c.latencyMs>n).length,i=t.length-r;return{averageLatencyMs:o.length>0?Math.round(o.reduce((c,l)=>c+l,0)/o.length):void 0,events:[...e],failed:r,lastEvent:e.at(-1),passed:i,status:e.length===0?"empty":r>0?"fail":t.length===0?"warn":"pass",thresholdMs:n,total:t.length}},ue=(e={})=>{let n=new Set,t=e.thresholdMs??250,o=e.fetch??globalThis.fetch,r=[],i=()=>{for(let g of n)g()},c=(g)=>{if(!e.path||typeof o!=="function")return;o(e.path,{body:JSON.stringify(g),headers:{"Content-Type":"application/json"},method:"POST"}).catch(()=>{})},l=(g,y)=>{let d={at:Date.now(),id:sn(),latencyMs:y.latencyMs,playbackStopLatencyMs:y.playbackStopLatencyMs,reason:y.reason,sessionId:y.sessionId,status:g,thresholdMs:t};return r.push(d),c(d),i(),d};return{getSnapshot:()=>ln(r,t),recordRequested:(g)=>l("requested",g),recordSkipped:(g)=>l("skipped",g),recordStopped:(g)=>l("stopped",g),subscribe:(g)=>{return n.add(g),()=>{n.delete(g)}}}};var an=0.08,dn=(e,n={})=>(n.enabled??!0)&&e>=(n.interruptThreshold??an),oe=(e,n,t={})=>{let o=e.partial,r=(c)=>{if(!n.isPlaying||t.enabled===!1){t.monitor?.recordSkipped({reason:c,sessionId:e.sessionId});return}t.monitor?.recordRequested({reason:c,sessionId:e.sessionId}),n.interrupt().then(()=>{t.monitor?.recordStopped({latencyMs:n.lastInterruptLatencyMs,playbackStopLatencyMs:n.lastPlaybackStopLatencyMs,reason:c,sessionId:e.sessionId})})},i=e.subscribe(()=>{if(t.interruptOnPartial===!1){o=e.partial;return}if(!o&&e.partial)r("partial-transcript");o=e.partial});return{close:()=>{i()},handleLevel:(c)=>{if(dn(c,t))r("input-level")},sendAudio:(c)=>{r("manual-audio"),e.sendAudio(c)}}};var se=48,gn=320,An=88,hn="Guided test",yn="General recording",Cn="Pick a scenario to begin the demo.",fn="I can walk you through a short guided voice test.",Tn="I can capture one freeform recording and confirm that it landed.",Sn="Choose a scenario to begin. Guided test asks follow-up prompts. General recording just captures what you say.",Mn="Click Start general recording to capture one freeform answer.",_e="Speak freely. When you pause, the recording will be captured.",re="Recording saved. Start again if you want another capture.",Ee="Guided test complete. Review the saved summary below.",Pe="All prompts are covered. You can stop the microphone or keep speaking for extra detail.",In="Ready. Start guided test or general recording to begin.",Vn="Live. Answer the prompt, then click Stop microphone when finished.",Re=["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."],De=(e,n,t)=>Math.min(t,Math.max(n,e)),q=(e)=>e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll(\'"\',""").replaceAll("\'","'"),te=(e,n)=>{let t=e[n];if(typeof t==="string"&&t.trim())return t;return null},ie=(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,t=te(n,"message")??te(n,"reason")??te(n,"description");if(t)return t;if("error"in n)return ie(n.error);if("cause"in n)return ie(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 t=Math.max(0,e.nextAttemptAt-Date.now());n.push(`retry in ${Math.ceil(t/100)/10}s`)}return n.join(" \xB7 ")},v=(e=se)=>Array.from({length:e},()=>0),be=(e,n,t=se)=>{let o=e.slice(-(t-1));o.push(De(n,0,1));while(o.length<t)o.unshift(0);return o},Ln=(e,n=gn,t=An)=>{let o=e.length>1?e:v(se),r=n/(o.length-1),i=t/2,c=t*0.34;if(Math.max(...o,0)<=0.015)return`M 0 ${i} L ${n} ${i}`;let g=o.map((d,h)=>{let a=h*0.76,M=Math.sin(a)*0.78+Math.sin(a*0.41)*0.22,C=d*c,I=r*h,w=De(i+M*C,8,t-8);return{x:I,y:w}});if(g.length===0)return`M 0 ${i} L ${n} ${i}`;let y=`M ${g[0]?.x??0} ${g[0]?.y??i}`;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},un=(e)=>{if(!e)return Re;try{let n=JSON.parse(e);if(Array.isArray(n)){let t=n.filter((o)=>typeof o==="string").map((o)=>o.trim()).filter(Boolean);if(t.length>0)return t}}catch{}return Re},ce=(e)=>{if(!e)return;let n=Number(e);return Number.isFinite(n)?n:void 0},Rn=(e,n,t)=>{let o=n?document.querySelector(n):e.querySelector(n??"");return o instanceof t?o:null},x=(e,n,t,o)=>{let r=n?document.querySelector(n):null;if(r instanceof t)return r;let i=e.querySelector(`#${o}`);if(i instanceof t)return i;throw Error(`Voice HTMX bootstrap could not find the required element "${o}".`)},bn=(e)=>{if(!e.mode)return Cn;if(!e.hasStarted)return e.mode==="guided"?fn:Tn;if(e.status==="completed")return e.mode==="guided"?Ee:re;if(e.mode==="general")return _e;return e.guidedPrompts[e.turnCount]??Pe},_n=(e)=>{if(!e.mode)return Sn;if(e.status==="completed")return e.mode==="guided"?Ee:re;if(!e.hasStarted)return e.mode==="guided"?`Click Start guided test to begin. First prompt: ${e.guidedPrompts[0]??"Answer the first prompt."}`:Mn;if(e.mode==="general")return e.turnCount===0?_e:re;return e.guidedPrompts[e.turnCount]??Pe},En=(e)=>{let n=e.dataset.voiceGuidedPath,t=e.dataset.voiceGeneralPath;if(!n||!t)throw Error("Voice HTMX bootstrap requires data-voice-guided-path and data-voice-general-path.");let o=un(e.dataset.voiceGuidedPrompts),r=e.dataset.voiceGuidedLabel??hn,i=e.dataset.voiceGeneralLabel??yn,c=e.dataset.voiceReconnectReportPath,l=e.dataset.voiceBargeInPath,g=l?ue({path:l,thresholdMs:ce(e.dataset.voiceBargeInThresholdMs)}):null,y=ce(e.dataset.voiceBargeInRecentWindowMs)??4000,d=ce(e.dataset.voiceBargeInSpeechThreshold)??0.04,h=x(document,e.dataset.voiceSync,HTMLElement,"voice-htmx-sync"),a=x(e,e.dataset.voiceConnection,HTMLElement,"metric-connection"),M=x(e,e.dataset.voiceError,HTMLElement,"status-error"),C=x(e,e.dataset.voiceMicrophone,HTMLElement,"status-mic"),I=x(e,e.dataset.voicePrompt,HTMLElement,"status-prompt"),w=Rn(e,e.dataset.voiceReconnect,HTMLElement),R=x(e,e.dataset.voiceChat,HTMLElement,"chat-list"),u=x(e,e.dataset.voiceStartGuided,HTMLButtonElement,"start-guided"),O=x(e,e.dataset.voiceStartGeneral,HTMLButtonElement,"start-general"),U=x(e,e.dataset.voiceStop,HTMLButtonElement,"stop-mic"),s=x(e,e.dataset.voiceMonitor,HTMLElement,"voice-monitor"),S=x(e,e.dataset.voiceMonitorCopy,HTMLElement,"voice-monitor-copy"),L=x(e,e.dataset.voiceWaveGlow,SVGPathElement,"voice-wave-glow"),X=x(e,e.dataset.voiceWavePath,SVGPathElement,"voice-wave-path"),_=null,W={general:!1,guided:!1},P=!1,$=null,N=v(),A=null,f=null,E=m(n,{capture:{onAudio:(T,G)=>{if(A){A.sendAudio(T);return}G(T)},onLevel:(T)=>{A?.handleLevel(T),N=be(N,T),F()}},connection:{reconnectReportPath:c},preset:"guided-intake"}),V=m(t,{capture:{onAudio:(T,G)=>{if(f){f.sendAudio(T);return}G(T)},onLevel:(T)=>{f?.handleLevel(T),N=be(N,T),F()}},connection:{reconnectReportPath:c},preset:"dictation"}),Y=E.bindHTMX({element:h}),xe=V.bindHTMX({element:h}),J=j(E),Q=j(V);A=oe(E,J,{interruptThreshold:d,monitor:g??void 0}),f=oe(V,Q,{interruptThreshold:d,monitor:g??void 0});let z=()=>_==="general"?V:E,Dn=()=>_==="general"?Q:J,F=()=>{let T=Ln(N);L.setAttribute("d",T),X.setAttribute("d",T),S.innerHTML=`<span class="voice-live-dot"></span>${P?"Microphone live":"Microphone idle"}`,S.classList.toggle("is-live",P),s.classList.toggle("is-live",P)},k=()=>{let T=z(),G=(_?W[_]:!1)||T.turns.length>0,ae=T.status;if(a.textContent=T.isConnected?"Connected":"Waiting",M.textContent=$||T.error||"None",w)w.textContent=wn(T.reconnect);C.textContent=P?Vn:In,I.textContent=_n({guidedPrompts:o,hasStarted:G,mode:_,status:ae,turnCount:T.turns.length}),u.hidden=P,O.hidden=P,U.hidden=!P,R.innerHTML=`<article class="voice-chat-message assistant">\n <div class="voice-chat-role">${q(_==="general"?i:_==="guided"?r:"Voice demo")}</div>\n <p class="voice-turn-text">${q(bn({generalLabel:i,guidedLabel:r,guidedPrompts:o,hasStarted:G,mode:_,status:ae,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">${q(p.text)}</p>\n </article>\n ${p.assistantText?`<article class="voice-chat-message assistant">\n <div class="voice-chat-role">${q(_==="general"?i:_==="guided"?r:"Guide")}</div>\n <p class="voice-turn-text">${q(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">${q(T.partial)}</p>\n</article>`:""}`,F()},Oe=()=>{z().stopRecording(),P=!1,$=null,N=v(),k()},le=async(T)=>{_=T,W={...W,[T]:!0};try{await z().startRecording(),$=null,P=!0,k()}catch(G){z().stopRecording(),P=!1,N=v(),$=ie(G),k()}};E.subscribe(()=>{if(E.assistantAudio.length>0)J.start().catch(()=>{});k()}),V.subscribe(()=>{if(V.assistantAudio.length>0)Q.start().catch(()=>{});k()}),u.addEventListener("click",()=>{le("guided")}),O.addEventListener("click",()=>{le("general")}),U.addEventListener("click",()=>{Oe()}),e.addEventListener("absolute-voice-simulate-disconnect",()=>{z().simulateDisconnect()}),window.addEventListener("beforeunload",()=>{E.stopRecording(),V.stopRecording(),A?.close(),f?.close(),J.close(),Q.close(),Y(),xe(),E.close(),V.close()}),k()},Pn=()=>{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)En(n)};Pn();export{Pn as initVoiceHTMX};\n';
|
|
5189
|
+
var HTMX_BOOTSTRAP_BUNDLE = 'var Ue=(e)=>{if(typeof e!=="string")return e;return document.querySelector(e)},Ne=(e,n,t,o)=>{let r=n??e.getAttribute("hx-get")??"";if(!r)return"";let i=new URL(r,window.location.origin);if(o)i.searchParams.set(t,o);else i.searchParams.delete(t);return`${i.pathname}${i.search}${i.hash}`},de=(e,n)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let t=Ue(n.element);if(!t)return()=>{};let o=n.eventName??"voice-refresh",r=n.sessionQueryParam??"sessionId",i=()=>{let l=window,g=Ne(t,n.route,r,e.sessionId);if(g)t.setAttribute("hx-get",g);l.htmx?.process?.(t),l.htmx?.trigger?.(t,o)},c=e.subscribe(i);return i(),()=>{c()}};var He=(e)=>Math.max(-1,Math.min(1,e)),Ge=(e)=>{let n=new Int16Array(e.length);for(let t=0;t<e.length;t+=1){let o=He(e[t]??0);n[t]=o<0?o*32768:o*32767}return new Uint8Array(n.buffer)},Be=(e)=>{let n=e instanceof Uint8Array?e:new Uint8Array(e);if(n.byteLength<2)return 0;let t=new Int16Array(n.buffer,n.byteOffset,Math.floor(n.byteLength/2));if(t.length===0)return 0;let o=0;for(let r of t){let i=r/32768;o+=i*i}return Math.min(1,Math.max(0,Math.sqrt(o/t.length)*5.5))},We=(e,n,t)=>{if(n===t)return e;let o=n/t,r=Math.round(e.length/o),i=new Float32Array(r),c=0,l=0;while(c<i.length){let g=Math.round((c+1)*o),y=0,d=0;for(let h=l;h<g&&h<e.length;h+=1)y+=e[h]??0,d+=1;i[c]=d>0?y/d:0,c+=1,l=g}return i},ge=(e)=>{let n=null,t=null,o=null,r=null;return{start:async()=>{if(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia)throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let l=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!l)throw Error("Browser microphone capture requires AudioContext support.");r=await navigator.mediaDevices.getUserMedia({audio:{channelCount:e.channelCount??1}}),n=new l,t=n.createMediaStreamSource(r),o=n.createScriptProcessor(4096,1,1),o.onaudioprocess=(g)=>{let y=g.inputBuffer.getChannelData(0),d=We(y,n?.sampleRate??48000,e.sampleRateHz??16000),h=Ge(d);e.onLevel?.(Be(h)),e.onAudio(h)},t.connect(o),o.connect(n.destination)},stop:()=>{o?.disconnect(),t?.disconnect(),r?.getTracks().forEach((l)=>l.stop()),n?.close(),e.onLevel?.(0),n=null,r=null,o=null,t=null}}};var ee=(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 t of["message","reason","description"]){let o=n[t];if(typeof o==="string"&&o.trim())return o}if("error"in n)return ee(n.error);if("cause"in n)return ee(n.cause);try{return JSON.stringify(e)}catch{}}return"Unexpected error"},Ae=(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:ee(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 H=(e,n,t,o)=>{e.push({code:t,message:o,severity:n})};var $e=(e)=>e.length===0?void 0:e.reduce((n,t)=>n+t,0)/e.length,K=(e)=>e.length===0?void 0:Math.max(...e);var b=(e,n)=>{let t=e[n];return typeof t==="number"&&Number.isFinite(t)?t:void 0},Z=(e,n)=>{let t=e[n];return typeof t==="boolean"?t:void 0},D=(e,n)=>{let t=e[n];return typeof t==="string"?t:void 0},ne=(e)=>String(e.id??D(e,"ssrc")??b(e,"ssrc")??D(e,"trackIdentifier")??D(e,"mid")??"unknown"),he=(e)=>e===void 0?void 0:e*1000;var ke=(e)=>{let n={};for(let[t,o]of Object.entries(e))if(o===null||typeof o==="boolean"||typeof o==="number"||typeof o==="string")n[t]=o;return n};var ye=(e={})=>{let n=e.stats??[],t=[],o=n.filter((s)=>s.type==="inbound-rtp"&&D(s,"kind")!=="video"),r=n.filter((s)=>s.type==="outbound-rtp"&&D(s,"kind")!=="video"),i=n.filter((s)=>s.type==="candidate-pair"),c=n.filter((s)=>(s.type==="track"||s.type==="media-source")&&D(s,"kind")==="audio"),l=i.filter((s)=>Z(s,"selected")===!0||Z(s,"nominated")===!0||D(s,"state")==="succeeded").length,g=c.filter((s)=>D(s,"readyState")!=="ended"&&D(s,"trackState")!=="ended"&&Z(s,"ended")!==!0).length,y=c.filter((s)=>D(s,"readyState")==="ended"||D(s,"trackState")==="ended"||Z(s,"ended")===!0).length,d=o.reduce((s,S)=>s+(b(S,"packetsReceived")??0),0),h=r.reduce((s,S)=>s+(b(S,"packetsSent")??0),0),a=[...o,...r].reduce((s,S)=>s+Math.max(0,b(S,"packetsLost")??0),0),M=d+a,C=M===0?0:a/M,I=o.reduce((s,S)=>s+(b(S,"bytesReceived")??0),0),w=r.reduce((s,S)=>s+(b(S,"bytesSent")??0),0),R=K(i.map((s)=>he(b(s,"currentRoundTripTime")??b(s,"roundTripTime"))).filter((s)=>s!==void 0)),u=K([...o,...r].map((s)=>he(b(s,"jitter"))).filter((s)=>s!==void 0)),O=K(o.map((s)=>{let S=b(s,"jitterBufferDelay"),L=b(s,"jitterBufferEmittedCount");return S!==void 0&&L!==void 0&&L>0?S/L*1000:void 0}).filter((s)=>s!==void 0)),U=c.map((s)=>b(s,"audioLevel")).filter((s)=>s!==void 0);if(e.requireConnectedCandidatePair&&i.length>0&&l===0)H(t,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(e.requireLiveAudioTrack&&g===0)H(t,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(e.maxPacketLossRatio!==void 0&&C>e.maxPacketLossRatio)H(t,"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)H(t,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(R)}ms above ${String(e.maxRoundTripTimeMs)}ms.`);if(e.maxJitterMs!==void 0&&u!==void 0&&u>e.maxJitterMs)H(t,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(u)}ms above ${String(e.maxJitterMs)}ms.`);return{activeCandidatePairs:l,audioLevelAverage:$e(U),bytesReceived:I,bytesSent:w,checkedAt:Date.now(),endedAudioTracks:y,inboundPackets:d,issues:t,jitterBufferDelayMs:O,jitterMs:u,liveAudioTracks:g,outboundPackets:h,packetLossRatio:C,packetsLost:a,roundTripTimeMs:R,status:t.some((s)=>s.severity==="error")?"fail":t.length>0?"warn":"pass",totalStats:n.length}},Ce=async(e)=>{return[...(await e.peerConnection.getStats(e.selector??null)).values()].map(ke)};var fe=(e={})=>{let n=e.stats??[],t=e.previousStats??[],o=[],r=new Map(t.map((a)=>[ne(a),a])),c=n.filter((a)=>(a.type==="inbound-rtp"||a.type==="outbound-rtp")&&D(a,"kind")!=="video"&&D(a,"mediaType")!=="video").map((a)=>{let M=a.type==="outbound-rtp"?"outbound":"inbound",C=M==="outbound"?"packetsSent":"packetsReceived",I=M==="outbound"?"bytesSent":"bytesReceived",w=r.get(ne(a)),R=b(a,C),u=w?b(w,C):void 0,O=b(a,I),U=w?b(w,I):void 0,s=a.timestamp!==void 0&&w?.timestamp!==void 0?a.timestamp-w.timestamp:void 0;return{bytesDelta:O!==void 0&&U!==void 0?O-U:void 0,currentPackets:R,direction:M,id:ne(a),packetDelta:R!==void 0&&u!==void 0?R-u:void 0,previousPackets:u,timeDeltaMs:s}}),l=c.filter((a)=>a.direction==="inbound"),g=c.filter((a)=>a.direction==="outbound"),y=K(c.map((a)=>a.timeDeltaMs).filter((a)=>a!==void 0)),d=l.filter((a)=>e.maxInboundPacketStallMs!==void 0&&a.timeDeltaMs!==void 0&&a.timeDeltaMs>=e.maxInboundPacketStallMs&&a.packetDelta!==void 0&&a.packetDelta<=0).length,h=g.filter((a)=>e.maxOutboundPacketStallMs!==void 0&&a.timeDeltaMs!==void 0&&a.timeDeltaMs>=e.maxOutboundPacketStallMs&&a.packetDelta!==void 0&&a.packetDelta<=0).length;if(e.requireInboundAudio&&l.length===0)H(o,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(e.requireOutboundAudio&&g.length===0)H(o,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(e.maxGapMs!==void 0&&y!==void 0&&y>e.maxGapMs)H(o,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(y)}ms above ${String(e.maxGapMs)}ms.`);if(d>0)H(o,"error","media.webrtc_inbound_stalled",`${String(d)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(h>0)H(o,"error","media.webrtc_outbound_stalled",`${String(h)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:l.length,issues:o,maxObservedGapMs:y,outboundAudioStreams:g.length,stalledInboundStreams:d,stalledOutboundStreams:h,status:o.some((a)=>a.severity==="error")?"fail":o.length>0?"warn":"pass",streams:c,totalStats:n.length}};var qe="/api/voice/browser-media",Xe=5000,ze=async(e)=>e.peerConnection??await e.getPeerConnection?.()??null,Ye=async(e,n)=>{let t=n.fetch??globalThis.fetch;if(!t)return;await t(n.path??qe,{body:JSON.stringify(e),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},Te=(e)=>{let n=null,t=[],o=async()=>{let c=await ze(e);if(!c)return;let l=await Ce({peerConnection:c}),g=ye({...e,stats:l}),y=e.continuity===!1?void 0:fe({...e.continuity,previousStats:t,stats:l}),d={at:Date.now(),continuity:y,report:g,scenarioId:e.getScenarioId?.()??null,sessionId:e.getSessionId?.()??null};return t=l,e.onReport?.(d),await Ye(d,e),d},r=()=>{o().catch((c)=>{e.onError?.(c)})},i=()=>{if(n)clearInterval(n),n=null};return{close:i,reportOnce:o,start:()=>{if(n)return;r(),n=setInterval(r,e.intervalMs??Xe)},stop:i}};var B=()=>{},Je=()=>B,Qe={callControl:B,close:B,endTurn:B,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",send:B,sendAudio:B,simulateDisconnect:B,start:()=>{},subscribe:Je},Ze=()=>crypto.randomUUID(),Ke=(e,n,t)=>{let{hostname:o,port:r,protocol:i}=window.location,c=i==="https:"?"wss:":"ws:",l=r?`:${r}`:"",g=new URL(`${c}//${o}${l}${e}`);if(g.searchParams.set("sessionId",n),t)g.searchParams.set("scenarioId",t);return g.toString()},me=(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}},je=(e)=>{if(typeof e.data!=="string")return null;try{let n=JSON.parse(e.data);return me(n)?n:null}catch{return null}},Se=(e,n={})=>{if(typeof window>"u")return Qe;let t=new Set,o=n.reconnect!==!1,r=n.maxReconnectAttempts??10,i=n.pingInterval??30000,c={isConnected:!1,pendingMessages:[],scenarioId:n.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:n.sessionId??Ze(),ws:null},l=(s)=>{t.forEach((S)=>S(s))},g=()=>{if(c.pingInterval)clearInterval(c.pingInterval),c.pingInterval=null;if(c.reconnectTimeout)clearTimeout(c.reconnectTimeout),c.reconnectTimeout=null},y=()=>{if(c.ws?.readyState!==1)return;while(c.pendingMessages.length>0){let s=c.pendingMessages.shift();if(s!==void 0)c.ws.send(s)}},d=()=>{let s=Date.now()+500;c.reconnectAttempts+=1,l({reconnect:{attempts:c.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:r,nextAttemptAt:s,status:"reconnecting"},type:"connection"}),c.reconnectTimeout=setTimeout(()=>{if(c.reconnectAttempts>r){l({reconnect:{attempts:c.reconnectAttempts,maxAttempts:r,status:"exhausted"},type:"connection"});return}h()},500)},h=()=>{let s=new WebSocket(Ke(e,c.sessionId,c.scenarioId));s.binaryType="arraybuffer",s.onopen=()=>{let S=c.reconnectAttempts>0;if(c.isConnected=!0,y(),S)l({reconnect:{attempts:c.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:r,status:"resumed"},type:"connection"}),c.reconnectAttempts=0;t.forEach((L)=>L({scenarioId:c.scenarioId??void 0,sessionId:c.sessionId,status:"active",type:"session"})),c.pingInterval=setInterval(()=>{if(s.readyState===1)s.send(JSON.stringify({type:"ping"}))},i)},s.onmessage=(S)=>{let L=je(S);if(!L)return;if(L.type==="session")c.sessionId=L.sessionId,c.scenarioId=L.scenarioId??c.scenarioId;t.forEach((X)=>X(L))},s.onclose=(S)=>{if(c.isConnected=!1,g(),o&&S.code!==1000&&c.reconnectAttempts<r)d();else if(o&&S.code!==1000)l({reconnect:{attempts:c.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:r,status:"exhausted"},type:"connection"})},c.ws=s},a=(s)=>{if(c.ws?.readyState===1){c.ws.send(s);return}c.pendingMessages.push(s)},M=(s)=>{a(JSON.stringify(s))},C=(s={})=>{if(s.sessionId)c.sessionId=s.sessionId;if(s.scenarioId)c.scenarioId=s.scenarioId;M({type:"start",sessionId:c.sessionId,scenarioId:c.scenarioId??void 0})},I=(s)=>{a(s)},w=()=>{M({type:"end_turn"})},R=(s)=>{M({...s,type:"call_control"})},u=()=>{if(g(),c.ws)c.ws.close(1000),c.ws=null;c.isConnected=!1,t.clear()},O=()=>{if(c.ws?.readyState===1)c.ws.close(4000,"absolutejs-voice-reconnect-proof")},U=(s)=>{return t.add(s),()=>{t.delete(s)}};return h(),{callControl:R,close:u,endTurn:w,getReadyState:()=>c.ws?.readyState??3,getScenarioId:()=>c.scenarioId??"",getSessionId:()=>c.sessionId,send:M,sendAudio:I,simulateDisconnect:O,start:C,subscribe:U}};var ve=()=>({attempts:0,maxAttempts:0,status:"idle"}),Fe=()=>({assistantAudio:[],assistantTexts:[],call:null,error:null,isConnected:!1,sessionMetadata:null,scenarioId:null,partial:"",reconnect:ve(),sessionId:null,status:"idle",turns:[]}),Me=()=>{let e=Fe(),n=new Set,t=()=>{n.forEach((r)=>r())};return{dispatch:(r)=>{switch(r.type){case"audio":e={...e,assistantAudio:[...e.assistantAudio,{chunk:r.chunk,format:r.format,receivedAt:r.receivedAt,turnId:r.turnId}]};break;case"assistant":e={...e,assistantTexts:[...e.assistantTexts,r.text]};break;case"complete":e={...e,sessionId:r.sessionId,status:"completed"};break;case"call_lifecycle":e={...e,call:{...e.call,disposition:r.event.type==="end"?r.event.disposition:e.call?.disposition,endedAt:r.event.type==="end"?r.event.at:e.call?.endedAt,events:[...e.call?.events??[],r.event],lastEventAt:r.event.at,startedAt:e.call?.startedAt??r.event.at},sessionId:r.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:r.reconnect};break;case"disconnected":e={...e,isConnected:!1};break;case"error":e={...e,error:r.message};break;case"final":e={...e,partial:r.transcript.text,turns:e.turns.map((i)=>i)};break;case"partial":e={...e,partial:r.transcript.text};break;case"replay":e={...e,assistantTexts:[...r.assistantTexts],call:r.call??null,error:null,isConnected:r.status==="active",partial:r.partial,reconnect:e.reconnect.status==="reconnecting"?{...e.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:e.reconnect,scenarioId:r.scenarioId??e.scenarioId,sessionId:r.sessionId,sessionMetadata:r.sessionMetadata??e.sessionMetadata,status:r.status,turns:[...r.turns]};break;case"session":e={...e,error:null,scenarioId:r.scenarioId??e.scenarioId,isConnected:r.status==="active",sessionId:r.sessionId,sessionMetadata:r.sessionMetadata??e.sessionMetadata,status:r.status};break;case"turn":e={...e,partial:"",turns:[...e.turns,r.turn]};break}t()},getServerSnapshot:()=>e,getSnapshot:()=>e,subscribe:(r)=>{return n.add(r),()=>{n.delete(r)}}}};var Ie=(e,n={})=>{let t=Se(e,n),o=Me(),r=n.browserMedia&&typeof window<"u"?Te({...n.browserMedia,getScenarioId:()=>n.browserMedia?n.browserMedia.getScenarioId?.()??t.getScenarioId():t.getScenarioId(),getSessionId:()=>n.browserMedia?n.browserMedia.getSessionId?.()??t.getSessionId():t.getSessionId()}):null,i=new Set,c=(d)=>Promise.resolve().then(()=>{if(!d?.sessionId&&!d?.scenarioId)return;t.start(d),r?.start()}),l=()=>{i.forEach((d)=>d())},g=()=>{if(!n.reconnectReportPath||typeof fetch>"u")return;let d=o.getSnapshot(),h=JSON.stringify({at:Date.now(),reconnect:d.reconnect,scenarioId:d.scenarioId,sessionId:t.getSessionId(),turnIds:d.turns.map((a)=>a.id)});fetch(n.reconnectReportPath,{body:h,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},y=t.subscribe((d)=>{let h=Ae(d);if(h){if(o.dispatch(h),d.type==="connection")g();l()}});return{callControl(d){t.callControl(d)},close(){y(),r?.close(),t.close(),o.dispatch({type:"disconnected"}),l()},endTurn(){t.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:c,get partial(){return o.getSnapshot().partial},get reconnect(){return o.getSnapshot().reconnect},get sessionId(){return t.getSessionId()},get status(){return o.getSnapshot().status},get turns(){return o.getSnapshot().turns},get assistantTexts(){return o.getSnapshot().assistantTexts},get assistantAudio(){return o.getSnapshot().assistantAudio},get call(){return o.getSnapshot().call},sendAudio(d){t.sendAudio(d)},simulateDisconnect(){t.simulateDisconnect()},subscribe(d){return i.add(d),()=>{i.delete(d)}}}};var Ve=(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 pe={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}},en={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 we=(e)=>{let n=e?.profile??"fast",t=e?.qualityProfile??"general",o=pe[n],r=en[t];return{profile:n,qualityProfile:t,silenceMs:e?.silenceMs??r.silenceMs??o.silenceMs,speechThreshold:e?.speechThreshold??r.speechThreshold??o.speechThreshold,transcriptStabilityMs:e?.transcriptStabilityMs??r.transcriptStabilityMs??o.transcriptStabilityMs}};var nn={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"}}},Le=(e="default")=>{let n=nn[e];return{audioConditioning:Ve(n.audioConditioning),capture:{channelCount:n.capture?.channelCount??1,sampleRateHz:n.capture?.sampleRateHz??16000},connection:{...n.connection},name:e,sttLifecycle:n.sttLifecycle??"continuous",turnDetection:we(n.turnDetection)}};var on=(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]}),m=(e,n={})=>{let t=Le(n.preset),o=Ie(e,{...t.connection,...n.connection}),r=null,i=on(o),c=new Set,l=()=>{for(let C of c)C()},g=()=>{if(i={...i,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&&i.status==="completed"&&i.isRecording)r?.stop(),r=null,i={...i,isRecording:!1};l()},y=o.subscribe(g);g();let d=()=>{if(r)return r;return r=ge({channelCount:n.capture?.channelCount??t.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??t.capture.sampleRateHz}),r},h=()=>{r?.stop(),r=null,i={...i,isRecording:!1},l()},a=async()=>{if(i.isRecording)return;try{i={...i,recordingError:null},l(),await d().start(),i={...i,isRecording:!0},l()}catch(C){throw r=null,i={...i,isRecording:!1,recordingError:C instanceof Error?C.message:String(C)},l(),C}};return{bindHTMX(C){return de(o,C)},callControl:(C)=>o.callControl(C),close:()=>{y(),h(),o.close()},endTurn:()=>o.endTurn(),get error(){return i.error},getServerSnapshot:()=>i,getSnapshot:()=>i,get isConnected(){return i.isConnected},get isRecording(){return i.isRecording},get partial(){return i.partial},get recordingError(){return i.recordingError},get reconnect(){return i.reconnect},sendAudio:(C)=>o.sendAudio(C),simulateDisconnect:()=>o.simulateDisconnect(),get sessionId(){return i.sessionId},get sessionMetadata(){return i.sessionMetadata},get scenarioId(){return i.scenarioId},startRecording:a,get status(){return i.status},stopRecording:h,subscribe:(C)=>{return c.add(C),()=>{c.delete(C)}},toggleRecording:async()=>{if(i.isRecording){h();return}await a()},get turns(){return i.turns},get assistantTexts(){return i.assistantTexts},get assistantAudio(){return i.assistantAudio},get call(){return i.call}}};var tn=()=>({activeSourceCount:0,error:null,isActive:!1,isPlaying:!1,lastInterruptLatencyMs:void 0,lastPlaybackStopLatencyMs:void 0,processedChunkCount:0,queuedChunkCount:0}),cn=()=>{if(typeof window>"u")return typeof AudioContext>"u"?void 0:AudioContext;return window.AudioContext??window.webkitAudioContext},rn=(e,n)=>{let t=n.format;if(t.container!=="raw"||t.encoding!=="pcm_s16le")throw Error(`Unsupported assistant audio format: ${t.container}/${t.encoding}`);let o=n.chunk,r=Math.max(1,t.channels),i=Math.floor(o.byteLength/2),c=Math.max(1,Math.floor(i/r)),l=e.createBuffer(r,c,t.sampleRateHz),g=new DataView(o.buffer,o.byteOffset,o.byteLength);for(let y=0;y<r;y+=1){let d=l.getChannelData(y);for(let h=0;h<c;h+=1){let M=(h*r+y)*2;if(M+1>=o.byteLength){d[h]=0;continue}d[h]=g.getInt16(M,!0)/32768}}return l},j=(e,n={})=>{let t=new Set,o=new Set,r=(n.lookaheadMs??15)/1000,i=tn(),c=null,l=null,g=0,y=Promise.resolve(),d=null,h=null,a=null,M=null,C=()=>{for(let A of t)A()},I=(A)=>{i={...i,...A},C()},w=()=>{if(i.error!==null)I({error:null})},R=()=>{if(M!==null)clearTimeout(M),M=null},u=(A)=>{R(),d=null,I({activeSourceCount:o.size,isPlaying:!1,lastInterruptLatencyMs:A,lastPlaybackStopLatencyMs:i.lastPlaybackStopLatencyMs??A}),a?.(),a=null,h=null},O=(A)=>{if(!A)return 0;return Math.max(0,((A.baseLatency??0)+(A.outputLatency??0))*1000)},U=(A)=>{if(!l)return;let f=1;if(l.gain.setValueAtTime){l.gain.setValueAtTime(f,A?.currentTime??0);return}l.gain.value=f},s=(A)=>{if(!l)return;let f=0;if(l.gain.setValueAtTime){l.gain.setValueAtTime(f,A?.currentTime??0);return}l.gain.value=f},S=()=>{if(d===null||o.size>0)return;u(Date.now()-d)},L=async()=>{if(c)return c;if(n.createAudioContext)c=n.createAudioContext();else{let A=cn();if(!A)throw Error("Assistant audio playback requires AudioContext support.");c=new A}if(c.createGain)l=c.createGain(),l.connect?.(c.destination);return g=c.currentTime,c},X=async(A)=>{let f=await L(),E=rn(f,A),V=f.createBufferSource();V.buffer=E,V.connect(l??f.destination),V.onended=()=>{o.delete(V),V.disconnect?.(),I({activeSourceCount:o.size,isPlaying:o.size>0&&i.isActive}),S()};let Y=Math.max(f.currentTime+r,g);g=Y+E.duration,o.add(V),I({activeSourceCount:o.size,isPlaying:!0}),V.start(Y)},_=(A)=>{for(let f of[...o])f.stop?.();if(g=c?c.currentTime:0,A?.forceClear){for(let f of o)f.disconnect?.();o.clear(),S()}},W=async()=>{if(!i.isActive)return;let A=e.assistantAudio.slice(i.processedChunkCount);if(A.length===0)return;try{w();for(let f of A)await X(f);I({processedChunkCount:e.assistantAudio.length,queuedChunkCount:i.queuedChunkCount+A.length})}catch(f){I({error:f instanceof Error?f.message:String(f)})}},P=()=>{return y=y.then(()=>W(),()=>W()),y},$=e.subscribe(()=>{if(n.autoStart&&!i.isActive&&e.assistantAudio.length>0){N.start();return}if(i.isActive)P()}),N={close:async()=>{if($(),_({forceClear:!0}),R(),a?.(),a=null,h=null,d=null,c&&c.state!=="closed")await c.close();c=null,l?.disconnect?.(),l=null,g=0,I({activeSourceCount:0,isActive:!1,isPlaying:!1})},get activeSourceCount(){return i.activeSourceCount},get error(){return i.error},getSnapshot:()=>i,get isActive(){return i.isActive},get isPlaying(){return i.isPlaying},interrupt:async()=>{let A=Date.now(),f=await L();d=A,s(f);let E=Date.now()-A+O(f);if(I({isActive:!1,isPlaying:o.size>0,lastPlaybackStopLatencyMs:E}),o.size===0){u(E);return}if(!h)h=new Promise((V)=>{a=V});R(),M=setTimeout(()=>{for(let V of o)V.disconnect?.();o.clear(),u(Date.now()-A)},250),_(),await h},get lastInterruptLatencyMs(){return i.lastInterruptLatencyMs},get lastPlaybackStopLatencyMs(){return i.lastPlaybackStopLatencyMs},pause:async()=>{if(!c){I({activeSourceCount:0,isActive:!1,isPlaying:!1});return}await c.suspend(),I({activeSourceCount:o.size,isActive:!1,isPlaying:!1})},get processedChunkCount(){return i.processedChunkCount},get queuedChunkCount(){return i.queuedChunkCount},start:async()=>{try{w();let A=await L();if(U(A),A.state==="suspended")await A.resume();I({activeSourceCount:o.size,isActive:!0,isPlaying:A.state==="running"}),await P()}catch(A){throw I({error:A instanceof Error?A.message:String(A),isActive:!1,isPlaying:!1}),A}},subscribe:(A)=>{return t.add(A),()=>{t.delete(A)}}};return N};var sn=()=>`barge-in:${Date.now()}:${crypto.randomUUID?.()??Math.random().toString(36).slice(2)}`,ln=(e,n)=>{let t=e.filter((c)=>c.status==="stopped"),o=t.map((c)=>c.latencyMs).filter((c)=>typeof c==="number"),r=t.filter((c)=>typeof c.latencyMs==="number"&&c.latencyMs>n).length,i=t.length-r;return{averageLatencyMs:o.length>0?Math.round(o.reduce((c,l)=>c+l,0)/o.length):void 0,events:[...e],failed:r,lastEvent:e.at(-1),passed:i,status:e.length===0?"empty":r>0?"fail":t.length===0?"warn":"pass",thresholdMs:n,total:t.length}},ue=(e={})=>{let n=new Set,t=e.thresholdMs??250,o=e.fetch??globalThis.fetch,r=[],i=()=>{for(let g of n)g()},c=(g)=>{if(!e.path||typeof o!=="function")return;o(e.path,{body:JSON.stringify(g),headers:{"Content-Type":"application/json"},method:"POST"}).catch(()=>{})},l=(g,y)=>{let d={at:Date.now(),id:sn(),latencyMs:y.latencyMs,playbackStopLatencyMs:y.playbackStopLatencyMs,reason:y.reason,sessionId:y.sessionId,status:g,thresholdMs:t};return r.push(d),c(d),i(),d};return{getSnapshot:()=>ln(r,t),recordRequested:(g)=>l("requested",g),recordSkipped:(g)=>l("skipped",g),recordStopped:(g)=>l("stopped",g),subscribe:(g)=>{return n.add(g),()=>{n.delete(g)}}}};var an=0.08,dn=(e,n={})=>(n.enabled??!0)&&e>=(n.interruptThreshold??an),oe=(e,n,t={})=>{let o=e.partial,r=(c)=>{if(!n.isPlaying||t.enabled===!1){t.monitor?.recordSkipped({reason:c,sessionId:e.sessionId});return}t.monitor?.recordRequested({reason:c,sessionId:e.sessionId}),n.interrupt().then(()=>{t.monitor?.recordStopped({latencyMs:n.lastInterruptLatencyMs,playbackStopLatencyMs:n.lastPlaybackStopLatencyMs,reason:c,sessionId:e.sessionId})})},i=e.subscribe(()=>{if(t.interruptOnPartial===!1){o=e.partial;return}if(!o&&e.partial)r("partial-transcript");o=e.partial});return{close:()=>{i()},handleLevel:(c)=>{if(dn(c,t))r("input-level")},sendAudio:(c)=>{r("manual-audio"),e.sendAudio(c)}}};var se=48,gn=320,An=88,hn="Guided test",yn="General recording",Cn="Pick a scenario to begin the demo.",fn="I can walk you through a short guided voice test.",Tn="I can capture one freeform recording and confirm that it landed.",Sn="Choose a scenario to begin. Guided test asks follow-up prompts. General recording just captures what you say.",Mn="Click Start general recording to capture one freeform answer.",_e="Speak freely. When you pause, the recording will be captured.",re="Recording saved. Start again if you want another capture.",Ee="Guided test complete. Review the saved summary below.",Pe="All prompts are covered. You can stop the microphone or keep speaking for extra detail.",In="Ready. Start guided test or general recording to begin.",Vn="Live. Answer the prompt, then click Stop microphone when finished.",Re=["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."],De=(e,n,t)=>Math.min(t,Math.max(n,e)),q=(e)=>e.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll(\'"\',""").replaceAll("\'","'"),te=(e,n)=>{let t=e[n];if(typeof t==="string"&&t.trim())return t;return null},ie=(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,t=te(n,"message")??te(n,"reason")??te(n,"description");if(t)return t;if("error"in n)return ie(n.error);if("cause"in n)return ie(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 t=Math.max(0,e.nextAttemptAt-Date.now());n.push(`retry in ${Math.ceil(t/100)/10}s`)}return n.join(" \xB7 ")},v=(e=se)=>Array.from({length:e},()=>0),be=(e,n,t=se)=>{let o=e.slice(-(t-1));o.push(De(n,0,1));while(o.length<t)o.unshift(0);return o},Ln=(e,n=gn,t=An)=>{let o=e.length>1?e:v(se),r=n/(o.length-1),i=t/2,c=t*0.34;if(Math.max(...o,0)<=0.015)return`M 0 ${i} L ${n} ${i}`;let g=o.map((d,h)=>{let a=h*0.76,M=Math.sin(a)*0.78+Math.sin(a*0.41)*0.22,C=d*c,I=r*h,w=De(i+M*C,8,t-8);return{x:I,y:w}});if(g.length===0)return`M 0 ${i} L ${n} ${i}`;let y=`M ${g[0]?.x??0} ${g[0]?.y??i}`;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},un=(e)=>{if(!e)return Re;try{let n=JSON.parse(e);if(Array.isArray(n)){let t=n.filter((o)=>typeof o==="string").map((o)=>o.trim()).filter(Boolean);if(t.length>0)return t}}catch{}return Re},ce=(e)=>{if(!e)return;let n=Number(e);return Number.isFinite(n)?n:void 0},Rn=(e,n,t)=>{if(!n)return null;let o=document.querySelector(n);return o instanceof t?o:null},x=(e,n,t,o)=>{let r=n?document.querySelector(n):null;if(r instanceof t)return r;let i=e.querySelector(`#${o}`);if(i instanceof t)return i;throw Error(`Voice HTMX bootstrap could not find the required element "${o}".`)},bn=(e)=>{if(!e.mode)return Cn;if(!e.hasStarted)return e.mode==="guided"?fn:Tn;if(e.status==="completed")return e.mode==="guided"?Ee:re;if(e.mode==="general")return _e;return e.guidedPrompts[e.turnCount]??Pe},_n=(e)=>{if(!e.mode)return Sn;if(e.status==="completed")return e.mode==="guided"?Ee:re;if(!e.hasStarted)return e.mode==="guided"?`Click Start guided test to begin. First prompt: ${e.guidedPrompts[0]??"Answer the first prompt."}`:Mn;if(e.mode==="general")return e.turnCount===0?_e:re;return e.guidedPrompts[e.turnCount]??Pe},En=(e)=>{let n=e.dataset.voiceGuidedPath,t=e.dataset.voiceGeneralPath;if(!n||!t)throw Error("Voice HTMX bootstrap requires data-voice-guided-path and data-voice-general-path.");let o=un(e.dataset.voiceGuidedPrompts),r=e.dataset.voiceGuidedLabel??hn,i=e.dataset.voiceGeneralLabel??yn,c=e.dataset.voiceReconnectReportPath,l=e.dataset.voiceBargeInPath,g=l?ue({path:l,thresholdMs:ce(e.dataset.voiceBargeInThresholdMs)}):null,y=ce(e.dataset.voiceBargeInRecentWindowMs)??4000,d=ce(e.dataset.voiceBargeInSpeechThreshold)??0.04,h=x(document,e.dataset.voiceSync,HTMLElement,"voice-htmx-sync"),a=x(e,e.dataset.voiceConnection,HTMLElement,"metric-connection"),M=x(e,e.dataset.voiceError,HTMLElement,"status-error"),C=x(e,e.dataset.voiceMicrophone,HTMLElement,"status-mic"),I=x(e,e.dataset.voicePrompt,HTMLElement,"status-prompt"),w=Rn(e,e.dataset.voiceReconnect,HTMLElement),R=x(e,e.dataset.voiceChat,HTMLElement,"chat-list"),u=x(e,e.dataset.voiceStartGuided,HTMLButtonElement,"start-guided"),O=x(e,e.dataset.voiceStartGeneral,HTMLButtonElement,"start-general"),U=x(e,e.dataset.voiceStop,HTMLButtonElement,"stop-mic"),s=x(e,e.dataset.voiceMonitor,HTMLElement,"voice-monitor"),S=x(e,e.dataset.voiceMonitorCopy,HTMLElement,"voice-monitor-copy"),L=x(e,e.dataset.voiceWaveGlow,SVGPathElement,"voice-wave-glow"),X=x(e,e.dataset.voiceWavePath,SVGPathElement,"voice-wave-path"),_=null,W={general:!1,guided:!1},P=!1,$=null,N=v(),A=null,f=null,E=m(n,{capture:{onAudio:(T,G)=>{if(A){A.sendAudio(T);return}G(T)},onLevel:(T)=>{A?.handleLevel(T),N=be(N,T),F()}},connection:{reconnectReportPath:c},preset:"guided-intake"}),V=m(t,{capture:{onAudio:(T,G)=>{if(f){f.sendAudio(T);return}G(T)},onLevel:(T)=>{f?.handleLevel(T),N=be(N,T),F()}},connection:{reconnectReportPath:c},preset:"dictation"}),Y=E.bindHTMX({element:h}),xe=V.bindHTMX({element:h}),J=j(E),Q=j(V);A=oe(E,J,{interruptThreshold:d,monitor:g??void 0}),f=oe(V,Q,{interruptThreshold:d,monitor:g??void 0});let z=()=>_==="general"?V:E,Dn=()=>_==="general"?Q:J,F=()=>{let T=Ln(N);L.setAttribute("d",T),X.setAttribute("d",T),S.innerHTML=`<span class="voice-live-dot"></span>${P?"Microphone live":"Microphone idle"}`,S.classList.toggle("is-live",P),s.classList.toggle("is-live",P)},k=()=>{let T=z(),G=(_?W[_]:!1)||T.turns.length>0,ae=T.status;if(a.textContent=T.isConnected?"Connected":"Waiting",M.textContent=$||T.error||"None",w)w.textContent=wn(T.reconnect);C.textContent=P?Vn:In,I.textContent=_n({guidedPrompts:o,hasStarted:G,mode:_,status:ae,turnCount:T.turns.length}),u.hidden=P,O.hidden=P,U.hidden=!P,R.innerHTML=`<article class="voice-chat-message assistant">\n <div class="voice-chat-role">${q(_==="general"?i:_==="guided"?r:"Voice demo")}</div>\n <p class="voice-turn-text">${q(bn({generalLabel:i,guidedLabel:r,guidedPrompts:o,hasStarted:G,mode:_,status:ae,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">${q(p.text)}</p>\n </article>\n ${p.assistantText?`<article class="voice-chat-message assistant">\n <div class="voice-chat-role">${q(_==="general"?i:_==="guided"?r:"Guide")}</div>\n <p class="voice-turn-text">${q(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">${q(T.partial)}</p>\n</article>`:""}`,F()},Oe=()=>{z().stopRecording(),P=!1,$=null,N=v(),k()},le=async(T)=>{_=T,W={...W,[T]:!0};try{await z().startRecording(),$=null,P=!0,k()}catch(G){z().stopRecording(),P=!1,N=v(),$=ie(G),k()}};E.subscribe(()=>{if(E.assistantAudio.length>0)J.start().catch(()=>{});k()}),V.subscribe(()=>{if(V.assistantAudio.length>0)Q.start().catch(()=>{});k()}),u.addEventListener("click",()=>{le("guided")}),O.addEventListener("click",()=>{le("general")}),U.addEventListener("click",()=>{Oe()}),e.addEventListener("absolute-voice-simulate-disconnect",()=>{z().simulateDisconnect()}),window.addEventListener("beforeunload",()=>{E.stopRecording(),V.stopRecording(),A?.close(),f?.close(),J.close(),Q.close(),Y(),xe(),E.close(),V.close()}),k()},Pn=()=>{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)En(n)};Pn();export{Pn as initVoiceHTMX};\n';
|
|
5190
5190
|
|
|
5191
5191
|
// src/profileSwitchRecommendation.ts
|
|
5192
5192
|
import { Elysia } from "elysia";
|
|
@@ -16525,6 +16525,7 @@ var readString2 = (value) => typeof value === "string" && value.trim() ? value :
|
|
|
16525
16525
|
var readNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
16526
16526
|
var readProofTrendMaxLiveP95 = (report) => report.summary.maxLiveP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.liveLatency?.p95Ms));
|
|
16527
16527
|
var readProofTrendMaxProviderP95 = (report) => report.summary.maxProviderP95Ms ?? maxNumber((report.summary.providers ?? []).map((provider) => provider.p95Ms)) ?? maxNumber(report.cycles.flatMap((cycle) => (cycle.providers ?? []).map((provider) => provider.p95Ms)));
|
|
16528
|
+
var readProofTrendMaxReconnectP95 = (report) => report.summary.maxReconnectP95Ms ?? report.summary.reconnect?.resumeLatencyP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.reconnect?.resumeLatencyP95Ms));
|
|
16528
16529
|
var readProofTrendMaxTurnP95 = (report) => report.summary.maxTurnP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.turnLatency?.p95Ms));
|
|
16529
16530
|
var readRuntimeChannelMetric = (report, key) => {
|
|
16530
16531
|
const summaryValue = report.summary.runtimeChannel?.[key];
|
|
@@ -16586,6 +16587,42 @@ var aggregateProofTrendRuntimeChannel = (channels) => {
|
|
|
16586
16587
|
status: channels.some((channel) => channel.status === "fail") ? "fail" : channels.some((channel) => channel.status === "warn") ? "warn" : channels.every((channel) => channel.status === "pass") ? "pass" : undefined
|
|
16587
16588
|
};
|
|
16588
16589
|
};
|
|
16590
|
+
var aggregateProofTrendReconnect = (reconnects) => {
|
|
16591
|
+
if (reconnects.length === 0) {
|
|
16592
|
+
return;
|
|
16593
|
+
}
|
|
16594
|
+
const hasFailure = reconnects.some((reconnect) => reconnect.status === "fail" || reconnect.exhausted === true || reconnect.reconnected === false || reconnect.resumed === false);
|
|
16595
|
+
return {
|
|
16596
|
+
attempts: maxNumber(reconnects.map((reconnect) => reconnect.attempts)),
|
|
16597
|
+
exhausted: reconnects.some((reconnect) => reconnect.exhausted === true),
|
|
16598
|
+
maxAttempts: maxNumber(reconnects.map((reconnect) => reconnect.maxAttempts)),
|
|
16599
|
+
resumeLatencyP95Ms: maxNumber(reconnects.map((reconnect) => reconnect.resumeLatencyP95Ms)),
|
|
16600
|
+
reconnected: reconnects.some((reconnect) => reconnect.reconnected === true),
|
|
16601
|
+
resumed: reconnects.some((reconnect) => reconnect.resumed === true),
|
|
16602
|
+
samples: reconnects.reduce((total, reconnect) => total + (reconnect.samples ?? 0), 0),
|
|
16603
|
+
snapshotCount: reconnects.reduce((total, reconnect) => total + (reconnect.snapshotCount ?? 0), 0),
|
|
16604
|
+
status: hasFailure ? "fail" : reconnects.some((reconnect) => reconnect.status === "warn") ? "warn" : "pass"
|
|
16605
|
+
};
|
|
16606
|
+
};
|
|
16607
|
+
var summarizeReconnectTraceEvidence = (events) => {
|
|
16608
|
+
const reconnectEvents = events.filter((event) => event.type === "client.reconnect");
|
|
16609
|
+
if (reconnectEvents.length === 0) {
|
|
16610
|
+
return;
|
|
16611
|
+
}
|
|
16612
|
+
const statuses = reconnectEvents.map((event) => readString2(readTraceRecord(event).status)).filter((status) => status !== undefined);
|
|
16613
|
+
const reconnectPayloads = reconnectEvents.map((event) => readTraceRecord(event));
|
|
16614
|
+
return {
|
|
16615
|
+
attempts: maxNumber(reconnectPayloads.map((payload) => readNumber2(payload.attempts))),
|
|
16616
|
+
exhausted: statuses.includes("exhausted"),
|
|
16617
|
+
maxAttempts: maxNumber(reconnectPayloads.map((payload) => readNumber2(payload.maxAttempts))),
|
|
16618
|
+
resumeLatencyP95Ms: percentile2(reconnectPayloads.map((payload) => readNumber2(payload.resumeLatencyP95Ms) ?? readNumber2(payload.resumeLatencyMs)).filter((value) => value !== undefined), 95),
|
|
16619
|
+
reconnected: statuses.includes("reconnecting"),
|
|
16620
|
+
resumed: statuses.includes("resumed") || statuses.includes("pass"),
|
|
16621
|
+
samples: reconnectEvents.length,
|
|
16622
|
+
snapshotCount: reconnectEvents.length,
|
|
16623
|
+
status: reconnectEvents.some((event) => isFailingTraceStatus(readTraceStatus(readTraceRecord(event)))) ? "fail" : "pass"
|
|
16624
|
+
};
|
|
16625
|
+
};
|
|
16589
16626
|
var readTraceRecord = (event) => event.payload;
|
|
16590
16627
|
var readTraceProfileId = (events, options) => {
|
|
16591
16628
|
for (const event of events) {
|
|
@@ -16733,6 +16770,7 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
|
|
|
16733
16770
|
}).filter((value) => value !== undefined);
|
|
16734
16771
|
const turnP95Ms = summarizeTurnTraceP95(sessionEvents);
|
|
16735
16772
|
const providerP95Ms = maxNumber(providers.map((provider) => provider.p95Ms));
|
|
16773
|
+
const reconnect = summarizeReconnectTraceEvidence(sessionEvents);
|
|
16736
16774
|
const runtimeChannel = summarizeRuntimeChannelTraceEvidence(sessionEvents);
|
|
16737
16775
|
const surfaces = readRealCallProfileTraceSurfaces(sessionEvents);
|
|
16738
16776
|
if (providers.length === 0 && runtimeChannel === undefined && liveLatencies.length === 0 && surfaces.length === 0) {
|
|
@@ -16748,6 +16786,7 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
|
|
|
16748
16786
|
profileLabel: options.profileLabels?.[profileId] ?? (profileId === options.defaultProfileId ? options.defaultProfileLabel : undefined),
|
|
16749
16787
|
providerP95Ms,
|
|
16750
16788
|
providers,
|
|
16789
|
+
reconnect,
|
|
16751
16790
|
runtimeChannel,
|
|
16752
16791
|
sessionId,
|
|
16753
16792
|
surfaces: surfaces.length > 0 ? surfaces : undefined,
|
|
@@ -16755,11 +16794,45 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
|
|
|
16755
16794
|
};
|
|
16756
16795
|
}).filter((evidence) => evidence !== undefined);
|
|
16757
16796
|
};
|
|
16797
|
+
var normalizeReconnectReport = (report) => ("contract" in report) ? report.contract : report;
|
|
16798
|
+
var buildVoiceRealCallProfileEvidenceFromReconnectProofReports = (input, options = {}) => {
|
|
16799
|
+
const reports = Array.isArray(input) ? input : [input];
|
|
16800
|
+
const profileId = options.profileId ?? "reconnect-resume";
|
|
16801
|
+
return reports.map((report, index) => {
|
|
16802
|
+
const contract = normalizeReconnectReport(report);
|
|
16803
|
+
const generatedAt = "generatedAt" in report ? report.generatedAt : new Date(contract.checkedAt).toISOString();
|
|
16804
|
+
const ok = "ok" in report ? report.ok : contract.pass;
|
|
16805
|
+
const operationsRecordHref = typeof options.operationsRecordHref === "function" ? options.operationsRecordHref(report, index) : options.operationsRecordHref;
|
|
16806
|
+
const sessionId = typeof options.sessionId === "function" ? options.sessionId(report, index) : options.sessionId ?? `reconnect-proof-${String(index + 1)}-${String(contract.checkedAt)}`;
|
|
16807
|
+
return {
|
|
16808
|
+
generatedAt,
|
|
16809
|
+
ok,
|
|
16810
|
+
operationsRecordHref,
|
|
16811
|
+
profileDescription: options.profileDescription,
|
|
16812
|
+
profileId,
|
|
16813
|
+
profileLabel: options.profileLabel,
|
|
16814
|
+
reconnect: {
|
|
16815
|
+
attempts: contract.summary.attempts,
|
|
16816
|
+
exhausted: contract.summary.exhausted,
|
|
16817
|
+
maxAttempts: contract.summary.maxAttempts,
|
|
16818
|
+
resumeLatencyP95Ms: contract.resumeLatencyP95Ms,
|
|
16819
|
+
reconnected: contract.summary.reconnected,
|
|
16820
|
+
resumed: contract.summary.resumed,
|
|
16821
|
+
samples: 1,
|
|
16822
|
+
snapshotCount: contract.snapshotCount,
|
|
16823
|
+
status: ok ? "pass" : "fail"
|
|
16824
|
+
},
|
|
16825
|
+
sessionId,
|
|
16826
|
+
surfaces: [...new Set(["reconnect", ...options.surfaces ?? []])].sort()
|
|
16827
|
+
};
|
|
16828
|
+
});
|
|
16829
|
+
};
|
|
16758
16830
|
var loadVoiceRealCallProfileEvidenceFromTraceStore = async (options) => buildVoiceRealCallProfileEvidenceFromTraceEvents(await options.store.list({ limit: options.limit ?? 5000 }), options);
|
|
16759
16831
|
var realCallProfileTraceSignalTypes = new Set([
|
|
16760
16832
|
"client.barge_in",
|
|
16761
16833
|
"client.browser_media",
|
|
16762
16834
|
"client.live_latency",
|
|
16835
|
+
"client.reconnect",
|
|
16763
16836
|
"client.telephony_media",
|
|
16764
16837
|
"provider.decision",
|
|
16765
16838
|
"session.error",
|
|
@@ -16822,15 +16895,16 @@ var readProofTrendProviders = (reports) => aggregateProofTrendProviders(reports.
|
|
|
16822
16895
|
var exceedsProofTrendBudget = (value, budget) => value !== undefined && (!Number.isFinite(value) || value > budget);
|
|
16823
16896
|
var readProofTrendProfileStatus = (profile, budgets) => {
|
|
16824
16897
|
const runtimeChannel = profile.runtimeChannel;
|
|
16825
|
-
const
|
|
16826
|
-
const
|
|
16898
|
+
const reconnect = profile.reconnect;
|
|
16899
|
+
const hasBudgetEvidence = profile.maxLiveP95Ms !== undefined || profile.maxProviderP95Ms !== undefined || profile.maxReconnectP95Ms !== undefined || profile.maxTurnP95Ms !== undefined || profile.providers?.some((provider) => provider.p95Ms !== undefined) === true || runtimeChannel?.maxFirstAudioLatencyMs !== undefined || runtimeChannel?.maxInterruptionP95Ms !== undefined || runtimeChannel?.maxJitterMs !== undefined || runtimeChannel?.maxTimestampDriftMs !== undefined || runtimeChannel?.maxBackpressureEvents !== undefined || reconnect?.resumeLatencyP95Ms !== undefined || reconnect?.samples !== undefined;
|
|
16900
|
+
const budgetFailed = exceedsProofTrendBudget(profile.maxLiveP95Ms, budgets.maxLiveP95Ms) || exceedsProofTrendBudget(profile.maxProviderP95Ms, budgets.maxProviderP95Ms) || exceedsProofTrendBudget(profile.maxReconnectP95Ms ?? reconnect?.resumeLatencyP95Ms, budgets.maxReconnectP95Ms) || exceedsProofTrendBudget(profile.maxTurnP95Ms, budgets.maxTurnP95Ms) || exceedsProofTrendBudget(runtimeChannel?.maxFirstAudioLatencyMs, budgets.maxRuntimeFirstAudioLatencyMs) || exceedsProofTrendBudget(runtimeChannel?.maxInterruptionP95Ms, budgets.maxRuntimeInterruptionP95Ms) || exceedsProofTrendBudget(runtimeChannel?.maxJitterMs, budgets.maxRuntimeJitterMs) || exceedsProofTrendBudget(runtimeChannel?.maxTimestampDriftMs, budgets.maxRuntimeTimestampDriftMs) || exceedsProofTrendBudget(runtimeChannel?.maxBackpressureEvents, 0) || reconnect?.status === "fail";
|
|
16827
16901
|
if (budgetFailed || !hasBudgetEvidence && profile.status === "fail") {
|
|
16828
16902
|
return "fail";
|
|
16829
16903
|
}
|
|
16830
|
-
if (profile.status === "warn" || runtimeChannel?.status === "warn" || profile.providers?.some((provider) => provider.status === "warn") === true) {
|
|
16904
|
+
if (profile.status === "warn" || reconnect?.status === "warn" || runtimeChannel?.status === "warn" || profile.providers?.some((provider) => provider.status === "warn") === true) {
|
|
16831
16905
|
return "warn";
|
|
16832
16906
|
}
|
|
16833
|
-
if (hasBudgetEvidence || profile.status === "pass" || runtimeChannel?.status === "pass" || profile.providers?.some((provider) => provider.status === "pass") === true) {
|
|
16907
|
+
if (hasBudgetEvidence || profile.status === "pass" || reconnect?.status === "pass" || runtimeChannel?.status === "pass" || profile.providers?.some((provider) => provider.status === "pass") === true) {
|
|
16834
16908
|
return "pass";
|
|
16835
16909
|
}
|
|
16836
16910
|
return;
|
|
@@ -16840,6 +16914,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
|
|
|
16840
16914
|
const definitions = options.profiles ?? DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS;
|
|
16841
16915
|
const providerCap = options.maxProviderP95Ms ?? 1000;
|
|
16842
16916
|
const liveCap = options.maxLiveP95Ms ?? 800;
|
|
16917
|
+
const reconnectCap = options.maxReconnectP95Ms ?? 1500;
|
|
16843
16918
|
const turnCap = options.maxTurnP95Ms ?? 700;
|
|
16844
16919
|
const runtimeFirstAudioCap = options.maxRuntimeFirstAudioLatencyMs ?? 600;
|
|
16845
16920
|
const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
|
|
@@ -16848,6 +16923,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
|
|
|
16848
16923
|
const budgets = {
|
|
16849
16924
|
maxLiveP95Ms: liveCap,
|
|
16850
16925
|
maxProviderP95Ms: providerCap,
|
|
16926
|
+
maxReconnectP95Ms: reconnectCap,
|
|
16851
16927
|
maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
|
|
16852
16928
|
maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
|
|
16853
16929
|
maxRuntimeJitterMs: runtimeJitterCap,
|
|
@@ -16870,8 +16946,10 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
|
|
|
16870
16946
|
label: definition.label ?? profiles.find(Boolean)?.label,
|
|
16871
16947
|
maxLiveP95Ms: maxNumber(profiles.map((profile) => profile.maxLiveP95Ms)),
|
|
16872
16948
|
maxProviderP95Ms: maxNumber(profiles.map((profile) => profile.maxProviderP95Ms)),
|
|
16949
|
+
maxReconnectP95Ms: maxNumber(profiles.map((profile) => profile.maxReconnectP95Ms)),
|
|
16873
16950
|
maxTurnP95Ms: maxNumber(profiles.map((profile) => profile.maxTurnP95Ms)),
|
|
16874
16951
|
providers: aggregateProofTrendProviders(profiles.flatMap((profile) => profile.providers ?? [])),
|
|
16952
|
+
reconnect: aggregateProofTrendReconnect(profiles.map((profile) => profile.reconnect).filter((reconnect) => reconnect !== undefined)),
|
|
16875
16953
|
runtimeChannel: aggregateProofTrendRuntimeChannel(profiles.map((profile) => profile.runtimeChannel).filter((channel) => channel !== undefined)),
|
|
16876
16954
|
sessionCount: profiles.reduce((total, profile) => total + (profile.sessionCount ?? 0), 0),
|
|
16877
16955
|
surfaces: [
|
|
@@ -16911,6 +16989,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
|
|
|
16911
16989
|
var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
|
|
16912
16990
|
const providerCap = options.maxProviderP95Ms ?? 1000;
|
|
16913
16991
|
const liveCap = options.maxLiveP95Ms ?? 800;
|
|
16992
|
+
const reconnectCap = options.maxReconnectP95Ms ?? 1500;
|
|
16914
16993
|
const turnCap = options.maxTurnP95Ms ?? 700;
|
|
16915
16994
|
const runtimeFirstAudioCap = options.maxRuntimeFirstAudioLatencyMs ?? 600;
|
|
16916
16995
|
const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
|
|
@@ -16919,6 +16998,7 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
|
|
|
16919
16998
|
const budgets = {
|
|
16920
16999
|
maxLiveP95Ms: liveCap,
|
|
16921
17000
|
maxProviderP95Ms: providerCap,
|
|
17001
|
+
maxReconnectP95Ms: reconnectCap,
|
|
16922
17002
|
maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
|
|
16923
17003
|
maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
|
|
16924
17004
|
maxRuntimeJitterMs: runtimeJitterCap,
|
|
@@ -16948,8 +17028,10 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
|
|
|
16948
17028
|
label: definition.label ?? matchingEvidence.find((evidence) => evidence.profileLabel)?.profileLabel,
|
|
16949
17029
|
maxLiveP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.liveP95Ms)),
|
|
16950
17030
|
maxProviderP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.providerP95Ms)),
|
|
17031
|
+
maxReconnectP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.reconnect?.resumeLatencyP95Ms)),
|
|
16951
17032
|
maxTurnP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.turnP95Ms)),
|
|
16952
17033
|
providers: aggregateProofTrendProviders(matchingEvidence.flatMap((evidence) => evidence.providers ?? [])),
|
|
17034
|
+
reconnect: aggregateProofTrendReconnect(matchingEvidence.map((evidence) => evidence.reconnect).filter((reconnect) => reconnect !== undefined)),
|
|
16953
17035
|
runtimeChannel: aggregateProofTrendRuntimeChannel(matchingEvidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined)),
|
|
16954
17036
|
sessionCount: new Set(matchingEvidence.map((evidence) => evidence.sessionId)).size,
|
|
16955
17037
|
surfaces: [
|
|
@@ -16968,6 +17050,7 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
|
|
|
16968
17050
|
ok: evidence.ok !== false,
|
|
16969
17051
|
providers: evidence.providers,
|
|
16970
17052
|
runtimeChannel: evidence.runtimeChannel,
|
|
17053
|
+
reconnect: evidence.reconnect,
|
|
16971
17054
|
turnLatency: evidence.turnP95Ms === undefined ? undefined : {
|
|
16972
17055
|
p95Ms: evidence.turnP95Ms,
|
|
16973
17056
|
samples: 1,
|
|
@@ -16978,9 +17061,11 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
|
|
|
16978
17061
|
cycles: options.evidence.length,
|
|
16979
17062
|
maxLiveP95Ms: maxNumber(options.evidence.map((evidence) => evidence.liveP95Ms)),
|
|
16980
17063
|
maxProviderP95Ms: maxNumber(options.evidence.map((evidence) => evidence.providerP95Ms)),
|
|
17064
|
+
maxReconnectP95Ms: maxNumber(options.evidence.map((evidence) => evidence.reconnect?.resumeLatencyP95Ms)),
|
|
16981
17065
|
maxTurnP95Ms: maxNumber(options.evidence.map((evidence) => evidence.turnP95Ms)),
|
|
16982
17066
|
profiles,
|
|
16983
17067
|
providers: aggregateProofTrendProviders(options.evidence.flatMap((evidence) => evidence.providers ?? [])),
|
|
17068
|
+
reconnect: aggregateProofTrendReconnect(options.evidence.map((evidence) => evidence.reconnect).filter((reconnect) => reconnect !== undefined)),
|
|
16984
17069
|
runtimeChannel: aggregateProofTrendRuntimeChannel(options.evidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined))
|
|
16985
17070
|
};
|
|
16986
17071
|
return buildVoiceProofTrendReport({
|
|
@@ -17799,10 +17884,12 @@ var buildVoiceRealCallProfileHistoryReport = (options = {}) => {
|
|
|
17799
17884
|
failedReports: history.filter((report) => report.ok !== true).length,
|
|
17800
17885
|
maxLiveP95Ms: maxNumber(profileHistory.map(readProofTrendMaxLiveP95)),
|
|
17801
17886
|
maxProviderP95Ms: maxNumber(profileHistory.map(readProofTrendMaxProviderP95)),
|
|
17887
|
+
maxReconnectP95Ms: maxNumber(profileHistory.map(readProofTrendMaxReconnectP95)),
|
|
17802
17888
|
maxTurnP95Ms: maxNumber(profileHistory.map(readProofTrendMaxTurnP95)),
|
|
17803
17889
|
profileCount: profiles.length,
|
|
17804
17890
|
profiles,
|
|
17805
17891
|
providers: readProofTrendProviders(profileHistory),
|
|
17892
|
+
reconnect: aggregateProofTrendReconnect(profileHistory.map((report) => report.summary.reconnect).filter((reconnect) => reconnect !== undefined)),
|
|
17806
17893
|
runtimeChannel: aggregateProofTrendRuntimeChannel(profileHistory.map(readProofTrendRuntimeChannel).filter((channel) => channel !== undefined))
|
|
17807
17894
|
};
|
|
17808
17895
|
const trend = buildVoiceProofTrendReport({
|
|
@@ -20505,6 +20592,7 @@ var normalizeSample = (input) => {
|
|
|
20505
20592
|
interruptionP95Ms: input.summary.runtimeChannel?.maxInterruptionP95Ms,
|
|
20506
20593
|
ok: input.ok,
|
|
20507
20594
|
providerP95Ms: input.summary.maxProviderP95Ms,
|
|
20595
|
+
reconnectP95Ms: input.summary.maxReconnectP95Ms ?? input.summary.reconnect?.resumeLatencyP95Ms,
|
|
20508
20596
|
runId: input.runId,
|
|
20509
20597
|
source: input.source || input.outputDir,
|
|
20510
20598
|
turnP95Ms: input.summary.maxTurnP95Ms
|
|
@@ -42631,6 +42719,7 @@ export {
|
|
|
42631
42719
|
buildVoiceRealCallProfileReadinessCheck,
|
|
42632
42720
|
buildVoiceRealCallProfileHistoryReport,
|
|
42633
42721
|
buildVoiceRealCallProfileEvidenceFromTraceEvents,
|
|
42722
|
+
buildVoiceRealCallProfileEvidenceFromReconnectProofReports,
|
|
42634
42723
|
buildVoiceRealCallProfileDefaults,
|
|
42635
42724
|
buildVoiceReadinessRecoveryActions,
|
|
42636
42725
|
buildVoiceProviderSloReport,
|
package/dist/proofTrends.d.ts
CHANGED
|
@@ -2,14 +2,17 @@ import { Elysia } from "elysia";
|
|
|
2
2
|
import type { Database } from "bun:sqlite";
|
|
3
3
|
import { type VoiceProviderDecisionStatus } from "./providerDecisionTraces";
|
|
4
4
|
import type { VoiceProductionReadinessAction, VoiceProductionReadinessCheck, VoiceReadinessRecoveryAction } from "./productionReadiness";
|
|
5
|
+
import type { VoiceReconnectContractReport, VoiceReconnectProofReport } from "./reconnectContract";
|
|
5
6
|
import { StoredVoiceTraceEvent, VoiceTraceEventStore } from "./trace";
|
|
6
7
|
export type VoiceProofTrendStatus = "empty" | "fail" | "pass" | "stale";
|
|
7
8
|
export type VoiceProofTrendSummary = {
|
|
8
9
|
cycles?: number;
|
|
9
10
|
maxLiveP95Ms?: number;
|
|
10
11
|
maxProviderP95Ms?: number;
|
|
12
|
+
maxReconnectP95Ms?: number;
|
|
11
13
|
profiles?: VoiceProofTrendProfileSummary[];
|
|
12
14
|
providers?: VoiceProofTrendProviderSummary[];
|
|
15
|
+
reconnect?: VoiceProofTrendReconnectSummary;
|
|
13
16
|
runtimeChannel?: VoiceProofTrendRuntimeChannelSummary;
|
|
14
17
|
maxTurnP95Ms?: number;
|
|
15
18
|
};
|
|
@@ -20,8 +23,10 @@ export type VoiceProofTrendProfileSummary = {
|
|
|
20
23
|
label?: string;
|
|
21
24
|
maxLiveP95Ms?: number;
|
|
22
25
|
maxProviderP95Ms?: number;
|
|
26
|
+
maxReconnectP95Ms?: number;
|
|
23
27
|
maxTurnP95Ms?: number;
|
|
24
28
|
providers?: VoiceProofTrendProviderSummary[];
|
|
29
|
+
reconnect?: VoiceProofTrendReconnectSummary;
|
|
25
30
|
runtimeChannel?: VoiceProofTrendRuntimeChannelSummary;
|
|
26
31
|
sessionCount?: number;
|
|
27
32
|
status?: string;
|
|
@@ -34,6 +39,7 @@ export type VoiceProofTrendProfileDefinition = {
|
|
|
34
39
|
liveOffsetMs?: number;
|
|
35
40
|
maxLiveP95Ms?: number;
|
|
36
41
|
maxProviderP95Ms?: number;
|
|
42
|
+
maxReconnectP95Ms?: number;
|
|
37
43
|
maxRuntimeFirstAudioLatencyMs?: number;
|
|
38
44
|
maxRuntimeInterruptionP95Ms?: number;
|
|
39
45
|
maxRuntimeJitterMs?: number;
|
|
@@ -62,6 +68,17 @@ export type VoiceProofTrendRuntimeChannelSummary = {
|
|
|
62
68
|
samples?: number;
|
|
63
69
|
status?: string;
|
|
64
70
|
};
|
|
71
|
+
export type VoiceProofTrendReconnectSummary = {
|
|
72
|
+
attempts?: number;
|
|
73
|
+
exhausted?: boolean;
|
|
74
|
+
maxAttempts?: number;
|
|
75
|
+
resumeLatencyP95Ms?: number;
|
|
76
|
+
reconnected?: boolean;
|
|
77
|
+
resumed?: boolean;
|
|
78
|
+
samples?: number;
|
|
79
|
+
snapshotCount?: number;
|
|
80
|
+
status?: string;
|
|
81
|
+
};
|
|
65
82
|
export type VoiceProofTrendCycle = {
|
|
66
83
|
at?: string;
|
|
67
84
|
cycle?: number;
|
|
@@ -83,6 +100,7 @@ export type VoiceProofTrendCycle = {
|
|
|
83
100
|
status?: string;
|
|
84
101
|
};
|
|
85
102
|
providers?: VoiceProofTrendProviderSummary[];
|
|
103
|
+
reconnect?: VoiceProofTrendReconnectSummary;
|
|
86
104
|
runtimeChannel?: VoiceProofTrendRuntimeChannelSummary;
|
|
87
105
|
turnLatency?: {
|
|
88
106
|
p95Ms?: number;
|
|
@@ -120,6 +138,7 @@ export type VoiceProofTrendReport = {
|
|
|
120
138
|
export type VoiceProofTrendProfileSummaryOptions = {
|
|
121
139
|
maxLiveP95Ms?: number;
|
|
122
140
|
maxProviderP95Ms?: number;
|
|
141
|
+
maxReconnectP95Ms?: number;
|
|
123
142
|
maxRuntimeFirstAudioLatencyMs?: number;
|
|
124
143
|
maxRuntimeInterruptionP95Ms?: number;
|
|
125
144
|
maxRuntimeJitterMs?: number;
|
|
@@ -137,11 +156,20 @@ export type VoiceProofTrendRealCallProfileEvidence = {
|
|
|
137
156
|
profileLabel?: string;
|
|
138
157
|
providerP95Ms?: number;
|
|
139
158
|
providers?: VoiceProofTrendProviderSummary[];
|
|
159
|
+
reconnect?: VoiceProofTrendReconnectSummary;
|
|
140
160
|
runtimeChannel?: VoiceProofTrendRuntimeChannelSummary;
|
|
141
161
|
sessionId: string;
|
|
142
162
|
surfaces?: string[];
|
|
143
163
|
turnP95Ms?: number;
|
|
144
164
|
};
|
|
165
|
+
export type VoiceReconnectRealCallProfileEvidenceOptions = {
|
|
166
|
+
operationsRecordHref?: string | ((report: VoiceReconnectProofReport | VoiceReconnectContractReport, index: number) => string | undefined);
|
|
167
|
+
profileDescription?: string;
|
|
168
|
+
profileId?: string;
|
|
169
|
+
profileLabel?: string;
|
|
170
|
+
sessionId?: string | ((report: VoiceReconnectProofReport | VoiceReconnectContractReport, index: number) => string);
|
|
171
|
+
surfaces?: readonly string[];
|
|
172
|
+
};
|
|
145
173
|
export type VoiceRealCallProfileTraceEvidenceOptions = {
|
|
146
174
|
defaultProfileId?: string;
|
|
147
175
|
defaultProfileLabel?: string;
|
|
@@ -577,6 +605,7 @@ export declare const readVoiceProofTrendReportFile: (path: string, options?: {
|
|
|
577
605
|
maxAgeMs?: number;
|
|
578
606
|
}) => Promise<VoiceProofTrendReport>;
|
|
579
607
|
export declare const buildVoiceRealCallProfileEvidenceFromTraceEvents: (events: readonly StoredVoiceTraceEvent[], options?: VoiceRealCallProfileTraceEvidenceOptions) => VoiceProofTrendRealCallProfileEvidence[];
|
|
608
|
+
export declare const buildVoiceRealCallProfileEvidenceFromReconnectProofReports: (input: VoiceReconnectProofReport | VoiceReconnectContractReport | readonly (VoiceReconnectProofReport | VoiceReconnectContractReport)[], options?: VoiceReconnectRealCallProfileEvidenceOptions) => VoiceProofTrendRealCallProfileEvidence[];
|
|
580
609
|
export declare const loadVoiceRealCallProfileEvidenceFromTraceStore: (options: VoiceRealCallProfileTraceStoreEvidenceOptions) => Promise<VoiceProofTrendRealCallProfileEvidence[]>;
|
|
581
610
|
export declare const createVoiceRealCallProfileTraceCollector: <TEvent extends StoredVoiceTraceEvent = StoredVoiceTraceEvent>(options: VoiceRealCallProfileTraceCollectorOptions<TEvent>) => VoiceRealCallProfileTraceCollector<TEvent>;
|
|
582
611
|
export declare const buildVoiceProofTrendProfileSummaries: (input: VoiceProofTrendReport | readonly VoiceProofTrendReport[], options?: VoiceProofTrendProfileSummaryOptions) => VoiceProofTrendProfileSummary[];
|