@absolutejs/voice 0.0.22-beta.565 → 0.0.22-beta.567

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.
@@ -181,8 +181,15 @@ var serverMessageToAction = (message) => {
181
181
  case "assistant":
182
182
  return {
183
183
  text: message.text,
184
+ turnId: message.turnId,
184
185
  type: "assistant"
185
186
  };
187
+ case "assistant_delta":
188
+ return {
189
+ delta: message.delta,
190
+ turnId: message.turnId,
191
+ type: "assistant_delta"
192
+ };
186
193
  case "complete":
187
194
  return {
188
195
  sessionId: message.sessionId,
@@ -750,6 +757,7 @@ var createInitialReconnectState = () => ({
750
757
  });
751
758
  var createInitialState = () => ({
752
759
  assistantAudio: [],
760
+ assistantStreamingText: "",
753
761
  assistantTexts: [],
754
762
  call: null,
755
763
  error: null,
@@ -787,9 +795,16 @@ var createVoiceStreamStore = () => {
787
795
  case "assistant":
788
796
  state = {
789
797
  ...state,
798
+ assistantStreamingText: "",
790
799
  assistantTexts: [...state.assistantTexts, action.text]
791
800
  };
792
801
  break;
802
+ case "assistant_delta":
803
+ state = {
804
+ ...state,
805
+ assistantStreamingText: `${state.assistantStreamingText}${action.delta}`
806
+ };
807
+ break;
793
808
  case "complete":
794
809
  state = {
795
810
  ...state,
@@ -857,6 +872,7 @@ var createVoiceStreamStore = () => {
857
872
  case "replay":
858
873
  state = {
859
874
  ...state,
875
+ assistantStreamingText: "",
860
876
  assistantTexts: [...action.assistantTexts],
861
877
  call: action.call ?? null,
862
878
  error: null,
@@ -968,6 +984,9 @@ var createVoiceStream = (path, options = {}) => {
968
984
  get assistantTexts() {
969
985
  return store.getSnapshot().assistantTexts;
970
986
  },
987
+ get assistantStreamingText() {
988
+ return store.getSnapshot().assistantStreamingText;
989
+ },
971
990
  get call() {
972
991
  return store.getSnapshot().call;
973
992
  },
@@ -1314,6 +1333,7 @@ var resolveVoiceRuntimePreset = (name = "default") => {
1314
1333
  // src/client/controller.ts
1315
1334
  var createInitialState2 = (stream) => ({
1316
1335
  assistantAudio: [...stream.assistantAudio],
1336
+ assistantStreamingText: stream.assistantStreamingText,
1317
1337
  assistantTexts: [...stream.assistantTexts],
1318
1338
  call: stream.call,
1319
1339
  error: stream.error,
@@ -1346,6 +1366,7 @@ var createVoiceController = (path, options = {}) => {
1346
1366
  state = {
1347
1367
  ...state,
1348
1368
  assistantAudio: [...stream.assistantAudio],
1369
+ assistantStreamingText: stream.assistantStreamingText,
1349
1370
  assistantTexts: [...stream.assistantTexts],
1350
1371
  call: stream.call,
1351
1372
  error: stream.error,
@@ -1439,6 +1460,9 @@ var createVoiceController = (path, options = {}) => {
1439
1460
  get assistantTexts() {
1440
1461
  return state.assistantTexts;
1441
1462
  },
1463
+ get assistantStreamingText() {
1464
+ return state.assistantStreamingText;
1465
+ },
1442
1466
  bindHTMX(bindingOptions) {
1443
1467
  return bindVoiceHTMX(stream, bindingOptions);
1444
1468
  },
@@ -1,10 +1,10 @@
1
- (()=>{var{defineProperty:H,getOwnPropertyNames:oc,getOwnPropertyDescriptor:lc}=Object,Ac=Object.prototype.hasOwnProperty;function Cc(c){return this[c]}var ic=(c)=>{var n=(Y??=new WeakMap).get(c),o;if(n)return n;if(n=H({},"__esModule",{value:!0}),c&&typeof c==="object"||typeof c==="function"){for(var g of oc(c))if(!Ac.call(n,g))H(n,g,{get:Cc.bind(c,g),enumerable:!(o=lc(c,g))||o.enumerable})}return Y.set(c,n),n},Y;var yc=(c)=>c;function Tc(c,n){this[c]=yc.bind(null,n)}var Vc=(c,n)=>{for(var o in n)H(c,o,{get:n[o],enumerable:!0,configurable:!0,set:Tc.bind(n,o)})};var Bc={};Vc(Bc,{mount:()=>p,default:()=>Kc,VOICE_EMBED_VERSION:()=>cc});var Ic=(c)=>{if(typeof c!=="string")return c;return document.querySelector(c)},Rc=(c,n,o,g)=>{let l=n??c.getAttribute("hx-get")??"";if(!l)return"";let i=new URL(l,window.location.origin);if(g)i.searchParams.set(o,g);else i.searchParams.delete(o);return`${i.pathname}${i.search}${i.hash}`},Q=(c,n)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let o=Ic(n.element);if(!o)return()=>{};let g=n.eventName??"voice-refresh",l=n.sessionQueryParam??"sessionId",i=()=>{let V=window,I=Rc(o,n.route,l,c.sessionId);if(I)o.setAttribute("hx-get",I);V.htmx?.process?.(o),V.htmx?.trigger?.(o,g)},C=c.subscribe(i);return i(),()=>{C()}};var hc=(c)=>Math.max(-1,Math.min(1,c)),Sc=(c)=>{let n=new Int16Array(c.length);for(let o=0;o<c.length;o+=1){let g=hc(c[o]??0);n[o]=g<0?g*32768:g*32767}return new Uint8Array(n.buffer)},dc=(c)=>{let n=c instanceof Uint8Array?c:new Uint8Array(c);if(n.byteLength<2)return 0;let o=new Int16Array(n.buffer,n.byteOffset,Math.floor(n.byteLength/2));if(o.length===0)return 0;let g=0;for(let l of o){let i=l/32768;g+=i*i}return Math.min(1,Math.max(0,Math.sqrt(g/o.length)*5.5))},_c=(c,n,o)=>{if(n===o)return c;let g=n/o,l=Math.round(c.length/g),i=new Float32Array(l),C=0,V=0;while(C<i.length){let I=Math.round((C+1)*g),S=0,T=0;for(let h=V;h<I&&h<c.length;h+=1)S+=c[h]??0,T+=1;i[C]=T>0?S/T:0,C+=1,V=I}return i},J=(c)=>{let n=null,o=null,g=null,l=null;return{start:async()=>{if(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia)throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let V=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!V)throw Error("Browser microphone capture requires AudioContext support.");l=await navigator.mediaDevices.getUserMedia({audio:{channelCount:c.channelCount??1}}),n=new V,o=n.createMediaStreamSource(l),g=n.createScriptProcessor(4096,1,1),g.onaudioprocess=(I)=>{let S=I.inputBuffer.getChannelData(0),T=_c(S,n?.sampleRate??48000,c.sampleRateHz??16000),h=Sc(T);c.onLevel?.(dc(h)),c.onAudio(h)},o.connect(g),g.connect(n.destination)},stop:()=>{g?.disconnect(),o?.disconnect(),l?.getTracks().forEach((V)=>V.stop()),n?.close(),c.onLevel?.(0),n=null,l=null,g=null,o=null}}};var G=(c)=>{if(typeof c==="string"&&c.trim())return c;if(c instanceof Error&&c.message.trim())return c.message;if(c&&typeof c==="object"){let n=c;for(let o of["message","reason","description"]){let g=n[o];if(typeof g==="string"&&g.trim())return g}if("error"in n)return G(n.error);if("cause"in n)return G(n.cause);try{return JSON.stringify(c)}catch{}}return"Unexpected error"},q=(c)=>{switch(c.type){case"audio":return{chunk:Uint8Array.from(atob(c.chunkBase64),(n)=>n.charCodeAt(0)),format:c.format,receivedAt:c.receivedAt,turnId:c.turnId,type:"audio"};case"assistant":return{text:c.text,type:"assistant"};case"complete":return{sessionId:c.sessionId,type:"complete"};case"connection":return{reconnect:c.reconnect,type:"connection"};case"call_lifecycle":return{event:c.event,sessionId:c.sessionId,type:"call_lifecycle"};case"error":return{message:G(c.message),type:"error"};case"final":return{transcript:c.transcript,type:"final"};case"partial":return{transcript:c.transcript,type:"partial"};case"replay":return{assistantTexts:c.assistantTexts,call:c.call,partial:c.partial,scenarioId:c.scenarioId,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,status:c.status,turns:c.turns,type:"replay"};case"session":return{sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,scenarioId:c.scenarioId,status:c.status,type:"session"};case"turn":return{turn:c.turn,type:"turn"};default:return null}};var tc=Math.PI*2;var D=(c,n,o,g)=>{c.push({code:o,message:g,severity:n})};var wc=(c)=>c.length===0?void 0:c.reduce((n,o)=>n+o,0)/c.length,$=(c)=>c.length===0?void 0:Math.max(...c);var _=(c,n)=>{let o=c[n];return typeof o==="number"&&Number.isFinite(o)?o:void 0},N=(c,n)=>{let o=c[n];return typeof o==="boolean"?o:void 0},w=(c,n)=>{let o=c[n];return typeof o==="string"?o:void 0},X=(c)=>String(c.id??w(c,"ssrc")??_(c,"ssrc")??w(c,"trackIdentifier")??w(c,"mid")??"unknown"),Z=(c)=>c===void 0?void 0:c*1000;var Lc=(c)=>{let n={};for(let[o,g]of Object.entries(c))if(g===null||typeof g==="boolean"||typeof g==="number"||typeof g==="string")n[o]=g;return n};var z=(c={})=>{let n=c.stats??[],o=[],g=n.filter((A)=>A.type==="inbound-rtp"&&w(A,"kind")!=="video"),l=n.filter((A)=>A.type==="outbound-rtp"&&w(A,"kind")!=="video"),i=n.filter((A)=>A.type==="candidate-pair"),C=n.filter((A)=>(A.type==="track"||A.type==="media-source")&&w(A,"kind")==="audio"),V=i.filter((A)=>N(A,"selected")===!0||N(A,"nominated")===!0||w(A,"state")==="succeeded").length,I=C.filter((A)=>w(A,"readyState")!=="ended"&&w(A,"trackState")!=="ended"&&N(A,"ended")!==!0).length,S=C.filter((A)=>w(A,"readyState")==="ended"||w(A,"trackState")==="ended"||N(A,"ended")===!0).length,T=g.reduce((A,d)=>A+(_(d,"packetsReceived")??0),0),h=l.reduce((A,d)=>A+(_(d,"packetsSent")??0),0),y=[...g,...l].reduce((A,d)=>A+Math.max(0,_(d,"packetsLost")??0),0),L=T+y,R=L===0?0:y/L,x=g.reduce((A,d)=>A+(_(d,"bytesReceived")??0),0),U=l.reduce((A,d)=>A+(_(d,"bytesSent")??0),0),M=$(i.map((A)=>Z(_(A,"currentRoundTripTime")??_(A,"roundTripTime"))).filter((A)=>A!==void 0)),O=$([...g,...l].map((A)=>Z(_(A,"jitter"))).filter((A)=>A!==void 0)),W=$(g.map((A)=>{let d=_(A,"jitterBufferDelay"),b=_(A,"jitterBufferEmittedCount");return d!==void 0&&b!==void 0&&b>0?d/b*1000:void 0}).filter((A)=>A!==void 0)),P=C.map((A)=>_(A,"audioLevel")).filter((A)=>A!==void 0);if(c.requireConnectedCandidatePair&&i.length>0&&V===0)D(o,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(c.requireLiveAudioTrack&&I===0)D(o,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(c.maxPacketLossRatio!==void 0&&R>c.maxPacketLossRatio)D(o,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(R)} above ${String(c.maxPacketLossRatio)}.`);if(c.maxRoundTripTimeMs!==void 0&&M!==void 0&&M>c.maxRoundTripTimeMs)D(o,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(M)}ms above ${String(c.maxRoundTripTimeMs)}ms.`);if(c.maxJitterMs!==void 0&&O!==void 0&&O>c.maxJitterMs)D(o,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(O)}ms above ${String(c.maxJitterMs)}ms.`);return{activeCandidatePairs:V,audioLevelAverage:wc(P),bytesReceived:x,bytesSent:U,checkedAt:Date.now(),endedAudioTracks:S,inboundPackets:T,issues:o,jitterBufferDelayMs:W,jitterMs:O,liveAudioTracks:I,outboundPackets:h,packetLossRatio:R,packetsLost:y,roundTripTimeMs:M,status:o.some((A)=>A.severity==="error")?"fail":o.length>0?"warn":"pass",totalStats:n.length}},B=async(c)=>{return[...(await c.peerConnection.getStats(c.selector??null)).values()].map(Lc)};var K=(c={})=>{let n=c.stats??[],o=c.previousStats??[],g=[],l=new Map(o.map((y)=>[X(y),y])),C=n.filter((y)=>(y.type==="inbound-rtp"||y.type==="outbound-rtp")&&w(y,"kind")!=="video"&&w(y,"mediaType")!=="video").map((y)=>{let L=y.type==="outbound-rtp"?"outbound":"inbound",R=L==="outbound"?"packetsSent":"packetsReceived",x=L==="outbound"?"bytesSent":"bytesReceived",U=l.get(X(y)),M=_(y,R),O=U?_(U,R):void 0,W=_(y,x),P=U?_(U,x):void 0,A=y.timestamp!==void 0&&U?.timestamp!==void 0?y.timestamp-U.timestamp:void 0;return{bytesDelta:W!==void 0&&P!==void 0?W-P:void 0,currentPackets:M,direction:L,id:X(y),packetDelta:M!==void 0&&O!==void 0?M-O:void 0,previousPackets:O,timeDeltaMs:A}}),V=C.filter((y)=>y.direction==="inbound"),I=C.filter((y)=>y.direction==="outbound"),S=$(C.map((y)=>y.timeDeltaMs).filter((y)=>y!==void 0)),T=V.filter((y)=>c.maxInboundPacketStallMs!==void 0&&y.timeDeltaMs!==void 0&&y.timeDeltaMs>=c.maxInboundPacketStallMs&&y.packetDelta!==void 0&&y.packetDelta<=0).length,h=I.filter((y)=>c.maxOutboundPacketStallMs!==void 0&&y.timeDeltaMs!==void 0&&y.timeDeltaMs>=c.maxOutboundPacketStallMs&&y.packetDelta!==void 0&&y.packetDelta<=0).length;if(c.requireInboundAudio&&V.length===0)D(g,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(c.requireOutboundAudio&&I.length===0)D(g,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(c.maxGapMs!==void 0&&S!==void 0&&S>c.maxGapMs)D(g,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(S)}ms above ${String(c.maxGapMs)}ms.`);if(T>0)D(g,"error","media.webrtc_inbound_stalled",`${String(T)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(h>0)D(g,"error","media.webrtc_outbound_stalled",`${String(h)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:V.length,issues:g,maxObservedGapMs:S,outboundAudioStreams:I.length,stalledInboundStreams:T,stalledOutboundStreams:h,status:g.some((y)=>y.severity==="error")?"fail":g.length>0?"warn":"pass",streams:C,totalStats:n.length}};var bc="/api/voice/browser-media",Uc=5000,Mc=async(c)=>c.peerConnection??await c.getPeerConnection?.()??null,Oc=async(c,n)=>{let o=n.fetch??globalThis.fetch;if(!o)return;await o(n.path??bc,{body:JSON.stringify(c),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},j=(c)=>{let n=null,o=[],g=async()=>{let C=await Mc(c);if(!C)return;let V=await B({peerConnection:C}),I=z({...c,stats:V}),S=c.continuity===!1?void 0:K({...c.continuity,previousStats:o,stats:V}),T={at:Date.now(),continuity:S,report:I,scenarioId:c.getScenarioId?.()??null,sessionId:c.getSessionId?.()??null};return o=V,c.onReport?.(T),await Oc(T,c),T},l=()=>{g().catch((C)=>{c.onError?.(C)})},i=()=>{if(n)clearInterval(n),n=null};return{close:i,reportOnce:g,stop:i,start:()=>{if(n)return;l(),n=setInterval(l,c.intervalMs??Uc)}}};var E=()=>{},Dc=()=>E,sc={callControl:E,close:E,endTurn:E,send:E,sendAudio:E,simulateDisconnect:E,subscribe:Dc,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",start:()=>{}},Ec=()=>crypto.randomUUID(),xc=(c,n,o)=>{let{hostname:g,port:l,protocol:i}=window.location,C=i==="https:"?"wss:":"ws:",V=l?`:${l}`:"",I=new URL(`${C}//${g}${V}${c}`);if(I.searchParams.set("sessionId",n),o)I.searchParams.set("scenarioId",o);return I.toString()},Wc=(c)=>{if(!c||typeof c!=="object"||!("type"in c))return!1;switch(c.type){case"audio":case"assistant":case"call_lifecycle":case"complete":case"connection":case"error":case"final":case"partial":case"pong":case"replay":case"session":case"turn":return!0;default:return!1}},Pc=(c)=>{if(typeof c.data!=="string")return null;try{let n=JSON.parse(c.data);return Wc(n)?n:null}catch{return null}},f=(c,n={})=>{if(typeof window>"u")return sc;let o=new Set,g=n.reconnect!==!1,l=n.maxReconnectAttempts??10,i=n.pingInterval??30000,C={isConnected:!1,pendingMessages:[],scenarioId:n.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:n.sessionId??Ec(),ws:null},V=(A)=>{o.forEach((d)=>d(A))},I=()=>{if(C.pingInterval)clearInterval(C.pingInterval),C.pingInterval=null;if(C.reconnectTimeout)clearTimeout(C.reconnectTimeout),C.reconnectTimeout=null},S=()=>{if(C.ws?.readyState!==1)return;while(C.pendingMessages.length>0){let A=C.pendingMessages.shift();if(A!==void 0)C.ws.send(A)}},T=()=>{let A=Date.now()+500;C.reconnectAttempts+=1,V({reconnect:{attempts:C.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:l,nextAttemptAt:A,status:"reconnecting"},type:"connection"}),C.reconnectTimeout=setTimeout(()=>{if(C.reconnectAttempts>l){V({reconnect:{attempts:C.reconnectAttempts,maxAttempts:l,status:"exhausted"},type:"connection"});return}h()},500)},h=()=>{let A=new WebSocket(xc(c,C.sessionId,C.scenarioId));A.binaryType="arraybuffer",A.onopen=()=>{let d=C.reconnectAttempts>0;if(C.isConnected=!0,S(),d)V({reconnect:{attempts:C.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:l,status:"resumed"},type:"connection"}),C.reconnectAttempts=0;o.forEach((b)=>b({scenarioId:C.scenarioId??void 0,sessionId:C.sessionId,status:"active",type:"session"})),C.pingInterval=setInterval(()=>{if(A.readyState===1)A.send(JSON.stringify({type:"ping"}))},i)},A.onmessage=(d)=>{let b=Pc(d);if(!b)return;if(b.type==="session")C.sessionId=b.sessionId,C.scenarioId=b.scenarioId??C.scenarioId;o.forEach((gc)=>gc(b))},A.onclose=(d)=>{if(C.isConnected=!1,I(),g&&d.code!==1000&&C.reconnectAttempts<l)T();else if(g&&d.code!==1000)V({reconnect:{attempts:C.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:l,status:"exhausted"},type:"connection"})},C.ws=A},y=(A)=>{if(C.ws?.readyState===1){C.ws.send(A);return}C.pendingMessages.push(A)},L=(A)=>{y(JSON.stringify(A))},R=(A={})=>{if(A.sessionId)C.sessionId=A.sessionId;if(A.scenarioId)C.scenarioId=A.scenarioId;L({scenarioId:C.scenarioId??void 0,sessionId:C.sessionId,type:"start"})},x=(A)=>{y(A)},U=()=>{L({type:"end_turn"})},M=(A)=>{L({...A,type:"call_control"})},O=()=>{if(I(),C.ws)C.ws.close(1000),C.ws=null;C.isConnected=!1,o.clear()},W=()=>{if(C.ws?.readyState===1)C.ws.close(4000,"absolutejs-voice-reconnect-proof")},P=(A)=>{return o.add(A),()=>{o.delete(A)}};return h(),{callControl:M,close:O,endTurn:U,send:L,sendAudio:x,simulateDisconnect:W,start:R,subscribe:P,getReadyState:()=>C.ws?.readyState??3,getScenarioId:()=>C.scenarioId??"",getSessionId:()=>C.sessionId}};var Nc=()=>({attempts:0,maxAttempts:0,status:"idle"}),$c=()=>({assistantAudio:[],assistantTexts:[],call:null,error:null,isConnected:!1,partial:"",reconnect:Nc(),scenarioId:null,sessionId:null,sessionMetadata:null,status:"idle",turns:[]}),e=()=>{let c=$c(),n=new Set,o=()=>{n.forEach((l)=>l())};return{dispatch:(l)=>{switch(l.type){case"audio":c={...c,assistantAudio:[...c.assistantAudio,{chunk:l.chunk,format:l.format,receivedAt:l.receivedAt,turnId:l.turnId}]};break;case"assistant":c={...c,assistantTexts:[...c.assistantTexts,l.text]};break;case"complete":c={...c,sessionId:l.sessionId,status:"completed"};break;case"call_lifecycle":c={...c,call:{...c.call,disposition:l.event.type==="end"?l.event.disposition:c.call?.disposition,endedAt:l.event.type==="end"?l.event.at:c.call?.endedAt,events:[...c.call?.events??[],l.event],lastEventAt:l.event.at,startedAt:c.call?.startedAt??l.event.at},sessionId:l.sessionId};break;case"connected":c={...c,isConnected:!0,reconnect:c.reconnect.status==="reconnecting"?{...c.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:c.reconnect};break;case"connection":c={...c,reconnect:l.reconnect};break;case"disconnected":c={...c,isConnected:!1};break;case"error":c={...c,error:l.message};break;case"final":c={...c,partial:l.transcript.text,turns:c.turns.map((i)=>i)};break;case"partial":c={...c,partial:l.transcript.text};break;case"replay":c={...c,assistantTexts:[...l.assistantTexts],call:l.call??null,error:null,isConnected:l.status==="active",partial:l.partial,reconnect:c.reconnect.status==="reconnecting"?{...c.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:c.reconnect,scenarioId:l.scenarioId??c.scenarioId,sessionId:l.sessionId,sessionMetadata:l.sessionMetadata??c.sessionMetadata,status:l.status,turns:[...l.turns]};break;case"session":c={...c,error:null,scenarioId:l.scenarioId??c.scenarioId,isConnected:l.status==="active",sessionId:l.sessionId,sessionMetadata:l.sessionMetadata??c.sessionMetadata,status:l.status};break;case"turn":c={...c,partial:"",turns:[...c.turns,l.turn]};break}o()},getServerSnapshot:()=>c,getSnapshot:()=>c,subscribe:(l)=>{return n.add(l),()=>{n.delete(l)}}}};var k=(c,n={})=>{let o=f(c,n),g=e(),l=n.browserMedia&&typeof window<"u"?j({...n.browserMedia,getScenarioId:()=>n.browserMedia?n.browserMedia.getScenarioId?.()??o.getScenarioId():o.getScenarioId(),getSessionId:()=>n.browserMedia?n.browserMedia.getSessionId?.()??o.getSessionId():o.getSessionId()}):null,i=new Set,C=(T)=>Promise.resolve().then(()=>{if(!T?.sessionId&&!T?.scenarioId)return;o.start(T),l?.start()}),V=()=>{i.forEach((T)=>T())},I=()=>{if(!n.reconnectReportPath||typeof fetch>"u")return;let T=g.getSnapshot(),h=JSON.stringify({at:Date.now(),reconnect:T.reconnect,scenarioId:T.scenarioId,sessionId:o.getSessionId(),turnIds:T.turns.map((y)=>y.id)});fetch(n.reconnectReportPath,{body:h,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},S=o.subscribe((T)=>{let h=q(T);if(h){if(g.dispatch(h),T.type==="connection")I();V()}});return{start:C,get assistantAudio(){return g.getSnapshot().assistantAudio},get assistantTexts(){return g.getSnapshot().assistantTexts},get call(){return g.getSnapshot().call},callControl(T){o.callControl(T)},close(){S(),l?.close(),o.close(),g.dispatch({type:"disconnected"}),V()},endTurn(){o.endTurn()},get error(){return g.getSnapshot().error},getServerSnapshot(){return g.getServerSnapshot()},getSnapshot(){return g.getSnapshot()},get isConnected(){return g.getSnapshot().isConnected},get partial(){return g.getSnapshot().partial},get reconnect(){return g.getSnapshot().reconnect},get scenarioId(){return g.getSnapshot().scenarioId},sendAudio(T){o.sendAudio(T)},get sessionId(){return o.getSessionId()},get sessionMetadata(){return g.getSnapshot().sessionMetadata},simulateDisconnect(){o.simulateDisconnect()},get status(){return g.getSnapshot().status},subscribe(T){return i.add(T),()=>{i.delete(T)}},get turns(){return g.getSnapshot().turns}}};var t=(c)=>{if(!c||c.enabled===!1)return;return{enabled:!0,maxGain:c.maxGain??3,noiseGateAttenuation:c.noiseGateAttenuation??0.15,noiseGateThreshold:c.noiseGateThreshold??0.006,targetLevel:c.targetLevel??0.08}};var Hc={balanced:{qualityProfile:"general",silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:"general",silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},"long-form":{qualityProfile:"general",silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},Gc={"accent-heavy":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},general:{},"noisy-room":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},"short-command":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var F=(c)=>{let n=c?.profile??"fast",o=c?.qualityProfile??"general",g=Hc[n],l=Gc[o];return{profile:n,qualityProfile:o,silenceMs:c?.silenceMs??l.silenceMs??g.silenceMs,speechThreshold:c?.speechThreshold??l.speechThreshold??g.speechThreshold,transcriptStabilityMs:c?.transcriptStabilityMs??l.transcriptStabilityMs??g.transcriptStabilityMs}};var Xc={chat:{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"balanced",qualityProfile:"short-command"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"fast",qualityProfile:"general"}},dictation:{audioConditioning:{enabled:!0,maxGain:2.25,noiseGateAttenuation:0.05,noiseGateThreshold:0.003,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"guided-intake":{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"turn-scoped",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"noisy-room":{audioConditioning:{enabled:!0,maxGain:3,noiseGateAttenuation:0.12,noiseGateThreshold:0.006,targetLevel:0.085},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:2100,speechThreshold:0.02,transcriptStabilityMs:1650}},"pstn-balanced":{audioConditioning:{enabled:!0,maxGain:2.8,noiseGateAttenuation:0.07,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:660,speechThreshold:0.012,transcriptStabilityMs:300}},"pstn-fast":{audioConditioning:{enabled:!0,maxGain:2.75,noiseGateAttenuation:0.06,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:620,speechThreshold:0.012,transcriptStabilityMs:280}},reliability:{audioConditioning:{enabled:!0,maxGain:2.9,noiseGateAttenuation:0.08,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room"}}},r=(c="default")=>{let n=Xc[c];return{audioConditioning:t(n.audioConditioning),capture:{channelCount:n.capture?.channelCount??1,sampleRateHz:n.capture?.sampleRateHz??16000},connection:{...n.connection},name:c,sttLifecycle:n.sttLifecycle??"continuous",turnDetection:F(n.turnDetection)}};var Yc=(c)=>({assistantAudio:[...c.assistantAudio],assistantTexts:[...c.assistantTexts],call:c.call,error:c.error,isConnected:c.isConnected,isRecording:!1,partial:c.partial,reconnect:c.reconnect,recordingError:null,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,scenarioId:c.scenarioId,status:c.status,turns:[...c.turns]}),a=(c,n={})=>{let o=r(n.preset),g=k(c,{...o.connection,...n.connection}),l=null,i=Yc(g),C=new Set,V=()=>{for(let R of C)R()},I=()=>{if(i={...i,assistantAudio:[...g.assistantAudio],assistantTexts:[...g.assistantTexts],call:g.call,error:g.error,isConnected:g.isConnected,partial:g.partial,reconnect:g.reconnect,sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,scenarioId:g.scenarioId,status:g.status,turns:[...g.turns]},n.autoStopOnComplete!==!1&&i.status==="completed"&&i.isRecording)l?.stop(),l=null,i={...i,isRecording:!1};V()},S=g.subscribe(I);I();let T=()=>{if(l)return l;return l=J({channelCount:n.capture?.channelCount??o.capture.channelCount,onLevel:n.capture?.onLevel,onAudio:(R)=>{if(n.capture?.onAudio){n.capture.onAudio(R,g.sendAudio);return}g.sendAudio(R)},sampleRateHz:n.capture?.sampleRateHz??o.capture.sampleRateHz}),l},h=()=>{l?.stop(),l=null,i={...i,isRecording:!1},V()},y=async()=>{if(i.isRecording)return;try{i={...i,recordingError:null},V(),await T().start(),i={...i,isRecording:!0},V()}catch(R){throw l=null,i={...i,isRecording:!1,recordingError:R instanceof Error?R.message:String(R)},V(),R}};return{close:()=>{S(),h(),g.close()},startRecording:y,stopRecording:h,get assistantAudio(){return i.assistantAudio},get assistantTexts(){return i.assistantTexts},bindHTMX(R){return Q(g,R)},get call(){return i.call},callControl:(R)=>g.callControl(R),endTurn:()=>g.endTurn(),get error(){return i.error},getServerSnapshot:()=>i,getSnapshot:()=>i,get isConnected(){return i.isConnected},get isRecording(){return i.isRecording},get partial(){return i.partial},get reconnect(){return i.reconnect},get recordingError(){return i.recordingError},get scenarioId(){return i.scenarioId},sendAudio:(R)=>g.sendAudio(R),get sessionId(){return i.sessionId},get sessionMetadata(){return i.sessionMetadata},simulateDisconnect:()=>g.simulateDisconnect(),get status(){return i.status},subscribe:(R)=>{return C.add(R),()=>{C.delete(R)}},toggleRecording:async()=>{if(i.isRecording){h();return}await y()},get turns(){return i.turns}}};var s=(c)=>String(c).replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;");var v=(c)=>{if(!c.isConnected)return"idle";if(c.isPlaying)return"speaking";if(c.isRecording&&c.hasActivePartial)return"listening";if(c.isRecording)return"listening";if(c.lastTranscriptAt&&!c.lastAssistantAt)return"thinking";if(c.lastTranscriptAt&&c.lastAssistantAt&&c.lastTranscriptAt>c.lastAssistantAt)return"thinking";return"idle"};var Qc={accent:"#3b82f6",background:"#0f172a",errorAccent:"#ef4444",fontFamily:'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',foreground:"#f8fafc",radius:16},Jc={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},qc=(c,n)=>{switch(c){case"listening":return n.listening;case"speaking":return n.speaking;case"thinking":return n.thinking;case"idle":return n.idle}},u=(c)=>{let n={...Qc,...c.theme},o={...Jc,...c.labels},g=c.state.assistantAudio.at(-1)?.receivedAt,l=c.state.turns.at(-1)?.committedAt,i=v({hasActivePartial:c.state.partial.length>0,isConnected:c.state.isConnected,isPlaying:!1,isRecording:c.state.isRecording,lastAssistantAt:g,lastTranscriptAt:l}),C=!c.state.isConnected&&c.state.status!=="idle"&&!c.state.error,V=c.state.error?"Error":C?o.connecting:c.state.status==="completed"?o.callEnded:qc(i,o);return{agentState:i,classes:{container:`absolute-voice-widget absolute-voice-widget--${i}`,dot:`absolute-voice-widget__dot${c.state.error?" absolute-voice-widget__dot--error":""}`},controls:{canEnd:c.state.isConnected,canMute:c.state.isRecording,canStart:!c.state.isRecording&&c.state.status!=="completed"},errorMessage:c.state.error??void 0,labels:o,partial:c.state.partial||void 0,statusLabel:V,theme:n,title:c.title??"Voice"}},Zc=(c)=>typeof c==="number"?`${c}px`:c,m=(c)=>{let n=c.theme,o=`background:${n.background};border-radius:${Zc(n.radius)};color:${n.foreground};font-family:${n.fontFamily};min-width:240px;padding:20px 22px;`,g=`background:${c.errorMessage?n.errorAccent:c.agentState==="idle"?"rgba(148,163,184,0.6)":n.accent};border-radius:50%;height:10px;width:10px;`,l=[];if(c.controls.canStart)l.push(`<button type="button" data-action="start" style="background:${n.accent};border:none;border-radius:12px;color:${n.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${s(c.labels.startCall)}</button>`);if(c.controls.canMute)l.push(`<button type="button" data-action="mute" style="background:transparent;border:1px solid rgba(255,255,255,0.18);border-radius:12px;color:${n.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${s(c.labels.mute)}</button>`);if(c.controls.canEnd)l.push(`<button type="button" data-action="end" style="background:${n.errorAccent};border:none;border-radius:12px;color:${n.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${s(c.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${c.agentState}" class="${s(c.classes.container)}" style="${o}">
1
+ (()=>{var{defineProperty:H,getOwnPropertyNames:oc,getOwnPropertyDescriptor:lc}=Object,Ac=Object.prototype.hasOwnProperty;function ic(c){return this[c]}var Cc=(c)=>{var n=(Y??=new WeakMap).get(c),o;if(n)return n;if(n=H({},"__esModule",{value:!0}),c&&typeof c==="object"||typeof c==="function"){for(var g of oc(c))if(!Ac.call(n,g))H(n,g,{get:ic.bind(c,g),enumerable:!(o=lc(c,g))||o.enumerable})}return Y.set(c,n),n},Y;var Tc=(c)=>c;function yc(c,n){this[c]=Tc.bind(null,n)}var Vc=(c,n)=>{for(var o in n)H(c,o,{get:n[o],enumerable:!0,configurable:!0,set:yc.bind(n,o)})};var ec={};Vc(ec,{mount:()=>p,default:()=>Bc,VOICE_EMBED_VERSION:()=>cc});var Ic=(c)=>{if(typeof c!=="string")return c;return document.querySelector(c)},Sc=(c,n,o,g)=>{let l=n??c.getAttribute("hx-get")??"";if(!l)return"";let C=new URL(l,window.location.origin);if(g)C.searchParams.set(o,g);else C.searchParams.delete(o);return`${C.pathname}${C.search}${C.hash}`},Q=(c,n)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let o=Ic(n.element);if(!o)return()=>{};let g=n.eventName??"voice-refresh",l=n.sessionQueryParam??"sessionId",C=()=>{let V=window,I=Sc(o,n.route,l,c.sessionId);if(I)o.setAttribute("hx-get",I);V.htmx?.process?.(o),V.htmx?.trigger?.(o,g)},i=c.subscribe(C);return C(),()=>{i()}};var dc=(c)=>Math.max(-1,Math.min(1,c)),Rc=(c)=>{let n=new Int16Array(c.length);for(let o=0;o<c.length;o+=1){let g=dc(c[o]??0);n[o]=g<0?g*32768:g*32767}return new Uint8Array(n.buffer)},hc=(c)=>{let n=c instanceof Uint8Array?c:new Uint8Array(c);if(n.byteLength<2)return 0;let o=new Int16Array(n.buffer,n.byteOffset,Math.floor(n.byteLength/2));if(o.length===0)return 0;let g=0;for(let l of o){let C=l/32768;g+=C*C}return Math.min(1,Math.max(0,Math.sqrt(g/o.length)*5.5))},_c=(c,n,o)=>{if(n===o)return c;let g=n/o,l=Math.round(c.length/g),C=new Float32Array(l),i=0,V=0;while(i<C.length){let I=Math.round((i+1)*g),R=0,y=0;for(let d=V;d<I&&d<c.length;d+=1)R+=c[d]??0,y+=1;C[i]=y>0?R/y:0,i+=1,V=I}return C},J=(c)=>{let n=null,o=null,g=null,l=null;return{start:async()=>{if(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia)throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let V=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!V)throw Error("Browser microphone capture requires AudioContext support.");l=await navigator.mediaDevices.getUserMedia({audio:{channelCount:c.channelCount??1}}),n=new V,o=n.createMediaStreamSource(l),g=n.createScriptProcessor(4096,1,1),g.onaudioprocess=(I)=>{let R=I.inputBuffer.getChannelData(0),y=_c(R,n?.sampleRate??48000,c.sampleRateHz??16000),d=Rc(y);c.onLevel?.(hc(d)),c.onAudio(d)},o.connect(g),g.connect(n.destination)},stop:()=>{g?.disconnect(),o?.disconnect(),l?.getTracks().forEach((V)=>V.stop()),n?.close(),c.onLevel?.(0),n=null,l=null,g=null,o=null}}};var G=(c)=>{if(typeof c==="string"&&c.trim())return c;if(c instanceof Error&&c.message.trim())return c.message;if(c&&typeof c==="object"){let n=c;for(let o of["message","reason","description"]){let g=n[o];if(typeof g==="string"&&g.trim())return g}if("error"in n)return G(n.error);if("cause"in n)return G(n.cause);try{return JSON.stringify(c)}catch{}}return"Unexpected error"},q=(c)=>{switch(c.type){case"audio":return{chunk:Uint8Array.from(atob(c.chunkBase64),(n)=>n.charCodeAt(0)),format:c.format,receivedAt:c.receivedAt,turnId:c.turnId,type:"audio"};case"assistant":return{text:c.text,turnId:c.turnId,type:"assistant"};case"assistant_delta":return{delta:c.delta,turnId:c.turnId,type:"assistant_delta"};case"complete":return{sessionId:c.sessionId,type:"complete"};case"connection":return{reconnect:c.reconnect,type:"connection"};case"call_lifecycle":return{event:c.event,sessionId:c.sessionId,type:"call_lifecycle"};case"error":return{message:G(c.message),type:"error"};case"final":return{transcript:c.transcript,type:"final"};case"partial":return{transcript:c.transcript,type:"partial"};case"replay":return{assistantTexts:c.assistantTexts,call:c.call,partial:c.partial,scenarioId:c.scenarioId,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,status:c.status,turns:c.turns,type:"replay"};case"session":return{sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,scenarioId:c.scenarioId,status:c.status,type:"session"};case"turn":return{turn:c.turn,type:"turn"};default:return null}};var kc=Math.PI*2;var x=(c,n,o,g)=>{c.push({code:o,message:g,severity:n})};var sc=(c)=>c.length===0?void 0:c.reduce((n,o)=>n+o,0)/c.length,N=(c)=>c.length===0?void 0:Math.max(...c);var _=(c,n)=>{let o=c[n];return typeof o==="number"&&Number.isFinite(o)?o:void 0},$=(c,n)=>{let o=c[n];return typeof o==="boolean"?o:void 0},s=(c,n)=>{let o=c[n];return typeof o==="string"?o:void 0},X=(c)=>String(c.id??s(c,"ssrc")??_(c,"ssrc")??s(c,"trackIdentifier")??s(c,"mid")??"unknown"),Z=(c)=>c===void 0?void 0:c*1000;var wc=(c)=>{let n={};for(let[o,g]of Object.entries(c))if(g===null||typeof g==="boolean"||typeof g==="number"||typeof g==="string")n[o]=g;return n};var z=(c={})=>{let n=c.stats??[],o=[],g=n.filter((A)=>A.type==="inbound-rtp"&&s(A,"kind")!=="video"),l=n.filter((A)=>A.type==="outbound-rtp"&&s(A,"kind")!=="video"),C=n.filter((A)=>A.type==="candidate-pair"),i=n.filter((A)=>(A.type==="track"||A.type==="media-source")&&s(A,"kind")==="audio"),V=C.filter((A)=>$(A,"selected")===!0||$(A,"nominated")===!0||s(A,"state")==="succeeded").length,I=i.filter((A)=>s(A,"readyState")!=="ended"&&s(A,"trackState")!=="ended"&&$(A,"ended")!==!0).length,R=i.filter((A)=>s(A,"readyState")==="ended"||s(A,"trackState")==="ended"||$(A,"ended")===!0).length,y=g.reduce((A,h)=>A+(_(h,"packetsReceived")??0),0),d=l.reduce((A,h)=>A+(_(h,"packetsSent")??0),0),T=[...g,...l].reduce((A,h)=>A+Math.max(0,_(h,"packetsLost")??0),0),w=y+T,S=w===0?0:T/w,E=g.reduce((A,h)=>A+(_(h,"bytesReceived")??0),0),b=l.reduce((A,h)=>A+(_(h,"bytesSent")??0),0),U=N(C.map((A)=>Z(_(A,"currentRoundTripTime")??_(A,"roundTripTime"))).filter((A)=>A!==void 0)),M=N([...g,...l].map((A)=>Z(_(A,"jitter"))).filter((A)=>A!==void 0)),W=N(g.map((A)=>{let h=_(A,"jitterBufferDelay"),L=_(A,"jitterBufferEmittedCount");return h!==void 0&&L!==void 0&&L>0?h/L*1000:void 0}).filter((A)=>A!==void 0)),P=i.map((A)=>_(A,"audioLevel")).filter((A)=>A!==void 0);if(c.requireConnectedCandidatePair&&C.length>0&&V===0)x(o,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(c.requireLiveAudioTrack&&I===0)x(o,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(c.maxPacketLossRatio!==void 0&&S>c.maxPacketLossRatio)x(o,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(S)} above ${String(c.maxPacketLossRatio)}.`);if(c.maxRoundTripTimeMs!==void 0&&U!==void 0&&U>c.maxRoundTripTimeMs)x(o,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(U)}ms above ${String(c.maxRoundTripTimeMs)}ms.`);if(c.maxJitterMs!==void 0&&M!==void 0&&M>c.maxJitterMs)x(o,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(M)}ms above ${String(c.maxJitterMs)}ms.`);return{activeCandidatePairs:V,audioLevelAverage:sc(P),bytesReceived:E,bytesSent:b,checkedAt:Date.now(),endedAudioTracks:R,inboundPackets:y,issues:o,jitterBufferDelayMs:W,jitterMs:M,liveAudioTracks:I,outboundPackets:d,packetLossRatio:S,packetsLost:T,roundTripTimeMs:U,status:o.some((A)=>A.severity==="error")?"fail":o.length>0?"warn":"pass",totalStats:n.length}},e=async(c)=>{return[...(await c.peerConnection.getStats(c.selector??null)).values()].map(wc)};var B=(c={})=>{let n=c.stats??[],o=c.previousStats??[],g=[],l=new Map(o.map((T)=>[X(T),T])),i=n.filter((T)=>(T.type==="inbound-rtp"||T.type==="outbound-rtp")&&s(T,"kind")!=="video"&&s(T,"mediaType")!=="video").map((T)=>{let w=T.type==="outbound-rtp"?"outbound":"inbound",S=w==="outbound"?"packetsSent":"packetsReceived",E=w==="outbound"?"bytesSent":"bytesReceived",b=l.get(X(T)),U=_(T,S),M=b?_(b,S):void 0,W=_(T,E),P=b?_(b,E):void 0,A=T.timestamp!==void 0&&b?.timestamp!==void 0?T.timestamp-b.timestamp:void 0;return{bytesDelta:W!==void 0&&P!==void 0?W-P:void 0,currentPackets:U,direction:w,id:X(T),packetDelta:U!==void 0&&M!==void 0?U-M:void 0,previousPackets:M,timeDeltaMs:A}}),V=i.filter((T)=>T.direction==="inbound"),I=i.filter((T)=>T.direction==="outbound"),R=N(i.map((T)=>T.timeDeltaMs).filter((T)=>T!==void 0)),y=V.filter((T)=>c.maxInboundPacketStallMs!==void 0&&T.timeDeltaMs!==void 0&&T.timeDeltaMs>=c.maxInboundPacketStallMs&&T.packetDelta!==void 0&&T.packetDelta<=0).length,d=I.filter((T)=>c.maxOutboundPacketStallMs!==void 0&&T.timeDeltaMs!==void 0&&T.timeDeltaMs>=c.maxOutboundPacketStallMs&&T.packetDelta!==void 0&&T.packetDelta<=0).length;if(c.requireInboundAudio&&V.length===0)x(g,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(c.requireOutboundAudio&&I.length===0)x(g,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(c.maxGapMs!==void 0&&R!==void 0&&R>c.maxGapMs)x(g,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(R)}ms above ${String(c.maxGapMs)}ms.`);if(y>0)x(g,"error","media.webrtc_inbound_stalled",`${String(y)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(d>0)x(g,"error","media.webrtc_outbound_stalled",`${String(d)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:V.length,issues:g,maxObservedGapMs:R,outboundAudioStreams:I.length,stalledInboundStreams:y,stalledOutboundStreams:d,status:g.some((T)=>T.severity==="error")?"fail":g.length>0?"warn":"pass",streams:i,totalStats:n.length}};var Lc="/api/voice/browser-media",bc=5000,Uc=async(c)=>c.peerConnection??await c.getPeerConnection?.()??null,Mc=async(c,n)=>{let o=n.fetch??globalThis.fetch;if(!o)return;await o(n.path??Lc,{body:JSON.stringify(c),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},K=(c)=>{let n=null,o=[],g=async()=>{let i=await Uc(c);if(!i)return;let V=await e({peerConnection:i}),I=z({...c,stats:V}),R=c.continuity===!1?void 0:B({...c.continuity,previousStats:o,stats:V}),y={at:Date.now(),continuity:R,report:I,scenarioId:c.getScenarioId?.()??null,sessionId:c.getSessionId?.()??null};return o=V,c.onReport?.(y),await Mc(y,c),y},l=()=>{g().catch((i)=>{c.onError?.(i)})},C=()=>{if(n)clearInterval(n),n=null};return{close:C,reportOnce:g,stop:C,start:()=>{if(n)return;l(),n=setInterval(l,c.intervalMs??bc)}}};var D=()=>{},xc=()=>D,Oc={callControl:D,close:D,endTurn:D,send:D,sendAudio:D,simulateDisconnect:D,subscribe:xc,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",start:()=>{}},Dc=()=>crypto.randomUUID(),Ec=(c,n,o)=>{let{hostname:g,port:l,protocol:C}=window.location,i=C==="https:"?"wss:":"ws:",V=l?`:${l}`:"",I=new URL(`${i}//${g}${V}${c}`);if(I.searchParams.set("sessionId",n),o)I.searchParams.set("scenarioId",o);return I.toString()},Wc=(c)=>{if(!c||typeof c!=="object"||!("type"in c))return!1;switch(c.type){case"audio":case"assistant":case"call_lifecycle":case"complete":case"connection":case"error":case"final":case"partial":case"pong":case"replay":case"session":case"turn":return!0;default:return!1}},Pc=(c)=>{if(typeof c.data!=="string")return null;try{let n=JSON.parse(c.data);return Wc(n)?n:null}catch{return null}},j=(c,n={})=>{if(typeof window>"u")return Oc;let o=new Set,g=n.reconnect!==!1,l=n.maxReconnectAttempts??10,C=n.pingInterval??30000,i={isConnected:!1,pendingMessages:[],scenarioId:n.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:n.sessionId??Dc(),ws:null},V=(A)=>{o.forEach((h)=>h(A))},I=()=>{if(i.pingInterval)clearInterval(i.pingInterval),i.pingInterval=null;if(i.reconnectTimeout)clearTimeout(i.reconnectTimeout),i.reconnectTimeout=null},R=()=>{if(i.ws?.readyState!==1)return;while(i.pendingMessages.length>0){let A=i.pendingMessages.shift();if(A!==void 0)i.ws.send(A)}},y=()=>{let A=Date.now()+500;i.reconnectAttempts+=1,V({reconnect:{attempts:i.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:l,nextAttemptAt:A,status:"reconnecting"},type:"connection"}),i.reconnectTimeout=setTimeout(()=>{if(i.reconnectAttempts>l){V({reconnect:{attempts:i.reconnectAttempts,maxAttempts:l,status:"exhausted"},type:"connection"});return}d()},500)},d=()=>{let A=new WebSocket(Ec(c,i.sessionId,i.scenarioId));A.binaryType="arraybuffer",A.onopen=()=>{let h=i.reconnectAttempts>0;if(i.isConnected=!0,R(),h)V({reconnect:{attempts:i.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:l,status:"resumed"},type:"connection"}),i.reconnectAttempts=0;o.forEach((L)=>L({scenarioId:i.scenarioId??void 0,sessionId:i.sessionId,status:"active",type:"session"})),i.pingInterval=setInterval(()=>{if(A.readyState===1)A.send(JSON.stringify({type:"ping"}))},C)},A.onmessage=(h)=>{let L=Pc(h);if(!L)return;if(L.type==="session")i.sessionId=L.sessionId,i.scenarioId=L.scenarioId??i.scenarioId;o.forEach((gc)=>gc(L))},A.onclose=(h)=>{if(i.isConnected=!1,I(),g&&h.code!==1000&&i.reconnectAttempts<l)y();else if(g&&h.code!==1000)V({reconnect:{attempts:i.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:l,status:"exhausted"},type:"connection"})},i.ws=A},T=(A)=>{if(i.ws?.readyState===1){i.ws.send(A);return}i.pendingMessages.push(A)},w=(A)=>{T(JSON.stringify(A))},S=(A={})=>{if(A.sessionId)i.sessionId=A.sessionId;if(A.scenarioId)i.scenarioId=A.scenarioId;w({scenarioId:i.scenarioId??void 0,sessionId:i.sessionId,type:"start"})},E=(A)=>{T(A)},b=()=>{w({type:"end_turn"})},U=(A)=>{w({...A,type:"call_control"})},M=()=>{if(I(),i.ws)i.ws.close(1000),i.ws=null;i.isConnected=!1,o.clear()},W=()=>{if(i.ws?.readyState===1)i.ws.close(4000,"absolutejs-voice-reconnect-proof")},P=(A)=>{return o.add(A),()=>{o.delete(A)}};return d(),{callControl:U,close:M,endTurn:b,send:w,sendAudio:E,simulateDisconnect:W,start:S,subscribe:P,getReadyState:()=>i.ws?.readyState??3,getScenarioId:()=>i.scenarioId??"",getSessionId:()=>i.sessionId}};var $c=()=>({attempts:0,maxAttempts:0,status:"idle"}),Nc=()=>({assistantAudio:[],assistantStreamingText:"",assistantTexts:[],call:null,error:null,isConnected:!1,partial:"",reconnect:$c(),scenarioId:null,sessionId:null,sessionMetadata:null,status:"idle",turns:[]}),t=()=>{let c=Nc(),n=new Set,o=()=>{n.forEach((l)=>l())};return{dispatch:(l)=>{switch(l.type){case"audio":c={...c,assistantAudio:[...c.assistantAudio,{chunk:l.chunk,format:l.format,receivedAt:l.receivedAt,turnId:l.turnId}]};break;case"assistant":c={...c,assistantStreamingText:"",assistantTexts:[...c.assistantTexts,l.text]};break;case"assistant_delta":c={...c,assistantStreamingText:`${c.assistantStreamingText}${l.delta}`};break;case"complete":c={...c,sessionId:l.sessionId,status:"completed"};break;case"call_lifecycle":c={...c,call:{...c.call,disposition:l.event.type==="end"?l.event.disposition:c.call?.disposition,endedAt:l.event.type==="end"?l.event.at:c.call?.endedAt,events:[...c.call?.events??[],l.event],lastEventAt:l.event.at,startedAt:c.call?.startedAt??l.event.at},sessionId:l.sessionId};break;case"connected":c={...c,isConnected:!0,reconnect:c.reconnect.status==="reconnecting"?{...c.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:c.reconnect};break;case"connection":c={...c,reconnect:l.reconnect};break;case"disconnected":c={...c,isConnected:!1};break;case"error":c={...c,error:l.message};break;case"final":c={...c,partial:l.transcript.text,turns:c.turns.map((C)=>C)};break;case"partial":c={...c,partial:l.transcript.text};break;case"replay":c={...c,assistantStreamingText:"",assistantTexts:[...l.assistantTexts],call:l.call??null,error:null,isConnected:l.status==="active",partial:l.partial,reconnect:c.reconnect.status==="reconnecting"?{...c.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:c.reconnect,scenarioId:l.scenarioId??c.scenarioId,sessionId:l.sessionId,sessionMetadata:l.sessionMetadata??c.sessionMetadata,status:l.status,turns:[...l.turns]};break;case"session":c={...c,error:null,scenarioId:l.scenarioId??c.scenarioId,isConnected:l.status==="active",sessionId:l.sessionId,sessionMetadata:l.sessionMetadata??c.sessionMetadata,status:l.status};break;case"turn":c={...c,partial:"",turns:[...c.turns,l.turn]};break}o()},getServerSnapshot:()=>c,getSnapshot:()=>c,subscribe:(l)=>{return n.add(l),()=>{n.delete(l)}}}};var f=(c,n={})=>{let o=j(c,n),g=t(),l=n.browserMedia&&typeof window<"u"?K({...n.browserMedia,getScenarioId:()=>n.browserMedia?n.browserMedia.getScenarioId?.()??o.getScenarioId():o.getScenarioId(),getSessionId:()=>n.browserMedia?n.browserMedia.getSessionId?.()??o.getSessionId():o.getSessionId()}):null,C=new Set,i=(y)=>Promise.resolve().then(()=>{if(!y?.sessionId&&!y?.scenarioId)return;o.start(y),l?.start()}),V=()=>{C.forEach((y)=>y())},I=()=>{if(!n.reconnectReportPath||typeof fetch>"u")return;let y=g.getSnapshot(),d=JSON.stringify({at:Date.now(),reconnect:y.reconnect,scenarioId:y.scenarioId,sessionId:o.getSessionId(),turnIds:y.turns.map((T)=>T.id)});fetch(n.reconnectReportPath,{body:d,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},R=o.subscribe((y)=>{let d=q(y);if(d){if(g.dispatch(d),y.type==="connection")I();V()}});return{start:i,get assistantAudio(){return g.getSnapshot().assistantAudio},get assistantTexts(){return g.getSnapshot().assistantTexts},get assistantStreamingText(){return g.getSnapshot().assistantStreamingText},get call(){return g.getSnapshot().call},callControl(y){o.callControl(y)},close(){R(),l?.close(),o.close(),g.dispatch({type:"disconnected"}),V()},endTurn(){o.endTurn()},get error(){return g.getSnapshot().error},getServerSnapshot(){return g.getServerSnapshot()},getSnapshot(){return g.getSnapshot()},get isConnected(){return g.getSnapshot().isConnected},get partial(){return g.getSnapshot().partial},get reconnect(){return g.getSnapshot().reconnect},get scenarioId(){return g.getSnapshot().scenarioId},sendAudio(y){o.sendAudio(y)},get sessionId(){return o.getSessionId()},get sessionMetadata(){return g.getSnapshot().sessionMetadata},simulateDisconnect(){o.simulateDisconnect()},get status(){return g.getSnapshot().status},subscribe(y){return C.add(y),()=>{C.delete(y)}},get turns(){return g.getSnapshot().turns}}};var k=(c)=>{if(!c||c.enabled===!1)return;return{enabled:!0,maxGain:c.maxGain??3,noiseGateAttenuation:c.noiseGateAttenuation??0.15,noiseGateThreshold:c.noiseGateThreshold??0.006,targetLevel:c.targetLevel??0.08}};var Hc={balanced:{qualityProfile:"general",silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:"general",silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},"long-form":{qualityProfile:"general",silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},Gc={"accent-heavy":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},general:{},"noisy-room":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},"short-command":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var F=(c)=>{let n=c?.profile??"fast",o=c?.qualityProfile??"general",g=Hc[n],l=Gc[o];return{profile:n,qualityProfile:o,silenceMs:c?.silenceMs??l.silenceMs??g.silenceMs,speechThreshold:c?.speechThreshold??l.speechThreshold??g.speechThreshold,transcriptStabilityMs:c?.transcriptStabilityMs??l.transcriptStabilityMs??g.transcriptStabilityMs}};var Xc={chat:{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"balanced",qualityProfile:"short-command"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"fast",qualityProfile:"general"}},dictation:{audioConditioning:{enabled:!0,maxGain:2.25,noiseGateAttenuation:0.05,noiseGateThreshold:0.003,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"guided-intake":{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"turn-scoped",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"noisy-room":{audioConditioning:{enabled:!0,maxGain:3,noiseGateAttenuation:0.12,noiseGateThreshold:0.006,targetLevel:0.085},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:2100,speechThreshold:0.02,transcriptStabilityMs:1650}},"pstn-balanced":{audioConditioning:{enabled:!0,maxGain:2.8,noiseGateAttenuation:0.07,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:660,speechThreshold:0.012,transcriptStabilityMs:300}},"pstn-fast":{audioConditioning:{enabled:!0,maxGain:2.75,noiseGateAttenuation:0.06,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:620,speechThreshold:0.012,transcriptStabilityMs:280}},reliability:{audioConditioning:{enabled:!0,maxGain:2.9,noiseGateAttenuation:0.08,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room"}}},r=(c="default")=>{let n=Xc[c];return{audioConditioning:k(n.audioConditioning),capture:{channelCount:n.capture?.channelCount??1,sampleRateHz:n.capture?.sampleRateHz??16000},connection:{...n.connection},name:c,sttLifecycle:n.sttLifecycle??"continuous",turnDetection:F(n.turnDetection)}};var Yc=(c)=>({assistantAudio:[...c.assistantAudio],assistantStreamingText:c.assistantStreamingText,assistantTexts:[...c.assistantTexts],call:c.call,error:c.error,isConnected:c.isConnected,isRecording:!1,partial:c.partial,reconnect:c.reconnect,recordingError:null,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,scenarioId:c.scenarioId,status:c.status,turns:[...c.turns]}),a=(c,n={})=>{let o=r(n.preset),g=f(c,{...o.connection,...n.connection}),l=null,C=Yc(g),i=new Set,V=()=>{for(let S of i)S()},I=()=>{if(C={...C,assistantAudio:[...g.assistantAudio],assistantStreamingText:g.assistantStreamingText,assistantTexts:[...g.assistantTexts],call:g.call,error:g.error,isConnected:g.isConnected,partial:g.partial,reconnect:g.reconnect,sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,scenarioId:g.scenarioId,status:g.status,turns:[...g.turns]},n.autoStopOnComplete!==!1&&C.status==="completed"&&C.isRecording)l?.stop(),l=null,C={...C,isRecording:!1};V()},R=g.subscribe(I);I();let y=()=>{if(l)return l;return l=J({channelCount:n.capture?.channelCount??o.capture.channelCount,onLevel:n.capture?.onLevel,onAudio:(S)=>{if(n.capture?.onAudio){n.capture.onAudio(S,g.sendAudio);return}g.sendAudio(S)},sampleRateHz:n.capture?.sampleRateHz??o.capture.sampleRateHz}),l},d=()=>{l?.stop(),l=null,C={...C,isRecording:!1},V()},T=async()=>{if(C.isRecording)return;try{C={...C,recordingError:null},V(),await y().start(),C={...C,isRecording:!0},V()}catch(S){throw l=null,C={...C,isRecording:!1,recordingError:S instanceof Error?S.message:String(S)},V(),S}};return{close:()=>{R(),d(),g.close()},startRecording:T,stopRecording:d,get assistantAudio(){return C.assistantAudio},get assistantTexts(){return C.assistantTexts},get assistantStreamingText(){return C.assistantStreamingText},bindHTMX(S){return Q(g,S)},get call(){return C.call},callControl:(S)=>g.callControl(S),endTurn:()=>g.endTurn(),get error(){return C.error},getServerSnapshot:()=>C,getSnapshot:()=>C,get isConnected(){return C.isConnected},get isRecording(){return C.isRecording},get partial(){return C.partial},get reconnect(){return C.reconnect},get recordingError(){return C.recordingError},get scenarioId(){return C.scenarioId},sendAudio:(S)=>g.sendAudio(S),get sessionId(){return C.sessionId},get sessionMetadata(){return C.sessionMetadata},simulateDisconnect:()=>g.simulateDisconnect(),get status(){return C.status},subscribe:(S)=>{return i.add(S),()=>{i.delete(S)}},toggleRecording:async()=>{if(C.isRecording){d();return}await T()},get turns(){return C.turns}}};var O=(c)=>String(c).replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;");var v=(c)=>{if(!c.isConnected)return"idle";if(c.isPlaying)return"speaking";if(c.isRecording&&c.hasActivePartial)return"listening";if(c.isRecording)return"listening";if(c.lastTranscriptAt&&!c.lastAssistantAt)return"thinking";if(c.lastTranscriptAt&&c.lastAssistantAt&&c.lastTranscriptAt>c.lastAssistantAt)return"thinking";return"idle"};var Qc={accent:"#3b82f6",background:"#0f172a",errorAccent:"#ef4444",fontFamily:'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',foreground:"#f8fafc",radius:16},Jc={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},qc=(c,n)=>{switch(c){case"listening":return n.listening;case"speaking":return n.speaking;case"thinking":return n.thinking;case"idle":return n.idle}},u=(c)=>{let n={...Qc,...c.theme},o={...Jc,...c.labels},g=c.state.assistantAudio.at(-1)?.receivedAt,l=c.state.turns.at(-1)?.committedAt,C=v({hasActivePartial:c.state.partial.length>0,isConnected:c.state.isConnected,isPlaying:!1,isRecording:c.state.isRecording,lastAssistantAt:g,lastTranscriptAt:l}),i=!c.state.isConnected&&c.state.status!=="idle"&&!c.state.error,V=c.state.error?"Error":i?o.connecting:c.state.status==="completed"?o.callEnded:qc(C,o);return{agentState:C,classes:{container:`absolute-voice-widget absolute-voice-widget--${C}`,dot:`absolute-voice-widget__dot${c.state.error?" absolute-voice-widget__dot--error":""}`},controls:{canEnd:c.state.isConnected,canMute:c.state.isRecording,canStart:!c.state.isRecording&&c.state.status!=="completed"},errorMessage:c.state.error??void 0,labels:o,partial:c.state.partial||void 0,statusLabel:V,theme:n,title:c.title??"Voice"}},Zc=(c)=>typeof c==="number"?`${c}px`:c,m=(c)=>{let n=c.theme,o=`background:${n.background};border-radius:${Zc(n.radius)};color:${n.foreground};font-family:${n.fontFamily};min-width:240px;padding:20px 22px;`,g=`background:${c.errorMessage?n.errorAccent:c.agentState==="idle"?"rgba(148,163,184,0.6)":n.accent};border-radius:50%;height:10px;width:10px;`,l=[];if(c.controls.canStart)l.push(`<button type="button" data-action="start" style="background:${n.accent};border:none;border-radius:12px;color:${n.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${O(c.labels.startCall)}</button>`);if(c.controls.canMute)l.push(`<button type="button" data-action="mute" style="background:transparent;border:1px solid rgba(255,255,255,0.18);border-radius:12px;color:${n.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${O(c.labels.mute)}</button>`);if(c.controls.canEnd)l.push(`<button type="button" data-action="end" style="background:${n.errorAccent};border:none;border-radius:12px;color:${n.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${O(c.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${c.agentState}" class="${O(c.classes.container)}" style="${o}">
2
2
  <div style="align-items:center;display:flex;gap:10px;margin-bottom:12px;">
3
- <span aria-hidden="true" class="${s(c.classes.dot)}" style="${g}"></span>
4
- <strong style="font-size:15px;">${s(c.title)}</strong>
5
- <span style="font-size:13px;margin-left:auto;opacity:0.7;">${s(c.statusLabel)}</span>
3
+ <span aria-hidden="true" class="${O(c.classes.dot)}" style="${g}"></span>
4
+ <strong style="font-size:15px;">${O(c.title)}</strong>
5
+ <span style="font-size:13px;margin-left:auto;opacity:0.7;">${O(c.statusLabel)}</span>
6
6
  </div>
7
- ${c.partial?`<p style="font-size:13px;margin:8px 0 12px;opacity:0.85;word-break:break-word;">“${s(c.partial)}”</p>`:""}
7
+ ${c.partial?`<p style="font-size:13px;margin:8px 0 12px;opacity:0.85;word-break:break-word;">“${O(c.partial)}”</p>`:""}
8
8
  <div style="display:flex;gap:10px;">${l.join("")}</div>
9
- ${c.errorMessage?`<p style="color:${n.errorAccent};font-size:12px;margin-top:12px;">${s(c.errorMessage)}</p>`:""}
10
- </div>`};var zc=(c)=>{if(typeof c!=="string")return c;let n=document.querySelector(c);if(!n)throw Error(`AbsoluteVoice.mount: no element matches "${c}"`);return n},p=(c,n={})=>{let o=zc(c),g=a(n.path??"/voice",n.controllerOptions),l=null,i=null,C=()=>{let I=u({...n.labels!==void 0?{labels:n.labels}:{},state:{assistantAudio:g.assistantAudio,error:g.error,isConnected:g.isConnected,isRecording:g.isRecording,partial:g.partial,status:g.status,turns:g.turns},...n.theme!==void 0?{theme:n.theme}:{},...n.title!==void 0?{title:n.title}:{}});o.innerHTML=m(I);for(let S of o.querySelectorAll("button[data-action]")){let{action:T}=S.dataset;S.addEventListener("click",()=>{if(T==="start")g.startRecording();else if(T==="mute")g.stopRecording();else if(T==="end")g.close()})}if(g.error&&g.error!==l)l=g.error,n.onError?.(g.error);if(g.status!==i)i=g.status,n.onStatusChange?.(g.status)},V=g.subscribe(C);if(C(),n.autoStart)g.startRecording();return{controller:g,async end(){await g.close()},mute(){g.stopRecording()},async start(){await g.startRecording()},unmount(){V(),g.close(),o.innerHTML=""}}},cc="0.0.22-beta.516",nc={mount:p,version:cc};if(typeof globalThis<"u")globalThis.AbsoluteVoice=nc;var Kc=nc;})();
9
+ ${c.errorMessage?`<p style="color:${n.errorAccent};font-size:12px;margin-top:12px;">${O(c.errorMessage)}</p>`:""}
10
+ </div>`};var zc=(c)=>{if(typeof c!=="string")return c;let n=document.querySelector(c);if(!n)throw Error(`AbsoluteVoice.mount: no element matches "${c}"`);return n},p=(c,n={})=>{let o=zc(c),g=a(n.path??"/voice",n.controllerOptions),l=null,C=null,i=()=>{let I=u({...n.labels!==void 0?{labels:n.labels}:{},state:{assistantAudio:g.assistantAudio,error:g.error,isConnected:g.isConnected,isRecording:g.isRecording,partial:g.partial,status:g.status,turns:g.turns},...n.theme!==void 0?{theme:n.theme}:{},...n.title!==void 0?{title:n.title}:{}});o.innerHTML=m(I);for(let R of o.querySelectorAll("button[data-action]")){let{action:y}=R.dataset;R.addEventListener("click",()=>{if(y==="start")g.startRecording();else if(y==="mute")g.stopRecording();else if(y==="end")g.close()})}if(g.error&&g.error!==l)l=g.error,n.onError?.(g.error);if(g.status!==C)C=g.status,n.onStatusChange?.(g.status)},V=g.subscribe(i);if(i(),n.autoStart)g.startRecording();return{controller:g,async end(){await g.close()},mute(){g.stopRecording()},async start(){await g.startRecording()},unmount(){V(),g.close(),o.innerHTML=""}}},cc="0.0.22-beta.516",nc={mount:p,version:cc};if(typeof globalThis<"u")globalThis.AbsoluteVoice=nc;var Bc=nc;})();
package/dist/index.js CHANGED
@@ -5290,6 +5290,7 @@ var createVoiceSession = (options) => {
5290
5290
  return;
5291
5291
  streamed = true;
5292
5292
  full += delta;
5293
+ send({ delta, turnId: turn.id, type: "assistant_delta" });
5293
5294
  buffer += delta;
5294
5295
  let boundary = nextSpeakableBoundary(buffer);
5295
5296
  while (boundary !== -1) {
@@ -11281,8 +11281,15 @@ var serverMessageToAction = (message) => {
11281
11281
  case "assistant":
11282
11282
  return {
11283
11283
  text: message.text,
11284
+ turnId: message.turnId,
11284
11285
  type: "assistant"
11285
11286
  };
11287
+ case "assistant_delta":
11288
+ return {
11289
+ delta: message.delta,
11290
+ turnId: message.turnId,
11291
+ type: "assistant_delta"
11292
+ };
11286
11293
  case "complete":
11287
11294
  return {
11288
11295
  sessionId: message.sessionId,
@@ -11701,6 +11708,7 @@ var createInitialReconnectState = () => ({
11701
11708
  });
11702
11709
  var createInitialState = () => ({
11703
11710
  assistantAudio: [],
11711
+ assistantStreamingText: "",
11704
11712
  assistantTexts: [],
11705
11713
  call: null,
11706
11714
  error: null,
@@ -11738,9 +11746,16 @@ var createVoiceStreamStore = () => {
11738
11746
  case "assistant":
11739
11747
  state = {
11740
11748
  ...state,
11749
+ assistantStreamingText: "",
11741
11750
  assistantTexts: [...state.assistantTexts, action.text]
11742
11751
  };
11743
11752
  break;
11753
+ case "assistant_delta":
11754
+ state = {
11755
+ ...state,
11756
+ assistantStreamingText: `${state.assistantStreamingText}${action.delta}`
11757
+ };
11758
+ break;
11744
11759
  case "complete":
11745
11760
  state = {
11746
11761
  ...state,
@@ -11808,6 +11823,7 @@ var createVoiceStreamStore = () => {
11808
11823
  case "replay":
11809
11824
  state = {
11810
11825
  ...state,
11826
+ assistantStreamingText: "",
11811
11827
  assistantTexts: [...action.assistantTexts],
11812
11828
  call: action.call ?? null,
11813
11829
  error: null,
@@ -11919,6 +11935,9 @@ var createVoiceStream = (path, options = {}) => {
11919
11935
  get assistantTexts() {
11920
11936
  return store.getSnapshot().assistantTexts;
11921
11937
  },
11938
+ get assistantStreamingText() {
11939
+ return store.getSnapshot().assistantStreamingText;
11940
+ },
11922
11941
  get call() {
11923
11942
  return store.getSnapshot().call;
11924
11943
  },
@@ -12482,6 +12501,7 @@ var resolveVoiceRuntimePreset = (name = "default") => {
12482
12501
  // src/client/controller.ts
12483
12502
  var createInitialState2 = (stream) => ({
12484
12503
  assistantAudio: [...stream.assistantAudio],
12504
+ assistantStreamingText: stream.assistantStreamingText,
12485
12505
  assistantTexts: [...stream.assistantTexts],
12486
12506
  call: stream.call,
12487
12507
  error: stream.error,
@@ -12514,6 +12534,7 @@ var createVoiceController = (path, options = {}) => {
12514
12534
  state = {
12515
12535
  ...state,
12516
12536
  assistantAudio: [...stream.assistantAudio],
12537
+ assistantStreamingText: stream.assistantStreamingText,
12517
12538
  assistantTexts: [...stream.assistantTexts],
12518
12539
  call: stream.call,
12519
12540
  error: stream.error,
@@ -12607,6 +12628,9 @@ var createVoiceController = (path, options = {}) => {
12607
12628
  get assistantTexts() {
12608
12629
  return state.assistantTexts;
12609
12630
  },
12631
+ get assistantStreamingText() {
12632
+ return state.assistantStreamingText;
12633
+ },
12610
12634
  bindHTMX(bindingOptions) {
12611
12635
  return bindVoiceHTMX(stream, bindingOptions);
12612
12636
  },
@@ -18,6 +18,7 @@ export declare const useVoiceController: <TResult = unknown>(path: string, optio
18
18
  partial: string;
19
19
  turns: import("..").VoiceTurnRecord<TResult>[];
20
20
  assistantTexts: string[];
21
+ assistantStreamingText: string;
21
22
  assistantAudio: Array<{
22
23
  chunk: Uint8Array;
23
24
  format: import("..").AudioFormat;
@@ -14,6 +14,7 @@ export declare const useVoiceStream: <TResult = unknown>(path: string, options?:
14
14
  partial: string;
15
15
  turns: import("..").VoiceTurnRecord<TResult>[];
16
16
  assistantTexts: string[];
17
+ assistantStreamingText: string;
17
18
  assistantAudio: Array<{
18
19
  chunk: Uint8Array;
19
20
  format: import("..").AudioFormat;
@@ -598,8 +598,15 @@ var serverMessageToAction = (message) => {
598
598
  case "assistant":
599
599
  return {
600
600
  text: message.text,
601
+ turnId: message.turnId,
601
602
  type: "assistant"
602
603
  };
604
+ case "assistant_delta":
605
+ return {
606
+ delta: message.delta,
607
+ turnId: message.turnId,
608
+ type: "assistant_delta"
609
+ };
603
610
  case "complete":
604
611
  return {
605
612
  sessionId: message.sessionId,
@@ -1018,6 +1025,7 @@ var createInitialReconnectState = () => ({
1018
1025
  });
1019
1026
  var createInitialState = () => ({
1020
1027
  assistantAudio: [],
1028
+ assistantStreamingText: "",
1021
1029
  assistantTexts: [],
1022
1030
  call: null,
1023
1031
  error: null,
@@ -1055,9 +1063,16 @@ var createVoiceStreamStore = () => {
1055
1063
  case "assistant":
1056
1064
  state = {
1057
1065
  ...state,
1066
+ assistantStreamingText: "",
1058
1067
  assistantTexts: [...state.assistantTexts, action.text]
1059
1068
  };
1060
1069
  break;
1070
+ case "assistant_delta":
1071
+ state = {
1072
+ ...state,
1073
+ assistantStreamingText: `${state.assistantStreamingText}${action.delta}`
1074
+ };
1075
+ break;
1061
1076
  case "complete":
1062
1077
  state = {
1063
1078
  ...state,
@@ -1125,6 +1140,7 @@ var createVoiceStreamStore = () => {
1125
1140
  case "replay":
1126
1141
  state = {
1127
1142
  ...state,
1143
+ assistantStreamingText: "",
1128
1144
  assistantTexts: [...action.assistantTexts],
1129
1145
  call: action.call ?? null,
1130
1146
  error: null,
@@ -1236,6 +1252,9 @@ var createVoiceStream = (path, options = {}) => {
1236
1252
  get assistantTexts() {
1237
1253
  return store.getSnapshot().assistantTexts;
1238
1254
  },
1255
+ get assistantStreamingText() {
1256
+ return store.getSnapshot().assistantStreamingText;
1257
+ },
1239
1258
  get call() {
1240
1259
  return store.getSnapshot().call;
1241
1260
  },
@@ -1619,6 +1638,7 @@ var resolveVoiceRuntimePreset = (name = "default") => {
1619
1638
  // src/client/controller.ts
1620
1639
  var createInitialState2 = (stream) => ({
1621
1640
  assistantAudio: [...stream.assistantAudio],
1641
+ assistantStreamingText: stream.assistantStreamingText,
1622
1642
  assistantTexts: [...stream.assistantTexts],
1623
1643
  call: stream.call,
1624
1644
  error: stream.error,
@@ -1651,6 +1671,7 @@ var createVoiceController = (path, options = {}) => {
1651
1671
  state = {
1652
1672
  ...state,
1653
1673
  assistantAudio: [...stream.assistantAudio],
1674
+ assistantStreamingText: stream.assistantStreamingText,
1654
1675
  assistantTexts: [...stream.assistantTexts],
1655
1676
  call: stream.call,
1656
1677
  error: stream.error,
@@ -1744,6 +1765,9 @@ var createVoiceController = (path, options = {}) => {
1744
1765
  get assistantTexts() {
1745
1766
  return state.assistantTexts;
1746
1767
  },
1768
+ get assistantStreamingText() {
1769
+ return state.assistantStreamingText;
1770
+ },
1747
1771
  bindHTMX(bindingOptions) {
1748
1772
  return bindVoiceHTMX(stream, bindingOptions);
1749
1773
  },
@@ -1579,6 +1579,7 @@ var buildSessionCorrectionAudit = (raw, generic, experimental, benchmarkSeeded,
1579
1579
  };
1580
1580
  // src/client/audioPlayer.ts
1581
1581
  var DEFAULT_LOOKAHEAD_MS = 15;
1582
+ var DEFAULT_VOLUME = 1;
1582
1583
  var createInitialState = () => ({
1583
1584
  activeSourceCount: 0,
1584
1585
  error: null,
@@ -1595,6 +1596,12 @@ var getAudioContextCtor = () => {
1595
1596
  }
1596
1597
  return window.AudioContext ?? window.webkitAudioContext;
1597
1598
  };
1599
+ var clampVolume = (volume) => {
1600
+ if (typeof volume !== "number" || !Number.isFinite(volume)) {
1601
+ return DEFAULT_VOLUME;
1602
+ }
1603
+ return Math.min(1, Math.max(0, volume));
1604
+ };
1598
1605
  var decodePCM16LEChunk = (audioContext, chunk) => {
1599
1606
  const { format } = chunk;
1600
1607
  if (format.container !== "raw" || format.encoding !== "pcm_s16le") {
@@ -1627,6 +1634,7 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1627
1634
  let state = createInitialState();
1628
1635
  let audioContext = null;
1629
1636
  let outputNode = null;
1637
+ let volume = clampVolume(options.volume);
1630
1638
  let queueEndTime = 0;
1631
1639
  let syncPromise = Promise.resolve();
1632
1640
  let interruptStartedAt = null;
@@ -1675,11 +1683,11 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1675
1683
  }
1676
1684
  return Math.max(0, ((context.baseLatency ?? 0) + (context.outputLatency ?? 0)) * 1000);
1677
1685
  };
1678
- const restoreOutputGain = (context) => {
1686
+ const applyOutputGain = (context) => {
1679
1687
  if (!outputNode) {
1680
1688
  return;
1681
1689
  }
1682
- const gainValue = 1;
1690
+ const gainValue = volume;
1683
1691
  if (outputNode.gain.setValueAtTime) {
1684
1692
  outputNode.gain.setValueAtTime(gainValue, context?.currentTime ?? 0);
1685
1693
  return;
@@ -1890,11 +1898,15 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1890
1898
  get queuedChunkCount() {
1891
1899
  return state.queuedChunkCount;
1892
1900
  },
1901
+ setVolume: (nextVolume) => {
1902
+ volume = clampVolume(nextVolume);
1903
+ applyOutputGain(audioContext);
1904
+ },
1893
1905
  start: async () => {
1894
1906
  try {
1895
1907
  clearError();
1896
1908
  const context = await ensureAudioContext();
1897
- restoreOutputGain(context);
1909
+ applyOutputGain(context);
1898
1910
  if (context.state === "suspended") {
1899
1911
  await context.resume();
1900
1912
  }
@@ -1918,6 +1930,9 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1918
1930
  return () => {
1919
1931
  subscribers.delete(subscriber);
1920
1932
  };
1933
+ },
1934
+ get volume() {
1935
+ return volume;
1921
1936
  }
1922
1937
  };
1923
1938
  return player;
@@ -2107,8 +2122,15 @@ var serverMessageToAction = (message) => {
2107
2122
  case "assistant":
2108
2123
  return {
2109
2124
  text: message.text,
2125
+ turnId: message.turnId,
2110
2126
  type: "assistant"
2111
2127
  };
2128
+ case "assistant_delta":
2129
+ return {
2130
+ delta: message.delta,
2131
+ turnId: message.turnId,
2132
+ type: "assistant_delta"
2133
+ };
2112
2134
  case "complete":
2113
2135
  return {
2114
2136
  sessionId: message.sessionId,
@@ -2527,6 +2549,7 @@ var createInitialReconnectState = () => ({
2527
2549
  });
2528
2550
  var createInitialState2 = () => ({
2529
2551
  assistantAudio: [],
2552
+ assistantStreamingText: "",
2530
2553
  assistantTexts: [],
2531
2554
  call: null,
2532
2555
  error: null,
@@ -2564,9 +2587,16 @@ var createVoiceStreamStore = () => {
2564
2587
  case "assistant":
2565
2588
  state = {
2566
2589
  ...state,
2590
+ assistantStreamingText: "",
2567
2591
  assistantTexts: [...state.assistantTexts, action.text]
2568
2592
  };
2569
2593
  break;
2594
+ case "assistant_delta":
2595
+ state = {
2596
+ ...state,
2597
+ assistantStreamingText: `${state.assistantStreamingText}${action.delta}`
2598
+ };
2599
+ break;
2570
2600
  case "complete":
2571
2601
  state = {
2572
2602
  ...state,
@@ -2634,6 +2664,7 @@ var createVoiceStreamStore = () => {
2634
2664
  case "replay":
2635
2665
  state = {
2636
2666
  ...state,
2667
+ assistantStreamingText: "",
2637
2668
  assistantTexts: [...action.assistantTexts],
2638
2669
  call: action.call ?? null,
2639
2670
  error: null,
@@ -2745,6 +2776,9 @@ var createVoiceStream = (path, options = {}) => {
2745
2776
  get assistantTexts() {
2746
2777
  return store.getSnapshot().assistantTexts;
2747
2778
  },
2779
+ get assistantStreamingText() {
2780
+ return store.getSnapshot().assistantStreamingText;
2781
+ },
2748
2782
  get call() {
2749
2783
  return store.getSnapshot().call;
2750
2784
  },
@@ -3128,6 +3162,7 @@ var resolveVoiceRuntimePreset = (name = "default") => {
3128
3162
  // src/client/controller.ts
3129
3163
  var createInitialState3 = (stream) => ({
3130
3164
  assistantAudio: [...stream.assistantAudio],
3165
+ assistantStreamingText: stream.assistantStreamingText,
3131
3166
  assistantTexts: [...stream.assistantTexts],
3132
3167
  call: stream.call,
3133
3168
  error: stream.error,
@@ -3160,6 +3195,7 @@ var createVoiceController = (path, options = {}) => {
3160
3195
  state = {
3161
3196
  ...state,
3162
3197
  assistantAudio: [...stream.assistantAudio],
3198
+ assistantStreamingText: stream.assistantStreamingText,
3163
3199
  assistantTexts: [...stream.assistantTexts],
3164
3200
  call: stream.call,
3165
3201
  error: stream.error,
@@ -3253,6 +3289,9 @@ var createVoiceController = (path, options = {}) => {
3253
3289
  get assistantTexts() {
3254
3290
  return state.assistantTexts;
3255
3291
  },
3292
+ get assistantStreamingText() {
3293
+ return state.assistantStreamingText;
3294
+ },
3256
3295
  bindHTMX(bindingOptions) {
3257
3296
  return bindVoiceHTMX(stream, bindingOptions);
3258
3297
  },
@@ -7161,6 +7200,7 @@ var createVoiceSession = (options) => {
7161
7200
  return;
7162
7201
  streamed = true;
7163
7202
  full += delta;
7203
+ send({ delta, turnId: turn.id, type: "assistant_delta" });
7164
7204
  buffer += delta;
7165
7205
  let boundary = nextSpeakableBoundary(buffer);
7166
7206
  while (boundary !== -1) {