@absolutejs/voice 0.0.22-beta.647 → 0.0.22-beta.649
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 +67 -29
- package/dist/client/connection.d.ts +2 -1
- package/dist/client/htmxBootstrap.js +67 -29
- package/dist/client/index.js +67 -29
- package/dist/core/types.d.ts +21 -2
- package/dist/embed/index.js +66 -28
- package/dist/embed/voice-widget.js +7 -7
- package/dist/index.js +34 -1
- package/dist/react/index.js +67 -29
- package/dist/svelte/index.js +67 -29
- package/dist/testing/index.js +69 -31
- package/dist/vue/index.js +67 -29
- package/package.json +2 -2
package/dist/embed/index.js
CHANGED
|
@@ -108,29 +108,34 @@ var createMicrophoneCapture = (options) => {
|
|
|
108
108
|
if (!AudioContextCtor) {
|
|
109
109
|
throw new Error("Browser microphone capture requires AudioContext support.");
|
|
110
110
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
111
|
+
try {
|
|
112
|
+
mediaStream = options.stream ?? await navigator.mediaDevices.getUserMedia({
|
|
113
|
+
audio: {
|
|
114
|
+
autoGainControl: true,
|
|
115
|
+
channelCount: options.channelCount ?? 1,
|
|
116
|
+
echoCancellation: true,
|
|
117
|
+
noiseSuppression: true
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
audioContext = new AudioContextCtor;
|
|
121
|
+
if (audioContext.state === "suspended") {
|
|
122
|
+
await audioContext.resume();
|
|
117
123
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
124
|
+
sourceNode = audioContext.createMediaStreamSource(mediaStream);
|
|
125
|
+
processorNode = audioContext.createScriptProcessor(4096, 1, 1);
|
|
126
|
+
processorNode.onaudioprocess = (event) => {
|
|
127
|
+
const channel = event.inputBuffer.getChannelData(0);
|
|
128
|
+
const downsampled = downsampleBuffer(channel, audioContext?.sampleRate ?? 48000, options.sampleRateHz ?? 16000);
|
|
129
|
+
const pcm = floatTo16BitPCM(downsampled);
|
|
130
|
+
options.onLevel?.(getPcmLevel(pcm));
|
|
131
|
+
options.onAudio(pcm);
|
|
132
|
+
};
|
|
133
|
+
sourceNode.connect(processorNode);
|
|
134
|
+
processorNode.connect(audioContext.destination);
|
|
135
|
+
} catch (error) {
|
|
136
|
+
stop();
|
|
137
|
+
throw error;
|
|
122
138
|
}
|
|
123
|
-
sourceNode = audioContext.createMediaStreamSource(mediaStream);
|
|
124
|
-
processorNode = audioContext.createScriptProcessor(4096, 1, 1);
|
|
125
|
-
processorNode.onaudioprocess = (event) => {
|
|
126
|
-
const channel = event.inputBuffer.getChannelData(0);
|
|
127
|
-
const downsampled = downsampleBuffer(channel, audioContext?.sampleRate ?? 48000, options.sampleRateHz ?? 16000);
|
|
128
|
-
const pcm = floatTo16BitPCM(downsampled);
|
|
129
|
-
options.onLevel?.(getPcmLevel(pcm));
|
|
130
|
-
options.onAudio(pcm);
|
|
131
|
-
};
|
|
132
|
-
sourceNode.connect(processorNode);
|
|
133
|
-
processorNode.connect(audioContext.destination);
|
|
134
139
|
};
|
|
135
140
|
const stop = () => {
|
|
136
141
|
processorNode?.disconnect();
|
|
@@ -499,6 +504,7 @@ var noopUnsubscribe = () => noop;
|
|
|
499
504
|
var NOOP_CONNECTION = {
|
|
500
505
|
callControl: noop,
|
|
501
506
|
close: noop,
|
|
507
|
+
disconnect: noop,
|
|
502
508
|
endTurn: noop,
|
|
503
509
|
send: noop,
|
|
504
510
|
sendAudio: noop,
|
|
@@ -510,7 +516,7 @@ var NOOP_CONNECTION = {
|
|
|
510
516
|
start: () => {}
|
|
511
517
|
};
|
|
512
518
|
var createSessionId = () => crypto.randomUUID();
|
|
513
|
-
var buildWsUrl = (path, sessionId, scenarioId) => {
|
|
519
|
+
var buildWsUrl = (path, sessionId, scenarioId, query) => {
|
|
514
520
|
const { hostname, port, protocol } = window.location;
|
|
515
521
|
const wsProtocol = protocol === "https:" ? "wss:" : "ws:";
|
|
516
522
|
const portSuffix = port ? `:${port}` : "";
|
|
@@ -519,6 +525,11 @@ var buildWsUrl = (path, sessionId, scenarioId) => {
|
|
|
519
525
|
if (scenarioId) {
|
|
520
526
|
url.searchParams.set(DEFAULT_SCENARIO_QUERY_PARAM, scenarioId);
|
|
521
527
|
}
|
|
528
|
+
for (const [key, value] of Object.entries(query)) {
|
|
529
|
+
if (key !== "sessionId" && key !== DEFAULT_SCENARIO_QUERY_PARAM && value) {
|
|
530
|
+
url.searchParams.set(key, value);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
522
533
|
return url.toString();
|
|
523
534
|
};
|
|
524
535
|
var isVoiceServerMessage = (value) => {
|
|
@@ -628,7 +639,7 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
628
639
|
}, delayMs);
|
|
629
640
|
};
|
|
630
641
|
const connect = () => {
|
|
631
|
-
const ws = new WebSocket(buildWsUrl(path, state.sessionId, state.scenarioId));
|
|
642
|
+
const ws = new WebSocket(buildWsUrl(path, state.sessionId, state.scenarioId, options.query ?? {}));
|
|
632
643
|
ws.binaryType = "arraybuffer";
|
|
633
644
|
ws.onopen = () => {
|
|
634
645
|
const wasReconnecting = state.reconnectAttempts > 0;
|
|
@@ -724,7 +735,19 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
724
735
|
type: "call_control"
|
|
725
736
|
});
|
|
726
737
|
};
|
|
727
|
-
const close = () => {
|
|
738
|
+
const close = (reason = "client-close") => {
|
|
739
|
+
clearTimers();
|
|
740
|
+
if (state.ws) {
|
|
741
|
+
if (state.ws.readyState === WS_OPEN) {
|
|
742
|
+
state.ws.send(JSON.stringify({ reason, type: "close" }));
|
|
743
|
+
}
|
|
744
|
+
state.ws.close(WS_NORMAL_CLOSURE);
|
|
745
|
+
state.ws = null;
|
|
746
|
+
}
|
|
747
|
+
state.isConnected = false;
|
|
748
|
+
listeners.clear();
|
|
749
|
+
};
|
|
750
|
+
const disconnect = () => {
|
|
728
751
|
clearTimers();
|
|
729
752
|
if (state.ws) {
|
|
730
753
|
state.ws.close(WS_NORMAL_CLOSURE);
|
|
@@ -748,6 +771,7 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
748
771
|
return {
|
|
749
772
|
callControl,
|
|
750
773
|
close,
|
|
774
|
+
disconnect,
|
|
751
775
|
endTurn,
|
|
752
776
|
send,
|
|
753
777
|
sendAudio,
|
|
@@ -1028,10 +1052,17 @@ var createVoiceStream = (path, options = {}) => {
|
|
|
1028
1052
|
callControl(message) {
|
|
1029
1053
|
connection.callControl(message);
|
|
1030
1054
|
},
|
|
1031
|
-
close() {
|
|
1055
|
+
close(reason) {
|
|
1056
|
+
unsubscribeConnection();
|
|
1057
|
+
browserMediaReporter?.close();
|
|
1058
|
+
connection.close(reason);
|
|
1059
|
+
store.dispatch({ type: "disconnected" });
|
|
1060
|
+
notify();
|
|
1061
|
+
},
|
|
1062
|
+
disconnect() {
|
|
1032
1063
|
unsubscribeConnection();
|
|
1033
1064
|
browserMediaReporter?.close();
|
|
1034
|
-
connection.
|
|
1065
|
+
connection.disconnect();
|
|
1035
1066
|
store.dispatch({ type: "disconnected" });
|
|
1036
1067
|
notify();
|
|
1037
1068
|
},
|
|
@@ -1476,6 +1507,7 @@ var createVoiceController = (path, options = {}) => {
|
|
|
1476
1507
|
};
|
|
1477
1508
|
notify();
|
|
1478
1509
|
} catch (error) {
|
|
1510
|
+
capture?.stop();
|
|
1479
1511
|
capture = null;
|
|
1480
1512
|
state = {
|
|
1481
1513
|
...state,
|
|
@@ -1486,13 +1518,19 @@ var createVoiceController = (path, options = {}) => {
|
|
|
1486
1518
|
throw error;
|
|
1487
1519
|
}
|
|
1488
1520
|
};
|
|
1489
|
-
const close = () => {
|
|
1521
|
+
const close = (reason) => {
|
|
1522
|
+
unsubscribeStream();
|
|
1523
|
+
stopRecording();
|
|
1524
|
+
stream.close(reason);
|
|
1525
|
+
};
|
|
1526
|
+
const disconnect = () => {
|
|
1490
1527
|
unsubscribeStream();
|
|
1491
1528
|
stopRecording();
|
|
1492
|
-
stream.
|
|
1529
|
+
stream.disconnect();
|
|
1493
1530
|
};
|
|
1494
1531
|
return {
|
|
1495
1532
|
close,
|
|
1533
|
+
disconnect,
|
|
1496
1534
|
startRecording,
|
|
1497
1535
|
stopRecording,
|
|
1498
1536
|
get assistantAudio() {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
(()=>{var{defineProperty:j,getOwnPropertyNames:c0,getOwnPropertyDescriptor:R0}=Object,_0=Object.prototype.hasOwnProperty;function $0(g){return this[g]}var I0=(g)=>{var A=(x??=new WeakMap).get(g),C;if(A)return A;if(A=j({},"__esModule",{value:!0}),g&&typeof g==="object"||typeof g==="function"){for(var V of c0(g))if(!_0.call(A,V))j(A,V,{get:$0.bind(g,V),enumerable:!(C=R0(g,V))||C.enumerable})}return x.set(g,A),A},x;var H0=(g)=>g;function U0(g,A){this[g]=H0.bind(null,A)}var L0=(g,A)=>{for(var C in A)j(g,C,{get:A[C],enumerable:!0,configurable:!0,set:U0.bind(A,C)})};var o0={};L0(o0,{mount:()=>A0,default:()=>m0,VOICE_EMBED_VERSION:()=>V0});var G0=(g)=>{if(typeof g!=="string")return g;return document.querySelector(g)},W0=(g,A,C,V)=>{let R=A??g.getAttribute("hx-get")??"";if(!R)return"";let T=new URL(R,window.location.origin);if(V)T.searchParams.set(C,V);else T.searchParams.delete(C);return`${T.pathname}${T.search}${T.hash}`},k=(g,A)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let C=G0(A.element);if(!C)return()=>{};let V=A.eventName??"voice-refresh",R=A.sessionQueryParam??"sessionId",T=()=>{let H=window,c=W0(C,A.route,R,g.sessionId);if(c)C.setAttribute("hx-get",c);H.htmx?.process?.(C),H.htmx?.trigger?.(C,V)},L=g.subscribe(T);return T(),()=>{L()}};var y0=(g)=>Math.max(-1,Math.min(1,g)),w0=(g)=>{let A=new Int16Array(g.length);for(let C=0;C<g.length;C+=1){let V=y0(g[C]??0);A[C]=V<0?V*32768:V*32767}return new Uint8Array(A.buffer)},D0=(g)=>{let A=g instanceof Uint8Array?g:new Uint8Array(g);if(A.byteLength<2)return 0;let C=new Int16Array(A.buffer,A.byteOffset,Math.floor(A.byteLength/2));if(C.length===0)return 0;let V=0;for(let R of C){let T=R/32768;V+=T*T}return Math.min(1,Math.max(0,Math.sqrt(V/C.length)*5.5))},S0=(g,A,C)=>{if(A===C)return g;let V=A/C,R=Math.round(g.length/V),T=new Float32Array(R),L=0,H=0;while(L<T.length){let c=Math.round((L+1)*V),W=0,I=0;for(let y=H;y<c&&y<g.length;y+=1)W+=g[y]??0,I+=1;T[L]=I>0?W/I:0,L+=1,H=c}return T},F=(g)=>{let A=null,C=null,V=null,R=null;return{start:async()=>{if(!g.stream&&(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia))throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let H=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!H)throw Error("Browser microphone capture requires AudioContext support.");if(R=g.stream??await navigator.mediaDevices.getUserMedia({audio:{autoGainControl:!0,channelCount:g.channelCount??1,echoCancellation:!0,noiseSuppression:!0}}),A=new H,A.state==="suspended")await A.resume();C=A.createMediaStreamSource(R),V=A.createScriptProcessor(4096,1,1),V.onaudioprocess=(c)=>{let W=c.inputBuffer.getChannelData(0),I=S0(W,A?.sampleRate??48000,g.sampleRateHz??16000),y=w0(I);g.onLevel?.(D0(y)),g.onAudio(y)},C.connect(V),V.connect(A.destination)},stop:()=>{V?.disconnect(),C?.disconnect(),R?.getTracks().forEach((H)=>H.stop()),A?.close(),g.onLevel?.(0),A=null,R=null,V=null,C=null}}};var B=(g)=>{if(typeof g==="string"&&g.trim())return g;if(g instanceof Error&&g.message.trim())return g.message;if(g&&typeof g==="object"){let A=g;for(let C of["message","reason","description"]){let V=A[C];if(typeof V==="string"&&V.trim())return V}if("error"in A)return B(A.error);if("cause"in A)return B(A.cause);try{return JSON.stringify(g)}catch{}}return"Unexpected error"},f=(g)=>{switch(g.type){case"audio":return{chunk:Uint8Array.from(atob(g.chunkBase64),(A)=>A.charCodeAt(0)),format:g.format,receivedAt:g.receivedAt,turnId:g.turnId,type:"audio"};case"assistant":return{text:g.text,turnId:g.turnId,type:"assistant"};case"assistant_delta":return{delta:g.delta,turnId:g.turnId,type:"assistant_delta"};case"complete":return{sessionId:g.sessionId,type:"complete"};case"connection":return{reconnect:g.reconnect,type:"connection"};case"call_lifecycle":return{event:g.event,sessionId:g.sessionId,type:"call_lifecycle"};case"error":return{message:B(g.message),type:"error"};case"final":return{transcript:g.transcript,type:"final"};case"partial":return{transcript:g.transcript,type:"partial"};case"replay":return{assistantTexts:g.assistantTexts,call:g.call,partial:g.partial,scenarioId:g.scenarioId,sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,status:g.status,turns:g.turns,type:"replay"};case"session":return{sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,scenarioId:g.scenarioId,status:g.status,type:"session"};case"turn":return{turn:g.turn,type:"turn"};default:return null}};var p0=Math.PI*2;var Z=(g,A,C,V)=>{g.push({code:C,message:V,severity:A})};var X0=(g)=>g.length===0?void 0:g.reduce((A,C)=>A+C,0)/g.length,b=(g)=>g.length===0?void 0:Math.max(...g);var w=(g,A)=>{let C=g[A];return typeof C==="number"&&Number.isFinite(C)?C:void 0},z=(g,A)=>{let C=g[A];return typeof C==="boolean"?C:void 0},S=(g,A)=>{let C=g[A];return typeof C==="string"?C:void 0},M=(g)=>String(g.id??S(g,"ssrc")??w(g,"ssrc")??S(g,"trackIdentifier")??S(g,"mid")??"unknown"),l=(g)=>g===void 0?void 0:g*1000;var h0=(g)=>{let A={};for(let[C,V]of Object.entries(g))if(V===null||typeof V==="boolean"||typeof V==="number"||typeof V==="string")A[C]=V;return A};var d=(g={})=>{let A=g.stats??[],C=[],V=A.filter((_)=>_.type==="inbound-rtp"&&S(_,"kind")!=="video"),R=A.filter((_)=>_.type==="outbound-rtp"&&S(_,"kind")!=="video"),T=A.filter((_)=>_.type==="candidate-pair"),L=A.filter((_)=>(_.type==="track"||_.type==="media-source")&&S(_,"kind")==="audio"),H=T.filter((_)=>z(_,"selected")===!0||z(_,"nominated")===!0||S(_,"state")==="succeeded").length,c=L.filter((_)=>S(_,"readyState")!=="ended"&&S(_,"trackState")!=="ended"&&z(_,"ended")!==!0).length,W=L.filter((_)=>S(_,"readyState")==="ended"||S(_,"trackState")==="ended"||z(_,"ended")===!0).length,I=V.reduce((_,D)=>_+(w(D,"packetsReceived")??0),0),y=R.reduce((_,D)=>_+(w(D,"packetsSent")??0),0),$=[...V,...R].reduce((_,D)=>_+Math.max(0,w(D,"packetsLost")??0),0),X=I+$,G=X===0?0:$/X,P=V.reduce((_,D)=>_+(w(D,"bytesReceived")??0),0),h=R.reduce((_,D)=>_+(w(D,"bytesSent")??0),0),O=b(T.map((_)=>l(w(_,"currentRoundTripTime")??w(_,"roundTripTime"))).filter((_)=>_!==void 0)),J=b([...V,...R].map((_)=>l(w(_,"jitter"))).filter((_)=>_!==void 0)),E=b(V.map((_)=>{let D=w(_,"jitterBufferDelay"),U=w(_,"jitterBufferEmittedCount");return D!==void 0&&U!==void 0&&U>0?D/U*1000:void 0}).filter((_)=>_!==void 0)),N=L.map((_)=>w(_,"audioLevel")).filter((_)=>_!==void 0);if(g.requireConnectedCandidatePair&&T.length>0&&H===0)Z(C,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(g.requireLiveAudioTrack&&c===0)Z(C,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(g.maxPacketLossRatio!==void 0&&G>g.maxPacketLossRatio)Z(C,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(G)} above ${String(g.maxPacketLossRatio)}.`);if(g.maxRoundTripTimeMs!==void 0&&O!==void 0&&O>g.maxRoundTripTimeMs)Z(C,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(O)}ms above ${String(g.maxRoundTripTimeMs)}ms.`);if(g.maxJitterMs!==void 0&&J!==void 0&&J>g.maxJitterMs)Z(C,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(J)}ms above ${String(g.maxJitterMs)}ms.`);return{activeCandidatePairs:H,audioLevelAverage:X0(N),bytesReceived:P,bytesSent:h,checkedAt:Date.now(),endedAudioTracks:W,inboundPackets:I,issues:C,jitterBufferDelayMs:E,jitterMs:J,liveAudioTracks:c,outboundPackets:y,packetLossRatio:G,packetsLost:$,roundTripTimeMs:O,status:C.some((_)=>_.severity==="error")?"fail":C.length>0?"warn":"pass",totalStats:A.length}},v=async(g)=>{return[...(await g.peerConnection.getStats(g.selector??null)).values()].map(h0)};var i=(g={})=>{let A=g.stats??[],C=g.previousStats??[],V=[],R=new Map(C.map(($)=>[M($),$])),L=A.filter(($)=>($.type==="inbound-rtp"||$.type==="outbound-rtp")&&S($,"kind")!=="video"&&S($,"mediaType")!=="video").map(($)=>{let X=$.type==="outbound-rtp"?"outbound":"inbound",G=X==="outbound"?"packetsSent":"packetsReceived",P=X==="outbound"?"bytesSent":"bytesReceived",h=R.get(M($)),O=w($,G),J=h?w(h,G):void 0,E=w($,P),N=h?w(h,P):void 0,_=$.timestamp!==void 0&&h?.timestamp!==void 0?$.timestamp-h.timestamp:void 0;return{bytesDelta:E!==void 0&&N!==void 0?E-N:void 0,currentPackets:O,direction:X,id:M($),packetDelta:O!==void 0&&J!==void 0?O-J:void 0,previousPackets:J,timeDeltaMs:_}}),H=L.filter(($)=>$.direction==="inbound"),c=L.filter(($)=>$.direction==="outbound"),W=b(L.map(($)=>$.timeDeltaMs).filter(($)=>$!==void 0)),I=H.filter(($)=>g.maxInboundPacketStallMs!==void 0&&$.timeDeltaMs!==void 0&&$.timeDeltaMs>=g.maxInboundPacketStallMs&&$.packetDelta!==void 0&&$.packetDelta<=0).length,y=c.filter(($)=>g.maxOutboundPacketStallMs!==void 0&&$.timeDeltaMs!==void 0&&$.timeDeltaMs>=g.maxOutboundPacketStallMs&&$.packetDelta!==void 0&&$.packetDelta<=0).length;if(g.requireInboundAudio&&H.length===0)Z(V,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(g.requireOutboundAudio&&c.length===0)Z(V,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(g.maxGapMs!==void 0&&W!==void 0&&W>g.maxGapMs)Z(V,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(W)}ms above ${String(g.maxGapMs)}ms.`);if(I>0)Z(V,"error","media.webrtc_inbound_stalled",`${String(I)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(y>0)Z(V,"error","media.webrtc_outbound_stalled",`${String(y)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:H.length,issues:V,maxObservedGapMs:W,outboundAudioStreams:c.length,stalledInboundStreams:I,stalledOutboundStreams:y,status:V.some(($)=>$.severity==="error")?"fail":V.length>0?"warn":"pass",streams:L,totalStats:A.length}};var O0="/api/voice/browser-media",J0=5000,Y0=async(g)=>g.peerConnection??await g.getPeerConnection?.()??null,Z0=async(g,A)=>{let C=A.fetch??globalThis.fetch;if(!C)return;await C(A.path??O0,{body:JSON.stringify(g),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},n=(g)=>{let A=null,C=[],V=async()=>{let L=await Y0(g);if(!L)return;let H=await v({peerConnection:L}),c=d({...g,stats:H}),W=g.continuity===!1?void 0:i({...g.continuity,previousStats:C,stats:H}),I={at:Date.now(),continuity:W,report:c,scenarioId:g.getScenarioId?.()??null,sessionId:g.getSessionId?.()??null};return C=H,g.onReport?.(I),await Z0(I,g),I},R=()=>{V().catch((L)=>{g.onError?.(L)})},T=()=>{if(A)clearInterval(A),A=null};return{close:T,reportOnce:V,stop:T,start:()=>{if(A)return;R(),A=setInterval(R,g.intervalMs??J0)}}};var Q0=(g,A,C)=>Math.min(C,A*2**(Math.max(1,g)-1));var q=()=>{},P0=()=>q,K0={callControl:q,close:q,endTurn:q,send:q,sendAudio:q,simulateDisconnect:q,subscribe:P0,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",start:()=>{}},q0=()=>crypto.randomUUID(),E0=(g,A,C)=>{let{hostname:V,port:R,protocol:T}=window.location,L=T==="https:"?"wss:":"ws:",H=R?`:${R}`:"",c=new URL(`${L}//${V}${H}${g}`);if(c.searchParams.set("sessionId",A),C)c.searchParams.set("scenarioId",C);return c.toString()},N0=(g)=>{if(!g||typeof g!=="object"||!("type"in g))return!1;switch(g.type){case"audio":case"assistant":case"call_lifecycle":case"complete":case"connection":case"error":case"final":case"partial":case"pong":case"replay":case"session":case"turn":return!0;default:return!1}},z0=(g)=>{if(typeof g.data!=="string")return null;try{let A=JSON.parse(g.data);return N0(A)?A:null}catch{return null}},o=(g,A={})=>{if(typeof window>"u")return K0;let C=new Set,V=A.reconnect!==!1,R=A.maxReconnectAttempts??15,T=A.reconnectMaxDelayMs??8000,L=A.pingInterval??30000,H=(U)=>Q0(U,500,T),c={isConnected:!1,pendingMessages:[],scenarioId:A.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:A.sessionId??q0(),ws:null},W=(U)=>{C.forEach((Y)=>Y(U))},I=()=>{if(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 U=c.pendingMessages.shift();if(U!==void 0)c.ws.send(U)}},$=()=>{c.reconnectAttempts+=1;let U=H(c.reconnectAttempts),Y=Date.now()+U;W({reconnect:{attempts:c.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:R,nextAttemptAt:Y,status:"reconnecting"},type:"connection"}),c.reconnectTimeout=setTimeout(()=>{if(c.reconnectAttempts>R){W({reconnect:{attempts:c.reconnectAttempts,maxAttempts:R,status:"exhausted"},type:"connection"});return}X()},U)},X=()=>{let U=new WebSocket(E0(g,c.sessionId,c.scenarioId));U.binaryType="arraybuffer",U.onopen=()=>{let Y=c.reconnectAttempts>0;if(c.isConnected=!0,y(),Y)W({reconnect:{attempts:c.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:R,status:"resumed"},type:"connection"}),c.reconnectAttempts=0;C.forEach((K)=>K({scenarioId:c.scenarioId??void 0,sessionId:c.sessionId,status:"active",type:"session"})),c.pingInterval=setInterval(()=>{if(U.readyState===1)U.send(JSON.stringify({type:"ping"}))},L)},U.onmessage=(Y)=>{let K=z0(Y);if(!K)return;if(K.type==="session")c.sessionId=K.sessionId,c.scenarioId=K.scenarioId??c.scenarioId;C.forEach((T0)=>T0(K))},U.onclose=(Y)=>{if(c.isConnected=!1,I(),V&&Y.code!==1000&&c.reconnectAttempts<R)$();else if(V&&Y.code!==1000)W({reconnect:{attempts:c.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:R,status:"exhausted"},type:"connection"})},c.ws=U},G=(U)=>{if(c.ws?.readyState===1){c.ws.send(U);return}c.pendingMessages.push(U)},P=(U)=>{G(JSON.stringify(U))},h=(U={})=>{if(U.sessionId)c.sessionId=U.sessionId;if(U.scenarioId)c.scenarioId=U.scenarioId;P({scenarioId:c.scenarioId??void 0,sessionId:c.sessionId,type:"start"})},O=(U)=>{G(U)},J=()=>{P({type:"end_turn"})},E=(U)=>{P({...U,type:"call_control"})},N=()=>{if(I(),c.ws)c.ws.close(1000),c.ws=null;c.isConnected=!1,C.clear()},_=()=>{if(c.ws?.readyState===1)c.ws.close(4000,"absolutejs-voice-reconnect-proof")},D=(U)=>{return C.add(U),()=>{C.delete(U)}};return X(),{callControl:E,close:N,endTurn:J,send:P,sendAudio:O,simulateDisconnect:_,start:h,subscribe:D,getReadyState:()=>c.ws?.readyState??3,getScenarioId:()=>c.scenarioId??"",getSessionId:()=>c.sessionId}};var b0=()=>({attempts:0,maxAttempts:0,status:"idle"}),j0=(g,A)=>{let C=A.trim().replace(/\s+/g," ");if(!C)return g;if(!g)return C;if(g===C||g.endsWith(C))return g;if(C.includes(g))return C;return`${g} ${C}`},B0=(g,A)=>{let C=A.trim().replace(/\s+/g," ");if(!g)return C;if(!C||g.endsWith(C))return g;return`${g} ${C}`},M0=()=>({assistantAudio:[],assistantStreamingText:"",assistantTexts:[],call:null,error:null,isConnected:!1,partial:"",reconnect:b0(),scenarioId:null,sessionId:null,sessionMetadata:null,status:"idle",turns:[]}),m=()=>{let g=M0(),A="",C=new Set,V=()=>{C.forEach((T)=>T())};return{dispatch:(T)=>{switch(T.type){case"audio":g={...g,assistantAudio:[...g.assistantAudio,{chunk:T.chunk,format:T.format,receivedAt:T.receivedAt,turnId:T.turnId}]};break;case"assistant":g={...g,assistantStreamingText:"",assistantTexts:[...g.assistantTexts,T.text]};break;case"assistant_delta":g={...g,assistantStreamingText:`${g.assistantStreamingText}${T.delta}`};break;case"complete":g={...g,sessionId:T.sessionId,status:"completed"};break;case"call_lifecycle":g={...g,call:{...g.call,disposition:T.event.type==="end"?T.event.disposition:g.call?.disposition,endedAt:T.event.type==="end"?T.event.at:g.call?.endedAt,events:[...g.call?.events??[],T.event],lastEventAt:T.event.at,startedAt:g.call?.startedAt??T.event.at},sessionId:T.sessionId};break;case"connected":g={...g,isConnected:!0,reconnect:g.reconnect.status==="reconnecting"?{...g.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:g.reconnect};break;case"connection":g={...g,reconnect:T.reconnect};break;case"disconnected":g={...g,isConnected:!1};break;case"error":g={...g,error:T.message};break;case"final":A=j0(A,T.transcript.text),g={...g,partial:A};break;case"partial":g={...g,partial:B0(A,T.transcript.text)};break;case"replay":A=T.partial,g={...g,assistantStreamingText:"",assistantTexts:[...T.assistantTexts],call:T.call??null,error:null,isConnected:T.status==="active",partial:T.partial,reconnect:g.reconnect.status==="reconnecting"?{...g.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:g.reconnect,scenarioId:T.scenarioId??g.scenarioId,sessionId:T.sessionId,sessionMetadata:T.sessionMetadata??g.sessionMetadata,status:T.status,turns:[...T.turns]};break;case"session":g={...g,error:null,scenarioId:T.scenarioId??g.scenarioId,isConnected:T.status==="active",sessionId:T.sessionId,sessionMetadata:T.sessionMetadata??g.sessionMetadata,status:T.status};break;case"turn":A="",g={...g,partial:"",turns:[...g.turns,T.turn]};break}V()},getServerSnapshot:()=>g,getSnapshot:()=>g,subscribe:(T)=>{return C.add(T),()=>{C.delete(T)}}}};var r=(g,A={})=>{let C=o(g,A),V=m(),R=A.browserMedia&&typeof window<"u"?n({...A.browserMedia,getScenarioId:()=>A.browserMedia?A.browserMedia.getScenarioId?.()??C.getScenarioId():C.getScenarioId(),getSessionId:()=>A.browserMedia?A.browserMedia.getSessionId?.()??C.getSessionId():C.getSessionId()}):null,T=new Set,L=(I)=>Promise.resolve().then(()=>{if(!I?.sessionId&&!I?.scenarioId)return;C.start(I),R?.start()}),H=()=>{T.forEach((I)=>I())},c=()=>{if(!A.reconnectReportPath||typeof fetch>"u")return;let I=V.getSnapshot(),y=JSON.stringify({at:Date.now(),reconnect:I.reconnect,scenarioId:I.scenarioId,sessionId:C.getSessionId(),turnIds:I.turns.map(($)=>$.id)});fetch(A.reconnectReportPath,{body:y,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},W=C.subscribe((I)=>{let y=f(I);if(y){if(V.dispatch(y),I.type==="connection")c();H()}});return{start:L,get assistantAudio(){return V.getSnapshot().assistantAudio},get assistantTexts(){return V.getSnapshot().assistantTexts},get assistantStreamingText(){return V.getSnapshot().assistantStreamingText},get call(){return V.getSnapshot().call},callControl(I){C.callControl(I)},close(){W(),R?.close(),C.close(),V.dispatch({type:"disconnected"}),H()},endTurn(){C.endTurn()},get error(){return V.getSnapshot().error},getServerSnapshot(){return V.getServerSnapshot()},getSnapshot(){return V.getSnapshot()},get isConnected(){return V.getSnapshot().isConnected},get partial(){return V.getSnapshot().partial},get reconnect(){return V.getSnapshot().reconnect},get scenarioId(){return V.getSnapshot().scenarioId},sendAudio(I){C.sendAudio(I)},get sessionId(){return C.getSessionId()},get sessionMetadata(){return V.getSnapshot().sessionMetadata},simulateDisconnect(){C.simulateDisconnect()},get status(){return V.getSnapshot().status},subscribe(I){return T.add(I),()=>{T.delete(I)}},get turns(){return V.getSnapshot().turns}}};var u=(g)=>{if(!g||g.enabled===!1)return;return{enabled:!0,maxGain:g.maxGain??3,noiseGateAttenuation:g.noiseGateAttenuation??0.15,noiseGateThreshold:g.noiseGateThreshold??0.006,targetLevel:g.targetLevel??0.08}};var x0={balanced:{qualityProfile:"general",minSilenceMs:400,silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:"general",minSilenceMs:300,silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},"long-form":{qualityProfile:"general",minSilenceMs:600,silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},k0={"accent-heavy":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},general:{},"noisy-room":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},"short-command":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var s=(g)=>{let A=g?.profile??"fast",C=g?.qualityProfile??"general",V=x0[A],R=k0[C],T=g?.silenceMs??R.silenceMs??V.silenceMs;return{profile:A,qualityProfile:C,minSilenceMs:Math.min(T,g?.minSilenceMs??R.minSilenceMs??V.minSilenceMs),silenceMs:T,speechThreshold:g?.speechThreshold??R.speechThreshold??V.speechThreshold,transcriptStabilityMs:g?.transcriptStabilityMs??R.transcriptStabilityMs??V.transcriptStabilityMs}};var F0={chat:{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"balanced",qualityProfile:"short-command"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"fast",qualityProfile:"general"}},dictation:{audioConditioning:{enabled:!0,maxGain:2.25,noiseGateAttenuation:0.05,noiseGateThreshold:0.003,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"guided-intake":{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"turn-scoped",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"noisy-room":{audioConditioning:{enabled:!0,maxGain:3,noiseGateAttenuation:0.12,noiseGateThreshold:0.006,targetLevel:0.085},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:2100,speechThreshold:0.02,transcriptStabilityMs:1650}},"pstn-balanced":{audioConditioning:{enabled:!0,maxGain:2.8,noiseGateAttenuation:0.07,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:660,speechThreshold:0.012,transcriptStabilityMs:300}},"pstn-fast":{audioConditioning:{enabled:!0,maxGain:2.75,noiseGateAttenuation:0.06,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:620,speechThreshold:0.012,transcriptStabilityMs:280}},reliability:{audioConditioning:{enabled:!0,maxGain:2.9,noiseGateAttenuation:0.08,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room"}}},a=(g="default")=>{let A=F0[g];return{audioConditioning:u(A.audioConditioning),capture:{channelCount:A.capture?.channelCount??1,sampleRateHz:A.capture?.sampleRateHz??16000},connection:{...A.connection},name:g,sttLifecycle:A.sttLifecycle??"continuous",turnDetection:s(A.turnDetection)}};var f0=(g)=>({assistantAudio:[...g.assistantAudio],assistantStreamingText:g.assistantStreamingText,assistantTexts:[...g.assistantTexts],call:g.call,error:g.error,isConnected:g.isConnected,isRecording:!1,partial:g.partial,reconnect:g.reconnect,recordingError:null,sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,scenarioId:g.scenarioId,status:g.status,turns:[...g.turns]}),p=(g,A={})=>{let C=a(A.preset),V=r(g,{...C.connection,...A.connection}),R=null,T=f0(V),L=new Set,H=()=>{for(let G of L)G()},c=()=>{if(T={...T,assistantAudio:[...V.assistantAudio],assistantStreamingText:V.assistantStreamingText,assistantTexts:[...V.assistantTexts],call:V.call,error:V.error,isConnected:V.isConnected,partial:V.partial,reconnect:V.reconnect,sessionId:V.sessionId,sessionMetadata:V.sessionMetadata,scenarioId:V.scenarioId,status:V.status,turns:[...V.turns]},A.autoStopOnComplete!==!1&&T.status==="completed"&&T.isRecording)R?.stop(),R=null,T={...T,isRecording:!1};H()},W=V.subscribe(c);c();let I=()=>{if(R)return R;return R=F({channelCount:A.capture?.channelCount??C.capture.channelCount,onLevel:A.capture?.onLevel,onAudio:(G)=>{if(A.capture?.onAudio){A.capture.onAudio(G,V.sendAudio);return}V.sendAudio(G)},sampleRateHz:A.capture?.sampleRateHz??C.capture.sampleRateHz,...A.capture?.stream?{stream:A.capture.stream}:{}}),R},y=()=>{R?.stop(),R=null,T={...T,isRecording:!1},H()},$=async()=>{if(T.isRecording)return;try{T={...T,recordingError:null},H(),await I().start(),T={...T,isRecording:!0},H()}catch(G){throw R=null,T={...T,isRecording:!1,recordingError:G instanceof Error?G.message:String(G)},H(),G}};return{close:()=>{W(),y(),V.close()},startRecording:$,stopRecording:y,get assistantAudio(){return T.assistantAudio},get assistantTexts(){return T.assistantTexts},get assistantStreamingText(){return T.assistantStreamingText},bindHTMX(G){return k(V,G)},get call(){return T.call},callControl:(G)=>V.callControl(G),endTurn:()=>V.endTurn(),get error(){return T.error},getServerSnapshot:()=>T,getSnapshot:()=>T,get isConnected(){return T.isConnected},get isRecording(){return T.isRecording},get partial(){return T.partial},get reconnect(){return T.reconnect},get recordingError(){return T.recordingError},get scenarioId(){return T.scenarioId},sendAudio:(G)=>V.sendAudio(G),get sessionId(){return T.sessionId},get sessionMetadata(){return T.sessionMetadata},simulateDisconnect:()=>V.simulateDisconnect(),get status(){return T.status},subscribe:(G)=>{return L.add(G),()=>{L.delete(G)}},toggleRecording:async()=>{if(T.isRecording){y();return}await $()},get turns(){return T.turns}}};var Q=(g)=>String(g).replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'");var t=(g)=>{if(!g.isConnected)return"idle";if(g.isPlaying)return"speaking";if(g.isRecording&&g.hasActivePartial)return"listening";if(g.isRecording)return"listening";if(g.lastTranscriptAt&&!g.lastAssistantAt)return"thinking";if(g.lastTranscriptAt&&g.lastAssistantAt&&g.lastTranscriptAt>g.lastAssistantAt)return"thinking";return"idle"};var l0={accent:"#3b82f6",background:"#0f172a",errorAccent:"#ef4444",fontFamily:'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',foreground:"#f8fafc",radius:16},d0={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},v0=(g,A)=>{switch(g){case"listening":return A.listening;case"speaking":return A.speaking;case"thinking":return A.thinking;case"idle":return A.idle}},e=(g)=>{let A={...l0,...g.theme},C={...d0,...g.labels},V=g.state.assistantAudio.at(-1)?.receivedAt,R=g.state.turns.at(-1)?.committedAt,T=t({hasActivePartial:g.state.partial.length>0,isConnected:g.state.isConnected,isPlaying:!1,isRecording:g.state.isRecording,lastAssistantAt:V,lastTranscriptAt:R}),L=!g.state.isConnected&&g.state.status!=="idle"&&!g.state.error,H=g.state.error?"Error":L?C.connecting:g.state.status==="completed"?C.callEnded:v0(T,C);return{agentState:T,classes:{container:`absolute-voice-widget absolute-voice-widget--${T}`,dot:`absolute-voice-widget__dot${g.state.error?" absolute-voice-widget__dot--error":""}`},controls:{canEnd:g.state.isConnected,canMute:g.state.isRecording,canStart:!g.state.isRecording&&g.state.status!=="completed"},errorMessage:g.state.error??void 0,labels:C,partial:g.state.partial||void 0,statusLabel:H,theme:A,title:g.title??"Voice"}},i0=(g)=>typeof g==="number"?`${g}px`:g,g0=(g)=>{let A=g.theme,C=`background:${A.background};border-radius:${i0(A.radius)};color:${A.foreground};font-family:${A.fontFamily};min-width:240px;padding:20px 22px;`,V=`background:${g.errorMessage?A.errorAccent:g.agentState==="idle"?"rgba(148,163,184,0.6)":A.accent};border-radius:50%;height:10px;width:10px;`,R=[];if(g.controls.canStart)R.push(`<button type="button" data-action="start" style="background:${A.accent};border:none;border-radius:12px;color:${A.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${Q(g.labels.startCall)}</button>`);if(g.controls.canMute)R.push(`<button type="button" data-action="mute" style="background:transparent;border:1px solid rgba(255,255,255,0.18);border-radius:12px;color:${A.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${Q(g.labels.mute)}</button>`);if(g.controls.canEnd)R.push(`<button type="button" data-action="end" style="background:${A.errorAccent};border:none;border-radius:12px;color:${A.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${Q(g.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${g.agentState}" class="${Q(g.classes.container)}" style="${C}">
|
|
1
|
+
(()=>{var{defineProperty:B,getOwnPropertyNames:R0,getOwnPropertyDescriptor:_0}=Object,$0=Object.prototype.hasOwnProperty;function I0(g){return this[g]}var H0=(g)=>{var A=(k??=new WeakMap).get(g),C;if(A)return A;if(A=B({},"__esModule",{value:!0}),g&&typeof g==="object"||typeof g==="function"){for(var V of R0(g))if(!$0.call(A,V))B(A,V,{get:I0.bind(g,V),enumerable:!(C=_0(g,V))||C.enumerable})}return k.set(g,A),A},k;var U0=(g)=>g;function L0(g,A){this[g]=U0.bind(null,A)}var G0=(g,A)=>{for(var C in A)B(g,C,{get:A[C],enumerable:!0,configurable:!0,set:L0.bind(A,C)})};var m0={};G0(m0,{mount:()=>V0,default:()=>r0,VOICE_EMBED_VERSION:()=>C0});var W0=(g)=>{if(typeof g!=="string")return g;return document.querySelector(g)},w0=(g,A,C,V)=>{let R=A??g.getAttribute("hx-get")??"";if(!R)return"";let c=new URL(R,window.location.origin);if(V)c.searchParams.set(C,V);else c.searchParams.delete(C);return`${c.pathname}${c.search}${c.hash}`},F=(g,A)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let C=W0(A.element);if(!C)return()=>{};let V=A.eventName??"voice-refresh",R=A.sessionQueryParam??"sessionId",c=()=>{let H=window,T=w0(C,A.route,R,g.sessionId);if(T)C.setAttribute("hx-get",T);H.htmx?.process?.(C),H.htmx?.trigger?.(C,V)},U=g.subscribe(c);return c(),()=>{U()}};var y0=(g)=>Math.max(-1,Math.min(1,g)),D0=(g)=>{let A=new Int16Array(g.length);for(let C=0;C<g.length;C+=1){let V=y0(g[C]??0);A[C]=V<0?V*32768:V*32767}return new Uint8Array(A.buffer)},S0=(g)=>{let A=g instanceof Uint8Array?g:new Uint8Array(g);if(A.byteLength<2)return 0;let C=new Int16Array(A.buffer,A.byteOffset,Math.floor(A.byteLength/2));if(C.length===0)return 0;let V=0;for(let R of C){let c=R/32768;V+=c*c}return Math.min(1,Math.max(0,Math.sqrt(V/C.length)*5.5))},X0=(g,A,C)=>{if(A===C)return g;let V=A/C,R=Math.round(g.length/V),c=new Float32Array(R),U=0,H=0;while(U<c.length){let T=Math.round((U+1)*V),G=0,$=0;for(let w=H;w<T&&w<g.length;w+=1)G+=g[w]??0,$+=1;c[U]=$>0?G/$:0,U+=1,H=T}return c},f=(g)=>{let A=null,C=null,V=null,R=null,c=async()=>{if(!g.stream&&(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia))throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let H=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!H)throw Error("Browser microphone capture requires AudioContext support.");try{if(R=g.stream??await navigator.mediaDevices.getUserMedia({audio:{autoGainControl:!0,channelCount:g.channelCount??1,echoCancellation:!0,noiseSuppression:!0}}),A=new H,A.state==="suspended")await A.resume();C=A.createMediaStreamSource(R),V=A.createScriptProcessor(4096,1,1),V.onaudioprocess=(T)=>{let G=T.inputBuffer.getChannelData(0),$=X0(G,A?.sampleRate??48000,g.sampleRateHz??16000),w=D0($);g.onLevel?.(S0(w)),g.onAudio(w)},C.connect(V),V.connect(A.destination)}catch(T){throw U(),T}},U=()=>{V?.disconnect(),C?.disconnect(),R?.getTracks().forEach((H)=>H.stop()),A?.close(),g.onLevel?.(0),A=null,R=null,V=null,C=null};return{start:c,stop:U}};var M=(g)=>{if(typeof g==="string"&&g.trim())return g;if(g instanceof Error&&g.message.trim())return g.message;if(g&&typeof g==="object"){let A=g;for(let C of["message","reason","description"]){let V=A[C];if(typeof V==="string"&&V.trim())return V}if("error"in A)return M(A.error);if("cause"in A)return M(A.cause);try{return JSON.stringify(g)}catch{}}return"Unexpected error"},d=(g)=>{switch(g.type){case"audio":return{chunk:Uint8Array.from(atob(g.chunkBase64),(A)=>A.charCodeAt(0)),format:g.format,receivedAt:g.receivedAt,turnId:g.turnId,type:"audio"};case"assistant":return{text:g.text,turnId:g.turnId,type:"assistant"};case"assistant_delta":return{delta:g.delta,turnId:g.turnId,type:"assistant_delta"};case"complete":return{sessionId:g.sessionId,type:"complete"};case"connection":return{reconnect:g.reconnect,type:"connection"};case"call_lifecycle":return{event:g.event,sessionId:g.sessionId,type:"call_lifecycle"};case"error":return{message:M(g.message),type:"error"};case"final":return{transcript:g.transcript,type:"final"};case"partial":return{transcript:g.transcript,type:"partial"};case"replay":return{assistantTexts:g.assistantTexts,call:g.call,partial:g.partial,scenarioId:g.scenarioId,sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,status:g.status,turns:g.turns,type:"replay"};case"session":return{sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,scenarioId:g.scenarioId,status:g.status,type:"session"};case"turn":return{turn:g.turn,type:"turn"};default:return null}};var t0=Math.PI*2;var Q=(g,A,C,V)=>{g.push({code:C,message:V,severity:A})};var h0=(g)=>g.length===0?void 0:g.reduce((A,C)=>A+C,0)/g.length,j=(g)=>g.length===0?void 0:Math.max(...g);var y=(g,A)=>{let C=g[A];return typeof C==="number"&&Number.isFinite(C)?C:void 0},b=(g,A)=>{let C=g[A];return typeof C==="boolean"?C:void 0},S=(g,A)=>{let C=g[A];return typeof C==="string"?C:void 0},x=(g)=>String(g.id??S(g,"ssrc")??y(g,"ssrc")??S(g,"trackIdentifier")??S(g,"mid")??"unknown"),l=(g)=>g===void 0?void 0:g*1000;var O0=(g)=>{let A={};for(let[C,V]of Object.entries(g))if(V===null||typeof V==="boolean"||typeof V==="number"||typeof V==="string")A[C]=V;return A};var v=(g={})=>{let A=g.stats??[],C=[],V=A.filter((_)=>_.type==="inbound-rtp"&&S(_,"kind")!=="video"),R=A.filter((_)=>_.type==="outbound-rtp"&&S(_,"kind")!=="video"),c=A.filter((_)=>_.type==="candidate-pair"),U=A.filter((_)=>(_.type==="track"||_.type==="media-source")&&S(_,"kind")==="audio"),H=c.filter((_)=>b(_,"selected")===!0||b(_,"nominated")===!0||S(_,"state")==="succeeded").length,T=U.filter((_)=>S(_,"readyState")!=="ended"&&S(_,"trackState")!=="ended"&&b(_,"ended")!==!0).length,G=U.filter((_)=>S(_,"readyState")==="ended"||S(_,"trackState")==="ended"||b(_,"ended")===!0).length,$=V.reduce((_,D)=>_+(y(D,"packetsReceived")??0),0),w=R.reduce((_,D)=>_+(y(D,"packetsSent")??0),0),I=[...V,...R].reduce((_,D)=>_+Math.max(0,y(D,"packetsLost")??0),0),X=$+I,h=X===0?0:I/X,W=V.reduce((_,D)=>_+(y(D,"bytesReceived")??0),0),O=R.reduce((_,D)=>_+(y(D,"bytesSent")??0),0),J=j(c.map((_)=>l(y(_,"currentRoundTripTime")??y(_,"roundTripTime"))).filter((_)=>_!==void 0)),Y=j([...V,...R].map((_)=>l(y(_,"jitter"))).filter((_)=>_!==void 0)),N=j(V.map((_)=>{let D=y(_,"jitterBufferDelay"),z=y(_,"jitterBufferEmittedCount");return D!==void 0&&z!==void 0&&z>0?D/z*1000:void 0}).filter((_)=>_!==void 0)),q=U.map((_)=>y(_,"audioLevel")).filter((_)=>_!==void 0);if(g.requireConnectedCandidatePair&&c.length>0&&H===0)Q(C,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(g.requireLiveAudioTrack&&T===0)Q(C,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(g.maxPacketLossRatio!==void 0&&h>g.maxPacketLossRatio)Q(C,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(h)} above ${String(g.maxPacketLossRatio)}.`);if(g.maxRoundTripTimeMs!==void 0&&J!==void 0&&J>g.maxRoundTripTimeMs)Q(C,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(J)}ms above ${String(g.maxRoundTripTimeMs)}ms.`);if(g.maxJitterMs!==void 0&&Y!==void 0&&Y>g.maxJitterMs)Q(C,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(Y)}ms above ${String(g.maxJitterMs)}ms.`);return{activeCandidatePairs:H,audioLevelAverage:h0(q),bytesReceived:W,bytesSent:O,checkedAt:Date.now(),endedAudioTracks:G,inboundPackets:$,issues:C,jitterBufferDelayMs:N,jitterMs:Y,liveAudioTracks:T,outboundPackets:w,packetLossRatio:h,packetsLost:I,roundTripTimeMs:J,status:C.some((_)=>_.severity==="error")?"fail":C.length>0?"warn":"pass",totalStats:A.length}},i=async(g)=>{return[...(await g.peerConnection.getStats(g.selector??null)).values()].map(O0)};var n=(g={})=>{let A=g.stats??[],C=g.previousStats??[],V=[],R=new Map(C.map((I)=>[x(I),I])),U=A.filter((I)=>(I.type==="inbound-rtp"||I.type==="outbound-rtp")&&S(I,"kind")!=="video"&&S(I,"mediaType")!=="video").map((I)=>{let X=I.type==="outbound-rtp"?"outbound":"inbound",h=X==="outbound"?"packetsSent":"packetsReceived",W=X==="outbound"?"bytesSent":"bytesReceived",O=R.get(x(I)),J=y(I,h),Y=O?y(O,h):void 0,N=y(I,W),q=O?y(O,W):void 0,_=I.timestamp!==void 0&&O?.timestamp!==void 0?I.timestamp-O.timestamp:void 0;return{bytesDelta:N!==void 0&&q!==void 0?N-q:void 0,currentPackets:J,direction:X,id:x(I),packetDelta:J!==void 0&&Y!==void 0?J-Y:void 0,previousPackets:Y,timeDeltaMs:_}}),H=U.filter((I)=>I.direction==="inbound"),T=U.filter((I)=>I.direction==="outbound"),G=j(U.map((I)=>I.timeDeltaMs).filter((I)=>I!==void 0)),$=H.filter((I)=>g.maxInboundPacketStallMs!==void 0&&I.timeDeltaMs!==void 0&&I.timeDeltaMs>=g.maxInboundPacketStallMs&&I.packetDelta!==void 0&&I.packetDelta<=0).length,w=T.filter((I)=>g.maxOutboundPacketStallMs!==void 0&&I.timeDeltaMs!==void 0&&I.timeDeltaMs>=g.maxOutboundPacketStallMs&&I.packetDelta!==void 0&&I.packetDelta<=0).length;if(g.requireInboundAudio&&H.length===0)Q(V,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(g.requireOutboundAudio&&T.length===0)Q(V,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(g.maxGapMs!==void 0&&G!==void 0&&G>g.maxGapMs)Q(V,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(G)}ms above ${String(g.maxGapMs)}ms.`);if($>0)Q(V,"error","media.webrtc_inbound_stalled",`${String($)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(w>0)Q(V,"error","media.webrtc_outbound_stalled",`${String(w)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:H.length,issues:V,maxObservedGapMs:G,outboundAudioStreams:T.length,stalledInboundStreams:$,stalledOutboundStreams:w,status:V.some((I)=>I.severity==="error")?"fail":V.length>0?"warn":"pass",streams:U,totalStats:A.length}};var J0="/api/voice/browser-media",Y0=5000,Z0=async(g)=>g.peerConnection??await g.getPeerConnection?.()??null,Q0=async(g,A)=>{let C=A.fetch??globalThis.fetch;if(!C)return;await C(A.path??J0,{body:JSON.stringify(g),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},o=(g)=>{let A=null,C=[],V=async()=>{let U=await Z0(g);if(!U)return;let H=await i({peerConnection:U}),T=v({...g,stats:H}),G=g.continuity===!1?void 0:n({...g.continuity,previousStats:C,stats:H}),$={at:Date.now(),continuity:G,report:T,scenarioId:g.getScenarioId?.()??null,sessionId:g.getSessionId?.()??null};return C=H,g.onReport?.($),await Q0($,g),$},R=()=>{V().catch((U)=>{g.onError?.(U)})},c=()=>{if(A)clearInterval(A),A=null};return{close:c,reportOnce:V,stop:c,start:()=>{if(A)return;R(),A=setInterval(R,g.intervalMs??Y0)}}};var P0=(g,A,C)=>Math.min(C,A*2**(Math.max(1,g)-1));var E=()=>{},K0=()=>E,E0={callControl:E,close:E,disconnect:E,endTurn:E,send:E,sendAudio:E,simulateDisconnect:E,subscribe:K0,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",start:()=>{}},N0=()=>crypto.randomUUID(),q0=(g,A,C,V)=>{let{hostname:R,port:c,protocol:U}=window.location,H=U==="https:"?"wss:":"ws:",T=c?`:${c}`:"",G=new URL(`${H}//${R}${T}${g}`);if(G.searchParams.set("sessionId",A),C)G.searchParams.set("scenarioId",C);for(let[$,w]of Object.entries(V))if($!=="sessionId"&&$!=="scenarioId"&&w)G.searchParams.set($,w);return G.toString()},z0=(g)=>{if(!g||typeof g!=="object"||!("type"in g))return!1;switch(g.type){case"audio":case"assistant":case"call_lifecycle":case"complete":case"connection":case"error":case"final":case"partial":case"pong":case"replay":case"session":case"turn":return!0;default:return!1}},b0=(g)=>{if(typeof g.data!=="string")return null;try{let A=JSON.parse(g.data);return z0(A)?A:null}catch{return null}},m=(g,A={})=>{if(typeof window>"u")return E0;let C=new Set,V=A.reconnect!==!1,R=A.maxReconnectAttempts??15,c=A.reconnectMaxDelayMs??8000,U=A.pingInterval??30000,H=(L)=>P0(L,500,c),T={isConnected:!1,pendingMessages:[],scenarioId:A.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:A.sessionId??N0(),ws:null},G=(L)=>{C.forEach((Z)=>Z(L))},$=()=>{if(T.pingInterval)clearInterval(T.pingInterval),T.pingInterval=null;if(T.reconnectTimeout)clearTimeout(T.reconnectTimeout),T.reconnectTimeout=null},w=()=>{if(T.ws?.readyState!==1)return;while(T.pendingMessages.length>0){let L=T.pendingMessages.shift();if(L!==void 0)T.ws.send(L)}},I=()=>{T.reconnectAttempts+=1;let L=H(T.reconnectAttempts),Z=Date.now()+L;G({reconnect:{attempts:T.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:R,nextAttemptAt:Z,status:"reconnecting"},type:"connection"}),T.reconnectTimeout=setTimeout(()=>{if(T.reconnectAttempts>R){G({reconnect:{attempts:T.reconnectAttempts,maxAttempts:R,status:"exhausted"},type:"connection"});return}X()},L)},X=()=>{let L=new WebSocket(q0(g,T.sessionId,T.scenarioId,A.query??{}));L.binaryType="arraybuffer",L.onopen=()=>{let Z=T.reconnectAttempts>0;if(T.isConnected=!0,w(),Z)G({reconnect:{attempts:T.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:R,status:"resumed"},type:"connection"}),T.reconnectAttempts=0;C.forEach((K)=>K({scenarioId:T.scenarioId??void 0,sessionId:T.sessionId,status:"active",type:"session"})),T.pingInterval=setInterval(()=>{if(L.readyState===1)L.send(JSON.stringify({type:"ping"}))},U)},L.onmessage=(Z)=>{let K=b0(Z);if(!K)return;if(K.type==="session")T.sessionId=K.sessionId,T.scenarioId=K.scenarioId??T.scenarioId;C.forEach((T0)=>T0(K))},L.onclose=(Z)=>{if(T.isConnected=!1,$(),V&&Z.code!==1000&&T.reconnectAttempts<R)I();else if(V&&Z.code!==1000)G({reconnect:{attempts:T.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:R,status:"exhausted"},type:"connection"})},T.ws=L},h=(L)=>{if(T.ws?.readyState===1){T.ws.send(L);return}T.pendingMessages.push(L)},W=(L)=>{h(JSON.stringify(L))},O=(L={})=>{if(L.sessionId)T.sessionId=L.sessionId;if(L.scenarioId)T.scenarioId=L.scenarioId;W({scenarioId:T.scenarioId??void 0,sessionId:T.sessionId,type:"start"})},J=(L)=>{h(L)},Y=()=>{W({type:"end_turn"})},N=(L)=>{W({...L,type:"call_control"})},q=(L="client-close")=>{if($(),T.ws){if(T.ws.readyState===1)T.ws.send(JSON.stringify({reason:L,type:"close"}));T.ws.close(1000),T.ws=null}T.isConnected=!1,C.clear()},_=()=>{if($(),T.ws)T.ws.close(1000),T.ws=null;T.isConnected=!1,C.clear()},D=()=>{if(T.ws?.readyState===1)T.ws.close(4000,"absolutejs-voice-reconnect-proof")},z=(L)=>{return C.add(L),()=>{C.delete(L)}};return X(),{callControl:N,close:q,disconnect:_,endTurn:Y,send:W,sendAudio:J,simulateDisconnect:D,start:O,subscribe:z,getReadyState:()=>T.ws?.readyState??3,getScenarioId:()=>T.scenarioId??"",getSessionId:()=>T.sessionId}};var j0=()=>({attempts:0,maxAttempts:0,status:"idle"}),B0=(g,A)=>{let C=A.trim().replace(/\s+/g," ");if(!C)return g;if(!g)return C;if(g===C||g.endsWith(C))return g;if(C.includes(g))return C;return`${g} ${C}`},M0=(g,A)=>{let C=A.trim().replace(/\s+/g," ");if(!g)return C;if(!C||g.endsWith(C))return g;return`${g} ${C}`},x0=()=>({assistantAudio:[],assistantStreamingText:"",assistantTexts:[],call:null,error:null,isConnected:!1,partial:"",reconnect:j0(),scenarioId:null,sessionId:null,sessionMetadata:null,status:"idle",turns:[]}),r=()=>{let g=x0(),A="",C=new Set,V=()=>{C.forEach((c)=>c())};return{dispatch:(c)=>{switch(c.type){case"audio":g={...g,assistantAudio:[...g.assistantAudio,{chunk:c.chunk,format:c.format,receivedAt:c.receivedAt,turnId:c.turnId}]};break;case"assistant":g={...g,assistantStreamingText:"",assistantTexts:[...g.assistantTexts,c.text]};break;case"assistant_delta":g={...g,assistantStreamingText:`${g.assistantStreamingText}${c.delta}`};break;case"complete":g={...g,sessionId:c.sessionId,status:"completed"};break;case"call_lifecycle":g={...g,call:{...g.call,disposition:c.event.type==="end"?c.event.disposition:g.call?.disposition,endedAt:c.event.type==="end"?c.event.at:g.call?.endedAt,events:[...g.call?.events??[],c.event],lastEventAt:c.event.at,startedAt:g.call?.startedAt??c.event.at},sessionId:c.sessionId};break;case"connected":g={...g,isConnected:!0,reconnect:g.reconnect.status==="reconnecting"?{...g.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:g.reconnect};break;case"connection":g={...g,reconnect:c.reconnect};break;case"disconnected":g={...g,isConnected:!1};break;case"error":g={...g,error:c.message};break;case"final":A=B0(A,c.transcript.text),g={...g,partial:A};break;case"partial":g={...g,partial:M0(A,c.transcript.text)};break;case"replay":A=c.partial,g={...g,assistantStreamingText:"",assistantTexts:[...c.assistantTexts],call:c.call??null,error:null,isConnected:c.status==="active",partial:c.partial,reconnect:g.reconnect.status==="reconnecting"?{...g.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:g.reconnect,scenarioId:c.scenarioId??g.scenarioId,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata??g.sessionMetadata,status:c.status,turns:[...c.turns]};break;case"session":g={...g,error:null,scenarioId:c.scenarioId??g.scenarioId,isConnected:c.status==="active",sessionId:c.sessionId,sessionMetadata:c.sessionMetadata??g.sessionMetadata,status:c.status};break;case"turn":A="",g={...g,partial:"",turns:[...g.turns,c.turn]};break}V()},getServerSnapshot:()=>g,getSnapshot:()=>g,subscribe:(c)=>{return C.add(c),()=>{C.delete(c)}}}};var u=(g,A={})=>{let C=m(g,A),V=r(),R=A.browserMedia&&typeof window<"u"?o({...A.browserMedia,getScenarioId:()=>A.browserMedia?A.browserMedia.getScenarioId?.()??C.getScenarioId():C.getScenarioId(),getSessionId:()=>A.browserMedia?A.browserMedia.getSessionId?.()??C.getSessionId():C.getSessionId()}):null,c=new Set,U=($)=>Promise.resolve().then(()=>{if(!$?.sessionId&&!$?.scenarioId)return;C.start($),R?.start()}),H=()=>{c.forEach(($)=>$())},T=()=>{if(!A.reconnectReportPath||typeof fetch>"u")return;let $=V.getSnapshot(),w=JSON.stringify({at:Date.now(),reconnect:$.reconnect,scenarioId:$.scenarioId,sessionId:C.getSessionId(),turnIds:$.turns.map((I)=>I.id)});fetch(A.reconnectReportPath,{body:w,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},G=C.subscribe(($)=>{let w=d($);if(w){if(V.dispatch(w),$.type==="connection")T();H()}});return{start:U,get assistantAudio(){return V.getSnapshot().assistantAudio},get assistantTexts(){return V.getSnapshot().assistantTexts},get assistantStreamingText(){return V.getSnapshot().assistantStreamingText},get call(){return V.getSnapshot().call},callControl($){C.callControl($)},close($){G(),R?.close(),C.close($),V.dispatch({type:"disconnected"}),H()},disconnect(){G(),R?.close(),C.disconnect(),V.dispatch({type:"disconnected"}),H()},endTurn(){C.endTurn()},get error(){return V.getSnapshot().error},getServerSnapshot(){return V.getServerSnapshot()},getSnapshot(){return V.getSnapshot()},get isConnected(){return V.getSnapshot().isConnected},get partial(){return V.getSnapshot().partial},get reconnect(){return V.getSnapshot().reconnect},get scenarioId(){return V.getSnapshot().scenarioId},sendAudio($){C.sendAudio($)},get sessionId(){return C.getSessionId()},get sessionMetadata(){return V.getSnapshot().sessionMetadata},simulateDisconnect(){C.simulateDisconnect()},get status(){return V.getSnapshot().status},subscribe($){return c.add($),()=>{c.delete($)}},get turns(){return V.getSnapshot().turns}}};var s=(g)=>{if(!g||g.enabled===!1)return;return{enabled:!0,maxGain:g.maxGain??3,noiseGateAttenuation:g.noiseGateAttenuation??0.15,noiseGateThreshold:g.noiseGateThreshold??0.006,targetLevel:g.targetLevel??0.08}};var k0={balanced:{qualityProfile:"general",minSilenceMs:400,silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:"general",minSilenceMs:300,silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},"long-form":{qualityProfile:"general",minSilenceMs:600,silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},F0={"accent-heavy":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},general:{},"noisy-room":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},"short-command":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var a=(g)=>{let A=g?.profile??"fast",C=g?.qualityProfile??"general",V=k0[A],R=F0[C],c=g?.silenceMs??R.silenceMs??V.silenceMs;return{profile:A,qualityProfile:C,minSilenceMs:Math.min(c,g?.minSilenceMs??R.minSilenceMs??V.minSilenceMs),silenceMs:c,speechThreshold:g?.speechThreshold??R.speechThreshold??V.speechThreshold,transcriptStabilityMs:g?.transcriptStabilityMs??R.transcriptStabilityMs??V.transcriptStabilityMs}};var f0={chat:{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"balanced",qualityProfile:"short-command"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"fast",qualityProfile:"general"}},dictation:{audioConditioning:{enabled:!0,maxGain:2.25,noiseGateAttenuation:0.05,noiseGateThreshold:0.003,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"guided-intake":{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"turn-scoped",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"noisy-room":{audioConditioning:{enabled:!0,maxGain:3,noiseGateAttenuation:0.12,noiseGateThreshold:0.006,targetLevel:0.085},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:2100,speechThreshold:0.02,transcriptStabilityMs:1650}},"pstn-balanced":{audioConditioning:{enabled:!0,maxGain:2.8,noiseGateAttenuation:0.07,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:660,speechThreshold:0.012,transcriptStabilityMs:300}},"pstn-fast":{audioConditioning:{enabled:!0,maxGain:2.75,noiseGateAttenuation:0.06,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:620,speechThreshold:0.012,transcriptStabilityMs:280}},reliability:{audioConditioning:{enabled:!0,maxGain:2.9,noiseGateAttenuation:0.08,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room"}}},p=(g="default")=>{let A=f0[g];return{audioConditioning:s(A.audioConditioning),capture:{channelCount:A.capture?.channelCount??1,sampleRateHz:A.capture?.sampleRateHz??16000},connection:{...A.connection},name:g,sttLifecycle:A.sttLifecycle??"continuous",turnDetection:a(A.turnDetection)}};var d0=(g)=>({assistantAudio:[...g.assistantAudio],assistantStreamingText:g.assistantStreamingText,assistantTexts:[...g.assistantTexts],call:g.call,error:g.error,isConnected:g.isConnected,isRecording:!1,partial:g.partial,reconnect:g.reconnect,recordingError:null,sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,scenarioId:g.scenarioId,status:g.status,turns:[...g.turns]}),t=(g,A={})=>{let C=p(A.preset),V=u(g,{...C.connection,...A.connection}),R=null,c=d0(V),U=new Set,H=()=>{for(let W of U)W()},T=()=>{if(c={...c,assistantAudio:[...V.assistantAudio],assistantStreamingText:V.assistantStreamingText,assistantTexts:[...V.assistantTexts],call:V.call,error:V.error,isConnected:V.isConnected,partial:V.partial,reconnect:V.reconnect,sessionId:V.sessionId,sessionMetadata:V.sessionMetadata,scenarioId:V.scenarioId,status:V.status,turns:[...V.turns]},A.autoStopOnComplete!==!1&&c.status==="completed"&&c.isRecording)R?.stop(),R=null,c={...c,isRecording:!1};H()},G=V.subscribe(T);T();let $=()=>{if(R)return R;return R=f({channelCount:A.capture?.channelCount??C.capture.channelCount,onLevel:A.capture?.onLevel,onAudio:(W)=>{if(A.capture?.onAudio){A.capture.onAudio(W,V.sendAudio);return}V.sendAudio(W)},sampleRateHz:A.capture?.sampleRateHz??C.capture.sampleRateHz,...A.capture?.stream?{stream:A.capture.stream}:{}}),R},w=()=>{R?.stop(),R=null,c={...c,isRecording:!1},H()},I=async()=>{if(c.isRecording)return;try{c={...c,recordingError:null},H(),await $().start(),c={...c,isRecording:!0},H()}catch(W){throw R?.stop(),R=null,c={...c,isRecording:!1,recordingError:W instanceof Error?W.message:String(W)},H(),W}};return{close:(W)=>{G(),w(),V.close(W)},disconnect:()=>{G(),w(),V.disconnect()},startRecording:I,stopRecording:w,get assistantAudio(){return c.assistantAudio},get assistantTexts(){return c.assistantTexts},get assistantStreamingText(){return c.assistantStreamingText},bindHTMX(W){return F(V,W)},get call(){return c.call},callControl:(W)=>V.callControl(W),endTurn:()=>V.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:(W)=>V.sendAudio(W),get sessionId(){return c.sessionId},get sessionMetadata(){return c.sessionMetadata},simulateDisconnect:()=>V.simulateDisconnect(),get status(){return c.status},subscribe:(W)=>{return U.add(W),()=>{U.delete(W)}},toggleRecording:async()=>{if(c.isRecording){w();return}await I()},get turns(){return c.turns}}};var P=(g)=>String(g).replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll('"',""").replaceAll("'","'");var e=(g)=>{if(!g.isConnected)return"idle";if(g.isPlaying)return"speaking";if(g.isRecording&&g.hasActivePartial)return"listening";if(g.isRecording)return"listening";if(g.lastTranscriptAt&&!g.lastAssistantAt)return"thinking";if(g.lastTranscriptAt&&g.lastAssistantAt&&g.lastTranscriptAt>g.lastAssistantAt)return"thinking";return"idle"};var l0={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},v0={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},i0=(g,A)=>{switch(g){case"listening":return A.listening;case"speaking":return A.speaking;case"thinking":return A.thinking;case"idle":return A.idle}},g0=(g)=>{let A={...l0,...g.theme},C={...v0,...g.labels},V=g.state.assistantAudio.at(-1)?.receivedAt,R=g.state.turns.at(-1)?.committedAt,c=e({hasActivePartial:g.state.partial.length>0,isConnected:g.state.isConnected,isPlaying:!1,isRecording:g.state.isRecording,lastAssistantAt:V,lastTranscriptAt:R}),U=!g.state.isConnected&&g.state.status!=="idle"&&!g.state.error,H=g.state.error?"Error":U?C.connecting:g.state.status==="completed"?C.callEnded:i0(c,C);return{agentState:c,classes:{container:`absolute-voice-widget absolute-voice-widget--${c}`,dot:`absolute-voice-widget__dot${g.state.error?" absolute-voice-widget__dot--error":""}`},controls:{canEnd:g.state.isConnected,canMute:g.state.isRecording,canStart:!g.state.isRecording&&g.state.status!=="completed"},errorMessage:g.state.error??void 0,labels:C,partial:g.state.partial||void 0,statusLabel:H,theme:A,title:g.title??"Voice"}},n0=(g)=>typeof g==="number"?`${g}px`:g,A0=(g)=>{let A=g.theme,C=`background:${A.background};border-radius:${n0(A.radius)};color:${A.foreground};font-family:${A.fontFamily};min-width:240px;padding:20px 22px;`,V=`background:${g.errorMessage?A.errorAccent:g.agentState==="idle"?"rgba(148,163,184,0.6)":A.accent};border-radius:50%;height:10px;width:10px;`,R=[];if(g.controls.canStart)R.push(`<button type="button" data-action="start" style="background:${A.accent};border:none;border-radius:12px;color:${A.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${P(g.labels.startCall)}</button>`);if(g.controls.canMute)R.push(`<button type="button" data-action="mute" style="background:transparent;border:1px solid rgba(255,255,255,0.18);border-radius:12px;color:${A.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${P(g.labels.mute)}</button>`);if(g.controls.canEnd)R.push(`<button type="button" data-action="end" style="background:${A.errorAccent};border:none;border-radius:12px;color:${A.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${P(g.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${g.agentState}" class="${P(g.classes.container)}" style="${C}">
|
|
2
2
|
<div style="align-items:center;display:flex;gap:10px;margin-bottom:12px;">
|
|
3
|
-
<span aria-hidden="true" class="${
|
|
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(g.classes.dot)}" style="${V}"></span>
|
|
4
|
+
<strong style="font-size:15px;">${P(g.title)}</strong>
|
|
5
|
+
<span style="font-size:13px;margin-left:auto;opacity:0.7;">${P(g.statusLabel)}</span>
|
|
6
6
|
</div>
|
|
7
|
-
${g.partial?`<p style="font-size:13px;margin:8px 0 12px;opacity:0.85;word-break:break-word;">“${
|
|
7
|
+
${g.partial?`<p style="font-size:13px;margin:8px 0 12px;opacity:0.85;word-break:break-word;">“${P(g.partial)}”</p>`:""}
|
|
8
8
|
<div style="display:flex;gap:10px;">${R.join("")}</div>
|
|
9
|
-
${g.errorMessage?`<p style="color:${A.errorAccent};font-size:12px;margin-top:12px;">${
|
|
10
|
-
</div>`};var
|
|
9
|
+
${g.errorMessage?`<p style="color:${A.errorAccent};font-size:12px;margin-top:12px;">${P(g.errorMessage)}</p>`:""}
|
|
10
|
+
</div>`};var o0=(g)=>{if(typeof g!=="string")return g;let A=document.querySelector(g);if(!A)throw Error(`AbsoluteVoice.mount: no element matches "${g}"`);return A},V0=(g,A={})=>{let C=o0(g),V=t(A.path??"/voice",A.controllerOptions),R=null,c=null,U=()=>{let T=g0({...A.labels!==void 0?{labels:A.labels}:{},state:{assistantAudio:V.assistantAudio,error:V.error,isConnected:V.isConnected,isRecording:V.isRecording,partial:V.partial,status:V.status,turns:V.turns},...A.theme!==void 0?{theme:A.theme}:{},...A.title!==void 0?{title:A.title}:{}});C.innerHTML=A0(T);for(let G of C.querySelectorAll("button[data-action]")){let{action:$}=G.dataset;G.addEventListener("click",()=>{if($==="start")V.startRecording();else if($==="mute")V.stopRecording();else if($==="end")V.close()})}if(V.error&&V.error!==R)R=V.error,A.onError?.(V.error);if(V.status!==c)c=V.status,A.onStatusChange?.(V.status)},H=V.subscribe(U);if(U(),A.autoStart)V.startRecording();return{controller:V,async end(){await V.close()},mute(){V.stopRecording()},async start(){await V.startRecording()},unmount(){H(),V.close(),C.innerHTML=""}}},C0="0.0.22-beta.516",c0={mount:V0,version:C0};if(typeof globalThis<"u")globalThis.AbsoluteVoice=c0;var r0=c0;})();
|
package/dist/index.js
CHANGED
|
@@ -5054,7 +5054,7 @@ var createVoiceSession = (options) => {
|
|
|
5054
5054
|
clearSilenceTimer();
|
|
5055
5055
|
let didFail = false;
|
|
5056
5056
|
const session = await writeSession((currentSession) => {
|
|
5057
|
-
if (currentSession.status === "failed") {
|
|
5057
|
+
if (currentSession.status === "completed" || currentSession.status === "failed") {
|
|
5058
5058
|
return;
|
|
5059
5059
|
}
|
|
5060
5060
|
didFail = true;
|
|
@@ -39954,6 +39954,7 @@ var resolveSessionId3 = (runtime, ws) => {
|
|
|
39954
39954
|
runtime.socketSessions.set(ws, resolved);
|
|
39955
39955
|
return resolved;
|
|
39956
39956
|
};
|
|
39957
|
+
var resolveSocketQuery = (ws) => ws.data && typeof ws.data === "object" && ("query" in ws.data) ? ws.data.query ?? {} : {};
|
|
39957
39958
|
var resolveMaybeFunction = async (value, input) => typeof value === "function" ? await value(input) : value;
|
|
39958
39959
|
var toAudioChunk = (raw) => {
|
|
39959
39960
|
if (raw instanceof ArrayBuffer) {
|
|
@@ -40126,6 +40127,31 @@ var voice = (config) => {
|
|
|
40126
40127
|
profileSwitchGuardedSessions: new Set,
|
|
40127
40128
|
socketSessions: new WeakMap
|
|
40128
40129
|
};
|
|
40130
|
+
const authorizedSockets = new WeakSet;
|
|
40131
|
+
const authorizeSocket = async (ws, sessionId, scenarioId) => {
|
|
40132
|
+
if (!config.authorizeConnection) {
|
|
40133
|
+
authorizedSockets.add(ws);
|
|
40134
|
+
return true;
|
|
40135
|
+
}
|
|
40136
|
+
let authorized = false;
|
|
40137
|
+
try {
|
|
40138
|
+
authorized = await config.authorizeConnection({
|
|
40139
|
+
context: ws.data,
|
|
40140
|
+
path: config.path,
|
|
40141
|
+
query: resolveSocketQuery(ws),
|
|
40142
|
+
scenarioId,
|
|
40143
|
+
sessionId
|
|
40144
|
+
});
|
|
40145
|
+
} catch (error) {
|
|
40146
|
+
runtime.logger.warn?.(`[voice] connection authorization failed for session "${sessionId}": ${error instanceof Error ? error.message : String(error)}`);
|
|
40147
|
+
}
|
|
40148
|
+
if (!authorized) {
|
|
40149
|
+
ws.close(4401, "unauthorized");
|
|
40150
|
+
return false;
|
|
40151
|
+
}
|
|
40152
|
+
authorizedSockets.add(ws);
|
|
40153
|
+
return true;
|
|
40154
|
+
};
|
|
40129
40155
|
const { monitor } = config;
|
|
40130
40156
|
const registerMonitorSession = (sessionId, handle) => {
|
|
40131
40157
|
if (!monitor)
|
|
@@ -40451,6 +40477,10 @@ var voice = (config) => {
|
|
|
40451
40477
|
}
|
|
40452
40478
|
},
|
|
40453
40479
|
message: async (ws, raw) => {
|
|
40480
|
+
if (!authorizedSockets.has(ws)) {
|
|
40481
|
+
ws.close(4401, "unauthorized");
|
|
40482
|
+
return;
|
|
40483
|
+
}
|
|
40454
40484
|
const sessionState = resolveSessionId3(runtime, ws);
|
|
40455
40485
|
const current = runtime.activeSessions.get(sessionState.sessionId);
|
|
40456
40486
|
const message = parseClientMessage(raw);
|
|
@@ -40540,6 +40570,9 @@ var voice = (config) => {
|
|
|
40540
40570
|
},
|
|
40541
40571
|
open: async (ws) => {
|
|
40542
40572
|
const sessionState = resolveSessionId3(runtime, ws);
|
|
40573
|
+
if (!await authorizeSocket(ws, sessionState.sessionId, sessionState.scenarioId ?? undefined)) {
|
|
40574
|
+
return;
|
|
40575
|
+
}
|
|
40543
40576
|
const existing = runtime.activeSessions.get(sessionState.sessionId);
|
|
40544
40577
|
if (existing) {
|
|
40545
40578
|
await existing.close("superseded");
|
package/dist/react/index.js
CHANGED
|
@@ -11444,6 +11444,7 @@ var noopUnsubscribe = () => noop;
|
|
|
11444
11444
|
var NOOP_CONNECTION = {
|
|
11445
11445
|
callControl: noop,
|
|
11446
11446
|
close: noop,
|
|
11447
|
+
disconnect: noop,
|
|
11447
11448
|
endTurn: noop,
|
|
11448
11449
|
send: noop,
|
|
11449
11450
|
sendAudio: noop,
|
|
@@ -11455,7 +11456,7 @@ var NOOP_CONNECTION = {
|
|
|
11455
11456
|
start: () => {}
|
|
11456
11457
|
};
|
|
11457
11458
|
var createSessionId = () => crypto.randomUUID();
|
|
11458
|
-
var buildWsUrl = (path, sessionId, scenarioId) => {
|
|
11459
|
+
var buildWsUrl = (path, sessionId, scenarioId, query) => {
|
|
11459
11460
|
const { hostname, port, protocol } = window.location;
|
|
11460
11461
|
const wsProtocol = protocol === "https:" ? "wss:" : "ws:";
|
|
11461
11462
|
const portSuffix = port ? `:${port}` : "";
|
|
@@ -11464,6 +11465,11 @@ var buildWsUrl = (path, sessionId, scenarioId) => {
|
|
|
11464
11465
|
if (scenarioId) {
|
|
11465
11466
|
url.searchParams.set(DEFAULT_SCENARIO_QUERY_PARAM, scenarioId);
|
|
11466
11467
|
}
|
|
11468
|
+
for (const [key, value] of Object.entries(query)) {
|
|
11469
|
+
if (key !== "sessionId" && key !== DEFAULT_SCENARIO_QUERY_PARAM && value) {
|
|
11470
|
+
url.searchParams.set(key, value);
|
|
11471
|
+
}
|
|
11472
|
+
}
|
|
11467
11473
|
return url.toString();
|
|
11468
11474
|
};
|
|
11469
11475
|
var isVoiceServerMessage = (value) => {
|
|
@@ -11573,7 +11579,7 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
11573
11579
|
}, delayMs);
|
|
11574
11580
|
};
|
|
11575
11581
|
const connect = () => {
|
|
11576
|
-
const ws = new WebSocket(buildWsUrl(path, state.sessionId, state.scenarioId));
|
|
11582
|
+
const ws = new WebSocket(buildWsUrl(path, state.sessionId, state.scenarioId, options.query ?? {}));
|
|
11577
11583
|
ws.binaryType = "arraybuffer";
|
|
11578
11584
|
ws.onopen = () => {
|
|
11579
11585
|
const wasReconnecting = state.reconnectAttempts > 0;
|
|
@@ -11669,7 +11675,19 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
11669
11675
|
type: "call_control"
|
|
11670
11676
|
});
|
|
11671
11677
|
};
|
|
11672
|
-
const close = () => {
|
|
11678
|
+
const close = (reason = "client-close") => {
|
|
11679
|
+
clearTimers();
|
|
11680
|
+
if (state.ws) {
|
|
11681
|
+
if (state.ws.readyState === WS_OPEN) {
|
|
11682
|
+
state.ws.send(JSON.stringify({ reason, type: "close" }));
|
|
11683
|
+
}
|
|
11684
|
+
state.ws.close(WS_NORMAL_CLOSURE);
|
|
11685
|
+
state.ws = null;
|
|
11686
|
+
}
|
|
11687
|
+
state.isConnected = false;
|
|
11688
|
+
listeners.clear();
|
|
11689
|
+
};
|
|
11690
|
+
const disconnect = () => {
|
|
11673
11691
|
clearTimers();
|
|
11674
11692
|
if (state.ws) {
|
|
11675
11693
|
state.ws.close(WS_NORMAL_CLOSURE);
|
|
@@ -11693,6 +11711,7 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
11693
11711
|
return {
|
|
11694
11712
|
callControl,
|
|
11695
11713
|
close,
|
|
11714
|
+
disconnect,
|
|
11696
11715
|
endTurn,
|
|
11697
11716
|
send,
|
|
11698
11717
|
sendAudio,
|
|
@@ -11973,10 +11992,17 @@ var createVoiceStream = (path, options = {}) => {
|
|
|
11973
11992
|
callControl(message) {
|
|
11974
11993
|
connection.callControl(message);
|
|
11975
11994
|
},
|
|
11976
|
-
close() {
|
|
11995
|
+
close(reason) {
|
|
11977
11996
|
unsubscribeConnection();
|
|
11978
11997
|
browserMediaReporter?.close();
|
|
11979
|
-
connection.close();
|
|
11998
|
+
connection.close(reason);
|
|
11999
|
+
store.dispatch({ type: "disconnected" });
|
|
12000
|
+
notify();
|
|
12001
|
+
},
|
|
12002
|
+
disconnect() {
|
|
12003
|
+
unsubscribeConnection();
|
|
12004
|
+
browserMediaReporter?.close();
|
|
12005
|
+
connection.disconnect();
|
|
11980
12006
|
store.dispatch({ type: "disconnected" });
|
|
11981
12007
|
notify();
|
|
11982
12008
|
},
|
|
@@ -12179,29 +12205,34 @@ var createMicrophoneCapture = (options) => {
|
|
|
12179
12205
|
if (!AudioContextCtor) {
|
|
12180
12206
|
throw new Error("Browser microphone capture requires AudioContext support.");
|
|
12181
12207
|
}
|
|
12182
|
-
|
|
12183
|
-
|
|
12184
|
-
|
|
12185
|
-
|
|
12186
|
-
|
|
12187
|
-
|
|
12208
|
+
try {
|
|
12209
|
+
mediaStream = options.stream ?? await navigator.mediaDevices.getUserMedia({
|
|
12210
|
+
audio: {
|
|
12211
|
+
autoGainControl: true,
|
|
12212
|
+
channelCount: options.channelCount ?? 1,
|
|
12213
|
+
echoCancellation: true,
|
|
12214
|
+
noiseSuppression: true
|
|
12215
|
+
}
|
|
12216
|
+
});
|
|
12217
|
+
audioContext = new AudioContextCtor;
|
|
12218
|
+
if (audioContext.state === "suspended") {
|
|
12219
|
+
await audioContext.resume();
|
|
12188
12220
|
}
|
|
12189
|
-
|
|
12190
|
-
|
|
12191
|
-
|
|
12192
|
-
|
|
12193
|
-
|
|
12194
|
-
|
|
12195
|
-
|
|
12196
|
-
|
|
12197
|
-
|
|
12198
|
-
|
|
12199
|
-
|
|
12200
|
-
|
|
12201
|
-
|
|
12202
|
-
|
|
12203
|
-
|
|
12204
|
-
processorNode.connect(audioContext.destination);
|
|
12221
|
+
sourceNode = audioContext.createMediaStreamSource(mediaStream);
|
|
12222
|
+
processorNode = audioContext.createScriptProcessor(4096, 1, 1);
|
|
12223
|
+
processorNode.onaudioprocess = (event) => {
|
|
12224
|
+
const channel = event.inputBuffer.getChannelData(0);
|
|
12225
|
+
const downsampled = downsampleBuffer(channel, audioContext?.sampleRate ?? 48000, options.sampleRateHz ?? 16000);
|
|
12226
|
+
const pcm = floatTo16BitPCM(downsampled);
|
|
12227
|
+
options.onLevel?.(getPcmLevel(pcm));
|
|
12228
|
+
options.onAudio(pcm);
|
|
12229
|
+
};
|
|
12230
|
+
sourceNode.connect(processorNode);
|
|
12231
|
+
processorNode.connect(audioContext.destination);
|
|
12232
|
+
} catch (error) {
|
|
12233
|
+
stop();
|
|
12234
|
+
throw error;
|
|
12235
|
+
}
|
|
12205
12236
|
};
|
|
12206
12237
|
const stop = () => {
|
|
12207
12238
|
processorNode?.disconnect();
|
|
@@ -12644,6 +12675,7 @@ var createVoiceController = (path, options = {}) => {
|
|
|
12644
12675
|
};
|
|
12645
12676
|
notify();
|
|
12646
12677
|
} catch (error) {
|
|
12678
|
+
capture?.stop();
|
|
12647
12679
|
capture = null;
|
|
12648
12680
|
state = {
|
|
12649
12681
|
...state,
|
|
@@ -12654,13 +12686,19 @@ var createVoiceController = (path, options = {}) => {
|
|
|
12654
12686
|
throw error;
|
|
12655
12687
|
}
|
|
12656
12688
|
};
|
|
12657
|
-
const close = () => {
|
|
12689
|
+
const close = (reason) => {
|
|
12690
|
+
unsubscribeStream();
|
|
12691
|
+
stopRecording();
|
|
12692
|
+
stream.close(reason);
|
|
12693
|
+
};
|
|
12694
|
+
const disconnect = () => {
|
|
12658
12695
|
unsubscribeStream();
|
|
12659
12696
|
stopRecording();
|
|
12660
|
-
stream.
|
|
12697
|
+
stream.disconnect();
|
|
12661
12698
|
};
|
|
12662
12699
|
return {
|
|
12663
12700
|
close,
|
|
12701
|
+
disconnect,
|
|
12664
12702
|
startRecording,
|
|
12665
12703
|
stopRecording,
|
|
12666
12704
|
get assistantAudio() {
|