@openclaw/voice-call 2026.8.1-beta.2 → 2026.8.1

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 (141) hide show
  1. package/README.md +4 -3
  2. package/dist/doctor-contract-api.js +10 -5
  3. package/dist/{guarded-json-api-DEDwgf4J.js → guarded-json-api-CQXCe4aR.js} +1 -1
  4. package/dist/index.js +390 -338
  5. package/dist/{mock-BCq0q36l.js → mock-Dv7KxYGt.js} +3 -1
  6. package/dist/{plivo-DbI17uAc.js → plivo-Ct5BT5LQ.js} +2 -2
  7. package/dist/{realtime-handler-uXR0QgVa.js → realtime-handler-CSK1A1Gg.js} +270 -144
  8. package/dist/{response-generator-D-hkzo5Y.js → response-generator-xbT37lYx.js} +59 -12
  9. package/dist/{runtime-entry-F13eJ4pp.js → runtime-entry-q02qkX8F.js} +435 -231
  10. package/dist/runtime-entry.js +1 -1
  11. package/dist/{store-path-51_oC_xn.js → store-path-Cf6k3F2U.js} +3 -10
  12. package/dist/{telnyx-sIS_WgnY.js → telnyx-C9Yizvox.js} +12 -8
  13. package/dist/{twilio-CbeVrVAl.js → twilio-BLjVv246.js} +40 -27
  14. package/node_modules/typebox/build/format/date.d.mts +1 -1
  15. package/node_modules/typebox/build/format/date.mjs +1 -1
  16. package/node_modules/typebox/build/format/date_time.d.mts +2 -2
  17. package/node_modules/typebox/build/format/date_time.mjs +3 -3
  18. package/node_modules/typebox/build/format/email.d.mts +1 -1
  19. package/node_modules/typebox/build/format/email.mjs +2 -2
  20. package/node_modules/typebox/build/format/hostname.mjs +2 -10
  21. package/node_modules/typebox/build/format/idn_email.d.mts +1 -1
  22. package/node_modules/typebox/build/format/idn_email.mjs +3 -3
  23. package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -0
  24. package/node_modules/typebox/build/format/idn_hostname.mjs +3 -11
  25. package/node_modules/typebox/build/format/idna/format/bidi.d.mts +3 -0
  26. package/node_modules/typebox/build/format/idna/format/bidi.mjs +98 -0
  27. package/node_modules/typebox/build/format/idna/format/puny.d.mts +3 -0
  28. package/node_modules/typebox/build/format/idna/format/puny.mjs +144 -0
  29. package/node_modules/typebox/build/format/idna/hostname.d.mts +1 -0
  30. package/node_modules/typebox/build/format/idna/hostname.mjs +16 -0
  31. package/node_modules/typebox/build/format/idna/idn-hostname.d.mts +1 -0
  32. package/node_modules/typebox/build/format/idna/idn-hostname.mjs +23 -0
  33. package/node_modules/typebox/build/format/idna/index.d.mts +2 -0
  34. package/node_modules/typebox/build/format/idna/index.mjs +2 -0
  35. package/node_modules/typebox/build/format/idna/label/ascii.d.mts +1 -0
  36. package/node_modules/typebox/build/format/idna/label/ascii.mjs +9 -0
  37. package/node_modules/typebox/build/format/idna/label/puny.d.mts +1 -0
  38. package/node_modules/typebox/build/format/idna/label/puny.mjs +28 -0
  39. package/node_modules/typebox/build/format/idna/label/unicode.d.mts +1 -0
  40. package/node_modules/typebox/build/format/idna/label/unicode.mjs +106 -0
  41. package/node_modules/typebox/build/format/idna/pattern/pattern.d.mts +30 -0
  42. package/node_modules/typebox/build/format/idna/pattern/pattern.mjs +70 -0
  43. package/node_modules/typebox/build/format/ipv4.d.mts +0 -1
  44. package/node_modules/typebox/build/format/ipv4.mjs +2 -32
  45. package/node_modules/typebox/build/format/ipv6.mjs +2 -61
  46. package/node_modules/typebox/build/format/iri.d.mts +2 -2
  47. package/node_modules/typebox/build/format/iri.mjs +26 -8
  48. package/node_modules/typebox/build/format/iri_reference.d.mts +2 -2
  49. package/node_modules/typebox/build/format/iri_reference.mjs +9 -55
  50. package/node_modules/typebox/build/format/regex.d.mts +1 -2
  51. package/node_modules/typebox/build/format/regex.mjs +2 -6
  52. package/node_modules/typebox/build/format/time.d.mts +2 -2
  53. package/node_modules/typebox/build/format/time.mjs +19 -14
  54. package/node_modules/typebox/build/format/uri.d.mts +1 -1
  55. package/node_modules/typebox/build/format/uri.mjs +3 -130
  56. package/node_modules/typebox/build/format/uri_reference.d.mts +1 -1
  57. package/node_modules/typebox/build/format/uri_reference.mjs +2 -3
  58. package/node_modules/typebox/build/format/uri_template.d.mts +2 -3
  59. package/node_modules/typebox/build/format/uri_template.mjs +4 -5
  60. package/node_modules/typebox/build/format/url.d.mts +3 -3
  61. package/node_modules/typebox/build/format/url.mjs +4 -5
  62. package/node_modules/typebox/build/guard/emit.d.mts +3 -1
  63. package/node_modules/typebox/build/guard/emit.mjs +12 -5
  64. package/node_modules/typebox/build/guard/guard.d.mts +8 -2
  65. package/node_modules/typebox/build/guard/guard.mjs +25 -4
  66. package/node_modules/typebox/build/guard/string.d.mts +2 -2
  67. package/node_modules/typebox/build/guard/string.mjs +7 -7
  68. package/node_modules/typebox/build/schema/engine/_context.mjs +3 -3
  69. package/node_modules/typebox/build/schema/engine/_functions.mjs +1 -2
  70. package/node_modules/typebox/build/schema/engine/_regexp.d.mts +2 -0
  71. package/node_modules/typebox/build/schema/engine/_regexp.mjs +4 -0
  72. package/node_modules/typebox/build/schema/engine/_stack.mjs +12 -13
  73. package/node_modules/typebox/build/schema/engine/additionalItems.mjs +17 -7
  74. package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +4 -3
  75. package/node_modules/typebox/build/schema/engine/contains.mjs +18 -6
  76. package/node_modules/typebox/build/schema/engine/enum.mjs +1 -1
  77. package/node_modules/typebox/build/schema/engine/items.mjs +26 -6
  78. package/node_modules/typebox/build/schema/engine/maxContains.mjs +3 -3
  79. package/node_modules/typebox/build/schema/engine/minContains.mjs +14 -4
  80. package/node_modules/typebox/build/schema/engine/not.mjs +4 -2
  81. package/node_modules/typebox/build/schema/engine/oneOf.mjs +7 -6
  82. package/node_modules/typebox/build/schema/engine/pattern.mjs +3 -2
  83. package/node_modules/typebox/build/schema/engine/patternProperties.mjs +4 -3
  84. package/node_modules/typebox/build/schema/engine/ref.mjs +1 -7
  85. package/node_modules/typebox/build/schema/engine/schema.mjs +13 -4
  86. package/node_modules/typebox/build/schema/engine/type.mjs +1 -1
  87. package/node_modules/typebox/build/schema/resolve/resolve.d.mts +4 -1
  88. package/node_modules/typebox/build/schema/resolve/resolve.mjs +202 -1
  89. package/node_modules/typebox/build/schema/static/propertyNames.d.mts +5 -0
  90. package/node_modules/typebox/build/schema/static/propertyNames.mjs +3 -0
  91. package/node_modules/typebox/build/schema/static/schema.d.mts +3 -0
  92. package/node_modules/typebox/build/schema/types/index.d.mts +0 -2
  93. package/node_modules/typebox/build/schema/types/index.mjs +0 -2
  94. package/node_modules/typebox/build/system/memory/clone.mjs +14 -11
  95. package/node_modules/typebox/build/system/memory/discard.mjs +6 -5
  96. package/node_modules/typebox/build/system/settings/settings.d.mts +9 -0
  97. package/node_modules/typebox/build/system/settings/settings.mjs +2 -0
  98. package/node_modules/typebox/build/type/engine/call/distribute_arguments.d.mts +6 -1
  99. package/node_modules/typebox/build/type/engine/call/distribute_arguments.mjs +16 -3
  100. package/node_modules/typebox/build/type/engine/call/instantiate.mjs +34 -4
  101. package/node_modules/typebox/build/type/engine/evaluate/broaden.d.mts +5 -5
  102. package/node_modules/typebox/build/type/engine/evaluate/broaden.mjs +20 -26
  103. package/node_modules/typebox/build/type/engine/evaluate/compare.d.mts +6 -10
  104. package/node_modules/typebox/build/type/engine/evaluate/compare.mjs +9 -13
  105. package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +7 -4
  106. package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +12 -19
  107. package/node_modules/typebox/build/type/engine/evaluate/distribute.d.mts +4 -20
  108. package/node_modules/typebox/build/type/engine/evaluate/distribute.mjs +8 -20
  109. package/node_modules/typebox/build/type/engine/evaluate/evaluate.d.mts +4 -4
  110. package/node_modules/typebox/build/type/engine/evaluate/evaluate.mjs +11 -8
  111. package/node_modules/typebox/build/type/engine/evaluate/flatten.d.mts +1 -1
  112. package/node_modules/typebox/build/type/engine/evaluate/flatten.mjs +3 -4
  113. package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +17 -2
  114. package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +26 -6
  115. package/node_modules/typebox/build/type/engine/exclude/operation.mjs +3 -4
  116. package/node_modules/typebox/build/type/engine/extract/operation.d.mts +1 -1
  117. package/node_modules/typebox/build/type/engine/extract/operation.mjs +3 -4
  118. package/node_modules/typebox/build/type/engine/priority/priority.d.mts +2 -2
  119. package/node_modules/typebox/build/type/engine/priority/priority.mjs +3 -4
  120. package/node_modules/typebox/build/type/script/mapping.d.mts +32 -29
  121. package/node_modules/typebox/build/type/script/mapping.mjs +18 -25
  122. package/node_modules/typebox/build/type/script/parser.d.mts +19 -25
  123. package/node_modules/typebox/build/type/script/parser.mjs +27 -23
  124. package/node_modules/typebox/build/value/errors/errors.d.mts +2 -2
  125. package/node_modules/typebox/build/value/errors/errors.mjs +1 -1
  126. package/node_modules/typebox/package.json +27 -27
  127. package/node_modules/typebox/readme.md +79 -82
  128. package/node_modules/ws/lib/permessage-deflate.js +3 -1
  129. package/node_modules/ws/package.json +1 -1
  130. package/openclaw.plugin.json +20 -2
  131. package/package.json +6 -6
  132. package/node_modules/typebox/build/format/_idna.d.mts +0 -2
  133. package/node_modules/typebox/build/format/_idna.mjs +0 -246
  134. package/node_modules/typebox/build/format/_puny.d.mts +0 -1
  135. package/node_modules/typebox/build/format/_puny.mjs +0 -77
  136. package/node_modules/typebox/build/schema/resolve/ref.d.mts +0 -3
  137. package/node_modules/typebox/build/schema/resolve/ref.mjs +0 -146
  138. package/node_modules/typebox/build/schema/types/contentEncoding.d.mts +0 -9
  139. package/node_modules/typebox/build/schema/types/contentEncoding.mjs +0 -13
  140. package/node_modules/typebox/build/schema/types/contentMediaType.d.mts +0 -9
  141. package/node_modules/typebox/build/schema/types/contentMediaType.mjs +0 -13
