@absolutejs/voice 0.0.22-beta.656 → 0.0.22-beta.657

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.
@@ -100,48 +100,89 @@ var createMicrophoneCapture = (options) => {
100
100
  let sourceNode = null;
101
101
  let processorNode = null;
102
102
  let mediaStream = null;
103
- const start = async () => {
104
- if (!options.stream && (typeof navigator === "undefined" || !navigator.mediaDevices?.getUserMedia)) {
105
- throw new Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");
106
- }
107
- const AudioContextCtor = (typeof window !== "undefined" ? window.AudioContext ?? window.webkitAudioContext : undefined) ?? AudioContext;
108
- if (!AudioContextCtor) {
109
- throw new Error("Browser microphone capture requires AudioContext support.");
110
- }
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
103
+ let lifecycleGeneration = 0;
104
+ let startPromise = null;
105
+ const release = (resources) => {
106
+ resources.processorNode?.disconnect();
107
+ resources.sourceNode?.disconnect();
108
+ resources.mediaStream?.getTracks().forEach((track) => track.stop());
109
+ resources.audioContext?.close();
110
+ };
111
+ const start = () => {
112
+ if (mediaStream)
113
+ return Promise.resolve();
114
+ if (startPromise)
115
+ return startPromise;
116
+ const generation = ++lifecycleGeneration;
117
+ const operation = async () => {
118
+ if (!options.stream && (typeof navigator === "undefined" || !navigator.mediaDevices?.getUserMedia)) {
119
+ throw new Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");
120
+ }
121
+ const AudioContextCtor = (typeof window !== "undefined" ? window.AudioContext ?? window.webkitAudioContext : undefined) ?? AudioContext;
122
+ if (!AudioContextCtor) {
123
+ throw new Error("Browser microphone capture requires AudioContext support.");
124
+ }
125
+ let pendingAudioContext = null;
126
+ let pendingMediaStream = null;
127
+ let pendingProcessorNode = null;
128
+ let pendingSourceNode = null;
129
+ try {
130
+ pendingMediaStream = options.stream ?? await navigator.mediaDevices.getUserMedia({
131
+ audio: {
132
+ autoGainControl: true,
133
+ channelCount: options.channelCount ?? 1,
134
+ echoCancellation: true,
135
+ noiseSuppression: true
136
+ }
137
+ });
138
+ if (generation !== lifecycleGeneration) {
139
+ throw new Error("Microphone capture stopped during startup.");
118
140
  }
119
- });
120
- audioContext = new AudioContextCtor;
121
- if (audioContext.state === "suspended") {
122
- await audioContext.resume();
141
+ pendingAudioContext = new AudioContextCtor;
142
+ if (pendingAudioContext.state === "suspended") {
143
+ await pendingAudioContext.resume();
144
+ }
145
+ if (generation !== lifecycleGeneration) {
146
+ throw new Error("Microphone capture stopped during startup.");
147
+ }
148
+ pendingSourceNode = pendingAudioContext.createMediaStreamSource(pendingMediaStream);
149
+ pendingProcessorNode = pendingAudioContext.createScriptProcessor(4096, 1, 1);
150
+ pendingProcessorNode.onaudioprocess = (event) => {
151
+ const channel = event.inputBuffer.getChannelData(0);
152
+ const downsampled = downsampleBuffer(channel, pendingAudioContext?.sampleRate ?? 48000, options.sampleRateHz ?? 16000);
153
+ const pcm = floatTo16BitPCM(downsampled);
154
+ options.onLevel?.(getPcmLevel(pcm));
155
+ options.onAudio(pcm);
156
+ };
157
+ pendingSourceNode.connect(pendingProcessorNode);
158
+ pendingProcessorNode.connect(pendingAudioContext.destination);
159
+ if (generation !== lifecycleGeneration) {
160
+ throw new Error("Microphone capture stopped during startup.");
161
+ }
162
+ audioContext = pendingAudioContext;
163
+ mediaStream = pendingMediaStream;
164
+ processorNode = pendingProcessorNode;
165
+ sourceNode = pendingSourceNode;
166
+ } catch (error) {
167
+ release({
168
+ audioContext: pendingAudioContext,
169
+ mediaStream: pendingMediaStream,
170
+ processorNode: pendingProcessorNode,
171
+ sourceNode: pendingSourceNode
172
+ });
173
+ throw error;
123
174
  }
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;
138
- }
175
+ };
176
+ const pending = operation().finally(() => {
177
+ if (startPromise === pending)
178
+ startPromise = null;
179
+ });
180
+ startPromise = pending;
181
+ return pending;
139
182
  };
