@absolutejs/voice 0.0.22-beta.584 → 0.0.22-beta.586

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.
@@ -1198,22 +1198,146 @@ var resolveAudioConditioningConfig = (config) => {
1198
1198
  };
1199
1199
  };
1200
1200
 
1201
+ // src/core/turnDetection.ts
1202
+ var DEFAULT_SILENCE_MS = 700;
1203
+ var DEFAULT_SPEECH_THRESHOLD = 0.015;
1204
+ var DEFAULT_SEMANTIC_VETO_RECHECK_MS = 1200;
1205
+ var toUint8Array = (audio) => {
1206
+ if (audio instanceof ArrayBuffer) {
1207
+ return new Uint8Array(audio);
1208
+ }
1209
+ return new Uint8Array(audio.buffer, audio.byteOffset, audio.byteLength);
1210
+ };
1211
+ var measureAudioLevel = (audio) => {
1212
+ const bytes = toUint8Array(audio);
1213
+ if (bytes.byteLength < 2) {
1214
+ return 0;
1215
+ }
1216
+ const samples = new Int16Array(bytes.buffer, bytes.byteOffset, Math.floor(bytes.byteLength / 2));
1217
+ if (samples.length === 0) {
1218
+ return 0;
1219
+ }
1220
+ let sumSquares = 0;
1221
+ for (const sample of samples) {
1222
+ const normalized = sample / 32768;
1223
+ sumSquares += normalized * normalized;
1224
+ }
1225
+ return Math.sqrt(sumSquares / samples.length);
1226
+ };
1227
+ var normalizeText = (value) => value.trim().replace(/\s+/g, " ");
1228
+ var countWords = (value) => value.length > 0 ? value.split(" ").length : 0;
1229
+ var selectPreferredTranscriptText = (currentText, nextText) => {
1230
+ const current = normalizeText(currentText);
1231
+ const next = normalizeText(nextText);
1232
+ if (!current) {
1233
+ return next;
1234
+ }
1235
+ if (!next) {
1236
+ return current;
1237
+ }
1238
+ if (current === next || current.includes(next)) {
1239
+ return current;
1240
+ }
1241
+ if (next.includes(current)) {
1242
+ return next;
1243
+ }
1244
+ if (countWords(next) > countWords(current)) {
1245
+ return next;
1246
+ }
1247
+ if (countWords(next) === countWords(current) && next.length > current.length) {
1248
+ return next;
1249
+ }
1250
+ return current;
1251
+ };
1252
+ var mergeSequentialTranscriptText = (currentText, nextText) => {
1253
+ const current = normalizeText(currentText);
1254
+ const next = normalizeText(nextText);
1255
+ if (!current) {
1256
+ return next;
1257
+ }
1258
+ if (!next) {
1259
+ return current;
1260
+ }
1261
+ const currentWords = current.split(" ");
1262
+ const nextWords = next.split(" ");
1263
+ const maxOverlap = Math.min(currentWords.length, nextWords.length);
1264
+ for (let overlap = maxOverlap;overlap > 0; overlap -= 1) {
1265
+ const currentSuffix = currentWords.slice(-overlap).join(" ");
1266
+ const nextPrefix = nextWords.slice(0, overlap).join(" ");
1267
+ if (currentSuffix === nextPrefix) {
1268
+ return [...currentWords, ...nextWords.slice(overlap)].join(" ");
1269
+ }
1270
+ }
1271
+ return `${current} ${next}`.trim();
1272
+ };
1273
+ var countCommonPrefixWords = (currentText, nextText) => {
1274
+ const currentWords = normalizeText(currentText).split(" ").filter(Boolean);
1275
+ const nextWords = normalizeText(nextText).split(" ").filter(Boolean);
1276
+ const maxWords = Math.min(currentWords.length, nextWords.length);
1277
+ let count = 0;
1278
+ for (let index = 0;index < maxWords; index += 1) {
1279
+ if (currentWords[index] !== nextWords[index]) {
1280
+ break;
1281
+ }
1282
+ count += 1;
1283
+ }
1284
+ return count;
1285
+ };
1286
+ var mergeTranscriptTexts = (transcripts) => {
1287
+ const merged = [];
1288
+ for (const transcript of transcripts) {
1289
+ const nextText = normalizeText(transcript.text);
1290
+ if (!nextText) {
1291
+ continue;
1292
+ }
1293
+ const previous = merged.at(-1);
1294
+ if (!previous) {
1295
+ merged.push(nextText);
1296
+ continue;
1297
+ }
1298
+ if (nextText === previous || previous.includes(nextText)) {
1299
+ continue;
1300
+ }
1301
+ if (nextText.includes(previous)) {
1302
+ merged[merged.length - 1] = nextText;
1303
+ continue;
1304
+ }
1305
+ merged.push(nextText);
1306
+ }
1307
+ return merged.join(" ").trim();
1308
+ };
1309
+ var buildTurnText = (transcripts, partialText, options = {}) => {
1310
+ const finalText = mergeTranscriptTexts(transcripts);
1311
+ const nextPartial = normalizeText(partialText);
1312
+ const lastFinalEndedAtMs = [...transcripts].reverse().find((transcript) => typeof transcript.endedAtMs === "number")?.endedAtMs;
1313
+ if (finalText && nextPartial && typeof lastFinalEndedAtMs === "number" && typeof options.partialStartedAtMs === "number" && options.partialStartedAtMs - lastFinalEndedAtMs >= 250 && countCommonPrefixWords(finalText, nextPartial) === 0) {
1314
+ return mergeSequentialTranscriptText(finalText, nextPartial);
1315
+ }
1316
+ return selectPreferredTranscriptText(finalText, nextPartial);
1317
+ };
1318
+
1201
1319
  // src/core/turnProfiles.ts
