@absolutejs/voice 0.0.22-beta.572 → 0.0.22-beta.574
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/angular/index.js +7 -1
- package/dist/client/htmxBootstrap.js +7 -1
- package/dist/client/index.js +7 -1
- package/dist/embed/index.js +7 -1
- package/dist/embed/voice-widget.js +8 -8
- package/dist/index.js +25 -0
- package/dist/react/index.js +7 -1
- package/dist/svelte/index.js +7 -1
- package/dist/testing/index.js +32 -1
- package/dist/vue/index.js +7 -1
- package/package.json +1 -1
package/dist/angular/index.js
CHANGED
|
@@ -345,10 +345,16 @@ var createMicrophoneCapture = (options) => {
|
|
|
345
345
|
}
|
|
346
346
|
mediaStream = await navigator.mediaDevices.getUserMedia({
|
|
347
347
|
audio: {
|
|
348
|
-
|
|
348
|
+
autoGainControl: true,
|
|
349
|
+
channelCount: options.channelCount ?? 1,
|
|
350
|
+
echoCancellation: true,
|
|
351
|
+
noiseSuppression: true
|
|
349
352
|
}
|
|
350
353
|
});
|
|
351
354
|
audioContext = new AudioContextCtor;
|
|
355
|
+
if (audioContext.state === "suspended") {
|
|
356
|
+
await audioContext.resume();
|
|
357
|
+
}
|
|
352
358
|
sourceNode = audioContext.createMediaStreamSource(mediaStream);
|
|
353
359
|
processorNode = audioContext.createScriptProcessor(4096, 1, 1);
|
|
354
360
|
processorNode.onaudioprocess = (event) => {
|
|
@@ -113,10 +113,16 @@ var createMicrophoneCapture = (options) => {
|
|
|
113
113
|
}
|
|
114
114
|
mediaStream = await navigator.mediaDevices.getUserMedia({
|
|
115
115
|
audio: {
|
|
116
|
-
|
|
116
|
+
autoGainControl: true,
|
|
117
|
+
channelCount: options.channelCount ?? 1,
|
|
118
|
+
echoCancellation: true,
|
|
119
|
+
noiseSuppression: true
|
|
117
120
|
}
|
|
118
121
|
});
|
|
119
122
|
audioContext = new AudioContextCtor;
|
|
123
|
+
if (audioContext.state === "suspended") {
|
|
124
|
+
await audioContext.resume();
|
|
125
|
+
}
|
|
120
126
|
sourceNode = audioContext.createMediaStreamSource(mediaStream);
|
|
121
127
|
processorNode = audioContext.createScriptProcessor(4096, 1, 1);
|
|
122
128
|
processorNode.onaudioprocess = (event) => {
|
package/dist/client/index.js
CHANGED
|
@@ -1333,10 +1333,16 @@ var createMicrophoneCapture = (options) => {
|
|
|
1333
1333
|
}
|
|
1334
1334
|
mediaStream = await navigator.mediaDevices.getUserMedia({
|
|
1335
1335
|
audio: {
|
|
1336
|
-
|
|
1336
|
+
autoGainControl: true,
|
|
1337
|
+
channelCount: options.channelCount ?? 1,
|
|
1338
|
+
echoCancellation: true,
|
|
1339
|
+
noiseSuppression: true
|
|
1337
1340
|
}
|
|
1338
1341
|
});
|
|
1339
1342
|
audioContext = new AudioContextCtor;
|
|
1343
|
+
if (audioContext.state === "suspended") {
|
|
1344
|
+
await audioContext.resume();
|
|
1345
|
+
}
|
|
1340
1346
|
sourceNode = audioContext.createMediaStreamSource(mediaStream);
|
|
1341
1347
|
processorNode = audioContext.createScriptProcessor(4096, 1, 1);
|
|
1342
1348
|
processorNode.onaudioprocess = (event) => {
|
package/dist/embed/index.js
CHANGED
|
@@ -110,10 +110,16 @@ var createMicrophoneCapture = (options) => {
|
|
|
110
110
|
}
|
|
111
111
|
mediaStream = await navigator.mediaDevices.getUserMedia({
|
|
112
112
|
audio: {
|
|
113
|
-
|
|
113
|
+
autoGainControl: true,
|
|
114
|
+
channelCount: options.channelCount ?? 1,
|
|
115
|
+
echoCancellation: true,
|
|
116
|
+
noiseSuppression: true
|
|
114
117
|
}
|
|
115
118
|
});
|
|
116
119
|
audioContext = new AudioContextCtor;
|
|
120
|
+
if (audioContext.state === "suspended") {
|
|
121
|
+
await audioContext.resume();
|
|
122
|
+
}
|
|
117
123
|
sourceNode = audioContext.createMediaStreamSource(mediaStream);
|
|
118
124
|
processorNode = audioContext.createScriptProcessor(4096, 1, 1);
|
|
119
125
|
processorNode.onaudioprocess = (event) => {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
(()=>{var{defineProperty:H,getOwnPropertyNames:oc,getOwnPropertyDescriptor:lc}=Object,Ac=Object.prototype.hasOwnProperty;function ic(c){return this[c]}var Cc=(c)=>{var n=(Y??=new WeakMap).get(c),o;if(n)return n;if(n=H({},"__esModule",{value:!0}),c&&typeof c==="object"||typeof c==="function"){for(var g of oc(c))if(!Ac.call(n,g))H(n,g,{get:ic.bind(c,g),enumerable:!(o=lc(c,g))||o.enumerable})}return Y.set(c,n),n},Y;var Tc=(c)=>c;function yc(c,n){this[c]=Tc.bind(null,n)}var Vc=(c,n)=>{for(var o in n)H(c,o,{get:n[o],enumerable:!0,configurable:!0,set:yc.bind(n,o)})};var ec={};Vc(ec,{mount:()=>p,default:()=>Bc,VOICE_EMBED_VERSION:()=>cc});var Ic=(c)=>{if(typeof c!=="string")return c;return document.querySelector(c)},Sc=(c,n,o,g)=>{let l=n??c.getAttribute("hx-get")??"";if(!l)return"";let C=new URL(l,window.location.origin);if(g)C.searchParams.set(o,g);else C.searchParams.delete(o);return`${C.pathname}${C.search}${C.hash}`},Q=(c,n)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let o=Ic(n.element);if(!o)return()=>{};let g=n.eventName??"voice-refresh",l=n.sessionQueryParam??"sessionId",C=()=>{let V=window,I=Sc(o,n.route,l,c.sessionId);if(I)o.setAttribute("hx-get",I);V.htmx?.process?.(o),V.htmx?.trigger?.(o,g)},i=c.subscribe(C);return C(),()=>{i()}};var dc=(c)=>Math.max(-1,Math.min(1,c)),Rc=(c)=>{let n=new Int16Array(c.length);for(let o=0;o<c.length;o+=1){let g=dc(c[o]??0);n[o]=g<0?g*32768:g*32767}return new Uint8Array(n.buffer)},hc=(c)=>{let n=c instanceof Uint8Array?c:new Uint8Array(c);if(n.byteLength<2)return 0;let o=new Int16Array(n.buffer,n.byteOffset,Math.floor(n.byteLength/2));if(o.length===0)return 0;let g=0;for(let l of o){let C=l/32768;g+=C*C}return Math.min(1,Math.max(0,Math.sqrt(g/o.length)*5.5))},_c=(c,n,o)=>{if(n===o)return c;let g=n/o,l=Math.round(c.length/g),C=new Float32Array(l),i=0,V=0;while(i<C.length){let I=Math.round((i+1)*g),R=0,y=0;for(let d=V;d<I&&d<c.length;d+=1)R+=c[d]??0,y+=1;C[i]=y>0?R/y:0,i+=1,V=I}return C},J=(c)=>{let n=null,o=null,g=null,l=null;return{start:async()=>{if(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia)throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let V=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!V)throw Error("Browser microphone capture requires AudioContext support.");l=await navigator.mediaDevices.getUserMedia({audio:{channelCount:c.channelCount??1}}),n=new V,o=n.createMediaStreamSource(l),g=n.createScriptProcessor(4096,1,1),g.onaudioprocess=(I)=>{let R=I.inputBuffer.getChannelData(0),y=_c(R,n?.sampleRate??48000,c.sampleRateHz??16000),d=Rc(y);c.onLevel?.(hc(d)),c.onAudio(d)},o.connect(g),g.connect(n.destination)},stop:()=>{g?.disconnect(),o?.disconnect(),l?.getTracks().forEach((V)=>V.stop()),n?.close(),c.onLevel?.(0),n=null,l=null,g=null,o=null}}};var G=(c)=>{if(typeof c==="string"&&c.trim())return c;if(c instanceof Error&&c.message.trim())return c.message;if(c&&typeof c==="object"){let n=c;for(let o of["message","reason","description"]){let g=n[o];if(typeof g==="string"&&g.trim())return g}if("error"in n)return G(n.error);if("cause"in n)return G(n.cause);try{return JSON.stringify(c)}catch{}}return"Unexpected error"},q=(c)=>{switch(c.type){case"audio":return{chunk:Uint8Array.from(atob(c.chunkBase64),(n)=>n.charCodeAt(0)),format:c.format,receivedAt:c.receivedAt,turnId:c.turnId,type:"audio"};case"assistant":return{text:c.text,turnId:c.turnId,type:"assistant"};case"assistant_delta":return{delta:c.delta,turnId:c.turnId,type:"assistant_delta"};case"complete":return{sessionId:c.sessionId,type:"complete"};case"connection":return{reconnect:c.reconnect,type:"connection"};case"call_lifecycle":return{event:c.event,sessionId:c.sessionId,type:"call_lifecycle"};case"error":return{message:G(c.message),type:"error"};case"final":return{transcript:c.transcript,type:"final"};case"partial":return{transcript:c.transcript,type:"partial"};case"replay":return{assistantTexts:c.assistantTexts,call:c.call,partial:c.partial,scenarioId:c.scenarioId,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,status:c.status,turns:c.turns,type:"replay"};case"session":return{sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,scenarioId:c.scenarioId,status:c.status,type:"session"};case"turn":return{turn:c.turn,type:"turn"};default:return null}};var kc=Math.PI*2;var x=(c,n,o,g)=>{c.push({code:o,message:g,severity:n})};var sc=(c)=>c.length===0?void 0:c.reduce((n,o)=>n+o,0)/c.length,N=(c)=>c.length===0?void 0:Math.max(...c);var _=(c,n)=>{let o=c[n];return typeof o==="number"&&Number.isFinite(o)?o:void 0},$=(c,n)=>{let o=c[n];return typeof o==="boolean"?o:void 0},s=(c,n)=>{let o=c[n];return typeof o==="string"?o:void 0},X=(c)=>String(c.id??s(c,"ssrc")??_(c,"ssrc")??s(c,"trackIdentifier")??s(c,"mid")??"unknown"),Z=(c)=>c===void 0?void 0:c*1000;var wc=(c)=>{let n={};for(let[o,g]of Object.entries(c))if(g===null||typeof g==="boolean"||typeof g==="number"||typeof g==="string")n[o]=g;return n};var z=(c={})=>{let n=c.stats??[],o=[],g=n.filter((A)=>A.type==="inbound-rtp"&&s(A,"kind")!=="video"),l=n.filter((A)=>A.type==="outbound-rtp"&&s(A,"kind")!=="video"),C=n.filter((A)=>A.type==="candidate-pair"),i=n.filter((A)=>(A.type==="track"||A.type==="media-source")&&s(A,"kind")==="audio"),V=C.filter((A)=>$(A,"selected")===!0||$(A,"nominated")===!0||s(A,"state")==="succeeded").length,I=i.filter((A)=>s(A,"readyState")!=="ended"&&s(A,"trackState")!=="ended"&&$(A,"ended")!==!0).length,R=i.filter((A)=>s(A,"readyState")==="ended"||s(A,"trackState")==="ended"||$(A,"ended")===!0).length,y=g.reduce((A,h)=>A+(_(h,"packetsReceived")??0),0),d=l.reduce((A,h)=>A+(_(h,"packetsSent")??0),0),T=[...g,...l].reduce((A,h)=>A+Math.max(0,_(h,"packetsLost")??0),0),w=y+T,S=w===0?0:T/w,E=g.reduce((A,h)=>A+(_(h,"bytesReceived")??0),0),b=l.reduce((A,h)=>A+(_(h,"bytesSent")??0),0),U=N(C.map((A)=>Z(_(A,"currentRoundTripTime")??_(A,"roundTripTime"))).filter((A)=>A!==void 0)),M=N([...g,...l].map((A)=>Z(_(A,"jitter"))).filter((A)=>A!==void 0)),W=N(g.map((A)=>{let h=_(A,"jitterBufferDelay"),L=_(A,"jitterBufferEmittedCount");return h!==void 0&&L!==void 0&&L>0?h/L*1000:void 0}).filter((A)=>A!==void 0)),P=i.map((A)=>_(A,"audioLevel")).filter((A)=>A!==void 0);if(c.requireConnectedCandidatePair&&C.length>0&&V===0)x(o,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(c.requireLiveAudioTrack&&I===0)x(o,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(c.maxPacketLossRatio!==void 0&&S>c.maxPacketLossRatio)x(o,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(S)} above ${String(c.maxPacketLossRatio)}.`);if(c.maxRoundTripTimeMs!==void 0&&U!==void 0&&U>c.maxRoundTripTimeMs)x(o,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(U)}ms above ${String(c.maxRoundTripTimeMs)}ms.`);if(c.maxJitterMs!==void 0&&M!==void 0&&M>c.maxJitterMs)x(o,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(M)}ms above ${String(c.maxJitterMs)}ms.`);return{activeCandidatePairs:V,audioLevelAverage:sc(P),bytesReceived:E,bytesSent:b,checkedAt:Date.now(),endedAudioTracks:R,inboundPackets:y,issues:o,jitterBufferDelayMs:W,jitterMs:M,liveAudioTracks:I,outboundPackets:d,packetLossRatio:S,packetsLost:T,roundTripTimeMs:U,status:o.some((A)=>A.severity==="error")?"fail":o.length>0?"warn":"pass",totalStats:n.length}},e=async(c)=>{return[...(await c.peerConnection.getStats(c.selector??null)).values()].map(wc)};var B=(c={})=>{let n=c.stats??[],o=c.previousStats??[],g=[],l=new Map(o.map((T)=>[X(T),T])),i=n.filter((T)=>(T.type==="inbound-rtp"||T.type==="outbound-rtp")&&s(T,"kind")!=="video"&&s(T,"mediaType")!=="video").map((T)=>{let w=T.type==="outbound-rtp"?"outbound":"inbound",S=w==="outbound"?"packetsSent":"packetsReceived",E=w==="outbound"?"bytesSent":"bytesReceived",b=l.get(X(T)),U=_(T,S),M=b?_(b,S):void 0,W=_(T,E),P=b?_(b,E):void 0,A=T.timestamp!==void 0&&b?.timestamp!==void 0?T.timestamp-b.timestamp:void 0;return{bytesDelta:W!==void 0&&P!==void 0?W-P:void 0,currentPackets:U,direction:w,id:X(T),packetDelta:U!==void 0&&M!==void 0?U-M:void 0,previousPackets:M,timeDeltaMs:A}}),V=i.filter((T)=>T.direction==="inbound"),I=i.filter((T)=>T.direction==="outbound"),R=N(i.map((T)=>T.timeDeltaMs).filter((T)=>T!==void 0)),y=V.filter((T)=>c.maxInboundPacketStallMs!==void 0&&T.timeDeltaMs!==void 0&&T.timeDeltaMs>=c.maxInboundPacketStallMs&&T.packetDelta!==void 0&&T.packetDelta<=0).length,d=I.filter((T)=>c.maxOutboundPacketStallMs!==void 0&&T.timeDeltaMs!==void 0&&T.timeDeltaMs>=c.maxOutboundPacketStallMs&&T.packetDelta!==void 0&&T.packetDelta<=0).length;if(c.requireInboundAudio&&V.length===0)x(g,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(c.requireOutboundAudio&&I.length===0)x(g,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(c.maxGapMs!==void 0&&R!==void 0&&R>c.maxGapMs)x(g,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(R)}ms above ${String(c.maxGapMs)}ms.`);if(y>0)x(g,"error","media.webrtc_inbound_stalled",`${String(y)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(d>0)x(g,"error","media.webrtc_outbound_stalled",`${String(d)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:V.length,issues:g,maxObservedGapMs:R,outboundAudioStreams:I.length,stalledInboundStreams:y,stalledOutboundStreams:d,status:g.some((T)=>T.severity==="error")?"fail":g.length>0?"warn":"pass",streams:i,totalStats:n.length}};var Lc="/api/voice/browser-media",bc=5000,Uc=async(c)=>c.peerConnection??await c.getPeerConnection?.()??null,Mc=async(c,n)=>{let o=n.fetch??globalThis.fetch;if(!o)return;await o(n.path??Lc,{body:JSON.stringify(c),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},K=(c)=>{let n=null,o=[],g=async()=>{let i=await Uc(c);if(!i)return;let V=await e({peerConnection:i}),I=z({...c,stats:V}),R=c.continuity===!1?void 0:B({...c.continuity,previousStats:o,stats:V}),y={at:Date.now(),continuity:R,report:I,scenarioId:c.getScenarioId?.()??null,sessionId:c.getSessionId?.()??null};return o=V,c.onReport?.(y),await Mc(y,c),y},l=()=>{g().catch((i)=>{c.onError?.(i)})},C=()=>{if(n)clearInterval(n),n=null};return{close:C,reportOnce:g,stop:C,start:()=>{if(n)return;l(),n=setInterval(l,c.intervalMs??bc)}}};var D=()=>{},xc=()=>D,Oc={callControl:D,close:D,endTurn:D,send:D,sendAudio:D,simulateDisconnect:D,subscribe:xc,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",start:()=>{}},Dc=()=>crypto.randomUUID(),Ec=(c,n,o)=>{let{hostname:g,port:l,protocol:C}=window.location,i=C==="https:"?"wss:":"ws:",V=l?`:${l}`:"",I=new URL(`${i}//${g}${V}${c}`);if(I.searchParams.set("sessionId",n),o)I.searchParams.set("scenarioId",o);return I.toString()},Wc=(c)=>{if(!c||typeof c!=="object"||!("type"in c))return!1;switch(c.type){case"audio":case"assistant":case"call_lifecycle":case"complete":case"connection":case"error":case"final":case"partial":case"pong":case"replay":case"session":case"turn":return!0;default:return!1}},Pc=(c)=>{if(typeof c.data!=="string")return null;try{let n=JSON.parse(c.data);return Wc(n)?n:null}catch{return null}},j=(c,n={})=>{if(typeof window>"u")return Oc;let o=new Set,g=n.reconnect!==!1,l=n.maxReconnectAttempts??10,C=n.pingInterval??30000,i={isConnected:!1,pendingMessages:[],scenarioId:n.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:n.sessionId??Dc(),ws:null},V=(A)=>{o.forEach((h)=>h(A))},I=()=>{if(i.pingInterval)clearInterval(i.pingInterval),i.pingInterval=null;if(i.reconnectTimeout)clearTimeout(i.reconnectTimeout),i.reconnectTimeout=null},R=()=>{if(i.ws?.readyState!==1)return;while(i.pendingMessages.length>0){let A=i.pendingMessages.shift();if(A!==void 0)i.ws.send(A)}},y=()=>{let A=Date.now()+500;i.reconnectAttempts+=1,V({reconnect:{attempts:i.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:l,nextAttemptAt:A,status:"reconnecting"},type:"connection"}),i.reconnectTimeout=setTimeout(()=>{if(i.reconnectAttempts>l){V({reconnect:{attempts:i.reconnectAttempts,maxAttempts:l,status:"exhausted"},type:"connection"});return}d()},500)},d=()=>{let A=new WebSocket(Ec(c,i.sessionId,i.scenarioId));A.binaryType="arraybuffer",A.onopen=()=>{let h=i.reconnectAttempts>0;if(i.isConnected=!0,R(),h)V({reconnect:{attempts:i.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:l,status:"resumed"},type:"connection"}),i.reconnectAttempts=0;o.forEach((L)=>L({scenarioId:i.scenarioId??void 0,sessionId:i.sessionId,status:"active",type:"session"})),i.pingInterval=setInterval(()=>{if(A.readyState===1)A.send(JSON.stringify({type:"ping"}))},C)},A.onmessage=(h)=>{let L=Pc(h);if(!L)return;if(L.type==="session")i.sessionId=L.sessionId,i.scenarioId=L.scenarioId??i.scenarioId;o.forEach((gc)=>gc(L))},A.onclose=(h)=>{if(i.isConnected=!1,I(),g&&h.code!==1000&&i.reconnectAttempts<l)y();else if(g&&h.code!==1000)V({reconnect:{attempts:i.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:l,status:"exhausted"},type:"connection"})},i.ws=A},T=(A)=>{if(i.ws?.readyState===1){i.ws.send(A);return}i.pendingMessages.push(A)},w=(A)=>{T(JSON.stringify(A))},S=(A={})=>{if(A.sessionId)i.sessionId=A.sessionId;if(A.scenarioId)i.scenarioId=A.scenarioId;w({scenarioId:i.scenarioId??void 0,sessionId:i.sessionId,type:"start"})},E=(A)=>{T(A)},b=()=>{w({type:"end_turn"})},U=(A)=>{w({...A,type:"call_control"})},M=()=>{if(I(),i.ws)i.ws.close(1000),i.ws=null;i.isConnected=!1,o.clear()},W=()=>{if(i.ws?.readyState===1)i.ws.close(4000,"absolutejs-voice-reconnect-proof")},P=(A)=>{return o.add(A),()=>{o.delete(A)}};return d(),{callControl:U,close:M,endTurn:b,send:w,sendAudio:E,simulateDisconnect:W,start:S,subscribe:P,getReadyState:()=>i.ws?.readyState??3,getScenarioId:()=>i.scenarioId??"",getSessionId:()=>i.sessionId}};var $c=()=>({attempts:0,maxAttempts:0,status:"idle"}),Nc=()=>({assistantAudio:[],assistantStreamingText:"",assistantTexts:[],call:null,error:null,isConnected:!1,partial:"",reconnect:$c(),scenarioId:null,sessionId:null,sessionMetadata:null,status:"idle",turns:[]}),t=()=>{let c=Nc(),n=new Set,o=()=>{n.forEach((l)=>l())};return{dispatch:(l)=>{switch(l.type){case"audio":c={...c,assistantAudio:[...c.assistantAudio,{chunk:l.chunk,format:l.format,receivedAt:l.receivedAt,turnId:l.turnId}]};break;case"assistant":c={...c,assistantStreamingText:"",assistantTexts:[...c.assistantTexts,l.text]};break;case"assistant_delta":c={...c,assistantStreamingText:`${c.assistantStreamingText}${l.delta}`};break;case"complete":c={...c,sessionId:l.sessionId,status:"completed"};break;case"call_lifecycle":c={...c,call:{...c.call,disposition:l.event.type==="end"?l.event.disposition:c.call?.disposition,endedAt:l.event.type==="end"?l.event.at:c.call?.endedAt,events:[...c.call?.events??[],l.event],lastEventAt:l.event.at,startedAt:c.call?.startedAt??l.event.at},sessionId:l.sessionId};break;case"connected":c={...c,isConnected:!0,reconnect:c.reconnect.status==="reconnecting"?{...c.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:c.reconnect};break;case"connection":c={...c,reconnect:l.reconnect};break;case"disconnected":c={...c,isConnected:!1};break;case"error":c={...c,error:l.message};break;case"final":c={...c,partial:l.transcript.text,turns:c.turns.map((C)=>C)};break;case"partial":c={...c,partial:l.transcript.text};break;case"replay":c={...c,assistantStreamingText:"",assistantTexts:[...l.assistantTexts],call:l.call??null,error:null,isConnected:l.status==="active",partial:l.partial,reconnect:c.reconnect.status==="reconnecting"?{...c.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:c.reconnect,scenarioId:l.scenarioId??c.scenarioId,sessionId:l.sessionId,sessionMetadata:l.sessionMetadata??c.sessionMetadata,status:l.status,turns:[...l.turns]};break;case"session":c={...c,error:null,scenarioId:l.scenarioId??c.scenarioId,isConnected:l.status==="active",sessionId:l.sessionId,sessionMetadata:l.sessionMetadata??c.sessionMetadata,status:l.status};break;case"turn":c={...c,partial:"",turns:[...c.turns,l.turn]};break}o()},getServerSnapshot:()=>c,getSnapshot:()=>c,subscribe:(l)=>{return n.add(l),()=>{n.delete(l)}}}};var f=(c,n={})=>{let o=j(c,n),g=t(),l=n.browserMedia&&typeof window<"u"?K({...n.browserMedia,getScenarioId:()=>n.browserMedia?n.browserMedia.getScenarioId?.()??o.getScenarioId():o.getScenarioId(),getSessionId:()=>n.browserMedia?n.browserMedia.getSessionId?.()??o.getSessionId():o.getSessionId()}):null,C=new Set,i=(y)=>Promise.resolve().then(()=>{if(!y?.sessionId&&!y?.scenarioId)return;o.start(y),l?.start()}),V=()=>{C.forEach((y)=>y())},I=()=>{if(!n.reconnectReportPath||typeof fetch>"u")return;let y=g.getSnapshot(),d=JSON.stringify({at:Date.now(),reconnect:y.reconnect,scenarioId:y.scenarioId,sessionId:o.getSessionId(),turnIds:y.turns.map((T)=>T.id)});fetch(n.reconnectReportPath,{body:d,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},R=o.subscribe((y)=>{let d=q(y);if(d){if(g.dispatch(d),y.type==="connection")I();V()}});return{start:i,get assistantAudio(){return g.getSnapshot().assistantAudio},get assistantTexts(){return g.getSnapshot().assistantTexts},get assistantStreamingText(){return g.getSnapshot().assistantStreamingText},get call(){return g.getSnapshot().call},callControl(y){o.callControl(y)},close(){R(),l?.close(),o.close(),g.dispatch({type:"disconnected"}),V()},endTurn(){o.endTurn()},get error(){return g.getSnapshot().error},getServerSnapshot(){return g.getServerSnapshot()},getSnapshot(){return g.getSnapshot()},get isConnected(){return g.getSnapshot().isConnected},get partial(){return g.getSnapshot().partial},get reconnect(){return g.getSnapshot().reconnect},get scenarioId(){return g.getSnapshot().scenarioId},sendAudio(y){o.sendAudio(y)},get sessionId(){return o.getSessionId()},get sessionMetadata(){return g.getSnapshot().sessionMetadata},simulateDisconnect(){o.simulateDisconnect()},get status(){return g.getSnapshot().status},subscribe(y){return C.add(y),()=>{C.delete(y)}},get turns(){return g.getSnapshot().turns}}};var k=(c)=>{if(!c||c.enabled===!1)return;return{enabled:!0,maxGain:c.maxGain??3,noiseGateAttenuation:c.noiseGateAttenuation??0.15,noiseGateThreshold:c.noiseGateThreshold??0.006,targetLevel:c.targetLevel??0.08}};var Hc={balanced:{qualityProfile:"general",silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:"general",silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},"long-form":{qualityProfile:"general",silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},Gc={"accent-heavy":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},general:{},"noisy-room":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},"short-command":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var F=(c)=>{let n=c?.profile??"fast",o=c?.qualityProfile??"general",g=Hc[n],l=Gc[o];return{profile:n,qualityProfile:o,silenceMs:c?.silenceMs??l.silenceMs??g.silenceMs,speechThreshold:c?.speechThreshold??l.speechThreshold??g.speechThreshold,transcriptStabilityMs:c?.transcriptStabilityMs??l.transcriptStabilityMs??g.transcriptStabilityMs}};var Xc={chat:{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"balanced",qualityProfile:"short-command"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"fast",qualityProfile:"general"}},dictation:{audioConditioning:{enabled:!0,maxGain:2.25,noiseGateAttenuation:0.05,noiseGateThreshold:0.003,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"guided-intake":{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"turn-scoped",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"noisy-room":{audioConditioning:{enabled:!0,maxGain:3,noiseGateAttenuation:0.12,noiseGateThreshold:0.006,targetLevel:0.085},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:2100,speechThreshold:0.02,transcriptStabilityMs:1650}},"pstn-balanced":{audioConditioning:{enabled:!0,maxGain:2.8,noiseGateAttenuation:0.07,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:660,speechThreshold:0.012,transcriptStabilityMs:300}},"pstn-fast":{audioConditioning:{enabled:!0,maxGain:2.75,noiseGateAttenuation:0.06,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:620,speechThreshold:0.012,transcriptStabilityMs:280}},reliability:{audioConditioning:{enabled:!0,maxGain:2.9,noiseGateAttenuation:0.08,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room"}}},r=(c="default")=>{let n=Xc[c];return{audioConditioning:k(n.audioConditioning),capture:{channelCount:n.capture?.channelCount??1,sampleRateHz:n.capture?.sampleRateHz??16000},connection:{...n.connection},name:c,sttLifecycle:n.sttLifecycle??"continuous",turnDetection:F(n.turnDetection)}};var Yc=(c)=>({assistantAudio:[...c.assistantAudio],assistantStreamingText:c.assistantStreamingText,assistantTexts:[...c.assistantTexts],call:c.call,error:c.error,isConnected:c.isConnected,isRecording:!1,partial:c.partial,reconnect:c.reconnect,recordingError:null,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,scenarioId:c.scenarioId,status:c.status,turns:[...c.turns]}),a=(c,n={})=>{let o=r(n.preset),g=f(c,{...o.connection,...n.connection}),l=null,C=Yc(g),i=new Set,V=()=>{for(let S of i)S()},I=()=>{if(C={...C,assistantAudio:[...g.assistantAudio],assistantStreamingText:g.assistantStreamingText,assistantTexts:[...g.assistantTexts],call:g.call,error:g.error,isConnected:g.isConnected,partial:g.partial,reconnect:g.reconnect,sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,scenarioId:g.scenarioId,status:g.status,turns:[...g.turns]},n.autoStopOnComplete!==!1&&C.status==="completed"&&C.isRecording)l?.stop(),l=null,C={...C,isRecording:!1};V()},R=g.subscribe(I);I();let y=()=>{if(l)return l;return l=J({channelCount:n.capture?.channelCount??o.capture.channelCount,onLevel:n.capture?.onLevel,onAudio:(S)=>{if(n.capture?.onAudio){n.capture.onAudio(S,g.sendAudio);return}g.sendAudio(S)},sampleRateHz:n.capture?.sampleRateHz??o.capture.sampleRateHz}),l},d=()=>{l?.stop(),l=null,C={...C,isRecording:!1},V()},T=async()=>{if(C.isRecording)return;try{C={...C,recordingError:null},V(),await y().start(),C={...C,isRecording:!0},V()}catch(S){throw l=null,C={...C,isRecording:!1,recordingError:S instanceof Error?S.message:String(S)},V(),S}};return{close:()=>{R(),d(),g.close()},startRecording:T,stopRecording:d,get assistantAudio(){return C.assistantAudio},get assistantTexts(){return C.assistantTexts},get assistantStreamingText(){return C.assistantStreamingText},bindHTMX(S){return Q(g,S)},get call(){return C.call},callControl:(S)=>g.callControl(S),endTurn:()=>g.endTurn(),get error(){return C.error},getServerSnapshot:()=>C,getSnapshot:()=>C,get isConnected(){return C.isConnected},get isRecording(){return C.isRecording},get partial(){return C.partial},get reconnect(){return C.reconnect},get recordingError(){return C.recordingError},get scenarioId(){return C.scenarioId},sendAudio:(S)=>g.sendAudio(S),get sessionId(){return C.sessionId},get sessionMetadata(){return C.sessionMetadata},simulateDisconnect:()=>g.simulateDisconnect(),get status(){return C.status},subscribe:(S)=>{return i.add(S),()=>{i.delete(S)}},toggleRecording:async()=>{if(C.isRecording){d();return}await T()},get turns(){return C.turns}}};var O=(c)=>String(c).replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'");var v=(c)=>{if(!c.isConnected)return"idle";if(c.isPlaying)return"speaking";if(c.isRecording&&c.hasActivePartial)return"listening";if(c.isRecording)return"listening";if(c.lastTranscriptAt&&!c.lastAssistantAt)return"thinking";if(c.lastTranscriptAt&&c.lastAssistantAt&&c.lastTranscriptAt>c.lastAssistantAt)return"thinking";return"idle"};var Qc={accent:"#3b82f6",background:"#0f172a",errorAccent:"#ef4444",fontFamily:'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',foreground:"#f8fafc",radius:16},Jc={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},qc=(c,n)=>{switch(c){case"listening":return n.listening;case"speaking":return n.speaking;case"thinking":return n.thinking;case"idle":return n.idle}},u=(c)=>{let n={...Qc,...c.theme},o={...Jc,...c.labels},g=c.state.assistantAudio.at(-1)?.receivedAt,l=c.state.turns.at(-1)?.committedAt,C=v({hasActivePartial:c.state.partial.length>0,isConnected:c.state.isConnected,isPlaying:!1,isRecording:c.state.isRecording,lastAssistantAt:g,lastTranscriptAt:l}),i=!c.state.isConnected&&c.state.status!=="idle"&&!c.state.error,V=c.state.error?"Error":i?o.connecting:c.state.status==="completed"?o.callEnded:qc(C,o);return{agentState:C,classes:{container:`absolute-voice-widget absolute-voice-widget--${C}`,dot:`absolute-voice-widget__dot${c.state.error?" absolute-voice-widget__dot--error":""}`},controls:{canEnd:c.state.isConnected,canMute:c.state.isRecording,canStart:!c.state.isRecording&&c.state.status!=="completed"},errorMessage:c.state.error??void 0,labels:o,partial:c.state.partial||void 0,statusLabel:V,theme:n,title:c.title??"Voice"}},Zc=(c)=>typeof c==="number"?`${c}px`:c,m=(c)=>{let n=c.theme,o=`background:${n.background};border-radius:${Zc(n.radius)};color:${n.foreground};font-family:${n.fontFamily};min-width:240px;padding:20px 22px;`,g=`background:${c.errorMessage?n.errorAccent:c.agentState==="idle"?"rgba(148,163,184,0.6)":n.accent};border-radius:50%;height:10px;width:10px;`,l=[];if(c.controls.canStart)l.push(`<button type="button" data-action="start" style="background:${n.accent};border:none;border-radius:12px;color:${n.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${O(c.labels.startCall)}</button>`);if(c.controls.canMute)l.push(`<button type="button" data-action="mute" style="background:transparent;border:1px solid rgba(255,255,255,0.18);border-radius:12px;color:${n.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${O(c.labels.mute)}</button>`);if(c.controls.canEnd)l.push(`<button type="button" data-action="end" style="background:${n.errorAccent};border:none;border-radius:12px;color:${n.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${O(c.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${c.agentState}" class="${O(c.classes.container)}" style="${o}">
|
|
1
|
+
(()=>{var{defineProperty:X,getOwnPropertyNames:Ac,getOwnPropertyDescriptor:Tc}=Object,Cc=Object.prototype.hasOwnProperty;function Ic(c){return this[c]}var Vc=(c)=>{var g=(J??=new WeakMap).get(c),A;if(g)return g;if(g=X({},"__esModule",{value:!0}),c&&typeof c==="object"||typeof c==="function"){for(var n of Ac(c))if(!Cc.call(g,n))X(g,n,{get:Ic.bind(c,n),enumerable:!(A=Tc(c,n))||A.enumerable})}return J.set(c,g),g},J;var lc=(c)=>c;function Sc(c,g){this[c]=lc.bind(null,g)}var yc=(c,g)=>{for(var A in g)X(c,A,{get:g[A],enumerable:!0,configurable:!0,set:Sc.bind(g,A)})};var Bc={};yc(Bc,{mount:()=>p,default:()=>jc,VOICE_EMBED_VERSION:()=>cc});var _c=(c)=>{if(typeof c!=="string")return c;return document.querySelector(c)},Rc=(c,g,A,n)=>{let T=g??c.getAttribute("hx-get")??"";if(!T)return"";let V=new URL(T,window.location.origin);if(n)V.searchParams.set(A,n);else V.searchParams.delete(A);return`${V.pathname}${V.search}${V.hash}`},Z=(c,g)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let A=_c(g.element);if(!A)return()=>{};let n=g.eventName??"voice-refresh",T=g.sessionQueryParam??"sessionId",V=()=>{let y=window,_=Rc(A,g.route,T,c.sessionId);if(_)A.setAttribute("hx-get",_);y.htmx?.process?.(A),y.htmx?.trigger?.(A,n)},I=c.subscribe(V);return V(),()=>{I()}};var hc=(c)=>Math.max(-1,Math.min(1,c)),Lc=(c)=>{let g=new Int16Array(c.length);for(let A=0;A<c.length;A+=1){let n=hc(c[A]??0);g[A]=n<0?n*32768:n*32767}return new Uint8Array(g.buffer)},Uc=(c)=>{let g=c instanceof Uint8Array?c:new Uint8Array(c);if(g.byteLength<2)return 0;let A=new Int16Array(g.buffer,g.byteOffset,Math.floor(g.byteLength/2));if(A.length===0)return 0;let n=0;for(let T of A){let V=T/32768;n+=V*V}return Math.min(1,Math.max(0,Math.sqrt(n/A.length)*5.5))},wc=(c,g,A)=>{if(g===A)return c;let n=g/A,T=Math.round(c.length/n),V=new Float32Array(T),I=0,y=0;while(I<V.length){let _=Math.round((I+1)*n),L=0,S=0;for(let h=y;h<_&&h<c.length;h+=1)L+=c[h]??0,S+=1;V[I]=S>0?L/S:0,I+=1,y=_}return V},q=(c)=>{let g=null,A=null,n=null,T=null;return{start:async()=>{if(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia)throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let y=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!y)throw Error("Browser microphone capture requires AudioContext support.");if(T=await navigator.mediaDevices.getUserMedia({audio:{autoGainControl:!0,channelCount:c.channelCount??1,echoCancellation:!0,noiseSuppression:!0}}),g=new y,g.state==="suspended")await g.resume();A=g.createMediaStreamSource(T),n=g.createScriptProcessor(4096,1,1),n.onaudioprocess=(_)=>{let L=_.inputBuffer.getChannelData(0),S=wc(L,g?.sampleRate??48000,c.sampleRateHz??16000),h=Lc(S);c.onLevel?.(Uc(h)),c.onAudio(h)},A.connect(n),n.connect(g.destination)},stop:()=>{n?.disconnect(),A?.disconnect(),T?.getTracks().forEach((y)=>y.stop()),g?.close(),c.onLevel?.(0),g=null,T=null,n=null,A=null}}};var Y=(c)=>{if(typeof c==="string"&&c.trim())return c;if(c instanceof Error&&c.message.trim())return c.message;if(c&&typeof c==="object"){let g=c;for(let A of["message","reason","description"]){let n=g[A];if(typeof n==="string"&&n.trim())return n}if("error"in g)return Y(g.error);if("cause"in g)return Y(g.cause);try{return JSON.stringify(c)}catch{}}return"Unexpected error"},z=(c)=>{switch(c.type){case"audio":return{chunk:Uint8Array.from(atob(c.chunkBase64),(g)=>g.charCodeAt(0)),format:c.format,receivedAt:c.receivedAt,turnId:c.turnId,type:"audio"};case"assistant":return{text:c.text,turnId:c.turnId,type:"assistant"};case"assistant_delta":return{delta:c.delta,turnId:c.turnId,type:"assistant_delta"};case"complete":return{sessionId:c.sessionId,type:"complete"};case"connection":return{reconnect:c.reconnect,type:"connection"};case"call_lifecycle":return{event:c.event,sessionId:c.sessionId,type:"call_lifecycle"};case"error":return{message:Y(c.message),type:"error"};case"final":return{transcript:c.transcript,type:"final"};case"partial":return{transcript:c.transcript,type:"partial"};case"replay":return{assistantTexts:c.assistantTexts,call:c.call,partial:c.partial,scenarioId:c.scenarioId,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,status:c.status,turns:c.turns,type:"replay"};case"session":return{sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,scenarioId:c.scenarioId,status:c.status,type:"session"};case"turn":return{turn:c.turn,type:"turn"};default:return null}};var tc=Math.PI*2;var $=(c,g,A,n)=>{c.push({code:A,message:n,severity:g})};var Dc=(c)=>c.length===0?void 0:c.reduce((g,A)=>g+A,0)/c.length,G=(c)=>c.length===0?void 0:Math.max(...c);var w=(c,g)=>{let A=c[g];return typeof A==="number"&&Number.isFinite(A)?A:void 0},x=(c,g)=>{let A=c[g];return typeof A==="boolean"?A:void 0},D=(c,g)=>{let A=c[g];return typeof A==="string"?A:void 0},Q=(c)=>String(c.id??D(c,"ssrc")??w(c,"ssrc")??D(c,"trackIdentifier")??D(c,"mid")??"unknown"),K=(c)=>c===void 0?void 0:c*1000;var bc=(c)=>{let g={};for(let[A,n]of Object.entries(c))if(n===null||typeof n==="boolean"||typeof n==="number"||typeof n==="string")g[A]=n;return g};var o=(c={})=>{let g=c.stats??[],A=[],n=g.filter((C)=>C.type==="inbound-rtp"&&D(C,"kind")!=="video"),T=g.filter((C)=>C.type==="outbound-rtp"&&D(C,"kind")!=="video"),V=g.filter((C)=>C.type==="candidate-pair"),I=g.filter((C)=>(C.type==="track"||C.type==="media-source")&&D(C,"kind")==="audio"),y=V.filter((C)=>x(C,"selected")===!0||x(C,"nominated")===!0||D(C,"state")==="succeeded").length,_=I.filter((C)=>D(C,"readyState")!=="ended"&&D(C,"trackState")!=="ended"&&x(C,"ended")!==!0).length,L=I.filter((C)=>D(C,"readyState")==="ended"||D(C,"trackState")==="ended"||x(C,"ended")===!0).length,S=n.reduce((C,U)=>C+(w(U,"packetsReceived")??0),0),h=T.reduce((C,U)=>C+(w(U,"packetsSent")??0),0),l=[...n,...T].reduce((C,U)=>C+Math.max(0,w(U,"packetsLost")??0),0),b=S+l,R=b===0?0:l/b,W=n.reduce((C,U)=>C+(w(U,"bytesReceived")??0),0),E=T.reduce((C,U)=>C+(w(U,"bytesSent")??0),0),M=G(V.map((C)=>K(w(C,"currentRoundTripTime")??w(C,"roundTripTime"))).filter((C)=>C!==void 0)),i=G([...n,...T].map((C)=>K(w(C,"jitter"))).filter((C)=>C!==void 0)),d=G(n.map((C)=>{let U=w(C,"jitterBufferDelay"),O=w(C,"jitterBufferEmittedCount");return U!==void 0&&O!==void 0&&O>0?U/O*1000:void 0}).filter((C)=>C!==void 0)),N=I.map((C)=>w(C,"audioLevel")).filter((C)=>C!==void 0);if(c.requireConnectedCandidatePair&&V.length>0&&y===0)$(A,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(c.requireLiveAudioTrack&&_===0)$(A,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(c.maxPacketLossRatio!==void 0&&R>c.maxPacketLossRatio)$(A,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(R)} above ${String(c.maxPacketLossRatio)}.`);if(c.maxRoundTripTimeMs!==void 0&&M!==void 0&&M>c.maxRoundTripTimeMs)$(A,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(M)}ms above ${String(c.maxRoundTripTimeMs)}ms.`);if(c.maxJitterMs!==void 0&&i!==void 0&&i>c.maxJitterMs)$(A,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(i)}ms above ${String(c.maxJitterMs)}ms.`);return{activeCandidatePairs:y,audioLevelAverage:Dc(N),bytesReceived:W,bytesSent:E,checkedAt:Date.now(),endedAudioTracks:L,inboundPackets:S,issues:A,jitterBufferDelayMs:d,jitterMs:i,liveAudioTracks:_,outboundPackets:h,packetLossRatio:R,packetsLost:l,roundTripTimeMs:M,status:A.some((C)=>C.severity==="error")?"fail":A.length>0?"warn":"pass",totalStats:g.length}},B=async(c)=>{return[...(await c.peerConnection.getStats(c.selector??null)).values()].map(bc)};var j=(c={})=>{let g=c.stats??[],A=c.previousStats??[],n=[],T=new Map(A.map((l)=>[Q(l),l])),I=g.filter((l)=>(l.type==="inbound-rtp"||l.type==="outbound-rtp")&&D(l,"kind")!=="video"&&D(l,"mediaType")!=="video").map((l)=>{let b=l.type==="outbound-rtp"?"outbound":"inbound",R=b==="outbound"?"packetsSent":"packetsReceived",W=b==="outbound"?"bytesSent":"bytesReceived",E=T.get(Q(l)),M=w(l,R),i=E?w(E,R):void 0,d=w(l,W),N=E?w(E,W):void 0,C=l.timestamp!==void 0&&E?.timestamp!==void 0?l.timestamp-E.timestamp:void 0;return{bytesDelta:d!==void 0&&N!==void 0?d-N:void 0,currentPackets:M,direction:b,id:Q(l),packetDelta:M!==void 0&&i!==void 0?M-i:void 0,previousPackets:i,timeDeltaMs:C}}),y=I.filter((l)=>l.direction==="inbound"),_=I.filter((l)=>l.direction==="outbound"),L=G(I.map((l)=>l.timeDeltaMs).filter((l)=>l!==void 0)),S=y.filter((l)=>c.maxInboundPacketStallMs!==void 0&&l.timeDeltaMs!==void 0&&l.timeDeltaMs>=c.maxInboundPacketStallMs&&l.packetDelta!==void 0&&l.packetDelta<=0).length,h=_.filter((l)=>c.maxOutboundPacketStallMs!==void 0&&l.timeDeltaMs!==void 0&&l.timeDeltaMs>=c.maxOutboundPacketStallMs&&l.packetDelta!==void 0&&l.packetDelta<=0).length;if(c.requireInboundAudio&&y.length===0)$(n,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(c.requireOutboundAudio&&_.length===0)$(n,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(c.maxGapMs!==void 0&&L!==void 0&&L>c.maxGapMs)$(n,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(L)}ms above ${String(c.maxGapMs)}ms.`);if(S>0)$(n,"error","media.webrtc_inbound_stalled",`${String(S)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(h>0)$(n,"error","media.webrtc_outbound_stalled",`${String(h)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:y.length,issues:n,maxObservedGapMs:L,outboundAudioStreams:_.length,stalledInboundStreams:S,stalledOutboundStreams:h,status:n.some((l)=>l.severity==="error")?"fail":n.length>0?"warn":"pass",streams:I,totalStats:g.length}};var Oc="/api/voice/browser-media",Ec=5000,Mc=async(c)=>c.peerConnection??await c.getPeerConnection?.()??null,ic=async(c,g)=>{let A=g.fetch??globalThis.fetch;if(!A)return;await A(g.path??Oc,{body:JSON.stringify(c),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},s=(c)=>{let g=null,A=[],n=async()=>{let I=await Mc(c);if(!I)return;let y=await B({peerConnection:I}),_=o({...c,stats:y}),L=c.continuity===!1?void 0:j({...c.continuity,previousStats:A,stats:y}),S={at:Date.now(),continuity:L,report:_,scenarioId:c.getScenarioId?.()??null,sessionId:c.getSessionId?.()??null};return A=y,c.onReport?.(S),await ic(S,c),S},T=()=>{n().catch((I)=>{c.onError?.(I)})},V=()=>{if(g)clearInterval(g),g=null};return{close:V,reportOnce:n,stop:V,start:()=>{if(g)return;T(),g=setInterval(T,c.intervalMs??Ec)}}};var H=()=>{},$c=()=>H,Pc={callControl:H,close:H,endTurn:H,send:H,sendAudio:H,simulateDisconnect:H,subscribe:$c,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",start:()=>{}},Hc=()=>crypto.randomUUID(),Wc=(c,g,A)=>{let{hostname:n,port:T,protocol:V}=window.location,I=V==="https:"?"wss:":"ws:",y=T?`:${T}`:"",_=new URL(`${I}//${n}${y}${c}`);if(_.searchParams.set("sessionId",g),A)_.searchParams.set("scenarioId",A);return _.toString()},dc=(c)=>{if(!c||typeof c!=="object"||!("type"in c))return!1;switch(c.type){case"audio":case"assistant":case"call_lifecycle":case"complete":case"connection":case"error":case"final":case"partial":case"pong":case"replay":case"session":case"turn":return!0;default:return!1}},Nc=(c)=>{if(typeof c.data!=="string")return null;try{let g=JSON.parse(c.data);return dc(g)?g:null}catch{return null}},k=(c,g={})=>{if(typeof window>"u")return Pc;let A=new Set,n=g.reconnect!==!1,T=g.maxReconnectAttempts??10,V=g.pingInterval??30000,I={isConnected:!1,pendingMessages:[],scenarioId:g.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:g.sessionId??Hc(),ws:null},y=(C)=>{A.forEach((U)=>U(C))},_=()=>{if(I.pingInterval)clearInterval(I.pingInterval),I.pingInterval=null;if(I.reconnectTimeout)clearTimeout(I.reconnectTimeout),I.reconnectTimeout=null},L=()=>{if(I.ws?.readyState!==1)return;while(I.pendingMessages.length>0){let C=I.pendingMessages.shift();if(C!==void 0)I.ws.send(C)}},S=()=>{let C=Date.now()+500;I.reconnectAttempts+=1,y({reconnect:{attempts:I.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:T,nextAttemptAt:C,status:"reconnecting"},type:"connection"}),I.reconnectTimeout=setTimeout(()=>{if(I.reconnectAttempts>T){y({reconnect:{attempts:I.reconnectAttempts,maxAttempts:T,status:"exhausted"},type:"connection"});return}h()},500)},h=()=>{let C=new WebSocket(Wc(c,I.sessionId,I.scenarioId));C.binaryType="arraybuffer",C.onopen=()=>{let U=I.reconnectAttempts>0;if(I.isConnected=!0,L(),U)y({reconnect:{attempts:I.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:T,status:"resumed"},type:"connection"}),I.reconnectAttempts=0;A.forEach((O)=>O({scenarioId:I.scenarioId??void 0,sessionId:I.sessionId,status:"active",type:"session"})),I.pingInterval=setInterval(()=>{if(C.readyState===1)C.send(JSON.stringify({type:"ping"}))},V)},C.onmessage=(U)=>{let O=Nc(U);if(!O)return;if(O.type==="session")I.sessionId=O.sessionId,I.scenarioId=O.scenarioId??I.scenarioId;A.forEach((nc)=>nc(O))},C.onclose=(U)=>{if(I.isConnected=!1,_(),n&&U.code!==1000&&I.reconnectAttempts<T)S();else if(n&&U.code!==1000)y({reconnect:{attempts:I.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:T,status:"exhausted"},type:"connection"})},I.ws=C},l=(C)=>{if(I.ws?.readyState===1){I.ws.send(C);return}I.pendingMessages.push(C)},b=(C)=>{l(JSON.stringify(C))},R=(C={})=>{if(C.sessionId)I.sessionId=C.sessionId;if(C.scenarioId)I.scenarioId=C.scenarioId;b({scenarioId:I.scenarioId??void 0,sessionId:I.sessionId,type:"start"})},W=(C)=>{l(C)},E=()=>{b({type:"end_turn"})},M=(C)=>{b({...C,type:"call_control"})},i=()=>{if(_(),I.ws)I.ws.close(1000),I.ws=null;I.isConnected=!1,A.clear()},d=()=>{if(I.ws?.readyState===1)I.ws.close(4000,"absolutejs-voice-reconnect-proof")},N=(C)=>{return A.add(C),()=>{A.delete(C)}};return h(),{callControl:M,close:i,endTurn:E,send:b,sendAudio:W,simulateDisconnect:d,start:R,subscribe:N,getReadyState:()=>I.ws?.readyState??3,getScenarioId:()=>I.scenarioId??"",getSessionId:()=>I.sessionId}};var xc=()=>({attempts:0,maxAttempts:0,status:"idle"}),Gc=()=>({assistantAudio:[],assistantStreamingText:"",assistantTexts:[],call:null,error:null,isConnected:!1,partial:"",reconnect:xc(),scenarioId:null,sessionId:null,sessionMetadata:null,status:"idle",turns:[]}),f=()=>{let c=Gc(),g=new Set,A=()=>{g.forEach((T)=>T())};return{dispatch:(T)=>{switch(T.type){case"audio":c={...c,assistantAudio:[...c.assistantAudio,{chunk:T.chunk,format:T.format,receivedAt:T.receivedAt,turnId:T.turnId}]};break;case"assistant":c={...c,assistantStreamingText:"",assistantTexts:[...c.assistantTexts,T.text]};break;case"assistant_delta":c={...c,assistantStreamingText:`${c.assistantStreamingText}${T.delta}`};break;case"complete":c={...c,sessionId:T.sessionId,status:"completed"};break;case"call_lifecycle":c={...c,call:{...c.call,disposition:T.event.type==="end"?T.event.disposition:c.call?.disposition,endedAt:T.event.type==="end"?T.event.at:c.call?.endedAt,events:[...c.call?.events??[],T.event],lastEventAt:T.event.at,startedAt:c.call?.startedAt??T.event.at},sessionId:T.sessionId};break;case"connected":c={...c,isConnected:!0,reconnect:c.reconnect.status==="reconnecting"?{...c.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:c.reconnect};break;case"connection":c={...c,reconnect:T.reconnect};break;case"disconnected":c={...c,isConnected:!1};break;case"error":c={...c,error:T.message};break;case"final":c={...c,partial:T.transcript.text,turns:c.turns.map((V)=>V)};break;case"partial":c={...c,partial:T.transcript.text};break;case"replay":c={...c,assistantStreamingText:"",assistantTexts:[...T.assistantTexts],call:T.call??null,error:null,isConnected:T.status==="active",partial:T.partial,reconnect:c.reconnect.status==="reconnecting"?{...c.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:c.reconnect,scenarioId:T.scenarioId??c.scenarioId,sessionId:T.sessionId,sessionMetadata:T.sessionMetadata??c.sessionMetadata,status:T.status,turns:[...T.turns]};break;case"session":c={...c,error:null,scenarioId:T.scenarioId??c.scenarioId,isConnected:T.status==="active",sessionId:T.sessionId,sessionMetadata:T.sessionMetadata??c.sessionMetadata,status:T.status};break;case"turn":c={...c,partial:"",turns:[...c.turns,T.turn]};break}A()},getServerSnapshot:()=>c,getSnapshot:()=>c,subscribe:(T)=>{return g.add(T),()=>{g.delete(T)}}}};var F=(c,g={})=>{let A=k(c,g),n=f(),T=g.browserMedia&&typeof window<"u"?s({...g.browserMedia,getScenarioId:()=>g.browserMedia?g.browserMedia.getScenarioId?.()??A.getScenarioId():A.getScenarioId(),getSessionId:()=>g.browserMedia?g.browserMedia.getSessionId?.()??A.getSessionId():A.getSessionId()}):null,V=new Set,I=(S)=>Promise.resolve().then(()=>{if(!S?.sessionId&&!S?.scenarioId)return;A.start(S),T?.start()}),y=()=>{V.forEach((S)=>S())},_=()=>{if(!g.reconnectReportPath||typeof fetch>"u")return;let S=n.getSnapshot(),h=JSON.stringify({at:Date.now(),reconnect:S.reconnect,scenarioId:S.scenarioId,sessionId:A.getSessionId(),turnIds:S.turns.map((l)=>l.id)});fetch(g.reconnectReportPath,{body:h,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},L=A.subscribe((S)=>{let h=z(S);if(h){if(n.dispatch(h),S.type==="connection")_();y()}});return{start:I,get assistantAudio(){return n.getSnapshot().assistantAudio},get assistantTexts(){return n.getSnapshot().assistantTexts},get assistantStreamingText(){return n.getSnapshot().assistantStreamingText},get call(){return n.getSnapshot().call},callControl(S){A.callControl(S)},close(){L(),T?.close(),A.close(),n.dispatch({type:"disconnected"}),y()},endTurn(){A.endTurn()},get error(){return n.getSnapshot().error},getServerSnapshot(){return n.getServerSnapshot()},getSnapshot(){return n.getSnapshot()},get isConnected(){return n.getSnapshot().isConnected},get partial(){return n.getSnapshot().partial},get reconnect(){return n.getSnapshot().reconnect},get scenarioId(){return n.getSnapshot().scenarioId},sendAudio(S){A.sendAudio(S)},get sessionId(){return A.getSessionId()},get sessionMetadata(){return n.getSnapshot().sessionMetadata},simulateDisconnect(){A.simulateDisconnect()},get status(){return n.getSnapshot().status},subscribe(S){return V.add(S),()=>{V.delete(S)}},get turns(){return n.getSnapshot().turns}}};var t=(c)=>{if(!c||c.enabled===!1)return;return{enabled:!0,maxGain:c.maxGain??3,noiseGateAttenuation:c.noiseGateAttenuation??0.15,noiseGateThreshold:c.noiseGateThreshold??0.006,targetLevel:c.targetLevel??0.08}};var Xc={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}},Yc={"accent-heavy":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},general:{},"noisy-room":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},"short-command":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var e=(c)=>{let g=c?.profile??"fast",A=c?.qualityProfile??"general",n=Xc[g],T=Yc[A];return{profile:g,qualityProfile:A,silenceMs:c?.silenceMs??T.silenceMs??n.silenceMs,speechThreshold:c?.speechThreshold??T.speechThreshold??n.speechThreshold,transcriptStabilityMs:c?.transcriptStabilityMs??T.transcriptStabilityMs??n.transcriptStabilityMs}};var Qc={chat:{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"balanced",qualityProfile:"short-command"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"fast",qualityProfile:"general"}},dictation:{audioConditioning:{enabled:!0,maxGain:2.25,noiseGateAttenuation:0.05,noiseGateThreshold:0.003,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"guided-intake":{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"turn-scoped",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"noisy-room":{audioConditioning:{enabled:!0,maxGain:3,noiseGateAttenuation:0.12,noiseGateThreshold:0.006,targetLevel:0.085},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:2100,speechThreshold:0.02,transcriptStabilityMs:1650}},"pstn-balanced":{audioConditioning:{enabled:!0,maxGain:2.8,noiseGateAttenuation:0.07,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:660,speechThreshold:0.012,transcriptStabilityMs:300}},"pstn-fast":{audioConditioning:{enabled:!0,maxGain:2.75,noiseGateAttenuation:0.06,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:620,speechThreshold:0.012,transcriptStabilityMs:280}},reliability:{audioConditioning:{enabled:!0,maxGain:2.9,noiseGateAttenuation:0.08,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room"}}},v=(c="default")=>{let g=Qc[c];return{audioConditioning:t(g.audioConditioning),capture:{channelCount:g.capture?.channelCount??1,sampleRateHz:g.capture?.sampleRateHz??16000},connection:{...g.connection},name:c,sttLifecycle:g.sttLifecycle??"continuous",turnDetection:e(g.turnDetection)}};var Jc=(c)=>({assistantAudio:[...c.assistantAudio],assistantStreamingText:c.assistantStreamingText,assistantTexts:[...c.assistantTexts],call:c.call,error:c.error,isConnected:c.isConnected,isRecording:!1,partial:c.partial,reconnect:c.reconnect,recordingError:null,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,scenarioId:c.scenarioId,status:c.status,turns:[...c.turns]}),r=(c,g={})=>{let A=v(g.preset),n=F(c,{...A.connection,...g.connection}),T=null,V=Jc(n),I=new Set,y=()=>{for(let R of I)R()},_=()=>{if(V={...V,assistantAudio:[...n.assistantAudio],assistantStreamingText:n.assistantStreamingText,assistantTexts:[...n.assistantTexts],call:n.call,error:n.error,isConnected:n.isConnected,partial:n.partial,reconnect:n.reconnect,sessionId:n.sessionId,sessionMetadata:n.sessionMetadata,scenarioId:n.scenarioId,status:n.status,turns:[...n.turns]},g.autoStopOnComplete!==!1&&V.status==="completed"&&V.isRecording)T?.stop(),T=null,V={...V,isRecording:!1};y()},L=n.subscribe(_);_();let S=()=>{if(T)return T;return T=q({channelCount:g.capture?.channelCount??A.capture.channelCount,onLevel:g.capture?.onLevel,onAudio:(R)=>{if(g.capture?.onAudio){g.capture.onAudio(R,n.sendAudio);return}n.sendAudio(R)},sampleRateHz:g.capture?.sampleRateHz??A.capture.sampleRateHz}),T},h=()=>{T?.stop(),T=null,V={...V,isRecording:!1},y()},l=async()=>{if(V.isRecording)return;try{V={...V,recordingError:null},y(),await S().start(),V={...V,isRecording:!0},y()}catch(R){throw T=null,V={...V,isRecording:!1,recordingError:R instanceof Error?R.message:String(R)},y(),R}};return{close:()=>{L(),h(),n.close()},startRecording:l,stopRecording:h,get assistantAudio(){return V.assistantAudio},get assistantTexts(){return V.assistantTexts},get assistantStreamingText(){return V.assistantStreamingText},bindHTMX(R){return Z(n,R)},get call(){return V.call},callControl:(R)=>n.callControl(R),endTurn:()=>n.endTurn(),get error(){return V.error},getServerSnapshot:()=>V,getSnapshot:()=>V,get isConnected(){return V.isConnected},get isRecording(){return V.isRecording},get partial(){return V.partial},get reconnect(){return V.reconnect},get recordingError(){return V.recordingError},get scenarioId(){return V.scenarioId},sendAudio:(R)=>n.sendAudio(R),get sessionId(){return V.sessionId},get sessionMetadata(){return V.sessionMetadata},simulateDisconnect:()=>n.simulateDisconnect(),get status(){return V.status},subscribe:(R)=>{return I.add(R),()=>{I.delete(R)}},toggleRecording:async()=>{if(V.isRecording){h();return}await l()},get turns(){return V.turns}}};var P=(c)=>String(c).replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'");var a=(c)=>{if(!c.isConnected)return"idle";if(c.isPlaying)return"speaking";if(c.isRecording&&c.hasActivePartial)return"listening";if(c.isRecording)return"listening";if(c.lastTranscriptAt&&!c.lastAssistantAt)return"thinking";if(c.lastTranscriptAt&&c.lastAssistantAt&&c.lastTranscriptAt>c.lastAssistantAt)return"thinking";return"idle"};var Zc={accent:"#3b82f6",background:"#0f172a",errorAccent:"#ef4444",fontFamily:'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',foreground:"#f8fafc",radius:16},qc={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},zc=(c,g)=>{switch(c){case"listening":return g.listening;case"speaking":return g.speaking;case"thinking":return g.thinking;case"idle":return g.idle}},m=(c)=>{let g={...Zc,...c.theme},A={...qc,...c.labels},n=c.state.assistantAudio.at(-1)?.receivedAt,T=c.state.turns.at(-1)?.committedAt,V=a({hasActivePartial:c.state.partial.length>0,isConnected:c.state.isConnected,isPlaying:!1,isRecording:c.state.isRecording,lastAssistantAt:n,lastTranscriptAt:T}),I=!c.state.isConnected&&c.state.status!=="idle"&&!c.state.error,y=c.state.error?"Error":I?A.connecting:c.state.status==="completed"?A.callEnded:zc(V,A);return{agentState:V,classes:{container:`absolute-voice-widget absolute-voice-widget--${V}`,dot:`absolute-voice-widget__dot${c.state.error?" absolute-voice-widget__dot--error":""}`},controls:{canEnd:c.state.isConnected,canMute:c.state.isRecording,canStart:!c.state.isRecording&&c.state.status!=="completed"},errorMessage:c.state.error??void 0,labels:A,partial:c.state.partial||void 0,statusLabel:y,theme:g,title:c.title??"Voice"}},Kc=(c)=>typeof c==="number"?`${c}px`:c,u=(c)=>{let g=c.theme,A=`background:${g.background};border-radius:${Kc(g.radius)};color:${g.foreground};font-family:${g.fontFamily};min-width:240px;padding:20px 22px;`,n=`background:${c.errorMessage?g.errorAccent:c.agentState==="idle"?"rgba(148,163,184,0.6)":g.accent};border-radius:50%;height:10px;width:10px;`,T=[];if(c.controls.canStart)T.push(`<button type="button" data-action="start" style="background:${g.accent};border:none;border-radius:12px;color:${g.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${P(c.labels.startCall)}</button>`);if(c.controls.canMute)T.push(`<button type="button" data-action="mute" style="background:transparent;border:1px solid rgba(255,255,255,0.18);border-radius:12px;color:${g.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${P(c.labels.mute)}</button>`);if(c.controls.canEnd)T.push(`<button type="button" data-action="end" style="background:${g.errorAccent};border:none;border-radius:12px;color:${g.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${P(c.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${c.agentState}" class="${P(c.classes.container)}" style="${A}">
|
|
2
2
|
<div style="align-items:center;display:flex;gap:10px;margin-bottom:12px;">
|
|
3
|
-
<span aria-hidden="true" class="${
|
|
4
|
-
<strong style="font-size:15px;">${
|
|
5
|
-
<span style="font-size:13px;margin-left:auto;opacity:0.7;">${
|
|
3
|
+
<span aria-hidden="true" class="${P(c.classes.dot)}" style="${n}"></span>
|
|
4
|
+
<strong style="font-size:15px;">${P(c.title)}</strong>
|
|
5
|
+
<span style="font-size:13px;margin-left:auto;opacity:0.7;">${P(c.statusLabel)}</span>
|
|
6
6
|
</div>
|
|
7
|
-
${c.partial?`<p style="font-size:13px;margin:8px 0 12px;opacity:0.85;word-break:break-word;">“${
|
|
8
|
-
<div style="display:flex;gap:10px;">${
|
|
9
|
-
${c.errorMessage?`<p style="color:${
|
|
10
|
-
</div>`};var
|
|
7
|
+
${c.partial?`<p style="font-size:13px;margin:8px 0 12px;opacity:0.85;word-break:break-word;">“${P(c.partial)}”</p>`:""}
|
|
8
|
+
<div style="display:flex;gap:10px;">${T.join("")}</div>
|
|
9
|
+
${c.errorMessage?`<p style="color:${g.errorAccent};font-size:12px;margin-top:12px;">${P(c.errorMessage)}</p>`:""}
|
|
10
|
+
</div>`};var oc=(c)=>{if(typeof c!=="string")return c;let g=document.querySelector(c);if(!g)throw Error(`AbsoluteVoice.mount: no element matches "${c}"`);return g},p=(c,g={})=>{let A=oc(c),n=r(g.path??"/voice",g.controllerOptions),T=null,V=null,I=()=>{let _=m({...g.labels!==void 0?{labels:g.labels}:{},state:{assistantAudio:n.assistantAudio,error:n.error,isConnected:n.isConnected,isRecording:n.isRecording,partial:n.partial,status:n.status,turns:n.turns},...g.theme!==void 0?{theme:g.theme}:{},...g.title!==void 0?{title:g.title}:{}});A.innerHTML=u(_);for(let L of A.querySelectorAll("button[data-action]")){let{action:S}=L.dataset;L.addEventListener("click",()=>{if(S==="start")n.startRecording();else if(S==="mute")n.stopRecording();else if(S==="end")n.close()})}if(n.error&&n.error!==T)T=n.error,g.onError?.(n.error);if(n.status!==V)V=n.status,g.onStatusChange?.(n.status)},y=n.subscribe(I);if(I(),g.autoStart)n.startRecording();return{controller:n,async end(){await n.close()},mute(){n.stopRecording()},async start(){await n.startRecording()},unmount(){y(),n.close(),A.innerHTML=""}}},cc="0.0.22-beta.516",gc={mount:p,version:cc};if(typeof globalThis<"u")globalThis.AbsoluteVoice=gc;var jc=gc;})();
|
package/dist/index.js
CHANGED
|
@@ -3870,6 +3870,7 @@ var createVoiceSession = (options) => {
|
|
|
3870
3870
|
let adapterGenerationCounter = 0;
|
|
3871
3871
|
let activeAdapterGeneration = 0;
|
|
3872
3872
|
let activeTTSTurnId;
|
|
3873
|
+
let assistantSpeechEndsAt = 0;
|
|
3873
3874
|
let fillerTimer = null;
|
|
3874
3875
|
let fillerActive = false;
|
|
3875
3876
|
let fillerToken = 0;
|
|
@@ -4262,6 +4263,7 @@ var createVoiceSession = (options) => {
|
|
|
4262
4263
|
return;
|
|
4263
4264
|
}
|
|
4264
4265
|
activeTTSTurnId = undefined;
|
|
4266
|
+
assistantSpeechEndsAt = Date.now();
|
|
4265
4267
|
appendTurnLatencyStage({
|
|
4266
4268
|
metadata: { reason },
|
|
4267
4269
|
stage: "tts_canceled",
|
|
@@ -4304,6 +4306,12 @@ var createVoiceSession = (options) => {
|
|
|
4304
4306
|
turnId: activeTTSTurnId,
|
|
4305
4307
|
type: "audio"
|
|
4306
4308
|
});
|
|
4309
|
+
const bytesPerSample = input.format.encoding === "pcm_s16le" ? 2 : 1;
|
|
4310
|
+
const bytesPerSecond = input.format.sampleRateHz * input.format.channels * bytesPerSample;
|
|
4311
|
+
if (bytesPerSecond > 0) {
|
|
4312
|
+
const chunkMs = normalizedChunk.byteLength / bytesPerSecond * 1000;
|
|
4313
|
+
assistantSpeechEndsAt = Math.max(assistantSpeechEndsAt, Date.now()) + chunkMs;
|
|
4314
|
+
}
|
|
4307
4315
|
if (activeTTSTurnId) {
|
|
4308
4316
|
await appendTurnLatencyStage({
|
|
4309
4317
|
at: input.receivedAt,
|
|
@@ -4413,6 +4421,20 @@ var createVoiceSession = (options) => {
|
|
|
4413
4421
|
session
|
|
4414
4422
|
});
|
|
4415
4423
|
};
|
|
4424
|
+
const DRAIN_POLL_MS = 200;
|
|
4425
|
+
const DRAIN_TAIL_BUFFER_MS = 300;
|
|
4426
|
+
const DRAIN_MAX_MS = 12000;
|
|
4427
|
+
const drainAssistantSpeech = async () => {
|
|
4428
|
+
const startedAt = Date.now();
|
|
4429
|
+
while (Date.now() - startedAt < DRAIN_MAX_MS) {
|
|
4430
|
+
const remaining = assistantSpeechEndsAt + DRAIN_TAIL_BUFFER_MS - Date.now();
|
|
4431
|
+
if (remaining <= 0)
|
|
4432
|
+
return;
|
|
4433
|
+
await new Promise((resolve) => {
|
|
4434
|
+
setTimeout(resolve, Math.min(remaining, DRAIN_POLL_MS));
|
|
4435
|
+
});
|
|
4436
|
+
}
|
|
4437
|
+
};
|
|
4416
4438
|
const completeInternal = async (result, input = {}) => {
|
|
4417
4439
|
clearSilenceTimer();
|
|
4418
4440
|
const disposition = input.disposition ?? "completed";
|
|
@@ -4446,6 +4468,9 @@ var createVoiceSession = (options) => {
|
|
|
4446
4468
|
if (!didComplete) {
|
|
4447
4469
|
return;
|
|
4448
4470
|
}
|
|
4471
|
+
if (disposition === "completed") {
|
|
4472
|
+
await drainAssistantSpeech();
|
|
4473
|
+
}
|
|
4449
4474
|
await appendTrace({
|
|
4450
4475
|
payload: {
|
|
4451
4476
|
disposition,
|
package/dist/react/index.js
CHANGED
|
@@ -12152,10 +12152,16 @@ var createMicrophoneCapture = (options) => {
|
|
|
12152
12152
|
}
|
|
12153
12153
|
mediaStream = await navigator.mediaDevices.getUserMedia({
|
|
12154
12154
|
audio: {
|
|
12155
|
-
|
|
12155
|
+
autoGainControl: true,
|
|
12156
|
+
channelCount: options.channelCount ?? 1,
|
|
12157
|
+
echoCancellation: true,
|
|
12158
|
+
noiseSuppression: true
|
|
12156
12159
|
}
|
|
12157
12160
|
});
|
|
12158
12161
|
audioContext = new AudioContextCtor;
|
|
12162
|
+
if (audioContext.state === "suspended") {
|
|
12163
|
+
await audioContext.resume();
|
|
12164
|
+
}
|
|
12159
12165
|
sourceNode = audioContext.createMediaStreamSource(mediaStream);
|
|
12160
12166
|
processorNode = audioContext.createScriptProcessor(4096, 1, 1);
|
|
12161
12167
|
processorNode.onaudioprocess = (event) => {
|
package/dist/svelte/index.js
CHANGED
|
@@ -527,10 +527,16 @@ var createMicrophoneCapture = (options) => {
|
|
|
527
527
|
}
|
|
528
528
|
mediaStream = await navigator.mediaDevices.getUserMedia({
|
|
529
529
|
audio: {
|
|
530
|
-
|
|
530
|
+
autoGainControl: true,
|
|
531
|
+
channelCount: options.channelCount ?? 1,
|
|
532
|
+
echoCancellation: true,
|
|
533
|
+
noiseSuppression: true
|
|
531
534
|
}
|
|
532
535
|
});
|
|
533
536
|
audioContext = new AudioContextCtor;
|
|
537
|
+
if (audioContext.state === "suspended") {
|
|
538
|
+
await audioContext.resume();
|
|
539
|
+
}
|
|
534
540
|
sourceNode = audioContext.createMediaStreamSource(mediaStream);
|
|
535
541
|
processorNode = audioContext.createScriptProcessor(4096, 1, 1);
|
|
536
542
|
processorNode.onaudioprocess = (event) => {
|
package/dist/testing/index.js
CHANGED
|
@@ -2051,10 +2051,16 @@ var createMicrophoneCapture = (options) => {
|
|
|
2051
2051
|
}
|
|
2052
2052
|
mediaStream = await navigator.mediaDevices.getUserMedia({
|
|
2053
2053
|
audio: {
|
|
2054
|
-
|
|
2054
|
+
autoGainControl: true,
|
|
2055
|
+
channelCount: options.channelCount ?? 1,
|
|
2056
|
+
echoCancellation: true,
|
|
2057
|
+
noiseSuppression: true
|
|
2055
2058
|
}
|
|
2056
2059
|
});
|
|
2057
2060
|
audioContext = new AudioContextCtor;
|
|
2061
|
+
if (audioContext.state === "suspended") {
|
|
2062
|
+
await audioContext.resume();
|
|
2063
|
+
}
|
|
2058
2064
|
sourceNode = audioContext.createMediaStreamSource(mediaStream);
|
|
2059
2065
|
processorNode = audioContext.createScriptProcessor(4096, 1, 1);
|
|
2060
2066
|
processorNode.onaudioprocess = (event) => {
|
|
@@ -5780,6 +5786,7 @@ var createVoiceSession = (options) => {
|
|
|
5780
5786
|
let adapterGenerationCounter = 0;
|
|
5781
5787
|
let activeAdapterGeneration = 0;
|
|
5782
5788
|
let activeTTSTurnId;
|
|
5789
|
+
let assistantSpeechEndsAt = 0;
|
|
5783
5790
|
let fillerTimer = null;
|
|
5784
5791
|
let fillerActive = false;
|
|
5785
5792
|
let fillerToken = 0;
|
|
@@ -6172,6 +6179,7 @@ var createVoiceSession = (options) => {
|
|
|
6172
6179
|
return;
|
|
6173
6180
|
}
|
|
6174
6181
|
activeTTSTurnId = undefined;
|
|
6182
|
+
assistantSpeechEndsAt = Date.now();
|
|
6175
6183
|
appendTurnLatencyStage({
|
|
6176
6184
|
metadata: { reason },
|
|
6177
6185
|
stage: "tts_canceled",
|
|
@@ -6214,6 +6222,12 @@ var createVoiceSession = (options) => {
|
|
|
6214
6222
|
turnId: activeTTSTurnId,
|
|
6215
6223
|
type: "audio"
|
|
6216
6224
|
});
|
|
6225
|
+
const bytesPerSample = input.format.encoding === "pcm_s16le" ? 2 : 1;
|
|
6226
|
+
const bytesPerSecond = input.format.sampleRateHz * input.format.channels * bytesPerSample;
|
|
6227
|
+
if (bytesPerSecond > 0) {
|
|
6228
|
+
const chunkMs = normalizedChunk.byteLength / bytesPerSecond * 1000;
|
|
6229
|
+
assistantSpeechEndsAt = Math.max(assistantSpeechEndsAt, Date.now()) + chunkMs;
|
|
6230
|
+
}
|
|
6217
6231
|
if (activeTTSTurnId) {
|
|
6218
6232
|
await appendTurnLatencyStage({
|
|
6219
6233
|
at: input.receivedAt,
|
|
@@ -6323,6 +6337,20 @@ var createVoiceSession = (options) => {
|
|
|
6323
6337
|
session
|
|
6324
6338
|
});
|
|
6325
6339
|
};
|
|
6340
|
+
const DRAIN_POLL_MS = 200;
|
|
6341
|
+
const DRAIN_TAIL_BUFFER_MS = 300;
|
|
6342
|
+
const DRAIN_MAX_MS = 12000;
|
|
6343
|
+
const drainAssistantSpeech = async () => {
|
|
6344
|
+
const startedAt = Date.now();
|
|
6345
|
+
while (Date.now() - startedAt < DRAIN_MAX_MS) {
|
|
6346
|
+
const remaining = assistantSpeechEndsAt + DRAIN_TAIL_BUFFER_MS - Date.now();
|
|
6347
|
+
if (remaining <= 0)
|
|
6348
|
+
return;
|
|
6349
|
+
await new Promise((resolve2) => {
|
|
6350
|
+
setTimeout(resolve2, Math.min(remaining, DRAIN_POLL_MS));
|
|
6351
|
+
});
|
|
6352
|
+
}
|
|
6353
|
+
};
|
|
6326
6354
|
const completeInternal = async (result, input = {}) => {
|
|
6327
6355
|
clearSilenceTimer();
|
|
6328
6356
|
const disposition = input.disposition ?? "completed";
|
|
@@ -6356,6 +6384,9 @@ var createVoiceSession = (options) => {
|
|
|
6356
6384
|
if (!didComplete) {
|
|
6357
6385
|
return;
|
|
6358
6386
|
}
|
|
6387
|
+
if (disposition === "completed") {
|
|
6388
|
+
await drainAssistantSpeech();
|
|
6389
|
+
}
|
|
6359
6390
|
await appendTrace({
|
|
6360
6391
|
payload: {
|
|
6361
6392
|
disposition,
|
package/dist/vue/index.js
CHANGED
|
@@ -11569,10 +11569,16 @@ var createMicrophoneCapture = (options) => {
|
|
|
11569
11569
|
}
|
|
11570
11570
|
mediaStream = await navigator.mediaDevices.getUserMedia({
|
|
11571
11571
|
audio: {
|
|
11572
|
-
|
|
11572
|
+
autoGainControl: true,
|
|
11573
|
+
channelCount: options.channelCount ?? 1,
|
|
11574
|
+
echoCancellation: true,
|
|
11575
|
+
noiseSuppression: true
|
|
11573
11576
|
}
|
|
11574
11577
|
});
|
|
11575
11578
|
audioContext = new AudioContextCtor;
|
|
11579
|
+
if (audioContext.state === "suspended") {
|
|
11580
|
+
await audioContext.resume();
|
|
11581
|
+
}
|
|
11576
11582
|
sourceNode = audioContext.createMediaStreamSource(mediaStream);
|
|
11577
11583
|
processorNode = audioContext.createScriptProcessor(4096, 1, 1);
|
|
11578
11584
|
processorNode.onaudioprocess = (event) => {
|