getpatter 0.6.1 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -5,10 +5,10 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __glob = (map) => (path5) => {
9
- var fn = map[path5];
8
+ var __glob = (map) => (path6) => {
9
+ var fn = map[path6];
10
10
  if (fn) return fn();
11
- throw new Error("Module not found in bundle: " + path5);
11
+ throw new Error("Module not found in bundle: " + path6);
12
12
  };
13
13
  var __esm = (fn, res) => function __init() {
14
14
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
@@ -303,11 +303,10 @@ var init_openai_realtime = __esm({
303
303
  const url2 = `wss://api.openai.com/v1/realtime?model=${encodeURIComponent(this.model)}`;
304
304
  let ws = null;
305
305
  try {
306
- ws = await new Promise((resolve, reject) => {
306
+ ws = await new Promise((resolve2, reject) => {
307
307
  const sock = new import_ws.default(url2, {
308
308
  headers: {
309
- Authorization: `Bearer ${this.apiKey}`,
310
- "OpenAI-Beta": "realtime=v1"
309
+ Authorization: `Bearer ${this.apiKey}`
311
310
  }
312
311
  });
313
312
  const timer = setTimeout(() => {
@@ -319,22 +318,22 @@ var init_openai_realtime = __esm({
319
318
  }, 5e3);
320
319
  sock.once("open", () => {
321
320
  clearTimeout(timer);
322
- resolve(sock);
321
+ resolve2(sock);
323
322
  });
324
323
  sock.once("error", (err) => {
325
324
  clearTimeout(timer);
326
325
  reject(err);
327
326
  });
328
327
  });
329
- const sessionCreated = await new Promise((resolve) => {
330
- const timer = setTimeout(() => resolve(false), 2e3);
328
+ const sessionCreated = await new Promise((resolve2) => {
329
+ const timer = setTimeout(() => resolve2(false), 2e3);
331
330
  const onMsg = (raw) => {
332
331
  try {
333
332
  const data = JSON.parse(raw.toString());
334
333
  if (data.type === "session.created") {
335
334
  clearTimeout(timer);
336
335
  ws.off("message", onMsg);
337
- resolve(true);
336
+ resolve2(true);
338
337
  }
339
338
  } catch {
340
339
  }
@@ -347,15 +346,15 @@ var init_openai_realtime = __esm({
347
346
  } catch {
348
347
  return;
349
348
  }
350
- await new Promise((resolve) => {
351
- const timer = setTimeout(() => resolve(), 1500);
349
+ await new Promise((resolve2) => {
350
+ const timer = setTimeout(() => resolve2(), 1500);
352
351
  const onMsg = (raw) => {
353
352
  try {
354
353
  const data = JSON.parse(raw.toString());
355
354
  if (data.type === "session.updated") {
356
355
  clearTimeout(timer);
357
356
  ws.off("message", onMsg);
358
- resolve();
357
+ resolve2();
359
358
  }
360
359
  } catch {
361
360
  }
@@ -378,11 +377,10 @@ var init_openai_realtime = __esm({
378
377
  const url2 = `wss://api.openai.com/v1/realtime?model=${encodeURIComponent(this.model)}`;
379
378
  this.ws = new import_ws.default(url2, {
380
379
  headers: {
381
- Authorization: `Bearer ${this.apiKey}`,
382
- "OpenAI-Beta": "realtime=v1"
380
+ Authorization: `Bearer ${this.apiKey}`
383
381
  }
384
382
  });
385
- await new Promise((resolve, reject) => {
383
+ await new Promise((resolve2, reject) => {
386
384
  let sessionCreated = false;
387
385
  let settled = false;
388
386
  const ws = this.ws;
@@ -399,7 +397,7 @@ var init_openai_realtime = __esm({
399
397
  ws.send(JSON.stringify({ type: "session.update", session: this.buildSessionConfig() }));
400
398
  } else if (msg.type === "session.updated") {
401
399
  cleanup();
402
- resolve();
400
+ resolve2();
403
401
  }
404
402
  };
405
403
  const onSetupError = (err) => {
@@ -472,11 +470,10 @@ var init_openai_realtime = __esm({
472
470
  const url2 = `wss://api.openai.com/v1/realtime?model=${encodeURIComponent(this.model)}`;
473
471
  const ws = new import_ws.default(url2, {
474
472
  headers: {
475
- Authorization: `Bearer ${this.apiKey}`,
476
- "OpenAI-Beta": "realtime=v1"
473
+ Authorization: `Bearer ${this.apiKey}`
477
474
  }
478
475
  });
479
- await new Promise((resolve, reject) => {
476
+ await new Promise((resolve2, reject) => {
480
477
  let sessionCreated = false;
481
478
  let settled = false;
482
479
  const onMessage = (raw) => {
@@ -496,7 +493,7 @@ var init_openai_realtime = __esm({
496
493
  }
497
494
  } else if (msg.type === "session.updated") {
498
495
  cleanup();
499
- resolve();
496
+ resolve2();
500
497
  }
501
498
  };
502
499
  const onError = (err) => {
@@ -621,22 +618,23 @@ var init_openai_realtime = __esm({
621
618
  */
622
619
  cancelResponse() {
623
620
  if (!this.ws) return;
624
- if (this.currentResponseItemId) {
625
- let audioEndMs = this.currentResponseAudioMs;
626
- if (this.currentResponseFirstAudioAt !== null) {
627
- const elapsedMs = Date.now() - this.currentResponseFirstAudioAt;
628
- audioEndMs = Math.min(audioEndMs, Math.max(elapsedMs, 0));
629
- }
630
- try {
631
- this.ws.send(JSON.stringify({
632
- type: "conversation.item.truncate",
633
- item_id: this.currentResponseItemId,
634
- content_index: 0,
635
- audio_end_ms: audioEndMs
636
- }));
637
- } catch (err) {
638
- getLogger().debug?.(`conversation.item.truncate failed: ${String(err)}`);
639
- }
621
+ if (!this.currentResponseItemId) {
622
+ return;
623
+ }
624
+ let audioEndMs = this.currentResponseAudioMs;
625
+ if (this.currentResponseFirstAudioAt !== null) {
626
+ const elapsedMs = Date.now() - this.currentResponseFirstAudioAt;
627
+ audioEndMs = Math.min(audioEndMs, Math.max(elapsedMs, 0));
628
+ }
629
+ try {
630
+ this.ws.send(JSON.stringify({
631
+ type: "conversation.item.truncate",
632
+ item_id: this.currentResponseItemId,
633
+ content_index: 0,
634
+ audio_end_ms: audioEndMs
635
+ }));
636
+ } catch (err) {
637
+ getLogger().debug?.(`conversation.item.truncate failed: ${String(err)}`);
640
638
  }
641
639
  this.ws.send(JSON.stringify({ type: "response.cancel" }));
642
640
  this.currentResponseItemId = null;
@@ -651,6 +649,19 @@ var init_openai_realtime = __esm({
651
649
  }));
652
650
  this.ws?.send(JSON.stringify({ type: "response.create" }));
653
651
  }
652
+ /**
653
+ * Trigger `response.create` with no new user item.
654
+ *
655
+ * Used by the Realtime stream-handler to drive a response after the
656
+ * client-side hallucination filter accepts an
657
+ * `input_audio_transcription.completed` event. The server VAD config
658
+ * sets `create_response: false` so OpenAI no longer auto-creates a
659
+ * response on every `input_audio_buffer.committed`; Patter is now
660
+ * responsible for triggering it explicitly when a real user turn lands.
661
+ */
662
+ async requestResponse() {
663
+ this.ws?.send(JSON.stringify({ type: "response.create" }));
664
+ }
654
665
  /**
655
666
  * Make the AI speak ``text`` as its opening line.
656
667
  *
@@ -1103,6 +1114,10 @@ var init_transcoding = __esm({
1103
1114
  });
1104
1115
 
1105
1116
  // src/providers/openai-realtime-2.ts
1117
+ var openai_realtime_2_exports = {};
1118
+ __export(openai_realtime_2_exports, {
1119
+ OpenAIRealtime2Adapter: () => OpenAIRealtime2Adapter
1120
+ });
1106
1121
  var import_ws2, GA_TO_V1_EVENT_NAMES, OpenAIRealtime2Adapter;
1107
1122
  var init_openai_realtime_2 = __esm({
1108
1123
  "src/providers/openai-realtime-2.ts"() {
@@ -1159,17 +1174,33 @@ var init_openai_realtime_2 = __esm({
1159
1174
  transcription: {
1160
1175
  model: opts.inputAudioTranscriptionModel ?? OpenAITranscriptionModel.WHISPER_1
1161
1176
  },
1162
- // Lower threshold (0.3 vs the 0.5 default) because the inbound
1163
- // audio is telephony-band (8 kHz) linearly upsampled to 24 kHz —
1164
- // the upper 4-12 kHz band is interpolation, not real harmonics,
1165
- // and the GA server VAD's default tuning was calibrated against
1166
- // studio-quality 24 kHz audio. A more permissive threshold
1167
- // recovers reliable speech detection on phone-band input.
1177
+ // VAD threshold raised back to the OpenAI default (0.5) on
1178
+ // 2026-05-22. The earlier 0.1 tuning (motivated by the
1179
+ // upsampled telephony-band loss in high frequencies) made the
1180
+ // server VAD trigger on the carrier-loopback echo of the
1181
+ // agent's OWN outbound audio in PSTN no-AEC scenarios.
1182
+ // Combined with the default ``turn_detection.create_response:
1183
+ // true``, every phantom ``speech_started`` ended a turn early
1184
+ // and auto-created a new response that the agent immediately
1185
+ // spoke over, leading to a runaway loop where the first
1186
+ // message was repeatedly cut and re-generated.
1168
1187
  turn_detection: {
1169
1188
  type: opts.vadType ?? OpenAIRealtimeVADType.SERVER_VAD,
1170
- threshold: 0.1,
1189
+ threshold: 0.5,
1171
1190
  prefix_padding_ms: 300,
1172
- silence_duration_ms: opts.silenceDurationMs ?? 500
1191
+ silence_duration_ms: opts.silenceDurationMs ?? 500,
1192
+ // Defer ``response.create`` to the application: when OpenAI's
1193
+ // server VAD commits an ``input_audio_buffer.committed`` segment
1194
+ // that turns out to be a Whisper hallucination on silence/echo,
1195
+ // auto-creating a response would generate a phantom turn (the
1196
+ // model reads the hallucinated text as user input). Patter
1197
+ // triggers ``response.create`` explicitly in the Realtime
1198
+ // stream-handler AFTER validating ``transcript_input`` against
1199
+ // the hallucination filter. Pair with ``interrupt_response:
1200
+ // false`` so server VAD also leaves in-flight responses alone —
1201
+ // barge-in is gated client-side.
1202
+ create_response: false,
1203
+ interrupt_response: false
1173
1204
  }
1174
1205
  },
1175
1206
  output: {
@@ -1244,7 +1275,7 @@ var init_openai_realtime_2 = __esm({
1244
1275
  };
1245
1276
  return originalOn(event, wrapped);
1246
1277
  };
1247
- await new Promise((resolve, reject) => {
1278
+ await new Promise((resolve2, reject) => {
1248
1279
  let sessionCreated = false;
1249
1280
  let settled = false;
1250
1281
  const ws = this.ws;
@@ -1261,7 +1292,7 @@ var init_openai_realtime_2 = __esm({
1261
1292
  ws.send(JSON.stringify({ type: "session.update", session: this.buildGASessionConfig() }));
1262
1293
  } else if (msg.type === "session.updated") {
1263
1294
  cleanup();
1264
- resolve();
1295
+ resolve2();
1265
1296
  } else if (msg.type === "error") {
1266
1297
  cleanup();
1267
1298
  try {
@@ -1299,6 +1330,131 @@ var init_openai_realtime_2 = __esm({
1299
1330
  });
1300
1331
  this.armHeartbeatAndListener();
1301
1332
  }
1333
+ /**
1334
+ * GA-API variant of {@link OpenAIRealtimeAdapter.openParkedConnection}.
1335
+ * Opens a fresh Realtime WS against the GA endpoint, exchanges
1336
+ * `session.created` → GA-shape `session.update` → `session.updated`
1337
+ * so the upstream session is fully primed, and returns the OPEN
1338
+ * socket WITHOUT taking it on `this.ws` or arming the heartbeat /
1339
+ * message listener.
1340
+ *
1341
+ * Used by `Patter.parkProviderConnections` during the carrier
1342
+ * ringing window so the per-call `StreamHandler` can adopt the
1343
+ * primed socket at carrier `start` — eliminating the TCP + TLS +
1344
+ * HTTP-101 + `session.update` ack round-trip from the critical path.
1345
+ * Saves ~300-600 ms of first-audible-word latency.
1346
+ *
1347
+ * Bounded by 8 s. Throws on timeout / handshake failure / GA-side
1348
+ * rejection. Callers treat any error as a cache miss and fall
1349
+ * through to the cold {@link connect} path.
1350
+ *
1351
+ * Billing safety: confirmed by OpenAI's Managing Realtime Costs
1352
+ * guide — `session.update` does NOT invoke the model and bills no
1353
+ * tokens. An idle parked socket costs $0.
1354
+ */
1355
+ async openParkedConnection() {
1356
+ const url2 = `wss://api.openai.com/v1/realtime?model=${encodeURIComponent(this.model)}`;
1357
+ const ws = new import_ws2.default(url2, {
1358
+ headers: { Authorization: `Bearer ${this.apiKey}` }
1359
+ });
1360
+ await new Promise((resolve2, reject) => {
1361
+ let sessionCreated = false;
1362
+ let settled = false;
1363
+ const onMessage = (raw) => {
1364
+ let msg;
1365
+ try {
1366
+ msg = JSON.parse(raw.toString());
1367
+ } catch {
1368
+ return;
1369
+ }
1370
+ if (msg.type === "session.created" && !sessionCreated) {
1371
+ sessionCreated = true;
1372
+ try {
1373
+ ws.send(JSON.stringify({ type: "session.update", session: this.buildGASessionConfig() }));
1374
+ } catch (err) {
1375
+ cleanup();
1376
+ reject(err instanceof Error ? err : new Error(String(err)));
1377
+ }
1378
+ } else if (msg.type === "session.updated") {
1379
+ cleanup();
1380
+ resolve2();
1381
+ } else if (msg.type === "error") {
1382
+ cleanup();
1383
+ reject(new Error(`OpenAI Realtime 2 parked-setup error: ${msg.error?.message ?? JSON.stringify(msg)}`));
1384
+ }
1385
+ };
1386
+ const onError = (err) => {
1387
+ cleanup();
1388
+ reject(err);
1389
+ };
1390
+ const cleanup = () => {
1391
+ if (settled) return;
1392
+ settled = true;
1393
+ clearTimeout(timer);
1394
+ ws.off("message", onMessage);
1395
+ ws.off("error", onError);
1396
+ };
1397
+ const timer = setTimeout(() => {
1398
+ cleanup();
1399
+ reject(new Error("OpenAI Realtime 2 park connect timeout"));
1400
+ }, 8e3);
1401
+ ws.on("message", onMessage);
1402
+ ws.on("error", onError);
1403
+ });
1404
+ const keepalive = setInterval(() => {
1405
+ if (ws.readyState !== ws.OPEN) {
1406
+ clearInterval(keepalive);
1407
+ return;
1408
+ }
1409
+ try {
1410
+ ws.send(JSON.stringify({ type: "session.update", session: this.buildGASessionConfig() }));
1411
+ } catch {
1412
+ clearInterval(keepalive);
1413
+ }
1414
+ }, 3e3);
1415
+ ws._parkedKeepalive = keepalive;
1416
+ return ws;
1417
+ }
1418
+ /**
1419
+ * GA-API variant of {@link OpenAIRealtimeAdapter.adoptWebSocket}. Takes
1420
+ * over a WS that {@link openParkedConnection} produced (already through
1421
+ * `session.created` + `session.update` + `session.updated`) and arms
1422
+ * the heartbeat + message listener so the GA event-translation shim
1423
+ * is wired up. Skips the cold-connect path — saves ~300-600 ms on
1424
+ * first audible word.
1425
+ *
1426
+ * Caller MUST verify `ws.readyState === OPEN` before calling. If the
1427
+ * parked WS died between park and adopt, fall back to {@link connect}.
1428
+ */
1429
+ adoptWebSocket(ws) {
1430
+ const wsAny = ws;
1431
+ if (wsAny._parkedKeepalive) {
1432
+ clearInterval(wsAny._parkedKeepalive);
1433
+ delete wsAny._parkedKeepalive;
1434
+ }
1435
+ this.ws = ws;
1436
+ const wsRef = ws;
1437
+ const originalOn = wsRef.on.bind(ws);
1438
+ wsRef.on = (event, handler) => {
1439
+ if (event !== "message") return originalOn(event, handler);
1440
+ const wrapped = (raw, ...rest) => {
1441
+ try {
1442
+ const text = typeof raw === "string" ? raw : raw.toString();
1443
+ const parsed = JSON.parse(text);
1444
+ const t = parsed.type;
1445
+ if (t && Object.prototype.hasOwnProperty.call(GA_TO_V1_EVENT_NAMES, t)) {
1446
+ parsed.type = GA_TO_V1_EVENT_NAMES[t];
1447
+ handler(JSON.stringify(parsed), ...rest);
1448
+ return;
1449
+ }
1450
+ } catch {
1451
+ }
1452
+ handler(raw, ...rest);
1453
+ };
1454
+ return originalOn(event, wrapped);
1455
+ };
1456
+ this.armHeartbeatAndListener();
1457
+ }
1302
1458
  /**
1303
1459
  * GA-API variant of {@link OpenAIRealtimeAdapter.sendFirstMessage}. Two
1304
1460
  * differences from the v1 path:
@@ -1392,15 +1548,15 @@ var init_openai_realtime_2 = __esm({
1392
1548
  return pcm16ToMulaw(pcm8);
1393
1549
  }
1394
1550
  async sendFirstMessage(text) {
1395
- this.ws?.send(JSON.stringify({
1396
- type: "response.create",
1397
- response: {
1398
- output_modalities: ["audio"],
1399
- audio: { output: { voice: this.voice } },
1400
- reasoning: { effort: "minimal" },
1401
- instructions: `Say exactly the following sentence as your first turn and nothing else: "${text}"`
1402
- }
1403
- }));
1551
+ const responseBody = {
1552
+ output_modalities: ["audio"],
1553
+ audio: { output: { voice: this.voice } },
1554
+ instructions: `Say exactly the following sentence as your first turn and nothing else: "${text}"`
1555
+ };
1556
+ if (this.options.reasoningEffort !== void 0) {
1557
+ responseBody.reasoning = { effort: this.options.reasoningEffort };
1558
+ }
1559
+ this.ws?.send(JSON.stringify({ type: "response.create", response: responseBody }));
1404
1560
  }
1405
1561
  };
1406
1562
  }
@@ -1536,7 +1692,7 @@ var init_elevenlabs_convai = __esm({
1536
1692
  wsOptions = { headers: { "xi-api-key": this.apiKey } };
1537
1693
  }
1538
1694
  this.ws = new import_ws3.default(wsUrl, wsOptions);
1539
- await new Promise((resolve, reject) => {
1695
+ await new Promise((resolve2, reject) => {
1540
1696
  const timeout = setTimeout(
1541
1697
  () => reject(new Error("ElevenLabs ConvAI connect timeout")),
1542
1698
  15e3
@@ -1560,7 +1716,7 @@ var init_elevenlabs_convai = __esm({
1560
1716
  conversation_config_override: override
1561
1717
  };
1562
1718
  this.ws.send(JSON.stringify(config2));
1563
- resolve();
1719
+ resolve2();
1564
1720
  });
1565
1721
  this.ws.once("error", (err) => {
1566
1722
  clearTimeout(timeout);
@@ -1717,20 +1873,20 @@ var init_elevenlabs_convai = __esm({
1717
1873
  return;
1718
1874
  }
1719
1875
  const ws = this.ws;
1720
- this.closePromise = new Promise((resolve) => {
1876
+ this.closePromise = new Promise((resolve2) => {
1721
1877
  if (ws.readyState === import_ws3.default.CLOSED || ws.readyState === import_ws3.default.CLOSING) {
1722
- resolve();
1878
+ resolve2();
1723
1879
  return;
1724
1880
  }
1725
1881
  const done = () => {
1726
- resolve();
1882
+ resolve2();
1727
1883
  };
1728
1884
  ws.once("close", done);
1729
1885
  ws.once("error", done);
1730
1886
  try {
1731
1887
  ws.close();
1732
1888
  } catch {
1733
- resolve();
1889
+ resolve2();
1734
1890
  }
1735
1891
  });
1736
1892
  try {
@@ -1901,11 +2057,13 @@ function calculateTelephonyCost(provider2, durationSeconds, pricing) {
1901
2057
  const minutes = provider2 === "twilio" ? Math.ceil(durationSeconds / 60) : durationSeconds / 60;
1902
2058
  return minutes * (config2.price ?? 0);
1903
2059
  }
1904
- var PricingUnit, DEFAULT_PRICING, llmPricing;
2060
+ var PRICING_VERSION, PRICING_LAST_UPDATED, PricingUnit, DEFAULT_PRICING, llmPricing;
1905
2061
  var init_pricing = __esm({
1906
2062
  "src/pricing.ts"() {
1907
2063
  "use strict";
1908
2064
  init_cjs_shims();
2065
+ PRICING_VERSION = "2026.3";
2066
+ PRICING_LAST_UPDATED = "2026-05-08";
1909
2067
  PricingUnit = {
1910
2068
  MINUTE: "minute",
1911
2069
  THOUSAND_CHARS: "1k_chars",
@@ -2213,7 +2371,31 @@ var init_pricing = __esm({
2213
2371
  }
2214
2372
  });
2215
2373
 
2374
+ // src/version.ts
2375
+ function readVersion() {
2376
+ try {
2377
+ const pkgPath = path.resolve(__dirname, "..", "package.json");
2378
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
2379
+ return typeof pkg.version === "string" && pkg.version.length > 0 ? pkg.version : "";
2380
+ } catch {
2381
+ return "";
2382
+ }
2383
+ }
2384
+ var fs, path, VERSION;
2385
+ var init_version = __esm({
2386
+ "src/version.ts"() {
2387
+ "use strict";
2388
+ init_cjs_shims();
2389
+ fs = __toESM(require("fs"));
2390
+ path = __toESM(require("path"));
2391
+ VERSION = readVersion();
2392
+ }
2393
+ });
2394
+
2216
2395
  // src/dashboard/store.ts
2396
+ function sdkVersion() {
2397
+ return VERSION;
2398
+ }
2217
2399
  function metricsFromTopLevel(meta2) {
2218
2400
  const cost = meta2.cost && typeof meta2.cost === "object" ? meta2.cost : null;
2219
2401
  const latency = meta2.latency && typeof meta2.latency === "object" ? meta2.latency : null;
@@ -2268,8 +2450,8 @@ function metadataToCallRecord(callId, meta2) {
2268
2450
  }
2269
2451
  function loadTranscriptJsonl(filePath) {
2270
2452
  try {
2271
- if (!fs.existsSync(filePath)) return [];
2272
- const raw = fs.readFileSync(filePath, "utf8");
2453
+ if (!fs2.existsSync(filePath)) return [];
2454
+ const raw = fs2.readFileSync(filePath, "utf8");
2273
2455
  const lines = raw.split("\n").filter((l) => l.trim().length > 0);
2274
2456
  const out = [];
2275
2457
  for (const line of lines) {
@@ -2306,15 +2488,16 @@ function parseTimestamp(raw) {
2306
2488
  }
2307
2489
  return null;
2308
2490
  }
2309
- var import_events, fs, path, MetricsStore;
2491
+ var import_events, fs2, path2, MetricsStore;
2310
2492
  var init_store = __esm({
2311
2493
  "src/dashboard/store.ts"() {
2312
2494
  "use strict";
2313
2495
  init_cjs_shims();
2314
2496
  import_events = require("events");
2315
- fs = __toESM(require("fs"));
2316
- path = __toESM(require("path"));
2497
+ fs2 = __toESM(require("fs"));
2498
+ path2 = __toESM(require("path"));
2317
2499
  init_logger();
2500
+ init_version();
2318
2501
  MetricsStore = class extends import_events.EventEmitter {
2319
2502
  maxCalls;
2320
2503
  calls = [];
@@ -2597,15 +2780,15 @@ var init_store = __esm({
2597
2780
  persistDeletedIds() {
2598
2781
  if (this.deletedIdsPath === null) return;
2599
2782
  try {
2600
- const dir = path.dirname(this.deletedIdsPath);
2601
- fs.mkdirSync(dir, { recursive: true });
2783
+ const dir = path2.dirname(this.deletedIdsPath);
2784
+ fs2.mkdirSync(dir, { recursive: true });
2602
2785
  const tmp = this.deletedIdsPath + ".tmp";
2603
2786
  const payload = {
2604
2787
  version: 1,
2605
2788
  deleted_call_ids: Array.from(this.deletedCallIds).sort()
2606
2789
  };
2607
- fs.writeFileSync(tmp, JSON.stringify(payload, null, 2), "utf8");
2608
- fs.renameSync(tmp, this.deletedIdsPath);
2790
+ fs2.writeFileSync(tmp, JSON.stringify(payload, null, 2), "utf8");
2791
+ fs2.renameSync(tmp, this.deletedIdsPath);
2609
2792
  } catch (err) {
2610
2793
  getLogger().debug(
2611
2794
  `MetricsStore.persistDeletedIds: ${String(err)}`
@@ -2638,7 +2821,8 @@ var init_store = __esm({
2638
2821
  avg_duration: 0,
2639
2822
  avg_latency_ms: 0,
2640
2823
  cost_breakdown: { stt: 0, tts: 0, llm: 0, telephony: 0 },
2641
- active_calls: this.activeCalls.size
2824
+ active_calls: this.activeCalls.size,
2825
+ sdk_version: sdkVersion()
2642
2826
  };
2643
2827
  }
2644
2828
  let totalCost = 0;
@@ -2677,7 +2861,8 @@ var init_store = __esm({
2677
2861
  llm: Math.round(costLlm * 1e6) / 1e6,
2678
2862
  telephony: Math.round(costTel * 1e6) / 1e6
2679
2863
  },
2680
- active_calls: this.activeCalls.size
2864
+ active_calls: this.activeCalls.size,
2865
+ sdk_version: sdkVersion()
2681
2866
  };
2682
2867
  }
2683
2868
  /**
@@ -2713,11 +2898,11 @@ var init_store = __esm({
2713
2898
  */
2714
2899
  hydrate(logRoot) {
2715
2900
  if (!logRoot) return 0;
2716
- const deletedIdsPath = path.join(logRoot, ".deleted_call_ids.json");
2901
+ const deletedIdsPath = path2.join(logRoot, ".deleted_call_ids.json");
2717
2902
  this.deletedIdsPath = deletedIdsPath;
2718
- if (fs.existsSync(deletedIdsPath)) {
2903
+ if (fs2.existsSync(deletedIdsPath)) {
2719
2904
  try {
2720
- const raw = fs.readFileSync(deletedIdsPath, "utf8");
2905
+ const raw = fs2.readFileSync(deletedIdsPath, "utf8");
2721
2906
  const payload = JSON.parse(raw);
2722
2907
  const arr = Array.isArray(payload.deleted_call_ids) ? payload.deleted_call_ids : [];
2723
2908
  for (const cid of arr) {
@@ -2731,19 +2916,19 @@ var init_store = __esm({
2731
2916
  );
2732
2917
  }
2733
2918
  }
2734
- const callsRoot = path.join(logRoot, "calls");
2735
- if (!fs.existsSync(callsRoot)) return 0;
2919
+ const callsRoot = path2.join(logRoot, "calls");
2920
+ if (!fs2.existsSync(callsRoot)) return 0;
2736
2921
  const collected = [];
2737
2922
  const seen = new Set(this.calls.map((c) => c.call_id));
2738
2923
  const walk = (dir, depth) => {
2739
2924
  let entries;
2740
2925
  try {
2741
- entries = fs.readdirSync(dir, { withFileTypes: true });
2926
+ entries = fs2.readdirSync(dir, { withFileTypes: true });
2742
2927
  } catch {
2743
2928
  return;
2744
2929
  }
2745
2930
  for (const entry of entries) {
2746
- const childPath = path.join(dir, entry.name);
2931
+ const childPath = path2.join(dir, entry.name);
2747
2932
  if (depth < 3) {
2748
2933
  if (entry.isDirectory() && /^\d+$/.test(entry.name)) {
2749
2934
  walk(childPath, depth + 1);
@@ -2751,10 +2936,10 @@ var init_store = __esm({
2751
2936
  continue;
2752
2937
  }
2753
2938
  if (!entry.isDirectory()) continue;
2754
- const metadataPath = path.join(childPath, "metadata.json");
2755
- if (!fs.existsSync(metadataPath)) continue;
2939
+ const metadataPath = path2.join(childPath, "metadata.json");
2940
+ if (!fs2.existsSync(metadataPath)) continue;
2756
2941
  try {
2757
- const raw = fs.readFileSync(metadataPath, "utf8");
2942
+ const raw = fs2.readFileSync(metadataPath, "utf8");
2758
2943
  const meta2 = JSON.parse(raw);
2759
2944
  const callId = meta2.call_id || entry.name;
2760
2945
  if (!callId || seen.has(callId)) continue;
@@ -2767,7 +2952,7 @@ var init_store = __esm({
2767
2952
  }
2768
2953
  if (!record2.transcript || record2.transcript.length === 0) {
2769
2954
  const fromJsonl = loadTranscriptJsonl(
2770
- path.join(childPath, "transcript.jsonl")
2955
+ path2.join(childPath, "transcript.jsonl")
2771
2956
  );
2772
2957
  if (fromJsonl.length > 0) record2.transcript = fromJsonl;
2773
2958
  }
@@ -2905,9 +3090,9 @@ function loadDashboardHtml() {
2905
3090
  (0, import_node_path.join)(here, "dashboard", "ui.html"),
2906
3091
  (0, import_node_path.join)(here, "..", "dashboard", "ui.html")
2907
3092
  ];
2908
- for (const path5 of candidates) {
3093
+ for (const path6 of candidates) {
2909
3094
  try {
2910
- return (0, import_node_fs.readFileSync)(path5, "utf8");
3095
+ return (0, import_node_fs.readFileSync)(path6, "utf8");
2911
3096
  } catch {
2912
3097
  }
2913
3098
  }
@@ -3298,10 +3483,10 @@ var init_remote_message = __esm({
3298
3483
  }
3299
3484
  });
3300
3485
  try {
3301
- await new Promise((resolve, reject) => {
3486
+ await new Promise((resolve2, reject) => {
3302
3487
  ws.on("open", () => {
3303
3488
  ws.send(JSON.stringify(data));
3304
- resolve();
3489
+ resolve2();
3305
3490
  });
3306
3491
  ws.on("error", (err) => {
3307
3492
  reject(err);
@@ -3311,11 +3496,11 @@ var init_remote_message = __esm({
3311
3496
  yield chunks.shift();
3312
3497
  }
3313
3498
  while (!done && !error2) {
3314
- const text = await new Promise((resolve) => {
3499
+ const text = await new Promise((resolve2) => {
3315
3500
  if (chunks.length > 0) {
3316
- resolve(chunks.shift());
3501
+ resolve2(chunks.shift());
3317
3502
  } else {
3318
- resolveNext = resolve;
3503
+ resolveNext = resolve2;
3319
3504
  }
3320
3505
  });
3321
3506
  if (text === null) break;
@@ -3461,7 +3646,7 @@ var init_deepgram_stt = __esm({
3461
3646
  const url2 = `${DEEPGRAM_WS_URL}?${params.toString()}`;
3462
3647
  let ws = null;
3463
3648
  try {
3464
- ws = await new Promise((resolve, reject) => {
3649
+ ws = await new Promise((resolve2, reject) => {
3465
3650
  const sock = new import_ws4.default(url2, {
3466
3651
  headers: { Authorization: `Token ${this.apiKey}` }
3467
3652
  });
@@ -3474,7 +3659,7 @@ var init_deepgram_stt = __esm({
3474
3659
  }, 5e3);
3475
3660
  sock.once("open", () => {
3476
3661
  clearTimeout(timer);
3477
- resolve(sock);
3662
+ resolve2(sock);
3478
3663
  });
3479
3664
  sock.once("error", (err) => {
3480
3665
  clearTimeout(timer);
@@ -3505,7 +3690,7 @@ var init_deepgram_stt = __esm({
3505
3690
  headers: { Authorization: `Token ${this.apiKey}` }
3506
3691
  });
3507
3692
  this.ws = ws;
3508
- await new Promise((resolve, reject) => {
3693
+ await new Promise((resolve2, reject) => {
3509
3694
  let settled = false;
3510
3695
  const settle = (fn) => {
3511
3696
  if (settled) return;
@@ -3517,7 +3702,7 @@ var init_deepgram_stt = __esm({
3517
3702
  () => settle(() => reject(new PatterConnectionError("Deepgram connect timeout"))),
3518
3703
  1e4
3519
3704
  );
3520
- ws.once("open", () => settle(resolve));
3705
+ ws.once("open", () => settle(resolve2));
3521
3706
  ws.once("error", (err) => settle(() => reject(err)));
3522
3707
  ws.once("unexpected-response", (_req, res) => {
3523
3708
  const status = res?.statusCode ?? 0;
@@ -3817,6 +4002,21 @@ var init_metrics = __esm({
3817
4002
  _bargeinStoppedAt = null;
3818
4003
  _turnUserText = "";
3819
4004
  _turnSttAudioSeconds = 0;
4005
+ /**
4006
+ * Guard against the recordTurnInterrupted / recordTurnComplete race.
4007
+ *
4008
+ * A VAD-path barge-in fires ``recordTurnInterrupted`` synchronously
4009
+ * inside ``handleAudioAsync`` while the in-flight pipeline LLM stream
4010
+ * keeps unwinding on its own task. When the LLM stream eventually
4011
+ * exits, the existing pipeline path falls through to
4012
+ * ``recordTurnComplete``, which would push a second turn for the same
4013
+ * logical exchange (this time carrying ``user_text=''`` because the
4014
+ * field was already reset). ``_turnAlreadyClosed`` is flipped by
4015
+ * ``recordTurnInterrupted`` and read by ``recordTurnComplete`` so the
4016
+ * late ``recordTurnComplete`` becomes a no-op until the next
4017
+ * ``startTurn`` re-arms the accumulator.
4018
+ */
4019
+ _turnAlreadyClosed = false;
3820
4020
  // Cumulative usage counters
3821
4021
  _totalSttAudioSeconds = 0;
3822
4022
  _totalTtsCharacters = 0;
@@ -3914,6 +4114,7 @@ var init_metrics = __esm({
3914
4114
  this._bargeinStoppedAt = null;
3915
4115
  this._turnUserText = "";
3916
4116
  this._turnSttAudioSeconds = 0;
4117
+ this._turnAlreadyClosed = false;
3917
4118
  this._vadStoppedAt = null;
3918
4119
  this._sttFinalAt = null;
3919
4120
  this._turnCommittedAt = null;
@@ -4070,8 +4271,18 @@ var init_metrics = __esm({
4070
4271
  recordTtsStopped(ts) {
4071
4272
  this._bargeinStoppedAt = ts ?? hrTimeMs();
4072
4273
  }
4073
- /** Close the current turn cleanly and append a `TurnMetrics` record. */
4274
+ /**
4275
+ * Close the current turn cleanly and append a `TurnMetrics` record.
4276
+ *
4277
+ * Returns ``null`` when ``recordTurnInterrupted`` has already closed
4278
+ * the current turn — this protects against the VAD-barge-in /
4279
+ * pipeline-LLM race where both paths try to finalise the same logical
4280
+ * turn and the second would otherwise push a phantom entry with
4281
+ * ``user_text=''``. The caller treats ``null`` as "nothing to emit";
4282
+ * ``emitTurnMetrics`` is already null-safe.
4283
+ */
4074
4284
  recordTurnComplete(agentText) {
4285
+ if (this._turnAlreadyClosed) return null;
4075
4286
  const latency = this._computeTurnLatency();
4076
4287
  const turn = {
4077
4288
  turn_index: this._turns.length,
@@ -4084,13 +4295,23 @@ var init_metrics = __esm({
4084
4295
  };
4085
4296
  this._turns.push(turn);
4086
4297
  this._resetTurnState();
4298
+ this._turnAlreadyClosed = true;
4087
4299
  this._eventBus?.emit("turn_ended", { callId: this.callId, turn });
4088
4300
  this._eventBus?.emit("metrics_collected", { callId: this.callId, turn });
4089
4301
  return turn;
4090
4302
  }
4091
- /** Close the current turn as interrupted (barge-in) and return the recorded metrics. */
4303
+ /**
4304
+ * Close the current turn as interrupted (barge-in) and return the
4305
+ * recorded metrics. Returns ``null`` when no turn is open, OR when
4306
+ * ``recordTurnComplete`` has already finalised the current turn —
4307
+ * bidirectional parity with the guard at the top of
4308
+ * ``recordTurnComplete``. Prevents an out-of-order interruption (e.g.
4309
+ * a future refactor that reorders the bargein + LLM-unwind paths)
4310
+ * from overwriting a turn that the complete path already emitted.
4311
+ */
4092
4312
  recordTurnInterrupted() {
4093
4313
  if (this._turnStart === null) return null;
4314
+ if (this._turnAlreadyClosed) return null;
4094
4315
  const latency = this._computeTurnLatency();
4095
4316
  const turn = {
4096
4317
  turn_index: this._turns.length,
@@ -4105,6 +4326,7 @@ var init_metrics = __esm({
4105
4326
  this._eventBus?.emit("turn_ended", { callId: this.callId, turn });
4106
4327
  this._eventBus?.emit("metrics_collected", { callId: this.callId, turn });
4107
4328
  this._resetTurnState();
4329
+ this._turnAlreadyClosed = true;
4108
4330
  this._turnCommittedMono = null;
4109
4331
  this._endpointSignalAt = null;
4110
4332
  return turn;
@@ -5615,10 +5837,10 @@ function mergeDefs(...defs) {
5615
5837
  function cloneDef(schema) {
5616
5838
  return mergeDefs(schema._zod.def);
5617
5839
  }
5618
- function getElementAtPath(obj, path5) {
5619
- if (!path5)
5840
+ function getElementAtPath(obj, path6) {
5841
+ if (!path6)
5620
5842
  return obj;
5621
- return path5.reduce((acc, key) => acc?.[key], obj);
5843
+ return path6.reduce((acc, key) => acc?.[key], obj);
5622
5844
  }
5623
5845
  function promiseAllObject(promisesObj) {
5624
5846
  const keys = Object.keys(promisesObj);
@@ -5946,11 +6168,11 @@ function explicitlyAborted(x, startIndex = 0) {
5946
6168
  }
5947
6169
  return false;
5948
6170
  }
5949
- function prefixIssues(path5, issues) {
6171
+ function prefixIssues(path6, issues) {
5950
6172
  return issues.map((iss) => {
5951
6173
  var _a3;
5952
6174
  (_a3 = iss).path ?? (_a3.path = []);
5953
- iss.path.unshift(path5);
6175
+ iss.path.unshift(path6);
5954
6176
  return iss;
5955
6177
  });
5956
6178
  }
@@ -6169,16 +6391,16 @@ function flattenError(error2, mapper = (issue2) => issue2.message) {
6169
6391
  }
6170
6392
  function formatError(error2, mapper = (issue2) => issue2.message) {
6171
6393
  const fieldErrors = { _errors: [] };
6172
- const processError = (error3, path5 = []) => {
6394
+ const processError = (error3, path6 = []) => {
6173
6395
  for (const issue2 of error3.issues) {
6174
6396
  if (issue2.code === "invalid_union" && issue2.errors.length) {
6175
- issue2.errors.map((issues) => processError({ issues }, [...path5, ...issue2.path]));
6397
+ issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
6176
6398
  } else if (issue2.code === "invalid_key") {
6177
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
6399
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
6178
6400
  } else if (issue2.code === "invalid_element") {
6179
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
6401
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
6180
6402
  } else {
6181
- const fullpath = [...path5, ...issue2.path];
6403
+ const fullpath = [...path6, ...issue2.path];
6182
6404
  if (fullpath.length === 0) {
6183
6405
  fieldErrors._errors.push(mapper(issue2));
6184
6406
  } else {
@@ -13684,7 +13906,7 @@ var init_protocol = __esm({
13684
13906
  return;
13685
13907
  }
13686
13908
  const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
13687
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
13909
+ await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
13688
13910
  options?.signal?.throwIfAborted();
13689
13911
  }
13690
13912
  } catch (error2) {
@@ -13701,7 +13923,7 @@ var init_protocol = __esm({
13701
13923
  */
13702
13924
  request(request, resultSchema, options) {
13703
13925
  const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
13704
- return new Promise((resolve, reject) => {
13926
+ return new Promise((resolve2, reject) => {
13705
13927
  const earlyReject = (error2) => {
13706
13928
  reject(error2);
13707
13929
  };
@@ -13779,7 +14001,7 @@ var init_protocol = __esm({
13779
14001
  if (!parseResult.success) {
13780
14002
  reject(parseResult.error);
13781
14003
  } else {
13782
- resolve(parseResult.data);
14004
+ resolve2(parseResult.data);
13783
14005
  }
13784
14006
  } catch (error2) {
13785
14007
  reject(error2);
@@ -14040,12 +14262,12 @@ var init_protocol = __esm({
14040
14262
  }
14041
14263
  } catch {
14042
14264
  }
14043
- return new Promise((resolve, reject) => {
14265
+ return new Promise((resolve2, reject) => {
14044
14266
  if (signal.aborted) {
14045
14267
  reject(new McpError(ErrorCode2.InvalidRequest, "Request cancelled"));
14046
14268
  return;
14047
14269
  }
14048
- const timeoutId = setTimeout(resolve, interval);
14270
+ const timeoutId = setTimeout(resolve2, interval);
14049
14271
  signal.addEventListener("abort", () => {
14050
14272
  clearTimeout(timeoutId);
14051
14273
  reject(new McpError(ErrorCode2.InvalidRequest, "Request cancelled"));
@@ -17093,7 +17315,7 @@ var require_compile = __commonJS({
17093
17315
  const schOrFunc = root.refs[ref];
17094
17316
  if (schOrFunc)
17095
17317
  return schOrFunc;
17096
- let _sch = resolve.call(this, root, ref);
17318
+ let _sch = resolve2.call(this, root, ref);
17097
17319
  if (_sch === void 0) {
17098
17320
  const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
17099
17321
  const { schemaId } = this.opts;
@@ -17120,7 +17342,7 @@ var require_compile = __commonJS({
17120
17342
  function sameSchemaEnv(s1, s2) {
17121
17343
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
17122
17344
  }
17123
- function resolve(root, ref) {
17345
+ function resolve2(root, ref) {
17124
17346
  let sch;
17125
17347
  while (typeof (sch = this.refs[ref]) == "string")
17126
17348
  ref = sch;
@@ -17339,8 +17561,8 @@ var require_utils = __commonJS({
17339
17561
  }
17340
17562
  return ind;
17341
17563
  }
17342
- function removeDotSegments(path5) {
17343
- let input = path5;
17564
+ function removeDotSegments(path6) {
17565
+ let input = path6;
17344
17566
  const output = [];
17345
17567
  let nextSlash = -1;
17346
17568
  let len = 0;
@@ -17593,8 +17815,8 @@ var require_schemes = __commonJS({
17593
17815
  wsComponent.secure = void 0;
17594
17816
  }
17595
17817
  if (wsComponent.resourceName) {
17596
- const [path5, query] = wsComponent.resourceName.split("?");
17597
- wsComponent.path = path5 && path5 !== "/" ? path5 : void 0;
17818
+ const [path6, query] = wsComponent.resourceName.split("?");
17819
+ wsComponent.path = path6 && path6 !== "/" ? path6 : void 0;
17598
17820
  wsComponent.query = query;
17599
17821
  wsComponent.resourceName = void 0;
17600
17822
  }
@@ -17754,7 +17976,7 @@ var require_fast_uri = __commonJS({
17754
17976
  }
17755
17977
  return uri;
17756
17978
  }
17757
- function resolve(baseURI, relativeURI, options) {
17979
+ function resolve2(baseURI, relativeURI, options) {
17758
17980
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
17759
17981
  const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
17760
17982
  schemelessOptions.skipEscape = true;
@@ -18012,7 +18234,7 @@ var require_fast_uri = __commonJS({
18012
18234
  var fastUri = {
18013
18235
  SCHEMES,
18014
18236
  normalize,
18015
- resolve,
18237
+ resolve: resolve2,
18016
18238
  resolveComponent,
18017
18239
  equal,
18018
18240
  serialize,
@@ -21033,12 +21255,12 @@ var require_dist = __commonJS({
21033
21255
  throw new Error(`Unknown format "${name}"`);
21034
21256
  return f;
21035
21257
  };
21036
- function addFormats(ajv, list, fs5, exportName) {
21258
+ function addFormats(ajv, list, fs6, exportName) {
21037
21259
  var _a3;
21038
21260
  var _b;
21039
21261
  (_a3 = (_b = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
21040
21262
  for (const f of list)
21041
- ajv.addFormat(f, fs5[f]);
21263
+ ajv.addFormat(f, fs6[f]);
21042
21264
  }
21043
21265
  module2.exports = exports2 = formatsPlugin;
21044
21266
  Object.defineProperty(exports2, "__esModule", { value: true });
@@ -24732,7 +24954,7 @@ var require_tensor_factory_impl = __commonJS({
24732
24954
  throw new Error("Can not access image data");
24733
24955
  }
24734
24956
  } else if (isString) {
24735
- return new Promise((resolve, reject) => {
24957
+ return new Promise((resolve2, reject) => {
24736
24958
  const canvas = createCanvas();
24737
24959
  const context = createCanvasContext(canvas);
24738
24960
  if (!image || !context) {
@@ -24748,7 +24970,7 @@ var require_tensor_factory_impl = __commonJS({
24748
24970
  const img = context.getImageData(0, 0, canvas.width, canvas.height);
24749
24971
  bufferToTensorOptions.height = canvas.height;
24750
24972
  bufferToTensorOptions.width = canvas.width;
24751
- resolve((0, exports2.bufferToTensor)(img.data, bufferToTensorOptions));
24973
+ resolve2((0, exports2.bufferToTensor)(img.data, bufferToTensorOptions));
24752
24974
  };
24753
24975
  });
24754
24976
  } else {
@@ -25776,10 +25998,10 @@ var require_backend2 = __commonJS({
25776
25998
  endProfiling() {
25777
25999
  }
25778
26000
  async run(feeds, fetches, options) {
25779
- return new Promise((resolve, reject) => {
26001
+ return new Promise((resolve2, reject) => {
25780
26002
  setImmediate(() => {
25781
26003
  try {
25782
- resolve(__classPrivateFieldGet(this, _OnnxruntimeSessionHandler_inferenceSession, "f").run(feeds, fetches, options));
26004
+ resolve2(__classPrivateFieldGet(this, _OnnxruntimeSessionHandler_inferenceSession, "f").run(feeds, fetches, options));
25783
26005
  } catch (e) {
25784
26006
  reject(e);
25785
26007
  }
@@ -25793,10 +26015,10 @@ var require_backend2 = __commonJS({
25793
26015
  return Promise.resolve();
25794
26016
  }
25795
26017
  async createInferenceSessionHandler(pathOrBuffer, options) {
25796
- return new Promise((resolve, reject) => {
26018
+ return new Promise((resolve2, reject) => {
25797
26019
  setImmediate(() => {
25798
26020
  try {
25799
- resolve(new OnnxruntimeSessionHandler(pathOrBuffer, options || {}));
26021
+ resolve2(new OnnxruntimeSessionHandler(pathOrBuffer, options || {}));
25800
26022
  } catch (e) {
25801
26023
  reject(e);
25802
26024
  }
@@ -25873,20 +26095,20 @@ function resolveModuleDirs() {
25873
26095
  }
25874
26096
  try {
25875
26097
  const url2 = importMetaUrl;
25876
- if (url2) candidates.push(path2.dirname((0, import_node_url.fileURLToPath)(url2)));
26098
+ if (url2) candidates.push(path3.dirname((0, import_node_url.fileURLToPath)(url2)));
25877
26099
  } catch {
25878
26100
  }
25879
26101
  try {
25880
26102
  const url2 = importMetaUrl;
25881
26103
  if (url2) {
25882
26104
  const req = (0, import_node_module.createRequire)(url2);
25883
- candidates.push(path2.dirname(req.resolve("getpatter/package.json")));
26105
+ candidates.push(path3.dirname(req.resolve("getpatter/package.json")));
25884
26106
  }
25885
26107
  } catch {
25886
26108
  }
25887
26109
  try {
25888
- const req = (0, import_node_module.createRequire)(path2.join(process.cwd(), "package.json"));
25889
- candidates.push(path2.dirname(req.resolve("getpatter/package.json")));
26110
+ const req = (0, import_node_module.createRequire)(path3.join(process.cwd(), "package.json"));
26111
+ candidates.push(path3.dirname(req.resolve("getpatter/package.json")));
25890
26112
  } catch {
25891
26113
  }
25892
26114
  candidates.push(process.cwd());
@@ -25895,13 +26117,13 @@ function resolveModuleDirs() {
25895
26117
  function resolveDefaultModelPath() {
25896
26118
  for (const dir of MODULE_DIRS) {
25897
26119
  const candidates = [
25898
- path2.join(dir, "resources", "silero_vad.onnx"),
25899
- path2.join(dir, "..", "resources", "silero_vad.onnx"),
25900
- path2.join(dir, "dist", "resources", "silero_vad.onnx")
26120
+ path3.join(dir, "resources", "silero_vad.onnx"),
26121
+ path3.join(dir, "..", "resources", "silero_vad.onnx"),
26122
+ path3.join(dir, "dist", "resources", "silero_vad.onnx")
25901
26123
  ];
25902
- for (const c of candidates) if (fs2.existsSync(c)) return c;
26124
+ for (const c of candidates) if (fs3.existsSync(c)) return c;
25903
26125
  }
25904
- return path2.join(MODULE_DIRS[0] ?? process.cwd(), "resources", "silero_vad.onnx");
26126
+ return path3.join(MODULE_DIRS[0] ?? process.cwd(), "resources", "silero_vad.onnx");
25905
26127
  }
25906
26128
  function classifyOnnxError(err) {
25907
26129
  const msg = err?.message ?? String(err);
@@ -25919,7 +26141,7 @@ async function loadOnnxRuntime() {
25919
26141
  firstErr = e;
25920
26142
  }
25921
26143
  try {
25922
- const req = (0, import_node_module.createRequire)(path2.join(process.cwd(), "package.json"));
26144
+ const req = (0, import_node_module.createRequire)(path3.join(process.cwd(), "package.json"));
25923
26145
  return req("onnxruntime-node");
25924
26146
  } catch (secondErr) {
25925
26147
  const importClass = classifyOnnxError(firstErr);
@@ -25955,14 +26177,14 @@ ${remedy}
25955
26177
  throw err;
25956
26178
  }
25957
26179
  }
25958
- var import_node_module, fs2, path2, import_node_url, SUPPORTED_SAMPLE_RATES, MODULE_DIRS, DEFAULT_MODEL_PATH, ExpFilter, OnnxModel, SileroVAD;
26180
+ var import_node_module, fs3, path3, import_node_url, SUPPORTED_SAMPLE_RATES, MODULE_DIRS, DEFAULT_MODEL_PATH, ExpFilter, OnnxModel, SileroVAD;
25959
26181
  var init_silero_vad = __esm({
25960
26182
  "src/providers/silero-vad.ts"() {
25961
26183
  "use strict";
25962
26184
  init_cjs_shims();
25963
26185
  import_node_module = require("module");
25964
- fs2 = __toESM(require("fs"));
25965
- path2 = __toESM(require("path"));
26186
+ fs3 = __toESM(require("fs"));
26187
+ path3 = __toESM(require("path"));
25966
26188
  import_node_url = require("url");
25967
26189
  SUPPORTED_SAMPLE_RATES = [8e3, 16e3];
25968
26190
  MODULE_DIRS = resolveModuleDirs();
@@ -26127,6 +26349,19 @@ var init_silero_vad = __esm({
26127
26349
  static forPhoneCall(options = {}) {
26128
26350
  return _SileroVAD.load({
26129
26351
  sampleRate: 16e3,
26352
+ // Telephony bumps the activation threshold from the upstream
26353
+ // 0.5 → 0.8 (with deactivation 0.65) so background voices and
26354
+ // low-volume audio in the caller's room don't trip barge-in.
26355
+ // Near-mic speech typically scores 0.85-0.98 on Silero — above
26356
+ // 0.8 — while a distant second speaker through a phone's noise-
26357
+ // suppression pipeline lands around 0.4-0.6 and is now correctly
26358
+ // ignored. Bumped twice during 2026-05-20 acceptance: first 0.5
26359
+ // → 0.7 (still triggered on quiet voices), then 0.7 → 0.8.
26360
+ // Trade-off: a whispered legitimate input may not trigger;
26361
+ // typical phone-call speakers are unaffected. Pass an explicit
26362
+ // ``activationThreshold`` to override per call site.
26363
+ activationThreshold: 0.8,
26364
+ deactivationThreshold: 0.65,
26130
26365
  ...options
26131
26366
  });
26132
26367
  }
@@ -26587,7 +26822,23 @@ var init_stream_handler = __esm({
26587
26822
  ".",
26588
26823
  "bye",
26589
26824
  "right",
26590
- "cool"
26825
+ "cool",
26826
+ // Whisper YouTube-caption hallucinations
26827
+ "thank you for watching",
26828
+ "thanks for watching",
26829
+ "thank you for watching!",
26830
+ "thanks for watching!",
26831
+ "thank you so much for watching",
26832
+ "thanks for listening",
26833
+ "please subscribe",
26834
+ "subscribe",
26835
+ "music",
26836
+ "[music]",
26837
+ "\u266A",
26838
+ "[no audio]",
26839
+ "[silence]",
26840
+ "[blank_audio]",
26841
+ "(silence)"
26591
26842
  ]);
26592
26843
  StreamHandler = class _StreamHandler {
26593
26844
  deps;
@@ -26713,13 +26964,17 @@ var init_stream_handler = __esm({
26713
26964
  * Same as the AEC variant but for deployments where AEC is OFF
26714
26965
  * (default on PSTN — Twilio/Telnyx). Without an adaptive filter to
26715
26966
  * converge, the only justification for a gate is anti-flicker on
26716
- * micro-events (cough, click). 100 ms covers the first PSTN echo
26717
- * round-trip (~40-100 ms) while allowing barge-in from 100 ms into
26718
- * the agent's turn covering nearly all of any response.
26719
- * Previously 250 ms, which blocked barge-in entirely on short (<500 ms)
26720
- * agent responses.
26721
- */
26722
- static MIN_AGENT_SPEAKING_MS_BEFORE_BARGE_IN_NO_AEC = 100;
26967
+ * micro-events (cough, click). Raised 100 500 ms on 2026-05-19
26968
+ * after the 0.6.2 acceptance run showed a phantom VAD speech_start
26969
+ * firing on the very first inbound frame (~500 ms into the call,
26970
+ * which is past a 100 ms gate). The phantom barge-in cancelled the
26971
+ * prewarmed firstMessage, the user heard a clipped (graffiante)
26972
+ * audio fragment, and the SDK left ``_turnAlreadyClosed=true`` so
26973
+ * subsequent ``recordTurnComplete`` calls were no-ops. 500 ms
26974
+ * filters those phantoms while still letting a real interruption
26975
+ * land within half a second of agent onset.
26976
+ */
26977
+ static MIN_AGENT_SPEAKING_MS_BEFORE_BARGE_IN_NO_AEC = 500;
26723
26978
  /** Handle for the pending grace-period timer, so it can be cleared on cleanup. */
26724
26979
  graceTimer = null;
26725
26980
  /**
@@ -26759,30 +27014,12 @@ var init_stream_handler = __esm({
26759
27014
  * coexist without name collisions even when firstMessage finishes while
26760
27015
  * a Realtime turn is still streaming.
26761
27016
  */
26762
- firstMessageMarkCounter = 0;
26763
- /**
26764
- * Maximum unconfirmed Twilio marks while streaming firstMessage. Each
26765
- * chunk is 40 ms of audio at 16 kHz PCM16, so a window of 3 caps
26766
- * the in-flight queue at ~120 ms. This means a barge-in's
26767
- * ``sendClear`` has at most 120 ms of already-buffered audio to flush
26768
- * — vs. ~2-5 s with the previous burst-send code, which was the
26769
- * root cause of "firstMessage non interrompibile". Higher values
26770
- * smooth playback under jittery RTT (each mark echo adds ~150-250 ms
26771
- * RTT on PSTN) at the cost of longer barge-in latency; lower values
26772
- * risk under-buffering. 3 hit the smallest barge-in cap without
26773
- * audible gaps in 2026-05 acceptance.
26774
- */
26775
- static FIRST_MESSAGE_MARK_WINDOW = 3;
26776
- /**
26777
- * Per-chunk soft timeout (ms) while awaiting a mark echo. Twilio's
26778
- * mark echoes typically arrive within 100-250 ms of audio playback.
26779
- * Capping at 500 ms guards against carriers (or test doubles) that
26780
- * never echo — without it a stalled echo would deadlock the loop and
26781
- * the agent would freeze mid-utterance. On timeout we drop the
26782
- * waiter from the queue and continue: playout may glitch by one
26783
- * chunk but the call stays alive.
26784
- */
26785
- static MARK_AWAIT_TIMEOUT_MS = 500;
27017
+ // firstMessageMarkCounter / FIRST_MESSAGE_MARK_WINDOW /
27018
+ // MARK_AWAIT_TIMEOUT_MS were retired with the move to the Twilio-FIFO-
27019
+ // trusts model (sendPacedFirstMessageBytes no longer emits marks).
27020
+ // Marks are still consumed via ``onMark`` for any adapter that wants
27021
+ // to round-trip one, but the firstMessage path no longer back-pressures
27022
+ // on them.
26786
27023
  /**
26787
27024
  * Minimum drain window (ms) between a ``cancelSpeaking`` and the next
26788
27025
  * ``beginSpeaking``. 150 ms covers a typical PSTN jitter buffer drain
@@ -26847,6 +27084,14 @@ var init_stream_handler = __esm({
26847
27084
  } catch {
26848
27085
  }
26849
27086
  }
27087
+ const ttsCancelable = this.tts;
27088
+ if (typeof ttsCancelable?.cancelActiveStream === "function") {
27089
+ try {
27090
+ ttsCancelable.cancelActiveStream();
27091
+ } catch (err) {
27092
+ getLogger().debug(`TTS cancelActiveStream raised: ${String(err)}`);
27093
+ }
27094
+ }
26850
27095
  }
26851
27096
  /**
26852
27097
  * Resolve every entry in ``pendingMarks`` and empty the queue. Idempotent
@@ -26863,56 +27108,19 @@ var init_stream_handler = __esm({
26863
27108
  }
26864
27109
  this.pendingMarks.length = 0;
26865
27110
  }
27111
+ // Mark-based back-pressure (sendMarkAwaitable / waitForMarkWindow)
27112
+ // was removed when sendPacedFirstMessageBytes switched to the
27113
+ // Twilio-FIFO-trusts model — see that method's doc comment for
27114
+ // rationale. ``pendingMarks`` and ``onMark`` are still kept so an
27115
+ // adapter that wants to round-trip a mark for some other purpose can
27116
+ // still do so without breaking the firstMessage path.
26866
27117
  /**
26867
- * Push a Twilio ``mark`` event AFTER the corresponding audio chunk and
26868
- * return a promise that resolves when the mark is echoed back via
26869
- * ``onMark`` (or when ``cancelSpeaking`` drains the queue, or after
26870
- * ``MARK_AWAIT_TIMEOUT_MS``). Returns null on non-Twilio carriers the
26871
- * caller is expected to fall back to time-based pacing in that case.
26872
- */
26873
- sendMarkAwaitable() {
26874
- if (this.deps.bridge.telephonyProvider !== "twilio") return null;
26875
- this.firstMessageMarkCounter += 1;
26876
- const markName = `fm_${this.firstMessageMarkCounter}`;
26877
- let resolve;
26878
- const promise = new Promise((r) => {
26879
- resolve = r;
26880
- });
26881
- this.pendingMarks.push({ name: markName, resolve, promise });
26882
- try {
26883
- this.deps.bridge.sendMark(this.ws, markName, this.streamSid);
26884
- } catch (err) {
26885
- getLogger().debug(`sendMark failed (${markName}): ${String(err)}`);
26886
- const idx = this.pendingMarks.findIndex((m) => m.name === markName);
26887
- if (idx >= 0) this.pendingMarks.splice(idx, 1);
26888
- return Promise.resolve();
26889
- }
26890
- return promise;
26891
- }
26892
- /**
26893
- * If the in-flight mark queue is at or above ``FIRST_MESSAGE_MARK_WINDOW``
26894
- * entries, wait for the oldest entry to clear (mark echoed, agent
26895
- * cancelled, or per-mark timeout). Repeats until the queue depth is
26896
- * within the window — under high RTT the carrier may have several
26897
- * marks queued and we want every loop iteration to be naturally back-
26898
- * pressured by playback.
26899
- */
26900
- async waitForMarkWindow() {
26901
- while (this.isSpeaking && this.pendingMarks.length >= _StreamHandler.FIRST_MESSAGE_MARK_WINDOW) {
26902
- const oldest = this.pendingMarks[0];
26903
- const timeout = new Promise(
26904
- (resolve) => setTimeout(resolve, _StreamHandler.MARK_AWAIT_TIMEOUT_MS)
26905
- );
26906
- await Promise.race([oldest.promise, timeout]);
26907
- if (this.pendingMarks[0] === oldest) {
26908
- this.pendingMarks.shift();
26909
- }
26910
- }
26911
- }
26912
- /**
26913
- * Bytes-per-millisecond for a 16 kHz PCM16 mono stream. Used by the
26914
- * non-Twilio firstMessage pacing path to translate chunk size into a
26915
- * playout-duration sleep. 16000 samples/sec × 2 bytes = 32 bytes/ms.
27118
+ * Bytes-per-millisecond for a 16 kHz PCM16 mono stream. Used by
27119
+ * ``sendPacedFirstMessageBytes`` to translate chunk size into a
27120
+ * playout-duration sleep so we never deliver faster than the carrier
27121
+ * can decode + play out (which manifested as severe crackling on the
27122
+ * HTTP-TTS path with client-side resampling). 16000 samples/sec × 2
27123
+ * bytes/sample = 32 bytes/ms.
26916
27124
  */
26917
27125
  static PCM16_16K_BYTES_PER_MS = 32;
26918
27126
  /** Cancel and clear the pending grace timer, if any. */
@@ -27350,7 +27558,7 @@ var init_stream_handler = __esm({
27350
27558
  if (activeVad && !this.vadDisabled) {
27351
27559
  try {
27352
27560
  const vadPromise = activeVad.processFrame(pcm16k, 16e3);
27353
- const timeoutPromise = new Promise((resolve) => setTimeout(() => resolve(null), 25));
27561
+ const timeoutPromise = new Promise((resolve2) => setTimeout(() => resolve2(null), 25));
27354
27562
  const evt = await Promise.race([vadPromise, timeoutPromise]);
27355
27563
  if (evt) {
27356
27564
  getLogger().info(
@@ -27486,9 +27694,21 @@ var init_stream_handler = __esm({
27486
27694
  /** Handle call stop / stream end. */
27487
27695
  /** Handle a carrier-emitted `stop` event signalling the call has ended. */
27488
27696
  async handleStop() {
27697
+ if (this.llmAbort !== null) {
27698
+ try {
27699
+ this.llmAbort.abort();
27700
+ } catch {
27701
+ }
27702
+ }
27703
+ const ttsCancelable = this.tts;
27704
+ if (typeof ttsCancelable?.cancelActiveStream === "function") {
27705
+ try {
27706
+ ttsCancelable.cancelActiveStream();
27707
+ } catch {
27708
+ }
27709
+ }
27489
27710
  this.clearPendingBargeIn();
27490
27711
  this.drainPendingMarks();
27491
- this.firstMessageMarkCounter = 0;
27492
27712
  this.clearGraceTimer();
27493
27713
  this.flushResamplers();
27494
27714
  await this.closeSttOnce();
@@ -27501,9 +27721,21 @@ var init_stream_handler = __esm({
27501
27721
  /** Handle WebSocket close event. */
27502
27722
  /** Tear down adapter, STT/TTS, and per-call state when the carrier WebSocket closes. */
27503
27723
  async handleWsClose() {
27724
+ if (this.llmAbort !== null) {
27725
+ try {
27726
+ this.llmAbort.abort();
27727
+ } catch {
27728
+ }
27729
+ }
27730
+ const ttsCancelable = this.tts;
27731
+ if (typeof ttsCancelable?.cancelActiveStream === "function") {
27732
+ try {
27733
+ ttsCancelable.cancelActiveStream();
27734
+ } catch {
27735
+ }
27736
+ }
27504
27737
  this.clearPendingBargeIn();
27505
27738
  this.drainPendingMarks();
27506
- this.firstMessageMarkCounter = 0;
27507
27739
  this.clearGraceTimer();
27508
27740
  this.flushResamplers();
27509
27741
  await this.closeSttOnce();
@@ -27542,13 +27774,39 @@ var init_stream_handler = __esm({
27542
27774
  * Maintains a 1-byte carry across calls so unaligned HTTP chunks from
27543
27775
  * streaming TTS providers never byte-swap the PCM16 samples downstream.
27544
27776
  */
27545
- encodePipelineAudio(pcm16k) {
27546
- const aligned = this.alignPcm16(pcm16k);
27777
+ encodePipelineAudio(audioChunk) {
27778
+ if (this.ttsOutputFormatNativeForCarrier === true) {
27779
+ return audioChunk.toString("base64");
27780
+ }
27781
+ const aligned = this.alignPcm16(audioChunk);
27547
27782
  if (aligned.length === 0) return "";
27548
27783
  const pcm8k = this.outboundResampler.process(aligned);
27549
27784
  const mulaw = pcm16ToMulaw(pcm8k);
27550
27785
  return mulaw.toString("base64");
27551
27786
  }
27787
+ /**
27788
+ * Cached result of ``isTtsOutputFormatNativeForCarrier()`` — settled
27789
+ * once at ``initPipeline`` time after ``setTelephonyCarrier`` has run
27790
+ * on the TTS adapter. Stable for the call lifetime: changes to the
27791
+ * adapter's output format mid-call would NOT flip this. ``true`` means
27792
+ * ``encodePipelineAudio`` can take the bypass path.
27793
+ */
27794
+ ttsOutputFormatNativeForCarrier = false;
27795
+ /**
27796
+ * Probe whether the TTS adapter is configured to emit bytes already in
27797
+ * the carrier's wire codec. Currently: Twilio expects ``ulaw_8000``,
27798
+ * Telnyx expects ``pcm_16000`` (no client transcode in either case if
27799
+ * matched). Anything else takes the resample-and-encode path.
27800
+ */
27801
+ isTtsOutputFormatNativeForCarrier() {
27802
+ if (!this.tts) return false;
27803
+ const fmt = this.tts.outputFormat;
27804
+ if (typeof fmt !== "string") return false;
27805
+ const carrier = this.deps.bridge.telephonyProvider;
27806
+ if (carrier === "twilio") return fmt === "ulaw_8000";
27807
+ if (carrier === "telnyx") return fmt === "pcm_16000";
27808
+ return false;
27809
+ }
27552
27810
  /**
27553
27811
  * Prepend any carry byte from the previous chunk, return the even-length
27554
27812
  * portion, and stash the final odd byte (if any) for the next call.
@@ -27559,17 +27817,11 @@ var init_stream_handler = __esm({
27559
27817
  this.ttsByteCarry = alignedLen < combined.length ? combined.subarray(alignedLen) : null;
27560
27818
  return combined.subarray(0, alignedLen);
27561
27819
  }
27562
- /**
27563
- * 40 ms @ 16 kHz mono PCM16 = 1280 bytes. Sized to mirror the smallest
27564
- * live-TTS chunk boundary so cancel granularity (mark/clear bookkeeping)
27565
- * is identical regardless of whether the firstMessage came from the
27566
- * prewarm cache or a live ``tts.synthesizeStream`` stream.
27567
- */
27568
- static PREWARM_CHUNK_BYTES = 1280;
27569
27820
  /**
27570
27821
  * Stream a cached firstMessage buffer in pacing-friendly chunks.
27571
27822
  *
27572
- * Splits ``prewarmBytes`` into ``PREWARM_CHUNK_BYTES`` slices and
27823
+ * Splits ``prewarmBytes`` into 20 ms slices (matching Twilio's PSTN
27824
+ * frame quantum) and
27573
27825
  * forwards each through ``deps.bridge.sendAudio`` exactly like the
27574
27826
  * live TTS path does — preserving Twilio mark/clear granularity. A
27575
27827
  * single multi-second sendAudio call would push the whole intro into
@@ -27585,7 +27837,7 @@ var init_stream_handler = __esm({
27585
27837
  return this.sendPacedFirstMessageBytes(prewarmBytes);
27586
27838
  }
27587
27839
  /**
27588
- * Iterate ``bytes`` as ``PREWARM_CHUNK_BYTES``-sized PCM16 slices and
27840
+ * Iterate ``bytes`` in 20 ms slices (Twilio PSTN frame quantum) and
27589
27841
  * forward each via ``deps.bridge.sendAudio`` with mark-gated pacing
27590
27842
  * (Twilio) or playout-time-based pacing (Telnyx). Caps the carrier-
27591
27843
  * side buffer at ``FIRST_MESSAGE_MARK_WINDOW`` chunks so a barge-in's
@@ -27602,30 +27854,20 @@ var init_stream_handler = __esm({
27602
27854
  */
27603
27855
  async sendPacedFirstMessageBytes(bytes) {
27604
27856
  if (this.pendingMarks.length > 0) this.drainPendingMarks();
27605
- this.firstMessageMarkCounter = 0;
27606
27857
  let firstChunkSent = false;
27607
- let initialFillComplete = false;
27608
- for (let i = 0; i < bytes.length; i += _StreamHandler.PREWARM_CHUNK_BYTES) {
27858
+ const PSTN_FRAME_MS = 20;
27859
+ const bytesPerMs = this.ttsOutputFormatNativeForCarrier ? 8 : _StreamHandler.PCM16_16K_BYTES_PER_MS;
27860
+ const sliceBytes = bytesPerMs * PSTN_FRAME_MS;
27861
+ for (let i = 0; i < bytes.length; i += sliceBytes) {
27609
27862
  if (!this.isSpeaking) break;
27610
- await this.waitForMarkWindow();
27611
- if (!this.isSpeaking) break;
27612
- const chunk = bytes.subarray(i, i + _StreamHandler.PREWARM_CHUNK_BYTES);
27863
+ const chunk = bytes.subarray(i, i + sliceBytes);
27613
27864
  if (!firstChunkSent) firstChunkSent = true;
27614
- if (this.aec) this.aec.pushFarEnd(chunk);
27865
+ if (this.aec && !this.ttsOutputFormatNativeForCarrier) {
27866
+ this.aec.pushFarEnd(chunk);
27867
+ }
27615
27868
  const encoded = this.encodePipelineAudio(chunk);
27616
27869
  this.deps.bridge.sendAudio(this.ws, encoded, this.streamSid);
27617
27870
  this.markFirstAudioSent();
27618
- const markPromise = this.sendMarkAwaitable();
27619
- if (!initialFillComplete && this.pendingMarks.length >= _StreamHandler.FIRST_MESSAGE_MARK_WINDOW) {
27620
- initialFillComplete = true;
27621
- }
27622
- if (markPromise === null || initialFillComplete) {
27623
- const playoutMs = Math.max(
27624
- 1,
27625
- Math.floor(chunk.length / _StreamHandler.PCM16_16K_BYTES_PER_MS)
27626
- );
27627
- await new Promise((resolve) => setTimeout(resolve, playoutMs));
27628
- }
27629
27871
  }
27630
27872
  return firstChunkSent;
27631
27873
  }
@@ -27645,6 +27887,12 @@ var init_stream_handler = __esm({
27645
27887
  getLogger().debug(`TTS setTelephonyCarrier failed (${label}): ${String(e)}`);
27646
27888
  }
27647
27889
  }
27890
+ this.ttsOutputFormatNativeForCarrier = this.isTtsOutputFormatNativeForCarrier();
27891
+ if (this.ttsOutputFormatNativeForCarrier) {
27892
+ getLogger().debug(
27893
+ `TTS outputFormat matches ${this.deps.bridge.telephonyProvider} wire codec \u2014 bypassing client-side transcode`
27894
+ );
27895
+ }
27648
27896
  }
27649
27897
  if (!this.stt) {
27650
27898
  getLogger().debug(`Pipeline mode (${label}): no STT configured`);
@@ -28347,16 +28595,49 @@ var init_stream_handler = __esm({
28347
28595
  async initRealtimeAdapter(resolvedPrompt) {
28348
28596
  const label = this.deps.bridge.label;
28349
28597
  this.adapter = this.deps.buildAIAdapter(resolvedPrompt);
28350
- try {
28351
- await this.adapter.connect();
28352
- getLogger().debug(`AI adapter connected (${label})`);
28353
- } catch (e) {
28354
- getLogger().error(`AI adapter connect FAILED (${label}):`, e);
28598
+ let parked;
28599
+ if (typeof this.deps.popPrewarmedConnections === "function") {
28355
28600
  try {
28356
- await this.deps.bridge.endCall(this.callId, this.ws);
28357
- } catch {
28601
+ parked = this.deps.popPrewarmedConnections(this.callId);
28602
+ } catch (err) {
28603
+ getLogger().debug(`popPrewarmedConnections raised: ${String(err)}`);
28604
+ }
28605
+ }
28606
+ const parkedRealtimeWs = parked?.openaiRealtime;
28607
+ let adoptOk = false;
28608
+ if (parkedRealtimeWs !== void 0) {
28609
+ const adapterAny = this.adapter;
28610
+ const wsAlive = parkedRealtimeWs.readyState === 1;
28611
+ if (typeof adapterAny?.adoptWebSocket === "function" && wsAlive) {
28612
+ try {
28613
+ adapterAny.adoptWebSocket(parkedRealtimeWs);
28614
+ getLogger().info(
28615
+ `[CONNECT] callId=${this.callId} provider=openai_realtime source=adopted ms=0`
28616
+ );
28617
+ adoptOk = true;
28618
+ } catch (err) {
28619
+ getLogger().debug(`Realtime adoptWebSocket failed: ${String(err)}; falling back`);
28620
+ }
28621
+ }
28622
+ if (!adoptOk) {
28623
+ try {
28624
+ parkedRealtimeWs.close();
28625
+ } catch {
28626
+ }
28627
+ }
28628
+ }
28629
+ if (!adoptOk) {
28630
+ try {
28631
+ await this.adapter.connect();
28632
+ getLogger().debug(`AI adapter connected (${label})`);
28633
+ } catch (e) {
28634
+ getLogger().error(`AI adapter connect FAILED (${label}):`, e);
28635
+ try {
28636
+ await this.deps.bridge.endCall(this.callId, this.ws);
28637
+ } catch {
28638
+ }
28639
+ return;
28358
28640
  }
28359
- return;
28360
28641
  }
28361
28642
  if (this.deps.agent.firstMessage) {
28362
28643
  this.metricsAcc.startTurn();
@@ -28476,8 +28757,21 @@ var init_stream_handler = __esm({
28476
28757
  await this.emitUserSpeechEnded();
28477
28758
  }
28478
28759
  async onAdapterTranscriptInput(inputText) {
28760
+ const stripped = inputText.trim().toLowerCase();
28761
+ if (HALLUCINATIONS.has(stripped) || stripped === "") {
28762
+ getLogger().debug(
28763
+ `Realtime transcript_input dropped (likely Whisper hallucination on silence/echo): ${sanitizeLogValue(inputText.slice(0, 60))}`
28764
+ );
28765
+ this.userTranscriptPending = false;
28766
+ return;
28767
+ }
28479
28768
  getLogger().debug(`User (${this.deps.bridge.label}): ${sanitizeLogValue(inputText)}`);
28480
28769
  this.history.push({ role: "user", text: inputText, timestamp: Date.now() });
28770
+ if (this.adapter instanceof OpenAIRealtimeAdapter) {
28771
+ void this.adapter.requestResponse().catch(
28772
+ (err) => getLogger().debug(`Realtime requestResponse failed: ${String(err)}`)
28773
+ );
28774
+ }
28481
28775
  if (!this.metricsAcc.turnActive) {
28482
28776
  this.metricsAcc.startTurn();
28483
28777
  this.currentAgentText = "";
@@ -28629,6 +28923,18 @@ var init_stream_handler = __esm({
28629
28923
  await this.flushAssistantTurn(text);
28630
28924
  }
28631
28925
  async onAdapterSpeechInterrupt() {
28926
+ if (this.adapter instanceof OpenAIRealtimeAdapter) {
28927
+ const startedAt = this.adapter.currentResponseFirstAudioAt;
28928
+ if (startedAt !== null) {
28929
+ const elapsedMs = Date.now() - startedAt;
28930
+ if (elapsedMs < _StreamHandler.MIN_AGENT_SPEAKING_MS_BEFORE_BARGE_IN_NO_AEC) {
28931
+ getLogger().info(
28932
+ `Realtime barge-in suppressed (response < gate, ${elapsedMs}ms)`
28933
+ );
28934
+ return;
28935
+ }
28936
+ }
28937
+ }
28632
28938
  this.deps.bridge.sendClear(this.ws, this.streamSid);
28633
28939
  if (this.adapter instanceof OpenAIRealtimeAdapter) this.adapter.cancelResponse();
28634
28940
  this.metricsAcc.recordTurnInterrupted();
@@ -28835,24 +29141,24 @@ var init_stream_handler = __esm({
28835
29141
 
28836
29142
  // src/services/call-log.ts
28837
29143
  function xdgDataHome() {
28838
- return process.env.XDG_DATA_HOME || path3.join(os.homedir(), ".local", "share");
29144
+ return process.env.XDG_DATA_HOME || path4.join(os.homedir(), ".local", "share");
28839
29145
  }
28840
29146
  function platformDefaultRoot() {
28841
29147
  if (process.platform === "darwin") {
28842
- return path3.join(os.homedir(), "Library", "Application Support", "patter");
29148
+ return path4.join(os.homedir(), "Library", "Application Support", "patter");
28843
29149
  }
28844
29150
  if (process.platform === "win32") {
28845
29151
  const localAppData = process.env.LOCALAPPDATA;
28846
- if (localAppData) return path3.join(localAppData, "patter");
28847
- return path3.join(os.homedir(), "AppData", "Local", "patter");
29152
+ if (localAppData) return path4.join(localAppData, "patter");
29153
+ return path4.join(os.homedir(), "AppData", "Local", "patter");
28848
29154
  }
28849
- return path3.join(xdgDataHome(), "patter");
29155
+ return path4.join(xdgDataHome(), "patter");
28850
29156
  }
28851
29157
  function resolveLogRoot(explicit) {
28852
29158
  const value = explicit ?? process.env.PATTER_LOG_DIR;
28853
29159
  if (!value) return null;
28854
29160
  if (value.trim().toLowerCase() === "auto") return platformDefaultRoot();
28855
- if (value.startsWith("~")) return path3.join(os.homedir(), value.slice(1));
29161
+ if (value.startsWith("~")) return path4.join(os.homedir(), value.slice(1));
28856
29162
  return value;
28857
29163
  }
28858
29164
  function retentionDays() {
@@ -28863,9 +29169,9 @@ function retentionDays() {
28863
29169
  return Math.max(0, parsed);
28864
29170
  }
28865
29171
  function redactMode() {
28866
- const raw = (process.env.PATTER_LOG_REDACT_PHONE || "mask").trim().toLowerCase();
29172
+ const raw = (process.env.PATTER_LOG_REDACT_PHONE || "full").trim().toLowerCase();
28867
29173
  if (raw === "full" || raw === "mask" || raw === "hash_only") return raw;
28868
- return "mask";
29174
+ return "full";
28869
29175
  }
28870
29176
  function redactPhone(raw) {
28871
29177
  if (!raw) return "";
@@ -28881,9 +29187,9 @@ function utcIso(tsSeconds) {
28881
29187
  return new Date(ms).toISOString();
28882
29188
  }
28883
29189
  async function atomicWriteJson(filePath, payload) {
28884
- const dir = path3.dirname(filePath);
29190
+ const dir = path4.dirname(filePath);
28885
29191
  await import_node_fs2.promises.mkdir(dir, { recursive: true });
28886
- const tmp = path3.join(dir, `.tmp.${process.pid}.${crypto4.randomBytes(4).toString("hex")}.json`);
29192
+ const tmp = path4.join(dir, `.tmp.${process.pid}.${crypto4.randomBytes(4).toString("hex")}.json`);
28887
29193
  try {
28888
29194
  const handle = await import_node_fs2.promises.open(tmp, "w");
28889
29195
  try {
@@ -28902,37 +29208,37 @@ async function atomicWriteJson(filePath, payload) {
28902
29208
  }
28903
29209
  }
28904
29210
  async function appendJsonl(filePath, record2) {
28905
- await import_node_fs2.promises.mkdir(path3.dirname(filePath), { recursive: true });
29211
+ await import_node_fs2.promises.mkdir(path4.dirname(filePath), { recursive: true });
28906
29212
  await import_node_fs2.promises.appendFile(filePath, JSON.stringify(record2) + "\n", { encoding: "utf8" });
28907
29213
  }
28908
29214
  function rmTree(target) {
28909
29215
  try {
28910
- for (const child of fs3.readdirSync(target)) {
28911
- const childPath = path3.join(target, child);
28912
- const stat = fs3.lstatSync(childPath);
29216
+ for (const child of fs4.readdirSync(target)) {
29217
+ const childPath = path4.join(target, child);
29218
+ const stat = fs4.lstatSync(childPath);
28913
29219
  if (stat.isDirectory()) {
28914
29220
  rmTree(childPath);
28915
29221
  } else {
28916
29222
  try {
28917
- fs3.unlinkSync(childPath);
29223
+ fs4.unlinkSync(childPath);
28918
29224
  } catch {
28919
29225
  }
28920
29226
  }
28921
29227
  }
28922
- fs3.rmdirSync(target);
29228
+ fs4.rmdirSync(target);
28923
29229
  } catch {
28924
29230
  }
28925
29231
  }
28926
- var crypto4, fs3, import_node_fs2, os, path3, SCHEMA_VERSION, DEFAULT_RETENTION_DAYS, CallLogger;
29232
+ var crypto4, fs4, import_node_fs2, os, path4, SCHEMA_VERSION, DEFAULT_RETENTION_DAYS, CallLogger;
28927
29233
  var init_call_log = __esm({
28928
29234
  "src/services/call-log.ts"() {
28929
29235
  "use strict";
28930
29236
  init_cjs_shims();
28931
29237
  crypto4 = __toESM(require("crypto"));
28932
- fs3 = __toESM(require("fs"));
29238
+ fs4 = __toESM(require("fs"));
28933
29239
  import_node_fs2 = require("fs");
28934
29240
  os = __toESM(require("os"));
28935
- path3 = __toESM(require("path"));
29241
+ path4 = __toESM(require("path"));
28936
29242
  init_logger();
28937
29243
  init_stream_handler();
28938
29244
  SCHEMA_VERSION = "1.0";
@@ -28944,9 +29250,9 @@ var init_call_log = __esm({
28944
29250
  this.root = null;
28945
29251
  return;
28946
29252
  }
28947
- const resolved = root.startsWith("~") ? path3.join(os.homedir(), root.slice(1)) : root;
29253
+ const resolved = root.startsWith("~") ? path4.join(os.homedir(), root.slice(1)) : root;
28948
29254
  try {
28949
- fs3.mkdirSync(resolved, { recursive: true });
29255
+ fs4.mkdirSync(resolved, { recursive: true });
28950
29256
  this.root = resolved;
28951
29257
  getLogger().info(`Call logs: ${resolved}`);
28952
29258
  } catch (err) {
@@ -28968,7 +29274,7 @@ var init_call_log = __esm({
28968
29274
  const month = String(dt.getUTCMonth() + 1).padStart(2, "0");
28969
29275
  const day = String(dt.getUTCDate()).padStart(2, "0");
28970
29276
  const safeId = sanitizeLogValue(callId, 64).replace(/\//g, "_") || "unknown";
28971
- return path3.join(this.root, "calls", year, month, day, safeId);
29277
+ return path4.join(this.root, "calls", year, month, day, safeId);
28972
29278
  }
28973
29279
  /** Write the initial `metadata.json` for a new call. */
28974
29280
  async logCallStart(callId, input = {}) {
@@ -28986,6 +29292,7 @@ var init_call_log = __esm({
28986
29292
  status: "in_progress",
28987
29293
  caller: redactPhone(input.caller ?? ""),
28988
29294
  callee: redactPhone(input.callee ?? ""),
29295
+ direction: input.direction || "inbound",
28989
29296
  telephony_provider: input.telephonyProvider ?? "",
28990
29297
  provider_mode: input.providerMode ?? "",
28991
29298
  agent: input.agent ?? {},
@@ -28995,7 +29302,7 @@ var init_call_log = __esm({
28995
29302
  error: null
28996
29303
  };
28997
29304
  try {
28998
- await atomicWriteJson(path3.join(dir, "metadata.json"), metadata);
29305
+ await atomicWriteJson(path4.join(dir, "metadata.json"), metadata);
28999
29306
  } catch (err) {
29000
29307
  getLogger().warn(`call_log write failed (${sanitizeLogValue(callId)}): ${sanitizeLogValue(String(err))}`);
29001
29308
  }
@@ -29014,7 +29321,7 @@ var init_call_log = __esm({
29014
29321
  ...turn
29015
29322
  };
29016
29323
  try {
29017
- await appendJsonl(path3.join(dir, "transcript.jsonl"), record2);
29324
+ await appendJsonl(path4.join(dir, "transcript.jsonl"), record2);
29018
29325
  } catch (err) {
29019
29326
  getLogger().warn(
29020
29327
  `call_log turn write failed (${sanitizeLogValue(callId)}): ${sanitizeLogValue(String(err))}`
@@ -29033,7 +29340,7 @@ var init_call_log = __esm({
29033
29340
  data: payload
29034
29341
  };
29035
29342
  try {
29036
- await appendJsonl(path3.join(dir, "events.jsonl"), record2);
29343
+ await appendJsonl(path4.join(dir, "events.jsonl"), record2);
29037
29344
  } catch (err) {
29038
29345
  getLogger().warn(
29039
29346
  `call_log event write failed (${sanitizeLogValue(callId)}): ${sanitizeLogValue(String(err))}`
@@ -29045,7 +29352,7 @@ var init_call_log = __esm({
29045
29352
  if (!this.enabled) return;
29046
29353
  const dir = this.callDir(callId);
29047
29354
  if (dir === null) return;
29048
- const metadataPath = path3.join(dir, "metadata.json");
29355
+ const metadataPath = path4.join(dir, "metadata.json");
29049
29356
  let existing = {};
29050
29357
  try {
29051
29358
  existing = JSON.parse(await import_node_fs2.promises.readFile(metadataPath, "utf8"));
@@ -29080,20 +29387,20 @@ var init_call_log = __esm({
29080
29387
  const days = retentionDays();
29081
29388
  if (days === 0) return;
29082
29389
  const cutoff = Date.now() / 1e3 - days * 86400;
29083
- const callsRoot = path3.join(this.root, "calls");
29084
- if (!fs3.existsSync(callsRoot)) return;
29390
+ const callsRoot = path4.join(this.root, "calls");
29391
+ if (!fs4.existsSync(callsRoot)) return;
29085
29392
  try {
29086
- for (const yearName of fs3.readdirSync(callsRoot)) {
29393
+ for (const yearName of fs4.readdirSync(callsRoot)) {
29087
29394
  if (!/^\d+$/.test(yearName)) continue;
29088
- const yearDir = path3.join(callsRoot, yearName);
29089
- if (!fs3.statSync(yearDir).isDirectory()) continue;
29090
- for (const monthName of fs3.readdirSync(yearDir)) {
29395
+ const yearDir = path4.join(callsRoot, yearName);
29396
+ if (!fs4.statSync(yearDir).isDirectory()) continue;
29397
+ for (const monthName of fs4.readdirSync(yearDir)) {
29091
29398
  if (!/^\d+$/.test(monthName)) continue;
29092
- const monthDir = path3.join(yearDir, monthName);
29093
- if (!fs3.statSync(monthDir).isDirectory()) continue;
29094
- for (const dayName of fs3.readdirSync(monthDir)) {
29399
+ const monthDir = path4.join(yearDir, monthName);
29400
+ if (!fs4.statSync(monthDir).isDirectory()) continue;
29401
+ for (const dayName of fs4.readdirSync(monthDir)) {
29095
29402
  if (!/^\d+$/.test(dayName)) continue;
29096
- const dayDir = path3.join(monthDir, dayName);
29403
+ const dayDir = path4.join(monthDir, dayName);
29097
29404
  const y = Number.parseInt(yearName, 10);
29098
29405
  const m = Number.parseInt(monthName, 10);
29099
29406
  const d = Number.parseInt(dayName, 10);
@@ -29103,12 +29410,12 @@ var init_call_log = __esm({
29103
29410
  }
29104
29411
  }
29105
29412
  try {
29106
- if (fs3.readdirSync(monthDir).length === 0) fs3.rmdirSync(monthDir);
29413
+ if (fs4.readdirSync(monthDir).length === 0) fs4.rmdirSync(monthDir);
29107
29414
  } catch {
29108
29415
  }
29109
29416
  }
29110
29417
  try {
29111
- if (fs3.readdirSync(yearDir).length === 0) fs3.rmdirSync(yearDir);
29418
+ if (fs4.readdirSync(yearDir).length === 0) fs4.rmdirSync(yearDir);
29112
29419
  } catch {
29113
29420
  }
29114
29421
  }
@@ -29298,7 +29605,7 @@ function isValidTelnyxTransferTarget(target) {
29298
29605
  }
29299
29606
  async function sleep(ms) {
29300
29607
  if (ms <= 0) return;
29301
- await new Promise((resolve) => setTimeout(resolve, ms));
29608
+ await new Promise((resolve2) => setTimeout(resolve2, ms));
29302
29609
  }
29303
29610
  var import_node_crypto3, import_express, import_http, import_ws5, TRANSFER_CALL_TOOL, END_CALL_TOOL, TwilioBridge, TELNYX_DTMF_ALLOWED, TELNYX_DTMF_DURATION_MS, TelnyxBridge, GRACEFUL_SHUTDOWN_TIMEOUT_MS, EmbeddedServer;
29304
29611
  var init_server = __esm({
@@ -30035,7 +30342,7 @@ var init_server = __esm({
30035
30342
  this.handleTwilioStream(ws, url2);
30036
30343
  }
30037
30344
  });
30038
- await new Promise((resolve) => {
30345
+ await new Promise((resolve2) => {
30039
30346
  const bindHost = process.env.PATTER_BIND_HOST ?? "127.0.0.1";
30040
30347
  this.server.listen(port, bindHost, () => {
30041
30348
  getLogger().info(`Server on port ${port}`);
@@ -30057,7 +30364,7 @@ var init_server = __esm({
30057
30364
  }
30058
30365
  console.log("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n");
30059
30366
  }
30060
- resolve();
30367
+ resolve2();
30061
30368
  });
30062
30369
  });
30063
30370
  }
@@ -30100,7 +30407,7 @@ var init_server = __esm({
30100
30407
  `Telnyx voicemail speak failed: ${speakResp.status} ${(await speakResp.text()).slice(0, 200)}`
30101
30408
  );
30102
30409
  }
30103
- await new Promise((resolve) => setTimeout(resolve, estimatedMs));
30410
+ await new Promise((resolve2) => setTimeout(resolve2, estimatedMs));
30104
30411
  await fetch(`https://api.telnyx.com/v2/calls/${encoded}/actions/hangup`, {
30105
30412
  method: "POST",
30106
30413
  headers,
@@ -30171,9 +30478,11 @@ var init_server = __esm({
30171
30478
  const active = callId ? store.getActive(callId) : void 0;
30172
30479
  const resolvedCaller = dataCaller || active?.caller || "";
30173
30480
  const resolvedCallee = dataCallee || active?.callee || "";
30481
+ const resolvedDirection = (typeof data.direction === "string" ? data.direction : "") || active?.direction || "inbound";
30174
30482
  void logger2.logCallStart(callId, {
30175
30483
  caller: resolvedCaller,
30176
30484
  callee: resolvedCallee,
30485
+ direction: resolvedDirection,
30177
30486
  telephonyProvider: bridge.telephonyProvider,
30178
30487
  providerMode: agent.provider ?? "",
30179
30488
  agent: agentSnapshot()
@@ -30331,8 +30640,8 @@ var init_server = __esm({
30331
30640
  */
30332
30641
  async stop() {
30333
30642
  if (!this.server) return;
30334
- const httpClosePromise = new Promise((resolve) => {
30335
- this.server.close(() => resolve());
30643
+ const httpClosePromise = new Promise((resolve2) => {
30644
+ this.server.close(() => resolve2());
30336
30645
  });
30337
30646
  const isTelnyx = this.config.telephonyProvider === "telnyx";
30338
30647
  for (const [ws, callId] of this.activeCallIds) {
@@ -30352,15 +30661,15 @@ var init_server = __esm({
30352
30661
  if (this.activeConnections.size > 0) {
30353
30662
  getLogger().info(`Waiting for ${this.activeConnections.size} active connection(s) to close...`);
30354
30663
  await Promise.race([
30355
- new Promise((resolve) => {
30664
+ new Promise((resolve2) => {
30356
30665
  const checkInterval = setInterval(() => {
30357
30666
  if (this.activeConnections.size === 0) {
30358
30667
  clearInterval(checkInterval);
30359
- resolve();
30668
+ resolve2();
30360
30669
  }
30361
30670
  }, 100);
30362
30671
  }),
30363
- new Promise((resolve) => setTimeout(resolve, GRACEFUL_SHUTDOWN_TIMEOUT_MS))
30672
+ new Promise((resolve2) => setTimeout(resolve2, GRACEFUL_SHUTDOWN_TIMEOUT_MS))
30364
30673
  ]);
30365
30674
  }
30366
30675
  if (this.activeConnections.size > 0) {
@@ -30453,8 +30762,8 @@ async function startTunnel(port, timeoutMs = 3e4) {
30453
30762
  instance = result;
30454
30763
  }
30455
30764
  const tunnelUrl = await Promise.race([
30456
- new Promise((resolve) => {
30457
- instance.on("url", (url2) => resolve(url2));
30765
+ new Promise((resolve2) => {
30766
+ instance.on("url", (url2) => resolve2(url2));
30458
30767
  }),
30459
30768
  new Promise(
30460
30769
  (_, reject) => setTimeout(() => reject(new Error(
@@ -30663,7 +30972,7 @@ var init_test_mode = __esm({
30663
30972
  input: process.stdin,
30664
30973
  output: process.stdout
30665
30974
  });
30666
- const askQuestion = (prompt) => new Promise((resolve) => rl.question(prompt, resolve));
30975
+ const askQuestion = (prompt) => new Promise((resolve2) => rl.question(prompt, resolve2));
30667
30976
  try {
30668
30977
  while (!ended) {
30669
30978
  let userInput;
@@ -30852,11 +31161,11 @@ var require_tracked_promise = __commonJS({
30852
31161
  value;
30853
31162
  constructor(executor) {
30854
31163
  this.state = "pending";
30855
- this.promise = new Promise((resolve, reject) => {
31164
+ this.promise = new Promise((resolve2, reject) => {
30856
31165
  executor((value) => {
30857
31166
  this.state = "fulfilled";
30858
31167
  this.value = value;
30859
- resolve(value);
31168
+ resolve2(value);
30860
31169
  }, (error2) => {
30861
31170
  this.state = "rejected";
30862
31171
  this.error = error2;
@@ -30961,7 +31270,7 @@ var require_runner = __commonJS({
30961
31270
  }
30962
31271
  };
30963
31272
  const runTask = (date4) => {
30964
- return new Promise(async (resolve) => {
31273
+ return new Promise(async (resolve2) => {
30965
31274
  const execution = {
30966
31275
  id: (0, create_id_1.createID)("exec"),
30967
31276
  reason: "scheduled"
@@ -30986,18 +31295,18 @@ var require_runner = __commonJS({
30986
31295
  execution.error = error2;
30987
31296
  this.onError(date4, error2, execution);
30988
31297
  }
30989
- resolve(true);
31298
+ resolve2(true);
30990
31299
  }, randomDelay);
30991
31300
  }
30992
31301
  });
30993
31302
  };
30994
31303
  const checkAndRun = (date4) => {
30995
- return new tracked_promise_1.TrackedPromise(async (resolve, reject) => {
31304
+ return new tracked_promise_1.TrackedPromise(async (resolve2, reject) => {
30996
31305
  try {
30997
31306
  if (this.timeMatcher.match(date4)) {
30998
31307
  await runTask(date4);
30999
31308
  }
31000
- resolve(true);
31309
+ resolve2(true);
31001
31310
  } catch (err) {
31002
31311
  reject(err);
31003
31312
  }
@@ -31672,14 +31981,14 @@ var require_inline_scheduled_task = __commonJS({
31672
31981
  this.emitter.emit("task:destroyed", this.createContext(/* @__PURE__ */ new Date()));
31673
31982
  }
31674
31983
  execute() {
31675
- return new Promise((resolve, reject) => {
31984
+ return new Promise((resolve2, reject) => {
31676
31985
  const onFail = (context) => {
31677
31986
  this.off("execution:finished", onFail);
31678
31987
  reject(context.execution?.error);
31679
31988
  };
31680
31989
  const onFinished = (context) => {
31681
31990
  this.off("execution:failed", onFail);
31682
- resolve(context.execution?.result);
31991
+ resolve2(context.execution?.result);
31683
31992
  };
31684
31993
  this.once("execution:finished", onFinished);
31685
31994
  this.once("execution:failed", onFail);
@@ -31873,9 +32182,9 @@ var require_background_scheduled_task = __commonJS({
31873
32182
  return null;
31874
32183
  }
31875
32184
  start() {
31876
- return new Promise((resolve, reject) => {
32185
+ return new Promise((resolve2, reject) => {
31877
32186
  if (this.forkProcess) {
31878
- return resolve(void 0);
32187
+ return resolve2(void 0);
31879
32188
  }
31880
32189
  const timeout = setTimeout(() => {
31881
32190
  reject(new Error("Start operation timed out"));
@@ -31914,7 +32223,7 @@ var require_background_scheduled_task = __commonJS({
31914
32223
  this.once("task:started", () => {
31915
32224
  this.stateMachine.changeState("idle");
31916
32225
  clearTimeout(timeout);
31917
- resolve(void 0);
32226
+ resolve2(void 0);
31918
32227
  });
31919
32228
  this.forkProcess.send({
31920
32229
  command: "task:start",
@@ -31928,9 +32237,9 @@ var require_background_scheduled_task = __commonJS({
31928
32237
  });
31929
32238
  }
31930
32239
  stop() {
31931
- return new Promise((resolve, reject) => {
32240
+ return new Promise((resolve2, reject) => {
31932
32241
  if (!this.forkProcess) {
31933
- return resolve(void 0);
32242
+ return resolve2(void 0);
31934
32243
  }
31935
32244
  const timeoutId = setTimeout(() => {
31936
32245
  clearTimeout(timeoutId);
@@ -31940,7 +32249,7 @@ var require_background_scheduled_task = __commonJS({
31940
32249
  clearTimeout(timeoutId);
31941
32250
  this.off("task:stopped", onStopped);
31942
32251
  this.forkProcess = void 0;
31943
- resolve(void 0);
32252
+ resolve2(void 0);
31944
32253
  };
31945
32254
  const onStopped = () => {
31946
32255
  cleanupAndResolve();
@@ -31955,9 +32264,9 @@ var require_background_scheduled_task = __commonJS({
31955
32264
  return this.stateMachine.state;
31956
32265
  }
31957
32266
  destroy() {
31958
- return new Promise((resolve, reject) => {
32267
+ return new Promise((resolve2, reject) => {
31959
32268
  if (!this.forkProcess) {
31960
- return resolve(void 0);
32269
+ return resolve2(void 0);
31961
32270
  }
31962
32271
  const timeoutId = setTimeout(() => {
31963
32272
  clearTimeout(timeoutId);
@@ -31966,7 +32275,7 @@ var require_background_scheduled_task = __commonJS({
31966
32275
  const onDestroy = () => {
31967
32276
  clearTimeout(timeoutId);
31968
32277
  this.off("task:destroyed", onDestroy);
31969
- resolve(void 0);
32278
+ resolve2(void 0);
31970
32279
  };
31971
32280
  this.once("task:destroyed", onDestroy);
31972
32281
  this.forkProcess.send({
@@ -31975,7 +32284,7 @@ var require_background_scheduled_task = __commonJS({
31975
32284
  });
31976
32285
  }
31977
32286
  execute() {
31978
- return new Promise((resolve, reject) => {
32287
+ return new Promise((resolve2, reject) => {
31979
32288
  if (!this.forkProcess) {
31980
32289
  return reject(new Error("Cannot execute background task because it hasn't been started yet. Please initialize the task using the start() method before attempting to execute it."));
31981
32290
  }
@@ -31990,7 +32299,7 @@ var require_background_scheduled_task = __commonJS({
31990
32299
  };
31991
32300
  const onFinished = (context) => {
31992
32301
  cleanupListeners();
31993
- resolve(context.execution?.result);
32302
+ resolve2(context.execution?.result);
31994
32303
  };
31995
32304
  const onFail = (context) => {
31996
32305
  cleanupListeners();
@@ -32131,6 +32440,8 @@ __export(index_exports, {
32131
32440
  CallMetricsAccumulator: () => CallMetricsAccumulator,
32132
32441
  CartesiaSTT: () => STT4,
32133
32442
  CartesiaTTS: () => TTS4,
32443
+ CartesiaTTSModel: () => CartesiaTTSModel,
32444
+ CartesiaTTSVoiceMode: () => CartesiaTTSVoiceMode,
32134
32445
  CerebrasLLM: () => LLM4,
32135
32446
  ChatContext: () => ChatContext,
32136
32447
  CloudflareTunnel: () => CloudflareTunnel,
@@ -32138,10 +32449,13 @@ __export(index_exports, {
32138
32449
  DEFAULT_PRICING: () => DEFAULT_PRICING,
32139
32450
  DTMF_EVENTS: () => DTMF_EVENTS,
32140
32451
  DeepFilterNetFilter: () => DeepFilterNetFilter,
32452
+ DeepgramModel: () => DeepgramModel,
32141
32453
  DeepgramSTT: () => STT,
32142
32454
  DefaultToolExecutor: () => DefaultToolExecutor,
32143
32455
  ElevenLabsConvAI: () => ConvAI,
32144
32456
  ElevenLabsConvAIAdapter: () => ElevenLabsConvAIAdapter,
32457
+ ElevenLabsModel: () => ElevenLabsModel,
32458
+ ElevenLabsOutputFormat: () => ElevenLabsOutputFormat,
32145
32459
  ElevenLabsRestTTS: () => ElevenLabsTTS,
32146
32460
  ElevenLabsTTS: () => TTS,
32147
32461
  ElevenLabsWebSocketTTS: () => TTS2,
@@ -32170,8 +32484,15 @@ __export(index_exports, {
32170
32484
  OpenAIRealtime2: () => Realtime2,
32171
32485
  OpenAIRealtime2Adapter: () => OpenAIRealtime2Adapter,
32172
32486
  OpenAIRealtimeAdapter: () => OpenAIRealtimeAdapter,
32487
+ OpenAIRealtimeAudioFormat: () => OpenAIRealtimeAudioFormat,
32488
+ OpenAIRealtimeModel: () => OpenAIRealtimeModel,
32489
+ OpenAIRealtimeVADType: () => OpenAIRealtimeVADType,
32173
32490
  OpenAITTS: () => TTS3,
32174
32491
  OpenAITranscribeSTT: () => STT3,
32492
+ OpenAITranscriptionModel: () => OpenAITranscriptionModel,
32493
+ OpenAIVoice: () => OpenAIVoice,
32494
+ PRICING_LAST_UPDATED: () => PRICING_LAST_UPDATED,
32495
+ PRICING_VERSION: () => PRICING_VERSION,
32175
32496
  PartialStreamError: () => PartialStreamError,
32176
32497
  Patter: () => Patter,
32177
32498
  PatterConnectionError: () => PatterConnectionError,
@@ -32179,9 +32500,12 @@ __export(index_exports, {
32179
32500
  PatterTool: () => PatterTool,
32180
32501
  PcmCarry: () => PcmCarry,
32181
32502
  PipelineHookExecutor: () => PipelineHookExecutor,
32503
+ PricingUnit: () => PricingUnit,
32182
32504
  ProvisionError: () => ProvisionError,
32183
32505
  RateLimitError: () => RateLimitError,
32184
32506
  RemoteMessageHandler: () => RemoteMessageHandler,
32507
+ RimeAudioFormat: () => RimeAudioFormat,
32508
+ RimeModel: () => RimeModel,
32185
32509
  RimeTTS: () => TTS5,
32186
32510
  SPAN_BARGEIN: () => SPAN_BARGEIN,
32187
32511
  SPAN_CALL: () => SPAN_CALL,
@@ -32301,7 +32625,7 @@ var Realtime = class {
32301
32625
  );
32302
32626
  }
32303
32627
  this.apiKey = key;
32304
- this.model = opts.model ?? "gpt-4o-mini-realtime-preview";
32628
+ this.model = opts.model ?? "gpt-realtime-mini";
32305
32629
  this.voice = opts.voice ?? "alloy";
32306
32630
  this.reasoningEffort = opts.reasoningEffort;
32307
32631
  this.inputAudioTranscriptionModel = opts.inputAudioTranscriptionModel;
@@ -32760,7 +33084,9 @@ function resolvePersistRoot(persist) {
32760
33084
  if (persist === false) return null;
32761
33085
  if (persist === true) return resolveLogRoot("auto");
32762
33086
  if (typeof persist === "string") return resolveLogRoot(persist);
32763
- return resolveLogRoot();
33087
+ const envRoot = resolveLogRoot();
33088
+ if (envRoot !== null) return envRoot;
33089
+ return resolveLogRoot("auto");
32764
33090
  }
32765
33091
  function closeParkedConnections(slot) {
32766
33092
  if (slot.stt) {
@@ -32776,6 +33102,11 @@ function closeParkedConnections(slot) {
32776
33102
  }
32777
33103
  }
32778
33104
  if (slot.openaiRealtime) {
33105
+ const wsAny = slot.openaiRealtime;
33106
+ if (wsAny._parkedKeepalive) {
33107
+ clearInterval(wsAny._parkedKeepalive);
33108
+ delete wsAny._parkedKeepalive;
33109
+ }
32779
33110
  try {
32780
33111
  slot.openaiRealtime.close();
32781
33112
  } catch {
@@ -33007,8 +33338,8 @@ var Patter = class {
33007
33338
  openaiKey: options.openaiKey,
33008
33339
  persistRoot: resolvePersistRoot(options.persist)
33009
33340
  };
33010
- this._tunnelReady = new Promise((resolve, reject) => {
33011
- this._tunnelReadyResolve = resolve;
33341
+ this._tunnelReady = new Promise((resolve2, reject) => {
33342
+ this._tunnelReadyResolve = resolve2;
33012
33343
  this._tunnelReadyReject = reject;
33013
33344
  });
33014
33345
  this._tunnelReady.catch(() => {
@@ -33016,8 +33347,8 @@ var Patter = class {
33016
33347
  if (normalizedWebhook) {
33017
33348
  this._tunnelReadyResolve(normalizedWebhook);
33018
33349
  }
33019
- this._ready = new Promise((resolve, reject) => {
33020
- this._readyResolve = resolve;
33350
+ this._ready = new Promise((resolve2, reject) => {
33351
+ this._readyResolve = resolve2;
33021
33352
  this._readyReject = reject;
33022
33353
  });
33023
33354
  this._ready.catch(() => {
@@ -33347,7 +33678,9 @@ var Patter = class {
33347
33678
  const tts = agent.tts;
33348
33679
  const sttOpen = typeof stt?.openParkedConnection === "function" ? stt.openParkedConnection.bind(stt) : null;
33349
33680
  const ttsOpen = typeof tts?.openParkedConnection === "function" ? tts.openParkedConnection.bind(tts) : null;
33350
- if (!sttOpen && !ttsOpen) return;
33681
+ const providerStr = agent.provider ?? "";
33682
+ const wantsRealtimePark = providerStr === "openai_realtime" || providerStr === "openai_realtime_2";
33683
+ if (!sttOpen && !ttsOpen && !wantsRealtimePark) return;
33351
33684
  const slot = {};
33352
33685
  this.prewarmedConnections.set(callId, slot);
33353
33686
  const startedAt = Date.now();
@@ -33392,6 +33725,43 @@ var Patter = class {
33392
33725
  }
33393
33726
  })());
33394
33727
  }
33728
+ if (wantsRealtimePark) {
33729
+ tasks.push((async () => {
33730
+ const { OpenAIRealtime2Adapter: OpenAIRealtime2Adapter2 } = await Promise.resolve().then(() => (init_openai_realtime_2(), openai_realtime_2_exports));
33731
+ const apiKey = process.env.OPENAI_API_KEY ?? "";
33732
+ if (!apiKey) {
33733
+ getLogger().debug(`Park OpenAI Realtime skipped for ${callId}: no OPENAI_API_KEY`);
33734
+ return;
33735
+ }
33736
+ try {
33737
+ const tmpAdapter = new OpenAIRealtime2Adapter2(
33738
+ apiKey,
33739
+ agent.model ?? "gpt-realtime-mini",
33740
+ agent.voice ?? "alloy",
33741
+ agent.systemPrompt ?? "",
33742
+ [],
33743
+ // audioFormat — the GA adapter always emits audio/pcm@24000
33744
+ // internally regardless of this value, but it's a required
33745
+ // positional param. Default to g711_ulaw (Twilio wire format).
33746
+ void 0
33747
+ );
33748
+ const ws = await tmpAdapter.openParkedConnection();
33749
+ if (this.prewarmedConnections.get(callId) !== slot) {
33750
+ try {
33751
+ ws.close();
33752
+ } catch {
33753
+ }
33754
+ return;
33755
+ }
33756
+ slot.openaiRealtime = ws;
33757
+ getLogger().info(
33758
+ `[PREWARM] callId=${callId} provider=openai_realtime ms=${Date.now() - startedAt}`
33759
+ );
33760
+ } catch (err) {
33761
+ getLogger().debug(`Park OpenAI Realtime failed for ${callId}: ${String(err)}`);
33762
+ }
33763
+ })());
33764
+ }
33395
33765
  const task = (async () => {
33396
33766
  await Promise.allSettled(tasks);
33397
33767
  })();
@@ -33469,7 +33839,7 @@ var Patter = class {
33469
33839
  * with a warn when the cap is reached (the call still proceeds —
33470
33840
  * StreamHandler falls back to live TTS).
33471
33841
  */
33472
- spawnPrewarmFirstMessage(agent, callId, ringTimeout) {
33842
+ spawnPrewarmFirstMessage(agent, callId, ringTimeout, carrier) {
33473
33843
  if (!agent.prewarmFirstMessage) return;
33474
33844
  const providerMode = agent.provider ?? "openai_realtime";
33475
33845
  if (providerMode !== "pipeline") {
@@ -33482,6 +33852,18 @@ var Patter = class {
33482
33852
  const tts = agent.tts;
33483
33853
  if (!firstMessage || !tts) return;
33484
33854
  if (typeof tts.synthesizeStream !== "function") return;
33855
+ if (carrier) {
33856
+ const carrierAware = tts;
33857
+ if (typeof carrierAware.setTelephonyCarrier === "function") {
33858
+ try {
33859
+ carrierAware.setTelephonyCarrier(carrier);
33860
+ } catch (err) {
33861
+ getLogger().debug(
33862
+ `Prewarm TTS setTelephonyCarrier failed for ${callId}: ${String(err)}`
33863
+ );
33864
+ }
33865
+ }
33866
+ }
33485
33867
  const inFlight = this.prewarmAudio.size + this.prewarmTasks.size;
33486
33868
  if (inFlight >= PREWARM_CACHE_MAX) {
33487
33869
  getLogger().warn(
@@ -33594,16 +33976,25 @@ var Patter = class {
33594
33976
  telnyxCallId = body.data?.call_control_id;
33595
33977
  } catch {
33596
33978
  }
33597
- if (this.embeddedServer && telnyxCallId) {
33598
- this.embeddedServer.metricsStore.recordCallInitiated({
33979
+ if (telnyxCallId) {
33980
+ const initiatedPayload = {
33599
33981
  call_id: telnyxCallId,
33600
33982
  caller: phoneNumber,
33601
33983
  callee: options.to,
33602
- direction: "outbound"
33603
- });
33984
+ direction: "outbound",
33985
+ status: "initiated"
33986
+ };
33987
+ if (this.embeddedServer) {
33988
+ this.embeddedServer.metricsStore.recordCallInitiated(initiatedPayload);
33989
+ }
33990
+ try {
33991
+ const { notifyDashboard: notifyDashboard2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
33992
+ notifyDashboard2(initiatedPayload);
33993
+ } catch {
33994
+ }
33604
33995
  }
33605
33996
  if (telnyxCallId) {
33606
- this.spawnPrewarmFirstMessage(options.agent, telnyxCallId, effectiveRingTimeout);
33997
+ this.spawnPrewarmFirstMessage(options.agent, telnyxCallId, effectiveRingTimeout, "telnyx");
33607
33998
  if (options.agent.prewarm !== false) {
33608
33999
  this.parkProviderConnections(options.agent, telnyxCallId);
33609
34000
  }
@@ -33656,21 +34047,30 @@ var Patter = class {
33656
34047
  twilioNotificationsPath = body.subresource_uris?.notifications;
33657
34048
  } catch {
33658
34049
  }
33659
- if (this.embeddedServer && twilioCallSid) {
33660
- this.embeddedServer.metricsStore.recordCallInitiated({
34050
+ if (twilioCallSid) {
34051
+ const initiatedPayload = {
33661
34052
  call_id: twilioCallSid,
33662
34053
  caller: phoneNumber,
33663
34054
  callee: options.to,
33664
- direction: "outbound"
33665
- });
33666
- if (twilioNotificationsPath) {
33667
- getLogger().info(
33668
- `Outbound call ${twilioCallSid} placed. Twilio notifications: https://api.twilio.com${twilioNotificationsPath} (check here if the call drops with no audio).`
33669
- );
34055
+ direction: "outbound",
34056
+ status: "initiated"
34057
+ };
34058
+ if (this.embeddedServer) {
34059
+ this.embeddedServer.metricsStore.recordCallInitiated(initiatedPayload);
34060
+ if (twilioNotificationsPath) {
34061
+ getLogger().info(
34062
+ `Outbound call ${twilioCallSid} placed. Twilio notifications: https://api.twilio.com${twilioNotificationsPath} (check here if the call drops with no audio).`
34063
+ );
34064
+ }
34065
+ }
34066
+ try {
34067
+ const { notifyDashboard: notifyDashboard2 } = await Promise.resolve().then(() => (init_persistence(), persistence_exports));
34068
+ notifyDashboard2(initiatedPayload);
34069
+ } catch {
33670
34070
  }
33671
34071
  }
33672
34072
  if (twilioCallSid) {
33673
- this.spawnPrewarmFirstMessage(options.agent, twilioCallSid, effectiveRingTimeout);
34073
+ this.spawnPrewarmFirstMessage(options.agent, twilioCallSid, effectiveRingTimeout, "twilio");
33674
34074
  if (options.agent.prewarm !== false) {
33675
34075
  this.parkProviderConnections(options.agent, twilioCallSid);
33676
34076
  }
@@ -33695,7 +34095,7 @@ var Patter = class {
33695
34095
  if (this.prewarmTasks.size > 0) {
33696
34096
  const drain = Promise.allSettled(Array.from(this.prewarmTasks));
33697
34097
  const timer = new Promise(
33698
- (resolve) => setTimeout(resolve, 1e3).unref?.()
34098
+ (resolve2) => setTimeout(resolve2, 1e3).unref?.()
33699
34099
  );
33700
34100
  await Promise.race([drain, timer]);
33701
34101
  }
@@ -33722,8 +34122,8 @@ var Patter = class {
33722
34122
  this.localConfig = { ...this.localConfig, webhookUrl: void 0 };
33723
34123
  this.tunnelOwnsWebhookUrl = false;
33724
34124
  }
33725
- this._tunnelReady = new Promise((resolve, reject) => {
33726
- this._tunnelReadyResolve = resolve;
34125
+ this._tunnelReady = new Promise((resolve2, reject) => {
34126
+ this._tunnelReadyResolve = resolve2;
33727
34127
  this._tunnelReadyReject = reject;
33728
34128
  });
33729
34129
  this._tunnelReady.catch(() => {
@@ -33731,8 +34131,8 @@ var Patter = class {
33731
34131
  if (this.localConfig.webhookUrl) {
33732
34132
  this._tunnelReadyResolve(this.localConfig.webhookUrl);
33733
34133
  }
33734
- this._ready = new Promise((resolve, reject) => {
33735
- this._readyResolve = resolve;
34134
+ this._ready = new Promise((resolve2, reject) => {
34135
+ this._readyResolve = resolve2;
33736
34136
  this._readyReject = reject;
33737
34137
  });
33738
34138
  this._ready.catch(() => {
@@ -34382,13 +34782,13 @@ var PatterTool = class _PatterTool {
34382
34782
  ...args.first_message !== void 0 ? { firstMessage: args.first_message } : {}
34383
34783
  });
34384
34784
  const callId = await this.acquireCallId(args.to, overrideAgent);
34385
- return new Promise((resolve, reject) => {
34785
+ return new Promise((resolve2, reject) => {
34386
34786
  const timer = setTimeout(() => {
34387
34787
  this.pending.delete(callId);
34388
34788
  reject(new Error(`PatterTool.execute: call ${callId} exceeded ${timeoutSec}s timeout`));
34389
34789
  }, timeoutSec * 1e3);
34390
34790
  this.pending.set(callId, {
34391
- resolve,
34791
+ resolve: resolve2,
34392
34792
  reject,
34393
34793
  timer,
34394
34794
  startedAt: Date.now() / 1e3
@@ -34406,8 +34806,8 @@ var PatterTool = class _PatterTool {
34406
34806
  await previous;
34407
34807
  let captureTimer = null;
34408
34808
  try {
34409
- const callIdPromise = new Promise((resolve, reject) => {
34410
- this.pendingDial = resolve;
34809
+ const callIdPromise = new Promise((resolve2, reject) => {
34810
+ this.pendingDial = resolve2;
34411
34811
  captureTimer = setTimeout(() => {
34412
34812
  this.pendingDial = null;
34413
34813
  reject(
@@ -34759,11 +35159,11 @@ var UltravoxRealtimeAdapter = class {
34759
35159
  const call = await resp.json();
34760
35160
  if (!call.joinUrl) throw new Error("Ultravox response missing joinUrl");
34761
35161
  this.ws = new import_ws6.default(call.joinUrl);
34762
- await new Promise((resolve, reject) => {
35162
+ await new Promise((resolve2, reject) => {
34763
35163
  const ws = this.ws;
34764
35164
  const onOpen = () => {
34765
35165
  ws.off("error", onError);
34766
- resolve();
35166
+ resolve2();
34767
35167
  };
34768
35168
  const onError = (err) => {
34769
35169
  ws.off("open", onOpen);
@@ -34995,6 +35395,601 @@ function scheduleInterval(intervalOrOpts, callback) {
34995
35395
  };
34996
35396
  }
34997
35397
 
35398
+ // src/index.ts
35399
+ init_openai_realtime();
35400
+
35401
+ // src/providers/elevenlabs-tts.ts
35402
+ init_cjs_shims();
35403
+ var ELEVENLABS_BASE_URL = "https://api.elevenlabs.io/v1";
35404
+ var ELEVENLABS_VOICE_ID_BY_NAME = {
35405
+ rachel: "21m00Tcm4TlvDq8ikWAM",
35406
+ drew: "29vD33N1CtxCmqQRPOHJ",
35407
+ clyde: "2EiwWnXFnvU5JabPnv8n",
35408
+ paul: "5Q0t7uMcjvnagumLfvZi",
35409
+ domi: "AZnzlk1XvdvUeBnXmlld",
35410
+ dave: "CYw3kZ02Hs0563khs1Fj",
35411
+ fin: "D38z5RcWu1voky8WS1ja",
35412
+ bella: "EXAVITQu4vr4xnSDxMaL",
35413
+ antoni: "ErXwobaYiN019PkySvjV",
35414
+ thomas: "GBv7mTt0atIp3Br8iCZE",
35415
+ charlie: "IKne3meq5aSn9XLyUdCD",
35416
+ george: "JBFqnCBsd6RMkjVDRZzb",
35417
+ emily: "LcfcDJNUP1GQjkzn1xUU",
35418
+ elli: "MF3mGyEYCl7XYWbV9V6O",
35419
+ callum: "N2lVS1w4EtoT3dr4eOWO",
35420
+ patrick: "ODq5zmih8GrVes37Dizd",
35421
+ harry: "SOYHLrjzK2X1ezoPC6cr",
35422
+ liam: "TX3LPaxmHKxFdv7VOQHJ",
35423
+ dorothy: "ThT5KcBeYPX3keUQqHPh",
35424
+ josh: "TxGEqnHWrfWFTfGW9XjX",
35425
+ arnold: "VR6AewLTigWG4xSOukaG",
35426
+ charlotte: "XB0fDUnXU5powFXDhCwa",
35427
+ matilda: "XrExE9yKIg1WjnnlVkGX",
35428
+ matthew: "Yko7PKHZNXotIFUBG7I9",
35429
+ james: "ZQe5CZNOzWyzPSCn5a3c",
35430
+ joseph: "Zlb1dXrM653N07WRdFW3",
35431
+ jeremy: "bVMeCyTHy58xNoL34h3p",
35432
+ michael: "flq6f7yk4E4fJM5XTYuZ",
35433
+ ethan: "g5CIjZEefAph4nQFvHAz",
35434
+ gigi: "jBpfuIE2acCO8z3wKNLl",
35435
+ freya: "jsCqWAovK2LkecY7zXl4",
35436
+ brian: "nPczCjzI2devNBz1zQrb",
35437
+ grace: "oWAxZDx7w5VEj9dCyTzz",
35438
+ daniel: "onwK4e9ZLuTAKqWW03F9",
35439
+ lily: "pFZP5JQG7iQjIQuC4Bku",
35440
+ serena: "pMsXgVXv3BLzUgSXRplE",
35441
+ adam: "pNInz6obpgDQGcFmaJgB",
35442
+ nicole: "piTKgcLEGmPE4e6mEKli",
35443
+ bill: "pqHfZKP75CvOlQylNhV4",
35444
+ jessie: "t0jbNlBVZ17f02VDIeMI",
35445
+ ryan: "wViXBPUzp2ZZixB1xQuM",
35446
+ sam: "yoZ06aMxZJJ28mfd3POQ",
35447
+ glinda: "z9fAnlkpzviPz146aGWa",
35448
+ giovanni: "zcAOhNBS3c14rBihAFp1",
35449
+ mimi: "zrHiDhphv9ZnVXBqCLjz",
35450
+ sarah: "EXAVITQu4vr4xnSDxMaL",
35451
+ alloy: "EXAVITQu4vr4xnSDxMaL"
35452
+ };
35453
+ var VOICE_ID_PATTERN = /^[A-Za-z0-9]{20}$/;
35454
+ function resolveVoiceId(voice) {
35455
+ if (!voice) return voice;
35456
+ if (VOICE_ID_PATTERN.test(voice)) return voice;
35457
+ return ELEVENLABS_VOICE_ID_BY_NAME[voice.toLowerCase()] ?? voice;
35458
+ }
35459
+ var ElevenLabsModel = {
35460
+ V3: "eleven_v3",
35461
+ FLASH_V2_5: "eleven_flash_v2_5",
35462
+ TURBO_V2_5: "eleven_turbo_v2_5",
35463
+ MULTILINGUAL_V2: "eleven_multilingual_v2",
35464
+ MONOLINGUAL_V1: "eleven_monolingual_v1"
35465
+ };
35466
+ var ElevenLabsOutputFormat = {
35467
+ MP3_22050_32: "mp3_22050_32",
35468
+ MP3_44100_32: "mp3_44100_32",
35469
+ MP3_44100_64: "mp3_44100_64",
35470
+ MP3_44100_96: "mp3_44100_96",
35471
+ MP3_44100_128: "mp3_44100_128",
35472
+ MP3_44100_192: "mp3_44100_192",
35473
+ PCM_8000: "pcm_8000",
35474
+ PCM_16000: "pcm_16000",
35475
+ PCM_22050: "pcm_22050",
35476
+ PCM_24000: "pcm_24000",
35477
+ PCM_44100: "pcm_44100",
35478
+ ULAW_8000: "ulaw_8000"
35479
+ };
35480
+ var ElevenLabsTTS = class _ElevenLabsTTS {
35481
+ // Stable pricing/dashboard key — read by stream-handler / metrics via
35482
+ // ``(agent.tts.constructor as any).providerKey``. Without this the cost
35483
+ // calculator falls back to ``constructor.name`` ("ElevenLabsTTS") which
35484
+ // does NOT match the pricing table key "elevenlabs", silently zeroing
35485
+ // TTS cost for callers that construct the raw REST class directly
35486
+ // (exposed at top level as ``ElevenLabsRestTTS``).
35487
+ static providerKey = "elevenlabs";
35488
+ apiKey;
35489
+ voiceId;
35490
+ modelId;
35491
+ _outputFormat;
35492
+ _outputFormatExplicit;
35493
+ voiceSettings;
35494
+ languageCode;
35495
+ chunkSize;
35496
+ /**
35497
+ * Public view of the (possibly auto-flipped) wire format. Read by the
35498
+ * stream-handler to decide whether to skip the client-side resample +
35499
+ * mulaw encode when the bytes are already in the carrier's wire codec.
35500
+ */
35501
+ get outputFormat() {
35502
+ return this._outputFormat;
35503
+ }
35504
+ constructor(apiKey, voiceIdOrOptions = "21m00Tcm4TlvDq8ikWAM", modelId = ElevenLabsModel.FLASH_V2_5, outputFormat = ElevenLabsOutputFormat.PCM_16000) {
35505
+ this.apiKey = apiKey;
35506
+ if (typeof voiceIdOrOptions === "object") {
35507
+ const o = voiceIdOrOptions;
35508
+ this.voiceId = resolveVoiceId(o.voiceId ?? "21m00Tcm4TlvDq8ikWAM");
35509
+ this.modelId = o.modelId ?? ElevenLabsModel.FLASH_V2_5;
35510
+ this._outputFormatExplicit = o.outputFormat !== void 0;
35511
+ this._outputFormat = o.outputFormat ?? ElevenLabsOutputFormat.PCM_16000;
35512
+ this.voiceSettings = o.voiceSettings;
35513
+ this.languageCode = o.languageCode;
35514
+ this.chunkSize = o.chunkSize ?? 4096;
35515
+ } else {
35516
+ this.voiceId = resolveVoiceId(voiceIdOrOptions);
35517
+ this.modelId = modelId;
35518
+ this._outputFormatExplicit = outputFormat !== ElevenLabsOutputFormat.PCM_16000;
35519
+ this._outputFormat = outputFormat;
35520
+ this.voiceSettings = void 0;
35521
+ this.languageCode = void 0;
35522
+ this.chunkSize = 4096;
35523
+ }
35524
+ }
35525
+ /**
35526
+ * Hook called by ``StreamHandler.initPipeline`` to advise the carrier
35527
+ * wire format. When the user did NOT pass an explicit ``outputFormat``,
35528
+ * auto-flip to the carrier's native codec so the audio bytes ElevenLabs
35529
+ * returns are already in Twilio/Telnyx wire format — eliminating the
35530
+ * client-side 16 kHz → 8 kHz resample and PCM → μ-law encode. The
35531
+ * resample/encode chain was a source of audible artifacts on the
35532
+ * prewarmed firstMessage (see 0.6.2 acceptance notes — burst delivery
35533
+ * of resampled audio crackled on the carrier-side jitter buffer).
35534
+ *
35535
+ * No-op when the caller passed an explicit ``outputFormat`` (incl. via
35536
+ * the ``forTwilio`` / ``forTelnyx`` factories) — user wins.
35537
+ *
35538
+ * Parity with {@link ElevenLabsWebSocketTTS.setTelephonyCarrier}.
35539
+ */
35540
+ setTelephonyCarrier(carrier) {
35541
+ if (this._outputFormatExplicit) return;
35542
+ if (carrier === "twilio") {
35543
+ this._outputFormat = ElevenLabsOutputFormat.ULAW_8000;
35544
+ } else if (carrier === "telnyx") {
35545
+ this._outputFormat = ElevenLabsOutputFormat.PCM_16000;
35546
+ }
35547
+ }
35548
+ /**
35549
+ * Construct an instance pre-configured for Twilio Media Streams.
35550
+ *
35551
+ * Sets `outputFormat='ulaw_8000'` so ElevenLabs emits μ-law @ 8 kHz
35552
+ * directly — the exact wire format Twilio's media stream uses — letting
35553
+ * the SDK skip the 16 kHz→8 kHz resample and PCM→μ-law conversion in
35554
+ * `TwilioAudioSender`. Saves ~30–80 ms first-byte and per-frame CPU,
35555
+ * and removes a potential aliasing source.
35556
+ *
35557
+ * `voiceSettings` defaults to a low-bandwidth-friendly profile
35558
+ * (speaker boost off, modest stability) which sounds cleaner at 8 kHz
35559
+ * μ-law than the studio default. Pass an explicit object to override.
35560
+ */
35561
+ static forTwilio(apiKey, options = {}) {
35562
+ const voiceSettings = options.voiceSettings ?? {
35563
+ // Speaker boost adds high-frequency emphasis that aliases ugly over an
35564
+ // 8 kHz μ-law line. Slightly higher stability tames the excursions
35565
+ // that compander quantization noise can amplify.
35566
+ stability: 0.6,
35567
+ similarity_boost: 0.75,
35568
+ use_speaker_boost: false
35569
+ };
35570
+ return new _ElevenLabsTTS(apiKey, {
35571
+ ...options,
35572
+ voiceSettings,
35573
+ outputFormat: ElevenLabsOutputFormat.ULAW_8000
35574
+ });
35575
+ }
35576
+ /**
35577
+ * Construct an instance pre-configured for Telnyx bidirectional media.
35578
+ *
35579
+ * Telnyx's default media-streaming codec is L16 PCM @ 16 kHz, which
35580
+ * matches our default Telnyx handler. We pick `pcm_16000` so the audio
35581
+ * flows end-to-end with zero resampling or transcoding.
35582
+ *
35583
+ * Trade-off: if your Telnyx profile is pinned to PCMU/8000 (μ-law),
35584
+ * construct `ElevenLabsTTS` directly with `outputFormat: 'ulaw_8000'`
35585
+ * — Telnyx supports that natively too.
35586
+ */
35587
+ static forTelnyx(apiKey, options = {}) {
35588
+ return new _ElevenLabsTTS(apiKey, {
35589
+ ...options,
35590
+ outputFormat: ElevenLabsOutputFormat.PCM_16000
35591
+ });
35592
+ }
35593
+ /**
35594
+ * Synthesise text to speech and return the full audio as a single Buffer.
35595
+ *
35596
+ * For large chunks (or when latency matters) call `synthesizeStream` instead.
35597
+ */
35598
+ async synthesize(text) {
35599
+ const chunks = [];
35600
+ for await (const chunk of this.synthesizeStream(text)) {
35601
+ chunks.push(chunk);
35602
+ }
35603
+ return Buffer.concat(chunks);
35604
+ }
35605
+ /**
35606
+ * Synthesise text and yield audio chunks as they arrive (streaming).
35607
+ *
35608
+ * The yielded buffers are raw PCM at 16 kHz (or whatever `outputFormat` is
35609
+ * configured to). `chunkSize` controls the maximum yield size — 512 is a
35610
+ * good choice for low-latency telephony.
35611
+ */
35612
+ async *synthesizeStream(text) {
35613
+ const url2 = `${ELEVENLABS_BASE_URL}/text-to-speech/${encodeURIComponent(this.voiceId)}/stream?output_format=${encodeURIComponent(this._outputFormat)}`;
35614
+ const body = {
35615
+ text,
35616
+ model_id: this.modelId
35617
+ };
35618
+ if (this.voiceSettings) body["voice_settings"] = this.voiceSettings;
35619
+ if (this.languageCode) body["language_code"] = this.languageCode;
35620
+ const response = await fetch(url2, {
35621
+ method: "POST",
35622
+ headers: {
35623
+ "xi-api-key": this.apiKey,
35624
+ "Content-Type": "application/json"
35625
+ },
35626
+ body: JSON.stringify(body),
35627
+ signal: AbortSignal.timeout(3e4)
35628
+ });
35629
+ if (!response.ok) {
35630
+ const errBody = await response.text();
35631
+ throw new Error(`ElevenLabs TTS error ${response.status}: ${errBody}`);
35632
+ }
35633
+ if (!response.body) {
35634
+ throw new Error("ElevenLabs TTS: no response body");
35635
+ }
35636
+ const reader = response.body.getReader();
35637
+ try {
35638
+ while (true) {
35639
+ const { done, value } = await reader.read();
35640
+ if (done) break;
35641
+ if (!value || value.length === 0) continue;
35642
+ const buf = Buffer.from(value);
35643
+ for (let offset = 0; offset < buf.length; offset += this.chunkSize) {
35644
+ yield buf.subarray(offset, Math.min(offset + this.chunkSize, buf.length));
35645
+ }
35646
+ }
35647
+ } finally {
35648
+ if (typeof reader.cancel === "function") await reader.cancel().catch(() => {
35649
+ });
35650
+ reader.releaseLock();
35651
+ }
35652
+ }
35653
+ };
35654
+
35655
+ // src/index.ts
35656
+ init_deepgram_stt();
35657
+
35658
+ // src/providers/cartesia-tts.ts
35659
+ init_cjs_shims();
35660
+ init_logger();
35661
+ var CARTESIA_BASE_URL = "https://api.cartesia.ai";
35662
+ var CARTESIA_API_VERSION = "2025-04-16";
35663
+ var CARTESIA_DEFAULT_VOICE_ID = "f786b574-daa5-4673-aa0c-cbe3e8534c02";
35664
+ var CartesiaTTSModel = {
35665
+ SONIC_3: "sonic-3",
35666
+ SONIC_2: "sonic-2",
35667
+ SONIC: "sonic"
35668
+ };
35669
+ var CartesiaTTSContainer = {
35670
+ RAW: "raw",
35671
+ WAV: "wav",
35672
+ MP3: "mp3"
35673
+ };
35674
+ var CartesiaTTSEncoding = {
35675
+ PCM_S16LE: "pcm_s16le",
35676
+ PCM_F32LE: "pcm_f32le",
35677
+ PCM_MULAW: "pcm_mulaw",
35678
+ PCM_ALAW: "pcm_alaw"
35679
+ };
35680
+ var CartesiaTTSSampleRate = {
35681
+ HZ_8000: 8e3,
35682
+ HZ_16000: 16e3,
35683
+ HZ_22050: 22050,
35684
+ HZ_24000: 24e3,
35685
+ HZ_44100: 44100
35686
+ };
35687
+ var CartesiaTTSVoiceMode = {
35688
+ ID: "id",
35689
+ EMBEDDING: "embedding"
35690
+ };
35691
+ var CartesiaTTS = class _CartesiaTTS {
35692
+ /** Stable pricing/dashboard key — read by stream-handler/metrics. */
35693
+ static providerKey = "cartesia_tts";
35694
+ apiKey;
35695
+ model;
35696
+ voice;
35697
+ language;
35698
+ sampleRate;
35699
+ speed;
35700
+ emotion;
35701
+ volume;
35702
+ baseUrl;
35703
+ apiVersion;
35704
+ constructor(apiKey, opts = {}) {
35705
+ this.apiKey = apiKey;
35706
+ this.model = opts.model ?? CartesiaTTSModel.SONIC_3;
35707
+ this.voice = opts.voice ?? CARTESIA_DEFAULT_VOICE_ID;
35708
+ this.language = opts.language ?? "en";
35709
+ this.sampleRate = opts.sampleRate ?? CartesiaTTSSampleRate.HZ_16000;
35710
+ this.speed = opts.speed;
35711
+ this.emotion = typeof opts.emotion === "string" ? [opts.emotion] : opts.emotion;
35712
+ this.volume = opts.volume;
35713
+ this.baseUrl = opts.baseUrl ?? CARTESIA_BASE_URL;
35714
+ this.apiVersion = opts.apiVersion ?? CARTESIA_API_VERSION;
35715
+ }
35716
+ /**
35717
+ * Construct an instance pre-configured for Twilio Media Streams.
35718
+ *
35719
+ * Sets `sampleRate=8000` so Cartesia emits PCM_S16LE @ 8 kHz directly.
35720
+ * Twilio's media stream uses μ-law @ 8 kHz so the SDK still does the
35721
+ * PCM → μ-law transcode client-side, but the 16 kHz → 8 kHz resample
35722
+ * step is skipped. Saves ~10–30 ms first-byte plus per-frame CPU and
35723
+ * removes a potential aliasing source.
35724
+ */
35725
+ static forTwilio(apiKey, options = {}) {
35726
+ return new _CartesiaTTS(apiKey, {
35727
+ ...options,
35728
+ sampleRate: CartesiaTTSSampleRate.HZ_8000
35729
+ });
35730
+ }
35731
+ /**
35732
+ * Construct an instance pre-configured for Telnyx bidirectional media.
35733
+ *
35734
+ * Sets `sampleRate=16000` to match Telnyx's L16/16000 default codec —
35735
+ * audio flows end-to-end with zero resampling or transcoding. Same as
35736
+ * the bare-constructor default; exists for API symmetry with
35737
+ * {@link CartesiaTTS.forTwilio}.
35738
+ */
35739
+ static forTelnyx(apiKey, options = {}) {
35740
+ return new _CartesiaTTS(apiKey, {
35741
+ ...options,
35742
+ sampleRate: CartesiaTTSSampleRate.HZ_16000
35743
+ });
35744
+ }
35745
+ /** Build the JSON payload for the Cartesia bytes endpoint. */
35746
+ buildPayload(text) {
35747
+ const payload = {
35748
+ model_id: this.model,
35749
+ voice: { mode: CartesiaTTSVoiceMode.ID, id: this.voice },
35750
+ transcript: text,
35751
+ output_format: {
35752
+ container: CartesiaTTSContainer.RAW,
35753
+ encoding: CartesiaTTSEncoding.PCM_S16LE,
35754
+ sample_rate: this.sampleRate
35755
+ },
35756
+ language: this.language
35757
+ };
35758
+ const generationConfig = {};
35759
+ if (this.speed !== void 0) generationConfig.speed = this.speed;
35760
+ if (this.emotion && this.emotion.length > 0)
35761
+ generationConfig.emotion = this.emotion[0];
35762
+ if (this.volume !== void 0) generationConfig.volume = this.volume;
35763
+ if (Object.keys(generationConfig).length > 0) {
35764
+ payload.generation_config = generationConfig;
35765
+ }
35766
+ return payload;
35767
+ }
35768
+ /**
35769
+ * Pre-call HTTP warmup for the Cartesia `/tts/bytes` endpoint.
35770
+ *
35771
+ * Issues a lightweight `GET <baseUrl>/voices` so DNS, TLS, and HTTP/2
35772
+ * are already up by the time the first `synthesizeStream()` POST
35773
+ * lands. Best-effort: 5 s timeout, all exceptions swallowed at
35774
+ * debug level.
35775
+ *
35776
+ * Billing safety: `GET /voices` is a free metadata read on
35777
+ * Cartesia's REST surface (per https://docs.cartesia.ai). It does
35778
+ * not consume synthesis credits. The actual synthesis is billed
35779
+ * only when `POST /tts/bytes` runs with a non-empty `transcript`.
35780
+ *
35781
+ * Note: Cartesia TTS uses the HTTP path (vs the WebSocket variant
35782
+ * Cartesia also exposes) — connection warmup is therefore HTTP-GET
35783
+ * based, not WebSocket pre-handshake. The latency win is smaller
35784
+ * (~50-150 ms vs the ~200-500 ms of a WS prewarm) but still real.
35785
+ */
35786
+ async warmup() {
35787
+ try {
35788
+ await fetch(`${this.baseUrl}/voices`, {
35789
+ method: "GET",
35790
+ headers: {
35791
+ "X-API-Key": this.apiKey,
35792
+ "Cartesia-Version": this.apiVersion
35793
+ },
35794
+ signal: AbortSignal.timeout(5e3)
35795
+ });
35796
+ } catch (err) {
35797
+ getLogger().debug(`Cartesia TTS warmup failed (best-effort): ${String(err)}`);
35798
+ }
35799
+ }
35800
+ /** Synthesize text and return the concatenated audio buffer. */
35801
+ async synthesize(text) {
35802
+ const chunks = [];
35803
+ for await (const chunk of this.synthesizeStream(text)) {
35804
+ chunks.push(chunk);
35805
+ }
35806
+ return Buffer.concat(chunks);
35807
+ }
35808
+ /**
35809
+ * Synthesize text and yield raw PCM_S16LE chunks at the configured
35810
+ * `sampleRate` as they arrive from Cartesia.
35811
+ */
35812
+ async *synthesizeStream(text) {
35813
+ const response = await fetch(`${this.baseUrl}/tts/bytes`, {
35814
+ method: "POST",
35815
+ headers: {
35816
+ "X-API-Key": this.apiKey,
35817
+ "Cartesia-Version": this.apiVersion,
35818
+ "Content-Type": "application/json"
35819
+ },
35820
+ body: JSON.stringify(this.buildPayload(text)),
35821
+ signal: AbortSignal.timeout(3e4)
35822
+ });
35823
+ if (!response.ok) {
35824
+ const body = await response.text();
35825
+ throw new Error(`Cartesia TTS error ${response.status}: ${body}`);
35826
+ }
35827
+ if (!response.body) {
35828
+ throw new Error("Cartesia TTS: no response body");
35829
+ }
35830
+ const reader = response.body.getReader();
35831
+ try {
35832
+ while (true) {
35833
+ const { done, value } = await reader.read();
35834
+ if (done) break;
35835
+ if (value && value.length > 0) {
35836
+ yield Buffer.from(value);
35837
+ }
35838
+ }
35839
+ } finally {
35840
+ if (typeof reader.cancel === "function")
35841
+ await reader.cancel().catch(() => {
35842
+ });
35843
+ reader.releaseLock();
35844
+ }
35845
+ }
35846
+ };
35847
+
35848
+ // src/providers/rime-tts.ts
35849
+ init_cjs_shims();
35850
+ var RIME_BASE_URL = "https://users.rime.ai/v1/rime-tts";
35851
+ var RimeModel = {
35852
+ ARCANA: "arcana",
35853
+ MIST: "mist",
35854
+ MIST_V2: "mistv2"
35855
+ };
35856
+ var RimeAudioFormat = {
35857
+ PCM: "audio/pcm",
35858
+ MP3: "audio/mp3",
35859
+ WAV: "audio/wav",
35860
+ MULAW: "audio/mulaw"
35861
+ };
35862
+ var ARCANA_MODEL_TIMEOUT_MS = 60 * 4 * 1e3;
35863
+ var MIST_MODEL_TIMEOUT_MS = 30 * 1e3;
35864
+ function isMistModel(model) {
35865
+ return model.includes(RimeModel.MIST);
35866
+ }
35867
+ function timeoutForModel(model) {
35868
+ if (model === RimeModel.ARCANA) return ARCANA_MODEL_TIMEOUT_MS;
35869
+ return MIST_MODEL_TIMEOUT_MS;
35870
+ }
35871
+ var RimeTTS = class {
35872
+ /** Stable pricing/dashboard key — read by stream-handler/metrics. */
35873
+ static providerKey = "rime";
35874
+ apiKey;
35875
+ model;
35876
+ speaker;
35877
+ lang;
35878
+ sampleRate;
35879
+ repetitionPenalty;
35880
+ temperature;
35881
+ topP;
35882
+ maxTokens;
35883
+ speedAlpha;
35884
+ reduceLatency;
35885
+ pauseBetweenBrackets;
35886
+ phonemizeBetweenBrackets;
35887
+ baseUrl;
35888
+ totalTimeoutMs;
35889
+ constructor(apiKey, opts = {}) {
35890
+ this.apiKey = apiKey;
35891
+ this.model = opts.model ?? RimeModel.ARCANA;
35892
+ const defaultSpeaker = isMistModel(this.model) ? "cove" : "astra";
35893
+ this.speaker = opts.speaker ?? defaultSpeaker;
35894
+ this.lang = opts.lang ?? "eng";
35895
+ this.sampleRate = opts.sampleRate ?? 16e3;
35896
+ this.repetitionPenalty = opts.repetitionPenalty;
35897
+ this.temperature = opts.temperature;
35898
+ this.topP = opts.topP;
35899
+ this.maxTokens = opts.maxTokens;
35900
+ this.speedAlpha = opts.speedAlpha;
35901
+ this.reduceLatency = opts.reduceLatency;
35902
+ this.pauseBetweenBrackets = opts.pauseBetweenBrackets;
35903
+ this.phonemizeBetweenBrackets = opts.phonemizeBetweenBrackets;
35904
+ this.baseUrl = opts.baseUrl ?? RIME_BASE_URL;
35905
+ this.totalTimeoutMs = timeoutForModel(this.model);
35906
+ }
35907
+ buildPayload(text) {
35908
+ const payload = {
35909
+ speaker: this.speaker,
35910
+ text,
35911
+ modelId: this.model
35912
+ };
35913
+ if (this.model === RimeModel.ARCANA) {
35914
+ if (this.repetitionPenalty !== void 0)
35915
+ payload.repetition_penalty = this.repetitionPenalty;
35916
+ if (this.temperature !== void 0) payload.temperature = this.temperature;
35917
+ if (this.topP !== void 0) payload.top_p = this.topP;
35918
+ if (this.maxTokens !== void 0) payload.max_tokens = this.maxTokens;
35919
+ payload.lang = this.lang;
35920
+ payload.samplingRate = this.sampleRate;
35921
+ } else if (isMistModel(this.model)) {
35922
+ payload.lang = this.lang;
35923
+ payload.samplingRate = this.sampleRate;
35924
+ if (this.speedAlpha !== void 0) payload.speedAlpha = this.speedAlpha;
35925
+ if (this.model === RimeModel.MIST_V2 && this.reduceLatency !== void 0) {
35926
+ payload.reduceLatency = this.reduceLatency;
35927
+ }
35928
+ if (this.pauseBetweenBrackets !== void 0) {
35929
+ payload.pauseBetweenBrackets = this.pauseBetweenBrackets;
35930
+ }
35931
+ if (this.phonemizeBetweenBrackets !== void 0) {
35932
+ payload.phonemizeBetweenBrackets = this.phonemizeBetweenBrackets;
35933
+ }
35934
+ }
35935
+ return payload;
35936
+ }
35937
+ /** Synthesize text and return the concatenated audio buffer. */
35938
+ async synthesize(text) {
35939
+ const chunks = [];
35940
+ for await (const chunk of this.synthesizeStream(text)) {
35941
+ chunks.push(chunk);
35942
+ }
35943
+ return Buffer.concat(chunks);
35944
+ }
35945
+ /**
35946
+ * Synthesize text and yield raw PCM_S16LE chunks at the configured
35947
+ * `sampleRate` as they stream in.
35948
+ */
35949
+ async *synthesizeStream(text) {
35950
+ const response = await fetch(this.baseUrl, {
35951
+ method: "POST",
35952
+ headers: {
35953
+ accept: RimeAudioFormat.PCM,
35954
+ Authorization: `Bearer ${this.apiKey}`,
35955
+ "content-type": "application/json"
35956
+ },
35957
+ body: JSON.stringify(this.buildPayload(text)),
35958
+ signal: AbortSignal.timeout(this.totalTimeoutMs)
35959
+ });
35960
+ if (!response.ok) {
35961
+ const body = await response.text();
35962
+ throw new Error(`Rime TTS error ${response.status}: ${body}`);
35963
+ }
35964
+ const contentType = response.headers.get("content-type") ?? "";
35965
+ if (!contentType.startsWith("audio")) {
35966
+ const body = await response.text();
35967
+ throw new Error(`Rime returned non-audio response: ${body.slice(0, 500)}`);
35968
+ }
35969
+ if (!response.body) {
35970
+ throw new Error("Rime TTS: no response body");
35971
+ }
35972
+ const reader = response.body.getReader();
35973
+ try {
35974
+ while (true) {
35975
+ const { done, value } = await reader.read();
35976
+ if (done) break;
35977
+ if (value && value.length > 0) {
35978
+ yield Buffer.from(value);
35979
+ }
35980
+ }
35981
+ } finally {
35982
+ if (typeof reader.cancel === "function")
35983
+ await reader.cancel().catch(() => {
35984
+ });
35985
+ reader.releaseLock();
35986
+ }
35987
+ }
35988
+ };
35989
+
35990
+ // src/index.ts
35991
+ init_pricing();
35992
+
34998
35993
  // src/stt/deepgram.ts
34999
35994
  init_cjs_shims();
35000
35995
  init_deepgram_stt();
@@ -35325,14 +36320,14 @@ var CartesiaSTT = class {
35325
36320
  const ws = new import_ws7.default(url2, {
35326
36321
  headers: { "User-Agent": USER_AGENT }
35327
36322
  });
35328
- await new Promise((resolve, reject) => {
36323
+ await new Promise((resolve2, reject) => {
35329
36324
  const timer = setTimeout(
35330
36325
  () => reject(new Error("Cartesia STT park connect timeout")),
35331
36326
  CONNECT_TIMEOUT_MS
35332
36327
  );
35333
36328
  ws.once("open", () => {
35334
36329
  clearTimeout(timer);
35335
- resolve();
36330
+ resolve2();
35336
36331
  });
35337
36332
  ws.once("error", (err) => {
35338
36333
  clearTimeout(timer);
@@ -35382,7 +36377,7 @@ var CartesiaSTT = class {
35382
36377
  const url2 = this.buildWsUrl();
35383
36378
  let ws = null;
35384
36379
  try {
35385
- ws = await new Promise((resolve, reject) => {
36380
+ ws = await new Promise((resolve2, reject) => {
35386
36381
  const sock = new import_ws7.default(url2, {
35387
36382
  headers: { "User-Agent": USER_AGENT }
35388
36383
  });
@@ -35395,7 +36390,7 @@ var CartesiaSTT = class {
35395
36390
  }, 5e3);
35396
36391
  sock.once("open", () => {
35397
36392
  clearTimeout(timer);
35398
- resolve(sock);
36393
+ resolve2(sock);
35399
36394
  });
35400
36395
  sock.once("error", (err) => {
35401
36396
  clearTimeout(timer);
@@ -35422,14 +36417,14 @@ var CartesiaSTT = class {
35422
36417
  this.ws = new import_ws7.default(url2, {
35423
36418
  headers: { "User-Agent": USER_AGENT }
35424
36419
  });
35425
- await new Promise((resolve, reject) => {
36420
+ await new Promise((resolve2, reject) => {
35426
36421
  const timer = setTimeout(
35427
36422
  () => reject(new Error("Cartesia STT connect timeout")),
35428
36423
  CONNECT_TIMEOUT_MS
35429
36424
  );
35430
36425
  this.ws.once("open", () => {
35431
36426
  clearTimeout(timer);
35432
- resolve();
36427
+ resolve2();
35433
36428
  });
35434
36429
  this.ws.once("error", (err) => {
35435
36430
  clearTimeout(timer);
@@ -35517,12 +36512,12 @@ var CartesiaSTT = class {
35517
36512
  */
35518
36513
  async finalize() {
35519
36514
  if (!this.ws || this.ws.readyState !== import_ws7.default.OPEN) return;
35520
- await new Promise((resolve) => {
36515
+ await new Promise((resolve2) => {
35521
36516
  this.ws.send(CartesiaSTTClientFrame.FINALIZE, (err) => {
35522
36517
  if (err) {
35523
36518
  getLogger().debug(`Cartesia finalize send failed: ${String(err)}`);
35524
36519
  }
35525
- resolve();
36520
+ resolve2();
35526
36521
  });
35527
36522
  });
35528
36523
  }
@@ -35571,10 +36566,10 @@ var CartesiaSTT = class {
35571
36566
  if (!ws) return;
35572
36567
  if (ws.readyState === import_ws7.default.OPEN) {
35573
36568
  try {
35574
- await new Promise((resolve) => {
36569
+ await new Promise((resolve2) => {
35575
36570
  ws.send(CartesiaSTTClientFrame.FINALIZE, (err) => {
35576
36571
  if (err) getLogger().warn(`CartesiaSTT finalize send failed: ${String(err)}`);
35577
- resolve();
36572
+ resolve2();
35578
36573
  });
35579
36574
  });
35580
36575
  } catch (err) {
@@ -35582,18 +36577,18 @@ var CartesiaSTT = class {
35582
36577
  }
35583
36578
  }
35584
36579
  if (ws.readyState === import_ws7.default.OPEN || ws.readyState === import_ws7.default.CONNECTING) {
35585
- await new Promise((resolve) => {
36580
+ await new Promise((resolve2) => {
35586
36581
  const done = () => {
35587
36582
  ws.off("close", done);
35588
36583
  ws.off("error", done);
35589
- resolve();
36584
+ resolve2();
35590
36585
  };
35591
36586
  ws.once("close", done);
35592
36587
  ws.once("error", done);
35593
36588
  try {
35594
36589
  ws.close();
35595
36590
  } catch {
35596
- resolve();
36591
+ resolve2();
35597
36592
  }
35598
36593
  });
35599
36594
  }
@@ -35758,11 +36753,11 @@ var SonioxSTT = class _SonioxSTT {
35758
36753
  async connect() {
35759
36754
  this.final.reset();
35760
36755
  this.ws = new import_ws8.default(this.baseUrl);
35761
- await new Promise((resolve, reject) => {
36756
+ await new Promise((resolve2, reject) => {
35762
36757
  const timer = setTimeout(() => reject(new Error("Soniox connect timeout")), 1e4);
35763
36758
  this.ws.once("open", () => {
35764
36759
  clearTimeout(timer);
35765
- resolve();
36760
+ resolve2();
35766
36761
  });
35767
36762
  this.ws.once("error", (err) => {
35768
36763
  clearTimeout(timer);
@@ -36080,7 +37075,7 @@ var AssemblyAISTT = class _AssemblyAISTT {
36080
37075
  const headers = this.buildHeaders();
36081
37076
  let ws = null;
36082
37077
  try {
36083
- ws = await new Promise((resolve, reject) => {
37078
+ ws = await new Promise((resolve2, reject) => {
36084
37079
  const sock = new import_ws9.default(url2, { headers });
36085
37080
  const timer = setTimeout(() => {
36086
37081
  try {
@@ -36091,7 +37086,7 @@ var AssemblyAISTT = class _AssemblyAISTT {
36091
37086
  }, 5e3);
36092
37087
  sock.once("open", () => {
36093
37088
  clearTimeout(timer);
36094
- resolve(sock);
37089
+ resolve2(sock);
36095
37090
  });
36096
37091
  sock.once("error", (err) => {
36097
37092
  clearTimeout(timer);
@@ -36125,14 +37120,14 @@ var AssemblyAISTT = class _AssemblyAISTT {
36125
37120
  this.attachHandlers(this.ws);
36126
37121
  }
36127
37122
  async awaitOpen(ws) {
36128
- await new Promise((resolve, reject) => {
37123
+ await new Promise((resolve2, reject) => {
36129
37124
  const timer = setTimeout(
36130
37125
  () => reject(new Error("AssemblyAI connect timeout")),
36131
37126
  CONNECT_TIMEOUT_MS2
36132
37127
  );
36133
37128
  ws.once("open", () => {
36134
37129
  clearTimeout(timer);
36135
- resolve();
37130
+ resolve2();
36136
37131
  });
36137
37132
  ws.once("error", (err) => {
36138
37133
  clearTimeout(timer);
@@ -36319,14 +37314,14 @@ var AssemblyAISTT = class _AssemblyAISTT {
36319
37314
  this.ws.send(JSON.stringify({ type: AssemblyAIClientFrame.TERMINATE }));
36320
37315
  } catch {
36321
37316
  }
36322
- await new Promise((resolve) => {
37317
+ await new Promise((resolve2) => {
36323
37318
  const timer = setTimeout(() => {
36324
37319
  this.terminationResolve = null;
36325
- resolve();
37320
+ resolve2();
36326
37321
  }, TERMINATION_WAIT_TIMEOUT_MS);
36327
37322
  this.terminationResolve = () => {
36328
37323
  clearTimeout(timer);
36329
- resolve();
37324
+ resolve2();
36330
37325
  };
36331
37326
  });
36332
37327
  try {
@@ -36513,7 +37508,7 @@ var SpeechmaticsSTT = class {
36513
37508
  headers: { Authorization: `Bearer ${this.apiKey}` }
36514
37509
  });
36515
37510
  this.ws = ws;
36516
- await new Promise((resolve, reject) => {
37511
+ await new Promise((resolve2, reject) => {
36517
37512
  let settled = false;
36518
37513
  const settle = (fn) => {
36519
37514
  if (settled) return;
@@ -36527,7 +37522,7 @@ var SpeechmaticsSTT = class {
36527
37522
  ),
36528
37523
  CONNECT_TIMEOUT_MS3
36529
37524
  );
36530
- ws.once("open", () => settle(resolve));
37525
+ ws.once("open", () => settle(resolve2));
36531
37526
  ws.once("error", (err) => settle(() => reject(err)));
36532
37527
  ws.once("unexpected-response", (_req, res) => {
36533
37528
  const status = res?.statusCode ?? 0;
@@ -36722,228 +37717,6 @@ var STT7 = class extends SpeechmaticsSTT {
36722
37717
 
36723
37718
  // src/tts/elevenlabs.ts
36724
37719
  init_cjs_shims();
36725
-
36726
- // src/providers/elevenlabs-tts.ts
36727
- init_cjs_shims();
36728
- var ELEVENLABS_BASE_URL = "https://api.elevenlabs.io/v1";
36729
- var ELEVENLABS_VOICE_ID_BY_NAME = {
36730
- rachel: "21m00Tcm4TlvDq8ikWAM",
36731
- drew: "29vD33N1CtxCmqQRPOHJ",
36732
- clyde: "2EiwWnXFnvU5JabPnv8n",
36733
- paul: "5Q0t7uMcjvnagumLfvZi",
36734
- domi: "AZnzlk1XvdvUeBnXmlld",
36735
- dave: "CYw3kZ02Hs0563khs1Fj",
36736
- fin: "D38z5RcWu1voky8WS1ja",
36737
- bella: "EXAVITQu4vr4xnSDxMaL",
36738
- antoni: "ErXwobaYiN019PkySvjV",
36739
- thomas: "GBv7mTt0atIp3Br8iCZE",
36740
- charlie: "IKne3meq5aSn9XLyUdCD",
36741
- george: "JBFqnCBsd6RMkjVDRZzb",
36742
- emily: "LcfcDJNUP1GQjkzn1xUU",
36743
- elli: "MF3mGyEYCl7XYWbV9V6O",
36744
- callum: "N2lVS1w4EtoT3dr4eOWO",
36745
- patrick: "ODq5zmih8GrVes37Dizd",
36746
- harry: "SOYHLrjzK2X1ezoPC6cr",
36747
- liam: "TX3LPaxmHKxFdv7VOQHJ",
36748
- dorothy: "ThT5KcBeYPX3keUQqHPh",
36749
- josh: "TxGEqnHWrfWFTfGW9XjX",
36750
- arnold: "VR6AewLTigWG4xSOukaG",
36751
- charlotte: "XB0fDUnXU5powFXDhCwa",
36752
- matilda: "XrExE9yKIg1WjnnlVkGX",
36753
- matthew: "Yko7PKHZNXotIFUBG7I9",
36754
- james: "ZQe5CZNOzWyzPSCn5a3c",
36755
- joseph: "Zlb1dXrM653N07WRdFW3",
36756
- jeremy: "bVMeCyTHy58xNoL34h3p",
36757
- michael: "flq6f7yk4E4fJM5XTYuZ",
36758
- ethan: "g5CIjZEefAph4nQFvHAz",
36759
- gigi: "jBpfuIE2acCO8z3wKNLl",
36760
- freya: "jsCqWAovK2LkecY7zXl4",
36761
- brian: "nPczCjzI2devNBz1zQrb",
36762
- grace: "oWAxZDx7w5VEj9dCyTzz",
36763
- daniel: "onwK4e9ZLuTAKqWW03F9",
36764
- lily: "pFZP5JQG7iQjIQuC4Bku",
36765
- serena: "pMsXgVXv3BLzUgSXRplE",
36766
- adam: "pNInz6obpgDQGcFmaJgB",
36767
- nicole: "piTKgcLEGmPE4e6mEKli",
36768
- bill: "pqHfZKP75CvOlQylNhV4",
36769
- jessie: "t0jbNlBVZ17f02VDIeMI",
36770
- ryan: "wViXBPUzp2ZZixB1xQuM",
36771
- sam: "yoZ06aMxZJJ28mfd3POQ",
36772
- glinda: "z9fAnlkpzviPz146aGWa",
36773
- giovanni: "zcAOhNBS3c14rBihAFp1",
36774
- mimi: "zrHiDhphv9ZnVXBqCLjz",
36775
- sarah: "EXAVITQu4vr4xnSDxMaL",
36776
- alloy: "EXAVITQu4vr4xnSDxMaL"
36777
- };
36778
- var VOICE_ID_PATTERN = /^[A-Za-z0-9]{20}$/;
36779
- function resolveVoiceId(voice) {
36780
- if (!voice) return voice;
36781
- if (VOICE_ID_PATTERN.test(voice)) return voice;
36782
- return ELEVENLABS_VOICE_ID_BY_NAME[voice.toLowerCase()] ?? voice;
36783
- }
36784
- var ElevenLabsModel = {
36785
- V3: "eleven_v3",
36786
- FLASH_V2_5: "eleven_flash_v2_5",
36787
- TURBO_V2_5: "eleven_turbo_v2_5",
36788
- MULTILINGUAL_V2: "eleven_multilingual_v2",
36789
- MONOLINGUAL_V1: "eleven_monolingual_v1"
36790
- };
36791
- var ElevenLabsOutputFormat = {
36792
- MP3_22050_32: "mp3_22050_32",
36793
- MP3_44100_32: "mp3_44100_32",
36794
- MP3_44100_64: "mp3_44100_64",
36795
- MP3_44100_96: "mp3_44100_96",
36796
- MP3_44100_128: "mp3_44100_128",
36797
- MP3_44100_192: "mp3_44100_192",
36798
- PCM_8000: "pcm_8000",
36799
- PCM_16000: "pcm_16000",
36800
- PCM_22050: "pcm_22050",
36801
- PCM_24000: "pcm_24000",
36802
- PCM_44100: "pcm_44100",
36803
- ULAW_8000: "ulaw_8000"
36804
- };
36805
- var ElevenLabsTTS = class _ElevenLabsTTS {
36806
- // Stable pricing/dashboard key — read by stream-handler / metrics via
36807
- // ``(agent.tts.constructor as any).providerKey``. Without this the cost
36808
- // calculator falls back to ``constructor.name`` ("ElevenLabsTTS") which
36809
- // does NOT match the pricing table key "elevenlabs", silently zeroing
36810
- // TTS cost for callers that construct the raw REST class directly
36811
- // (exposed at top level as ``ElevenLabsRestTTS``).
36812
- static providerKey = "elevenlabs";
36813
- apiKey;
36814
- voiceId;
36815
- modelId;
36816
- outputFormat;
36817
- voiceSettings;
36818
- languageCode;
36819
- chunkSize;
36820
- constructor(apiKey, voiceIdOrOptions = "21m00Tcm4TlvDq8ikWAM", modelId = ElevenLabsModel.FLASH_V2_5, outputFormat = ElevenLabsOutputFormat.PCM_16000) {
36821
- this.apiKey = apiKey;
36822
- if (typeof voiceIdOrOptions === "object") {
36823
- const o = voiceIdOrOptions;
36824
- this.voiceId = resolveVoiceId(o.voiceId ?? "21m00Tcm4TlvDq8ikWAM");
36825
- this.modelId = o.modelId ?? ElevenLabsModel.FLASH_V2_5;
36826
- this.outputFormat = o.outputFormat ?? ElevenLabsOutputFormat.PCM_16000;
36827
- this.voiceSettings = o.voiceSettings;
36828
- this.languageCode = o.languageCode;
36829
- this.chunkSize = o.chunkSize ?? 4096;
36830
- } else {
36831
- this.voiceId = resolveVoiceId(voiceIdOrOptions);
36832
- this.modelId = modelId;
36833
- this.outputFormat = outputFormat;
36834
- this.voiceSettings = void 0;
36835
- this.languageCode = void 0;
36836
- this.chunkSize = 4096;
36837
- }
36838
- }
36839
- /**
36840
- * Construct an instance pre-configured for Twilio Media Streams.
36841
- *
36842
- * Sets `outputFormat='ulaw_8000'` so ElevenLabs emits μ-law @ 8 kHz
36843
- * directly — the exact wire format Twilio's media stream uses — letting
36844
- * the SDK skip the 16 kHz→8 kHz resample and PCM→μ-law conversion in
36845
- * `TwilioAudioSender`. Saves ~30–80 ms first-byte and per-frame CPU,
36846
- * and removes a potential aliasing source.
36847
- *
36848
- * `voiceSettings` defaults to a low-bandwidth-friendly profile
36849
- * (speaker boost off, modest stability) which sounds cleaner at 8 kHz
36850
- * μ-law than the studio default. Pass an explicit object to override.
36851
- */
36852
- static forTwilio(apiKey, options = {}) {
36853
- const voiceSettings = options.voiceSettings ?? {
36854
- // Speaker boost adds high-frequency emphasis that aliases ugly over an
36855
- // 8 kHz μ-law line. Slightly higher stability tames the excursions
36856
- // that compander quantization noise can amplify.
36857
- stability: 0.6,
36858
- similarity_boost: 0.75,
36859
- use_speaker_boost: false
36860
- };
36861
- return new _ElevenLabsTTS(apiKey, {
36862
- ...options,
36863
- voiceSettings,
36864
- outputFormat: ElevenLabsOutputFormat.ULAW_8000
36865
- });
36866
- }
36867
- /**
36868
- * Construct an instance pre-configured for Telnyx bidirectional media.
36869
- *
36870
- * Telnyx's default media-streaming codec is L16 PCM @ 16 kHz, which
36871
- * matches our default Telnyx handler. We pick `pcm_16000` so the audio
36872
- * flows end-to-end with zero resampling or transcoding.
36873
- *
36874
- * Trade-off: if your Telnyx profile is pinned to PCMU/8000 (μ-law),
36875
- * construct `ElevenLabsTTS` directly with `outputFormat: 'ulaw_8000'`
36876
- * — Telnyx supports that natively too.
36877
- */
36878
- static forTelnyx(apiKey, options = {}) {
36879
- return new _ElevenLabsTTS(apiKey, {
36880
- ...options,
36881
- outputFormat: ElevenLabsOutputFormat.PCM_16000
36882
- });
36883
- }
36884
- /**
36885
- * Synthesise text to speech and return the full audio as a single Buffer.
36886
- *
36887
- * For large chunks (or when latency matters) call `synthesizeStream` instead.
36888
- */
36889
- async synthesize(text) {
36890
- const chunks = [];
36891
- for await (const chunk of this.synthesizeStream(text)) {
36892
- chunks.push(chunk);
36893
- }
36894
- return Buffer.concat(chunks);
36895
- }
36896
- /**
36897
- * Synthesise text and yield audio chunks as they arrive (streaming).
36898
- *
36899
- * The yielded buffers are raw PCM at 16 kHz (or whatever `outputFormat` is
36900
- * configured to). `chunkSize` controls the maximum yield size — 512 is a
36901
- * good choice for low-latency telephony.
36902
- */
36903
- async *synthesizeStream(text) {
36904
- const url2 = `${ELEVENLABS_BASE_URL}/text-to-speech/${encodeURIComponent(this.voiceId)}/stream?output_format=${encodeURIComponent(this.outputFormat)}`;
36905
- const body = {
36906
- text,
36907
- model_id: this.modelId
36908
- };
36909
- if (this.voiceSettings) body["voice_settings"] = this.voiceSettings;
36910
- if (this.languageCode) body["language_code"] = this.languageCode;
36911
- const response = await fetch(url2, {
36912
- method: "POST",
36913
- headers: {
36914
- "xi-api-key": this.apiKey,
36915
- "Content-Type": "application/json"
36916
- },
36917
- body: JSON.stringify(body),
36918
- signal: AbortSignal.timeout(3e4)
36919
- });
36920
- if (!response.ok) {
36921
- const errBody = await response.text();
36922
- throw new Error(`ElevenLabs TTS error ${response.status}: ${errBody}`);
36923
- }
36924
- if (!response.body) {
36925
- throw new Error("ElevenLabs TTS: no response body");
36926
- }
36927
- const reader = response.body.getReader();
36928
- try {
36929
- while (true) {
36930
- const { done, value } = await reader.read();
36931
- if (done) break;
36932
- if (!value || value.length === 0) continue;
36933
- const buf = Buffer.from(value);
36934
- for (let offset = 0; offset < buf.length; offset += this.chunkSize) {
36935
- yield buf.subarray(offset, Math.min(offset + this.chunkSize, buf.length));
36936
- }
36937
- }
36938
- } finally {
36939
- if (typeof reader.cancel === "function") await reader.cancel().catch(() => {
36940
- });
36941
- reader.releaseLock();
36942
- }
36943
- }
36944
- };
36945
-
36946
- // src/tts/elevenlabs.ts
36947
37720
  function resolveApiKey(apiKey) {
36948
37721
  const key = apiKey ?? process.env.ELEVENLABS_API_KEY;
36949
37722
  if (!key) {
@@ -36959,7 +37732,7 @@ var TTS = class _TTS extends ElevenLabsTTS {
36959
37732
  super(resolveApiKey(opts.apiKey), {
36960
37733
  voiceId: opts.voiceId ?? "EXAVITQu4vr4xnSDxMaL",
36961
37734
  modelId: opts.modelId ?? "eleven_flash_v2_5",
36962
- outputFormat: opts.outputFormat ?? "pcm_16000",
37735
+ ...opts.outputFormat !== void 0 ? { outputFormat: opts.outputFormat } : {},
36963
37736
  languageCode: opts.languageCode,
36964
37737
  voiceSettings: opts.voiceSettings
36965
37738
  });
@@ -37031,6 +37804,20 @@ var ElevenLabsWebSocketTTS = class _ElevenLabsWebSocketTTS {
37031
37804
  * changes.
37032
37805
  */
37033
37806
  adoptedConnection = null;
37807
+ /**
37808
+ * Active WS for the in-flight ``synthesizeStream`` call, if any. Set
37809
+ * when a stream starts, cleared in its ``finally`` block. The
37810
+ * stream-handler calls ``cancelActiveStream()`` from ``cancelSpeaking``
37811
+ * to unblock the generator's inner ``await Promise<frame>`` — without
37812
+ * it, a barge-in on the firstMessage live path leaves the for-await
37813
+ * stuck waiting for the next frame; ElevenLabs never sends
37814
+ * ``isFinal=true`` after the consumer breaks, the 30 s frame timeout
37815
+ * fires post-call, and meanwhile ``initPipeline`` never returns so
37816
+ * the STT ``onTranscript`` callback never registers and subsequent
37817
+ * user turns are silently dropped (root cause of the 2026-05-20
37818
+ * "first message OK, then no response" symptom).
37819
+ */
37820
+ activeStreamWs = null;
37034
37821
  /**
37035
37822
  * The wire format requested over the ElevenLabs WS. Initially set from
37036
37823
  * the constructor; ``setTelephonyCarrier`` may auto-flip it to the
@@ -37079,6 +37866,32 @@ var ElevenLabsWebSocketTTS = class _ElevenLabsWebSocketTTS {
37079
37866
  if (!native) return;
37080
37867
  this._outputFormat = native;
37081
37868
  }
37869
+ /**
37870
+ * Force-close the WebSocket of any in-flight ``synthesizeStream`` call.
37871
+ * Called by the stream-handler from ``cancelSpeaking`` (barge-in) so
37872
+ * the generator's inner ``await Promise<frame>`` loop unblocks cleanly
37873
+ * via the ``onClose`` handler — instead of waiting up to 30 s for the
37874
+ * ``FRAME_TIMEOUT_MS`` watchdog to fire. No-op when no stream is in
37875
+ * flight or when the WS is already closing.
37876
+ *
37877
+ * Without this, a barge-in during the firstMessage live path left the
37878
+ * for-await stuck (ElevenLabs never sends ``isFinal=true`` after the
37879
+ * consumer breaks), ``initPipeline`` never returned, the STT
37880
+ * ``onTranscript`` callback never registered, and the entire remainder
37881
+ * of the call was silent for the user. Surfaced during the 2026-05-20
37882
+ * acceptance run.
37883
+ */
37884
+ cancelActiveStream() {
37885
+ const ws = this.activeStreamWs;
37886
+ if (!ws) return;
37887
+ this.activeStreamWs = null;
37888
+ try {
37889
+ if (ws.readyState === import_ws11.default.OPEN || ws.readyState === import_ws11.default.CONNECTING) {
37890
+ ws.close();
37891
+ }
37892
+ } catch {
37893
+ }
37894
+ }
37082
37895
  /** Pre-configured for Twilio Media Streams (`ulaw_8000`). */
37083
37896
  static forTwilio(opts) {
37084
37897
  return new _ElevenLabsWebSocketTTS({
@@ -37164,6 +37977,7 @@ var ElevenLabsWebSocketTTS = class _ElevenLabsWebSocketTTS {
37164
37977
  headers: { "xi-api-key": this.apiKey }
37165
37978
  });
37166
37979
  }
37980
+ this.activeStreamWs = ws;
37167
37981
  const queue = [];
37168
37982
  let done = false;
37169
37983
  let pendingError = null;
@@ -37234,7 +38048,7 @@ var ElevenLabsWebSocketTTS = class _ElevenLabsWebSocketTTS {
37234
38048
  ws.on("error", onError);
37235
38049
  try {
37236
38050
  if (!adopted) {
37237
- await new Promise((resolve, reject) => {
38051
+ await new Promise((resolve2, reject) => {
37238
38052
  connectTimer = setTimeout(
37239
38053
  () => reject(new Error("ElevenLabs WS connect timeout")),
37240
38054
  CONNECT_TIMEOUT_MS4
@@ -37242,7 +38056,7 @@ var ElevenLabsWebSocketTTS = class _ElevenLabsWebSocketTTS {
37242
38056
  ws.once("open", () => {
37243
38057
  if (connectTimer) clearTimeout(connectTimer);
37244
38058
  connectTimer = void 0;
37245
- resolve();
38059
+ resolve2();
37246
38060
  });
37247
38061
  ws.once("error", (err) => {
37248
38062
  if (connectTimer) clearTimeout(connectTimer);
@@ -37284,6 +38098,7 @@ var ElevenLabsWebSocketTTS = class _ElevenLabsWebSocketTTS {
37284
38098
  }
37285
38099
  } finally {
37286
38100
  if (connectTimer) clearTimeout(connectTimer);
38101
+ if (this.activeStreamWs === ws) this.activeStreamWs = null;
37287
38102
  try {
37288
38103
  if (ws.readyState === import_ws11.default.OPEN) {
37289
38104
  ws.send(JSON.stringify({ text: "" }));
@@ -37326,14 +38141,14 @@ var ElevenLabsWebSocketTTS = class _ElevenLabsWebSocketTTS {
37326
38141
  headers: { "xi-api-key": this.apiKey }
37327
38142
  });
37328
38143
  try {
37329
- await new Promise((resolve, reject) => {
38144
+ await new Promise((resolve2, reject) => {
37330
38145
  const timer = setTimeout(
37331
38146
  () => reject(new Error("ElevenLabs WS TTS warmup connect timeout")),
37332
38147
  CONNECT_TIMEOUT_MS4
37333
38148
  );
37334
38149
  ws.once("open", () => {
37335
38150
  clearTimeout(timer);
37336
- resolve();
38151
+ resolve2();
37337
38152
  });
37338
38153
  ws.once("error", (err) => {
37339
38154
  clearTimeout(timer);
@@ -37377,14 +38192,14 @@ var ElevenLabsWebSocketTTS = class _ElevenLabsWebSocketTTS {
37377
38192
  const ws = new import_ws11.default(this.buildUrl(), {
37378
38193
  headers: { "xi-api-key": this.apiKey }
37379
38194
  });
37380
- await new Promise((resolve, reject) => {
38195
+ await new Promise((resolve2, reject) => {
37381
38196
  const timer = setTimeout(
37382
38197
  () => reject(new Error("ElevenLabs WS park connect timeout")),
37383
38198
  CONNECT_TIMEOUT_MS4
37384
38199
  );
37385
38200
  ws.once("open", () => {
37386
38201
  clearTimeout(timer);
37387
- resolve();
38202
+ resolve2();
37388
38203
  });
37389
38204
  ws.once("error", (err) => {
37390
38205
  clearTimeout(timer);
@@ -37456,9 +38271,9 @@ function buildOpts(opts) {
37456
38271
  const out = {
37457
38272
  apiKey: resolveApiKey2(opts.apiKey),
37458
38273
  modelId: opts.modelId ?? "eleven_flash_v2_5",
37459
- outputFormat: opts.outputFormat ?? "pcm_16000",
37460
38274
  autoMode: opts.autoMode ?? true
37461
38275
  };
38276
+ if (opts.outputFormat !== void 0) out.outputFormat = opts.outputFormat;
37462
38277
  if (opts.voiceId !== void 0) out.voiceId = opts.voiceId;
37463
38278
  if (opts.voiceSettings !== void 0) out.voiceSettings = opts.voiceSettings;
37464
38279
  if (opts.languageCode !== void 0) out.languageCode = opts.languageCode;
@@ -37706,198 +38521,6 @@ var TTS3 = class extends OpenAITTS {
37706
38521
 
37707
38522
  // src/tts/cartesia.ts
37708
38523
  init_cjs_shims();
37709
-
37710
- // src/providers/cartesia-tts.ts
37711
- init_cjs_shims();
37712
- init_logger();
37713
- var CARTESIA_BASE_URL = "https://api.cartesia.ai";
37714
- var CARTESIA_API_VERSION = "2025-04-16";
37715
- var CARTESIA_DEFAULT_VOICE_ID = "f786b574-daa5-4673-aa0c-cbe3e8534c02";
37716
- var CartesiaTTSModel = {
37717
- SONIC_3: "sonic-3",
37718
- SONIC_2: "sonic-2",
37719
- SONIC: "sonic"
37720
- };
37721
- var CartesiaTTSContainer = {
37722
- RAW: "raw",
37723
- WAV: "wav",
37724
- MP3: "mp3"
37725
- };
37726
- var CartesiaTTSEncoding = {
37727
- PCM_S16LE: "pcm_s16le",
37728
- PCM_F32LE: "pcm_f32le",
37729
- PCM_MULAW: "pcm_mulaw",
37730
- PCM_ALAW: "pcm_alaw"
37731
- };
37732
- var CartesiaTTSSampleRate = {
37733
- HZ_8000: 8e3,
37734
- HZ_16000: 16e3,
37735
- HZ_22050: 22050,
37736
- HZ_24000: 24e3,
37737
- HZ_44100: 44100
37738
- };
37739
- var CartesiaTTSVoiceMode = {
37740
- ID: "id",
37741
- EMBEDDING: "embedding"
37742
- };
37743
- var CartesiaTTS = class _CartesiaTTS {
37744
- /** Stable pricing/dashboard key — read by stream-handler/metrics. */
37745
- static providerKey = "cartesia_tts";
37746
- apiKey;
37747
- model;
37748
- voice;
37749
- language;
37750
- sampleRate;
37751
- speed;
37752
- emotion;
37753
- volume;
37754
- baseUrl;
37755
- apiVersion;
37756
- constructor(apiKey, opts = {}) {
37757
- this.apiKey = apiKey;
37758
- this.model = opts.model ?? CartesiaTTSModel.SONIC_3;
37759
- this.voice = opts.voice ?? CARTESIA_DEFAULT_VOICE_ID;
37760
- this.language = opts.language ?? "en";
37761
- this.sampleRate = opts.sampleRate ?? CartesiaTTSSampleRate.HZ_16000;
37762
- this.speed = opts.speed;
37763
- this.emotion = typeof opts.emotion === "string" ? [opts.emotion] : opts.emotion;
37764
- this.volume = opts.volume;
37765
- this.baseUrl = opts.baseUrl ?? CARTESIA_BASE_URL;
37766
- this.apiVersion = opts.apiVersion ?? CARTESIA_API_VERSION;
37767
- }
37768
- /**
37769
- * Construct an instance pre-configured for Twilio Media Streams.
37770
- *
37771
- * Sets `sampleRate=8000` so Cartesia emits PCM_S16LE @ 8 kHz directly.
37772
- * Twilio's media stream uses μ-law @ 8 kHz so the SDK still does the
37773
- * PCM → μ-law transcode client-side, but the 16 kHz → 8 kHz resample
37774
- * step is skipped. Saves ~10–30 ms first-byte plus per-frame CPU and
37775
- * removes a potential aliasing source.
37776
- */
37777
- static forTwilio(apiKey, options = {}) {
37778
- return new _CartesiaTTS(apiKey, {
37779
- ...options,
37780
- sampleRate: CartesiaTTSSampleRate.HZ_8000
37781
- });
37782
- }
37783
- /**
37784
- * Construct an instance pre-configured for Telnyx bidirectional media.
37785
- *
37786
- * Sets `sampleRate=16000` to match Telnyx's L16/16000 default codec —
37787
- * audio flows end-to-end with zero resampling or transcoding. Same as
37788
- * the bare-constructor default; exists for API symmetry with
37789
- * {@link CartesiaTTS.forTwilio}.
37790
- */
37791
- static forTelnyx(apiKey, options = {}) {
37792
- return new _CartesiaTTS(apiKey, {
37793
- ...options,
37794
- sampleRate: CartesiaTTSSampleRate.HZ_16000
37795
- });
37796
- }
37797
- /** Build the JSON payload for the Cartesia bytes endpoint. */
37798
- buildPayload(text) {
37799
- const payload = {
37800
- model_id: this.model,
37801
- voice: { mode: CartesiaTTSVoiceMode.ID, id: this.voice },
37802
- transcript: text,
37803
- output_format: {
37804
- container: CartesiaTTSContainer.RAW,
37805
- encoding: CartesiaTTSEncoding.PCM_S16LE,
37806
- sample_rate: this.sampleRate
37807
- },
37808
- language: this.language
37809
- };
37810
- const generationConfig = {};
37811
- if (this.speed !== void 0) generationConfig.speed = this.speed;
37812
- if (this.emotion && this.emotion.length > 0)
37813
- generationConfig.emotion = this.emotion[0];
37814
- if (this.volume !== void 0) generationConfig.volume = this.volume;
37815
- if (Object.keys(generationConfig).length > 0) {
37816
- payload.generation_config = generationConfig;
37817
- }
37818
- return payload;
37819
- }
37820
- /**
37821
- * Pre-call HTTP warmup for the Cartesia `/tts/bytes` endpoint.
37822
- *
37823
- * Issues a lightweight `GET <baseUrl>/voices` so DNS, TLS, and HTTP/2
37824
- * are already up by the time the first `synthesizeStream()` POST
37825
- * lands. Best-effort: 5 s timeout, all exceptions swallowed at
37826
- * debug level.
37827
- *
37828
- * Billing safety: `GET /voices` is a free metadata read on
37829
- * Cartesia's REST surface (per https://docs.cartesia.ai). It does
37830
- * not consume synthesis credits. The actual synthesis is billed
37831
- * only when `POST /tts/bytes` runs with a non-empty `transcript`.
37832
- *
37833
- * Note: Cartesia TTS uses the HTTP path (vs the WebSocket variant
37834
- * Cartesia also exposes) — connection warmup is therefore HTTP-GET
37835
- * based, not WebSocket pre-handshake. The latency win is smaller
37836
- * (~50-150 ms vs the ~200-500 ms of a WS prewarm) but still real.
37837
- */
37838
- async warmup() {
37839
- try {
37840
- await fetch(`${this.baseUrl}/voices`, {
37841
- method: "GET",
37842
- headers: {
37843
- "X-API-Key": this.apiKey,
37844
- "Cartesia-Version": this.apiVersion
37845
- },
37846
- signal: AbortSignal.timeout(5e3)
37847
- });
37848
- } catch (err) {
37849
- getLogger().debug(`Cartesia TTS warmup failed (best-effort): ${String(err)}`);
37850
- }
37851
- }
37852
- /** Synthesize text and return the concatenated audio buffer. */
37853
- async synthesize(text) {
37854
- const chunks = [];
37855
- for await (const chunk of this.synthesizeStream(text)) {
37856
- chunks.push(chunk);
37857
- }
37858
- return Buffer.concat(chunks);
37859
- }
37860
- /**
37861
- * Synthesize text and yield raw PCM_S16LE chunks at the configured
37862
- * `sampleRate` as they arrive from Cartesia.
37863
- */
37864
- async *synthesizeStream(text) {
37865
- const response = await fetch(`${this.baseUrl}/tts/bytes`, {
37866
- method: "POST",
37867
- headers: {
37868
- "X-API-Key": this.apiKey,
37869
- "Cartesia-Version": this.apiVersion,
37870
- "Content-Type": "application/json"
37871
- },
37872
- body: JSON.stringify(this.buildPayload(text)),
37873
- signal: AbortSignal.timeout(3e4)
37874
- });
37875
- if (!response.ok) {
37876
- const body = await response.text();
37877
- throw new Error(`Cartesia TTS error ${response.status}: ${body}`);
37878
- }
37879
- if (!response.body) {
37880
- throw new Error("Cartesia TTS: no response body");
37881
- }
37882
- const reader = response.body.getReader();
37883
- try {
37884
- while (true) {
37885
- const { done, value } = await reader.read();
37886
- if (done) break;
37887
- if (value && value.length > 0) {
37888
- yield Buffer.from(value);
37889
- }
37890
- }
37891
- } finally {
37892
- if (typeof reader.cancel === "function")
37893
- await reader.cancel().catch(() => {
37894
- });
37895
- reader.releaseLock();
37896
- }
37897
- }
37898
- };
37899
-
37900
- // src/tts/cartesia.ts
37901
38524
  function resolveApiKey3(apiKey) {
37902
38525
  const key = apiKey ?? process.env.CARTESIA_API_KEY;
37903
38526
  if (!key) {
@@ -37927,150 +38550,6 @@ var TTS4 = class _TTS extends CartesiaTTS {
37927
38550
 
37928
38551
  // src/tts/rime.ts
37929
38552
  init_cjs_shims();
37930
-
37931
- // src/providers/rime-tts.ts
37932
- init_cjs_shims();
37933
- var RIME_BASE_URL = "https://users.rime.ai/v1/rime-tts";
37934
- var RimeModel = {
37935
- ARCANA: "arcana",
37936
- MIST: "mist",
37937
- MIST_V2: "mistv2"
37938
- };
37939
- var RimeAudioFormat = {
37940
- PCM: "audio/pcm",
37941
- MP3: "audio/mp3",
37942
- WAV: "audio/wav",
37943
- MULAW: "audio/mulaw"
37944
- };
37945
- var ARCANA_MODEL_TIMEOUT_MS = 60 * 4 * 1e3;
37946
- var MIST_MODEL_TIMEOUT_MS = 30 * 1e3;
37947
- function isMistModel(model) {
37948
- return model.includes(RimeModel.MIST);
37949
- }
37950
- function timeoutForModel(model) {
37951
- if (model === RimeModel.ARCANA) return ARCANA_MODEL_TIMEOUT_MS;
37952
- return MIST_MODEL_TIMEOUT_MS;
37953
- }
37954
- var RimeTTS = class {
37955
- /** Stable pricing/dashboard key — read by stream-handler/metrics. */
37956
- static providerKey = "rime";
37957
- apiKey;
37958
- model;
37959
- speaker;
37960
- lang;
37961
- sampleRate;
37962
- repetitionPenalty;
37963
- temperature;
37964
- topP;
37965
- maxTokens;
37966
- speedAlpha;
37967
- reduceLatency;
37968
- pauseBetweenBrackets;
37969
- phonemizeBetweenBrackets;
37970
- baseUrl;
37971
- totalTimeoutMs;
37972
- constructor(apiKey, opts = {}) {
37973
- this.apiKey = apiKey;
37974
- this.model = opts.model ?? RimeModel.ARCANA;
37975
- const defaultSpeaker = isMistModel(this.model) ? "cove" : "astra";
37976
- this.speaker = opts.speaker ?? defaultSpeaker;
37977
- this.lang = opts.lang ?? "eng";
37978
- this.sampleRate = opts.sampleRate ?? 16e3;
37979
- this.repetitionPenalty = opts.repetitionPenalty;
37980
- this.temperature = opts.temperature;
37981
- this.topP = opts.topP;
37982
- this.maxTokens = opts.maxTokens;
37983
- this.speedAlpha = opts.speedAlpha;
37984
- this.reduceLatency = opts.reduceLatency;
37985
- this.pauseBetweenBrackets = opts.pauseBetweenBrackets;
37986
- this.phonemizeBetweenBrackets = opts.phonemizeBetweenBrackets;
37987
- this.baseUrl = opts.baseUrl ?? RIME_BASE_URL;
37988
- this.totalTimeoutMs = timeoutForModel(this.model);
37989
- }
37990
- buildPayload(text) {
37991
- const payload = {
37992
- speaker: this.speaker,
37993
- text,
37994
- modelId: this.model
37995
- };
37996
- if (this.model === RimeModel.ARCANA) {
37997
- if (this.repetitionPenalty !== void 0)
37998
- payload.repetition_penalty = this.repetitionPenalty;
37999
- if (this.temperature !== void 0) payload.temperature = this.temperature;
38000
- if (this.topP !== void 0) payload.top_p = this.topP;
38001
- if (this.maxTokens !== void 0) payload.max_tokens = this.maxTokens;
38002
- payload.lang = this.lang;
38003
- payload.samplingRate = this.sampleRate;
38004
- } else if (isMistModel(this.model)) {
38005
- payload.lang = this.lang;
38006
- payload.samplingRate = this.sampleRate;
38007
- if (this.speedAlpha !== void 0) payload.speedAlpha = this.speedAlpha;
38008
- if (this.model === RimeModel.MIST_V2 && this.reduceLatency !== void 0) {
38009
- payload.reduceLatency = this.reduceLatency;
38010
- }
38011
- if (this.pauseBetweenBrackets !== void 0) {
38012
- payload.pauseBetweenBrackets = this.pauseBetweenBrackets;
38013
- }
38014
- if (this.phonemizeBetweenBrackets !== void 0) {
38015
- payload.phonemizeBetweenBrackets = this.phonemizeBetweenBrackets;
38016
- }
38017
- }
38018
- return payload;
38019
- }
38020
- /** Synthesize text and return the concatenated audio buffer. */
38021
- async synthesize(text) {
38022
- const chunks = [];
38023
- for await (const chunk of this.synthesizeStream(text)) {
38024
- chunks.push(chunk);
38025
- }
38026
- return Buffer.concat(chunks);
38027
- }
38028
- /**
38029
- * Synthesize text and yield raw PCM_S16LE chunks at the configured
38030
- * `sampleRate` as they stream in.
38031
- */
38032
- async *synthesizeStream(text) {
38033
- const response = await fetch(this.baseUrl, {
38034
- method: "POST",
38035
- headers: {
38036
- accept: RimeAudioFormat.PCM,
38037
- Authorization: `Bearer ${this.apiKey}`,
38038
- "content-type": "application/json"
38039
- },
38040
- body: JSON.stringify(this.buildPayload(text)),
38041
- signal: AbortSignal.timeout(this.totalTimeoutMs)
38042
- });
38043
- if (!response.ok) {
38044
- const body = await response.text();
38045
- throw new Error(`Rime TTS error ${response.status}: ${body}`);
38046
- }
38047
- const contentType = response.headers.get("content-type") ?? "";
38048
- if (!contentType.startsWith("audio")) {
38049
- const body = await response.text();
38050
- throw new Error(`Rime returned non-audio response: ${body.slice(0, 500)}`);
38051
- }
38052
- if (!response.body) {
38053
- throw new Error("Rime TTS: no response body");
38054
- }
38055
- const reader = response.body.getReader();
38056
- try {
38057
- while (true) {
38058
- const { done, value } = await reader.read();
38059
- if (done) break;
38060
- if (value && value.length > 0) {
38061
- yield Buffer.from(value);
38062
- }
38063
- }
38064
- } finally {
38065
- if (typeof reader.cancel === "function")
38066
- await reader.cancel().catch(() => {
38067
- });
38068
- reader.releaseLock();
38069
- }
38070
- }
38071
- };
38072
-
38073
- // src/tts/rime.ts
38074
38553
  var TTS5 = class extends RimeTTS {
38075
38554
  static providerKey = "rime";
38076
38555
  constructor(opts = {}) {
@@ -38715,12 +39194,7 @@ init_cjs_shims();
38715
39194
  init_cjs_shims();
38716
39195
  init_llm_loop();
38717
39196
  init_logger();
38718
-
38719
- // src/version.ts
38720
- init_cjs_shims();
38721
- var VERSION = "0.5.5";
38722
-
38723
- // src/providers/groq-llm.ts
39197
+ init_version();
38724
39198
  var GROQ_BASE_URL = "https://api.groq.com/openai/v1";
38725
39199
  var GroqModel = {
38726
39200
  LLAMA_3_3_70B_VERSATILE: "llama-3.3-70b-versatile",
@@ -38910,6 +39384,7 @@ init_cjs_shims();
38910
39384
  init_llm_loop();
38911
39385
  init_logger();
38912
39386
  init_errors();
39387
+ init_version();
38913
39388
  var CEREBRAS_BASE_URL = "https://api.cerebras.ai/v1";
38914
39389
  var CerebrasModel = {
38915
39390
  GPT_OSS_120B: "gpt-oss-120b",
@@ -40308,8 +40783,8 @@ var TwilioAdapter = class _TwilioAdapter {
40308
40783
  this.baseUrl = opts.region ? `https://api.${opts.region}.twilio.com/2010-04-01` : TWILIO_API_BASE2;
40309
40784
  this.authHeader = `Basic ${Buffer.from(`${accountSid}:${authToken}`).toString("base64")}`;
40310
40785
  }
40311
- async request(method, path5, body) {
40312
- const url2 = `${this.baseUrl}/Accounts/${encodeURIComponent(this.accountSid)}${path5}`;
40786
+ async request(method, path6, body) {
40787
+ const url2 = `${this.baseUrl}/Accounts/${encodeURIComponent(this.accountSid)}${path6}`;
40313
40788
  const headers = { Authorization: this.authHeader };
40314
40789
  if (body) headers["Content-Type"] = "application/x-www-form-urlencoded";
40315
40790
  const response = await fetch(url2, {
@@ -40320,7 +40795,7 @@ var TwilioAdapter = class _TwilioAdapter {
40320
40795
  });
40321
40796
  const text = await response.text();
40322
40797
  if (!response.ok) {
40323
- throw new Error(`Twilio ${method} ${path5} failed: ${response.status} ${text}`);
40798
+ throw new Error(`Twilio ${method} ${path6} failed: ${response.status} ${text}`);
40324
40799
  }
40325
40800
  if (!text) return {};
40326
40801
  try {
@@ -40338,8 +40813,8 @@ var TwilioAdapter = class _TwilioAdapter {
40338
40813
  const country = encodeURIComponent(opts.countryCode);
40339
40814
  const queryParts = ["PageSize=1"];
40340
40815
  if (opts.areaCode) queryParts.push(`AreaCode=${encodeURIComponent(opts.areaCode)}`);
40341
- const path5 = `/AvailablePhoneNumbers/${country}/Local.json?${queryParts.join("&")}`;
40342
- const available = await this.request("GET", path5);
40816
+ const path6 = `/AvailablePhoneNumbers/${country}/Local.json?${queryParts.join("&")}`;
40817
+ const available = await this.request("GET", path6);
40343
40818
  const first = available.available_phone_numbers?.[0]?.phone_number;
40344
40819
  if (!first) {
40345
40820
  throw new Error(`TwilioAdapter: no numbers available for country ${opts.countryCode}`);
@@ -40397,12 +40872,28 @@ var TwilioAdapter = class _TwilioAdapter {
40397
40872
  return { callSid: call.sid };
40398
40873
  }
40399
40874
  /**
40400
- * Build a minimal ``<Response><Connect><Stream url="..."/></Connect></Response>``
40401
- * TwiML document. Mirrors the Python adapter's ``generate_stream_twiml``.
40875
+ * Build a ``<Response><Connect><Stream url="...">`` TwiML document.
40876
+ *
40877
+ * ``parameters`` is forwarded as ``<Parameter name="..." value="..."/>``
40878
+ * children of ``<Stream>``. Twilio Media Streams strips query-string params
40879
+ * from the ``<Stream url=...>`` before the WS handshake, so
40880
+ * ``<Parameter>`` tags are the supported way to pre-populate
40881
+ * ``start.customParameters`` on the WS ``start`` frame. Used by the
40882
+ * inbound path to carry caller / callee through to the bridge.
40883
+ *
40884
+ * Mirrors the Python adapter's ``generate_stream_twiml``.
40402
40885
  */
40403
- static generateStreamTwiml(streamUrl) {
40404
- const escaped = streamUrl.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
40405
- return `<?xml version="1.0" encoding="UTF-8"?><Response><Connect><Stream url="${escaped}"/></Connect></Response>`;
40886
+ static generateStreamTwiml(streamUrl, parameters) {
40887
+ const esc2 = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
40888
+ const escapedUrl = esc2(streamUrl);
40889
+ let paramTags = "";
40890
+ if (parameters) {
40891
+ for (const [name, value] of Object.entries(parameters)) {
40892
+ if (value == null) continue;
40893
+ paramTags += `<Parameter name="${esc2(name)}" value="${esc2(String(value))}"/>`;
40894
+ }
40895
+ }
40896
+ return `<?xml version="1.0" encoding="UTF-8"?><Response><Connect><Stream url="${escapedUrl}">${paramTags}</Stream></Connect></Response>`;
40406
40897
  }
40407
40898
  /** Force-complete an in-progress call. */
40408
40899
  async endCall(callSid) {
@@ -40435,8 +40926,8 @@ var TelnyxAdapter = class {
40435
40926
  this.apiKey = apiKey;
40436
40927
  this.connectionId = connectionId;
40437
40928
  }
40438
- async request(method, path5, body) {
40439
- const url2 = `${this.baseUrl}${path5}`;
40929
+ async request(method, path6, body) {
40930
+ const url2 = `${this.baseUrl}${path6}`;
40440
40931
  const headers = {
40441
40932
  Authorization: `Bearer ${this.apiKey}`
40442
40933
  };
@@ -40449,7 +40940,7 @@ var TelnyxAdapter = class {
40449
40940
  });
40450
40941
  const text = await response.text();
40451
40942
  if (!response.ok) {
40452
- throw new Error(`Telnyx ${method} ${path5} failed: ${response.status} ${text}`);
40943
+ throw new Error(`Telnyx ${method} ${path6} failed: ${response.status} ${text}`);
40453
40944
  }
40454
40945
  if (!text) return {};
40455
40946
  try {
@@ -40610,11 +41101,11 @@ var TelnyxSTT = class {
40610
41101
  this.ws = new import_ws12.default(url2, {
40611
41102
  headers: { Authorization: `Bearer ${this.apiKey}` }
40612
41103
  });
40613
- await new Promise((resolve, reject) => {
41104
+ await new Promise((resolve2, reject) => {
40614
41105
  const timer = setTimeout(() => reject(new Error("Telnyx STT connect timeout")), 1e4);
40615
41106
  this.ws.once("open", () => {
40616
41107
  clearTimeout(timer);
40617
- resolve();
41108
+ resolve2();
40618
41109
  });
40619
41110
  this.ws.once("error", (err) => {
40620
41111
  clearTimeout(timer);
@@ -40722,11 +41213,11 @@ var TelnyxTTS = class {
40722
41213
  const ws = new import_ws13.default(url2, {
40723
41214
  headers: { Authorization: `Bearer ${this.apiKey}` }
40724
41215
  });
40725
- await new Promise((resolve, reject) => {
41216
+ await new Promise((resolve2, reject) => {
40726
41217
  const timer = setTimeout(() => reject(new Error("Telnyx TTS connect timeout")), 1e4);
40727
41218
  ws.once("open", () => {
40728
41219
  clearTimeout(timer);
40729
- resolve();
41220
+ resolve2();
40730
41221
  });
40731
41222
  ws.once("error", (err) => {
40732
41223
  clearTimeout(timer);
@@ -40772,7 +41263,7 @@ var TelnyxTTS = class {
40772
41263
  ws.send(JSON.stringify({ text: "" }));
40773
41264
  try {
40774
41265
  while (true) {
40775
- const item = queue.length > 0 ? queue.shift() : await new Promise((resolve) => waiters.push(resolve));
41266
+ const item = queue.length > 0 ? queue.shift() : await new Promise((resolve2) => waiters.push(resolve2));
40776
41267
  if (item === null) return;
40777
41268
  if (typeof item === "object" && "error" in item) throw item.error;
40778
41269
  yield item;
@@ -40801,6 +41292,8 @@ init_event_bus();
40801
41292
  CallMetricsAccumulator,
40802
41293
  CartesiaSTT,
40803
41294
  CartesiaTTS,
41295
+ CartesiaTTSModel,
41296
+ CartesiaTTSVoiceMode,
40804
41297
  CerebrasLLM,
40805
41298
  ChatContext,
40806
41299
  CloudflareTunnel,
@@ -40808,10 +41301,13 @@ init_event_bus();
40808
41301
  DEFAULT_PRICING,
40809
41302
  DTMF_EVENTS,
40810
41303
  DeepFilterNetFilter,
41304
+ DeepgramModel,
40811
41305
  DeepgramSTT,
40812
41306
  DefaultToolExecutor,
40813
41307
  ElevenLabsConvAI,
40814
41308
  ElevenLabsConvAIAdapter,
41309
+ ElevenLabsModel,
41310
+ ElevenLabsOutputFormat,
40815
41311
  ElevenLabsRestTTS,
40816
41312
  ElevenLabsTTS,
40817
41313
  ElevenLabsWebSocketTTS,
@@ -40840,8 +41336,15 @@ init_event_bus();
40840
41336
  OpenAIRealtime2,
40841
41337
  OpenAIRealtime2Adapter,
40842
41338
  OpenAIRealtimeAdapter,
41339
+ OpenAIRealtimeAudioFormat,
41340
+ OpenAIRealtimeModel,
41341
+ OpenAIRealtimeVADType,
40843
41342
  OpenAITTS,
40844
41343
  OpenAITranscribeSTT,
41344
+ OpenAITranscriptionModel,
41345
+ OpenAIVoice,
41346
+ PRICING_LAST_UPDATED,
41347
+ PRICING_VERSION,
40845
41348
  PartialStreamError,
40846
41349
  Patter,
40847
41350
  PatterConnectionError,
@@ -40849,9 +41352,12 @@ init_event_bus();
40849
41352
  PatterTool,
40850
41353
  PcmCarry,
40851
41354
  PipelineHookExecutor,
41355
+ PricingUnit,
40852
41356
  ProvisionError,
40853
41357
  RateLimitError,
40854
41358
  RemoteMessageHandler,
41359
+ RimeAudioFormat,
41360
+ RimeModel,
40855
41361
  RimeTTS,
40856
41362
  SPAN_BARGEIN,
40857
41363
  SPAN_CALL,