@@ -1,2 +1,2 @@
1
- import { t as createVoiceCallRuntime } from "./runtime-entry-F13eJ4pp.js";
1
+ import { t as createVoiceCallRuntime } from "./runtime-entry-q02qkX8F.js";
2
2
  export { createVoiceCallRuntime };
@@ -133,14 +133,7 @@ const { setRuntime: setVoiceCallStateRuntime, tryGetRuntime: getOptionalVoiceCal
133
133
  //#region extensions/voice-call/src/manager/replay-keys.ts
134
134
  /** Match the existing voice-call webhook replay-cache cardinality. */
135
135
  const MAX_MANAGER_REPLAY_KEYS = 1e4;
136
- function pruneOldestSetEntries(keys, maxEntries) {
137
- while (keys.size > maxEntries) {
138
- const oldest = keys.values().next().value;
139
- if (oldest === void 0) break;
140
- keys.delete(oldest);
141
- }
142
- }
143
- function pruneOldestMapEntries(keys, maxEntries) {
136
+ function pruneOldestEntries(keys, maxEntries) {
144
137
  while (keys.size > maxEntries) {
145
138
  const oldest = keys.keys().next().value;
146
139
  if (oldest === void 0) break;
@@ -150,7 +143,7 @@ function pruneOldestMapEntries(keys, maxEntries) {
150
143
  /** Remember one manager replay key without refreshing duplicate insertion order. */
151
144
  function rememberManagerReplayKey(keys, key, maxEntries = MAX_MANAGER_REPLAY_KEYS) {
152
145
  keys.add(key);
153
- pruneOldestSetEntries(keys, maxEntries);
146
+ pruneOldestEntries(keys, maxEntries);
154
147
  }
155
148
  /** Append one per-call replay key and retain only the newest bounded suffix. */
156
149
  function appendCallReplayKey(keys, key, maxEntries = 500) {
@@ -170,7 +163,7 @@ function reserveRejectedProviderCall(calls, providerCallId, maxEntries = MAX_MAN
170
163
  if (calls.has(providerCallId)) return;
171
164
  const reservation = Symbol(providerCallId);
172
165
  calls.set(providerCallId, reservation);
173
- pruneOldestMapEntries(calls, maxEntries);
166
+ pruneOldestEntries(calls, maxEntries);
174
167
  return reservation;
175
168
  }
176
169
  /** Release a rejected-call reservation only when the failing attempt still owns it. */
@@ -1,4 +1,4 @@
1
- import { s as verifyTelnyxWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-DEDwgf4J.js";
1
+ import { s as verifyTelnyxWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-CQXCe4aR.js";
2
2
  import crypto from "node:crypto";
3
3
  //#region extensions/voice-call/src/providers/telnyx.ts
4
4
  function normalizeTelnyxDirection(direction) {
@@ -122,13 +122,17 @@ var TelnyxProvider = class {
122
122
  type: "call.speaking",
123
123
  text: data.payload?.text || ""
124
124
  };
125
- case "call.transcription": return {
126
- ...baseEvent,
127
- type: "call.speech",
128
- transcript: data.payload?.transcription_data?.transcript ?? data.payload?.transcription ?? "",
129
- isFinal: data.payload?.transcription_data?.is_final ?? data.payload?.is_final ?? true,
130
- confidence: data.payload?.transcription_data?.confidence ?? data.payload?.confidence
131
- };
125
+ case "call.transcription": {
126
+ const transcript = data.payload?.transcription_data?.transcript ?? data.payload?.transcription ?? "";
127
+ if (!transcript.trim()) return null;
128
+ return {
129
+ ...baseEvent,
130
+ type: "call.speech",
131
+ transcript,
132
+ isFinal: data.payload?.transcription_data?.is_final ?? data.payload?.is_final ?? true,
133
+ confidence: data.payload?.transcription_data?.confidence ?? data.payload?.confidence
134
+ };
135
+ }
132
136
  case "call.hangup": return {
133
137
  ...baseEvent,
134
138
  type: "call.ended",
@@ -1,13 +1,13 @@
1
1
  import { fetchWithSsrFGuard } from "./runtime-api.js";
2
2
  import "./api.js";
3
3
  import { n as requireSupportedTwilioApiHostname, r as resolveTwilioApiBaseUrl } from "./twilio-region-4fkgz3UG.js";
4
- import { _ as escapeXml, a as normalizeProviderStatus, i as mapProviderStatusToEndReason, p as chunkAudio, r as isProviderStatusTerminal, s as getHeader, v as mapVoiceToPolly } from "./runtime-entry-F13eJ4pp.js";
5
- import { c as verifyTwilioWebhook, i as readVoiceCallProviderJsonResponse, n as cancelProviderResponseBody, r as readProviderErrorResponseSnippet, t as guardedJsonApiRequest } from "./guarded-json-api-DEDwgf4J.js";
4
+ import { _ as escapeXml, a as normalizeProviderStatus, i as mapProviderStatusToEndReason, p as chunkAudio, r as isProviderStatusTerminal, s as getHeader, v as mapVoiceToPolly } from "./runtime-entry-q02qkX8F.js";
5
+ import { c as verifyTwilioWebhook, i as readVoiceCallProviderJsonResponse, n as cancelProviderResponseBody, r as readProviderErrorResponseSnippet, t as guardedJsonApiRequest } from "./guarded-json-api-CQXCe4aR.js";
6
6
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
7
  import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
8
8
  import crypto from "node:crypto";
9
9
  import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
10
- import { setTimeout as setTimeout$1 } from "node:timers/promises";
10
+ import { retryAsync } from "openclaw/plugin-sdk/retry-runtime";
11
11
  //#region extensions/voice-call/src/providers/twilio/api.ts
12
12
  const TWILIO_API_TIMEOUT_MS = 3e4;
13
13
  /** Parse Twilio JSON error responses without trusting response shape. */
@@ -188,6 +188,7 @@ var TwilioProvider = class TwilioProvider {
188
188
  this.currentPublicUrl = null;
189
189
  this.ttsProvider = null;
190
190
  this.mediaStreamHandler = null;
191
+ this.playbackMarkSequence = 0;
191
192
  this.callStreamMap = /* @__PURE__ */ new Map();
192
193
  this.streamAuthTokens = /* @__PURE__ */ new Map();
193
194
  this.twimlStorage = /* @__PURE__ */ new Map();
@@ -261,15 +262,16 @@ var TwilioProvider = class TwilioProvider {
261
262
  });
262
263
  }
263
264
  async updateLiveCallTwiml(providerCallId, twiml, operation) {
264
- for (const retryDelayMs of TWILIO_CALL_UPDATE_RETRY_DELAYS_MS) try {
265
- await this.apiRequest(`/Calls/${providerCallId}.json`, { Twiml: twiml });
266
- return;
267
- } catch (err) {
268
- if (!isTwilioCallNotInProgressError(err)) throw err;
269
- console.warn(`[voice-call] Twilio ${operation} update hit call state race (21220); retrying in ${retryDelayMs}ms`);
270
- await setTimeout$1(retryDelayMs);
271
- }
272
- await this.apiRequest(`/Calls/${providerCallId}.json`, { Twiml: twiml });
265
+ await retryAsync(() => this.apiRequest(`/Calls/${providerCallId}.json`, { Twiml: twiml }), {
266
+ attempts: TWILIO_CALL_UPDATE_RETRY_DELAYS_MS.length + 1,
267
+ minDelayMs: 0,
268
+ shouldRetry: isTwilioCallNotInProgressError,
269
+ delayMs: ({ attempt }) => TWILIO_CALL_UPDATE_RETRY_DELAYS_MS[attempt - 1] ?? 0,
270
+ onRetry: ({ delayMs }) => {
271
+ console.warn(`[voice-call] Twilio ${operation} update hit call state race (21220); retrying in ${delayMs}ms`);
272
+ },
273
+ sleep: (delayMs) => sleepWithAbort(delayMs)
274
+ });
273
275
  }
274
276
  /**
275
277
  * Verify Twilio webhook signature using HMAC-SHA1.
@@ -301,6 +303,7 @@ var TwilioProvider = class TwilioProvider {
301
303
  dedupeKey,
302
304
  turnToken: turnTokenFromQuery
303
305
  });
306
+ if (event?.direction === "inbound" && event.type !== "call.ended" && event.providerCallId && this.currentPublicUrl && !this.callWebhookUrls.has(event.providerCallId)) this.callWebhookUrls.set(event.providerCallId, this.currentPublicUrl);
304
307
  const twiml = this.generateTwimlResponse(ctx);
305
308
  return {
306
309
  events: event ? [event] : [],
@@ -345,7 +348,7 @@ var TwilioProvider = class TwilioProvider {
345
348
  to: params.get("To") || void 0
346
349
  };
347
350
  const speechResult = params.get("SpeechResult");
348
- if (speechResult) return {
351
+ if (speechResult?.trim()) return {
349
352
  ...baseEvent,
350
353
  type: "call.speech",
351
354
  transcript: speechResult,
@@ -587,10 +590,6 @@ var TwilioProvider = class TwilioProvider {
587
590
  const raw = handler.sendAudio(streamSid, audio);
588
591
  return normalizeSendResult(raw);
589
592
  };
590
- const sendPlaybackMark = (name) => {
591
- const raw = handler.sendMark(streamSid, name);
592
- return normalizeSendResult(raw);
593
- };
594
593
  await handler.queueTts(streamSid, async (signal) => {
595
594
  const sendKeepAlive = () => {
596
595
  sendAudioChunk(SILENCE_CHUNK);
@@ -601,6 +600,7 @@ var TwilioProvider = class TwilioProvider {
601
600
  }, CHUNK_DELAY_MS);
602
601
  let muLawAudio;
603
602
  let synthTimeout = null;
603
+ let removeAbortListener = () => {};
604
604
  const synthTimeoutMs = ttsProvider.synthesisTimeoutMs;
605
605
  try {
606
606
  const synthPromise = ttsProvider.synthesizeForTelephony(text);
@@ -609,10 +609,23 @@ var TwilioProvider = class TwilioProvider {
609
609
  reject(/* @__PURE__ */ new Error(`Telephony TTS synthesis timed out after ${synthTimeoutMs}ms`));
610
610
  }, synthTimeoutMs);
611
611
  });
612
- muLawAudio = await Promise.race([synthPromise, timeoutPromise]);
612
+ const abortPromise = new Promise((_, reject) => {
613
+ const onAbort = () => {
614
+ reject(signal.reason instanceof Error ? signal.reason : /* @__PURE__ */ new Error("Telephony TTS synthesis aborted"));
615
+ };
616
+ signal.addEventListener("abort", onAbort, { once: true });
617
+ removeAbortListener = () => signal.removeEventListener("abort", onAbort);
618
+ if (signal.aborted) onAbort();
619
+ });
620
+ muLawAudio = await Promise.race([
621
+ synthPromise,
622
+ timeoutPromise,
623
+ abortPromise
624
+ ]);
613
625
  } finally {
614
626
  if (synthTimeout) clearTimeout(synthTimeout);
615
627
  clearInterval(keepAlive);
628
+ removeAbortListener();
616
629
  }
617
630
  if (muLawAudio.length === 0) throw new Error("Telephony TTS produced no audio");
618
631
  let chunkAttempts = 0;
@@ -621,7 +634,11 @@ var TwilioProvider = class TwilioProvider {
621
634
  for (const chunk of chunkAudio(muLawAudio, CHUNK_SIZE)) {
622
635
  if (signal.aborted) break;
623
636
  chunkAttempts += 1;
624
- if (sendAudioChunk(chunk).sent) chunkDelivered += 1;
637
+ if (!sendAudioChunk(chunk).sent) {
638
+ handler.clearAudio(streamSid);
639
+ throw new Error(`Telephony stream playback failed: audio chunk ${chunkAttempts} not delivered`);
640
+ }
641
+ chunkDelivered += 1;
625
642
  const waitMs = nextChunkDueAt - Date.now();
626
643
  if (waitMs > 0) try {
627
644
  await sleepWithAbort(Math.ceil(waitMs), signal);
@@ -632,14 +649,10 @@ var TwilioProvider = class TwilioProvider {
632
649
  nextChunkDueAt += CHUNK_DELAY_MS;
633
650
  if (signal.aborted) break;
634
651
  }
635
- let markSent = true;
636
- if (!signal.aborted) markSent = sendPlaybackMark(`tts-${Date.now()}`).sent;
637
- if (!signal.aborted && chunkAttempts > 0 && (chunkDelivered === 0 || !markSent)) {
638
- const failures = [];
639
- if (chunkDelivered === 0) failures.push("no audio chunks delivered");
640
- if (!markSent) failures.push("completion mark not delivered");
641
- throw new Error(`Telephony stream playback failed: ${failures.join("; ")}`);
642
- }
652
+ if (signal.aborted) return;
653
+ if (chunkAttempts === 0 || chunkDelivered !== chunkAttempts) throw new Error("Telephony stream playback failed: incomplete audio delivery");
654
+ const markName = `tts-${Date.now()}-${++this.playbackMarkSequence}`;
655
+ await handler.sendMarkAndWait(streamSid, markName, muLawAudio.length / 8, signal);
643
656
  });
644
657
  }
645
658
  /**
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Returns true if the value is a ISO8601 Date component string
3
- * @source ajv-formats
3
+ * @description https://datatracker.ietf.org/doc/html/rfc3339
4
4
  * @example `2020-12-12`
5
5
  */
6
6
  export declare function IsDate(value: string): boolean;
@@ -5,7 +5,7 @@ function IsLeapYear(year) {
5
5
  }
6
6
  /**
7
7
  * Returns true if the value is a ISO8601 Date component string
8
- * @source ajv-formats
8
+ * @description https://datatracker.ietf.org/doc/html/rfc3339
9
9
  * @example `2020-12-12`
10
10
  */
11
11
  export function IsDate(value) {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Returns true if the value is a ISO8601 DateTime string
3
- * @source ajv-formats
3
+ * @specification https://datatracker.ietf.org/doc/html/rfc3339
4
4
  * @example `2020-12-12T20:20:40+00:00`
5
5
  */
6
- export declare function IsDateTime(value: string, strictTimeZone?: boolean): boolean;
6
+ export declare function IsDateTime(value: string): boolean;
@@ -2,10 +2,10 @@ import { IsDate } from './date.mjs';
2
2
  import { IsTime } from './time.mjs';
3
3
  /**
4
4
  * Returns true if the value is a ISO8601 DateTime string
5
- * @source ajv-formats
5
+ * @specification https://datatracker.ietf.org/doc/html/rfc3339
6
6
  * @example `2020-12-12T20:20:40+00:00`
7
7
  */
8
- export function IsDateTime(value, strictTimeZone = true) {
8
+ export function IsDateTime(value) {
9
9
  const dateTime = value.split(/T/i);
10
- return dateTime.length === 2 && IsDate(dateTime[0]) && IsTime(dateTime[1], strictTimeZone);
10
+ return dateTime.length === 2 && IsDate(dateTime[0]) && IsTime(dateTime[1]);
11
11
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * Returns true if the value is an Email
3
- * @specification ajv-formats
3
+ * @specification https://datatracker.ietf.org/doc/html/rfc5322
4
4
  */
5
5
  export declare function IsEmail(value: string): boolean;
@@ -1,7 +1,7 @@
1
- const Email = /^(?!.*\.\.)[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i;
1
+ const Email = /^(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[^"\\]|\\.)*")@(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*|\[(?:IPv6:[a-f0-9:]+|(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])(?:\.(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3})\])$/i;
2
2
  /**
3
3
  * Returns true if the value is an Email
4
- * @specification ajv-formats
4
+ * @specification https://datatracker.ietf.org/doc/html/rfc5322
5
5
  */
6
6
  export function IsEmail(value) {
7
7
  return Email.test(value);
@@ -1,4 +1,4 @@
1
- import * as Idna from './_idna.mjs';
1
+ import * as Idna from './idna/index.mjs';
2
2
  /**
3
3
  * Returns true if the value is a valid hostname.
4
4
  * @specification https://tools.ietf.org/html/rfc1123
@@ -6,13 +6,5 @@ import * as Idna from './_idna.mjs';
6
6
  * @specification https://tools.ietf.org/html/rfc5892
7
7
  */
8
8
  export function IsHostname(value) {
9
- if (value.length === 0 || value.length > 253)
10
- return false;
11
- if (value.charCodeAt(value.length - 1) === 46)
12
- return false;
13
- for (const label of value.split('.')) {
14
- if (!Idna.IsLabel(label))
15
- return false;
16
- }
17
- return true;
9
+ return Idna.IsHostname(value);
18
10
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * Returns true if the value is an IdnEmail
3
- * @specification ajv-formats (unicode-extension)
3
+ * @specification https://datatracker.ietf.org/doc/html/rfc4952
4
4
  */
5
5
  export declare function IsIdnEmail(value: string): boolean;
@@ -1,8 +1,8 @@
1
- const IdnEmail = /^(?!.*\.\.)[\p{L}\p{N}!#$%&'*+/=?^_`{|}~-]+(?:\.[\p{L}\p{N}!#$%&'*+/=?^_`{|}~-]+)*@[\p{L}\p{N}](?:[\p{L}\p{N}-]{0,61}[\p{L}\p{N}])?(?:\.[\p{L}\p{N}](?:[\p{L}\p{N}-]{0,61}[\p{L}\p{N}])?)*$/iu;
1
+ const IdnEmail = /^(?:[A-Za-z0-9!#$%&'*+\/=?^_`{|}~\u{0080}-\u{10FFFF}-]+(?:\.[A-Za-z0-9!#$%&'*+\/=?^_`{|}~\u{0080}-\u{10FFFF}-]+)*|"(?:[^"\\]|\\.)*")@[\p{L}\p{N}](?:[\p{L}\p{N}-]{0,62})(?<!-)(?:\.[\p{L}\p{N}](?:[\p{L}\p{N}-]{0,62})(?<!-))*$/iu;
2
2
  /**
3
3
  * Returns true if the value is an IdnEmail
4
- * @specification ajv-formats (unicode-extension)
4
+ * @specification https://datatracker.ietf.org/doc/html/rfc4952
5
5
  */
6
6
  export function IsIdnEmail(value) {
7
- return IdnEmail.test(value);
7
+ return IdnEmail.test(value.normalize('NFC'));
8
8
  }
@@ -3,5 +3,6 @@
3
3
  * @specification https://tools.ietf.org/html/rfc3490
4
4
  * @specification https://tools.ietf.org/html/rfc5891
5
5
  * @specification https://tools.ietf.org/html/rfc5892
6
+ * @specification https://tools.ietf.org/html/rfc5893
6
7
  */
7
8
  export declare function IsIdnHostname(value: string): boolean;
@@ -1,19 +1,11 @@
1
- import { IsIdnLabel } from './_idna.mjs';
1
+ import * as Idna from './idna/index.mjs';
2
2
  /**
3
3
  * Returns true if the value is a valid internationalized (IDN) hostname.
4
4
  * @specification https://tools.ietf.org/html/rfc3490
5
5
  * @specification https://tools.ietf.org/html/rfc5891
6
6
  * @specification https://tools.ietf.org/html/rfc5892
7
+ * @specification https://tools.ietf.org/html/rfc5893
7
8
  */
8
9
  export function IsIdnHostname(value) {
9
- if (value.length === 0 || value.includes(' '))
10
- return false;
11
- const canonical = value.normalize('NFC').replace(/[\u002E\u3002\uFF0E\uFF61]/g, '.');
12
- if (canonical.length > 253)
13
- return false;
14
- for (const label of canonical.split('.')) {
15
- if (!IsIdnLabel(label))
16
- return false;
17
- }
18
- return true;
10
+ return Idna.IsIdnHostname(value);
19
11
  }
@@ -0,0 +1,3 @@
1
+ export declare function HasBidiChars(value: string): boolean;
2
+ export declare function HasRightToLeftCharacters(value: string): boolean;
3
+ export declare function SatisfiesBidiRule(value: string): boolean;
@@ -0,0 +1,98 @@
1
+ // deno-fmt-ignore-file
2
+ import * as FormatPuny from './puny.mjs';
3
+ import * as Pattern from '../pattern/pattern.mjs';
4
+ // ------------------------------------------------------------------
5
+ // LOCAL REGEX: Allowed Bidi classes per direction (RFC 5893)
6
+ // ------------------------------------------------------------------
7
+ const RE_RTL_ALLOWED = /^(?:R|AL|AN|EN|ES|CS|ET|ON|BN|NSM)$/;
8
+ const RE_LTR_ALLOWED = /^(?:L|EN|ES|CS|ET|ON|BN|NSM)$/;
9
+ const RE_RTL_CLASSES = /^(?:R|AL|AN)$/;
10
+ // ------------------------------------------------------------------
11
+ // HasBidiChars
12
+ //
13
+ // Decodes a punycode label to its Unicode form (if it is one) and
14
+ // checks whether the result is an RTL label per RFC 5893 §1.4.
15
+ // ------------------------------------------------------------------
16
+ export function HasBidiChars(value) {
17
+ if (FormatPuny.IsAcePrefixed(value)) {
18
+ try {
19
+ return HasRightToLeftCharacters(FormatPuny.Decode(value.slice(4).toLowerCase()));
20
+ }
21
+ catch {
22
+ return false;
23
+ }
24
+ }
25
+ return HasRightToLeftCharacters(value);
26
+ }
27
+ // ------------------------------------------------------------------
28
+ // GetBidiClass
29
+ // ------------------------------------------------------------------
30
+ function GetBidiClass(codePoint) {
31
+ const char = String.fromCodePoint(codePoint);
32
+ return (Pattern.RE_EUROPEAN_NUMBER.test(char) ? 'EN' :
33
+ Pattern.RE_ARABIC_INDIC_DIGIT.test(char) ? 'AN' :
34
+ // Pattern.RE_EUROPEAN_SEPARATOR.test(char) ? 'ES' : // (no-spec-coverage)
35
+ // Pattern.RE_COMMON_SEPARATOR.test(char) ? 'CS' : // (no-spec-coverage)
36
+ Pattern.RE_MARK_NONSPACING.test(char) ? 'NSM' :
37
+ Pattern.RE_SCRIPT_HEBREW.test(char) ? 'R' :
38
+ Pattern.RE_SCRIPT_ARABIC_LETTER.test(char) ? 'AL' :
39
+ Pattern.RE_LETTER.test(char) ? 'L' :
40
+ 'ON');
41
+ }
42
+ // ------------------------------------------------------------------
43
+ // HasRightToLeftCharacters
44
+ //
45
+ // Per RFC 5893 §1.4: "An RTL label is a label that contains at least
46
+ // one character of type R, AL, or AN." Used to determine whether a
47
+ // domain name is a "Bidi domain name" (RFC 5893 §1.4), in which case
48
+ // the Bidi Rule applies to every label in the domain.
49
+ // ------------------------------------------------------------------
50
+ export function HasRightToLeftCharacters(value) {
51
+ for (const ch of value)
52
+ if (RE_RTL_CLASSES.test(GetBidiClass(ch.codePointAt(0))))
53
+ return true;
54
+ return false;
55
+ }
56
+ // ------------------------------------------------------------------
57
+ // SatisfiesBidiRule
58
+ // ------------------------------------------------------------------
59
+ export function SatisfiesBidiRule(value) {
60
+ let isRtl = false;
61
+ let allowed = RE_LTR_ALLOWED;
62
+ let sawEN = false;
63
+ let sawAN = false;
64
+ let isFirst = true;
65
+ // let lastClass: string | null = null // (no-spec-coverage)
66
+ for (const ch of value) {
67
+ const bidiClass = GetBidiClass(ch.codePointAt(0));
68
+ if (isFirst) {
69
+ // 1. The first character must be a character with Bidi property L, R, or AL.
70
+ if (bidiClass !== 'L' && bidiClass !== 'R' && bidiClass !== 'AL')
71
+ return false;
72
+ isRtl = bidiClass === 'R' || bidiClass === 'AL';
73
+ allowed = isRtl ? RE_RTL_ALLOWED : RE_LTR_ALLOWED;
74
+ isFirst = false;
75
+ }
76
+ // 2 / 5. Only characters in the allowed set for this direction may appear.
77
+ if (!allowed.test(bidiClass))
78
+ return false;
79
+ if (bidiClass === 'EN')
80
+ sawEN = true;
81
+ else if (bidiClass === 'AN')
82
+ sawAN = true;
83
+ // if (bidiClass !== 'NSM') lastClass = bidiClass // (no-spec-coverage)
84
+ }
85
+ if (isRtl && sawEN && sawAN)
86
+ return false; // spec coverage path
87
+ // (no-spec-coverage)
88
+ // if (isRtl) {
89
+ // // 3. The end of the label must be R, AL, EN, or AN (ignoring trailing NSM).
90
+ // // if (lastClass !== 'R' && lastClass !== 'AL' && lastClass !== 'EN' && lastClass !== 'AN') return false
91
+ // // 4. If an EN is present, no AN may be present, and vice versa.
92
+ // if (sawEN && sawAN) return false
93
+ // } else {
94
+ // // 6. The end of the label must be L or EN (ignoring trailing NSM).
95
+ // // if (lastClass !== 'L' && lastClass !== 'EN') return false
96
+ // }
97
+ return true;
98
+ }
@@ -0,0 +1,3 @@
1
+ export declare function IsAcePrefixed(value: string): boolean;
2
+ export declare function Decode(value: string): string;
3
+ export declare function Encode(input: string): string;
@@ -0,0 +1,144 @@
1
+ import * as Pattern from '../pattern/pattern.mjs';
2
+ // ------------------------------------------------------------------
3
+ // PunyCode (RFC 3492)
4
+ // ------------------------------------------------------------------
5
+ const PUNYCODE_BASE = 36;
6
+ const PUNYCODE_TMIN = 1;
7
+ const PUNYCODE_TMAX = 26;
8
+ const PUNYCODE_SKEW = 38;
9
+ const PUNYCODE_DAMP = 700;
10
+ const PUNYCODE_INITIAL_BIAS = 72;
11
+ const PUNYCODE_INITIAL_N = 128;
12
+ // ------------------------------------------------------------------
13
+ // ThrowDontCare
14
+ // ------------------------------------------------------------------
15
+ function ThrowDontCare() {
16
+ throw null;
17
+ }
18
+ // ------------------------------------------------------------------
19
+ // IsAcePrefixed
20
+ // ------------------------------------------------------------------
21
+ export function IsAcePrefixed(value) {
22
+ return value.toLowerCase().startsWith('xn--');
23
+ }
24
+ // ------------------------------------------------------------------
25
+ // Adapt
26
+ // ------------------------------------------------------------------
27
+ function Adapt(delta, numPoints, firstTime) {
28
+ delta = firstTime ? Math.floor(delta / PUNYCODE_DAMP) : delta >> 1;
29
+ delta += Math.floor(delta / numPoints);
30
+ let k = 0;
31
+ while (delta > (((PUNYCODE_BASE - PUNYCODE_TMIN) * PUNYCODE_TMAX) >> 1)) {
32
+ delta = Math.floor(delta / (PUNYCODE_BASE - PUNYCODE_TMIN));
33
+ k += PUNYCODE_BASE;
34
+ }
35
+ return k + Math.floor(((PUNYCODE_BASE - PUNYCODE_TMIN + 1) * delta) / (delta + PUNYCODE_SKEW));
36
+ }
37
+ // ------------------------------------------------------------------
38
+ // Decode
39
+ // ------------------------------------------------------------------
40
+ export function Decode(value) {
41
+ const output = [];
42
+ let n = PUNYCODE_INITIAL_N;
43
+ let i = 0;
44
+ let bias = PUNYCODE_INITIAL_BIAS;
45
+ const delimIdx = value.lastIndexOf('-');
46
+ if (delimIdx > 0) {
47
+ for (let j = 0; j < delimIdx; j++) {
48
+ const cp = value.charCodeAt(j);
49
+ if (cp >= 128)
50
+ ThrowDontCare(); // throw new Error('Invalid punycode: non-basic before delimiter')
51
+ output.push(cp);
52
+ }
53
+ }
54
+ let inIdx = delimIdx < 0 ? 0 : delimIdx + 1;
55
+ while (inIdx < value.length) {
56
+ const oldi = i;
57
+ let w = 1;
58
+ let k = PUNYCODE_BASE;
59
+ while (true) {
60
+ if (inIdx >= value.length)
61
+ ThrowDontCare(); // throw new Error('Invalid punycode: unexpected end of input')
62
+ const ch = value.charCodeAt(inIdx++);
63
+ let digit;
64
+ if (ch >= 0x61 && ch <= 0x7a)
65
+ digit = ch - 0x61; // a-z => 0-25
66
+ else if (ch >= 0x30 && ch <= 0x39)
67
+ digit = ch - 0x30 + 26; // 0-9 => 26-35
68
+ // else if (ch >= 0x41 && ch <= 0x5a) digit = ch - 0x41 // A-Z => 0-25 // (no-spec-coverage)
69
+ else
70
+ ThrowDontCare(); // throw new Error('Invalid punycode: bad digit character')
71
+ i += digit * w;
72
+ const t = k <= bias ? PUNYCODE_TMIN : k >= bias + PUNYCODE_TMAX ? PUNYCODE_TMAX : k - bias;
73
+ if (digit < t)
74
+ break;
75
+ w *= PUNYCODE_BASE - t;
76
+ k += PUNYCODE_BASE;
77
+ }
78
+ const outLen = output.length + 1;
79
+ bias = Adapt(i - oldi, outLen, oldi === 0);
80
+ n += Math.floor(i / outLen);
81
+ i %= outLen;
82
+ output.splice(i, 0, n);
83
+ i++;
84
+ }
85
+ return String.fromCodePoint(...output);
86
+ }
87
+ // ------------------------------------------------------------------
88
+ // DigitToChar
89
+ // ------------------------------------------------------------------
90
+ function DigitToChar(digit) {
91
+ return digit < 26 ? String.fromCharCode(digit + 0x61) : String.fromCharCode(digit - 26 + 0x30);
92
+ }
93
+ // ------------------------------------------------------------------
94
+ // Encode
95
+ //
96
+ // Encodes a Unicode string into its Punycode payload (the part that
97
+ // follows the 'xn--' ACE prefix). Used to compute the true A-label
98
+ // length of a U-label, since the 63-octet label length limit (RFC
99
+ // 5891 §4.2.4) applies to the encoded form, not the code point count.
100
+ // ------------------------------------------------------------------
101
+ const RE_REPLACE_NON_ASCII = new RegExp(Pattern.RE_NON_ASCII.source, 'gu');
102
+ export function Encode(input) {
103
+ // Extract basic ASCII characters and set initial output array state
104
+ const basic = input.replace(RE_REPLACE_NON_ASCII, '');
105
+ const basicLength = basic.length;
106
+ const result = basicLength > 0 ? [basic, '-'] : [];
107
+ // Extract unicode codepoints
108
+ const codePoints = Array.from(input, (char) => char.codePointAt(0));
109
+ let n = PUNYCODE_INITIAL_N;
110
+ let delta = 0;
111
+ let bias = PUNYCODE_INITIAL_BIAS;
112
+ let handledCPCount = basicLength;
113
+ while (handledCPCount < codePoints.length) {
114
+ let m = Infinity;
115
+ for (const cp of codePoints) {
116
+ if (cp >= n && cp < m)
117
+ m = cp;
118
+ }
119
+ delta += (m - n) * (handledCPCount + 1);
120
+ n = m;
121
+ for (const cp of codePoints) {
122
+ if (cp < n)
123
+ delta++;
124
+ if (cp === n) {
125
+ let q = delta;
126
+ for (let k = PUNYCODE_BASE;; k += PUNYCODE_BASE) {
127
+ const t = k <= bias ? PUNYCODE_TMIN : k >= bias + PUNYCODE_TMAX ? PUNYCODE_TMAX : k - bias;
128
+ if (q < t)
129
+ break;
130
+ const digit = t + ((q - t) % (PUNYCODE_BASE - t));
131
+ result.push(DigitToChar(digit));
132
+ q = Math.floor((q - t) / (PUNYCODE_BASE - t));
133
+ }
134
+ result.push(DigitToChar(q));
135
+ bias = Adapt(delta, handledCPCount + 1, handledCPCount === basicLength);
136
+ delta = 0;
137
+ handledCPCount++;
138
+ }
139
+ }
140
+ delta++;
141
+ n++;
142
+ }
143
+ return result.join('');
144
+ }
@@ -0,0 +1 @@
1
+ export declare function IsHostname(value: string): boolean;
@@ -0,0 +1,16 @@
1
+ import * as LabelAscii from './label/ascii.mjs';
2
+ import * as LabelPuny from './label/puny.mjs';
3
+ function IsValidLabelLength(value) {
4
+ return value.length > 0 && value.length <= 63;
5
+ }
6
+ function IsLabel(value) {
7
+ return IsValidLabelLength(value) && (LabelPuny.IsPunyLabel(value) ||
8
+ LabelAscii.IsAsciiLabel(value));
9
+ }
10
+ export function IsHostname(value) {
11
+ if (value.length === 0 || value.length > 253)
12
+ return false;
13
+ if (value.charCodeAt(value.length - 1) === 46)
14
+ return false;
15
+ return value.split('.').every((label) => IsLabel(label));
16
+ }
@@ -0,0 +1 @@
1
+ export declare function IsIdnHostname(value: string): boolean;