@openclaw/voice-call 2026.5.14-beta.1 → 2026.5.14-beta.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.
@@ -1,6 +1,6 @@
1
1
  import { fetchWithSsrFGuard } from "./runtime-api.js";
2
2
  import "./api.js";
3
- import { t as getHeader } from "./http-headers-B5L5gMpK.js";
3
+ import { a as getHeader } from "./runtime-entry-ox4PGoxT.js";
4
4
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
5
5
  import { isLoopbackHost } from "openclaw/plugin-sdk/gateway-runtime";
6
6
  import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { definePluginEntry, sleep } from "./runtime-api.js";
2
2
  import "./api.js";
3
3
  import { i as resolveVoiceCallConfig, s as validateProviderConfig } from "./config-C8gX5Cik.js";
4
- import { a as setupTailscaleExposureRoute, i as getTailscaleSelfInfo, n as resolveWebhookExposureStatus, r as cleanupTailscaleExposureRoute, s as resolveUserPath, t as createVoiceCallRuntime } from "./runtime-entry-CmYLshgT.js";
5
- import { i as parseVoiceCallPluginConfig, r as normalizeVoiceCallLegacyConfigInput, t as formatVoiceCallLegacyConfigWarnings } from "./config-compat-Monzho4R.js";
4
+ import { c as getTailscaleSelfInfo, l as setupTailscaleExposureRoute, o as resolveWebhookExposureStatus, p as resolveUserPath, s as cleanupTailscaleExposureRoute, t as createVoiceCallRuntime } from "./runtime-entry-ox4PGoxT.js";
5
+ import { i as parseVoiceCallPluginConfig, r as normalizeVoiceCallLegacyConfigInput, t as formatVoiceCallLegacyConfigWarnings } from "./config-compat-DJqJ8NzH.js";
6
6
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
7
7
  import { ErrorCodes, callGatewayFromCli, errorShape } from "openclaw/plugin-sdk/gateway-runtime";
