@absolutejs/voice 0.0.22-beta.652 → 0.0.22-beta.654

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.
@@ -1256,11 +1256,19 @@ export type VoiceClientCloseMessage = {
1256
1256
  };
1257
1257
  export type VoiceClientCallControlMessage = {
1258
1258
  type: "call_control";
1259
+ requestId?: string;
1259
1260
  action: "complete" | "escalate" | "no-answer" | "pause" | "resume" | "transfer" | "voicemail";
1260
1261
  metadata?: Record<string, unknown>;
1261
1262
  reason?: string;
1262
1263
  target?: string;
1263
1264
  };
1265
+ export type VoiceServerCallControlAckMessage = {
1266
+ type: "call_control_ack";
1267
+ action: VoiceClientCallControlMessage["action"];
1268
+ message?: string;
1269
+ ok: boolean;
1270
+ requestId: string;
1271
+ };
1264
1272
  export type VoiceClientPingMessage = {
1265
1273
  type: "ping";
1266
1274
  };
@@ -1333,7 +1341,7 @@ export type VoiceServerConnectionMessage = {
1333
1341
  type: "connection";
1334
1342
  reconnect: VoiceReconnectClientState;
1335
1343
  };
1336
- export type VoiceServerMessage<TResult = unknown> = VoiceServerSessionMessage | VoiceServerReplayMessage<TResult> | VoiceServerPartialMessage | VoiceServerFinalMessage | VoiceServerTurnMessage<TResult> | VoiceServerAssistantMessage | VoiceServerAssistantDeltaMessage | VoiceServerAudioMessage | VoiceServerCallLifecycleMessage | VoiceServerCompleteMessage | VoiceServerErrorMessage | VoiceServerPongMessage | VoiceServerConnectionMessage;
1344
+ export type VoiceServerMessage<TResult = unknown> = VoiceServerSessionMessage | VoiceServerReplayMessage<TResult> | VoiceServerPartialMessage | VoiceServerFinalMessage | VoiceServerTurnMessage<TResult> | VoiceServerAssistantMessage | VoiceServerAssistantDeltaMessage | VoiceServerAudioMessage | VoiceServerCallControlAckMessage | VoiceServerCallLifecycleMessage | VoiceServerCompleteMessage | VoiceServerErrorMessage | VoiceServerPongMessage | VoiceServerConnectionMessage;
1337
1345
  export type VoiceConnectionOptions = {
1338
1346
  browserMedia?: false | VoiceBrowserMediaReporterOptions;
1339
1347
  protocols?: string[];
@@ -1351,6 +1359,15 @@ export type VoiceConnectionOptions = {
1351
1359
  * reset. This prevents a server that accepts and immediately drops sockets
1352
1360
  * from creating an unbounded reconnect loop. Default 30000. */
1353
1361
  reconnectResetAfterMs?: number;
1362
+ /** Refresh application-owned admission state before each reconnect attempt.
1363
+ * Throwing keeps the transport disconnected and consumes one bounded retry
1364
+ * attempt instead of opening a socket with stale credentials. */
1365
+ prepareReconnect?: (input: {
1366
+ attempt: number;
1367
+ path: string;
1368
+ scenarioId: string | null;
1369
+ sessionId: string;
1370
+ }) => Promise<void> | void;
1354
1371
  pingInterval?: number;
1355
1372
  /** Additional query values sent on every initial or reconnecting socket. */
1356
1373
  query?: Record<string, string>;
@@ -1524,7 +1541,7 @@ export type VoiceStreamState<TResult = unknown> = {
1524
1541
  };
1525
1542
  export type VoiceStream<TResult = unknown> = {
1526
1543
  call: VoiceCallLifecycleState | null;
1527
- callControl: (message: Omit<VoiceClientCallControlMessage, "type">) => void;
1544
+ callControl: (message: Omit<VoiceClientCallControlMessage, "requestId" | "type">) => Promise<void>;
1528
1545
  close: (reason?: string) => void;
1529
1546
  /** Release the transport without terminally completing the server session. */
1530
1547
  disconnect: () => void;
@@ -1621,7 +1638,7 @@ export type VoiceBargeInBinding = {
1621
1638
  export type VoiceController<TResult = unknown> = {
1622
1639
  bindHTMX: (options: VoiceHTMXBindingOptions) => () => void;
1623
1640
  call: VoiceCallLifecycleState | null;
1624
- callControl: (message: Omit<VoiceClientCallControlMessage, "type">) => void;
1641
+ callControl: (message: Omit<VoiceClientCallControlMessage, "requestId" | "type">) => Promise<void>;
1625
1642
  close: (reason?: string) => void;
1626
1643
  /** Release the transport while leaving the server session resumable. */
1627
1644
  disconnect: () => void;
@@ -1717,6 +1734,7 @@ export type VoiceStoreAction<TResult = unknown> = {
1717
1734
  } | {
1718
1735
  type: "error";
1719
1736
  message: string;
1737
+ recoverable?: boolean;
1720
1738
  } | {
1721
1739
  type: "connected";
1722
1740
  } | {
@@ -220,8 +220,11 @@ var serverMessageToAction = (message) => {
220
220
  case "error":
221
221
  return {
222
222
  message: normalizeErrorMessage(message.message),
223
+ recoverable: message.recoverable,
223
224
  type: "error"
224
225
  };
226
+ case "call_control_ack":
227
+ return null;
225
228
  case "final":
226
229
  return {
227
230
  transcript: message.transcript,
@@ -498,14 +501,17 @@ var WS_FORBIDDEN = 4403;
498
501
  var DEFAULT_MAX_RECONNECT_ATTEMPTS = 15;
499
502
  var DEFAULT_PING_INTERVAL = 30000;
500
503
  var DEFAULT_RECONNECT_RESET_AFTER_MS = 30000;
504
+ var DEFAULT_CALL_CONTROL_ACK_TIMEOUT_MS = 5000;
505
+ var MAX_PENDING_CONTROL_MESSAGES = 100;
501
506
  var RECONNECT_BASE_DELAY_MS = 500;
502
507
  var DEFAULT_RECONNECT_MAX_DELAY_MS = 8000;
503
508
  var computeVoiceReconnectDelayMs = (attempt, baseMs, maxDelayMs) => Math.min(maxDelayMs, baseMs * 2 ** (Math.max(1, attempt) - 1));
504
509
  var DEFAULT_SCENARIO_QUERY_PARAM = "scenarioId";
505
510
  var noop = () => {};
511
+ var noopAsync = () => Promise.resolve();
506
512
  var noopUnsubscribe = () => noop;
507
513
  var NOOP_CONNECTION = {
508
- callControl: noop,
514
+ callControl: noopAsync,
509
515
  close: noop,
510
516
  disconnect: noop,
511
517
  endTurn: noop,
@@ -543,6 +549,7 @@ var isVoiceServerMessage = (value) => {
543
549
  case "audio":
544
550
  case "assistant":
545
551
  case "call_lifecycle":
552
+ case "call_control_ack":
546
553
  case "complete":
547
554
  case "connection":
548
555
  case "error":
@@ -573,6 +580,7 @@ var createVoiceConnection = (path, options = {}) => {
573
580
  return NOOP_CONNECTION;
574
581
  }
575
582
  const listeners = new Set;
583
+ const pendingCallControls = new Map;
576
584
  const shouldReconnect = options.reconnect !== false;
577
585
  const maxReconnectAttempts = options.maxReconnectAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS;
578
586
  const reconnectMaxDelayMs = options.reconnectMaxDelayMs ?? DEFAULT_RECONNECT_MAX_DELAY_MS;
@@ -618,6 +626,13 @@ var createVoiceConnection = (path, options = {}) => {
618
626
  }
619
627
  }
620
628
  };
629
+ const rejectPendingCallControls = (message) => {
630
+ for (const pending of pendingCallControls.values()) {
631
+ clearTimeout(pending.timeout);
632
+ pending.reject(new Error(message));
633
+ }
634
+ pendingCallControls.clear();
635
+ };
621
636
  const scheduleReconnect = () => {
622
637
  state.reconnectAttempts += 1;
623
638
  const delayMs = computeReconnectDelayMs(state.reconnectAttempts);
@@ -632,7 +647,7 @@ var createVoiceConnection = (path, options = {}) => {
632
647
  },
633
648
  type: "connection"
634
649
  });
635
- state.reconnectTimeout = setTimeout(() => {
650
+ state.reconnectTimeout = setTimeout(async () => {
636
651
  if (state.reconnectAttempts > maxReconnectAttempts) {
637
652
  emitConnection({
638
653
  reconnect: {
@@ -644,6 +659,28 @@ var createVoiceConnection = (path, options = {}) => {
644
659
  });
645
660
  return;
646
661
  }
662
+ try {
663
+ await options.prepareReconnect?.({
664
+ attempt: state.reconnectAttempts,
665
+ path,
666
+ scenarioId: state.scenarioId,
667
+ sessionId: state.sessionId
668
+ });
669
+ } catch {
670
+ if (state.reconnectAttempts < maxReconnectAttempts) {
671
+ scheduleReconnect();
672
+ } else {
673
+ emitConnection({
674
+ reconnect: {
675
+ attempts: state.reconnectAttempts,
676
+ maxAttempts: maxReconnectAttempts,
677
+ status: "exhausted"
678
+ },
679
+ type: "connection"
680
+ });
681
+ }
682
+ return;
683
+ }
647
684
  connect();
648
685
  }, delayMs);
649
686
  };
@@ -698,6 +735,18 @@ var createVoiceConnection = (path, options = {}) => {
698
735
  state.sessionId = parsed.sessionId;
699
736
  state.scenarioId = parsed.scenarioId ?? state.scenarioId;
700
737
  }
738
+ if (parsed.type === "call_control_ack") {
739
+ const pending = pendingCallControls.get(parsed.requestId);
740
+ if (pending) {
741
+ clearTimeout(pending.timeout);
742
+ pendingCallControls.delete(parsed.requestId);
743
+ if (parsed.ok) {
744
+ pending.resolve();
745
+ } else {
746
+ pending.reject(new Error(parsed.message ?? `${parsed.action} was not applied`));
747
+ }
748
+ }
749
+ }
701
750
  listeners.forEach((listener) => listener(parsed));
702
751
  };
703
752
  ws.onclose = (event) => {
@@ -707,6 +756,14 @@ var createVoiceConnection = (path, options = {}) => {
707
756
  state.isConnected = false;
708
757
  clearTimers();
709
758
  const reconnectable = shouldReconnect && event.code !== WS_NORMAL_CLOSURE && event.code !== WS_UNAUTHORIZED && event.code !== WS_FORBIDDEN && state.reconnectAttempts < maxReconnectAttempts;
759
+ const authorizationFailure = event.code === WS_UNAUTHORIZED || event.code === WS_FORBIDDEN;
760
+ if (authorizationFailure) {
761
+ listeners.forEach((listener) => listener({
762
+ message: event.code === WS_UNAUTHORIZED ? "Voice authorization expired or was rejected." : "Voice authorization was forbidden.",
763
+ recoverable: false,
764
+ type: "error"
765
+ }));
766
+ }
710
767
  if (reconnectable) {
711
768
  scheduleReconnect();
712
769
  } else if (shouldReconnect && event.code !== WS_NORMAL_CLOSURE) {
@@ -728,6 +785,12 @@ var createVoiceConnection = (path, options = {}) => {
728
785
  state.ws.send(value);
729
786
  return;
730
787
  }
788
+ if (typeof value !== "string") {
789
+ return;
790
+ }
791
+ if (state.pendingMessages.length >= MAX_PENDING_CONTROL_MESSAGES) {
792
+ state.pendingMessages.shift();
793
+ }
731
794
  state.pendingMessages.push(value);
732
795
  };
733
796
  const send = (message) => {
@@ -753,13 +816,23 @@ var createVoiceConnection = (path, options = {}) => {
753
816
  send({ type: "end_turn" });
754
817
  };
755
818
  const callControl = (message) => {
819
+ const requestId = crypto.randomUUID();
820
+ const { promise, reject, resolve } = Promise.withResolvers();
821
+ const timeout = setTimeout(() => {
822
+ pendingCallControls.delete(requestId);
823
+ reject(new Error(`${message.action} acknowledgement timed out`));
824
+ }, DEFAULT_CALL_CONTROL_ACK_TIMEOUT_MS);
825
+ pendingCallControls.set(requestId, { reject, resolve, timeout });
756
826
  send({
757
827
  ...message,
828
+ requestId,
758
829
  type: "call_control"
759
830
  });
831
+ return promise;
760
832
  };
761
833
  const close = (reason = "client-close") => {
762
834
  clearTimers();
835
+ rejectPendingCallControls("Voice connection closed");
763
836
  if (state.ws) {
764
837
  const ws = state.ws;
765
838
  state.ws = null;
@@ -773,6 +846,7 @@ var createVoiceConnection = (path, options = {}) => {
773
846
  };
774
847
  const disconnect = () => {
775
848
  clearTimers();
849
+ rejectPendingCallControls("Voice connection disconnected");
776
850
  if (state.ws) {
777
851
  const ws = state.ws;
778
852
  state.ws = null;
@@ -935,7 +1009,8 @@ var createVoiceStreamStore = () => {
935
1009
  case "error":
936
1010
  state = {
937
1011
  ...state,
938
- error: action.message
1012
+ error: action.message,
1013
+ ...action.recoverable === false ? { isConnected: false, status: "failed" } : {}
939
1014
  };
940
1015
  break;
941
1016
  case "final":
@@ -1075,7 +1150,7 @@ var createVoiceStream = (path, options = {}) => {
1075
1150
  return store.getSnapshot().call;
1076
1151
  },
1077
1152
  callControl(message) {
1078
- connection.callControl(message);
1153
+ return connection.callControl(message);
1079
1154
  },
1080
1155
  close(reason) {
1081
1156
  unsubscribeConnection();
@@ -1,10 +1,10 @@
1
- (()=>{var{defineProperty:B,getOwnPropertyNames:R0,getOwnPropertyDescriptor:$0}=Object,I0=Object.prototype.hasOwnProperty;function H0(g){return this[g]}var U0=(g)=>{var A=(k??=new WeakMap).get(g),C;if(A)return A;if(A=B({},"__esModule",{value:!0}),g&&typeof g==="object"||typeof g==="function"){for(var V of R0(g))if(!I0.call(A,V))B(A,V,{get:H0.bind(g,V),enumerable:!(C=$0(g,V))||C.enumerable})}return k.set(g,A),A},k;var L0=(g)=>g;function G0(g,A){this[g]=L0.bind(null,A)}var W0=(g,A)=>{for(var C in A)B(g,C,{get:A[C],enumerable:!0,configurable:!0,set:G0.bind(A,C)})};var r0={};W0(r0,{mount:()=>V0,default:()=>u0,VOICE_EMBED_VERSION:()=>C0});var y0=(g)=>{if(typeof g!=="string")return g;return document.querySelector(g)},D0=(g,A,C,V)=>{let _=A??g.getAttribute("hx-get")??"";if(!_)return"";let c=new URL(_,window.location.origin);if(V)c.searchParams.set(C,V);else c.searchParams.delete(C);return`${c.pathname}${c.search}${c.hash}`},F=(g,A)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let C=y0(A.element);if(!C)return()=>{};let V=A.eventName??"voice-refresh",_=A.sessionQueryParam??"sessionId",c=()=>{let U=window,W=D0(C,A.route,_,g.sessionId);if(W)C.setAttribute("hx-get",W);U.htmx?.process?.(C),U.htmx?.trigger?.(C,V)},L=g.subscribe(c);return c(),()=>{L()}};var S0=(g)=>Math.max(-1,Math.min(1,g)),X0=(g)=>{let A=new Int16Array(g.length);for(let C=0;C<g.length;C+=1){let V=S0(g[C]??0);A[C]=V<0?V*32768:V*32767}return new Uint8Array(A.buffer)},h0=(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 c=_/32768;V+=c*c}return Math.min(1,Math.max(0,Math.sqrt(V/C.length)*5.5))},O0=(g,A,C)=>{if(A===C)return g;let V=A/C,_=Math.round(g.length/V),c=new Float32Array(_),L=0,U=0;while(L<c.length){let W=Math.round((L+1)*V),T=0,$=0;for(let G=U;G<W&&G<g.length;G+=1)T+=g[G]??0,$+=1;c[L]=$>0?T/$:0,L+=1,U=W}return c},f=(g)=>{let A=null,C=null,V=null,_=null,c=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=(W)=>{let T=W.inputBuffer.getChannelData(0),$=O0(T,A?.sampleRate??48000,g.sampleRateHz??16000),G=X0($);g.onLevel?.(h0(G)),g.onAudio(G)},C.connect(V),V.connect(A.destination)}catch(W){throw L(),W}},L=()=>{V?.disconnect(),C?.disconnect(),_?.getTracks().forEach((U)=>U.stop()),A?.close(),g.onLevel?.(0),A=null,_=null,V=null,C=null};return{start:c,stop:L}};var M=(g)=>{if(typeof g==="string"&&g.trim())return g;if(g instanceof Error&&g.message.trim())return g.message;if(g&&typeof g==="object"){let A=g;for(let C of["message","reason","description"]){let V=A[C];if(typeof V==="string"&&V.trim())return V}if("error"in A)return M(A.error);if("cause"in A)return M(A.cause);try{return JSON.stringify(g)}catch{}}return"Unexpected error"},d=(g)=>{switch(g.type){case"audio":return{chunk:Uint8Array.from(atob(g.chunkBase64),(A)=>A.charCodeAt(0)),format:g.format,receivedAt:g.receivedAt,turnId:g.turnId,type:"audio"};case"assistant":return{text:g.text,turnId:g.turnId,type:"assistant"};case"assistant_delta":return{delta:g.delta,turnId:g.turnId,type:"assistant_delta"};case"complete":return{sessionId:g.sessionId,type:"complete"};case"connection":return{reconnect:g.reconnect,type:"connection"};case"call_lifecycle":return{event:g.event,sessionId:g.sessionId,type:"call_lifecycle"};case"error":return{message:M(g.message),type:"error"};case"final":return{transcript:g.transcript,type:"final"};case"partial":return{transcript:g.transcript,type:"partial"};case"replay":return{assistantTexts:g.assistantTexts,call:g.call,partial:g.partial,scenarioId:g.scenarioId,sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,status:g.status,turns:g.turns,type:"replay"};case"session":return{sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,scenarioId:g.scenarioId,status:g.status,type:"session"};case"turn":return{turn:g.turn,type:"turn"};default:return null}};var e0=Math.PI*2;var w=(g,A,C,V)=>{g.push({code:C,message:V,severity:A})};var J0=(g)=>g.length===0?void 0:g.reduce((A,C)=>A+C,0)/g.length,j=(g)=>g.length===0?void 0:Math.max(...g);var S=(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},O=(g,A)=>{let C=g[A];return typeof C==="string"?C:void 0},x=(g)=>String(g.id??O(g,"ssrc")??S(g,"ssrc")??O(g,"trackIdentifier")??O(g,"mid")??"unknown"),l=(g)=>g===void 0?void 0:g*1000;var Y0=(g)=>{let A={};for(let[C,V]of Object.entries(g))if(V===null||typeof V==="boolean"||typeof V==="number"||typeof V==="string")A[C]=V;return A};var v=(g={})=>{let A=g.stats??[],C=[],V=A.filter((R)=>R.type==="inbound-rtp"&&O(R,"kind")!=="video"),_=A.filter((R)=>R.type==="outbound-rtp"&&O(R,"kind")!=="video"),c=A.filter((R)=>R.type==="candidate-pair"),L=A.filter((R)=>(R.type==="track"||R.type==="media-source")&&O(R,"kind")==="audio"),U=c.filter((R)=>b(R,"selected")===!0||b(R,"nominated")===!0||O(R,"state")==="succeeded").length,W=L.filter((R)=>O(R,"readyState")!=="ended"&&O(R,"trackState")!=="ended"&&b(R,"ended")!==!0).length,T=L.filter((R)=>O(R,"readyState")==="ended"||O(R,"trackState")==="ended"||b(R,"ended")===!0).length,$=V.reduce((R,X)=>R+(S(X,"packetsReceived")??0),0),G=_.reduce((R,X)=>R+(S(X,"packetsSent")??0),0),I=[...V,..._].reduce((R,X)=>R+Math.max(0,S(X,"packetsLost")??0),0),P=$+I,J=P===0?0:I/P,y=V.reduce((R,X)=>R+(S(X,"bytesReceived")??0),0),h=_.reduce((R,X)=>R+(S(X,"bytesSent")??0),0),Y=j(c.map((R)=>l(S(R,"currentRoundTripTime")??S(R,"roundTripTime"))).filter((R)=>R!==void 0)),Z=j([...V,..._].map((R)=>l(S(R,"jitter"))).filter((R)=>R!==void 0)),N=j(V.map((R)=>{let X=S(R,"jitterBufferDelay"),z=S(R,"jitterBufferEmittedCount");return X!==void 0&&z!==void 0&&z>0?X/z*1000:void 0}).filter((R)=>R!==void 0)),q=L.map((R)=>S(R,"audioLevel")).filter((R)=>R!==void 0);if(g.requireConnectedCandidatePair&&c.length>0&&U===0)w(C,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(g.requireLiveAudioTrack&&W===0)w(C,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(g.maxPacketLossRatio!==void 0&&J>g.maxPacketLossRatio)w(C,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(J)} above ${String(g.maxPacketLossRatio)}.`);if(g.maxRoundTripTimeMs!==void 0&&Y!==void 0&&Y>g.maxRoundTripTimeMs)w(C,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(Y)}ms above ${String(g.maxRoundTripTimeMs)}ms.`);if(g.maxJitterMs!==void 0&&Z!==void 0&&Z>g.maxJitterMs)w(C,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(Z)}ms above ${String(g.maxJitterMs)}ms.`);return{activeCandidatePairs:U,audioLevelAverage:J0(q),bytesReceived:y,bytesSent:h,checkedAt:Date.now(),endedAudioTracks:T,inboundPackets:$,issues:C,jitterBufferDelayMs:N,jitterMs:Z,liveAudioTracks:W,outboundPackets:G,packetLossRatio:J,packetsLost:I,roundTripTimeMs:Y,status:C.some((R)=>R.severity==="error")?"fail":C.length>0?"warn":"pass",totalStats:A.length}},i=async(g)=>{return[...(await g.peerConnection.getStats(g.selector??null)).values()].map(Y0)};var n=(g={})=>{let A=g.stats??[],C=g.previousStats??[],V=[],_=new Map(C.map((I)=>[x(I),I])),L=A.filter((I)=>(I.type==="inbound-rtp"||I.type==="outbound-rtp")&&O(I,"kind")!=="video"&&O(I,"mediaType")!=="video").map((I)=>{let P=I.type==="outbound-rtp"?"outbound":"inbound",J=P==="outbound"?"packetsSent":"packetsReceived",y=P==="outbound"?"bytesSent":"bytesReceived",h=_.get(x(I)),Y=S(I,J),Z=h?S(h,J):void 0,N=S(I,y),q=h?S(h,y):void 0,R=I.timestamp!==void 0&&h?.timestamp!==void 0?I.timestamp-h.timestamp:void 0;return{bytesDelta:N!==void 0&&q!==void 0?N-q:void 0,currentPackets:Y,direction:P,id:x(I),packetDelta:Y!==void 0&&Z!==void 0?Y-Z:void 0,previousPackets:Z,timeDeltaMs:R}}),U=L.filter((I)=>I.direction==="inbound"),W=L.filter((I)=>I.direction==="outbound"),T=j(L.map((I)=>I.timeDeltaMs).filter((I)=>I!==void 0)),$=U.filter((I)=>g.maxInboundPacketStallMs!==void 0&&I.timeDeltaMs!==void 0&&I.timeDeltaMs>=g.maxInboundPacketStallMs&&I.packetDelta!==void 0&&I.packetDelta<=0).length,G=W.filter((I)=>g.maxOutboundPacketStallMs!==void 0&&I.timeDeltaMs!==void 0&&I.timeDeltaMs>=g.maxOutboundPacketStallMs&&I.packetDelta!==void 0&&I.packetDelta<=0).length;if(g.requireInboundAudio&&U.length===0)w(V,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(g.requireOutboundAudio&&W.length===0)w(V,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(g.maxGapMs!==void 0&&T!==void 0&&T>g.maxGapMs)w(V,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(T)}ms above ${String(g.maxGapMs)}ms.`);if($>0)w(V,"error","media.webrtc_inbound_stalled",`${String($)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(G>0)w(V,"error","media.webrtc_outbound_stalled",`${String(G)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:U.length,issues:V,maxObservedGapMs:T,outboundAudioStreams:W.length,stalledInboundStreams:$,stalledOutboundStreams:G,status:V.some((I)=>I.severity==="error")?"fail":V.length>0?"warn":"pass",streams:L,totalStats:A.length}};var Z0="/api/voice/browser-media",w0=5000,Q0=async(g)=>g.peerConnection??await g.getPeerConnection?.()??null,P0=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"})},o=(g)=>{let A=null,C=[],V=async()=>{let L=await Q0(g);if(!L)return;let U=await i({peerConnection:L}),W=v({...g,stats:U}),T=g.continuity===!1?void 0:n({...g.continuity,previousStats:C,stats:U}),$={at:Date.now(),continuity:T,report:W,scenarioId:g.getScenarioId?.()??null,sessionId:g.getSessionId?.()??null};return C=U,g.onReport?.($),await P0($,g),$},_=()=>{V().catch((L)=>{g.onError?.(L)})},c=()=>{if(A)clearInterval(A),A=null};return{close:c,reportOnce:V,stop:c,start:()=>{if(A)return;_(),A=setInterval(_,g.intervalMs??w0)}}};var K0=(g,A,C)=>Math.min(C,A*2**(Math.max(1,g)-1));var E=()=>{},E0=()=>E,N0={callControl:E,close:E,disconnect:E,endTurn:E,send:E,sendAudio:E,simulateDisconnect:E,subscribe:E0,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",start:()=>{}},q0=()=>crypto.randomUUID(),z0=(g,A,C,V)=>{let{hostname:_,port:c,protocol:L}=window.location,U=L==="https:"?"wss:":"ws:",W=c?`:${c}`:"",T=new URL(`${U}//${_}${W}${g}`);if(T.searchParams.set("sessionId",A),C)T.searchParams.set("scenarioId",C);for(let[$,G]of Object.entries(V))if($!=="sessionId"&&$!=="scenarioId"&&G)T.searchParams.set($,G);return T.toString()},b0=(g)=>{if(!g||typeof g!=="object"||!("type"in g))return!1;switch(g.type){case"audio":case"assistant":case"call_lifecycle":case"complete":case"connection":case"error":case"final":case"partial":case"pong":case"replay":case"session":case"turn":return!0;default:return!1}},j0=(g)=>{if(typeof g.data!=="string")return null;try{let A=JSON.parse(g.data);return b0(A)?A:null}catch{return null}},m=(g,A={})=>{if(typeof window>"u")return N0;let C=new Set,V=A.reconnect!==!1,_=A.maxReconnectAttempts??15,c=A.reconnectMaxDelayMs??8000,L=A.pingInterval??30000,U=A.reconnectResetAfterMs??30000,W=(H)=>K0(H,500,c),T={isConnected:!1,pendingMessages:[],scenarioId:A.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectResetTimeout:null,reconnectTimeout:null,sessionId:A.sessionId??q0(),ws:null},$=(H)=>{C.forEach((D)=>D(H))},G=()=>{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},I=()=>{if(T.ws?.readyState!==1)return;while(T.pendingMessages.length>0){let H=T.pendingMessages.shift();if(H!==void 0)T.ws.send(H)}},P=()=>{T.reconnectAttempts+=1;let H=W(T.reconnectAttempts),D=Date.now()+H;$({reconnect:{attempts:T.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:_,nextAttemptAt:D,status:"reconnecting"},type:"connection"}),T.reconnectTimeout=setTimeout(()=>{if(T.reconnectAttempts>_){$({reconnect:{attempts:T.reconnectAttempts,maxAttempts:_,status:"exhausted"},type:"connection"});return}J()},H)},J=()=>{let H=new WebSocket(z0(g,T.sessionId,T.scenarioId,A.query??{}));H.binaryType="arraybuffer",H.onopen=()=>{if(T.ws!==H){H.close(1000);return}let D=T.reconnectAttempts>0;if(T.isConnected=!0,I(),D)$({reconnect:{attempts:T.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:_,status:"resumed"},type:"connection"}),T.reconnectResetTimeout=setTimeout(()=>{if(T.ws===H&&H.readyState===1)T.reconnectAttempts=0;T.reconnectResetTimeout=null},U);C.forEach((K)=>K({scenarioId:T.scenarioId??void 0,sessionId:T.sessionId,status:"active",type:"session"})),T.pingInterval=setInterval(()=>{if(H.readyState===1)H.send(JSON.stringify({type:"ping"}))},L)},H.onmessage=(D)=>{if(T.ws!==H)return;let K=j0(D);if(!K)return;if(K.type==="session")T.sessionId=K.sessionId,T.scenarioId=K.scenarioId??T.scenarioId;C.forEach((_0)=>_0(K))},H.onclose=(D)=>{if(T.ws!==H)return;if(T.ws=null,T.isConnected=!1,G(),V&&D.code!==1000&&D.code!==4401&&D.code!==4403&&T.reconnectAttempts<_)P();else if(V&&D.code!==1000)$({reconnect:{attempts:T.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:_,status:"exhausted"},type:"connection"})},T.ws=H},y=(H)=>{if(T.ws?.readyState===1){T.ws.send(H);return}T.pendingMessages.push(H)},h=(H)=>{y(JSON.stringify(H))},Y=(H={})=>{if(H.sessionId)T.sessionId=H.sessionId;if(H.scenarioId)T.scenarioId=H.scenarioId;h({scenarioId:T.scenarioId??void 0,sessionId:T.sessionId,type:"start"})},Z=(H)=>{y(H)},N=()=>{h({type:"end_turn"})},q=(H)=>{h({...H,type:"call_control"})},R=(H="client-close")=>{if(G(),T.ws){let D=T.ws;if(T.ws=null,D.readyState===1)D.send(JSON.stringify({reason:H,type:"close"}));D.close(1000)}T.isConnected=!1,C.clear()},X=()=>{if(G(),T.ws){let H=T.ws;T.ws=null,H.close(1000)}T.isConnected=!1,C.clear()},z=()=>{if(T.ws?.readyState===1)T.ws.close(4000,"absolutejs-voice-reconnect-proof")},T0=(H)=>{return C.add(H),()=>{C.delete(H)}};return J(),{callControl:q,close:R,disconnect:X,endTurn:N,send:h,sendAudio:Z,simulateDisconnect:z,start:Y,subscribe:T0,getReadyState:()=>T.ws?.readyState??3,getScenarioId:()=>T.scenarioId??"",getSessionId:()=>T.sessionId}};var B0=()=>({attempts:0,maxAttempts:0,status:"idle"}),M0=(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}`},x0=(g,A)=>{let C=A.trim().replace(/\s+/g," ");if(!g)return C;if(!C||g.endsWith(C))return g;return`${g} ${C}`},k0=()=>({assistantAudio:[],assistantStreamingText:"",assistantTexts:[],call:null,error:null,isConnected:!1,partial:"",reconnect:B0(),scenarioId:null,sessionId:null,sessionMetadata:null,status:"idle",turns:[]}),r=()=>{let g=k0(),A="",C=new Set,V=()=>{C.forEach((c)=>c())};return{dispatch:(c)=>{switch(c.type){case"audio":g={...g,assistantAudio:[...g.assistantAudio,{chunk:c.chunk,format:c.format,receivedAt:c.receivedAt,turnId:c.turnId}]};break;case"assistant":g={...g,assistantStreamingText:"",assistantTexts:[...g.assistantTexts,c.text]};break;case"assistant_delta":g={...g,assistantStreamingText:`${g.assistantStreamingText}${c.delta}`};break;case"complete":g={...g,sessionId:c.sessionId,status:"completed"};break;case"call_lifecycle":g={...g,call:{...g.call,disposition:c.event.type==="end"?c.event.disposition:g.call?.disposition,endedAt:c.event.type==="end"?c.event.at:g.call?.endedAt,events:[...g.call?.events??[],c.event],lastEventAt:c.event.at,startedAt:g.call?.startedAt??c.event.at},sessionId:c.sessionId};break;case"connected":g={...g,isConnected:!0,reconnect:g.reconnect.status==="reconnecting"?{...g.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:g.reconnect};break;case"connection":g={...g,reconnect:c.reconnect};break;case"disconnected":g={...g,isConnected:!1};break;case"error":g={...g,error:c.message};break;case"final":A=M0(A,c.transcript.text),g={...g,partial:A};break;case"partial":g={...g,partial:x0(A,c.transcript.text)};break;case"replay":A=c.partial,g={...g,assistantStreamingText:"",assistantTexts:[...c.assistantTexts],call:c.call??null,error:null,isConnected:c.status==="active",partial:c.partial,reconnect:g.reconnect.status==="reconnecting"?{...g.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:g.reconnect,scenarioId:c.scenarioId??g.scenarioId,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata??g.sessionMetadata,status:c.status,turns:[...c.turns]};break;case"session":g={...g,error:null,scenarioId:c.scenarioId??g.scenarioId,isConnected:c.status==="active",sessionId:c.sessionId,sessionMetadata:c.sessionMetadata??g.sessionMetadata,status:c.status};break;case"turn":A="",g={...g,partial:"",turns:[...g.turns,c.turn]};break}V()},getServerSnapshot:()=>g,getSnapshot:()=>g,subscribe:(c)=>{return C.add(c),()=>{C.delete(c)}}}};var u=(g,A={})=>{let C=m(g,A),V=r(),_=A.browserMedia&&typeof window<"u"?o({...A.browserMedia,getScenarioId:()=>A.browserMedia?A.browserMedia.getScenarioId?.()??C.getScenarioId():C.getScenarioId(),getSessionId:()=>A.browserMedia?A.browserMedia.getSessionId?.()??C.getSessionId():C.getSessionId()}):null,c=new Set,L=($)=>Promise.resolve().then(()=>{if(!$?.sessionId&&!$?.scenarioId)return;C.start($),_?.start()}),U=()=>{c.forEach(($)=>$())},W=()=>{if(!A.reconnectReportPath||typeof fetch>"u")return;let $=V.getSnapshot(),G=JSON.stringify({at:Date.now(),reconnect:$.reconnect,scenarioId:$.scenarioId,sessionId:C.getSessionId(),turnIds:$.turns.map((I)=>I.id)});fetch(A.reconnectReportPath,{body:G,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},T=C.subscribe(($)=>{let G=d($);if(G){if(V.dispatch(G),$.type==="connection")W();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($){C.callControl($)},close($){T(),_?.close(),C.close($),V.dispatch({type:"disconnected"}),U()},disconnect(){T(),_?.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($){C.sendAudio($)},get sessionId(){return C.getSessionId()},get sessionMetadata(){return V.getSnapshot().sessionMetadata},simulateDisconnect(){C.simulateDisconnect()},get status(){return V.getSnapshot().status},subscribe($){return c.add($),()=>{c.delete($)}},get turns(){return V.getSnapshot().turns}}};var s=(g)=>{if(!g||g.enabled===!1)return;return{enabled:!0,maxGain:g.maxGain??3,noiseGateAttenuation:g.noiseGateAttenuation??0.15,noiseGateThreshold:g.noiseGateThreshold??0.006,targetLevel:g.targetLevel??0.08}};var F0={balanced:{qualityProfile:"general",minSilenceMs:400,silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:"general",minSilenceMs:300,silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},"long-form":{qualityProfile:"general",minSilenceMs:600,silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},f0={"accent-heavy":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},general:{},"noisy-room":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},"short-command":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var a=(g)=>{let A=g?.profile??"fast",C=g?.qualityProfile??"general",V=F0[A],_=f0[C],c=g?.silenceMs??_.silenceMs??V.silenceMs;return{profile:A,qualityProfile:C,minSilenceMs:Math.min(c,g?.minSilenceMs??_.minSilenceMs??V.minSilenceMs),silenceMs:c,speechThreshold:g?.speechThreshold??_.speechThreshold??V.speechThreshold,transcriptStabilityMs:g?.transcriptStabilityMs??_.transcriptStabilityMs??V.transcriptStabilityMs}};var d0={chat:{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"balanced",qualityProfile:"short-command"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"fast",qualityProfile:"general"}},dictation:{audioConditioning:{enabled:!0,maxGain:2.25,noiseGateAttenuation:0.05,noiseGateThreshold:0.003,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"guided-intake":{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"turn-scoped",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"noisy-room":{audioConditioning:{enabled:!0,maxGain:3,noiseGateAttenuation:0.12,noiseGateThreshold:0.006,targetLevel:0.085},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:2100,speechThreshold:0.02,transcriptStabilityMs:1650}},"pstn-balanced":{audioConditioning:{enabled:!0,maxGain:2.8,noiseGateAttenuation:0.07,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:660,speechThreshold:0.012,transcriptStabilityMs:300}},"pstn-fast":{audioConditioning:{enabled:!0,maxGain:2.75,noiseGateAttenuation:0.06,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:620,speechThreshold:0.012,transcriptStabilityMs:280}},reliability:{audioConditioning:{enabled:!0,maxGain:2.9,noiseGateAttenuation:0.08,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room"}}},p=(g="default")=>{let A=d0[g];return{audioConditioning:s(A.audioConditioning),capture:{channelCount:A.capture?.channelCount??1,sampleRateHz:A.capture?.sampleRateHz??16000},connection:{...A.connection},name:g,sttLifecycle:A.sttLifecycle??"continuous",turnDetection:a(A.turnDetection)}};var l0=(g)=>({assistantAudio:[...g.assistantAudio],assistantStreamingText:g.assistantStreamingText,assistantTexts:[...g.assistantTexts],call:g.call,error:g.error,isConnected:g.isConnected,isRecording:!1,partial:g.partial,reconnect:g.reconnect,recordingError:null,sessionId:g.sessionId,sessionMetadata:g.sessionMetadata,scenarioId:g.scenarioId,status:g.status,turns:[...g.turns]}),t=(g,A={})=>{let C=p(A.preset),V=u(g,{...C.connection,...A.connection}),_=null,c=l0(V),L=new Set,U=()=>{for(let y of L)y()},W=()=>{if(c={...c,assistantAudio:[...V.assistantAudio],assistantStreamingText:V.assistantStreamingText,assistantTexts:[...V.assistantTexts],call:V.call,error:V.error,isConnected:V.isConnected,partial:V.partial,reconnect:V.reconnect,sessionId:V.sessionId,sessionMetadata:V.sessionMetadata,scenarioId:V.scenarioId,status:V.status,turns:[...V.turns]},A.autoStopOnComplete!==!1&&c.status==="completed"&&c.isRecording)_?.stop(),_=null,c={...c,isRecording:!1};U()},T=V.subscribe(W);W();let $=()=>{if(_)return _;return _=f({channelCount:A.capture?.channelCount??C.capture.channelCount,onLevel:A.capture?.onLevel,onAudio:(y)=>{if(A.capture?.onAudio){A.capture.onAudio(y,V.sendAudio);return}V.sendAudio(y)},sampleRateHz:A.capture?.sampleRateHz??C.capture.sampleRateHz,...A.capture?.stream?{stream:A.capture.stream}:{}}),_},G=()=>{_?.stop(),_=null,c={...c,isRecording:!1},U()},I=async()=>{if(c.isRecording)return;try{c={...c,recordingError:null},U(),await $().start(),c={...c,isRecording:!0},U()}catch(y){throw _?.stop(),_=null,c={...c,isRecording:!1,recordingError:y instanceof Error?y.message:String(y)},U(),y}};return{close:(y)=>{T(),G(),V.close(y)},disconnect:()=>{T(),G(),V.disconnect()},startRecording:I,stopRecording:G,get assistantAudio(){return c.assistantAudio},get assistantTexts(){return c.assistantTexts},get assistantStreamingText(){return c.assistantStreamingText},bindHTMX(y){return F(V,y)},get call(){return c.call},callControl:(y)=>V.callControl(y),endTurn:()=>V.endTurn(),get error(){return c.error},getServerSnapshot:()=>c,getSnapshot:()=>c,get isConnected(){return c.isConnected},get isRecording(){return c.isRecording},get partial(){return c.partial},get reconnect(){return c.reconnect},get recordingError(){return c.recordingError},get scenarioId(){return c.scenarioId},sendAudio:(y)=>V.sendAudio(y),get sessionId(){return c.sessionId},get sessionMetadata(){return c.sessionMetadata},simulateDisconnect:()=>V.simulateDisconnect(),get status(){return c.status},subscribe:(y)=>{return L.add(y),()=>{L.delete(y)}},toggleRecording:async()=>{if(c.isRecording){G();return}await I()},get turns(){return c.turns}}};var Q=(g)=>String(g).replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;");var e=(g)=>{if(!g.isConnected)return"idle";if(g.isPlaying)return"speaking";if(g.isRecording&&g.hasActivePartial)return"listening";if(g.isRecording)return"listening";if(g.lastTranscriptAt&&!g.lastAssistantAt)return"thinking";if(g.lastTranscriptAt&&g.lastAssistantAt&&g.lastTranscriptAt>g.lastAssistantAt)return"thinking";return"idle"};var v0={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},i0={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},n0=(g,A)=>{switch(g){case"listening":return A.listening;case"speaking":return A.speaking;case"thinking":return A.thinking;case"idle":return A.idle}},g0=(g)=>{let A={...v0,...g.theme},C={...i0,...g.labels},V=g.state.assistantAudio.at(-1)?.receivedAt,_=g.state.turns.at(-1)?.committedAt,c=e({hasActivePartial:g.state.partial.length>0,isConnected:g.state.isConnected,isPlaying:!1,isRecording:g.state.isRecording,lastAssistantAt:V,lastTranscriptAt:_}),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:n0(c,C);return{agentState:c,classes:{container:`absolute-voice-widget absolute-voice-widget--${c}`,dot:`absolute-voice-widget__dot${g.state.error?" absolute-voice-widget__dot--error":""}`},controls:{canEnd:g.state.isConnected,canMute:g.state.isRecording,canStart:!g.state.isRecording&&g.state.status!=="completed"},errorMessage:g.state.error??void 0,labels:C,partial:g.state.partial||void 0,statusLabel:U,theme:A,title:g.title??"Voice"}},o0=(g)=>typeof g==="number"?`${g}px`:g,A0=(g)=>{let A=g.theme,C=`background:${A.background};border-radius:${o0(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;">${Q(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;">${Q(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;">${Q(g.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${g.agentState}" class="${Q(g.classes.container)}" style="${C}">
1
+ (()=>{var{defineProperty:x,getOwnPropertyNames:U0,getOwnPropertyDescriptor:L0}=Object,G0=Object.prototype.hasOwnProperty;function W0(g){return this[g]}var D0=(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 U0(g))if(!G0.call(A,V))x(A,V,{get:W0.bind(g,V),enumerable:!(C=L0(g,V))||C.enumerable})}return f.set(g,A),A},f;var S0=(g)=>g;function y0(g,A){this[g]=S0.bind(null,A)}var X0=(g,A)=>{for(var C in A)x(g,C,{get:A[C],enumerable:!0,configurable:!0,set:y0.bind(A,C)})};var t0={};X0(t0,{mount:()=>T0,default:()=>e0,VOICE_EMBED_VERSION:()=>_0});var O0=(g)=>{if(typeof g!=="string")return g;return document.querySelector(g)},h0=(g,A,C,V)=>{let c=A??g.getAttribute("hx-get")??"";if(!c)return"";let T=new URL(c,window.location.origin);if(V)T.searchParams.set(C,V);else T.searchParams.delete(C);return`${T.pathname}${T.search}${T.hash}`},d=(g,A)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let C=O0(A.element);if(!C)return()=>{};let V=A.eventName??"voice-refresh",c=A.sessionQueryParam??"sessionId",T=()=>{let I=window,G=h0(C,A.route,c,g.sessionId);if(G)C.setAttribute("hx-get",G);I.htmx?.process?.(C),I.htmx?.trigger?.(C,V)},U=g.subscribe(T);return T(),()=>{U()}};var J0=(g)=>Math.max(-1,Math.min(1,g)),Y0=(g)=>{let A=new Int16Array(g.length);for(let C=0;C<g.length;C+=1){let V=J0(g[C]??0);A[C]=V<0?V*32768:V*32767}return new Uint8Array(A.buffer)},Z0=(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 c of C){let T=c/32768;V+=T*T}return Math.min(1,Math.max(0,Math.sqrt(V/C.length)*5.5))},w0=(g,A,C)=>{if(A===C)return g;let V=A/C,c=Math.round(g.length/V),T=new Float32Array(c),U=0,I=0;while(U<T.length){let G=Math.round((U+1)*V),W=0,_=0;for(let L=I;L<G&&L<g.length;L+=1)W+=g[L]??0,_+=1;T[U]=_>0?W/_:0,U+=1,I=G}return T},l=(g)=>{let A=null,C=null,V=null,c=null,T=async()=>{if(!g.stream&&(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia))throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let I=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!I)throw Error("Browser microphone capture requires AudioContext support.");try{if(c=g.stream??await navigator.mediaDevices.getUserMedia({audio:{autoGainControl:!0,channelCount:g.channelCount??1,echoCancellation:!0,noiseSuppression:!0}}),A=new I,A.state==="suspended")await A.resume();C=A.createMediaStreamSource(c),V=A.createScriptProcessor(4096,1,1),V.onaudioprocess=(G)=>{let W=G.inputBuffer.getChannelData(0),_=w0(W,A?.sampleRate??48000,g.sampleRateHz??16000),L=Y0(_);g.onLevel?.(Z0(L)),g.onAudio(L)},C.connect(V),V.connect(A.destination)}catch(G){throw U(),G}},U=()=>{V?.disconnect(),C?.disconnect(),c?.getTracks().forEach((I)=>I.stop()),A?.close(),g.onLevel?.(0),A=null,c=null,V=null,C=null};return{start:T,stop:U}};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 Tg=Math.PI*2;var P=(g,A,C,V)=>{g.push({code:C,message:V,severity:A})};var Q0=(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 X=(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},J=(g,A)=>{let C=g[A];return typeof C==="string"?C:void 0},F=(g)=>String(g.id??J(g,"ssrc")??X(g,"ssrc")??J(g,"trackIdentifier")??J(g,"mid")??"unknown"),i=(g)=>g===void 0?void 0:g*1000;var P0=(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((R)=>R.type==="inbound-rtp"&&J(R,"kind")!=="video"),c=A.filter((R)=>R.type==="outbound-rtp"&&J(R,"kind")!=="video"),T=A.filter((R)=>R.type==="candidate-pair"),U=A.filter((R)=>(R.type==="track"||R.type==="media-source")&&J(R,"kind")==="audio"),I=T.filter((R)=>B(R,"selected")===!0||B(R,"nominated")===!0||J(R,"state")==="succeeded").length,G=U.filter((R)=>J(R,"readyState")!=="ended"&&J(R,"trackState")!=="ended"&&B(R,"ended")!==!0).length,W=U.filter((R)=>J(R,"readyState")==="ended"||J(R,"trackState")==="ended"||B(R,"ended")===!0).length,_=V.reduce((R,O)=>R+(X(O,"packetsReceived")??0),0),L=c.reduce((R,O)=>R+(X(O,"packetsSent")??0),0),$=[...V,...c].reduce((R,O)=>R+Math.max(0,X(O,"packetsLost")??0),0),E=_+$,w=E===0?0:$/E,D=V.reduce((R,O)=>R+(X(O,"bytesReceived")??0),0),Y=c.reduce((R,O)=>R+(X(O,"bytesSent")??0),0),Z=M(T.map((R)=>i(X(R,"currentRoundTripTime")??X(R,"roundTripTime"))).filter((R)=>R!==void 0)),h=M([...V,...c].map((R)=>i(X(R,"jitter"))).filter((R)=>R!==void 0)),z=M(V.map((R)=>{let O=X(R,"jitterBufferDelay"),b=X(R,"jitterBufferEmittedCount");return O!==void 0&&b!==void 0&&b>0?O/b*1000:void 0}).filter((R)=>R!==void 0)),q=U.map((R)=>X(R,"audioLevel")).filter((R)=>R!==void 0);if(g.requireConnectedCandidatePair&&T.length>0&&I===0)P(C,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(g.requireLiveAudioTrack&&G===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&&Z!==void 0&&Z>g.maxRoundTripTimeMs)P(C,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(Z)}ms above ${String(g.maxRoundTripTimeMs)}ms.`);if(g.maxJitterMs!==void 0&&h!==void 0&&h>g.maxJitterMs)P(C,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(h)}ms above ${String(g.maxJitterMs)}ms.`);return{activeCandidatePairs:I,audioLevelAverage:Q0(q),bytesReceived:D,bytesSent:Y,checkedAt:Date.now(),endedAudioTracks:W,inboundPackets:_,issues:C,jitterBufferDelayMs:z,jitterMs:h,liveAudioTracks:G,outboundPackets:L,packetLossRatio:w,packetsLost:$,roundTripTimeMs:Z,status:C.some((R)=>R.severity==="error")?"fail":C.length>0?"warn":"pass",totalStats:A.length}},o=async(g)=>{return[...(await g.peerConnection.getStats(g.selector??null)).values()].map(P0)};var m=(g={})=>{let A=g.stats??[],C=g.previousStats??[],V=[],c=new Map(C.map(($)=>[F($),$])),U=A.filter(($)=>($.type==="inbound-rtp"||$.type==="outbound-rtp")&&J($,"kind")!=="video"&&J($,"mediaType")!=="video").map(($)=>{let E=$.type==="outbound-rtp"?"outbound":"inbound",w=E==="outbound"?"packetsSent":"packetsReceived",D=E==="outbound"?"bytesSent":"bytesReceived",Y=c.get(F($)),Z=X($,w),h=Y?X(Y,w):void 0,z=X($,D),q=Y?X(Y,D):void 0,R=$.timestamp!==void 0&&Y?.timestamp!==void 0?$.timestamp-Y.timestamp:void 0;return{bytesDelta:z!==void 0&&q!==void 0?z-q:void 0,currentPackets:Z,direction:E,id:F($),packetDelta:Z!==void 0&&h!==void 0?Z-h:void 0,previousPackets:h,timeDeltaMs:R}}),I=U.filter(($)=>$.direction==="inbound"),G=U.filter(($)=>$.direction==="outbound"),W=M(U.map(($)=>$.timeDeltaMs).filter(($)=>$!==void 0)),_=I.filter(($)=>g.maxInboundPacketStallMs!==void 0&&$.timeDeltaMs!==void 0&&$.timeDeltaMs>=g.maxInboundPacketStallMs&&$.packetDelta!==void 0&&$.packetDelta<=0).length,L=G.filter(($)=>g.maxOutboundPacketStallMs!==void 0&&$.timeDeltaMs!==void 0&&$.timeDeltaMs>=g.maxOutboundPacketStallMs&&$.packetDelta!==void 0&&$.packetDelta<=0).length;if(g.requireInboundAudio&&I.length===0)P(V,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(g.requireOutboundAudio&&G.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(_>0)P(V,"error","media.webrtc_inbound_stalled",`${String(_)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(L>0)P(V,"error","media.webrtc_outbound_stalled",`${String(L)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:I.length,issues:V,maxObservedGapMs:W,outboundAudioStreams:G.length,stalledInboundStreams:_,stalledOutboundStreams:L,status:V.some(($)=>$.severity==="error")?"fail":V.length>0?"warn":"pass",streams:U,totalStats:A.length}};var K0="/api/voice/browser-media",E0=5000,N0=async(g)=>g.peerConnection??await g.getPeerConnection?.()??null,z0=async(g,A)=>{let C=A.fetch??globalThis.fetch;if(!C)return;await C(A.path??K0,{body:JSON.stringify(g),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},r=(g)=>{let A=null,C=[],V=async()=>{let U=await N0(g);if(!U)return;let I=await o({peerConnection:U}),G=n({...g,stats:I}),W=g.continuity===!1?void 0:m({...g.continuity,previousStats:C,stats:I}),_={at:Date.now(),continuity:W,report:G,scenarioId:g.getScenarioId?.()??null,sessionId:g.getSessionId?.()??null};return C=I,g.onReport?.(_),await z0(_,g),_},c=()=>{V().catch((U)=>{g.onError?.(U)})},T=()=>{if(A)clearInterval(A),A=null};return{close:T,reportOnce:V,stop:T,start:()=>{if(A)return;c(),A=setInterval(c,g.intervalMs??E0)}}};var q0=(g,A,C)=>Math.min(C,A*2**(Math.max(1,g)-1));var N=()=>{},b0=()=>Promise.resolve(),B0=()=>N,M0={callControl:b0,close:N,disconnect:N,endTurn:N,send:N,sendAudio:N,simulateDisconnect:N,subscribe:B0,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",start:()=>{}},j0=()=>crypto.randomUUID(),x0=(g,A,C,V)=>{let{hostname:c,port:T,protocol:U}=window.location,I=U==="https:"?"wss:":"ws:",G=T?`:${T}`:"",W=new URL(`${I}//${c}${G}${g}`);if(W.searchParams.set("sessionId",A),C)W.searchParams.set("scenarioId",C);for(let[_,L]of Object.entries(V))if(_!=="sessionId"&&_!=="scenarioId"&&L)W.searchParams.set(_,L);return W.toString()},k0=(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}},F0=(g)=>{if(typeof g.data!=="string")return null;try{let A=JSON.parse(g.data);return k0(A)?A:null}catch{return null}},u=(g,A={})=>{if(typeof window>"u")return M0;let C=new Set,V=new Map,c=A.reconnect!==!1,T=A.maxReconnectAttempts??15,U=A.reconnectMaxDelayMs??8000,I=A.pingInterval??30000,G=A.reconnectResetAfterMs??30000,W=(H)=>q0(H,500,U),_={isConnected:!1,pendingMessages:[],scenarioId:A.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectResetTimeout:null,reconnectTimeout:null,sessionId:A.sessionId??j0(),ws:null},L=(H)=>{C.forEach((S)=>S(H))},$=()=>{if(_.pingInterval)clearInterval(_.pingInterval),_.pingInterval=null;if(_.reconnectTimeout)clearTimeout(_.reconnectTimeout),_.reconnectTimeout=null;if(_.reconnectResetTimeout)clearTimeout(_.reconnectResetTimeout),_.reconnectResetTimeout=null},E=()=>{if(_.ws?.readyState!==1)return;while(_.pendingMessages.length>0){let H=_.pendingMessages.shift();if(H!==void 0)_.ws.send(H)}},w=(H)=>{for(let S of V.values())clearTimeout(S.timeout),S.reject(Error(H));V.clear()},D=()=>{_.reconnectAttempts+=1;let H=W(_.reconnectAttempts),S=Date.now()+H;L({reconnect:{attempts:_.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:T,nextAttemptAt:S,status:"reconnecting"},type:"connection"}),_.reconnectTimeout=setTimeout(async()=>{if(_.reconnectAttempts>T){L({reconnect:{attempts:_.reconnectAttempts,maxAttempts:T,status:"exhausted"},type:"connection"});return}try{await A.prepareReconnect?.({attempt:_.reconnectAttempts,path:g,scenarioId:_.scenarioId,sessionId:_.sessionId})}catch{if(_.reconnectAttempts<T)D();else L({reconnect:{attempts:_.reconnectAttempts,maxAttempts:T,status:"exhausted"},type:"connection"});return}Y()},H)},Y=()=>{let H=new WebSocket(x0(g,_.sessionId,_.scenarioId,A.query??{}));H.binaryType="arraybuffer",H.onopen=()=>{if(_.ws!==H){H.close(1000);return}let S=_.reconnectAttempts>0;if(_.isConnected=!0,E(),S)L({reconnect:{attempts:_.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:T,status:"resumed"},type:"connection"}),_.reconnectResetTimeout=setTimeout(()=>{if(_.ws===H&&H.readyState===1)_.reconnectAttempts=0;_.reconnectResetTimeout=null},G);C.forEach((y)=>y({scenarioId:_.scenarioId??void 0,sessionId:_.sessionId,status:"active",type:"session"})),_.pingInterval=setInterval(()=>{if(H.readyState===1)H.send(JSON.stringify({type:"ping"}))},I)},H.onmessage=(S)=>{if(_.ws!==H)return;let y=F0(S);if(!y)return;if(y.type==="session")_.sessionId=y.sessionId,_.scenarioId=y.scenarioId??_.scenarioId;if(y.type==="call_control_ack"){let Q=V.get(y.requestId);if(Q)if(clearTimeout(Q.timeout),V.delete(y.requestId),y.ok)Q.resolve();else Q.reject(Error(y.message??`${y.action} was not applied`))}C.forEach((Q)=>Q(y))},H.onclose=(S)=>{if(_.ws!==H)return;_.ws=null,_.isConnected=!1,$();let y=c&&S.code!==1000&&S.code!==4401&&S.code!==4403&&_.reconnectAttempts<T;if(S.code===4401||S.code===4403)C.forEach((j)=>j({message:S.code===4401?"Voice authorization expired or was rejected.":"Voice authorization was forbidden.",recoverable:!1,type:"error"}));if(y)D();else if(c&&S.code!==1000)L({reconnect:{attempts:_.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:T,status:"exhausted"},type:"connection"})},_.ws=H},Z=(H)=>{if(_.ws?.readyState===1){_.ws.send(H);return}if(typeof H!=="string")return;if(_.pendingMessages.length>=100)_.pendingMessages.shift();_.pendingMessages.push(H)},h=(H)=>{Z(JSON.stringify(H))},z=(H={})=>{if(H.sessionId)_.sessionId=H.sessionId;if(H.scenarioId)_.scenarioId=H.scenarioId;h({scenarioId:_.scenarioId??void 0,sessionId:_.sessionId,type:"start"})},q=(H)=>{Z(H)},R=()=>{h({type:"end_turn"})},O=(H)=>{let S=crypto.randomUUID(),{promise:y,reject:Q,resolve:j}=Promise.withResolvers(),I0=setTimeout(()=>{V.delete(S),Q(Error(`${H.action} acknowledgement timed out`))},5000);return V.set(S,{reject:Q,resolve:j,timeout:I0}),h({...H,requestId:S,type:"call_control"}),y},b=(H="client-close")=>{if($(),w("Voice connection closed"),_.ws){let S=_.ws;if(_.ws=null,S.readyState===1)S.send(JSON.stringify({reason:H,type:"close"}));S.close(1000)}_.isConnected=!1,C.clear()},R0=()=>{if($(),w("Voice connection disconnected"),_.ws){let H=_.ws;_.ws=null,H.close(1000)}_.isConnected=!1,C.clear()},$0=()=>{if(_.ws?.readyState===1)_.ws.close(4000,"absolutejs-voice-reconnect-proof")},H0=(H)=>{return C.add(H),()=>{C.delete(H)}};return Y(),{callControl:O,close:b,disconnect:R0,endTurn:R,send:h,sendAudio:q,simulateDisconnect:$0,start:z,subscribe:H0,getReadyState:()=>_.ws?.readyState??3,getScenarioId:()=>_.scenarioId??"",getSessionId:()=>_.sessionId}};var f0=()=>({attempts:0,maxAttempts:0,status:"idle"}),d0=(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}`},l0=(g,A)=>{let C=A.trim().replace(/\s+/g," ");if(!g)return C;if(!C||g.endsWith(C))return g;return`${g} ${C}`},v0=()=>({assistantAudio:[],assistantStreamingText:"",assistantTexts:[],call:null,error:null,isConnected:!1,partial:"",reconnect:f0(),scenarioId:null,sessionId:null,sessionMetadata:null,status:"idle",turns:[]}),s=()=>{let g=v0(),A="",C=new Set,V=()=>{C.forEach((T)=>T())};return{dispatch:(T)=>{switch(T.type){case"audio":g={...g,assistantAudio:[...g.assistantAudio,{chunk:T.chunk,format:T.format,receivedAt:T.receivedAt,turnId:T.turnId}]};break;case"assistant":g={...g,assistantStreamingText:"",assistantTexts:[...g.assistantTexts,T.text]};break;case"assistant_delta":g={...g,assistantStreamingText:`${g.assistantStreamingText}${T.delta}`};break;case"complete":g={...g,sessionId:T.sessionId,status:"completed"};break;case"call_lifecycle":g={...g,call:{...g.call,disposition:T.event.type==="end"?T.event.disposition:g.call?.disposition,endedAt:T.event.type==="end"?T.event.at:g.call?.endedAt,events:[...g.call?.events??[],T.event],lastEventAt:T.event.at,startedAt:g.call?.startedAt??T.event.at},sessionId:T.sessionId};break;case"connected":g={...g,isConnected:!0,reconnect:g.reconnect.status==="reconnecting"?{...g.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:g.reconnect};break;case"connection":g={...g,reconnect:T.reconnect};break;case"disconnected":g={...g,isConnected:!1};break;case"error":g={...g,error:T.message,...T.recoverable===!1?{isConnected:!1,status:"failed"}:{}};break;case"final":A=d0(A,T.transcript.text),g={...g,partial:A};break;case"partial":g={...g,partial:l0(A,T.transcript.text)};break;case"replay":A=T.partial,g={...g,assistantStreamingText:"",assistantTexts:[...T.assistantTexts],call:T.call??null,error:null,isConnected:T.status==="active",partial:T.partial,reconnect:g.reconnect.status==="reconnecting"?{...g.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:g.reconnect,scenarioId:T.scenarioId??g.scenarioId,sessionId:T.sessionId,sessionMetadata:T.sessionMetadata??g.sessionMetadata,status:T.status,turns:[...T.turns]};break;case"session":g={...g,error:null,scenarioId:T.scenarioId??g.scenarioId,isConnected:T.status==="active",sessionId:T.sessionId,sessionMetadata:T.sessionMetadata??g.sessionMetadata,status:T.status};break;case"turn":A="",g={...g,partial:"",turns:[...g.turns,T.turn]};break}V()},getServerSnapshot:()=>g,getSnapshot:()=>g,subscribe:(T)=>{return C.add(T),()=>{C.delete(T)}}}};var a=(g,A={})=>{let C=u(g,A),V=s(),c=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,T=new Set,U=(_)=>Promise.resolve().then(()=>{if(!_?.sessionId&&!_?.scenarioId)return;C.start(_),c?.start()}),I=()=>{T.forEach((_)=>_())},G=()=>{if(!A.reconnectReportPath||typeof fetch>"u")return;let _=V.getSnapshot(),L=JSON.stringify({at:Date.now(),reconnect:_.reconnect,scenarioId:_.scenarioId,sessionId:C.getSessionId(),turnIds:_.turns.map(($)=>$.id)});fetch(A.reconnectReportPath,{body:L,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},W=C.subscribe((_)=>{let L=v(_);if(L){if(V.dispatch(L),_.type==="connection")G();I()}});return{start:U,get assistantAudio(){return V.getSnapshot().assistantAudio},get assistantTexts(){return V.getSnapshot().assistantTexts},get assistantStreamingText(){return V.getSnapshot().assistantStreamingText},get call(){return V.getSnapshot().call},callControl(_){return C.callControl(_)},close(_){W(),c?.close(),C.close(_),V.dispatch({type:"disconnected"}),I()},disconnect(){W(),c?.close(),C.disconnect(),V.dispatch({type:"disconnected"}),I()},endTurn(){C.endTurn()},get error(){return V.getSnapshot().error},getServerSnapshot(){return V.getServerSnapshot()},getSnapshot(){return V.getSnapshot()},get isConnected(){return V.getSnapshot().isConnected},get partial(){return V.getSnapshot().partial},get reconnect(){return V.getSnapshot().reconnect},get scenarioId(){return V.getSnapshot().scenarioId},sendAudio(_){C.sendAudio(_)},get sessionId(){return C.getSessionId()},get sessionMetadata(){return V.getSnapshot().sessionMetadata},simulateDisconnect(){C.simulateDisconnect()},get status(){return V.getSnapshot().status},subscribe(_){return T.add(_),()=>{T.delete(_)}},get turns(){return V.getSnapshot().turns}}};var p=(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 i0={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}},n0={"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 t=(g)=>{let A=g?.profile??"fast",C=g?.qualityProfile??"general",V=i0[A],c=n0[C],T=g?.silenceMs??c.silenceMs??V.silenceMs;return{profile:A,qualityProfile:C,minSilenceMs:Math.min(T,g?.minSilenceMs??c.minSilenceMs??V.minSilenceMs),silenceMs:T,speechThreshold:g?.speechThreshold??c.speechThreshold??V.speechThreshold,transcriptStabilityMs:g?.transcriptStabilityMs??c.transcriptStabilityMs??V.transcriptStabilityMs}};var o0={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"}}},e=(g="default")=>{let A=o0[g];return{audioConditioning:p(A.audioConditioning),capture:{channelCount:A.capture?.channelCount??1,sampleRateHz:A.capture?.sampleRateHz??16000},connection:{...A.connection},name:g,sttLifecycle:A.sttLifecycle??"continuous",turnDetection:t(A.turnDetection)}};var m0=(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]}),g0=(g,A={})=>{let C=e(A.preset),V=a(g,{...C.connection,...A.connection}),c=null,T=m0(V),U=new Set,I=()=>{for(let D of U)D()},G=()=>{if(T={...T,assistantAudio:[...V.assistantAudio],assistantStreamingText:V.assistantStreamingText,assistantTexts:[...V.assistantTexts],call:V.call,error:V.error,isConnected:V.isConnected,partial:V.partial,reconnect:V.reconnect,sessionId:V.sessionId,sessionMetadata:V.sessionMetadata,scenarioId:V.scenarioId,status:V.status,turns:[...V.turns]},A.autoStopOnComplete!==!1&&T.status==="completed"&&T.isRecording)c?.stop(),c=null,T={...T,isRecording:!1};I()},W=V.subscribe(G);G();let _=()=>{if(c)return c;return c=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}:{}}),c},L=()=>{c?.stop(),c=null,T={...T,isRecording:!1},I()},$=async()=>{if(T.isRecording)return;try{T={...T,recordingError:null},I(),await _().start(),T={...T,isRecording:!0},I()}catch(D){throw c?.stop(),c=null,T={...T,isRecording:!1,recordingError:D instanceof Error?D.message:String(D)},I(),D}};return{close:(D)=>{W(),L(),V.close(D)},disconnect:()=>{W(),L(),V.disconnect()},startRecording:$,stopRecording:L,get assistantAudio(){return T.assistantAudio},get assistantTexts(){return T.assistantTexts},get assistantStreamingText(){return T.assistantStreamingText},bindHTMX(D){return d(V,D)},get call(){return T.call},callControl:(D)=>V.callControl(D),endTurn:()=>V.endTurn(),get error(){return T.error},getServerSnapshot:()=>T,getSnapshot:()=>T,get isConnected(){return T.isConnected},get isRecording(){return T.isRecording},get partial(){return T.partial},get reconnect(){return T.reconnect},get recordingError(){return T.recordingError},get scenarioId(){return T.scenarioId},sendAudio:(D)=>V.sendAudio(D),get sessionId(){return T.sessionId},get sessionMetadata(){return T.sessionMetadata},simulateDisconnect:()=>V.simulateDisconnect(),get status(){return T.status},subscribe:(D)=>{return U.add(D),()=>{U.delete(D)}},toggleRecording:async()=>{if(T.isRecording){L();return}await $()},get turns(){return T.turns}}};var K=(g)=>String(g).replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;");var A0=(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 r0={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},u0={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},s0=(g,A)=>{switch(g){case"listening":return A.listening;case"speaking":return A.speaking;case"thinking":return A.thinking;case"idle":return A.idle}},V0=(g)=>{let A={...r0,...g.theme},C={...u0,...g.labels},V=g.state.assistantAudio.at(-1)?.receivedAt,c=g.state.turns.at(-1)?.committedAt,T=A0({hasActivePartial:g.state.partial.length>0,isConnected:g.state.isConnected,isPlaying:!1,isRecording:g.state.isRecording,lastAssistantAt:V,lastTranscriptAt:c}),U=!g.state.isConnected&&g.state.status!=="idle"&&!g.state.error,I=g.state.error?"Error":U?C.connecting:g.state.status==="completed"?C.callEnded:s0(T,C);return{agentState:T,classes:{container:`absolute-voice-widget absolute-voice-widget--${T}`,dot:`absolute-voice-widget__dot${g.state.error?" absolute-voice-widget__dot--error":""}`},controls:{canEnd:g.state.isConnected,canMute:g.state.isRecording,canStart:!g.state.isRecording&&g.state.status!=="completed"},errorMessage:g.state.error??void 0,labels:C,partial:g.state.partial||void 0,statusLabel:I,theme:A,title:g.title??"Voice"}},a0=(g)=>typeof g==="number"?`${g}px`:g,C0=(g)=>{let A=g.theme,C=`background:${A.background};border-radius:${a0(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;`,c=[];if(g.controls.canStart)c.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;">${K(g.labels.startCall)}</button>`);if(g.controls.canMute)c.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;">${K(g.labels.mute)}</button>`);if(g.controls.canEnd)c.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;">${K(g.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${g.agentState}" class="${K(g.classes.container)}" style="${C}">
2
2
  <div style="align-items:center;display:flex;gap:10px;margin-bottom:12px;">
3
- <span aria-hidden="true" class="${Q(g.classes.dot)}" style="${V}"></span>
4
- <strong style="font-size:15px;">${Q(g.title)}</strong>
5
- <span style="font-size:13px;margin-left:auto;opacity:0.7;">${Q(g.statusLabel)}</span>
3
+ <span aria-hidden="true" class="${K(g.classes.dot)}" style="${V}"></span>
4
+ <strong style="font-size:15px;">${K(g.title)}</strong>
5
+ <span style="font-size:13px;margin-left:auto;opacity:0.7;">${K(g.statusLabel)}</span>
6
6
  </div>
7
- ${g.partial?`<p style="font-size:13px;margin:8px 0 12px;opacity:0.85;word-break:break-word;">“${Q(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;">${Q(g.errorMessage)}</p>`:""}
10
- </div>`};var m0=(g)=>{if(typeof g!=="string")return g;let A=document.querySelector(g);if(!A)throw Error(`AbsoluteVoice.mount: no element matches "${g}"`);return A},V0=(g,A={})=>{let C=m0(g),V=t(A.path??"/voice",A.controllerOptions),_=null,c=null,L=()=>{let W=g0({...A.labels!==void 0?{labels:A.labels}:{},state:{assistantAudio:V.assistantAudio,error:V.error,isConnected:V.isConnected,isRecording:V.isRecording,partial:V.partial,status:V.status,turns:V.turns},...A.theme!==void 0?{theme:A.theme}:{},...A.title!==void 0?{title:A.title}:{}});C.innerHTML=A0(W);for(let T of C.querySelectorAll("button[data-action]")){let{action:$}=T.dataset;T.addEventListener("click",()=>{if($==="start")V.startRecording();else if($==="mute")V.stopRecording();else if($==="end")V.close()})}if(V.error&&V.error!==_)_=V.error,A.onError?.(V.error);if(V.status!==c)c=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=""}}},C0="0.0.22-beta.516",c0={mount:V0,version:C0};if(typeof globalThis<"u")globalThis.AbsoluteVoice=c0;var u0=c0;})();
7
+ ${g.partial?`<p style="font-size:13px;margin:8px 0 12px;opacity:0.85;word-break:break-word;">“${K(g.partial)}”</p>`:""}
8
+ <div style="display:flex;gap:10px;">${c.join("")}</div>
9
+ ${g.errorMessage?`<p style="color:${A.errorAccent};font-size:12px;margin-top:12px;">${K(g.errorMessage)}</p>`:""}
10
+ </div>`};var p0=(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=p0(g),V=g0(A.path??"/voice",A.controllerOptions),c=null,T=null,U=()=>{let G=V0({...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=C0(G);for(let W of C.querySelectorAll("button[data-action]")){let{action:_}=W.dataset;W.addEventListener("click",()=>{if(_==="start")V.startRecording();else if(_==="mute")V.stopRecording();else if(_==="end")V.close()})}if(V.error&&V.error!==c)c=V.error,A.onError?.(V.error);if(V.status!==T)T=V.status,A.onStatusChange?.(V.status)},I=V.subscribe(U);if(U(),A.autoStart)V.startRecording();return{controller:V,async end(){await V.close()},mute(){V.stopRecording()},async start(){await V.startRecording()},unmount(){I(),V.close(),C.innerHTML=""}}},_0="0.0.22-beta.516",c0={mount:T0,version:_0};if(typeof globalThis<"u")globalThis.AbsoluteVoice=c0;var e0=c0;})();
package/dist/index.js CHANGED
@@ -39917,7 +39917,7 @@ var isVoiceClientMessage = (value) => {
39917
39917
  if (value.action !== "complete" && value.action !== "escalate" && value.action !== "no-answer" && value.action !== "pause" && value.action !== "resume" && value.action !== "transfer" && value.action !== "voicemail") {
39918
39918
  return false;
39919
39919
  }
39920
- return (!("metadata" in value) || value.metadata === undefined || value.metadata !== null && typeof value.metadata === "object") && (!("reason" in value) || value.reason === undefined || typeof value.reason === "string") && (!("target" in value) || value.target === undefined || typeof value.target === "string");
39920
+ return (!("metadata" in value) || value.metadata === undefined || value.metadata !== null && typeof value.metadata === "object") && (!("requestId" in value) || value.requestId === undefined || typeof value.requestId === "string") && (!("reason" in value) || value.reason === undefined || typeof value.reason === "string") && (!("target" in value) || value.target === undefined || typeof value.target === "string");
39921
39921
  case "close":
39922
39922
  return true;
39923
39923
  case "end_turn":
@@ -39960,6 +39960,9 @@ var resolveSocketQuery = (ws) => ws.data && typeof ws.data === "object" && ("que
39960
39960
  var normalizeSocketHeaders = (value) => {
39961
39961
  if (!value || typeof value !== "object")
39962
39962
  return null;
39963
+ if ("get" in value && typeof value.get === "function" && "entries" in value && typeof value.entries === "function") {
39964
+ return value;
39965
+ }
39963
39966
  try {
39964
39967
  return new Headers(value);
39965
39968
  } catch {
@@ -40526,47 +40529,62 @@ var voice = (config) => {
40526
40529
  runtime.activeSessions.delete(sessionState.sessionId);
40527
40530
  deregisterMonitorSession(sessionState.sessionId, message.reason);
40528
40531
  }
40529
- if (message.type === "call_control" && current) {
40530
- if (message.action === "transfer") {
40531
- if (message.target) {
40532
- await current.transfer({
40533
- metadata: message.metadata,
40534
- reason: message.reason,
40535
- target: message.target
40536
- });
40537
- } else {
40538
- ws.send(JSON.stringify({
40539
- message: "call_control transfer requires target",
40540
- recoverable: true,
40541
- type: "error"
40542
- }));
40532
+ if (message.type === "call_control") {
40533
+ const acknowledge = (ok, responseMessage) => {
40534
+ if (!message.requestId)
40535
+ return;
40536
+ ws.send(JSON.stringify({
40537
+ action: message.action,
40538
+ ...responseMessage ? { message: responseMessage } : {},
40539
+ ok,
40540
+ requestId: message.requestId,
40541
+ type: "call_control_ack"
40542
+ }));
40543
+ };
40544
+ if (!current) {
40545
+ acknowledge(false, "voice session is not ready");
40546
+ } else {
40547
+ try {
40548
+ if (message.action === "transfer") {
40549
+ if (!message.target) {
40550
+ throw new Error("call_control transfer requires target");
40551
+ }
40552
+ await current.transfer({
40553
+ metadata: message.metadata,
40554
+ reason: message.reason,
40555
+ target: message.target
40556
+ });
40557
+ }
40558
+ if (message.action === "escalate") {
40559
+ await current.escalate({
40560
+ metadata: message.metadata,
40561
+ reason: message.reason ?? "client-requested-escalation"
40562
+ });
40563
+ }
40564
+ if (message.action === "voicemail") {
40565
+ await current.markVoicemail({
40566
+ metadata: message.metadata
40567
+ });
40568
+ }
40569
+ if (message.action === "no-answer") {
40570
+ await current.markNoAnswer({
40571
+ metadata: message.metadata
40572
+ });
40573
+ }
40574
+ if (message.action === "complete") {
40575
+ await current.complete();
40576
+ }
40577
+ if (message.action === "pause") {
40578
+ await current.pause();
40579
+ }
40580
+ if (message.action === "resume") {
40581
+ await current.resume();
40582
+ }
40583
+ acknowledge(true);
40584
+ } catch (error) {
40585
+ acknowledge(false, error instanceof Error ? error.message : String(error));
40543
40586
  }
40544
40587
  }
40545
- if (message.action === "escalate") {
40546
- await current.escalate({
40547
- metadata: message.metadata,
40548
- reason: message.reason ?? "client-requested-escalation"
40549
- });
40550
- }
40551
- if (message.action === "voicemail") {
40552
- await current.markVoicemail({
40553
- metadata: message.metadata
40554
- });
40555
- }
40556
- if (message.action === "no-answer") {
40557
- await current.markNoAnswer({
40558
- metadata: message.metadata
40559
- });
40560
- }
40561
- if (message.action === "complete") {
40562
- await current.complete();
40563
- }
40564
- if (message.action === "pause") {
40565
- await current.pause();
40566
- }
40567
- if (message.action === "resume") {
40568
- await current.resume();
40569
- }
40570
40588
  }
40571
40589
  if (message.type === "start" && message.sessionId && message.sessionId !== sessionState.sessionId) {
40572
40590
  const currentSession = runtime.activeSessions.get(sessionState.sessionId);