1202
1320
  var TURN_PROFILE_DEFAULTS = {
1203
1321
  balanced: {
1204
1322
  qualityProfile: "general",
1323
+ semanticVetoMaxMs: 0,
1324
+ semanticVetoRecheckMs: DEFAULT_SEMANTIC_VETO_RECHECK_MS,
1205
1325
  silenceMs: 1400,
1206
1326
  speechThreshold: 0.012,
1207
1327
  transcriptStabilityMs: 1000
1208
1328
  },
1209
1329
  fast: {
1210
1330
  qualityProfile: "general",
1331
+ semanticVetoMaxMs: 0,
1332
+ semanticVetoRecheckMs: DEFAULT_SEMANTIC_VETO_RECHECK_MS,
1211
1333
  silenceMs: 700,
1212
1334
  speechThreshold: 0.015,
1213
1335
  transcriptStabilityMs: 450
1214
1336
  },
1215
1337
  "long-form": {
1216
1338
  qualityProfile: "general",
1339
+ semanticVetoMaxMs: 0,
1340
+ semanticVetoRecheckMs: DEFAULT_SEMANTIC_VETO_RECHECK_MS,
1217
1341
  silenceMs: 2200,
1218
1342
  speechThreshold: 0.01,
1219
1343
  transcriptStabilityMs: 1500
@@ -1247,6 +1371,8 @@ var resolveTurnDetectionConfig = (config) => {
1247
1371
  return {
1248
1372
  profile,
1249
1373
  qualityProfile,
1374
+ semanticVetoMaxMs: config?.semanticVetoMaxMs ?? preset.semanticVetoMaxMs,
1375
+ semanticVetoRecheckMs: config?.semanticVetoRecheckMs ?? preset.semanticVetoRecheckMs,
1250
1376
  silenceMs: config?.silenceMs ?? quality.silenceMs ?? preset.silenceMs,
1251
1377
  speechThreshold: config?.speechThreshold ?? quality.speechThreshold ?? preset.speechThreshold,
1252
1378
  transcriptStabilityMs: config?.transcriptStabilityMs ?? quality.transcriptStabilityMs ?? preset.transcriptStabilityMs
@@ -1078,22 +1078,31 @@ var resolveAudioConditioningConfig = (config) => {
1078
1078
  };
1079
1079
  };
1080
1080
 
1081
+ // src/core/turnDetection.ts
1082
+ var DEFAULT_SEMANTIC_VETO_RECHECK_MS = 1200;
1083
+
1081
1084
  // src/core/turnProfiles.ts
1082
1085
  var TURN_PROFILE_DEFAULTS = {
1083
1086
  balanced: {
1084
1087
  qualityProfile: "general",
1088
+ semanticVetoMaxMs: 0,
1089
+ semanticVetoRecheckMs: DEFAULT_SEMANTIC_VETO_RECHECK_MS,
1085
1090
  silenceMs: 1400,
1086
1091
  speechThreshold: 0.012,
1087
1092
  transcriptStabilityMs: 1000
1088
1093
  },
1089
1094
  fast: {
1090
1095
  qualityProfile: "general",
1096
+ semanticVetoMaxMs: 0,
1097
+ semanticVetoRecheckMs: DEFAULT_SEMANTIC_VETO_RECHECK_MS,
1091
1098
  silenceMs: 700,
1092
1099
  speechThreshold: 0.015,
1093
1100
  transcriptStabilityMs: 450
1094
1101
  },
1095
1102
  "long-form": {
1096
1103
  qualityProfile: "general",
1104
+ semanticVetoMaxMs: 0,
1105
+ semanticVetoRecheckMs: DEFAULT_SEMANTIC_VETO_RECHECK_MS,
1097
1106
  silenceMs: 2200,
1098
1107
  speechThreshold: 0.01,
1099
1108
  transcriptStabilityMs: 1500
@@ -1127,6 +1136,8 @@ var resolveTurnDetectionConfig = (config) => {
1127
1136
  return {
1128
1137
  profile,
1129
1138
  qualityProfile,
1139
+ semanticVetoMaxMs: config?.semanticVetoMaxMs ?? preset.semanticVetoMaxMs,
1140
+ semanticVetoRecheckMs: config?.semanticVetoRecheckMs ?? preset.semanticVetoRecheckMs,
1130
1141
  silenceMs: config?.silenceMs ?? quality.silenceMs ?? preset.silenceMs,
1131
1142
  speechThreshold: config?.speechThreshold ?? quality.speechThreshold ?? preset.speechThreshold,
1132
1143
  transcriptStabilityMs: config?.transcriptStabilityMs ?? quality.transcriptStabilityMs ?? preset.transcriptStabilityMs
@@ -1649,22 +1649,146 @@ var resolveAudioConditioningConfig = (config) => {
1649
1649
  };
1650
1650
  };
1651
1651
 
1652
+ // src/core/turnDetection.ts
1653
+ var DEFAULT_SILENCE_MS = 700;
1654
+ var DEFAULT_SPEECH_THRESHOLD = 0.015;
1655
+ var DEFAULT_SEMANTIC_VETO_RECHECK_MS = 1200;
1656
+ var toUint8Array = (audio) => {
1657
+ if (audio instanceof ArrayBuffer) {
1658
+ return new Uint8Array(audio);
1659
+ }
1660
+ return new Uint8Array(audio.buffer, audio.byteOffset, audio.byteLength);
1661
+ };
1662
+ var measureAudioLevel = (audio) => {
1663
+ const bytes = toUint8Array(audio);
1664
+ if (bytes.byteLength < 2) {
1665
+ return 0;
1666
+ }
1667
+ const samples = new Int16Array(bytes.buffer, bytes.byteOffset, Math.floor(bytes.byteLength / 2));
1668
+ if (samples.length === 0) {
1669
+ return 0;
1670
+ }
1671
+ let sumSquares = 0;
1672
+ for (const sample of samples) {
1673
+ const normalized = sample / 32768;
1674
+ sumSquares += normalized * normalized;
1675
+ }
1676
+ return Math.sqrt(sumSquares / samples.length);
1677
+ };
1678
+ var normalizeText = (value) => value.trim().replace(/\s+/g, " ");
1679
+ var countWords = (value) => value.length > 0 ? value.split(" ").length : 0;
1680
+ var selectPreferredTranscriptText = (currentText, nextText) => {
1681
+ const current = normalizeText(currentText);
1682
+ const next = normalizeText(nextText);
1683
+ if (!current) {
1684
+ return next;
1685
+ }
1686
+ if (!next) {
1687
+ return current;
1688
+ }
1689
+ if (current === next || current.includes(next)) {
1690
+ return current;
1691
+ }
1692
+ if (next.includes(current)) {
1693
+ return next;
1694
+ }
1695
+ if (countWords(next) > countWords(current)) {
1696
+ return next;
1697
+ }
1698
+ if (countWords(next) === countWords(current) && next.length > current.length) {
1699
+ return next;
1700
+ }
1701
+ return current;
1702
+ };
1703
+ var mergeSequentialTranscriptText = (currentText, nextText) => {
1704
+ const current = normalizeText(currentText);
1705
+ const next = normalizeText(nextText);
1706
+ if (!current) {
1707
+ return next;
1708
+ }
1709
+ if (!next) {
1710
+ return current;
1711
+ }
1712
+ const currentWords = current.split(" ");
1713
+ const nextWords = next.split(" ");
1714
+ const maxOverlap = Math.min(currentWords.length, nextWords.length);
1715
+ for (let overlap = maxOverlap;overlap > 0; overlap -= 1) {
1716
+ const currentSuffix = currentWords.slice(-overlap).join(" ");
1717
+ const nextPrefix = nextWords.slice(0, overlap).join(" ");
1718
+ if (currentSuffix === nextPrefix) {
1719
+ return [...currentWords, ...nextWords.slice(overlap)].join(" ");
1720
+ }
1721
+ }
1722
+ return `${current} ${next}`.trim();
1723
+ };
1724
+ var countCommonPrefixWords = (currentText, nextText) => {
1725
+ const currentWords = normalizeText(currentText).split(" ").filter(Boolean);
1726
+ const nextWords = normalizeText(nextText).split(" ").filter(Boolean);
1727
+ const maxWords = Math.min(currentWords.length, nextWords.length);
1728
+ let count = 0;
1729
+ for (let index = 0;index < maxWords; index += 1) {
1730
+ if (currentWords[index] !== nextWords[index]) {
1731
+ break;
1732
+ }
1733
+ count += 1;
1734
+ }
1735
+ return count;
1736
+ };
1737
+ var mergeTranscriptTexts = (transcripts) => {
1738
+ const merged = [];
1739
+ for (const transcript of transcripts) {
1740
+ const nextText = normalizeText(transcript.text);
1741
+ if (!nextText) {
1742
+ continue;
1743
+ }
1744
+ const previous = merged.at(-1);
1745
+ if (!previous) {
1746
+ merged.push(nextText);
1747
+ continue;
1748
+ }
1749
+ if (nextText === previous || previous.includes(nextText)) {
1750
+ continue;
1751
+ }
1752
+ if (nextText.includes(previous)) {
1753
+ merged[merged.length - 1] = nextText;
1754
+ continue;
1755
+ }
1756
+ merged.push(nextText);
1757
+ }
1758
+ return merged.join(" ").trim();
1759
+ };
1760
+ var buildTurnText = (transcripts, partialText, options = {}) => {
1761
+ const finalText = mergeTranscriptTexts(transcripts);
1762
+ const nextPartial = normalizeText(partialText);
1763
+ const lastFinalEndedAtMs = [...transcripts].reverse().find((transcript) => typeof transcript.endedAtMs === "number")?.endedAtMs;
1764
+ if (finalText && nextPartial && typeof lastFinalEndedAtMs === "number" && typeof options.partialStartedAtMs === "number" && options.partialStartedAtMs - lastFinalEndedAtMs >= 250 && countCommonPrefixWords(finalText, nextPartial) === 0) {
1765
+ return mergeSequentialTranscriptText(finalText, nextPartial);
1766
+ }
1767
+ return selectPreferredTranscriptText(finalText, nextPartial);
1768
+ };
1769
+
1652
1770
  // src/core/turnProfiles.ts
1653
1771
  var TURN_PROFILE_DEFAULTS = {
1654
1772
  balanced: {
1655
1773
  qualityProfile: "general",
1774
+ semanticVetoMaxMs: 0,
1775
+ semanticVetoRecheckMs: DEFAULT_SEMANTIC_VETO_RECHECK_MS,
1656
1776
  silenceMs: 1400,
1657
1777
  speechThreshold: 0.012,
1658
1778
  transcriptStabilityMs: 1000
1659
1779
  },
1660
1780
  fast: {
1661
1781
  qualityProfile: "general",
1782
+ semanticVetoMaxMs: 0,
1783
+ semanticVetoRecheckMs: DEFAULT_SEMANTIC_VETO_RECHECK_MS,
1662
1784
  silenceMs: 700,
1663
1785
  speechThreshold: 0.015,
1664
1786
  transcriptStabilityMs: 450
1665
1787
  },
1666
1788
  "long-form": {
1667
1789
  qualityProfile: "general",
1790
+ semanticVetoMaxMs: 0,
1791
+ semanticVetoRecheckMs: DEFAULT_SEMANTIC_VETO_RECHECK_MS,
1668
1792
  silenceMs: 2200,
1669
1793
  speechThreshold: 0.01,
1670
1794
  transcriptStabilityMs: 1500
@@ -1698,6 +1822,8 @@ var resolveTurnDetectionConfig = (config) => {
1698
1822
  return {
1699
1823
  profile,
1700
1824
  qualityProfile,
1825
+ semanticVetoMaxMs: config?.semanticVetoMaxMs ?? preset.semanticVetoMaxMs,
1826
+ semanticVetoRecheckMs: config?.semanticVetoRecheckMs ?? preset.semanticVetoRecheckMs,
1701
1827
  silenceMs: config?.silenceMs ?? quality.silenceMs ?? preset.silenceMs,
1702
1828
  speechThreshold: config?.speechThreshold ?? quality.speechThreshold ?? preset.speechThreshold,
1703
1829
  transcriptStabilityMs: config?.transcriptStabilityMs ?? quality.transcriptStabilityMs ?? preset.transcriptStabilityMs
@@ -1,6 +1,7 @@
1
1
  import type { AudioChunk, Transcript } from "./types";
2
2
  export declare const DEFAULT_SILENCE_MS = 700;
3
3
  export declare const DEFAULT_SPEECH_THRESHOLD = 0.015;
4
+ export declare const DEFAULT_SEMANTIC_VETO_RECHECK_MS = 1200;
4
5
  export declare const measureAudioLevel: (audio: AudioChunk) => number;
5
6
  export declare const selectPreferredTranscriptText: (currentText: string, nextText: string) => string;
6
7
  export declare const buildTurnText: (transcripts: Transcript[], partialText: string, options?: {
@@ -438,6 +438,8 @@ export type VoiceTurnDetectionConfig = {
438
438
  silenceMs?: number;
439
439
  speechThreshold?: number;
440
440
  transcriptStabilityMs?: number;
441
+ semanticVetoMaxMs?: number;
442
+ semanticVetoRecheckMs?: number;
441
443
  };
442
444
  export type VoiceResolvedTurnDetectionConfig = {
443
445
  qualityProfile: VoiceTurnQualityProfile;
@@ -445,6 +447,8 @@ export type VoiceResolvedTurnDetectionConfig = {
445
447
  silenceMs: number;
446
448
  speechThreshold: number;
447
449
  transcriptStabilityMs: number;
450
+ semanticVetoMaxMs: number;
451
+ semanticVetoRecheckMs: number;
448
452
  };
449
453
  export type VoiceAudioConditioningConfig = {
450
454
  enabled?: boolean;
@@ -1075,22 +1075,31 @@ var resolveAudioConditioningConfig = (config) => {
1075
1075
  };
1076
1076
  };
1077
1077
 
1078
+ // src/core/turnDetection.ts
1079
+ var DEFAULT_SEMANTIC_VETO_RECHECK_MS = 1200;
1080
+
1078
1081
  // src/core/turnProfiles.ts
1079
1082
  var TURN_PROFILE_DEFAULTS = {
1080
1083
  balanced: {
1081
1084
  qualityProfile: "general",
1085
+ semanticVetoMaxMs: 0,
1086
+ semanticVetoRecheckMs: DEFAULT_SEMANTIC_VETO_RECHECK_MS,
1082
1087
  silenceMs: 1400,
1083
1088
  speechThreshold: 0.012,
1084
1089
  transcriptStabilityMs: 1000
1085
1090
  },
1086
1091
  fast: {
1087
1092
  qualityProfile: "general",
1093
+ semanticVetoMaxMs: 0,
1094
+ semanticVetoRecheckMs: DEFAULT_SEMANTIC_VETO_RECHECK_MS,
1088
1095
  silenceMs: 700,
1089
1096
  speechThreshold: 0.015,
1090
1097
  transcriptStabilityMs: 450
1091
1098
  },
1092
1099
  "long-form": {
1093
1100
  qualityProfile: "general",
1101
+ semanticVetoMaxMs: 0,
1102
+ semanticVetoRecheckMs: DEFAULT_SEMANTIC_VETO_RECHECK_MS,
1094
1103
  silenceMs: 2200,
1095
1104
  speechThreshold: 0.01,
1096
1105
  transcriptStabilityMs: 1500
@@ -1124,6 +1133,8 @@ var resolveTurnDetectionConfig = (config) => {
1124
1133
  return {
1125
1134
  profile,
1126
1135
  qualityProfile,
1136
+ semanticVetoMaxMs: config?.semanticVetoMaxMs ?? preset.semanticVetoMaxMs,
1137
+ semanticVetoRecheckMs: config?.semanticVetoRecheckMs ?? preset.semanticVetoRecheckMs,
1127
1138
  silenceMs: config?.silenceMs ?? quality.silenceMs ?? preset.silenceMs,
1128
1139
  speechThreshold: config?.speechThreshold ?? quality.speechThreshold ?? preset.speechThreshold,
1129
1140
  transcriptStabilityMs: config?.transcriptStabilityMs ?? quality.transcriptStabilityMs ?? preset.transcriptStabilityMs
@@ -1,10 +1,10 @@
1
- (()=>{var{defineProperty:X,getOwnPropertyNames:Ac,getOwnPropertyDescriptor:Tc}=Object,Cc=Object.prototype.hasOwnProperty;function Ic(c){return this[c]}var Vc=(c)=>{var g=(J??=new WeakMap).get(c),A;if(g)return g;if(g=X({},"__esModule",{value:!0}),c&&typeof c==="object"||typeof c==="function"){for(var n of Ac(c))if(!Cc.call(g,n))X(g,n,{get:Ic.bind(c,n),enumerable:!(A=Tc(c,n))||A.enumerable})}return J.set(c,g),g},J;var lc=(c)=>c;function Sc(c,g){this[c]=lc.bind(null,g)}var yc=(c,g)=>{for(var A in g)X(c,A,{get:g[A],enumerable:!0,configurable:!0,set:Sc.bind(g,A)})};var jc={};yc(jc,{mount:()=>p,default:()=>oc,VOICE_EMBED_VERSION:()=>cc});var _c=(c)=>{if(typeof c!=="string")return c;return document.querySelector(c)},Rc=(c,g,A,n)=>{let T=g??c.getAttribute("hx-get")??"";if(!T)return"";let V=new URL(T,window.location.origin);if(n)V.searchParams.set(A,n);else V.searchParams.delete(A);return`${V.pathname}${V.search}${V.hash}`},Z=(c,g)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let A=_c(g.element);if(!A)return()=>{};let n=g.eventName??"voice-refresh",T=g.sessionQueryParam??"sessionId",V=()=>{let y=window,_=Rc(A,g.route,T,c.sessionId);if(_)A.setAttribute("hx-get",_);y.htmx?.process?.(A),y.htmx?.trigger?.(A,n)},I=c.subscribe(V);return V(),()=>{I()}};var hc=(c)=>Math.max(-1,Math.min(1,c)),Lc=(c)=>{let g=new Int16Array(c.length);for(let A=0;A<c.length;A+=1){let n=hc(c[A]??0);g[A]=n<0?n*32768:n*32767}return new Uint8Array(g.buffer)},Uc=(c)=>{let g=c instanceof Uint8Array?c:new Uint8Array(c);if(g.byteLength<2)return 0;let A=new Int16Array(g.buffer,g.byteOffset,Math.floor(g.byteLength/2));if(A.length===0)return 0;let n=0;for(let T of A){let V=T/32768;n+=V*V}return Math.min(1,Math.max(0,Math.sqrt(n/A.length)*5.5))},wc=(c,g,A)=>{if(g===A)return c;let n=g/A,T=Math.round(c.length/n),V=new Float32Array(T),I=0,y=0;while(I<V.length){let _=Math.round((I+1)*n),L=0,S=0;for(let h=y;h<_&&h<c.length;h+=1)L+=c[h]??0,S+=1;V[I]=S>0?L/S:0,I+=1,y=_}return V},q=(c)=>{let g=null,A=null,n=null,T=null;return{start:async()=>{if(!c.stream&&(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia))throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let y=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!y)throw Error("Browser microphone capture requires AudioContext support.");if(T=c.stream??await navigator.mediaDevices.getUserMedia({audio:{autoGainControl:!0,channelCount:c.channelCount??1,echoCancellation:!0,noiseSuppression:!0}}),g=new y,g.state==="suspended")await g.resume();A=g.createMediaStreamSource(T),n=g.createScriptProcessor(4096,1,1),n.onaudioprocess=(_)=>{let L=_.inputBuffer.getChannelData(0),S=wc(L,g?.sampleRate??48000,c.sampleRateHz??16000),h=Lc(S);c.onLevel?.(Uc(h)),c.onAudio(h)},A.connect(n),n.connect(g.destination)},stop:()=>{n?.disconnect(),A?.disconnect(),T?.getTracks().forEach((y)=>y.stop()),g?.close(),c.onLevel?.(0),g=null,T=null,n=null,A=null}}};var Y=(c)=>{if(typeof c==="string"&&c.trim())return c;if(c instanceof Error&&c.message.trim())return c.message;if(c&&typeof c==="object"){let g=c;for(let A of["message","reason","description"]){let n=g[A];if(typeof n==="string"&&n.trim())return n}if("error"in g)return Y(g.error);if("cause"in g)return Y(g.cause);try{return JSON.stringify(c)}catch{}}return"Unexpected error"},z=(c)=>{switch(c.type){case"audio":return{chunk:Uint8Array.from(atob(c.chunkBase64),(g)=>g.charCodeAt(0)),format:c.format,receivedAt:c.receivedAt,turnId:c.turnId,type:"audio"};case"assistant":return{text:c.text,turnId:c.turnId,type:"assistant"};case"assistant_delta":return{delta:c.delta,turnId:c.turnId,type:"assistant_delta"};case"complete":return{sessionId:c.sessionId,type:"complete"};case"connection":return{reconnect:c.reconnect,type:"connection"};case"call_lifecycle":return{event:c.event,sessionId:c.sessionId,type:"call_lifecycle"};case"error":return{message:Y(c.message),type:"error"};case"final":return{transcript:c.transcript,type:"final"};case"partial":return{transcript:c.transcript,type:"partial"};case"replay":return{assistantTexts:c.assistantTexts,call:c.call,partial:c.partial,scenarioId:c.scenarioId,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,status:c.status,turns:c.turns,type:"replay"};case"session":return{sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,scenarioId:c.scenarioId,status:c.status,type:"session"};case"turn":return{turn:c.turn,type:"turn"};default:return null}};var ec=Math.PI*2;var P=(c,g,A,n)=>{c.push({code:A,message:n,severity:g})};var Dc=(c)=>c.length===0?void 0:c.reduce((g,A)=>g+A,0)/c.length,G=(c)=>c.length===0?void 0:Math.max(...c);var w=(c,g)=>{let A=c[g];return typeof A==="number"&&Number.isFinite(A)?A:void 0},x=(c,g)=>{let A=c[g];return typeof A==="boolean"?A:void 0},D=(c,g)=>{let A=c[g];return typeof A==="string"?A:void 0},Q=(c)=>String(c.id??D(c,"ssrc")??w(c,"ssrc")??D(c,"trackIdentifier")??D(c,"mid")??"unknown"),K=(c)=>c===void 0?void 0:c*1000;var bc=(c)=>{let g={};for(let[A,n]of Object.entries(c))if(n===null||typeof n==="boolean"||typeof n==="number"||typeof n==="string")g[A]=n;return g};var B=(c={})=>{let g=c.stats??[],A=[],n=g.filter((C)=>C.type==="inbound-rtp"&&D(C,"kind")!=="video"),T=g.filter((C)=>C.type==="outbound-rtp"&&D(C,"kind")!=="video"),V=g.filter((C)=>C.type==="candidate-pair"),I=g.filter((C)=>(C.type==="track"||C.type==="media-source")&&D(C,"kind")==="audio"),y=V.filter((C)=>x(C,"selected")===!0||x(C,"nominated")===!0||D(C,"state")==="succeeded").length,_=I.filter((C)=>D(C,"readyState")!=="ended"&&D(C,"trackState")!=="ended"&&x(C,"ended")!==!0).length,L=I.filter((C)=>D(C,"readyState")==="ended"||D(C,"trackState")==="ended"||x(C,"ended")===!0).length,S=n.reduce((C,U)=>C+(w(U,"packetsReceived")??0),0),h=T.reduce((C,U)=>C+(w(U,"packetsSent")??0),0),l=[...n,...T].reduce((C,U)=>C+Math.max(0,w(U,"packetsLost")??0),0),b=S+l,R=b===0?0:l/b,W=n.reduce((C,U)=>C+(w(U,"bytesReceived")??0),0),E=T.reduce((C,U)=>C+(w(U,"bytesSent")??0),0),M=G(V.map((C)=>K(w(C,"currentRoundTripTime")??w(C,"roundTripTime"))).filter((C)=>C!==void 0)),$=G([...n,...T].map((C)=>K(w(C,"jitter"))).filter((C)=>C!==void 0)),d=G(n.map((C)=>{let U=w(C,"jitterBufferDelay"),O=w(C,"jitterBufferEmittedCount");return U!==void 0&&O!==void 0&&O>0?U/O*1000:void 0}).filter((C)=>C!==void 0)),N=I.map((C)=>w(C,"audioLevel")).filter((C)=>C!==void 0);if(c.requireConnectedCandidatePair&&V.length>0&&y===0)P(A,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(c.requireLiveAudioTrack&&_===0)P(A,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(c.maxPacketLossRatio!==void 0&&R>c.maxPacketLossRatio)P(A,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(R)} above ${String(c.maxPacketLossRatio)}.`);if(c.maxRoundTripTimeMs!==void 0&&M!==void 0&&M>c.maxRoundTripTimeMs)P(A,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(M)}ms above ${String(c.maxRoundTripTimeMs)}ms.`);if(c.maxJitterMs!==void 0&&$!==void 0&&$>c.maxJitterMs)P(A,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String($)}ms above ${String(c.maxJitterMs)}ms.`);return{activeCandidatePairs:y,audioLevelAverage:Dc(N),bytesReceived:W,bytesSent:E,checkedAt:Date.now(),endedAudioTracks:L,inboundPackets:S,issues:A,jitterBufferDelayMs:d,jitterMs:$,liveAudioTracks:_,outboundPackets:h,packetLossRatio:R,packetsLost:l,roundTripTimeMs:M,status:A.some((C)=>C.severity==="error")?"fail":A.length>0?"warn":"pass",totalStats:g.length}},j=async(c)=>{return[...(await c.peerConnection.getStats(c.selector??null)).values()].map(bc)};var o=(c={})=>{let g=c.stats??[],A=c.previousStats??[],n=[],T=new Map(A.map((l)=>[Q(l),l])),I=g.filter((l)=>(l.type==="inbound-rtp"||l.type==="outbound-rtp")&&D(l,"kind")!=="video"&&D(l,"mediaType")!=="video").map((l)=>{let b=l.type==="outbound-rtp"?"outbound":"inbound",R=b==="outbound"?"packetsSent":"packetsReceived",W=b==="outbound"?"bytesSent":"bytesReceived",E=T.get(Q(l)),M=w(l,R),$=E?w(E,R):void 0,d=w(l,W),N=E?w(E,W):void 0,C=l.timestamp!==void 0&&E?.timestamp!==void 0?l.timestamp-E.timestamp:void 0;return{bytesDelta:d!==void 0&&N!==void 0?d-N:void 0,currentPackets:M,direction:b,id:Q(l),packetDelta:M!==void 0&&$!==void 0?M-$:void 0,previousPackets:$,timeDeltaMs:C}}),y=I.filter((l)=>l.direction==="inbound"),_=I.filter((l)=>l.direction==="outbound"),L=G(I.map((l)=>l.timeDeltaMs).filter((l)=>l!==void 0)),S=y.filter((l)=>c.maxInboundPacketStallMs!==void 0&&l.timeDeltaMs!==void 0&&l.timeDeltaMs>=c.maxInboundPacketStallMs&&l.packetDelta!==void 0&&l.packetDelta<=0).length,h=_.filter((l)=>c.maxOutboundPacketStallMs!==void 0&&l.timeDeltaMs!==void 0&&l.timeDeltaMs>=c.maxOutboundPacketStallMs&&l.packetDelta!==void 0&&l.packetDelta<=0).length;if(c.requireInboundAudio&&y.length===0)P(n,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(c.requireOutboundAudio&&_.length===0)P(n,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(c.maxGapMs!==void 0&&L!==void 0&&L>c.maxGapMs)P(n,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(L)}ms above ${String(c.maxGapMs)}ms.`);if(S>0)P(n,"error","media.webrtc_inbound_stalled",`${String(S)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(h>0)P(n,"error","media.webrtc_outbound_stalled",`${String(h)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:y.length,issues:n,maxObservedGapMs:L,outboundAudioStreams:_.length,stalledInboundStreams:S,stalledOutboundStreams:h,status:n.some((l)=>l.severity==="error")?"fail":n.length>0?"warn":"pass",streams:I,totalStats:g.length}};var Oc="/api/voice/browser-media",Ec=5000,Mc=async(c)=>c.peerConnection??await c.getPeerConnection?.()??null,$c=async(c,g)=>{let A=g.fetch??globalThis.fetch;if(!A)return;await A(g.path??Oc,{body:JSON.stringify(c),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},s=(c)=>{let g=null,A=[],n=async()=>{let I=await Mc(c);if(!I)return;let y=await j({peerConnection:I}),_=B({...c,stats:y}),L=c.continuity===!1?void 0:o({...c.continuity,previousStats:A,stats:y}),S={at:Date.now(),continuity:L,report:_,scenarioId:c.getScenarioId?.()??null,sessionId:c.getSessionId?.()??null};return A=y,c.onReport?.(S),await $c(S,c),S},T=()=>{n().catch((I)=>{c.onError?.(I)})},V=()=>{if(g)clearInterval(g),g=null};return{close:V,reportOnce:n,stop:V,start:()=>{if(g)return;T(),g=setInterval(T,c.intervalMs??Ec)}}};var H=()=>{},Pc=()=>H,ic={callControl:H,close:H,endTurn:H,send:H,sendAudio:H,simulateDisconnect:H,subscribe:Pc,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",start:()=>{}},Hc=()=>crypto.randomUUID(),Wc=(c,g,A)=>{let{hostname:n,port:T,protocol:V}=window.location,I=V==="https:"?"wss:":"ws:",y=T?`:${T}`:"",_=new URL(`${I}//${n}${y}${c}`);if(_.searchParams.set("sessionId",g),A)_.searchParams.set("scenarioId",A);return _.toString()},dc=(c)=>{if(!c||typeof c!=="object"||!("type"in c))return!1;switch(c.type){case"audio":case"assistant":case"call_lifecycle":case"complete":case"connection":case"error":case"final":case"partial":case"pong":case"replay":case"session":case"turn":return!0;default:return!1}},Nc=(c)=>{if(typeof c.data!=="string")return null;try{let g=JSON.parse(c.data);return dc(g)?g:null}catch{return null}},k=(c,g={})=>{if(typeof window>"u")return ic;let A=new Set,n=g.reconnect!==!1,T=g.maxReconnectAttempts??10,V=g.pingInterval??30000,I={isConnected:!1,pendingMessages:[],scenarioId:g.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:g.sessionId??Hc(),ws:null},y=(C)=>{A.forEach((U)=>U(C))},_=()=>{if(I.pingInterval)clearInterval(I.pingInterval),I.pingInterval=null;if(I.reconnectTimeout)clearTimeout(I.reconnectTimeout),I.reconnectTimeout=null},L=()=>{if(I.ws?.readyState!==1)return;while(I.pendingMessages.length>0){let C=I.pendingMessages.shift();if(C!==void 0)I.ws.send(C)}},S=()=>{let C=Date.now()+500;I.reconnectAttempts+=1,y({reconnect:{attempts:I.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:T,nextAttemptAt:C,status:"reconnecting"},type:"connection"}),I.reconnectTimeout=setTimeout(()=>{if(I.reconnectAttempts>T){y({reconnect:{attempts:I.reconnectAttempts,maxAttempts:T,status:"exhausted"},type:"connection"});return}h()},500)},h=()=>{let C=new WebSocket(Wc(c,I.sessionId,I.scenarioId));C.binaryType="arraybuffer",C.onopen=()=>{let U=I.reconnectAttempts>0;if(I.isConnected=!0,L(),U)y({reconnect:{attempts:I.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:T,status:"resumed"},type:"connection"}),I.reconnectAttempts=0;A.forEach((O)=>O({scenarioId:I.scenarioId??void 0,sessionId:I.sessionId,status:"active",type:"session"})),I.pingInterval=setInterval(()=>{if(C.readyState===1)C.send(JSON.stringify({type:"ping"}))},V)},C.onmessage=(U)=>{let O=Nc(U);if(!O)return;if(O.type==="session")I.sessionId=O.sessionId,I.scenarioId=O.scenarioId??I.scenarioId;A.forEach((nc)=>nc(O))},C.onclose=(U)=>{if(I.isConnected=!1,_(),n&&U.code!==1000&&I.reconnectAttempts<T)S();else if(n&&U.code!==1000)y({reconnect:{attempts:I.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:T,status:"exhausted"},type:"connection"})},I.ws=C},l=(C)=>{if(I.ws?.readyState===1){I.ws.send(C);return}I.pendingMessages.push(C)},b=(C)=>{l(JSON.stringify(C))},R=(C={})=>{if(C.sessionId)I.sessionId=C.sessionId;if(C.scenarioId)I.scenarioId=C.scenarioId;b({scenarioId:I.scenarioId??void 0,sessionId:I.sessionId,type:"start"})},W=(C)=>{l(C)},E=()=>{b({type:"end_turn"})},M=(C)=>{b({...C,type:"call_control"})},$=()=>{if(_(),I.ws)I.ws.close(1000),I.ws=null;I.isConnected=!1,A.clear()},d=()=>{if(I.ws?.readyState===1)I.ws.close(4000,"absolutejs-voice-reconnect-proof")},N=(C)=>{return A.add(C),()=>{A.delete(C)}};return h(),{callControl:M,close:$,endTurn:E,send:b,sendAudio:W,simulateDisconnect:d,start:R,subscribe:N,getReadyState:()=>I.ws?.readyState??3,getScenarioId:()=>I.scenarioId??"",getSessionId:()=>I.sessionId}};var xc=()=>({attempts:0,maxAttempts:0,status:"idle"}),Gc=()=>({assistantAudio:[],assistantStreamingText:"",assistantTexts:[],call:null,error:null,isConnected:!1,partial:"",reconnect:xc(),scenarioId:null,sessionId:null,sessionMetadata:null,status:"idle",turns:[]}),f=()=>{let c=Gc(),g=new Set,A=()=>{g.forEach((T)=>T())};return{dispatch:(T)=>{switch(T.type){case"audio":c={...c,assistantAudio:[...c.assistantAudio,{chunk:T.chunk,format:T.format,receivedAt:T.receivedAt,turnId:T.turnId}]};break;case"assistant":c={...c,assistantStreamingText:"",assistantTexts:[...c.assistantTexts,T.text]};break;case"assistant_delta":c={...c,assistantStreamingText:`${c.assistantStreamingText}${T.delta}`};break;case"complete":c={...c,sessionId:T.sessionId,status:"completed"};break;case"call_lifecycle":c={...c,call:{...c.call,disposition:T.event.type==="end"?T.event.disposition:c.call?.disposition,endedAt:T.event.type==="end"?T.event.at:c.call?.endedAt,events:[...c.call?.events??[],T.event],lastEventAt:T.event.at,startedAt:c.call?.startedAt??T.event.at},sessionId:T.sessionId};break;case"connected":c={...c,isConnected:!0,reconnect:c.reconnect.status==="reconnecting"?{...c.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:c.reconnect};break;case"connection":c={...c,reconnect:T.reconnect};break;case"disconnected":c={...c,isConnected:!1};break;case"error":c={...c,error:T.message};break;case"final":c={...c,partial:T.transcript.text,turns:c.turns.map((V)=>V)};break;case"partial":c={...c,partial:T.transcript.text};break;case"replay":c={...c,assistantStreamingText:"",assistantTexts:[...T.assistantTexts],call:T.call??null,error:null,isConnected:T.status==="active",partial:T.partial,reconnect:c.reconnect.status==="reconnecting"?{...c.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:c.reconnect,scenarioId:T.scenarioId??c.scenarioId,sessionId:T.sessionId,sessionMetadata:T.sessionMetadata??c.sessionMetadata,status:T.status,turns:[...T.turns]};break;case"session":c={...c,error:null,scenarioId:T.scenarioId??c.scenarioId,isConnected:T.status==="active",sessionId:T.sessionId,sessionMetadata:T.sessionMetadata??c.sessionMetadata,status:T.status};break;case"turn":c={...c,partial:"",turns:[...c.turns,T.turn]};break}A()},getServerSnapshot:()=>c,getSnapshot:()=>c,subscribe:(T)=>{return g.add(T),()=>{g.delete(T)}}}};var F=(c,g={})=>{let A=k(c,g),n=f(),T=g.browserMedia&&typeof window<"u"?s({...g.browserMedia,getScenarioId:()=>g.browserMedia?g.browserMedia.getScenarioId?.()??A.getScenarioId():A.getScenarioId(),getSessionId:()=>g.browserMedia?g.browserMedia.getSessionId?.()??A.getSessionId():A.getSessionId()}):null,V=new Set,I=(S)=>Promise.resolve().then(()=>{if(!S?.sessionId&&!S?.scenarioId)return;A.start(S),T?.start()}),y=()=>{V.forEach((S)=>S())},_=()=>{if(!g.reconnectReportPath||typeof fetch>"u")return;let S=n.getSnapshot(),h=JSON.stringify({at:Date.now(),reconnect:S.reconnect,scenarioId:S.scenarioId,sessionId:A.getSessionId(),turnIds:S.turns.map((l)=>l.id)});fetch(g.reconnectReportPath,{body:h,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},L=A.subscribe((S)=>{let h=z(S);if(h){if(n.dispatch(h),S.type==="connection")_();y()}});return{start:I,get assistantAudio(){return n.getSnapshot().assistantAudio},get assistantTexts(){return n.getSnapshot().assistantTexts},get assistantStreamingText(){return n.getSnapshot().assistantStreamingText},get call(){return n.getSnapshot().call},callControl(S){A.callControl(S)},close(){L(),T?.close(),A.close(),n.dispatch({type:"disconnected"}),y()},endTurn(){A.endTurn()},get error(){return n.getSnapshot().error},getServerSnapshot(){return n.getServerSnapshot()},getSnapshot(){return n.getSnapshot()},get isConnected(){return n.getSnapshot().isConnected},get partial(){return n.getSnapshot().partial},get reconnect(){return n.getSnapshot().reconnect},get scenarioId(){return n.getSnapshot().scenarioId},sendAudio(S){A.sendAudio(S)},get sessionId(){return A.getSessionId()},get sessionMetadata(){return n.getSnapshot().sessionMetadata},simulateDisconnect(){A.simulateDisconnect()},get status(){return n.getSnapshot().status},subscribe(S){return V.add(S),()=>{V.delete(S)}},get turns(){return n.getSnapshot().turns}}};var e=(c)=>{if(!c||c.enabled===!1)return;return{enabled:!0,maxGain:c.maxGain??3,noiseGateAttenuation:c.noiseGateAttenuation??0.15,noiseGateThreshold:c.noiseGateThreshold??0.006,targetLevel:c.targetLevel??0.08}};var Xc={balanced:{qualityProfile:"general",silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:"general",silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},"long-form":{qualityProfile:"general",silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},Yc={"accent-heavy":{silenceMs:1200,speechThreshold:0.01,transcriptStabilityMs:1200},general:{},"noisy-room":{silenceMs:2000,speechThreshold:0.02,transcriptStabilityMs:1600},"short-command":{silenceMs:500,speechThreshold:0.016,transcriptStabilityMs:420}};var t=(c)=>{let g=c?.profile??"fast",A=c?.qualityProfile??"general",n=Xc[g],T=Yc[A];return{profile:g,qualityProfile:A,silenceMs:c?.silenceMs??T.silenceMs??n.silenceMs,speechThreshold:c?.speechThreshold??T.speechThreshold??n.speechThreshold,transcriptStabilityMs:c?.transcriptStabilityMs??T.transcriptStabilityMs??n.transcriptStabilityMs}};var Qc={chat:{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"balanced",qualityProfile:"short-command"}},default:{capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:10,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"fast",qualityProfile:"general"}},dictation:{audioConditioning:{enabled:!0,maxGain:2.25,noiseGateAttenuation:0.05,noiseGateThreshold:0.003,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"guided-intake":{audioConditioning:{enabled:!0,maxGain:2.5,noiseGateAttenuation:0,noiseGateThreshold:0.004,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:12,pingInterval:30000,reconnect:!0},sttLifecycle:"turn-scoped",turnDetection:{profile:"long-form",qualityProfile:"accent-heavy"}},"noisy-room":{audioConditioning:{enabled:!0,maxGain:3,noiseGateAttenuation:0.12,noiseGateThreshold:0.006,targetLevel:0.085},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:2100,speechThreshold:0.02,transcriptStabilityMs:1650}},"pstn-balanced":{audioConditioning:{enabled:!0,maxGain:2.8,noiseGateAttenuation:0.07,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:660,speechThreshold:0.012,transcriptStabilityMs:300}},"pstn-fast":{audioConditioning:{enabled:!0,maxGain:2.75,noiseGateAttenuation:0.06,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room",silenceMs:620,speechThreshold:0.012,transcriptStabilityMs:280}},reliability:{audioConditioning:{enabled:!0,maxGain:2.9,noiseGateAttenuation:0.08,noiseGateThreshold:0.005,targetLevel:0.08},capture:{channelCount:1,sampleRateHz:16000},connection:{maxReconnectAttempts:14,pingInterval:45000,reconnect:!0},sttLifecycle:"continuous",turnDetection:{profile:"long-form",qualityProfile:"noisy-room"}}},r=(c="default")=>{let g=Qc[c];return{audioConditioning:e(g.audioConditioning),capture:{channelCount:g.capture?.channelCount??1,sampleRateHz:g.capture?.sampleRateHz??16000},connection:{...g.connection},name:c,sttLifecycle:g.sttLifecycle??"continuous",turnDetection:t(g.turnDetection)}};var Jc=(c)=>({assistantAudio:[...c.assistantAudio],assistantStreamingText:c.assistantStreamingText,assistantTexts:[...c.assistantTexts],call:c.call,error:c.error,isConnected:c.isConnected,isRecording:!1,partial:c.partial,reconnect:c.reconnect,recordingError:null,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,scenarioId:c.scenarioId,status:c.status,turns:[...c.turns]}),a=(c,g={})=>{let A=r(g.preset),n=F(c,{...A.connection,...g.connection}),T=null,V=Jc(n),I=new Set,y=()=>{for(let R of I)R()},_=()=>{if(V={...V,assistantAudio:[...n.assistantAudio],assistantStreamingText:n.assistantStreamingText,assistantTexts:[...n.assistantTexts],call:n.call,error:n.error,isConnected:n.isConnected,partial:n.partial,reconnect:n.reconnect,sessionId:n.sessionId,sessionMetadata:n.sessionMetadata,scenarioId:n.scenarioId,status:n.status,turns:[...n.turns]},g.autoStopOnComplete!==!1&&V.status==="completed"&&V.isRecording)T?.stop(),T=null,V={...V,isRecording:!1};y()},L=n.subscribe(_);_();let S=()=>{if(T)return T;return T=q({channelCount:g.capture?.channelCount??A.capture.channelCount,onLevel:g.capture?.onLevel,onAudio:(R)=>{if(g.capture?.onAudio){g.capture.onAudio(R,n.sendAudio);return}n.sendAudio(R)},sampleRateHz:g.capture?.sampleRateHz??A.capture.sampleRateHz,...g.capture?.stream?{stream:g.capture.stream}:{}}),T},h=()=>{T?.stop(),T=null,V={...V,isRecording:!1},y()},l=async()=>{if(V.isRecording)return;try{V={...V,recordingError:null},y(),await S().start(),V={...V,isRecording:!0},y()}catch(R){throw T=null,V={...V,isRecording:!1,recordingError:R instanceof Error?R.message:String(R)},y(),R}};return{close:()=>{L(),h(),n.close()},startRecording:l,stopRecording:h,get assistantAudio(){return V.assistantAudio},get assistantTexts(){return V.assistantTexts},get assistantStreamingText(){return V.assistantStreamingText},bindHTMX(R){return Z(n,R)},get call(){return V.call},callControl:(R)=>n.callControl(R),endTurn:()=>n.endTurn(),get error(){return V.error},getServerSnapshot:()=>V,getSnapshot:()=>V,get isConnected(){return V.isConnected},get isRecording(){return V.isRecording},get partial(){return V.partial},get reconnect(){return V.reconnect},get recordingError(){return V.recordingError},get scenarioId(){return V.scenarioId},sendAudio:(R)=>n.sendAudio(R),get sessionId(){return V.sessionId},get sessionMetadata(){return V.sessionMetadata},simulateDisconnect:()=>n.simulateDisconnect(),get status(){return V.status},subscribe:(R)=>{return I.add(R),()=>{I.delete(R)}},toggleRecording:async()=>{if(V.isRecording){h();return}await l()},get turns(){return V.turns}}};var i=(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 Zc={accent:"#3b82f6",background:"#0f172a",errorAccent:"#ef4444",fontFamily:'ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',foreground:"#f8fafc",radius:16},qc={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},zc=(c,g)=>{switch(c){case"listening":return g.listening;case"speaking":return g.speaking;case"thinking":return g.thinking;case"idle":return g.idle}},m=(c)=>{let g={...Zc,...c.theme},A={...qc,...c.labels},n=c.state.assistantAudio.at(-1)?.receivedAt,T=c.state.turns.at(-1)?.committedAt,V=v({hasActivePartial:c.state.partial.length>0,isConnected:c.state.isConnected,isPlaying:!1,isRecording:c.state.isRecording,lastAssistantAt:n,lastTranscriptAt:T}),I=!c.state.isConnected&&c.state.status!=="idle"&&!c.state.error,y=c.state.error?"Error":I?A.connecting:c.state.status==="completed"?A.callEnded:zc(V,A);return{agentState:V,classes:{container:`absolute-voice-widget absolute-voice-widget--${V}`,dot:`absolute-voice-widget__dot${c.state.error?" absolute-voice-widget__dot--error":""}`},controls:{canEnd:c.state.isConnected,canMute:c.state.isRecording,canStart:!c.state.isRecording&&c.state.status!=="completed"},errorMessage:c.state.error??void 0,labels:A,partial:c.state.partial||void 0,statusLabel:y,theme:g,title:c.title??"Voice"}},Kc=(c)=>typeof c==="number"?`${c}px`:c,u=(c)=>{let g=c.theme,A=`background:${g.background};border-radius:${Kc(g.radius)};color:${g.foreground};font-family:${g.fontFamily};min-width:240px;padding:20px 22px;`,n=`background:${c.errorMessage?g.errorAccent:c.agentState==="idle"?"rgba(148,163,184,0.6)":g.accent};border-radius:50%;height:10px;width:10px;`,T=[];if(c.controls.canStart)T.push(`<button type="button" data-action="start" style="background:${g.accent};border:none;border-radius:12px;color:${g.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${i(c.labels.startCall)}</button>`);if(c.controls.canMute)T.push(`<button type="button" data-action="mute" style="background:transparent;border:1px solid rgba(255,255,255,0.18);border-radius:12px;color:${g.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${i(c.labels.mute)}</button>`);if(c.controls.canEnd)T.push(`<button type="button" data-action="end" style="background:${g.errorAccent};border:none;border-radius:12px;color:${g.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${i(c.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${c.agentState}" class="${i(c.classes.container)}" style="${A}">
1
+ (()=>{var{defineProperty:Q,getOwnPropertyNames:Ic,getOwnPropertyDescriptor:Vc}=Object,Sc=Object.prototype.hasOwnProperty;function Rc(c){return this[c]}var yc=(c)=>{var g=(z??=new WeakMap).get(c),C;if(g)return g;if(g=Q({},"__esModule",{value:!0}),c&&typeof c==="object"||typeof c==="function"){for(var A of Ic(c))if(!Sc.call(g,A))Q(g,A,{get:Rc.bind(c,A),enumerable:!(C=Vc(c,A))||C.enumerable})}return z.set(c,g),g},z;var _c=(c)=>c;function Tc(c,g){this[c]=_c.bind(null,g)}var wc=(c,g)=>{for(var C in g)Q(c,C,{get:g[C],enumerable:!0,configurable:!0,set:Tc.bind(g,C)})};var Fc={};wc(Fc,{mount:()=>cc,default:()=>oc,VOICE_EMBED_VERSION:()=>gc});var hc=(c)=>{if(typeof c!=="string")return c;return document.querySelector(c)},Lc=(c,g,C,A)=>{let I=g??c.getAttribute("hx-get")??"";if(!I)return"";let R=new URL(I,window.location.origin);if(A)R.searchParams.set(C,A);else R.searchParams.delete(C);return`${R.pathname}${R.search}${R.hash}`},B=(c,g)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let C=hc(g.element);if(!C)return()=>{};let A=g.eventName??"voice-refresh",I=g.sessionQueryParam??"sessionId",R=()=>{let T=window,w=Lc(C,g.route,I,c.sessionId);if(w)C.setAttribute("hx-get",w);T.htmx?.process?.(C),T.htmx?.trigger?.(C,A)},S=c.subscribe(R);return R(),()=>{S()}};var Uc=(c)=>Math.max(-1,Math.min(1,c)),$c=(c)=>{let g=new Int16Array(c.length);for(let C=0;C<c.length;C+=1){let A=Uc(c[C]??0);g[C]=A<0?A*32768:A*32767}return new Uint8Array(g.buffer)},nc=(c)=>{let g=c instanceof Uint8Array?c:new Uint8Array(c);if(g.byteLength<2)return 0;let C=new Int16Array(g.buffer,g.byteOffset,Math.floor(g.byteLength/2));if(C.length===0)return 0;let A=0;for(let I of C){let R=I/32768;A+=R*R}return Math.min(1,Math.max(0,Math.sqrt(A/C.length)*5.5))},Oc=(c,g,C)=>{if(g===C)return c;let A=g/C,I=Math.round(c.length/A),R=new Float32Array(I),S=0,T=0;while(S<R.length){let w=Math.round((S+1)*A),U=0,_=0;for(let L=T;L<w&&L<c.length;L+=1)U+=c[L]??0,_+=1;R[S]=_>0?U/_:0,S+=1,T=w}return R},j=(c)=>{let g=null,C=null,A=null,I=null;return{start:async()=>{if(!c.stream&&(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia))throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let T=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!T)throw Error("Browser microphone capture requires AudioContext support.");if(I=c.stream??await navigator.mediaDevices.getUserMedia({audio:{autoGainControl:!0,channelCount:c.channelCount??1,echoCancellation:!0,noiseSuppression:!0}}),g=new T,g.state==="suspended")await g.resume();C=g.createMediaStreamSource(I),A=g.createScriptProcessor(4096,1,1),A.onaudioprocess=(w)=>{let U=w.inputBuffer.getChannelData(0),_=Oc(U,g?.sampleRate??48000,c.sampleRateHz??16000),L=$c(_);c.onLevel?.(nc(L)),c.onAudio(L)},C.connect(A),A.connect(g.destination)},stop:()=>{A?.disconnect(),C?.disconnect(),I?.getTracks().forEach((T)=>T.stop()),g?.close(),c.onLevel?.(0),g=null,I=null,A=null,C=null}}};var K=(c)=>{if(typeof c==="string"&&c.trim())return c;if(c instanceof Error&&c.message.trim())return c.message;if(c&&typeof c==="object"){let g=c;for(let C of["message","reason","description"]){let A=g[C];if(typeof A==="string"&&A.trim())return A}if("error"in g)return K(g.error);if("cause"in g)return K(g.cause);try{return JSON.stringify(c)}catch{}}return"Unexpected error"},d=(c)=>{switch(c.type){case"audio":return{chunk:Uint8Array.from(atob(c.chunkBase64),(g)=>g.charCodeAt(0)),format:c.format,receivedAt:c.receivedAt,turnId:c.turnId,type:"audio"};case"assistant":return{text:c.text,turnId:c.turnId,type:"assistant"};case"assistant_delta":return{delta:c.delta,turnId:c.turnId,type:"assistant_delta"};case"complete":return{sessionId:c.sessionId,type:"complete"};case"connection":return{reconnect:c.reconnect,type:"connection"};case"call_lifecycle":return{event:c.event,sessionId:c.sessionId,type:"call_lifecycle"};case"error":return{message:K(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 N=(c,g,C,A)=>{c.push({code:C,message:A,severity:g})};var bc=(c)=>c.length===0?void 0:c.reduce((g,C)=>g+C,0)/c.length,J=(c)=>c.length===0?void 0:Math.max(...c);var n=(c,g)=>{let C=c[g];return typeof C==="number"&&Number.isFinite(C)?C:void 0},Y=(c,g)=>{let C=c[g];return typeof C==="boolean"?C:void 0},O=(c,g)=>{let C=c[g];return typeof C==="string"?C:void 0},q=(c)=>String(c.id??O(c,"ssrc")??n(c,"ssrc")??O(c,"trackIdentifier")??O(c,"mid")??"unknown"),k=(c)=>c===void 0?void 0:c*1000;var Dc=(c)=>{let g={};for(let[C,A]of Object.entries(c))if(A===null||typeof A==="boolean"||typeof A==="number"||typeof A==="string")g[C]=A;return g};var x=(c={})=>{let g=c.stats??[],C=[],A=g.filter((V)=>V.type==="inbound-rtp"&&O(V,"kind")!=="video"),I=g.filter((V)=>V.type==="outbound-rtp"&&O(V,"kind")!=="video"),R=g.filter((V)=>V.type==="candidate-pair"),S=g.filter((V)=>(V.type==="track"||V.type==="media-source")&&O(V,"kind")==="audio"),T=R.filter((V)=>Y(V,"selected")===!0||Y(V,"nominated")===!0||O(V,"state")==="succeeded").length,w=S.filter((V)=>O(V,"readyState")!=="ended"&&O(V,"trackState")!=="ended"&&Y(V,"ended")!==!0).length,U=S.filter((V)=>O(V,"readyState")==="ended"||O(V,"trackState")==="ended"||Y(V,"ended")===!0).length,_=A.reduce((V,$)=>V+(n($,"packetsReceived")??0),0),L=I.reduce((V,$)=>V+(n($,"packetsSent")??0),0),y=[...A,...I].reduce((V,$)=>V+Math.max(0,n($,"packetsLost")??0),0),b=_+y,h=b===0?0:y/b,E=A.reduce((V,$)=>V+(n($,"bytesReceived")??0),0),H=I.reduce((V,$)=>V+(n($,"bytesSent")??0),0),G=J(R.map((V)=>k(n(V,"currentRoundTripTime")??n(V,"roundTripTime"))).filter((V)=>V!==void 0)),M=J([...A,...I].map((V)=>k(n(V,"jitter"))).filter((V)=>V!==void 0)),X=J(A.map((V)=>{let $=n(V,"jitterBufferDelay"),D=n(V,"jitterBufferEmittedCount");return $!==void 0&&D!==void 0&&D>0?$/D*1000:void 0}).filter((V)=>V!==void 0)),P=S.map((V)=>n(V,"audioLevel")).filter((V)=>V!==void 0);if(c.requireConnectedCandidatePair&&R.length>0&&T===0)N(C,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(c.requireLiveAudioTrack&&w===0)N(C,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(c.maxPacketLossRatio!==void 0&&h>c.maxPacketLossRatio)N(C,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(h)} above ${String(c.maxPacketLossRatio)}.`);if(c.maxRoundTripTimeMs!==void 0&&G!==void 0&&G>c.maxRoundTripTimeMs)N(C,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(G)}ms above ${String(c.maxRoundTripTimeMs)}ms.`);if(c.maxJitterMs!==void 0&&M!==void 0&&M>c.maxJitterMs)N(C,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(M)}ms above ${String(c.maxJitterMs)}ms.`);return{activeCandidatePairs:T,audioLevelAverage:bc(P),bytesReceived:E,bytesSent:H,checkedAt:Date.now(),endedAudioTracks:U,inboundPackets:_,issues:C,jitterBufferDelayMs:X,jitterMs:M,liveAudioTracks:w,outboundPackets:L,packetLossRatio:h,packetsLost:y,roundTripTimeMs:G,status:C.some((V)=>V.severity==="error")?"fail":C.length>0?"warn":"pass",totalStats:g.length}},i=async(c)=>{return[...(await c.peerConnection.getStats(c.selector??null)).values()].map(Dc)};var f=(c={})=>{let g=c.stats??[],C=c.previousStats??[],A=[],I=new Map(C.map((y)=>[q(y),y])),S=g.filter((y)=>(y.type==="inbound-rtp"||y.type==="outbound-rtp")&&O(y,"kind")!=="video"&&O(y,"mediaType")!=="video").map((y)=>{let b=y.type==="outbound-rtp"?"outbound":"inbound",h=b==="outbound"?"packetsSent":"packetsReceived",E=b==="outbound"?"bytesSent":"bytesReceived",H=I.get(q(y)),G=n(y,h),M=H?n(H,h):void 0,X=n(y,E),P=H?n(H,E):void 0,V=y.timestamp!==void 0&&H?.timestamp!==void 0?y.timestamp-H.timestamp:void 0;return{bytesDelta:X!==void 0&&P!==void 0?X-P:void 0,currentPackets:G,direction:b,id:q(y),packetDelta:G!==void 0&&M!==void 0?G-M:void 0,previousPackets:M,timeDeltaMs:V}}),T=S.filter((y)=>y.direction==="inbound"),w=S.filter((y)=>y.direction==="outbound"),U=J(S.map((y)=>y.timeDeltaMs).filter((y)=>y!==void 0)),_=T.filter((y)=>c.maxInboundPacketStallMs!==void 0&&y.timeDeltaMs!==void 0&&y.timeDeltaMs>=c.maxInboundPacketStallMs&&y.packetDelta!==void 0&&y.packetDelta<=0).length,L=w.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&&T.length===0)N(A,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(c.requireOutboundAudio&&w.length===0)N(A,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(c.maxGapMs!==void 0&&U!==void 0&&U>c.maxGapMs)N(A,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(U)}ms above ${String(c.maxGapMs)}ms.`);if(_>0)N(A,"error","media.webrtc_inbound_stalled",`${String(_)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(L>0)N(A,"error","media.webrtc_outbound_stalled",`${String(L)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:T.length,issues:A,maxObservedGapMs:U,outboundAudioStreams:w.length,stalledInboundStreams:_,stalledOutboundStreams:L,status:A.some((y)=>y.severity==="error")?"fail":A.length>0?"warn":"pass",streams:S,totalStats:g.length}};var Hc="/api/voice/browser-media",Gc=5000,Mc=async(c)=>c.peerConnection??await c.getPeerConnection?.()??null,Nc=async(c,g)=>{let C=g.fetch??globalThis.fetch;if(!C)return;await C(g.path??Hc,{body:JSON.stringify(c),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},F=(c)=>{let g=null,C=[],A=async()=>{let S=await Mc(c);if(!S)return;let T=await i({peerConnection:S}),w=x({...c,stats:T}),U=c.continuity===!1?void 0:f({...c.continuity,previousStats:C,stats:T}),_={at:Date.now(),continuity:U,report:w,scenarioId:c.getScenarioId?.()??null,sessionId:c.getSessionId?.()??null};return C=T,c.onReport?.(_),await Nc(_,c),_},I=()=>{A().catch((S)=>{c.onError?.(S)})},R=()=>{if(g)clearInterval(g),g=null};return{close:R,reportOnce:A,stop:R,start:()=>{if(g)return;I(),g=setInterval(I,c.intervalMs??Gc)}}};var W=()=>{},lc=()=>W,Wc={callControl:W,close:W,endTurn:W,send:W,sendAudio:W,simulateDisconnect:W,subscribe:lc,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",start:()=>{}},Ec=()=>crypto.randomUUID(),Xc=(c,g,C)=>{let{hostname:A,port:I,protocol:R}=window.location,S=R==="https:"?"wss:":"ws:",T=I?`:${I}`:"",w=new URL(`${S}//${A}${T}${c}`);if(w.searchParams.set("sessionId",g),C)w.searchParams.set("scenarioId",C);return w.toString()},Pc=(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}},Yc=(c)=>{if(typeof c.data!=="string")return null;try{let g=JSON.parse(c.data);return Pc(g)?g:null}catch{return null}},o=(c,g={})=>{if(typeof window>"u")return Wc;let C=new Set,A=g.reconnect!==!1,I=g.maxReconnectAttempts??10,R=g.pingInterval??30000,S={isConnected:!1,pendingMessages:[],scenarioId:g.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:g.sessionId??Ec(),ws:null},T=(V)=>{C.forEach(($)=>$(V))},w=()=>{if(S.pingInterval)clearInterval(S.pingInterval),S.pingInterval=null;if(S.reconnectTimeout)clearTimeout(S.reconnectTimeout),S.reconnectTimeout=null},U=()=>{if(S.ws?.readyState!==1)return;while(S.pendingMessages.length>0){let V=S.pendingMessages.shift();if(V!==void 0)S.ws.send(V)}},_=()=>{let V=Date.now()+500;S.reconnectAttempts+=1,T({reconnect:{attempts:S.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:I,nextAttemptAt:V,status:"reconnecting"},type:"connection"}),S.reconnectTimeout=setTimeout(()=>{if(S.reconnectAttempts>I){T({reconnect:{attempts:S.reconnectAttempts,maxAttempts:I,status:"exhausted"},type:"connection"});return}L()},500)},L=()=>{let V=new WebSocket(Xc(c,S.sessionId,S.scenarioId));V.binaryType="arraybuffer",V.onopen=()=>{let $=S.reconnectAttempts>0;if(S.isConnected=!0,U(),$)T({reconnect:{attempts:S.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:I,status:"resumed"},type:"connection"}),S.reconnectAttempts=0;C.forEach((D)=>D({scenarioId:S.scenarioId??void 0,sessionId:S.sessionId,status:"active",type:"session"})),S.pingInterval=setInterval(()=>{if(V.readyState===1)V.send(JSON.stringify({type:"ping"}))},R)},V.onmessage=($)=>{let D=Yc($);if(!D)return;if(D.type==="session")S.sessionId=D.sessionId,S.scenarioId=D.scenarioId??S.scenarioId;C.forEach((Cc)=>Cc(D))},V.onclose=($)=>{if(S.isConnected=!1,w(),A&&$.code!==1000&&S.reconnectAttempts<I)_();else if(A&&$.code!==1000)T({reconnect:{attempts:S.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:I,status:"exhausted"},type:"connection"})},S.ws=V},y=(V)=>{if(S.ws?.readyState===1){S.ws.send(V);return}S.pendingMessages.push(V)},b=(V)=>{y(JSON.stringify(V))},h=(V={})=>{if(V.sessionId)S.sessionId=V.sessionId;if(V.scenarioId)S.scenarioId=V.scenarioId;b({scenarioId:S.scenarioId??void 0,sessionId:S.sessionId,type:"start"})},E=(V)=>{y(V)},H=()=>{b({type:"end_turn"})},G=(V)=>{b({...V,type:"call_control"})},M=()=>{if(w(),S.ws)S.ws.close(1000),S.ws=null;S.isConnected=!1,C.clear()},X=()=>{if(S.ws?.readyState===1)S.ws.close(4000,"absolutejs-voice-reconnect-proof")},P=(V)=>{return C.add(V),()=>{C.delete(V)}};return L(),{callControl:G,close:M,endTurn:H,send:b,sendAudio:E,simulateDisconnect:X,start:h,subscribe:P,getReadyState:()=>S.ws?.readyState??3,getScenarioId:()=>S.scenarioId??"",getSessionId:()=>S.sessionId}};var Jc=()=>({attempts:0,maxAttempts:0,status:"idle"}),Zc=()=>({assistantAudio:[],assistantStreamingText:"",assistantTexts:[],call:null,error:null,isConnected:!1,partial:"",reconnect:Jc(),scenarioId:null,sessionId:null,sessionMetadata:null,status:"idle",turns:[]}),s=()=>{let c=Zc(),g=new Set,C=()=>{g.forEach((I)=>I())};return{dispatch:(I)=>{switch(I.type){case"audio":c={...c,assistantAudio:[...c.assistantAudio,{chunk:I.chunk,format:I.format,receivedAt:I.receivedAt,turnId:I.turnId}]};break;case"assistant":c={...c,assistantStreamingText:"",assistantTexts:[...c.assistantTexts,I.text]};break;case"assistant_delta":c={...c,assistantStreamingText:`${c.assistantStreamingText}${I.delta}`};break;case"complete":c={...c,sessionId:I.sessionId,status:"completed"};break;case"call_lifecycle":c={...c,call:{...c.call,disposition:I.event.type==="end"?I.event.disposition:c.call?.disposition,endedAt:I.event.type==="end"?I.event.at:c.call?.endedAt,events:[...c.call?.events??[],I.event],lastEventAt:I.event.at,startedAt:c.call?.startedAt??I.event.at},sessionId:I.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:I.reconnect};break;case"disconnected":c={...c,isConnected:!1};break;case"error":c={...c,error:I.message};break;case"final":c={...c,partial:I.transcript.text,turns:c.turns.map((R)=>R)};break;case"partial":c={...c,partial:I.transcript.text};break;case"replay":c={...c,assistantStreamingText:"",assistantTexts:[...I.assistantTexts],call:I.call??null,error:null,isConnected:I.status==="active",partial:I.partial,reconnect:c.reconnect.status==="reconnecting"?{...c.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:c.reconnect,scenarioId:I.scenarioId??c.scenarioId,sessionId:I.sessionId,sessionMetadata:I.sessionMetadata??c.sessionMetadata,status:I.status,turns:[...I.turns]};break;case"session":c={...c,error:null,scenarioId:I.scenarioId??c.scenarioId,isConnected:I.status==="active",sessionId:I.sessionId,sessionMetadata:I.sessionMetadata??c.sessionMetadata,status:I.status};break;case"turn":c={...c,partial:"",turns:[...c.turns,I.turn]};break}C()},getServerSnapshot:()=>c,getSnapshot:()=>c,subscribe:(I)=>{return g.add(I),()=>{g.delete(I)}}}};var v=(c,g={})=>{let C=o(c,g),A=s(),I=g.browserMedia&&typeof window<"u"?F({...g.browserMedia,getScenarioId:()=>g.browserMedia?g.browserMedia.getScenarioId?.()??C.getScenarioId():C.getScenarioId(),getSessionId:()=>g.browserMedia?g.browserMedia.getSessionId?.()??C.getSessionId():C.getSessionId()}):null,R=new Set,S=(_)=>Promise.resolve().then(()=>{if(!_?.sessionId&&!_?.scenarioId)return;C.start(_),I?.start()}),T=()=>{R.forEach((_)=>_())},w=()=>{if(!g.reconnectReportPath||typeof fetch>"u")return;let _=A.getSnapshot(),L=JSON.stringify({at:Date.now(),reconnect:_.reconnect,scenarioId:_.scenarioId,sessionId:C.getSessionId(),turnIds:_.turns.map((y)=>y.id)});fetch(g.reconnectReportPath,{body:L,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},U=C.subscribe((_)=>{let L=d(_);if(L){if(A.dispatch(L),_.type==="connection")w();T()}});return{start:S,get assistantAudio(){return A.getSnapshot().assistantAudio},get assistantTexts(){return A.getSnapshot().assistantTexts},get assistantStreamingText(){return A.getSnapshot().assistantStreamingText},get call(){return A.getSnapshot().call},callControl(_){C.callControl(_)},close(){U(),I?.close(),C.close(),A.dispatch({type:"disconnected"}),T()},endTurn(){C.endTurn()},get error(){return A.getSnapshot().error},getServerSnapshot(){return A.getServerSnapshot()},getSnapshot(){return A.getSnapshot()},get isConnected(){return A.getSnapshot().isConnected},get partial(){return A.getSnapshot().partial},get reconnect(){return A.getSnapshot().reconnect},get scenarioId(){return A.getSnapshot().scenarioId},sendAudio(_){C.sendAudio(_)},get sessionId(){return C.getSessionId()},get sessionMetadata(){return A.getSnapshot().sessionMetadata},simulateDisconnect(){C.simulateDisconnect()},get status(){return A.getSnapshot().status},subscribe(_){return R.add(_),()=>{R.delete(_)}},get turns(){return A.getSnapshot().turns}}};var a=(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 Z=1200;var Qc={balanced:{qualityProfile:"general",semanticVetoMaxMs:0,semanticVetoRecheckMs:Z,silenceMs:1400,speechThreshold:0.012,transcriptStabilityMs:1000},fast:{qualityProfile:"general",semanticVetoMaxMs:0,semanticVetoRecheckMs:Z,silenceMs:700,speechThreshold:0.015,transcriptStabilityMs:450},"long-form":{qualityProfile:"general",semanticVetoMaxMs:0,semanticVetoRecheckMs:Z,silenceMs:2200,speechThreshold:0.01,transcriptStabilityMs:1500}},Kc={"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}},qc="fast",zc="general",r=(c)=>{let g=c?.profile??qc,C=c?.qualityProfile??zc,A=Qc[g],I=Kc[C];return{profile:g,qualityProfile:C,semanticVetoMaxMs:c?.semanticVetoMaxMs??A.semanticVetoMaxMs,semanticVetoRecheckMs:c?.semanticVetoRecheckMs??A.semanticVetoRecheckMs,silenceMs:c?.silenceMs??I.silenceMs??A.silenceMs,speechThreshold:c?.speechThreshold??I.speechThreshold??A.speechThreshold,transcriptStabilityMs:c?.transcriptStabilityMs??I.transcriptStabilityMs??A.transcriptStabilityMs}};var Bc={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"}}},t=(c="default")=>{let g=Bc[c];return{audioConditioning:a(g.audioConditioning),capture:{channelCount:g.capture?.channelCount??1,sampleRateHz:g.capture?.sampleRateHz??16000},connection:{...g.connection},name:c,sttLifecycle:g.sttLifecycle??"continuous",turnDetection:r(g.turnDetection)}};var jc=(c)=>({assistantAudio:[...c.assistantAudio],assistantStreamingText:c.assistantStreamingText,assistantTexts:[...c.assistantTexts],call:c.call,error:c.error,isConnected:c.isConnected,isRecording:!1,partial:c.partial,reconnect:c.reconnect,recordingError:null,sessionId:c.sessionId,sessionMetadata:c.sessionMetadata,scenarioId:c.scenarioId,status:c.status,turns:[...c.turns]}),e=(c,g={})=>{let C=t(g.preset),A=v(c,{...C.connection,...g.connection}),I=null,R=jc(A),S=new Set,T=()=>{for(let h of S)h()},w=()=>{if(R={...R,assistantAudio:[...A.assistantAudio],assistantStreamingText:A.assistantStreamingText,assistantTexts:[...A.assistantTexts],call:A.call,error:A.error,isConnected:A.isConnected,partial:A.partial,reconnect:A.reconnect,sessionId:A.sessionId,sessionMetadata:A.sessionMetadata,scenarioId:A.scenarioId,status:A.status,turns:[...A.turns]},g.autoStopOnComplete!==!1&&R.status==="completed"&&R.isRecording)I?.stop(),I=null,R={...R,isRecording:!1};T()},U=A.subscribe(w);w();let _=()=>{if(I)return I;return I=j({channelCount:g.capture?.channelCount??C.capture.channelCount,onLevel:g.capture?.onLevel,onAudio:(h)=>{if(g.capture?.onAudio){g.capture.onAudio(h,A.sendAudio);return}A.sendAudio(h)},sampleRateHz:g.capture?.sampleRateHz??C.capture.sampleRateHz,...g.capture?.stream?{stream:g.capture.stream}:{}}),I},L=()=>{I?.stop(),I=null,R={...R,isRecording:!1},T()},y=async()=>{if(R.isRecording)return;try{R={...R,recordingError:null},T(),await _().start(),R={...R,isRecording:!0},T()}catch(h){throw I=null,R={...R,isRecording:!1,recordingError:h instanceof Error?h.message:String(h)},T(),h}};return{close:()=>{U(),L(),A.close()},startRecording:y,stopRecording:L,get assistantAudio(){return R.assistantAudio},get assistantTexts(){return R.assistantTexts},get assistantStreamingText(){return R.assistantStreamingText},bindHTMX(h){return B(A,h)},get call(){return R.call},callControl:(h)=>A.callControl(h),endTurn:()=>A.endTurn(),get error(){return R.error},getServerSnapshot:()=>R,getSnapshot:()=>R,get isConnected(){return R.isConnected},get isRecording(){return R.isRecording},get partial(){return R.partial},get reconnect(){return R.reconnect},get recordingError(){return R.recordingError},get scenarioId(){return R.scenarioId},sendAudio:(h)=>A.sendAudio(h),get sessionId(){return R.sessionId},get sessionMetadata(){return R.sessionMetadata},simulateDisconnect:()=>A.simulateDisconnect(),get status(){return R.status},subscribe:(h)=>{return S.add(h),()=>{S.delete(h)}},toggleRecording:async()=>{if(R.isRecording){L();return}await y()},get turns(){return R.turns}}};var l=(c)=>String(c).replaceAll("&","&amp;").replaceAll("<","&lt;").replaceAll(">","&gt;").replaceAll('"',"&quot;").replaceAll("'","&#39;");var m=(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 dc={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},kc={callEnded:"Call ended",connecting:"Connecting…",endCall:"End call",idle:"Idle",listening:"Listening",mute:"Mute",speaking:"Speaking",startCall:"Start call",thinking:"Thinking",unmute:"Unmute"},xc=(c,g)=>{switch(c){case"listening":return g.listening;case"speaking":return g.speaking;case"thinking":return g.thinking;case"idle":return g.idle}},u=(c)=>{let g={...dc,...c.theme},C={...kc,...c.labels},A=c.state.assistantAudio.at(-1)?.receivedAt,I=c.state.turns.at(-1)?.committedAt,R=m({hasActivePartial:c.state.partial.length>0,isConnected:c.state.isConnected,isPlaying:!1,isRecording:c.state.isRecording,lastAssistantAt:A,lastTranscriptAt:I}),S=!c.state.isConnected&&c.state.status!=="idle"&&!c.state.error,T=c.state.error?"Error":S?C.connecting:c.state.status==="completed"?C.callEnded:xc(R,C);return{agentState:R,classes:{container:`absolute-voice-widget absolute-voice-widget--${R}`,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:C,partial:c.state.partial||void 0,statusLabel:T,theme:g,title:c.title??"Voice"}},ic=(c)=>typeof c==="number"?`${c}px`:c,p=(c)=>{let g=c.theme,C=`background:${g.background};border-radius:${ic(g.radius)};color:${g.foreground};font-family:${g.fontFamily};min-width:240px;padding:20px 22px;`,A=`background:${c.errorMessage?g.errorAccent:c.agentState==="idle"?"rgba(148,163,184,0.6)":g.accent};border-radius:50%;height:10px;width:10px;`,I=[];if(c.controls.canStart)I.push(`<button type="button" data-action="start" style="background:${g.accent};border:none;border-radius:12px;color:${g.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${l(c.labels.startCall)}</button>`);if(c.controls.canMute)I.push(`<button type="button" data-action="mute" style="background:transparent;border:1px solid rgba(255,255,255,0.18);border-radius:12px;color:${g.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${l(c.labels.mute)}</button>`);if(c.controls.canEnd)I.push(`<button type="button" data-action="end" style="background:${g.errorAccent};border:none;border-radius:12px;color:${g.foreground};cursor:pointer;font-size:14px;font-weight:500;padding:10px 14px;">${l(c.labels.endCall)}</button>`);return`<div role="region" aria-live="polite" data-agent-state="${c.agentState}" class="${l(c.classes.container)}" style="${C}">
2
2
  <div style="align-items:center;display:flex;gap:10px;margin-bottom:12px;">
3
- <span aria-hidden="true" class="${i(c.classes.dot)}" style="${n}"></span>
4
- <strong style="font-size:15px;">${i(c.title)}</strong>
5
- <span style="font-size:13px;margin-left:auto;opacity:0.7;">${i(c.statusLabel)}</span>
3
+ <span aria-hidden="true" class="${l(c.classes.dot)}" style="${A}"></span>
4
+ <strong style="font-size:15px;">${l(c.title)}</strong>
5
+ <span style="font-size:13px;margin-left:auto;opacity:0.7;">${l(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;">“${i(c.partial)}”</p>`:""}
8
- <div style="display:flex;gap:10px;">${T.join("")}</div>
9
- ${c.errorMessage?`<p style="color:${g.errorAccent};font-size:12px;margin-top:12px;">${i(c.errorMessage)}</p>`:""}
10
- </div>`};var Bc=(c)=>{if(typeof c!=="string")return c;let g=document.querySelector(c);if(!g)throw Error(`AbsoluteVoice.mount: no element matches "${c}"`);return g},p=(c,g={})=>{let A=Bc(c),n=a(g.path??"/voice",g.controllerOptions),T=null,V=null,I=()=>{let _=m({...g.labels!==void 0?{labels:g.labels}:{},state:{assistantAudio:n.assistantAudio,error:n.error,isConnected:n.isConnected,isRecording:n.isRecording,partial:n.partial,status:n.status,turns:n.turns},...g.theme!==void 0?{theme:g.theme}:{},...g.title!==void 0?{title:g.title}:{}});A.innerHTML=u(_);for(let L of A.querySelectorAll("button[data-action]")){let{action:S}=L.dataset;L.addEventListener("click",()=>{if(S==="start")n.startRecording();else if(S==="mute")n.stopRecording();else if(S==="end")n.close()})}if(n.error&&n.error!==T)T=n.error,g.onError?.(n.error);if(n.status!==V)V=n.status,g.onStatusChange?.(n.status)},y=n.subscribe(I);if(I(),g.autoStart)n.startRecording();return{controller:n,async end(){await n.close()},mute(){n.stopRecording()},async start(){await n.startRecording()},unmount(){y(),n.close(),A.innerHTML=""}}},cc="0.0.22-beta.516",gc={mount:p,version:cc};if(typeof globalThis<"u")globalThis.AbsoluteVoice=gc;var oc=gc;})();
7
+ ${c.partial?`<p style="font-size:13px;margin:8px 0 12px;opacity:0.85;word-break:break-word;">“${l(c.partial)}”</p>`:""}
8
+ <div style="display:flex;gap:10px;">${I.join("")}</div>
9
+ ${c.errorMessage?`<p style="color:${g.errorAccent};font-size:12px;margin-top:12px;">${l(c.errorMessage)}</p>`:""}
10
+ </div>`};var fc=(c)=>{if(typeof c!=="string")return c;let g=document.querySelector(c);if(!g)throw Error(`AbsoluteVoice.mount: no element matches "${c}"`);return g},cc=(c,g={})=>{let C=fc(c),A=e(g.path??"/voice",g.controllerOptions),I=null,R=null,S=()=>{let w=u({...g.labels!==void 0?{labels:g.labels}:{},state:{assistantAudio:A.assistantAudio,error:A.error,isConnected:A.isConnected,isRecording:A.isRecording,partial:A.partial,status:A.status,turns:A.turns},...g.theme!==void 0?{theme:g.theme}:{},...g.title!==void 0?{title:g.title}:{}});C.innerHTML=p(w);for(let U of C.querySelectorAll("button[data-action]")){let{action:_}=U.dataset;U.addEventListener("click",()=>{if(_==="start")A.startRecording();else if(_==="mute")A.stopRecording();else if(_==="end")A.close()})}if(A.error&&A.error!==I)I=A.error,g.onError?.(A.error);if(A.status!==R)R=A.status,g.onStatusChange?.(A.status)},T=A.subscribe(S);if(S(),g.autoStart)A.startRecording();return{controller:A,async end(){await A.close()},mute(){A.stopRecording()},async start(){await A.startRecording()},unmount(){T(),A.close(),C.innerHTML=""}}},gc="0.0.22-beta.516",Ac={mount:cc,version:gc};if(typeof globalThis<"u")globalThis.AbsoluteVoice=Ac;var oc=Ac;})();