8
8
  import { normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
@@ -1,6 +1,5 @@
1
- import { t as escapeXml } from "./voice-mapping-DMm-YvxM.js";
2
- import { t as getHeader } from "./http-headers-B5L5gMpK.js";
3
- import { n as reconstructWebhookUrl, r as verifyPlivoWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-D26auOvl.js";
1
+ import { a as getHeader, m as escapeXml } from "./runtime-entry-ox4PGoxT.js";
2
+ import { n as reconstructWebhookUrl, r as verifyPlivoWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-D_aM8XmA.js";
4
3
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
5
4
  import crypto from "node:crypto";
6
5
  //#region extensions/voice-call/src/providers/plivo.ts
@@ -1,5 +1,5 @@
1
1
  import { o as resolveVoiceCallSessionKey } from "./config-C8gX5Cik.js";
2
- import { t as resolveVoiceResponseModel } from "./response-model-CyF5K80p.js";
2
+ import { f as resolveVoiceResponseModel } from "./runtime-entry-ox4PGoxT.js";
3
3
  import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  import crypto from "node:crypto";
5
5
  import { applyModelOverrideToSessionEntry } from "openclaw/plugin-sdk/model-session-runtime";
@@ -1,14 +1,10 @@
1
1
  import { isBlockedHostnameOrIp, isRequestBodyLimitError, readRequestBodyWithLimit, requestBodyErrorToText } from "./runtime-api.js";
2
2
  import "./api.js";
3
3
  import { a as resolveVoiceCallEffectiveConfig, c as deepMergeDefined, i as resolveVoiceCallConfig, n as normalizeVoiceCallConfig, o as resolveVoiceCallSessionKey, r as resolveTwilioAuthToken, s as validateProviderConfig } from "./config-C8gX5Cik.js";
4
- import { n as mapVoiceToPolly, t as escapeXml } from "./voice-mapping-DMm-YvxM.js";
5
- import { t as resolveVoiceResponseModel } from "./response-model-CyF5K80p.js";
6
- import { a as convertPcmToMulaw8k, t as isProviderStatusTerminal } from "./call-status-CuIeqfac.js";
7
- import { t as getHeader } from "./http-headers-B5L5gMpK.js";
8
4
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
9
5
  import { isLoopbackHost } from "openclaw/plugin-sdk/gateway-runtime";
10
- import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
11
- import { REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME, buildRealtimeVoiceAgentConsultPolicyInstructions, consultRealtimeVoiceAgent, createTalkSessionController, recordTalkObservabilityEvent, resolveRealtimeVoiceAgentConsultTools, resolveRealtimeVoiceAgentConsultToolsAllow, resolveRealtimeVoiceFastContextConsult } from "openclaw/plugin-sdk/realtime-voice";
6
+ import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
+ import { REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME, buildRealtimeVoiceAgentConsultPolicyInstructions, consultRealtimeVoiceAgent, convertPcmToMulaw8k, createTalkSessionController, recordTalkObservabilityEvent, resolveRealtimeVoiceAgentConsultTools, resolveRealtimeVoiceAgentConsultToolsAllow, resolveRealtimeVoiceFastContextConsult } from "openclaw/plugin-sdk/realtime-voice";
12
8
  import { z } from "zod";
13
9
  import fs from "node:fs";
14
10
  import os from "node:os";
@@ -351,6 +347,41 @@ function resolvePreferredTtsVoice(config) {
351
347
  return resolveProviderVoiceSetting(config.tts?.providers?.[providerId]);
352
348
  }
353
349
  //#endregion
350
+ //#region extensions/voice-call/src/voice-mapping.ts
351
+ /**
352
+ * Escape XML special characters for TwiML and other XML responses.
353
+ */
354
+ function escapeXml(text) {
355
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
356
+ }
357
+ /**
358
+ * Map of OpenAI voice names to similar Twilio Polly voices.
359
+ */
360
+ const OPENAI_TO_POLLY_MAP = {
361
+ alloy: "Polly.Joanna",
362
+ echo: "Polly.Matthew",
363
+ fable: "Polly.Amy",
364
+ onyx: "Polly.Brian",
365
+ nova: "Polly.Salli",
366
+ shimmer: "Polly.Kimberly"
367
+ };
368
+ /**
369
+ * Default Polly voice when no mapping is found.
370
+ */
371
+ const DEFAULT_POLLY_VOICE = "Polly.Joanna";
372
+ /**
373
+ * Map OpenAI voice names to Twilio Polly equivalents.
374
+ * Falls through if already a valid Polly/Google voice.
375
+ *
376
+ * @param voice - OpenAI voice name (alloy, echo, etc.) or Polly voice name
377
+ * @returns Polly voice name suitable for Twilio TwiML
378
+ */
379
+ function mapVoiceToPolly(voice) {
380
+ if (!voice) return DEFAULT_POLLY_VOICE;
381
+ if (voice.startsWith("Polly.") || voice.startsWith("Google.")) return voice;
382
+ return OPENAI_TO_POLLY_MAP[normalizeLowercaseStringOrEmpty(voice)] || "Polly.Joanna";
383
+ }
384
+ //#endregion
354
385
  //#region extensions/voice-call/src/manager/twiml.ts
355
386
  function generateNotifyTwiml(message, voice) {
356
387
  return `<?xml version="1.0" encoding="UTF-8"?>
@@ -1274,6 +1305,27 @@ async function resolveRealtimeFastContextConsult(params) {
1274
1305
  });
1275
1306
  }
1276
1307
  //#endregion
1308
+ //#region extensions/voice-call/src/response-model.ts
1309
+ function resolveVoiceResponseModel(params) {
1310
+ const modelRef = params.voiceConfig.responseModel ?? `${params.agentRuntime.defaults.provider}/${params.agentRuntime.defaults.model}`;
1311
+ const slashIndex = modelRef.indexOf("/");
1312
+ return {
1313
+ modelRef,
1314
+ provider: slashIndex === -1 ? params.agentRuntime.defaults.provider : modelRef.slice(0, slashIndex),
1315
+ model: slashIndex === -1 ? modelRef : modelRef.slice(slashIndex + 1)
1316
+ };
1317
+ }
1318
+ //#endregion
1319
+ //#region extensions/voice-call/src/telephony-audio.ts
1320
+ /**
1321
+ * Chunk audio buffer into 20ms frames for streaming (8kHz mono mu-law).
1322
+ */
1323
+ function chunkAudio(audio, chunkSize = 160) {
1324
+ return (function* () {
1325
+ for (let i = 0; i < audio.length; i += chunkSize) yield audio.subarray(i, Math.min(i + chunkSize, audio.length));
1326
+ })();
1327
+ }
1328
+ //#endregion
1277
1329
  //#region extensions/voice-call/src/telephony-tts.ts
1278
1330
  const TELEPHONY_DEFAULT_TTS_TIMEOUT_MS = 8e3;
1279
1331
  function createTelephonyTtsProvider(params) {
@@ -1755,6 +1807,14 @@ function resolveWebhookExposureStatus(config) {
1755
1807
  };
1756
1808
  }
1757
1809
  //#endregion
1810
+ //#region extensions/voice-call/src/http-headers.ts
1811
+ function getHeader(headers, name) {
1812
+ const target = normalizeLowercaseStringOrEmpty(name);
1813
+ const value = headers[target] ?? Object.entries(headers).find(([key]) => normalizeLowercaseStringOrEmpty(key) === target)?.[1];
1814
+ if (Array.isArray(value)) return value[0];
1815
+ return value;
1816
+ }
1817
+ //#endregion
1758
1818
  //#region extensions/voice-call/src/media-stream.ts
1759
1819
  const DEFAULT_PRE_START_TIMEOUT_MS = 5e3;
1760
1820
  const DEFAULT_MAX_PENDING_CONNECTIONS = 32;
@@ -1773,6 +1833,14 @@ function normalizeWsMessageData(data) {
1773
1833
  if (Array.isArray(data)) return Buffer.concat(data);
1774
1834
  return Buffer.from(data);
1775
1835
  }
1836
+ function parseTwilioMediaMessage(data) {
1837
+ const raw = normalizeWsMessageData(data);
1838
+ try {
1839
+ return JSON.parse(raw.toString("utf8"));
1840
+ } catch (cause) {
1841
+ throw new Error("Twilio media stream message was malformed JSON", { cause });
1842
+ }
1843
+ }
1776
1844
  /**
1777
1845
  * Manages WebSocket connections for Twilio media streams.
1778
1846
  */
@@ -1848,8 +1916,7 @@ var MediaStreamHandler = class {
1848
1916
  }
1849
1917
  ws.on("message", async (data) => {
1850
1918
  try {
1851
- const raw = normalizeWsMessageData(data);
1852
- const message = JSON.parse(raw.toString("utf8"));
1919
+ const message = parseTwilioMediaMessage(data);
1853
1920
  switch (message.event) {
1854
1921
  case "connected":
1855
1922
  console.log("[MediaStream] Twilio connected");
@@ -2382,6 +2449,25 @@ Content-Length: ${Buffer.byteLength(body)}\r\n\r
2382
2449
  }
2383
2450
  };
2384
2451
  //#endregion
2452
+ //#region extensions/voice-call/src/providers/shared/call-status.ts
2453
+ const TERMINAL_PROVIDER_STATUS_TO_END_REASON = {
2454
+ completed: "completed",
2455
+ failed: "failed",
2456
+ busy: "busy",
2457
+ "no-answer": "no-answer",
2458
+ canceled: "hangup-bot"
2459
+ };
2460
+ function normalizeProviderStatus(status) {
2461
+ const normalized = normalizeOptionalLowercaseString(status);
2462
+ return normalized && normalized.length > 0 ? normalized : "unknown";
2463
+ }
2464
+ function mapProviderStatusToEndReason(status) {
2465
+ return TERMINAL_PROVIDER_STATUS_TO_END_REASON[normalizeProviderStatus(status)] ?? null;
2466
+ }
2467
+ function isProviderStatusTerminal(status) {
2468
+ return mapProviderStatusToEndReason(status) !== null;
2469
+ }
2470
+ //#endregion
2385
2471
  //#region extensions/voice-call/src/webhook/stale-call-reaper.ts
2386
2472
  const CHECK_INTERVAL_MS = 3e4;
2387
2473
  function startStaleCallReaper(params) {
@@ -2415,11 +2501,11 @@ const TRANSCRIPT_LOG_MAX_CHARS = 200;
2415
2501
  let realtimeTranscriptionRuntimePromise;
2416
2502
  let responseGeneratorModulePromise;
2417
2503
  function loadRealtimeTranscriptionRuntime() {
2418
- realtimeTranscriptionRuntimePromise ??= import("./realtime-transcription.runtime-B2h70y2W.js");
2504
+ realtimeTranscriptionRuntimePromise ??= import("./realtime-transcription.runtime-CuOCFrMc.js");
2419
2505
  return realtimeTranscriptionRuntimePromise;
2420
2506
  }
2421
2507
  function loadResponseGeneratorModule() {
2422
- responseGeneratorModulePromise ??= import("./response-generator-4wAlt0EM.js");
2508
+ responseGeneratorModulePromise ??= import("./response-generator-ZeNpwsyL.js");
2423
2509
  return responseGeneratorModulePromise;
2424
2510
  }
2425
2511
  function sanitizeTranscriptForLog(value) {
@@ -3036,27 +3122,27 @@ let mockProviderPromise;
3036
3122
  let realtimeVoiceRuntimePromise;
3037
3123
  let realtimeHandlerPromise;
3038
3124
  function loadTelnyxProvider() {
3039
- telnyxProviderPromise ??= import("./telnyx-3TbRULg_.js");
3125
+ telnyxProviderPromise ??= import("./telnyx-Df3IfYAS.js");
3040
3126
  return telnyxProviderPromise;
3041
3127
  }
3042
3128
  function loadTwilioProvider() {
3043
- twilioProviderPromise ??= import("./twilio-DjcNE_Rb.js");
3129
+ twilioProviderPromise ??= import("./twilio-B3zpyWY5.js");
3044
3130
  return twilioProviderPromise;
3045
3131
  }
3046
3132
  function loadPlivoProvider() {
3047
- plivoProviderPromise ??= import("./plivo-P0n73IxS.js");
3133
+ plivoProviderPromise ??= import("./plivo--HUS8UBT.js");
3048
3134
  return plivoProviderPromise;
3049
3135
  }
3050
3136
  function loadMockProvider() {
3051
- mockProviderPromise ??= import("./mock-0CEjFJi5.js");
3137
+ mockProviderPromise ??= import("./mock-BvTP8Rmx.js");
3052
3138
  return mockProviderPromise;
3053
3139
  }
3054
3140
  function loadRealtimeVoiceRuntime() {
3055
- realtimeVoiceRuntimePromise ??= import("./realtime-voice.runtime-Bkh4nvLn.js");
3141
+ realtimeVoiceRuntimePromise ??= import("./realtime-voice.runtime-BM0lAf0B.js");
3056
3142
  return realtimeVoiceRuntimePromise;
3057
3143
  }
3058
3144
  function loadRealtimeHandler() {
3059
- realtimeHandlerPromise ??= import("./realtime-handler-DQPIcvi-.js");
3145
+ realtimeHandlerPromise ??= import("./realtime-handler-Cj_3954k.js");
3060
3146
  return realtimeHandlerPromise;
3061
3147
  }
3062
3148
  function resolveVoiceCallConsultSessionKey(call) {
@@ -3320,4 +3406,4 @@ async function createVoiceCallRuntime(params) {
3320
3406
  }
3321
3407
  }
3322
3408
  //#endregion
3323
- export { setupTailscaleExposureRoute as a, getTailscaleSelfInfo as i, resolveWebhookExposureStatus as n, TELEPHONY_DEFAULT_TTS_TIMEOUT_MS as o, cleanupTailscaleExposureRoute as r, resolveUserPath as s, createVoiceCallRuntime as t };
3409
+ export { getHeader as a, getTailscaleSelfInfo as c, chunkAudio as d, resolveVoiceResponseModel as f, mapVoiceToPolly as h, normalizeProviderStatus as i, setupTailscaleExposureRoute as l, escapeXml as m, isProviderStatusTerminal as n, resolveWebhookExposureStatus as o, resolveUserPath as p, mapProviderStatusToEndReason as r, cleanupTailscaleExposureRoute as s, createVoiceCallRuntime as t, TELEPHONY_DEFAULT_TTS_TIMEOUT_MS as u };
@@ -1,2 +1,2 @@
1
- import { t as createVoiceCallRuntime } from "./runtime-entry-CmYLshgT.js";
1
+ import { t as createVoiceCallRuntime } from "./runtime-entry-ox4PGoxT.js";
2
2
  export { createVoiceCallRuntime };
package/dist/setup-api.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as migrateVoiceCallLegacyConfigInput } from "./config-compat-Monzho4R.js";
1
+ import { n as migrateVoiceCallLegacyConfigInput } from "./config-compat-DJqJ8NzH.js";
2
2
  import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
3
3
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
4
4
  //#region extensions/voice-call/setup-api.ts
@@ -1,4 +1,4 @@
1
- import { i as verifyTelnyxWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-D26auOvl.js";
1
+ import { i as verifyTelnyxWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-D_aM8XmA.js";
2
2
  import crypto from "node:crypto";
3
3
  //#region extensions/voice-call/src/providers/telnyx.ts
4
4
  function normalizeTelnyxDirection(direction) {
@@ -1,9 +1,7 @@
1
1
  import { fetchWithSsrFGuard } from "./runtime-api.js";
2
2
  import "./api.js";
3
- import { n as mapVoiceToPolly, t as escapeXml } from "./voice-mapping-DMm-YvxM.js";
4
- import { i as chunkAudio, n as mapProviderStatusToEndReason, r as normalizeProviderStatus, t as isProviderStatusTerminal } from "./call-status-CuIeqfac.js";
5
- import { t as getHeader } from "./http-headers-B5L5gMpK.js";
6
- import { a as verifyTwilioWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-D26auOvl.js";
3
+ import { a as getHeader, d as chunkAudio, h as mapVoiceToPolly, i as normalizeProviderStatus, m as escapeXml, n as isProviderStatusTerminal, r as mapProviderStatusToEndReason } from "./runtime-entry-ox4PGoxT.js";
4
+ import { a as verifyTwilioWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-D_aM8XmA.js";
7
5
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
8
6
  import crypto from "node:crypto";
9
7
  import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/voice-call",
3
- "version": "2026.5.14-beta.1",
3
+ "version": "2026.5.14-beta.2",
4
4
  "description": "OpenClaw voice-call plugin",
5
5
  "repository": {
6
6
  "type": "git",
@@ -10,7 +10,7 @@
10
10
  "dependencies": {
11
11
  "commander": "14.0.3",
12
12
  "typebox": "1.1.38",
13
- "ws": "8.20.0",
13
+ "ws": "8.20.1",
14
14
  "zod": "4.4.3"
15
15
  },
16
16
  "devDependencies": {
@@ -18,7 +18,7 @@
18
18
  "openclaw": "workspace:*"
19
19
  },
20
20
  "peerDependencies": {
21
- "openclaw": ">=2026.5.14-beta.1"
21
+ "openclaw": ">=2026.5.14-beta.2"
22
22
  },
23
23
  "peerDependenciesMeta": {
24
24
  "openclaw": {
@@ -35,10 +35,10 @@
35
35
  "minHostVersion": ">=2026.4.10"
36
36
  },
37
37
  "compat": {
38
- "pluginApi": ">=2026.5.14-beta.1"
38
+ "pluginApi": ">=2026.5.14-beta.2"
39
39
  },
40
40
  "build": {
41
- "openclawVersion": "2026.5.14-beta.1"
41
+ "openclawVersion": "2026.5.14-beta.2"
42
42
  },
43
43
  "release": {
44
44
  "publishToClawHub": true,
@@ -1,32 +0,0 @@
1
- import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
2
- import { convertPcmToMulaw8k } from "openclaw/plugin-sdk/realtime-voice";
3
- //#region extensions/voice-call/src/telephony-audio.ts
4
- /**
5
- * Chunk audio buffer into 20ms frames for streaming (8kHz mono mu-law).
6
- */
7
- function chunkAudio(audio, chunkSize = 160) {
8
- return (function* () {
9
- for (let i = 0; i < audio.length; i += chunkSize) yield audio.subarray(i, Math.min(i + chunkSize, audio.length));
10
- })();
11
- }
12
- //#endregion
13
- //#region extensions/voice-call/src/providers/shared/call-status.ts
14
- const TERMINAL_PROVIDER_STATUS_TO_END_REASON = {
15
- completed: "completed",
16
- failed: "failed",
17
- busy: "busy",
18
- "no-answer": "no-answer",
19
- canceled: "hangup-bot"
20
- };
21
- function normalizeProviderStatus(status) {
22
- const normalized = normalizeOptionalLowercaseString(status);
23
- return normalized && normalized.length > 0 ? normalized : "unknown";
24
- }
25
- function mapProviderStatusToEndReason(status) {
26
- return TERMINAL_PROVIDER_STATUS_TO_END_REASON[normalizeProviderStatus(status)] ?? null;
27
- }
28
- function isProviderStatusTerminal(status) {
29
- return mapProviderStatusToEndReason(status) !== null;
30
- }
31
- //#endregion
32
- export { convertPcmToMulaw8k as a, chunkAudio as i, mapProviderStatusToEndReason as n, normalizeProviderStatus as r, isProviderStatusTerminal as t };
@@ -1,10 +0,0 @@
1
- import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
2
- //#region extensions/voice-call/src/http-headers.ts
3
- function getHeader(headers, name) {
4
- const target = normalizeLowercaseStringOrEmpty(name);
5
- const value = headers[target] ?? Object.entries(headers).find(([key]) => normalizeLowercaseStringOrEmpty(key) === target)?.[1];
6
- if (Array.isArray(value)) return value[0];
7
- return value;
8
- }
9
- //#endregion
10
- export { getHeader as t };
@@ -1,12 +0,0 @@
1
- //#region extensions/voice-call/src/response-model.ts
2
- function resolveVoiceResponseModel(params) {
3
- const modelRef = params.voiceConfig.responseModel ?? `${params.agentRuntime.defaults.provider}/${params.agentRuntime.defaults.model}`;
4
- const slashIndex = modelRef.indexOf("/");
5
- return {
6
- modelRef,
7
- provider: slashIndex === -1 ? params.agentRuntime.defaults.provider : modelRef.slice(0, slashIndex),
8
- model: slashIndex === -1 ? modelRef : modelRef.slice(slashIndex + 1)
9
- };
10
- }
11
- //#endregion
12
- export { resolveVoiceResponseModel as t };
@@ -1,37 +0,0 @@
1
- import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
2
- //#region extensions/voice-call/src/voice-mapping.ts
3
- /**
4
- * Escape XML special characters for TwiML and other XML responses.
5
- */
6
- function escapeXml(text) {
7
- return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
8
- }
9
- /**
10
- * Map of OpenAI voice names to similar Twilio Polly voices.
11
- */
12
- const OPENAI_TO_POLLY_MAP = {
13
- alloy: "Polly.Joanna",
14
- echo: "Polly.Matthew",
15
- fable: "Polly.Amy",
16
- onyx: "Polly.Brian",
17
- nova: "Polly.Salli",
18
- shimmer: "Polly.Kimberly"
19
- };
20
- /**
21
- * Default Polly voice when no mapping is found.
22
- */
23
- const DEFAULT_POLLY_VOICE = "Polly.Joanna";
24
- /**
25
- * Map OpenAI voice names to Twilio Polly equivalents.
26
- * Falls through if already a valid Polly/Google voice.
27
- *
28
- * @param voice - OpenAI voice name (alloy, echo, etc.) or Polly voice name
29
- * @returns Polly voice name suitable for Twilio TwiML
30
- */
31
- function mapVoiceToPolly(voice) {
32
- if (!voice) return DEFAULT_POLLY_VOICE;
33
- if (voice.startsWith("Polly.") || voice.startsWith("Google.")) return voice;
34
- return OPENAI_TO_POLLY_MAP[normalizeLowercaseStringOrEmpty(voice)] || "Polly.Joanna";
35
- }
36
- //#endregion
37
- export { mapVoiceToPolly as n, escapeXml as t };
File without changes