@openclaw/voice-call 2026.7.2-beta.7 → 2026.7.34

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.
Files changed (49) hide show
  1. package/README.md +3 -2
  2. package/dist/.setup/config-BBpso-fA.mjs +674 -0
  3. package/dist/.setup/config-compat-CYRE-SSo.mjs +156 -0
  4. package/dist/{guarded-json-api-DS9sZa3C.js → .setup/guarded-json-api-BukKSVNW.mjs} +56 -42
  5. package/dist/{mock-YsvBTX-7.js → .setup/mock-DY4yTqR2.mjs} +1 -1
  6. package/dist/{plivo-vZ--Wqxv.js → .setup/plivo-DFHh72MH.mjs} +3 -2
  7. package/dist/{realtime-handler--i_OERv6.js → .setup/realtime-handler-DWLxvZuO.mjs} +198 -130
  8. package/dist/{response-generator-D-7ApVfT.js → .setup/response-generator-DoC6lfcm.mjs} +6 -21
  9. package/dist/{runtime-entry-D0UJiLfi.js → .setup/runtime-entry-CH-CV0Sf.mjs} +384 -868
  10. package/dist/{store-path-nYL-yM0S.js → .setup/store-1UfyY3HS.mjs} +6 -13
  11. package/dist/{telnyx-Dx6cfLvV.js → .setup/telnyx-Bb8X-nin.mjs} +1 -1
  12. package/dist/{twilio-D_D8KysU.js → .setup/twilio-Bsbf3O9A.mjs} +11 -15
  13. package/dist/{webhook-replay-aaP79dI8.js → .setup/webhook-replay-g3cDA5wr.mjs} +1 -1
  14. package/dist/cli-metadata.js +1 -5
  15. package/dist/doctor-contract-api.js +14 -36
  16. package/dist/index.js +46 -48
  17. package/dist/runtime-entry.js +1 -1
  18. package/dist/setup-api.js +2 -86
  19. package/node_modules/commander/LICENSE +22 -0
  20. package/node_modules/commander/Readme.md +1172 -0
  21. package/node_modules/commander/index.js +21 -0
  22. package/node_modules/commander/lib/argument.js +147 -0
  23. package/node_modules/commander/lib/command.js +2790 -0
  24. package/node_modules/commander/lib/error.js +36 -0
  25. package/node_modules/commander/lib/help.js +731 -0
  26. package/node_modules/commander/lib/option.js +377 -0
  27. package/node_modules/commander/lib/suggestSimilar.js +99 -0
  28. package/node_modules/commander/package-support.json +19 -0
  29. package/node_modules/commander/package.json +64 -0
  30. package/node_modules/commander/typings/index.d.ts +1113 -0
  31. package/node_modules/typebox/build/compile/validator.d.mts +4 -1
  32. package/node_modules/typebox/build/guard/guard.d.mts +1 -1
  33. package/node_modules/typebox/build/guard/guard.mjs +2 -5
  34. package/node_modules/typebox/build/guard/string.mjs +4 -16
  35. package/node_modules/typebox/build/schema/engine/_functions.mjs +15 -34
  36. package/node_modules/typebox/build/type/action/readonly_object.d.mts +0 -5
  37. package/node_modules/typebox/build/type/action/readonly_object.mjs +0 -8
  38. package/node_modules/typebox/build/typebox.d.mts +1 -1
  39. package/node_modules/typebox/build/typebox.mjs +1 -1
  40. package/node_modules/typebox/package.json +1 -1
  41. package/node_modules/typebox/readme.md +32 -29
  42. package/node_modules/ws/lib/permessage-deflate.js +3 -1
  43. package/node_modules/ws/package.json +1 -1
  44. package/openclaw.plugin.json +15 -2
  45. package/package.json +8 -6
  46. package/dist/cli-output-mode.js +0 -27
  47. package/dist/twilio-region-4fkgz3UG.js +0 -25
  48. /package/dist/{realtime-transcription.runtime-CbJAs5t_.js → .setup/realtime-transcription.runtime-CbJAs5t_.mjs} +0 -0
  49. /package/dist/{realtime-voice.runtime-vtdCOWg-.js → .setup/realtime-voice.runtime-vtdCOWg-.mjs} +0 -0