140
183
  const stop = () => {
141
- processorNode?.disconnect();
142
- sourceNode?.disconnect();
143
- mediaStream?.getTracks().forEach((track) => track.stop());
144
- audioContext?.close();
184
+ lifecycleGeneration += 1;
185
+ release({ audioContext, mediaStream, processorNode, sourceNode });
145
186
  options.onLevel?.(0);
146
187
  audioContext = null;
147
188
  mediaStream = null;
@@ -502,6 +543,7 @@ var DEFAULT_MAX_RECONNECT_ATTEMPTS = 15;
502
543
  var DEFAULT_PING_INTERVAL = 30000;
503
544
  var DEFAULT_RECONNECT_RESET_AFTER_MS = 30000;
504
545
  var DEFAULT_CALL_CONTROL_ACK_TIMEOUT_MS = 5000;
546
+ var DEFAULT_PREPARE_RECONNECT_TIMEOUT_MS = 1e4;
505
547
  var MAX_PENDING_CONTROL_MESSAGES = 100;
506
548
  var RECONNECT_BASE_DELAY_MS = 500;
507
549
  var DEFAULT_RECONNECT_MAX_DELAY_MS = 8000;
@@ -595,6 +637,7 @@ var createVoiceConnection = (path, options = {}) => {
595
637
  const reconnectResetAfterMs = options.reconnectResetAfterMs ?? DEFAULT_RECONNECT_RESET_AFTER_MS;
596
638
  let lifecycleGeneration = 0;
597
639
  let disposed = false;
640
+ let reconnectPreparationAbort = null;
598
641
  const computeReconnectDelayMs = (attempt) => computeVoiceReconnectDelayMs(attempt, RECONNECT_BASE_DELAY_MS, reconnectMaxDelayMs);
599
642
  const state = {
600
643
  isConnected: false,
@@ -682,12 +725,30 @@ var createVoiceConnection = (path, options = {}) => {
682
725
  return;
683
726
  }
684
727
  try {
685
- await options.prepareReconnect?.({
686
- attempt: state.reconnectAttempts,
687
- path,
688
- scenarioId: state.scenarioId,
689
- sessionId: state.sessionId
690
- });
728
+ if (options.prepareReconnect) {
729
+ const preparationAbort = new AbortController;
730
+ reconnectPreparationAbort = preparationAbort;
731
+ const timeout = setTimeout(() => preparationAbort.abort("Voice reconnect preparation timed out"), options.prepareReconnectTimeoutMs ?? DEFAULT_PREPARE_RECONNECT_TIMEOUT_MS);
732
+ try {
733
+ await Promise.race([
734
+ options.prepareReconnect({
735
+ attempt: state.reconnectAttempts,
736
+ path,
737
+ scenarioId: state.scenarioId,
738
+ sessionId: state.sessionId,
739
+ signal: preparationAbort.signal
740
+ }),
741
+ new Promise((_, reject) => {
742
+ preparationAbort.signal.addEventListener("abort", () => reject(new Error("Voice reconnect preparation timed out.")), { once: true });
743
+ })
744
+ ]);
745
+ } finally {
746
+ clearTimeout(timeout);
747
+ if (reconnectPreparationAbort === preparationAbort) {
748
+ reconnectPreparationAbort = null;
749
+ }
750
+ }
751
+ }
691
752
  } catch (error) {
692
753
  if (disposed || lifecycleGeneration !== scheduledGeneration)
693
754
  return;
@@ -811,6 +872,8 @@ var createVoiceConnection = (path, options = {}) => {
811
872
  state.ws = ws;
812
873
  };
813
874
  const sendSerialized = (value) => {
875
+ if (disposed)
876
+ return;
814
877
  if (state.ws?.readyState === WS_OPEN) {
815
878
  state.ws.send(value);
816
879
  return;
@@ -865,6 +928,8 @@ var createVoiceConnection = (path, options = {}) => {
865
928
  };
866
929
  const close = (reason = "client-close") => {
867
930
  disposed = true;
931
+ reconnectPreparationAbort?.abort();
932
+ reconnectPreparationAbort = null;
868
933
  lifecycleGeneration += 1;
869
934
  clearTimers();
870
935
  rejectPendingCallControls("Voice connection closed");
@@ -882,6 +947,8 @@ var createVoiceConnection = (path, options = {}) => {
882
947
  };
883
948
  const disconnect = () => {
884
949
  disposed = true;
950
+ reconnectPreparationAbort?.abort();
951
+ reconnectPreparationAbort = null;
885
952
  lifecycleGeneration += 1;
886
953
  clearTimers();
887
954
  rejectPendingCallControls("Voice connection disconnected");
@@ -1133,7 +1200,10 @@ var createVoiceStream = (path, options = {}) => {
1133
1200
  getSessionId: () => options.browserMedia ? options.browserMedia.getSessionId?.() ?? connection.getSessionId() : connection.getSessionId()
1134
1201
  }) : null;
1135
1202
  const subscribers = new Set;
1203
+ let disposed = false;
1136
1204
  const start = (input) => Promise.resolve().then(() => {
1205
+ if (disposed)
1206
+ return;
1137
1207
  if (!input?.sessionId && !input?.scenarioId) {
1138
1208
  return;
1139
1209
  }
@@ -1192,6 +1262,7 @@ var createVoiceStream = (path, options = {}) => {
1192
1262
  return connection.callControl(message);
1193
1263
  },
1194
1264
  close(reason) {
1265
+ disposed = true;
1195
1266
  unsubscribeConnection();
1196
1267
  browserMediaReporter?.close();
1197
1268
  connection.close(reason);
@@ -1199,6 +1270,7 @@ var createVoiceStream = (path, options = {}) => {
1199
1270
  notify();
1200
1271
  },
1201
1272
  disconnect() {
1273
+ disposed = true;
1202
1274
  unsubscribeConnection();
1203
1275
  browserMediaReporter?.close();
1204
1276
  connection.disconnect();
@@ -1565,6 +1637,9 @@ var createVoiceController = (path, options = {}) => {
1565
1637
  ...options.connection
1566
1638
  });
1567
1639
  let capture = null;
1640
+ let captureGeneration = 0;
1641
+ let disposed = false;
1642
+ let startRecordingPromise = null;
1568
1643
  let state = createInitialState2(stream);
1569
1644
  const subscribers = new Set;
1570
1645
  const notify = () => {
@@ -1621,6 +1696,7 @@ var createVoiceController = (path, options = {}) => {
1621
1696
  return capture;
1622
1697
  };
1623
1698
  const stopRecording = () => {
1699
+ captureGeneration += 1;
1624
1700
  capture?.stop();
1625
1701
  capture = null;
1626
1702
  state = {
@@ -1630,39 +1706,63 @@ var createVoiceController = (path, options = {}) => {
1630
1706
  notify();
1631
1707
  };
1632
1708
  const startRecording = async () => {
1709
+ if (disposed) {
1710
+ throw new Error("Voice controller is closed.");
1711
+ }
1633
1712
  if (state.isRecording) {
1634
1713
  return;
1635
1714
  }
1636
- try {
1637
- state = {
1638
- ...state,
1639
- recordingError: null
1640
- };
1641
- notify();
1642
- await ensureCapture().start();
1643
- state = {
1644
- ...state,
1645
- isRecording: true
1646
- };
1647
- notify();
1648
- } catch (error) {
1649
- capture?.stop();
1650
- capture = null;
1651
- state = {
1652
- ...state,
1653
- isRecording: false,
1654
- recordingError: error instanceof Error ? error.message : String(error)
1655
- };
1656
- notify();
1657
- throw error;
1658
- }
1715
+ if (startRecordingPromise)
1716
+ return startRecordingPromise;
1717
+ const generation = ++captureGeneration;
1718
+ const activeCapture = ensureCapture();
1719
+ const operation = (async () => {
1720
+ try {
1721
+ state = {
1722
+ ...state,
1723
+ recordingError: null
1724
+ };
1725
+ notify();
1726
+ await activeCapture.start();
1727
+ if (disposed || generation !== captureGeneration || capture !== activeCapture) {
1728
+ activeCapture.stop();
1729
+ return;
1730
+ }
1731
+ state = {
1732
+ ...state,
1733
+ isRecording: true
1734
+ };
1735
+ notify();
1736
+ } catch (error) {
1737
+ activeCapture.stop();
1738
+ if (generation !== captureGeneration || capture !== activeCapture) {
1739
+ throw error;
1740
+ }
1741
+ capture = null;
1742
+ state = {
1743
+ ...state,
1744
+ isRecording: false,
1745
+ recordingError: error instanceof Error ? error.message : String(error)
1746
+ };
1747
+ notify();
1748
+ throw error;
1749
+ }
1750
+ })();
1751
+ const pending = operation.finally(() => {
1752
+ if (startRecordingPromise === pending)
1753
+ startRecordingPromise = null;
1754
+ });
1755
+ startRecordingPromise = pending;
1756
+ return startRecordingPromise;
1659
1757
  };
1660
1758
  const close = (reason) => {
1759
+ disposed = true;
1661
1760
  unsubscribeStream();
1662
1761
  stopRecording();
1663
1762
  stream.close(reason);
1664
1763
  };
1665
1764
  const disconnect = () => {
1765
+ disposed = true;
1666
1766
  unsubscribeStream();
1667
1767
  stopRecording();
1668
1768
  stream.disconnect();
@@ -1,10 +1,10 @@
1
- (()=>{var{defineProperty:x,getOwnPropertyNames:G0,getOwnPropertyDescriptor:S0}=Object,X0=Object.prototype.hasOwnProperty;function O0(g){return this[g]}var y0=(g)=>{var A=(f??=new WeakMap).get(g),C;if(A)return A;if(A=x({},"__esModule",{value:!0}),g&&typeof g==="object"||typeof g==="function"){for(var V of G0(g))if(!X0.call(A,V))x(A,V,{get:O0.bind(g,V),enumerable:!(C=S0(g,V))||C.enumerable})}return f.set(g,A),A},f;var J0=(g)=>g;function Y0(g,A){this[g]=J0.bind(null,A)}var Z0=(g,A)=>{for(var C in A)x(g,C,{get:A[C],enumerable:!0,configurable:!0,set:Y0.bind(A,C)})};var Vg={};Z0(Vg,{mount:()=>T0,default:()=>Cg,VOICE_EMBED_VERSION:()=>$0});var w0=(g)=>{if(typeof g!=="string")return g;return document.querySelector(g)},h0=(g,A,C,V)=>{let $=A??g.getAttribute("hx-get")??"";if(!$)return"";let _=new URL($,window.location.origin);if(V)_.searchParams.set(C,V);else _.searchParams.delete(C);return`${_.pathname}${_.search}${_.hash}`},d=(g,A)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let C=w0(A.element);if(!C)return()=>{};let V=A.eventName??"voice-refresh",$=A.sessionQueryParam??"sessionId",_=()=>{let U=window,c=h0(C,A.route,$,g.sessionId);if(c)C.setAttribute("hx-get",c);U.htmx?.process?.(C),U.htmx?.trigger?.(C,V)},L=g.subscribe(_);return _(),()=>{L()}};var Q0=(g)=>Math.max(-1,Math.min(1,g)),K0=(g)=>{let A=new Int16Array(g.length);for(let C=0;C<g.length;C+=1){let V=Q0(g[C]??0);A[C]=V<0?V*32768:V*32767}return new Uint8Array(A.buffer)},P0=(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 $ of C){let _=$/32768;V+=_*_}return Math.min(1,Math.max(0,Math.sqrt(V/C.length)*5.5))},N0=(g,A,C)=>{if(A===C)return g;let V=A/C,$=Math.round(g.length/V),_=new Float32Array($),L=0,U=0;while(L<_.length){let c=Math.round((L+1)*V),W=0,R=0;for(let S=U;S<c&&S<g.length;S+=1)W+=g[S]??0,R+=1;_[L]=R>0?W/R:0,L+=1,U=c}return _},l=(g)=>{let A=null,C=null,V=null,$=null,_=async()=>{if(!g.stream&&(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia))throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let U=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!U)throw Error("Browser microphone capture requires AudioContext support.");try{if($=g.stream??await navigator.mediaDevices.getUserMedia({audio:{autoGainControl:!0,channelCount:g.channelCount??1,echoCancellation:!0,noiseSuppression:!0}}),A=new U,A.state==="suspended")await A.resume();C=A.createMediaStreamSource($),V=A.createScriptProcessor(4096,1,1),V.onaudioprocess=(c)=>{let W=c.inputBuffer.getChannelData(0),R=N0(W,A?.sampleRate??48000,g.sampleRateHz??16000),S=K0(R);g.onLevel?.(P0(S)),g.onAudio(S)},C.connect(V),V.connect(A.destination)}catch(c){throw L(),c}},L=()=>{V?.disconnect(),C?.disconnect(),$?.getTracks().forEach((U)=>U.stop()),A?.close(),g.onLevel?.(0),A=null,$=null,V=null,C=null};return{start:_,stop:L}};var k=(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 k(A.error);if("cause"in A)return k(A.cause);try{return JSON.stringify(g)}catch{}}return"Unexpected error"},v=(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:k(g.message),recoverable:g.recoverable,type:"error"};case"call_control_ack":return null;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 Rg=Math.PI*2;var P=(g,A,C,V)=>{g.push({code:C,message:V,severity:A})};var E0=(g)=>g.length===0?void 0:g.reduce((A,C)=>A+C,0)/g.length,M=(g)=>g.length===0?void 0:Math.max(...g);var O=(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},Y=(g,A)=>{let C=g[A];return typeof C==="string"?C:void 0},F=(g)=>String(g.id??Y(g,"ssrc")??O(g,"ssrc")??Y(g,"trackIdentifier")??Y(g,"mid")??"unknown"),i=(g)=>g===void 0?void 0:g*1000;var q0=(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 n=(g={})=>{let A=g.stats??[],C=[],V=A.filter((H)=>H.type==="inbound-rtp"&&Y(H,"kind")!=="video"),$=A.filter((H)=>H.type==="outbound-rtp"&&Y(H,"kind")!=="video"),_=A.filter((H)=>H.type==="candidate-pair"),L=A.filter((H)=>(H.type==="track"||H.type==="media-source")&&Y(H,"kind")==="audio"),U=_.filter((H)=>B(H,"selected")===!0||B(H,"nominated")===!0||Y(H,"state")==="succeeded").length,c=L.filter((H)=>Y(H,"readyState")!=="ended"&&Y(H,"trackState")!=="ended"&&B(H,"ended")!==!0).length,W=L.filter((H)=>Y(H,"readyState")==="ended"||Y(H,"trackState")==="ended"||B(H,"ended")===!0).length,R=V.reduce((H,J)=>H+(O(J,"packetsReceived")??0),0),S=$.reduce((H,J)=>H+(O(J,"packetsSent")??0),0),T=[...V,...$].reduce((H,J)=>H+Math.max(0,O(J,"packetsLost")??0),0),Z=R+T,w=Z===0?0:T/Z,D=V.reduce((H,J)=>H+(O(J,"bytesReceived")??0),0),K=$.reduce((H,J)=>H+(O(J,"bytesSent")??0),0),h=M(_.map((H)=>i(O(H,"currentRoundTripTime")??O(H,"roundTripTime"))).filter((H)=>H!==void 0)),Q=M([...V,...$].map((H)=>i(O(H,"jitter"))).filter((H)=>H!==void 0)),E=M(V.map((H)=>{let J=O(H,"jitterBufferDelay"),b=O(H,"jitterBufferEmittedCount");return J!==void 0&&b!==void 0&&b>0?J/b*1000:void 0}).filter((H)=>H!==void 0)),q=L.map((H)=>O(H,"audioLevel")).filter((H)=>H!==void 0);if(g.requireConnectedCandidatePair&&_.length>0&&U===0)P(C,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(g.requireLiveAudioTrack&&c===0)P(C,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(g.maxPacketLossRatio!==void 0&&w>g.maxPacketLossRatio)P(C,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(w)} above ${String(g.maxPacketLossRatio)}.`);if(g.maxRoundTripTimeMs!==void 0&&h!==void 0&&h>g.maxRoundTripTimeMs)P(C,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(h)}ms above ${String(g.maxRoundTripTimeMs)}ms.`);if(g.maxJitterMs!==void 0&&Q!==void 0&&Q>g.maxJitterMs)P(C,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(Q)}ms above ${String(g.maxJitterMs)}ms.`);return{activeCandidatePairs:U,audioLevelAverage:E0(q),bytesReceived:D,bytesSent:K,checkedAt:Date.now(),endedAudioTracks:W,inboundPackets:R,issues:C,jitterBufferDelayMs:E,jitterMs:Q,liveAudioTracks:c,outboundPackets:S,packetLossRatio:w,packetsLost:T,roundTripTimeMs:h,status:C.some((H)=>H.severity==="error")?"fail":C.length>0?"warn":"pass",totalStats:A.length}},o=async(g)=>{return[...(await g.peerConnection.getStats(g.selector??null)).values()].map(q0)};var m=(g={})=>{let A=g.stats??[],C=g.previousStats??[],V=[],$=new Map(C.map((T)=>[F(T),T])),L=A.filter((T)=>(T.type==="inbound-rtp"||T.type==="outbound-rtp")&&Y(T,"kind")!=="video"&&Y(T,"mediaType")!=="video").map((T)=>{let Z=T.type==="outbound-rtp"?"outbound":"inbound",w=Z==="outbound"?"packetsSent":"packetsReceived",D=Z==="outbound"?"bytesSent":"bytesReceived",K=$.get(F(T)),h=O(T,w),Q=K?O(K,w):void 0,E=O(T,D),q=K?O(K,D):void 0,H=T.timestamp!==void 0&&K?.timestamp!==void 0?T.timestamp-K.timestamp:void 0;return{bytesDelta:E!==void 0&&q!==void 0?E-q:void 0,currentPackets:h,direction:Z,id:F(T),packetDelta:h!==void 0&&Q!==void 0?h-Q:void 0,previousPackets:Q,timeDeltaMs:H}}),U=L.filter((T)=>T.direction==="inbound"),c=L.filter((T)=>T.direction==="outbound"),W=M(L.map((T)=>T.timeDeltaMs).filter((T)=>T!==void 0)),R=U.filter((T)=>g.maxInboundPacketStallMs!==void 0&&T.timeDeltaMs!==void 0&&T.timeDeltaMs>=g.maxInboundPacketStallMs&&T.packetDelta!==void 0&&T.packetDelta<=0).length,S=c.filter((T)=>g.maxOutboundPacketStallMs!==void 0&&T.timeDeltaMs!==void 0&&T.timeDeltaMs>=g.maxOutboundPacketStallMs&&T.packetDelta!==void 0&&T.packetDelta<=0).length;if(g.requireInboundAudio&&U.length===0)P(V,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(g.requireOutboundAudio&&c.length===0)P(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)P(V,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(W)}ms above ${String(g.maxGapMs)}ms.`);if(R>0)P(V,"error","media.webrtc_inbound_stalled",`${String(R)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(S>0)P(V,"error","media.webrtc_outbound_stalled",`${String(S)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:U.length,issues:V,maxObservedGapMs:W,outboundAudioStreams:c.length,stalledInboundStreams:R,stalledOutboundStreams:S,status:V.some((T)=>T.severity==="error")?"fail":V.length>0?"warn":"pass",streams:L,totalStats:A.length}};var z0="/api/voice/browser-media",b0=5000,B0=async(g)=>g.peerConnection??await g.getPeerConnection?.()??null,M0=async(g,A)=>{let C=A.fetch??globalThis.fetch;if(!C)return;await C(A.path??z0,{body:JSON.stringify(g),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},r=(g)=>{let A=null,C=[],V=async()=>{let L=await B0(g);if(!L)return;let U=await o({peerConnection:L}),c=n({...g,stats:U}),W=g.continuity===!1?void 0:m({...g.continuity,previousStats:C,stats:U}),R={at:Date.now(),continuity:W,report:c,scenarioId:g.getScenarioId?.()??null,sessionId:g.getSessionId?.()??null};return C=U,g.onReport?.(R),await M0(R,g),R},$=()=>{V().catch((L)=>{g.onError?.(L)})},_=()=>{if(A)clearInterval(A),A=null};return{close:_,reportOnce:V,stop:_,start:()=>{if(A)return;$(),A=setInterval($,g.intervalMs??b0)}}};var j0=(g,A,C)=>Math.min(C,A*2**(Math.max(1,g)-1));class u extends Error{constructor(g="Voice authorization could not be renewed."){super(g);this.name="VoiceReconnectRejectedError"}}var z=()=>{},x0=()=>Promise.resolve(),k0=()=>z,F0={callControl:x0,close:z,disconnect:z,endTurn:z,send:z,sendAudio:z,simulateDisconnect:z,subscribe:k0,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",start:()=>{}},f0=()=>crypto.randomUUID(),d0=(g,A,C,V)=>{let{hostname:$,port:_,protocol:L}=window.location,U=L==="https:"?"wss:":"ws:",c=_?`:${_}`:"",W=new URL(`${U}//${$}${c}${g}`);if(W.searchParams.set("sessionId",A),C)W.searchParams.set("scenarioId",C);for(let[R,S]of Object.entries(V))if(R!=="sessionId"&&R!=="scenarioId"&&S)W.searchParams.set(R,S);return W.toString()},l0=(g)=>{if(!g||typeof g!=="object"||!("type"in g))return!1;switch(g.type){case"audio":case"assistant":case"call_lifecycle":case"call_control_ack":case"complete":case"connection":case"error":case"final":case"partial":case"pong":case"replay":case"session":case"turn":return!0;default:return!1}},v0=(g)=>{if(typeof g.data!=="string")return null;try{let A=JSON.parse(g.data);return l0(A)?A:null}catch{return null}},s=(g,A={})=>{if(typeof window>"u")return F0;let C=new Set,V=new Map,$=A.reconnect!==!1,_=A.maxReconnectAttempts??15,L=A.reconnectMaxDelayMs??8000,U=A.pingInterval??30000,c=A.reconnectResetAfterMs??30000,W=0,R=!1,S=(I)=>j0(I,500,L),T={isConnected:!1,pendingMessages:[],scenarioId:A.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectResetTimeout:null,reconnectTimeout:null,sessionId:A.sessionId??f0(),ws:null},Z=(I)=>{C.forEach((G)=>G(I))},w=(I)=>{C.forEach((G)=>G({message:I,recoverable:!1,type:"error"})),Z({reconnect:{attempts:T.reconnectAttempts,maxAttempts:_,status:"exhausted"},type:"connection"})},D=()=>{if(T.pingInterval)clearInterval(T.pingInterval),T.pingInterval=null;if(T.reconnectTimeout)clearTimeout(T.reconnectTimeout),T.reconnectTimeout=null;if(T.reconnectResetTimeout)clearTimeout(T.reconnectResetTimeout),T.reconnectResetTimeout=null},K=()=>{if(T.ws?.readyState!==1)return;while(T.pendingMessages.length>0){let I=T.pendingMessages.shift();if(I!==void 0)T.ws.send(I)}},h=(I)=>{for(let G of V.values())clearTimeout(G.timeout),G.reject(Error(I));V.clear()},Q=()=>{if(R)return;let I=W;T.reconnectAttempts+=1;let G=S(T.reconnectAttempts),X=Date.now()+G;Z({reconnect:{attempts:T.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:_,nextAttemptAt:X,status:"reconnecting"},type:"connection"}),T.reconnectTimeout=setTimeout(async()=>{if(R||W!==I)return;if(T.reconnectAttempts>_){w("Voice connection could not be restored.");return}try{await A.prepareReconnect?.({attempt:T.reconnectAttempts,path:g,scenarioId:T.scenarioId,sessionId:T.sessionId})}catch(y){if(R||W!==I)return;if(y instanceof u){w(y.message);return}if(T.reconnectAttempts<_)Q();else w("Voice authorization could not be renewed for reconnect.");return}if(R||W!==I)return;E()},G)},E=()=>{if(R)return;let I=new WebSocket(d0(g,T.sessionId,T.scenarioId,A.query??{}));I.binaryType="arraybuffer",I.onopen=()=>{if(T.ws!==I){I.close(1000);return}let G=T.reconnectAttempts>0;if(T.isConnected=!0,K(),G)Z({reconnect:{attempts:T.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:_,status:"resumed"},type:"connection"}),T.reconnectResetTimeout=setTimeout(()=>{if(T.ws===I&&I.readyState===1)T.reconnectAttempts=0;T.reconnectResetTimeout=null},c);C.forEach((X)=>X({scenarioId:T.scenarioId??void 0,sessionId:T.sessionId,status:"active",type:"session"})),T.pingInterval=setInterval(()=>{if(I.readyState===1)I.send(JSON.stringify({type:"ping"}))},U)},I.onmessage=(G)=>{if(T.ws!==I)return;let X=v0(G);if(!X)return;if(X.type==="session")T.sessionId=X.sessionId,T.scenarioId=X.scenarioId??T.scenarioId;if(X.type==="call_control_ack"){let y=V.get(X.requestId);if(y)if(clearTimeout(y.timeout),V.delete(X.requestId),X.ok)y.resolve();else y.reject(Error(X.message??`${X.action} was not applied`))}C.forEach((y)=>y(X))},I.onclose=(G)=>{if(T.ws!==I)return;T.ws=null,T.isConnected=!1,D(),h("Voice connection interrupted");let X=$&&G.code!==1000&&G.code!==4401&&G.code!==4403&&T.reconnectAttempts<_,y=G.code===4401||G.code===4403;if(y)C.forEach((j)=>j({message:G.code===4401?"Voice authorization expired or was rejected.":"Voice authorization was forbidden.",recoverable:!1,type:"error"}));if(X)Q();else if($&&G.code!==1000)if(y)Z({reconnect:{attempts:T.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:_,status:"exhausted"},type:"connection"});else w("Voice connection could not be restored.")},T.ws=I},q=(I)=>{if(T.ws?.readyState===1){T.ws.send(I);return}if(typeof I!=="string")return;if(T.pendingMessages.length>=100)T.pendingMessages.shift();T.pendingMessages.push(I)},H=(I)=>{q(JSON.stringify(I))},J=(I={})=>{if(I.sessionId)T.sessionId=I.sessionId;if(I.scenarioId)T.scenarioId=I.scenarioId;H({scenarioId:T.scenarioId??void 0,sessionId:T.sessionId,type:"start"})},b=(I)=>{q(I)},R0=()=>{H({type:"end_turn"})},I0=(I)=>{if(T.ws?.readyState!==1)return Promise.reject(Error(`${I.action} is unavailable while disconnected`));let G=crypto.randomUUID(),{promise:X,reject:y,resolve:j}=Promise.withResolvers(),D0=setTimeout(()=>{V.delete(G),y(Error(`${I.action} acknowledgement timed out`))},5000);return V.set(G,{reject:y,resolve:j,timeout:D0}),T.ws.send(JSON.stringify({...I,requestId:G,type:"call_control"})),X},U0=(I="client-close")=>{if(R=!0,W+=1,D(),h("Voice connection closed"),T.pendingMessages.length=0,T.ws){let G=T.ws;if(T.ws=null,G.readyState===1)G.send(JSON.stringify({reason:I,type:"close"}));G.close(1000)}T.isConnected=!1,C.clear()},L0=()=>{if(R=!0,W+=1,D(),h("Voice connection disconnected"),T.pendingMessages.length=0,T.ws){let I=T.ws;T.ws=null,I.close(1000)}T.isConnected=!1,C.clear()},W0=()=>{if(T.ws?.readyState===1)T.ws.close(4000,"absolutejs-voice-reconnect-proof")},c0=(I)=>{return C.add(I),()=>{C.delete(I)}};return E(),{callControl:I0,close:U0,disconnect:L0,endTurn:R0,send:H,sendAudio:b,simulateDisconnect:W0,start:J,subscribe:c0,getReadyState:()=>T.ws?.readyState??3,getScenarioId:()=>T.scenarioId??"",getSessionId:()=>T.sessionId}};var i0=()=>({attempts:0,maxAttempts:0,status:"idle"}),n0=(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}`},o0=(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:i0(),scenarioId:null,sessionId:null,sessionMetadata:null,status:"idle",turns:[]}),a=()=>{let g=m0(),A="",C=new Set,V=()=>{C.forEach((_)=>_())};return{dispatch:(_)=>{switch(_.type){case"audio":g={...g,assistantAudio:[...g.assistantAudio,{chunk:_.chunk,format:_.format,receivedAt:_.receivedAt,turnId:_.turnId}]};break;case"assistant":g={...g,assistantStreamingText:"",assistantTexts:[...g.assistantTexts,_.text]};break;case"assistant_delta":g={...g,assistantStreamingText:`${g.assistantStreamingText}${_.delta}`};break;case"complete":g={...g,sessionId:_.sessionId,status:"completed"};break;case"call_lifecycle":g={...g,call:{...g.call,disposition:_.event.type==="end"?_.event.disposition:g.call?.disposition,endedAt:_.event.type==="end"?_.event.at:g.call?.endedAt,events:[...g.call?.events??[],_.event],lastEventAt:_.event.at,startedAt:g.call?.startedAt??_.event.at},sessionId:_.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:_.reconnect};break;case"disconnected":g={...g,isConnected:!1};break;case"error":g={...g,error:_.message,..._.recoverable===!1?{isConnected:!1,status:"failed"}:{}};break;case"final":A=n0(A,_.transcript.text),g={...g,partial:A};break;case"partial":g={...g,partial:o0(A,_.transcript.text)};break;case"replay":A=_.partial,g={...g,assistantStreamingText:"",assistantTexts:[..._.assistantTexts],call:_.call??null,error:null,isConnected:_.status==="active",partial:_.partial,reconnect:g.reconnect.status==="reconnecting"?{...g.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:g.reconnect,scenarioId:_.scenarioId??g.scenarioId,sessionId:_.sessionId,sessionMetadata:_.sessionMetadata??g.sessionMetadata,status:_.status,turns:[..._.turns]};break;case"session":g={...g,error:null,scenarioId:_.scenarioId??g.scenarioId,isConnected:_.status==="active",sessionId:_.sessionId,sessionMetadata:_.sessionMetadata??g.sessionMetadata,status:_.status};break;case"turn":A="",g={...g,partial:"",turns:[...g.turns,_.turn]};break}V()},getServerSnapshot:()=>g,getSnapshot:()=>g,subscribe:(_)=>{return C.add(_),()=>{C.delete(_)}}}};var p=(g,A={})=>{let C=s(g,A),V=a(),$=A.browserMedia&&typeof window<"u"?r({...A.browserMedia,getScenarioId:()=>A.browserMedia?A.browserMedia.getScenarioId?.()??C.getScenarioId():C.getScenarioId(),getSessionId:()=>A.browserMedia?A.browserMedia.getSessionId?.()??C.getSessionId():C.getSessionId()}):null,_=new Set,L=(R)=>Promise.resolve().then(()=>{if(!R?.sessionId&&!R?.scenarioId)return;C.start(R),$?.start()}),U=()=>{_.forEach((R)=>R())},c=()=>{if(!A.reconnectReportPath||typeof fetch>"u")return;let R=V.getSnapshot(),S=JSON.stringify({at:Date.now(),reconnect:R.reconnect,scenarioId:R.scenarioId,sessionId:C.getSessionId(),turnIds:R.turns.map((T)=>T.id)});fetch(A.reconnectReportPath,{body:S,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},W=C.subscribe((R)=>{let S=v(R);if(S){if(V.dispatch(S),R.type==="connection")c();U()}});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(R){return C.callControl(R)},close(R){W(),$?.close(),C.close(R),V.dispatch({type:"disconnected"}),U()},disconnect(){W(),$?.close(),C.disconnect(),V.dispatch({type:"disconnected"}),U()},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(R){C.sendAudio(R)},get sessionId(){return C.getSessionId()},get sessionMetadata(){return V.getSnapshot().sessionMetadata},simulateDisconnect(){C.simulateDisconnect()},get status(){return V.getSnapshot().status},subscribe(R){return _.add(R),()=>{_.delete(R)}},get turns(){return V.getSnapshot().turns}}};var t=(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 r0={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}},u0={"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=(g)=>{let A=g?.profile??"fast",C=g?.qualityProfile??"general",V=r0[A],$=u0[C],_=g?.silenceMs??$.silenceMs??V.silenceMs;return{profile:A,qualityProfile:C,minSilenceMs:Math.min(_,g?.minSilenceMs??$.minSilenceMs??V.minSilenceMs),silenceMs:_,speechThreshold:g?.speechThreshold??$.speechThreshold??V.speechThreshold,transcriptStabilityMs:g?.transcriptStabilityMs??$.transcriptStabilityMs??V.transcriptStabilityMs}};var s0={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"}}},g0=(g="default")=>{let A=s0[g];return{audioConditioning:t(A.audioConditioning),capture:{channelCount:A.capture?.channelCount??1,sampleRateHz:A.capture?.sampleRateHz??16000},connection:{...A.connection},name:g,sttLifecycle:A.sttLifecycle??"continuous",turnDetection:e(A.turnDetection)}};var a0=(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]}),A0=(g,A={})=>{let C=g0(A.preset),V=p(g,{...C.connection,...A.connection}),$=null,_=a0(V),L=new Set,U=()=>{for(let D of L)D()},c=()=>{if(_={..._,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&&_.status==="completed"&&_.isRecording)$?.stop(),$=null,_={..._,isRecording:!1};U()},W=V.subscribe(c);c();let R=()=>{if($)return $;return $=l({channelCount:A.capture?.channelCount??C.capture.channelCount,onLevel:A.capture?.onLevel,onAudio:(D)=>{if(A.capture?.onAudio){A.capture.onAudio(D,V.sendAudio);return}V.sendAudio(D)},sampleRateHz:A.capture?.sampleRateHz??C.capture.sampleRateHz,...A.capture?.stream?{stream:A.capture.stream}:{}}),$},S=()=>{$?.stop(),$=null,_={..._,isRecording:!1},U()},T=async()=>{if(_.isRecording)return;try{_={..._,recordingError:null},U(),await R().start(),_={..._,isRecording:!0},U()}catch(D){throw $?.stop(),$=null,_={..._,isRecording:!1,recordingError:D instanceof Error?D.message:String(D)},U(),D}};return{close:(D)=>{W(),S(),V.close(D)},disconnect:()=>{W(),S(),V.disconnect()},startRecording:T,stopRecording:S,get assistantAudio(){return _.assistantAudio},get assistantTexts(){return _.assistantTexts},get assistantStreamingText(){return _.assistantStreamingText},bindHTMX(D){return d(V,D)},get call(){return _.call},callControl:(D)=>V.callControl(D),endTurn:()=>V.endTurn(),get error(){return _.error},getServerSnapshot:()=>_,getSnapshot:()=>_,get isConnected(){return _.isConnected},get isRecording(){return _.isRecording},get partial(){return _.partial},get reconnect(){return _.reconnect},get recordingError(){return _.recordingError},get scenarioId(){return _.scenarioId},sendAudio:(D)=>V.sendAudio(D),get sessionId(){return _.sessionId},get sessionMetadata(){return _.sessionMetadata},simulateDisconnect:()=>V.simulateDisconnect(),get status(){return _.status},subscribe:(D)=>{return L.add(D),()=>{L.delete(D)}},toggleRecording:async()=>{if(_.isRecording){S();return}await T()},get turns(){return _.turns}}};var N=(g)=>String(g).replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;");var V0=(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 p0={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},t0={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},e0=(g,A)=>{switch(g){case"listening":return A.listening;case"speaking":return A.speaking;case"thinking":return A.thinking;case"idle":return A.idle}},C0=(g)=>{let A={...p0,...g.theme},C={...t0,...g.labels},V=g.state.assistantAudio.at(-1)?.receivedAt,$=g.state.turns.at(-1)?.committedAt,_=V0({hasActivePartial:g.state.partial.length>0,isConnected:g.state.isConnected,isPlaying:!1,isRecording:g.state.isRecording,lastAssistantAt:V,lastTranscriptAt:$}),L=!g.state.isConnected&&g.state.status!=="idle"&&!g.state.error,U=g.state.error?"Error":L?C.connecting:g.state.status==="completed"?C.callEnded:e0(_,C);return{agentState:_,classes:{container:`absolute-voice-widget absolute-voice-widget--${_}`,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:U,theme:A,title:g.title??"Voice"}},gg=(g)=>typeof g==="number"?`${g}px`:g,_0=(g)=>{let A=g.theme,C=`background:${A.background};border-radius:${gg(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;`,$=[];if(g.controls.canStart)$.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;">${N(g.labels.startCall)}</button>`);if(g.controls.canMute)$.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;">${N(g.labels.mute)}</button>`);if(g.controls.canEnd)$.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;">${N(g.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${g.agentState}" class="${N(g.classes.container)}" style="${C}">
1
+ (()=>{var{defineProperty:x,getOwnPropertyNames:w0,getOwnPropertyDescriptor:J0}=Object,S0=Object.prototype.hasOwnProperty;function Y0(V){return this[V]}var Z0=(V)=>{var g=(l??=new WeakMap).get(V),C;if(g)return g;if(g=x({},"__esModule",{value:!0}),V&&typeof V==="object"||typeof V==="function"){for(var A of w0(V))if(!S0.call(g,A))x(g,A,{get:Y0.bind(V,A),enumerable:!(C=J0(V,A))||C.enumerable})}return l.set(V,g),g},l;var y0=(V)=>V;function h0(V,g){this[V]=y0.bind(null,g)}var Q0=(V,g)=>{for(var C in g)x(V,C,{get:g[C],enumerable:!0,configurable:!0,set:h0.bind(g,C)})};var _V={};Q0(_V,{mount:()=>$0,default:()=>TV,VOICE_EMBED_VERSION:()=>R0});var K0=(V)=>{if(typeof V!=="string")return V;return document.querySelector(V)},c0=(V,g,C,A)=>{let R=g??V.getAttribute("hx-get")??"";if(!R)return"";let _=new URL(R,window.location.origin);if(A)_.searchParams.set(C,A);else _.searchParams.delete(C);return`${_.pathname}${_.search}${_.hash}`},d=(V,g)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let C=K0(g.element);if(!C)return()=>{};let A=g.eventName??"voice-refresh",R=g.sessionQueryParam??"sessionId",_=()=>{let X=window,H=c0(C,g.route,R,V.sessionId);if(H)C.setAttribute("hx-get",H);X.htmx?.process?.(C),X.htmx?.trigger?.(C,A)},D=V.subscribe(_);return _(),()=>{D()}};var E0=(V)=>Math.max(-1,Math.min(1,V)),q0=(V)=>{let g=new Int16Array(V.length);for(let C=0;C<V.length;C+=1){let A=E0(V[C]??0);g[C]=A<0?A*32768:A*32767}return new Uint8Array(g.buffer)},z0=(V)=>{let g=V instanceof Uint8Array?V:new Uint8Array(V);if(g.byteLength<2)return 0;let C=new Int16Array(g.buffer,g.byteOffset,Math.floor(g.byteLength/2));if(C.length===0)return 0;let A=0;for(let R of C){let _=R/32768;A+=_*_}return Math.min(1,Math.max(0,Math.sqrt(A/C.length)*5.5))},N0=(V,g,C)=>{if(g===C)return V;let A=g/C,R=Math.round(V.length/A),_=new Float32Array(R),D=0,X=0;while(D<_.length){let H=Math.round((D+1)*A),O=0,L=0;for(let W=X;W<H&&W<V.length;W+=1)O+=V[W]??0,L+=1;_[D]=L>0?O/L:0,D+=1,X=H}return _},v=(V)=>{let g=null,C=null,A=null,R=null,_=0,D=null,X=(L)=>{L.processorNode?.disconnect(),L.sourceNode?.disconnect(),L.mediaStream?.getTracks().forEach((W)=>W.stop()),L.audioContext?.close()};return{start:()=>{if(R)return Promise.resolve();if(D)return D;let L=++_,I=(async()=>{if(!V.stream&&(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia))throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let T=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!T)throw Error("Browser microphone capture requires AudioContext support.");let J=null,h=null,Z=null,Q=null;try{if(h=V.stream??await navigator.mediaDevices.getUserMedia({audio:{autoGainControl:!0,channelCount:V.channelCount??1,echoCancellation:!0,noiseSuppression:!0}}),L!==_)throw Error("Microphone capture stopped during startup.");if(J=new T,J.state==="suspended")await J.resume();if(L!==_)throw Error("Microphone capture stopped during startup.");if(Q=J.createMediaStreamSource(h),Z=J.createScriptProcessor(4096,1,1),Z.onaudioprocess=(G)=>{let c=G.inputBuffer.getChannelData(0),q=N0(c,J?.sampleRate??48000,V.sampleRateHz??16000),$=q0(q);V.onLevel?.(z0($)),V.onAudio($)},Q.connect(Z),Z.connect(J.destination),L!==_)throw Error("Microphone capture stopped during startup.");g=J,R=h,A=Z,C=Q}catch(G){throw X({audioContext:J,mediaStream:h,processorNode:Z,sourceNode:Q}),G}})().finally(()=>{if(D===I)D=null});return D=I,I},stop:()=>{_+=1,X({audioContext:g,mediaStream:R,processorNode:A,sourceNode:C}),V.onLevel?.(0),g=null,R=null,A=null,C=null}}};var k=(V)=>{if(typeof V==="string"&&V.trim())return V;if(V instanceof Error&&V.message.trim())return V.message;if(V&&typeof V==="object"){let g=V;for(let C of["message","reason","description"]){let A=g[C];if(typeof A==="string"&&A.trim())return A}if("error"in g)return k(g.error);if("cause"in g)return k(g.cause);try{return JSON.stringify(V)}catch{}}return"Unexpected error"},i=(V)=>{switch(V.type){case"audio":return{chunk:Uint8Array.from(atob(V.chunkBase64),(g)=>g.charCodeAt(0)),format:V.format,receivedAt:V.receivedAt,turnId:V.turnId,type:"audio"};case"assistant":return{text:V.text,turnId:V.turnId,type:"assistant"};case"assistant_delta":return{delta:V.delta,turnId:V.turnId,type:"assistant_delta"};case"complete":return{sessionId:V.sessionId,type:"complete"};case"connection":return{reconnect:V.reconnect,type:"connection"};case"call_lifecycle":return{event:V.event,sessionId:V.sessionId,type:"call_lifecycle"};case"error":return{message:k(V.message),recoverable:V.recoverable,type:"error"};case"call_control_ack":return null;case"final":return{transcript:V.transcript,type:"final"};case"partial":return{transcript:V.transcript,type:"partial"};case"replay":return{assistantTexts:V.assistantTexts,call:V.call,partial:V.partial,scenarioId:V.scenarioId,sessionId:V.sessionId,sessionMetadata:V.sessionMetadata,status:V.status,turns:V.turns,type:"replay"};case"session":return{sessionId:V.sessionId,sessionMetadata:V.sessionMetadata,scenarioId:V.scenarioId,status:V.status,type:"session"};case"turn":return{turn:V.turn,type:"turn"};default:return null}};var UV=Math.PI*2;var z=(V,g,C,A)=>{V.push({code:C,message:A,severity:g})};var P0=(V)=>V.length===0?void 0:V.reduce((g,C)=>g+C,0)/V.length,j=(V)=>V.length===0?void 0:Math.max(...V);var K=(V,g)=>{let C=V[g];return typeof C==="number"&&Number.isFinite(C)?C:void 0},b=(V,g)=>{let C=V[g];return typeof C==="boolean"?C:void 0},E=(V,g)=>{let C=V[g];return typeof C==="string"?C:void 0},F=(V)=>String(V.id??E(V,"ssrc")??K(V,"ssrc")??E(V,"trackIdentifier")??E(V,"mid")??"unknown"),o=(V)=>V===void 0?void 0:V*1000;var B0=(V)=>{let g={};for(let[C,A]of Object.entries(V))if(A===null||typeof A==="boolean"||typeof A==="number"||typeof A==="string")g[C]=A;return g};var n=(V={})=>{let g=V.stats??[],C=[],A=g.filter(($)=>$.type==="inbound-rtp"&&E($,"kind")!=="video"),R=g.filter(($)=>$.type==="outbound-rtp"&&E($,"kind")!=="video"),_=g.filter(($)=>$.type==="candidate-pair"),D=g.filter(($)=>($.type==="track"||$.type==="media-source")&&E($,"kind")==="audio"),X=_.filter(($)=>b($,"selected")===!0||b($,"nominated")===!0||E($,"state")==="succeeded").length,H=D.filter(($)=>E($,"readyState")!=="ended"&&E($,"trackState")!=="ended"&&b($,"ended")!==!0).length,O=D.filter(($)=>E($,"readyState")==="ended"||E($,"trackState")==="ended"||b($,"ended")===!0).length,L=A.reduce(($,S)=>$+(K(S,"packetsReceived")??0),0),W=R.reduce(($,S)=>$+(K(S,"packetsSent")??0),0),I=[...A,...R].reduce(($,S)=>$+Math.max(0,K(S,"packetsLost")??0),0),T=L+I,J=T===0?0:I/T,h=A.reduce(($,S)=>$+(K(S,"bytesReceived")??0),0),Z=R.reduce(($,S)=>$+(K(S,"bytesSent")??0),0),Q=j(_.map(($)=>o(K($,"currentRoundTripTime")??K($,"roundTripTime"))).filter(($)=>$!==void 0)),G=j([...A,...R].map(($)=>o(K($,"jitter"))).filter(($)=>$!==void 0)),c=j(A.map(($)=>{let S=K($,"jitterBufferDelay"),B=K($,"jitterBufferEmittedCount");return S!==void 0&&B!==void 0&&B>0?S/B*1000:void 0}).filter(($)=>$!==void 0)),q=D.map(($)=>K($,"audioLevel")).filter(($)=>$!==void 0);if(V.requireConnectedCandidatePair&&_.length>0&&X===0)z(C,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(V.requireLiveAudioTrack&&H===0)z(C,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(V.maxPacketLossRatio!==void 0&&J>V.maxPacketLossRatio)z(C,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(J)} above ${String(V.maxPacketLossRatio)}.`);if(V.maxRoundTripTimeMs!==void 0&&Q!==void 0&&Q>V.maxRoundTripTimeMs)z(C,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(Q)}ms above ${String(V.maxRoundTripTimeMs)}ms.`);if(V.maxJitterMs!==void 0&&G!==void 0&&G>V.maxJitterMs)z(C,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(G)}ms above ${String(V.maxJitterMs)}ms.`);return{activeCandidatePairs:X,audioLevelAverage:P0(q),bytesReceived:h,bytesSent:Z,checkedAt:Date.now(),endedAudioTracks:O,inboundPackets:L,issues:C,jitterBufferDelayMs:c,jitterMs:G,liveAudioTracks:H,outboundPackets:W,packetLossRatio:J,packetsLost:I,roundTripTimeMs:Q,status:C.some(($)=>$.severity==="error")?"fail":C.length>0?"warn":"pass",totalStats:g.length}},m=async(V)=>{return[...(await V.peerConnection.getStats(V.selector??null)).values()].map(B0)};var u=(V={})=>{let g=V.stats??[],C=V.previousStats??[],A=[],R=new Map(C.map((I)=>[F(I),I])),D=g.filter((I)=>(I.type==="inbound-rtp"||I.type==="outbound-rtp")&&E(I,"kind")!=="video"&&E(I,"mediaType")!=="video").map((I)=>{let T=I.type==="outbound-rtp"?"outbound":"inbound",J=T==="outbound"?"packetsSent":"packetsReceived",h=T==="outbound"?"bytesSent":"bytesReceived",Z=R.get(F(I)),Q=K(I,J),G=Z?K(Z,J):void 0,c=K(I,h),q=Z?K(Z,h):void 0,$=I.timestamp!==void 0&&Z?.timestamp!==void 0?I.timestamp-Z.timestamp:void 0;return{bytesDelta:c!==void 0&&q!==void 0?c-q:void 0,currentPackets:Q,direction:T,id:F(I),packetDelta:Q!==void 0&&G!==void 0?Q-G:void 0,previousPackets:G,timeDeltaMs:$}}),X=D.filter((I)=>I.direction==="inbound"),H=D.filter((I)=>I.direction==="outbound"),O=j(D.map((I)=>I.timeDeltaMs).filter((I)=>I!==void 0)),L=X.filter((I)=>V.maxInboundPacketStallMs!==void 0&&I.timeDeltaMs!==void 0&&I.timeDeltaMs>=V.maxInboundPacketStallMs&&I.packetDelta!==void 0&&I.packetDelta<=0).length,W=H.filter((I)=>V.maxOutboundPacketStallMs!==void 0&&I.timeDeltaMs!==void 0&&I.timeDeltaMs>=V.maxOutboundPacketStallMs&&I.packetDelta!==void 0&&I.packetDelta<=0).length;if(V.requireInboundAudio&&X.length===0)z(A,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(V.requireOutboundAudio&&H.length===0)z(A,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(V.maxGapMs!==void 0&&O!==void 0&&O>V.maxGapMs)z(A,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(O)}ms above ${String(V.maxGapMs)}ms.`);if(L>0)z(A,"error","media.webrtc_inbound_stalled",`${String(L)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(W>0)z(A,"error","media.webrtc_outbound_stalled",`${String(W)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:X.length,issues:A,maxObservedGapMs:O,outboundAudioStreams:H.length,stalledInboundStreams:L,stalledOutboundStreams:W,status:A.some((I)=>I.severity==="error")?"fail":A.length>0?"warn":"pass",streams:D,totalStats:g.length}};var M0="/api/voice/browser-media",b0=5000,j0=async(V)=>V.peerConnection??await V.getPeerConnection?.()??null,x0=async(V,g)=>{let C=g.fetch??globalThis.fetch;if(!C)return;await C(g.path??M0,{body:JSON.stringify(V),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},r=(V)=>{let g=null,C=[],A=async()=>{let D=await j0(V);if(!D)return;let X=await m({peerConnection:D}),H=n({...V,stats:X}),O=V.continuity===!1?void 0:u({...V.continuity,previousStats:C,stats:X}),L={at:Date.now(),continuity:O,report:H,scenarioId:V.getScenarioId?.()??null,sessionId:V.getSessionId?.()??null};return C=X,V.onReport?.(L),await x0(L,V),L},R=()=>{A().catch((D)=>{V.onError?.(D)})},_=()=>{if(g)clearInterval(g),g=null};return{close:_,reportOnce:A,stop:_,start:()=>{if(g)return;R(),g=setInterval(R,V.intervalMs??b0)}}};var k0=(V,g,C)=>Math.min(C,g*2**(Math.max(1,V)-1));class s extends Error{constructor(V="Voice authorization could not be renewed."){super(V);this.name="VoiceReconnectRejectedError"}}var P=()=>{},F0=()=>Promise.resolve(),f0=()=>P,l0={callControl:F0,close:P,disconnect:P,endTurn:P,send:P,sendAudio:P,simulateDisconnect:P,subscribe:f0,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",start:()=>{}},d0=()=>crypto.randomUUID(),v0=(V,g,C,A)=>{let{hostname:R,port:_,protocol:D}=window.location,X=D==="https:"?"wss:":"ws:",H=_?`:${_}`:"",O=new URL(`${X}//${R}${H}${V}`);if(O.searchParams.set("sessionId",g),C)O.searchParams.set("scenarioId",C);for(let[L,W]of Object.entries(A))if(L!=="sessionId"&&L!=="scenarioId"&&W)O.searchParams.set(L,W);return O.toString()},i0=(V)=>{if(!V||typeof V!=="object"||!("type"in V))return!1;switch(V.type){case"audio":case"assistant":case"call_lifecycle":case"call_control_ack":case"complete":case"connection":case"error":case"final":case"partial":case"pong":case"replay":case"session":case"turn":return!0;default:return!1}},o0=(V)=>{if(typeof V.data!=="string")return null;try{let g=JSON.parse(V.data);return i0(g)?g:null}catch{return null}},a=(V,g={})=>{if(typeof window>"u")return l0;let C=new Set,A=new Map,R=g.reconnect!==!1,_=g.maxReconnectAttempts??15,D=g.reconnectMaxDelayMs??8000,X=g.pingInterval??30000,H=g.reconnectResetAfterMs??30000,O=0,L=!1,W=null,I=(U)=>k0(U,500,D),T={isConnected:!1,pendingMessages:[],scenarioId:g.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectResetTimeout:null,reconnectTimeout:null,sessionId:g.sessionId??d0(),ws:null},J=(U)=>{C.forEach((w)=>w(U))},h=(U)=>{C.forEach((w)=>w({message:U,recoverable:!1,type:"error"})),J({reconnect:{attempts:T.reconnectAttempts,maxAttempts:_,status:"exhausted"},type:"connection"})},Z=()=>{if(T.pingInterval)clearInterval(T.pingInterval),T.pingInterval=null;if(T.reconnectTimeout)clearTimeout(T.reconnectTimeout),T.reconnectTimeout=null;if(T.reconnectResetTimeout)clearTimeout(T.reconnectResetTimeout),T.reconnectResetTimeout=null},Q=()=>{if(T.ws?.readyState!==1)return;while(T.pendingMessages.length>0){let U=T.pendingMessages.shift();if(U!==void 0)T.ws.send(U)}},G=(U)=>{for(let w of A.values())clearTimeout(w.timeout),w.reject(Error(U));A.clear()},c=()=>{if(L)return;let U=O;T.reconnectAttempts+=1;let w=I(T.reconnectAttempts),y=Date.now()+w;J({reconnect:{attempts:T.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:_,nextAttemptAt:y,status:"reconnecting"},type:"connection"}),T.reconnectTimeout=setTimeout(async()=>{if(L||O!==U)return;if(T.reconnectAttempts>_){h("Voice connection could not be restored.");return}try{if(g.prepareReconnect){let Y=new AbortController;W=Y;let M=setTimeout(()=>Y.abort("Voice reconnect preparation timed out"),g.prepareReconnectTimeoutMs??1e4);try{await Promise.race([g.prepareReconnect({attempt:T.reconnectAttempts,path:V,scenarioId:T.scenarioId,sessionId:T.sessionId,signal:Y.signal}),new Promise((f,O0)=>{Y.signal.addEventListener("abort",()=>O0(Error("Voice reconnect preparation timed out.")),{once:!0})})])}finally{if(clearTimeout(M),W===Y)W=null}}}catch(Y){if(L||O!==U)return;if(Y instanceof s){h(Y.message);return}if(T.reconnectAttempts<_)c();else h("Voice authorization could not be renewed for reconnect.");return}if(L||O!==U)return;q()},w)},q=()=>{if(L)return;let U=new WebSocket(v0(V,T.sessionId,T.scenarioId,g.query??{}));U.binaryType="arraybuffer",U.onopen=()=>{if(T.ws!==U){U.close(1000);return}let w=T.reconnectAttempts>0;if(T.isConnected=!0,Q(),w)J({reconnect:{attempts:T.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:_,status:"resumed"},type:"connection"}),T.reconnectResetTimeout=setTimeout(()=>{if(T.ws===U&&U.readyState===1)T.reconnectAttempts=0;T.reconnectResetTimeout=null},H);C.forEach((y)=>y({scenarioId:T.scenarioId??void 0,sessionId:T.sessionId,status:"active",type:"session"})),T.pingInterval=setInterval(()=>{if(U.readyState===1)U.send(JSON.stringify({type:"ping"}))},X)},U.onmessage=(w)=>{if(T.ws!==U)return;let y=o0(w);if(!y)return;if(y.type==="session")T.sessionId=y.sessionId,T.scenarioId=y.scenarioId??T.scenarioId;if(y.type==="call_control_ack"){let Y=A.get(y.requestId);if(Y)if(clearTimeout(Y.timeout),A.delete(y.requestId),y.ok)Y.resolve();else Y.reject(Error(y.message??`${y.action} was not applied`))}C.forEach((Y)=>Y(y))},U.onclose=(w)=>{if(T.ws!==U)return;T.ws=null,T.isConnected=!1,Z(),G("Voice connection interrupted");let y=R&&w.code!==1000&&w.code!==4401&&w.code!==4403&&T.reconnectAttempts<_,Y=w.code===4401||w.code===4403;if(Y)C.forEach((M)=>M({message:w.code===4401?"Voice authorization expired or was rejected.":"Voice authorization was forbidden.",recoverable:!1,type:"error"}));if(y)c();else if(R&&w.code!==1000)if(Y)J({reconnect:{attempts:T.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:_,status:"exhausted"},type:"connection"});else h("Voice connection could not be restored.")},T.ws=U},$=(U)=>{if(L)return;if(T.ws?.readyState===1){T.ws.send(U);return}if(typeof U!=="string")return;if(T.pendingMessages.length>=100)T.pendingMessages.shift();T.pendingMessages.push(U)},S=(U)=>{$(JSON.stringify(U))},B=(U={})=>{if(U.sessionId)T.sessionId=U.sessionId;if(U.scenarioId)T.scenarioId=U.scenarioId;S({scenarioId:T.scenarioId??void 0,sessionId:T.sessionId,type:"start"})},I0=(U)=>{$(U)},U0=()=>{S({type:"end_turn"})},L0=(U)=>{if(T.ws?.readyState!==1)return Promise.reject(Error(`${U.action} is unavailable while disconnected`));let w=crypto.randomUUID(),{promise:y,reject:Y,resolve:M}=Promise.withResolvers(),f=setTimeout(()=>{A.delete(w),Y(Error(`${U.action} acknowledgement timed out`))},5000);return A.set(w,{reject:Y,resolve:M,timeout:f}),T.ws.send(JSON.stringify({...U,requestId:w,type:"call_control"})),y},W0=(U="client-close")=>{if(L=!0,W?.abort(),W=null,O+=1,Z(),G("Voice connection closed"),T.pendingMessages.length=0,T.ws){let w=T.ws;if(T.ws=null,w.readyState===1)w.send(JSON.stringify({reason:U,type:"close"}));w.close(1000)}T.isConnected=!1,C.clear()},D0=()=>{if(L=!0,W?.abort(),W=null,O+=1,Z(),G("Voice connection disconnected"),T.pendingMessages.length=0,T.ws){let U=T.ws;T.ws=null,U.close(1000)}T.isConnected=!1,C.clear()},X0=()=>{if(T.ws?.readyState===1)T.ws.close(4000,"absolutejs-voice-reconnect-proof")},G0=(U)=>{return C.add(U),()=>{C.delete(U)}};return q(),{callControl:L0,close:W0,disconnect:D0,endTurn:U0,send:S,sendAudio:I0,simulateDisconnect:X0,start:B,subscribe:G0,getReadyState:()=>T.ws?.readyState??3,getScenarioId:()=>T.scenarioId??"",getSessionId:()=>T.sessionId}};var n0=()=>({attempts:0,maxAttempts:0,status:"idle"}),m0=(V,g)=>{let C=g.trim().replace(/\s+/g," ");if(!C)return V;if(!V)return C;if(V===C||V.endsWith(C))return V;if(C.includes(V))return C;return`${V} ${C}`},u0=(V,g)=>{let C=g.trim().replace(/\s+/g," ");if(!V)return C;if(!C||V.endsWith(C))return V;return`${V} ${C}`},r0=()=>({assistantAudio:[],assistantStreamingText:"",assistantTexts:[],call:null,error:null,isConnected:!1,partial:"",reconnect:n0(),scenarioId:null,sessionId:null,sessionMetadata:null,status:"idle",turns:[]}),p=()=>{let V=r0(),g="",C=new Set,A=()=>{C.forEach((_)=>_())};return{dispatch:(_)=>{switch(_.type){case"audio":V={...V,assistantAudio:[...V.assistantAudio,{chunk:_.chunk,format:_.format,receivedAt:_.receivedAt,turnId:_.turnId}]};break;case"assistant":V={...V,assistantStreamingText:"",assistantTexts:[...V.assistantTexts,_.text]};break;case"assistant_delta":V={...V,assistantStreamingText:`${V.assistantStreamingText}${_.delta}`};break;case"complete":V={...V,sessionId:_.sessionId,status:"completed"};break;case"call_lifecycle":V={...V,call:{...V.call,disposition:_.event.type==="end"?_.event.disposition:V.call?.disposition,endedAt:_.event.type==="end"?_.event.at:V.call?.endedAt,events:[...V.call?.events??[],_.event],lastEventAt:_.event.at,startedAt:V.call?.startedAt??_.event.at},sessionId:_.sessionId};break;case"connected":V={...V,isConnected:!0,reconnect:V.reconnect.status==="reconnecting"?{...V.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:V.reconnect};break;case"connection":V={...V,reconnect:_.reconnect};break;case"disconnected":V={...V,isConnected:!1};break;case"error":V={...V,error:_.message,..._.recoverable===!1?{isConnected:!1,status:"failed"}:{}};break;case"final":g=m0(g,_.transcript.text),V={...V,partial:g};break;case"partial":V={...V,partial:u0(g,_.transcript.text)};break;case"replay":g=_.partial,V={...V,assistantStreamingText:"",assistantTexts:[..._.assistantTexts],call:_.call??null,error:null,isConnected:_.status==="active",partial:_.partial,reconnect:V.reconnect.status==="reconnecting"?{...V.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:V.reconnect,scenarioId:_.scenarioId??V.scenarioId,sessionId:_.sessionId,sessionMetadata:_.sessionMetadata??V.sessionMetadata,status:_.status,turns:[..._.turns]};break;case"session":V={...V,error:null,scenarioId:_.scenarioId??V.scenarioId,isConnected:_.status==="active",sessionId:_.sessionId,sessionMetadata:_.sessionMetadata??V.sessionMetadata,status:_.status};break;case"turn":g="",V={...V,partial:"",turns:[...V.turns,_.turn]};break}A()},getServerSnapshot:()=>V,getSnapshot:()=>V,subscribe:(_)=>{return C.add(_),()=>{C.delete(_)}}}};var t=(V,g={})=>{let C=a(V,g),A=p(),R=g.browserMedia&&typeof window<"u"?r({...g.browserMedia,getScenarioId:()=>g.browserMedia?g.browserMedia.getScenarioId?.()??C.getScenarioId():C.getScenarioId(),getSessionId:()=>g.browserMedia?g.browserMedia.getSessionId?.()??C.getSessionId():C.getSessionId()}):null,_=new Set,D=!1,X=(W)=>Promise.resolve().then(()=>{if(D)return;if(!W?.sessionId&&!W?.scenarioId)return;C.start(W),R?.start()}),H=()=>{_.forEach((W)=>W())},O=()=>{if(!g.reconnectReportPath||typeof fetch>"u")return;let W=A.getSnapshot(),I=JSON.stringify({at:Date.now(),reconnect:W.reconnect,scenarioId:W.scenarioId,sessionId:C.getSessionId(),turnIds:W.turns.map((T)=>T.id)});fetch(g.reconnectReportPath,{body:I,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},L=C.subscribe((W)=>{let I=i(W);if(I){if(A.dispatch(I),W.type==="connection")O();H()}});return{start:X,get assistantAudio(){return A.getSnapshot().assistantAudio},get assistantTexts(){return A.getSnapshot().assistantTexts},get assistantStreamingText(){return A.getSnapshot().assistantStreamingText},get call(){return A.getSnapshot().call},callControl(W){return C.callControl(W)},close(W){D=!0,L(),R?.close(),C.close(W),A.dispatch({type:"disconnected"}),H()},disconnect(){D=!0,L(),R?.close(),C.disconnect(),A.dispatch({type:"disconnected"}),H()},endTurn(){C.endTurn()},get error(){return A.getSnapshot().error},getServerSnapshot(){return A.getServerSnapshot()},getSnapshot(){return A.getSnapshot()},get isConnected(){return A.getSnapshot().isConnected},get partial(){return A.getSnapshot().partial},get reconnect(){return A.getSnapshot().reconnect},get scenarioId(){return A.getSnapshot().scenarioId},sendAudio(W){C.sendAudio(W)},get sessionId(){return C.getSessionId()},get sessionMetadata(){return A.getSnapshot().sessionMetadata},simulateDisconnect(){C.simulateDisconnect()},get status(){return A.getSnapshot().status},subscribe(W){return _.add(W),()=>{_.delete(W)}},get turns(){return A.getSnapshot().turns}}};var e=(V)=>{if(!V||V.enabled===!1)return;return{enabled:!0,maxGain:V.maxGain??3,noiseGateAttenuation:V.noiseGateAttenuation??0.15,noiseGateThreshold:V.noiseGateThreshold??0.006,targetLevel:V.targetLevel??0.08}};var s0={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}},a0={"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 V0=(V)=>{let g=V?.profile??"fast",C=V?.qualityProfile??"general",A=s0[g],R=a0[C],_=V?.silenceMs??R.silenceMs??A.silenceMs;return{profile:g,qualityProfile:C,minSilenceMs:Math.min(_,V?.minSilenceMs??R.minSilenceMs??A.minSilenceMs),silenceMs:_,speechThreshold:V?.speechThreshold??R.speechThreshold??A.speechThreshold,transcriptStabilityMs:V?.transcriptStabilityMs??R.transcriptStabilityMs??A.transcriptStabilityMs}};var p0={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"}}},g0=(V="default")=>{let g=p0[V];return{audioConditioning:e(g.audioConditioning),capture:{channelCount:g.capture?.channelCount??1,sampleRateHz:g.capture?.sampleRateHz??16000},connection:{...g.connection},name:V,sttLifecycle:g.sttLifecycle??"continuous",turnDetection:V0(g.turnDetection)}};var t0=(V)=>({assistantAudio:[...V.assistantAudio],assistantStreamingText:V.assistantStreamingText,assistantTexts:[...V.assistantTexts],call:V.call,error:V.error,isConnected:V.isConnected,isRecording:!1,partial:V.partial,reconnect:V.reconnect,recordingError:null,sessionId:V.sessionId,sessionMetadata:V.sessionMetadata,scenarioId:V.scenarioId,status:V.status,turns:[...V.turns]}),A0=(V,g={})=>{let C=g0(g.preset),A=t(V,{...C.connection,...g.connection}),R=null,_=0,D=!1,X=null,H=t0(A),O=new Set,L=()=>{for(let G of O)G()},W=()=>{if(H={...H,assistantAudio:[...A.assistantAudio],assistantStreamingText:A.assistantStreamingText,assistantTexts:[...A.assistantTexts],call:A.call,error:A.error,isConnected:A.isConnected,partial:A.partial,reconnect:A.reconnect,sessionId:A.sessionId,sessionMetadata:A.sessionMetadata,scenarioId:A.scenarioId,status:A.status,turns:[...A.turns]},g.autoStopOnComplete!==!1&&H.status==="completed"&&H.isRecording)R?.stop(),R=null,H={...H,isRecording:!1};L()},I=A.subscribe(W);W();let T=()=>{if(R)return R;return R=v({channelCount:g.capture?.channelCount??C.capture.channelCount,onLevel:g.capture?.onLevel,onAudio:(G)=>{if(g.capture?.onAudio){g.capture.onAudio(G,A.sendAudio);return}A.sendAudio(G)},sampleRateHz:g.capture?.sampleRateHz??C.capture.sampleRateHz,...g.capture?.stream?{stream:g.capture.stream}:{}}),R},J=()=>{_+=1,R?.stop(),R=null,H={...H,isRecording:!1},L()},h=async()=>{if(D)throw Error("Voice controller is closed.");if(H.isRecording)return;if(X)return X;let G=++_,c=T(),$=(async()=>{try{if(H={...H,recordingError:null},L(),await c.start(),D||G!==_||R!==c){c.stop();return}H={...H,isRecording:!0},L()}catch(S){if(c.stop(),G!==_||R!==c)throw S;throw R=null,H={...H,isRecording:!1,recordingError:S instanceof Error?S.message:String(S)},L(),S}})().finally(()=>{if(X===$)X=null});return X=$,X};return{close:(G)=>{D=!0,I(),J(),A.close(G)},disconnect:()=>{D=!0,I(),J(),A.disconnect()},startRecording:h,stopRecording:J,get assistantAudio(){return H.assistantAudio},get assistantTexts(){return H.assistantTexts},get assistantStreamingText(){return H.assistantStreamingText},bindHTMX(G){return d(A,G)},get call(){return H.call},callControl:(G)=>A.callControl(G),endTurn:()=>A.endTurn(),get error(){return H.error},getServerSnapshot:()=>H,getSnapshot:()=>H,get isConnected(){return H.isConnected},get isRecording(){return H.isRecording},get partial(){return H.partial},get reconnect(){return H.reconnect},get recordingError(){return H.recordingError},get scenarioId(){return H.scenarioId},sendAudio:(G)=>A.sendAudio(G),get sessionId(){return H.sessionId},get sessionMetadata(){return H.sessionMetadata},simulateDisconnect:()=>A.simulateDisconnect(),get status(){return H.status},subscribe:(G)=>{return O.add(G),()=>{O.delete(G)}},toggleRecording:async()=>{if(H.isRecording){J();return}await h()},get turns(){return H.turns}}};var N=(V)=>String(V).replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;");var C0=(V)=>{if(!V.isConnected)return"idle";if(V.isPlaying)return"speaking";if(V.isRecording&&V.hasActivePartial)return"listening";if(V.isRecording)return"listening";if(V.lastTranscriptAt&&!V.lastAssistantAt)return"thinking";if(V.lastTranscriptAt&&V.lastAssistantAt&&V.lastTranscriptAt>V.lastAssistantAt)return"thinking";return"idle"};var e0={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},VV={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},gV=(V,g)=>{switch(V){case"listening":return g.listening;case"speaking":return g.speaking;case"thinking":return g.thinking;case"idle":return g.idle}},_0=(V)=>{let g={...e0,...V.theme},C={...VV,...V.labels},A=V.state.assistantAudio.at(-1)?.receivedAt,R=V.state.turns.at(-1)?.committedAt,_=C0({hasActivePartial:V.state.partial.length>0,isConnected:V.state.isConnected,isPlaying:!1,isRecording:V.state.isRecording,lastAssistantAt:A,lastTranscriptAt:R}),D=!V.state.isConnected&&V.state.status!=="idle"&&!V.state.error,X=V.state.error?"Error":D?C.connecting:V.state.status==="completed"?C.callEnded:gV(_,C);return{agentState:_,classes:{container:`absolute-voice-widget absolute-voice-widget--${_}`,dot:`absolute-voice-widget__dot${V.state.error?" absolute-voice-widget__dot--error":""}`},controls:{canEnd:V.state.isConnected,canMute:V.state.isRecording,canStart:!V.state.isRecording&&V.state.status!=="completed"},errorMessage:V.state.error??void 0,labels:C,partial:V.state.partial||void 0,statusLabel:X,theme:g,title:V.title??"Voice"}},AV=(V)=>typeof V==="number"?`${V}px`:V,T0=(V)=>{let g=V.theme,C=`background:${g.background};border-radius:${AV(g.radius)};color:${g.foreground};font-family:${g.fontFamily};min-width:240px;padding:20px 22px;`,A=`background:${V.errorMessage?g.errorAccent:V.agentState==="idle"?"rgba(148,163,184,0.6)":g.accent};border-radius:50%;height:10px;width:10px;`,R=[];if(V.controls.canStart)R.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;">${N(V.labels.startCall)}</button>`);if(V.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:${g.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${N(V.labels.mute)}</button>`);if(V.controls.canEnd)R.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;">${N(V.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${V.agentState}" class="${N(V.classes.container)}" style="${C}">
2
2
  <div style="align-items:center;display:flex;gap:10px;margin-bottom:12px;">
3
- <span aria-hidden="true" class="${N(g.classes.dot)}" style="${V}"></span>
4
- <strong style="font-size:15px;">${N(g.title)}</strong>
5
- <span style="font-size:13px;margin-left:auto;opacity:0.7;">${N(g.statusLabel)}</span>
3
+ <span aria-hidden="true" class="${N(V.classes.dot)}" style="${A}"></span>
4
+ <strong style="font-size:15px;">${N(V.title)}</strong>
5
+ <span style="font-size:13px;margin-left:auto;opacity:0.7;">${N(V.statusLabel)}</span>
6
6
  </div>
7
- ${g.partial?`<p style="font-size:13px;margin:8px 0 12px;opacity:0.85;word-break:break-word;">“${N(g.partial)}”</p>`:""}
8
- <div style="display:flex;gap:10px;">${$.join("")}</div>
9
- ${g.errorMessage?`<p style="color:${A.errorAccent};font-size:12px;margin-top:12px;">${N(g.errorMessage)}</p>`:""}
10
- </div>`};var Ag=(g)=>{if(typeof g!=="string")return g;let A=document.querySelector(g);if(!A)throw Error(`AbsoluteVoice.mount: no element matches "${g}"`);return A},T0=(g,A={})=>{let C=Ag(g),V=A0(A.path??"/voice",A.controllerOptions),$=null,_=null,L=()=>{let c=C0({...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=_0(c);for(let W of C.querySelectorAll("button[data-action]")){let{action:R}=W.dataset;W.addEventListener("click",()=>{if(R==="start")V.startRecording();else if(R==="mute")V.stopRecording();else if(R==="end")V.close()})}if(V.error&&V.error!==$)$=V.error,A.onError?.(V.error);if(V.status!==_)_=V.status,A.onStatusChange?.(V.status)},U=V.subscribe(L);if(L(),A.autoStart)V.startRecording();return{controller:V,async end(){await V.close()},mute(){V.stopRecording()},async start(){await V.startRecording()},unmount(){U(),V.close(),C.innerHTML=""}}},$0="0.0.22-beta.516",H0={mount:T0,version:$0};if(typeof globalThis<"u")globalThis.AbsoluteVoice=H0;var Cg=H0;})();
7
+ ${V.partial?`<p style="font-size:13px;margin:8px 0 12px;opacity:0.85;word-break:break-word;">“${N(V.partial)}”</p>`:""}
8
+ <div style="display:flex;gap:10px;">${R.join("")}</div>
9
+ ${V.errorMessage?`<p style="color:${g.errorAccent};font-size:12px;margin-top:12px;">${N(V.errorMessage)}</p>`:""}
10
+ </div>`};var CV=(V)=>{if(typeof V!=="string")return V;let g=document.querySelector(V);if(!g)throw Error(`AbsoluteVoice.mount: no element matches "${V}"`);return g},$0=(V,g={})=>{let C=CV(V),A=A0(g.path??"/voice",g.controllerOptions),R=null,_=null,D=()=>{let H=_0({...g.labels!==void 0?{labels:g.labels}:{},state:{assistantAudio:A.assistantAudio,error:A.error,isConnected:A.isConnected,isRecording:A.isRecording,partial:A.partial,status:A.status,turns:A.turns},...g.theme!==void 0?{theme:g.theme}:{},...g.title!==void 0?{title:g.title}:{}});C.innerHTML=T0(H);for(let O of C.querySelectorAll("button[data-action]")){let{action:L}=O.dataset;O.addEventListener("click",()=>{if(L==="start")A.startRecording();else if(L==="mute")A.stopRecording();else if(L==="end")A.close()})}if(A.error&&A.error!==R)R=A.error,g.onError?.(A.error);if(A.status!==_)_=A.status,g.onStatusChange?.(A.status)},X=A.subscribe(D);if(D(),g.autoStart)A.startRecording();return{controller:A,async end(){await A.close()},mute(){A.stopRecording()},async start(){await A.startRecording()},unmount(){X(),A.close(),C.innerHTML=""}}},R0="0.0.22-beta.516",H0={mount:$0,version:R0};if(typeof globalThis<"u")globalThis.AbsoluteVoice=H0;var TV=H0;})();