@@ -0,0 +1,156 @@
1
+ import { t as VoiceCallConfigSchema } from "./config-BBpso-fA.mjs";
2
+ import { asOptionalRecord, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
3
+ //#region extensions/voice-call/src/config-compat.ts
4
+ /** Version where legacy voice-call config shape support is removed. */
5
+ const VOICE_CALL_LEGACY_CONFIG_REMOVAL_VERSION = "2026.6.0";
6
+ const asObject = asOptionalRecord;
7
+ const getString = readStringField;
8
+ /** Read finite numeric config values. */
9
+ function getNumber(obj, key) {
10
+ const value = obj?.[key];
11
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
12
+ }
13
+ /** Merge legacy provider-specific values into the canonical providers map. */
14
+ function mergeProviderConfig(providersValue, providerId, compatValues) {
15
+ if (Object.keys(compatValues).length === 0) return asObject(providersValue);
16
+ const providers = asObject(providersValue) ?? {};
17
+ const existing = asObject(providers[providerId]) ?? {};
18
+ return {
19
+ ...providers,
20
+ [providerId]: {
21
+ ...existing,
22
+ ...compatValues
23
+ }
24
+ };
25
+ }
26
+ /** Collect legacy voice-call config keys that should be migrated. */
27
+ function collectVoiceCallLegacyConfigIssues(value) {
28
+ const raw = asObject(value) ?? {};
29
+ const realtime = asObject(raw.realtime);
30
+ const realtimeAgentContext = asObject(realtime?.agentContext);
31
+ const twilio = asObject(raw.twilio);
32
+ const streaming = asObject(raw.streaming);
33
+ const issues = [];
34
+ if (raw.provider === "log") issues.push({
35
+ path: "provider",
36
+ replacement: "provider",
37
+ message: "Replace provider \"log\" with \"mock\"."
38
+ });
39
+ if (typeof twilio?.from === "string") issues.push({
40
+ path: "twilio.from",
41
+ replacement: "fromNumber",
42
+ message: "Move twilio.from to fromNumber."
43
+ });
44
+ if (typeof streaming?.sttProvider === "string") issues.push({
45
+ path: "streaming.sttProvider",
46
+ replacement: "streaming.provider",
47
+ message: "Move streaming.sttProvider to streaming.provider."
48
+ });
49
+ if (typeof streaming?.openaiApiKey === "string") issues.push({
50
+ path: "streaming.openaiApiKey",
51
+ replacement: "streaming.providers.openai.apiKey",
52
+ message: "Move streaming.openaiApiKey to streaming.providers.openai.apiKey."
53
+ });
54
+ if (typeof streaming?.sttModel === "string") issues.push({
55
+ path: "streaming.sttModel",
56
+ replacement: "streaming.providers.openai.model",
57
+ message: "Move streaming.sttModel to streaming.providers.openai.model."
58
+ });
59
+ if (typeof streaming?.silenceDurationMs === "number") issues.push({
60
+ path: "streaming.silenceDurationMs",
61
+ replacement: "streaming.providers.openai.silenceDurationMs",
62
+ message: "Move streaming.silenceDurationMs to streaming.providers.openai.silenceDurationMs."
63
+ });
64
+ if (typeof streaming?.vadThreshold === "number") issues.push({
65
+ path: "streaming.vadThreshold",
66
+ replacement: "streaming.providers.openai.vadThreshold",
67
+ message: "Move streaming.vadThreshold to streaming.providers.openai.vadThreshold."
68
+ });
69
+ if (realtimeAgentContext && Object.hasOwn(realtimeAgentContext, "includeSystemPrompt")) issues.push({
70
+ path: "realtime.agentContext.includeSystemPrompt",
71
+ replacement: "realtime.agentContext",
72
+ message: "Remove realtime.agentContext.includeSystemPrompt; realtime context now uses the generated agent prompt."
73
+ });
74
+ return issues;
75
+ }
76
+ /** Format runtime warnings for legacy voice-call config keys. */
77
+ function formatVoiceCallLegacyConfigWarnings(params) {
78
+ const issues = collectVoiceCallLegacyConfigIssues(params.value);
79
+ if (issues.length === 0) return [];
80
+ return [`[voice-call] legacy config keys detected under ${params.configPathPrefix}; runtime loading will not rewrite them, and support for the legacy shape will be removed in ${VOICE_CALL_LEGACY_CONFIG_REMOVAL_VERSION}. Run "${params.doctorFixCommand}".`, ...issues.map((issue) => `[voice-call] ${params.configPathPrefix}.${issue.path}: ${issue.message}`)];
81
+ }
82
+ /** Migrate legacy voice-call config input to the current canonical shape. */
83
+ function migrateVoiceCallLegacyConfigInput(params) {
84
+ const raw = asObject(params.value) ?? {};
85
+ const realtime = asObject(raw.realtime);
86
+ const realtimeAgentContext = asObject(realtime?.agentContext);
87
+ const twilio = asObject(raw.twilio);
88
+ const streaming = asObject(raw.streaming);
89
+ const configPathPrefix = params.configPathPrefix ?? "plugins.entries.voice-call.config";
90
+ const issues = collectVoiceCallLegacyConfigIssues(raw);
91
+ const legacyStreamingOpenAICompat = {};
92
+ const streamingOpenAIApiKey = getString(streaming, "openaiApiKey");
93
+ if (streamingOpenAIApiKey) legacyStreamingOpenAICompat.apiKey = streamingOpenAIApiKey;
94
+ const streamingSttModel = getString(streaming, "sttModel");
95
+ if (streamingSttModel) legacyStreamingOpenAICompat.model = streamingSttModel;
96
+ const streamingSilenceDurationMs = getNumber(streaming, "silenceDurationMs");
97
+ if (streamingSilenceDurationMs !== void 0) legacyStreamingOpenAICompat.silenceDurationMs = streamingSilenceDurationMs;
98
+ const streamingVadThreshold = getNumber(streaming, "vadThreshold");
99
+ if (streamingVadThreshold !== void 0) legacyStreamingOpenAICompat.vadThreshold = streamingVadThreshold;
100
+ const streamingProvider = getString(streaming, "provider");
101
+ const legacyStreamingProvider = getString(streaming, "sttProvider");
102
+ const normalizedStreaming = streaming ? {
103
+ ...streaming,
104
+ provider: streamingProvider ?? legacyStreamingProvider,
105
+ providers: mergeProviderConfig(streaming.providers, "openai", legacyStreamingOpenAICompat)
106
+ } : void 0;
107
+ if (normalizedStreaming) {
108
+ delete normalizedStreaming.sttProvider;
109
+ delete normalizedStreaming.openaiApiKey;
110
+ delete normalizedStreaming.sttModel;
111
+ delete normalizedStreaming.silenceDurationMs;
112
+ delete normalizedStreaming.vadThreshold;
113
+ }
114
+ const normalizedTwilio = twilio ? { ...twilio } : void 0;
115
+ if (normalizedTwilio) delete normalizedTwilio.from;
116
+ const normalizedRealtimeAgentContext = realtimeAgentContext ? { ...realtimeAgentContext } : void 0;
117
+ if (normalizedRealtimeAgentContext) delete normalizedRealtimeAgentContext.includeSystemPrompt;
118
+ const normalizedRealtime = realtime ? {
119
+ ...realtime,
120
+ agentContext: normalizedRealtimeAgentContext ?? realtime.agentContext
121
+ } : void 0;
122
+ const config = {
123
+ ...raw,
124
+ provider: raw.provider === "log" ? "mock" : raw.provider,
125
+ fromNumber: raw.fromNumber ?? (typeof twilio?.from === "string" ? twilio.from : void 0),
126
+ twilio: normalizedTwilio,
127
+ streaming: normalizedStreaming,
128
+ realtime: normalizedRealtime
129
+ };
130
+ const changes = [];
131
+ if (raw.provider === "log") changes.push(`Moved ${configPathPrefix}.provider "log" → "mock".`);
132
+ if (typeof twilio?.from === "string" && typeof raw.fromNumber !== "string") changes.push(`Moved ${configPathPrefix}.twilio.from → ${configPathPrefix}.fromNumber.`);
133
+ if (typeof streaming?.sttProvider === "string") changes.push(`Moved ${configPathPrefix}.streaming.sttProvider → ${configPathPrefix}.streaming.provider.`);
134
+ if (typeof streaming?.openaiApiKey === "string") changes.push(`Moved ${configPathPrefix}.streaming.openaiApiKey → ${configPathPrefix}.streaming.providers.openai.apiKey.`);
135
+ if (typeof streaming?.sttModel === "string") changes.push(`Moved ${configPathPrefix}.streaming.sttModel → ${configPathPrefix}.streaming.providers.openai.model.`);
136
+ if (getNumber(streaming, "silenceDurationMs") !== void 0) changes.push(`Moved ${configPathPrefix}.streaming.silenceDurationMs → ${configPathPrefix}.streaming.providers.openai.silenceDurationMs.`);
137
+ else if (typeof streaming?.silenceDurationMs === "number") changes.push(`Removed invalid ${configPathPrefix}.streaming.silenceDurationMs.`);
138
+ if (getNumber(streaming, "vadThreshold") !== void 0) changes.push(`Moved ${configPathPrefix}.streaming.vadThreshold → ${configPathPrefix}.streaming.providers.openai.vadThreshold.`);
139
+ else if (typeof streaming?.vadThreshold === "number") changes.push(`Removed invalid ${configPathPrefix}.streaming.vadThreshold.`);
140
+ if (realtimeAgentContext && Object.hasOwn(realtimeAgentContext, "includeSystemPrompt")) changes.push(`Removed ${configPathPrefix}.realtime.agentContext.includeSystemPrompt.`);
141
+ return {
142
+ config,
143
+ changes,
144
+ issues
145
+ };
146
+ }
147
+ /** Normalize legacy voice-call config input without returning migration metadata. */
148
+ function normalizeVoiceCallLegacyConfigInput(value) {
149
+ return migrateVoiceCallLegacyConfigInput({ value }).config;
150
+ }
151
+ /** Parse voice-call plugin config after applying legacy normalization. */
152
+ function parseVoiceCallPluginConfig(value) {
153
+ return VoiceCallConfigSchema.parse(normalizeVoiceCallLegacyConfigInput(value));
154
+ }
155
+ //#endregion
156
+ export { parseVoiceCallPluginConfig as i, migrateVoiceCallLegacyConfigInput as n, normalizeVoiceCallLegacyConfigInput as r, formatVoiceCallLegacyConfigWarnings as t };
@@ -1,14 +1,12 @@
1
- import { fetchWithSsrFGuard } from "./runtime-api.js";
2
- import "./api.js";
3
- import { n as normalizeProxyIp, s as getHeader } from "./runtime-entry-D0UJiLfi.js";
4
- import { n as markWebhookReplay, t as createWebhookReplayCache } from "./webhook-replay-aaP79dI8.js";
1
+ import { fetchWithSsrFGuard } from "../runtime-api.js";
2
+ import "../api.js";
3
+ import { n as normalizeProxyIp, o as getHeader } from "./runtime-entry-CH-CV0Sf.mjs";
4
+ import { n as markWebhookReplay, t as createWebhookReplayCache } from "./webhook-replay-g3cDA5wr.mjs";
5
5
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
6
6
  import { isLoopbackHost } from "openclaw/plugin-sdk/gateway-runtime";
7
7
  import { normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
8
8
  import crypto from "node:crypto";
9
9
  import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
10
- import { isIP } from "node:net";
11
- import { readResponseTextPrefix, readResponseWithLimit } from "openclaw/plugin-sdk/response-limit-runtime";
12
10
  //#region extensions/voice-call/src/webhook-security.ts
13
11
  const twilioReplayCache = createWebhookReplayCache();
14
12
  const plivoReplayCache = createWebhookReplayCache();
@@ -30,7 +28,8 @@ function createSkippedVerificationReplayKey(provider, ctx) {
30
28
  function validateTwilioSignature(authToken, signature, url, params) {
31
29
  if (!signature) return false;
32
30
  const dataToSign = buildTwilioDataToSign(url, params);
33
- return safeEqualSecret(signature, crypto.createHmac("sha1", authToken).update(dataToSign).digest("base64"));
31
+ const expectedSignature = crypto.createHmac("sha1", authToken).update(dataToSign).digest("base64");
32
+ return safeEqualSecret(signature, expectedSignature);
34
33
  }
35
34
  function buildTwilioDataToSign(url, params) {
36
35
  let dataToSign = url;
@@ -55,24 +54,16 @@ function isValidHostname(hostname) {
55
54
  */
56
55
  function extractHostname(hostHeader) {
57
56
  if (!hostHeader) return null;
57
+ let hostname;
58
58
  if (hostHeader.startsWith("[")) {
59
59
  const endBracket = hostHeader.indexOf("]");
60
60
  if (endBracket === -1) return null;
61
- const suffix = hostHeader.slice(endBracket + 1);
62
- if (suffix && !/^:\d+$/u.test(suffix)) return null;
63
- const hostname = hostHeader.slice(1, endBracket);
64
- if (isIP(hostname) !== 6) return null;
65
- try {
66
- const parsedHostname = new URL(`https://[${hostname}]/`).hostname;
67
- if (!parsedHostname.startsWith("[") || !parsedHostname.endsWith("]")) return null;
68
- return normalizeLowercaseStringOrEmpty(parsedHostname.slice(1, -1));
69
- } catch {
70
- return null;
71
- }
61
+ hostname = hostHeader.slice(1, endBracket);
62
+ return normalizeLowercaseStringOrEmpty(hostname);
72
63
  }
73
64
  if (hostHeader.includes("@")) return null;
74
- const hostname = hostHeader.split(":").at(0);
75
- if (!hostname || !isValidHostname(hostname)) return null;
65
+ hostname = hostHeader.split(":")[0];
66
+ if (!isValidHostname(hostname)) return null;
76
67
  return normalizeLowercaseStringOrEmpty(hostname);
77
68
  }
78
69
  function extractHostnameFromHeader(headerValue) {
@@ -89,9 +80,6 @@ function normalizeAllowedHosts(allowedHosts) {
89
80
  }
90
81
  return normalized.size > 0 ? normalized : null;
91
82
  }
92
- function formatHostnameForUrl(hostname) {
93
- return isIP(hostname) === 6 ? `[${hostname}]` : hostname;
94
- }
95
83
  /**
96
84
  * Reconstruct the public webhook URL from request headers.
97
85
  *
@@ -161,13 +149,15 @@ function reconstructWebhookUrl(ctx, options) {
161
149
  const parsed = new URL(ctx.url);
162
150
  path = parsed.pathname + parsed.search;
163
151
  } catch {}
164
- return `${proto}://${formatHostnameForUrl(host)}${path}`;
152
+ return `${proto}://${host}${path}`;
165
153
  }
166
154
  function buildTwilioVerificationUrl(ctx, publicUrl, urlOptions) {
167
155
  if (!publicUrl) return reconstructWebhookUrl(ctx, urlOptions);
168
156
  try {
169
157
  const base = new URL(publicUrl);
170
- base.search = new URL(ctx.url).search;
158
+ const requestUrl = new URL(ctx.url);
159
+ base.pathname = requestUrl.pathname;
160
+ base.search = requestUrl.search;
171
161
  return base.toString();
172
162
  } catch {
173
163
  return publicUrl;
@@ -270,8 +260,8 @@ function verifyTelnyxWebhook(ctx, publicKey, options) {
270
260
  ok: false,
271
261
  reason: "Missing signature or timestamp header"
272
262
  };
273
- const eventTimeSec = /^(?:0|[1-9]\d*)$/.test(timestamp) ? Number(timestamp) : void 0;
274
- if (eventTimeSec === void 0 || !Number.isSafeInteger(eventTimeSec)) return {
263
+ const eventTimeSec = Number.parseInt(timestamp, 10);
264
+ if (!Number.isFinite(eventTimeSec)) return {
275
265
  ok: false,
276
266
  reason: "Invalid timestamp header"
277
267
  };
@@ -284,7 +274,7 @@ function verifyTelnyxWebhook(ctx, publicKey, options) {
284
274
  ok: false,
285
275
  reason: "Invalid signature"
286
276
  };
287
- const maxSkewMs = options?.maxSkewMs ?? 300 * 1e3;
277
+ const maxSkewMs = options?.maxSkewMs ?? 3e5;
288
278
  const eventTimeMs = eventTimeSec * 1e3;
289
279
  if (Math.abs(Date.now() - eventTimeMs) > maxSkewMs) return {
290
280
  ok: false,
@@ -393,7 +383,9 @@ function createPlivoV3ReplayKey(params) {
393
383
  }
394
384
  function validatePlivoV2Signature(params) {
395
385
  const baseUrl = getBaseUrlNoQuery(params.url);
396
- return safeEqualSecret(normalizeSignatureBase64(crypto.createHmac("sha256", params.authToken).update(baseUrl + params.nonce).digest("base64")), normalizeSignatureBase64(params.signature));
386
+ const expected = normalizeSignatureBase64(crypto.createHmac("sha256", params.authToken).update(baseUrl + params.nonce).digest("base64"));
387
+ const provided = normalizeSignatureBase64(params.signature);
388
+ return safeEqualSecret(expected, provided);
397
389
  }
398
390
  function toParamMapFromSearchParams(sp) {
399
391
  const map = {};
@@ -405,18 +397,16 @@ function toParamMapFromSearchParams(sp) {
405
397
  }
406
398
  function sortedQueryString(params) {
407
399
  const parts = [];
408
- const entries = Object.entries(params).toSorted(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
409
- for (const [key, entryValues] of entries) {
410
- const values = [...entryValues].toSorted();
400
+ for (const key of Object.keys(params).toSorted()) {
401
+ const values = [...params[key]].toSorted();
411
402
  for (const value of values) parts.push(`${key}=${value}`);
412
403
  }
413
404
  return parts.join("&");
414
405
  }
415
406
  function sortedParamsString(params) {
416
407
  const parts = [];
417
- const entries = Object.entries(params).toSorted(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
418
- for (const [key, entryValues] of entries) {
419
- const values = [...entryValues].toSorted();
408
+ for (const key of Object.keys(params).toSorted()) {
409
+ const values = [...params[key]].toSorted();
420
410
  for (const value of values) parts.push(`${key}${value}`);
421
411
  }
422
412
  return parts.join("");
@@ -474,6 +464,7 @@ function verifyPlivoWebhook(ctx, authToken, options) {
474
464
  if (options?.publicUrl) try {
475
465
  const req = new URL(reconstructed);
476
466
  const base = new URL(options.publicUrl);
467
+ base.pathname = req.pathname;
477
468
  base.search = req.search;
478
469
  verificationUrl = base.toString();
479
470
  } catch {
@@ -546,8 +537,8 @@ function verifyPlivoWebhook(ctx, authToken, options) {
546
537
  }
547
538
  //#endregion
548
539
  //#region extensions/voice-call/src/providers/shared/response-body.ts
549
- const PROVIDER_JSON_RESPONSE_MAX_BYTES = 1 * 1024 * 1024;
550
- const PROVIDER_ERROR_RESPONSE_MAX_BYTES = 8 * 1024;
540
+ const PROVIDER_JSON_RESPONSE_MAX_BYTES = 1048576;
541
+ const PROVIDER_ERROR_RESPONSE_MAX_BYTES = 8192;
551
542
  const TRUNCATED_SUFFIX = "... [truncated]";
552
543
  async function cancelProviderResponseBody(response) {
553
544
  await response.body?.cancel().catch(() => void 0);
@@ -556,12 +547,35 @@ function appendTruncatedSuffix(text) {
556
547
  return `${text.trimEnd()}${TRUNCATED_SUFFIX}`;
557
548
  }
558
549
  async function readProviderResponseTextWithLimit(params) {
559
- if (params.truncateOnLimit) {
560
- const prefix = await readResponseTextPrefix(params.response, params.maxBytes);
561
- return prefix.truncated ? appendTruncatedSuffix(prefix.text) : prefix.text;
550
+ if (!params.response.body) return "";
551
+ const reader = params.response.body.getReader();
552
+ const decoder = new TextDecoder();
553
+ let totalBytes = 0;
554
+ let text = "";
555
+ try {
556
+ while (true) {
557
+ const { done, value } = await reader.read();
558
+ if (done) return text + decoder.decode();
559
+ if (!value?.byteLength) continue;
560
+ const remainingBytes = params.maxBytes - totalBytes;
561
+ if (value.byteLength > remainingBytes) {
562
+ if (params.truncateOnLimit) {
563
+ const clipped = remainingBytes > 0 ? value.slice(0, remainingBytes) : void 0;
564
+ if (clipped) text += decoder.decode(clipped, { stream: true });
565
+ await reader.cancel().catch(() => void 0);
566
+ return appendTruncatedSuffix(text + decoder.decode());
567
+ }
568
+ await reader.cancel().catch(() => void 0);
569
+ throw new Error(`provider response body too large: ${totalBytes + value.byteLength} bytes (limit: ${params.maxBytes} bytes)`);
570
+ }
571
+ text += decoder.decode(value, { stream: true });
572
+ totalBytes += value.byteLength;
573
+ }
574
+ } finally {
575
+ try {
576
+ reader.releaseLock();
577
+ } catch {}
562
578
  }
563
- const body = await readResponseWithLimit(params.response, params.maxBytes, { onOverflow: ({ size, maxBytes }) => /* @__PURE__ */ new Error(`provider response body too large: ${size} bytes (limit: ${maxBytes} bytes)`) });
564
- return new TextDecoder().decode(body);
565
579
  }
566
580
  async function readProviderJsonResponseText(response) {
567
581
  return await readProviderResponseTextWithLimit({
@@ -1,4 +1,4 @@
1
- import { n as markWebhookReplay, t as createWebhookReplayCache } from "./webhook-replay-aaP79dI8.js";
1
+ import { n as markWebhookReplay, t as createWebhookReplayCache } from "./webhook-replay-g3cDA5wr.mjs";
2
2
  import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
3
3
  import crypto from "node:crypto";
4
4
  //#region extensions/voice-call/src/providers/mock.ts
@@ -1,5 +1,5 @@
1
- import { _ as escapeXml, s as getHeader } from "./runtime-entry-D0UJiLfi.js";
2
- import { a as reconstructWebhookUrl, o as verifyPlivoWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-DS9sZa3C.js";
1
+ import { g as escapeXml, o as getHeader } from "./runtime-entry-CH-CV0Sf.mjs";
2
+ import { a as reconstructWebhookUrl, o as verifyPlivoWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-BukKSVNW.mjs";
3
3
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  import crypto from "node:crypto";
5
5
  //#region extensions/voice-call/src/providers/plivo.ts
@@ -371,6 +371,7 @@ var PlivoProvider = class PlivoProvider {
371
371
  try {
372
372
  if (this.options.publicUrl) {
373
373
  const base = new URL(this.options.publicUrl);
374
+ base.pathname = new URL(ctx.url).pathname;
374
375
  return `${base.origin}${base.pathname}`;
375
376
  }
376
377
  const u = new URL(reconstructWebhookUrl(ctx, {