@openclaw/voice-call 2026.8.1-beta.3 → 2026.8.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.
Files changed (142) hide show
  1. package/README.md +1 -1
  2. package/dist/doctor-contract-api.js +13 -7
  3. package/dist/{guarded-json-api-j4OEG0fR.js → guarded-json-api-BL8mhcJj.js} +10 -7
  4. package/dist/index.js +4 -5
  5. package/dist/{mock-Dv7KxYGt.js → mock-C57qj18W.js} +1 -1
  6. package/dist/{plivo-BbVnahnQ.js → plivo-CgYWuWVr.js} +2 -2
  7. package/dist/{realtime-handler-BaMEKbmJ.js → realtime-handler-oehCkuE2.js} +46 -31
  8. package/dist/{response-generator-tjyX4tDq.js → response-generator-BPKgHnuI.js} +2 -3
  9. package/dist/{runtime-entry-DK8kPCGC.js → runtime-entry-CJkbbEgg.js} +122 -89
  10. package/dist/runtime-entry.js +1 -1
  11. package/dist/setup-api.js +58 -38
  12. package/dist/{store-path-51_oC_xn.js → store-path-C_z9zKnA.js} +11 -21
  13. package/dist/{telnyx-DO8dFQcb.js → telnyx-BWmhDBtd.js} +1 -1
  14. package/dist/{twilio-JDt_1nlz.js → twilio-78mzH8Lk.js} +5 -4
  15. package/dist/{webhook-replay-FQrEGVSM.js → webhook-replay-CCQSoBNF.js} +1 -1
  16. package/node_modules/typebox/build/format/date.d.mts +1 -1
  17. package/node_modules/typebox/build/format/date.mjs +1 -1
  18. package/node_modules/typebox/build/format/date_time.d.mts +2 -2
  19. package/node_modules/typebox/build/format/date_time.mjs +3 -3
  20. package/node_modules/typebox/build/format/email.d.mts +1 -1
  21. package/node_modules/typebox/build/format/email.mjs +2 -2
  22. package/node_modules/typebox/build/format/hostname.mjs +2 -10
  23. package/node_modules/typebox/build/format/idn_email.d.mts +1 -1
  24. package/node_modules/typebox/build/format/idn_email.mjs +3 -3
  25. package/node_modules/typebox/build/format/idn_hostname.d.mts +1 -0
  26. package/node_modules/typebox/build/format/idn_hostname.mjs +3 -11
  27. package/node_modules/typebox/build/format/idna/format/bidi.d.mts +3 -0
  28. package/node_modules/typebox/build/format/idna/format/bidi.mjs +98 -0
  29. package/node_modules/typebox/build/format/idna/format/puny.d.mts +3 -0
  30. package/node_modules/typebox/build/format/idna/format/puny.mjs +144 -0
  31. package/node_modules/typebox/build/format/idna/hostname.d.mts +1 -0
  32. package/node_modules/typebox/build/format/idna/hostname.mjs +16 -0
  33. package/node_modules/typebox/build/format/idna/idn-hostname.d.mts +1 -0
  34. package/node_modules/typebox/build/format/idna/idn-hostname.mjs +41 -0
  35. package/node_modules/typebox/build/format/idna/index.d.mts +2 -0
  36. package/node_modules/typebox/build/format/idna/index.mjs +2 -0
  37. package/node_modules/typebox/build/format/idna/label/ascii.d.mts +1 -0
  38. package/node_modules/typebox/build/format/idna/label/ascii.mjs +9 -0
  39. package/node_modules/typebox/build/format/idna/label/puny.d.mts +1 -0
  40. package/node_modules/typebox/build/format/idna/label/puny.mjs +28 -0
  41. package/node_modules/typebox/build/format/idna/label/unicode.d.mts +1 -0
  42. package/node_modules/typebox/build/format/idna/label/unicode.mjs +106 -0
  43. package/node_modules/typebox/build/format/idna/pattern/pattern.d.mts +30 -0
  44. package/node_modules/typebox/build/format/idna/pattern/pattern.mjs +70 -0
  45. package/node_modules/typebox/build/format/ipv4.d.mts +0 -1
  46. package/node_modules/typebox/build/format/ipv4.mjs +2 -32
  47. package/node_modules/typebox/build/format/ipv6.mjs +2 -61
  48. package/node_modules/typebox/build/format/iri.d.mts +2 -2
  49. package/node_modules/typebox/build/format/iri.mjs +26 -8
  50. package/node_modules/typebox/build/format/iri_reference.d.mts +2 -2
  51. package/node_modules/typebox/build/format/iri_reference.mjs +9 -55
  52. package/node_modules/typebox/build/format/regex.d.mts +1 -2
  53. package/node_modules/typebox/build/format/regex.mjs +2 -6
  54. package/node_modules/typebox/build/format/time.d.mts +2 -2
  55. package/node_modules/typebox/build/format/time.mjs +19 -14
  56. package/node_modules/typebox/build/format/uri.d.mts +1 -1
  57. package/node_modules/typebox/build/format/uri.mjs +3 -130
  58. package/node_modules/typebox/build/format/uri_reference.d.mts +1 -1
  59. package/node_modules/typebox/build/format/uri_reference.mjs +2 -3
  60. package/node_modules/typebox/build/format/uri_template.d.mts +2 -3
  61. package/node_modules/typebox/build/format/uri_template.mjs +4 -5
  62. package/node_modules/typebox/build/format/url.d.mts +3 -3
  63. package/node_modules/typebox/build/format/url.mjs +4 -5
  64. package/node_modules/typebox/build/guard/emit.d.mts +3 -1
  65. package/node_modules/typebox/build/guard/emit.mjs +12 -5
  66. package/node_modules/typebox/build/guard/guard.d.mts +8 -2
  67. package/node_modules/typebox/build/guard/guard.mjs +25 -4
  68. package/node_modules/typebox/build/guard/string.d.mts +2 -2
  69. package/node_modules/typebox/build/guard/string.mjs +7 -7
  70. package/node_modules/typebox/build/schema/engine/_context.mjs +3 -3
  71. package/node_modules/typebox/build/schema/engine/_functions.mjs +1 -2
  72. package/node_modules/typebox/build/schema/engine/_regexp.d.mts +2 -0
  73. package/node_modules/typebox/build/schema/engine/_regexp.mjs +4 -0
  74. package/node_modules/typebox/build/schema/engine/_stack.mjs +12 -13
  75. package/node_modules/typebox/build/schema/engine/additionalItems.mjs +17 -7
  76. package/node_modules/typebox/build/schema/engine/additionalProperties.mjs +4 -3
  77. package/node_modules/typebox/build/schema/engine/contains.mjs +18 -6
  78. package/node_modules/typebox/build/schema/engine/enum.mjs +1 -1
  79. package/node_modules/typebox/build/schema/engine/items.mjs +26 -6
  80. package/node_modules/typebox/build/schema/engine/maxContains.mjs +3 -3
  81. package/node_modules/typebox/build/schema/engine/minContains.mjs +14 -4
  82. package/node_modules/typebox/build/schema/engine/not.mjs +4 -2
  83. package/node_modules/typebox/build/schema/engine/oneOf.mjs +7 -6
  84. package/node_modules/typebox/build/schema/engine/pattern.mjs +3 -2
  85. package/node_modules/typebox/build/schema/engine/patternProperties.mjs +4 -3
  86. package/node_modules/typebox/build/schema/engine/ref.mjs +1 -7
  87. package/node_modules/typebox/build/schema/engine/schema.mjs +13 -4
  88. package/node_modules/typebox/build/schema/engine/type.mjs +1 -1
  89. package/node_modules/typebox/build/schema/resolve/resolve.d.mts +4 -1
  90. package/node_modules/typebox/build/schema/resolve/resolve.mjs +202 -1
  91. package/node_modules/typebox/build/schema/static/propertyNames.d.mts +5 -0
  92. package/node_modules/typebox/build/schema/static/propertyNames.mjs +3 -0
  93. package/node_modules/typebox/build/schema/static/schema.d.mts +3 -0
  94. package/node_modules/typebox/build/schema/types/index.d.mts +0 -2
  95. package/node_modules/typebox/build/schema/types/index.mjs +0 -2
  96. package/node_modules/typebox/build/system/memory/clone.mjs +14 -11
  97. package/node_modules/typebox/build/system/memory/discard.mjs +6 -5
  98. package/node_modules/typebox/build/system/settings/settings.d.mts +9 -0
  99. package/node_modules/typebox/build/system/settings/settings.mjs +2 -0
  100. package/node_modules/typebox/build/type/engine/call/distribute_arguments.d.mts +6 -1
  101. package/node_modules/typebox/build/type/engine/call/distribute_arguments.mjs +16 -3
  102. package/node_modules/typebox/build/type/engine/call/instantiate.mjs +34 -4
  103. package/node_modules/typebox/build/type/engine/evaluate/broaden.d.mts +5 -5
  104. package/node_modules/typebox/build/type/engine/evaluate/broaden.mjs +20 -26
  105. package/node_modules/typebox/build/type/engine/evaluate/compare.d.mts +6 -10
  106. package/node_modules/typebox/build/type/engine/evaluate/compare.mjs +9 -13
  107. package/node_modules/typebox/build/type/engine/evaluate/composite.d.mts +7 -4
  108. package/node_modules/typebox/build/type/engine/evaluate/composite.mjs +12 -19
  109. package/node_modules/typebox/build/type/engine/evaluate/distribute.d.mts +4 -20
  110. package/node_modules/typebox/build/type/engine/evaluate/distribute.mjs +8 -20
  111. package/node_modules/typebox/build/type/engine/evaluate/evaluate.d.mts +4 -4
  112. package/node_modules/typebox/build/type/engine/evaluate/evaluate.mjs +11 -8
  113. package/node_modules/typebox/build/type/engine/evaluate/flatten.d.mts +1 -1
  114. package/node_modules/typebox/build/type/engine/evaluate/flatten.mjs +3 -4
  115. package/node_modules/typebox/build/type/engine/evaluate/narrow.d.mts +17 -2
  116. package/node_modules/typebox/build/type/engine/evaluate/narrow.mjs +26 -6
  117. package/node_modules/typebox/build/type/engine/exclude/operation.mjs +3 -4
  118. package/node_modules/typebox/build/type/engine/extract/operation.d.mts +1 -1
  119. package/node_modules/typebox/build/type/engine/extract/operation.mjs +3 -4
  120. package/node_modules/typebox/build/type/engine/priority/priority.d.mts +2 -2
  121. package/node_modules/typebox/build/type/engine/priority/priority.mjs +3 -4
  122. package/node_modules/typebox/build/type/script/mapping.d.mts +32 -29
  123. package/node_modules/typebox/build/type/script/mapping.mjs +18 -25
  124. package/node_modules/typebox/build/type/script/parser.d.mts +19 -25
  125. package/node_modules/typebox/build/type/script/parser.mjs +27 -23
  126. package/node_modules/typebox/build/value/errors/errors.d.mts +2 -2
  127. package/node_modules/typebox/build/value/errors/errors.mjs +1 -1
  128. package/node_modules/typebox/package.json +27 -27
  129. package/node_modules/typebox/readme.md +79 -82
  130. package/node_modules/ws/lib/permessage-deflate.js +3 -1
  131. package/node_modules/ws/package.json +1 -1
  132. package/package.json +9 -7
  133. package/node_modules/typebox/build/format/_idna.d.mts +0 -2
  134. package/node_modules/typebox/build/format/_idna.mjs +0 -246
  135. package/node_modules/typebox/build/format/_puny.d.mts +0 -1
  136. package/node_modules/typebox/build/format/_puny.mjs +0 -77
  137. package/node_modules/typebox/build/schema/resolve/ref.d.mts +0 -3
  138. package/node_modules/typebox/build/schema/resolve/ref.mjs +0 -146
  139. package/node_modules/typebox/build/schema/types/contentEncoding.d.mts +0 -9
  140. package/node_modules/typebox/build/schema/types/contentEncoding.mjs +0 -13
  141. package/node_modules/typebox/build/schema/types/contentMediaType.d.mts +0 -9
  142. package/node_modules/typebox/build/schema/types/contentMediaType.mjs +0 -13
@@ -1,7 +1,7 @@
1
1
  import { TtsConfigSchema, isBlockedHostnameOrIp, isRequestBodyLimitError, readRequestBodyWithLimit, requestBodyErrorToText } from "./runtime-api.js";
2
2
  import "./api.js";
3
3
  import { t as TWILIO_REGIONS } from "./twilio-region-4fkgz3UG.js";
4
- import { _ as releaseRejectedProviderCall, b as setVoiceCallStateRuntime, d as loadActiveCallsFromStore, g as appendCallReplayKey, l as findCallMatchesInStore, p as persistCallRecord, t as resolveDefaultVoiceCallStoreDir, u as getCallHistoryFromStore, v as rememberManagerReplayKey, x as TerminalStates, y as reserveRejectedProviderCall } from "./store-path-51_oC_xn.js";
4
+ import { _ as releaseRejectedProviderCall, b as setVoiceCallStateRuntime, d as loadActiveCallsFromStore, g as appendCallReplayKey, l as findCallInStore, p as persistCallRecord, t as resolveDefaultVoiceCallStoreDir, u as getCallHistoryFromStore, v as rememberManagerReplayKey, x as TerminalStates, y as reserveRejectedProviderCall } from "./store-path-C_z9zKnA.js";
5
5
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
6
6
  import { isLoopbackHost } from "openclaw/plugin-sdk/gateway-runtime";
7
7
  import { normalizeAgentId, parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
@@ -1077,8 +1077,8 @@ async function initiateCall(ctx, to, sessionKey, options) {
1077
1077
  ...requesterSessionKey ? { requesterSessionKey } : {}
1078
1078
  }
1079
1079
  };
1080
- ctx.activeCalls.set(callId, callRecord);
1081
1080
  persistCallRecord(ctx.storePath, callRecord);
1081
+ ctx.activeCalls.set(callId, callRecord);
1082
1082
  try {
1083
1083
  let inlineTwiml;
1084
1084
  let preConnectTwiml;
@@ -1109,10 +1109,12 @@ async function initiateCall(ctx, to, sessionKey, options) {
1109
1109
  streamAuthToken: streamSession.token
1110
1110
  } : {}
1111
1111
  });
1112
- callRecord.providerCallId = result.providerCallId;
1113
- ctx.providerCallIdMap.set(result.providerCallId, callId);
1114
- persistCallRecord(ctx.storePath, callRecord);
1115
- console.log(`[voice-call] Outbound call initiated: callId=${callId} providerCallId=${result.providerCallId} mode=${mode} preConnectDtmf=${preConnectTwiml ? "yes" : "no"} initialMessage=${initialMessage ? "yes" : "no"}`);
1112
+ if (ctx.activeCalls.get(callId) === callRecord && !callRecord.providerCallId) {
1113
+ callRecord.providerCallId = result.providerCallId;
1114
+ ctx.providerCallIdMap.set(result.providerCallId, callId);
1115
+ persistCallRecord(ctx.storePath, callRecord);
1116
+ }
1117
+ console.log(`[voice-call] Outbound call initiated: callId=${callId} providerCallId=${callRecord.providerCallId ?? result.providerCallId} mode=${mode} preConnectDtmf=${preConnectTwiml ? "yes" : "no"} initialMessage=${initialMessage ? "yes" : "no"}`);
1116
1118
  return {
1117
1119
  callId,
1118
1120
  success: true
@@ -1442,7 +1444,22 @@ function processEvent(ctx, event) {
1442
1444
  providerCallIdMap: ctx.providerCallIdMap,
1443
1445
  callIdOrProviderCallId: event.callId
1444
1446
  });
1445
- const providerCallId = event.providerCallId;
1447
+ let providerCallId = event.providerCallId;
1448
+ let retained;
1449
+ if (!call) {
1450
+ retained = findCallInStore(ctx.storePath, event.callId);
1451
+ if (!retained && providerCallId && providerCallId !== event.callId) retained = findCallInStore(ctx.storePath, providerCallId);
1452
+ if (retained && retained.metadata?.rejectionReason !== "inbound-policy") {
1453
+ call = ctx.activeCalls.get(retained.callId);
1454
+ if (!call) return TerminalStates.has(retained.state) ? { kind: "ignored" } : {
1455
+ kind: "ignored",
1456
+ replayable: true
1457
+ };
1458
+ }
1459
+ }
1460
+ if (call && providerCallId && providerCallId !== call.providerCallId) {
1461
+ if ((providerCallId === event.callId && retained ? retained : findCallInStore(ctx.storePath, providerCallId))?.callId === call.callId) providerCallId = call.providerCallId;
1462
+ }
1446
1463
  const eventDirection = event.direction === "inbound" || event.direction === "outbound" ? event.direction : void 0;
1447
1464
  if (!call && providerCallId && eventDirection) {
1448
1465
  if (eventDirection === "inbound" && !shouldAcceptInbound(ctx.config, event.from)) {
@@ -1514,14 +1531,14 @@ function processEvent(ctx, event) {
1514
1531
  }
1515
1532
  };
1516
1533
  const publishProviderCallId = (terminal = false) => {
1517
- if (!event.providerCallId || event.providerCallId === previousCall.providerCallId) return;
1518
- if (!terminal) ctx.providerCallIdMap.set(event.providerCallId, activeCall.callId);
1534
+ if (!providerCallId || providerCallId === previousCall.providerCallId) return;
1535
+ if (!terminal) ctx.providerCallIdMap.set(providerCallId, activeCall.callId);
1519
1536
  if (previousCall.providerCallId) {
1520
1537
  if (ctx.providerCallIdMap.get(previousCall.providerCallId) === activeCall.callId) ctx.providerCallIdMap.delete(previousCall.providerCallId);
1521
1538
  }
1522
1539
  };
1523
1540
  try {
1524
- if (event.providerCallId && event.providerCallId !== activeCall.providerCallId) activeCall.providerCallId = event.providerCallId;
1541
+ if (providerCallId && providerCallId !== activeCall.providerCallId) activeCall.providerCallId = providerCallId;
1525
1542
  if (shouldCommitReplayKey) appendCallReplayKey(activeCall.processedEventIds, dedupeKey);
1526
1543
  switch (event.type) {
1527
1544
  case "call.initiated": {
@@ -1587,6 +1604,7 @@ function processEvent(ctx, event) {
1587
1604
  resolveTranscriptWaiter(ctx, activeCall.callId, event.transcript, event.turnToken);
1588
1605
  });
1589
1606
  }
1607
+ if (event.transcript.trim()) effects.push(() => ctx.onCallerSpeech?.(activeCall));
1590
1608
  prepareLiveDurationTimer();
1591
1609
  transitionState(activeCall, "listening");
1592
1610
  break;
@@ -1619,7 +1637,6 @@ function processEvent(ctx, event) {
1619
1637
  kind: "processed",
1620
1638
  replayable: true
1621
1639
  };
1622
- break;
1623
1640
  }
1624
1641
  persistCallRecord(ctx.storePath, activeCall);
1625
1642
  publishProviderCallId();
@@ -1646,7 +1663,7 @@ function resolveUserPath(input) {
1646
1663
  const trimmed = input.trim();
1647
1664
  if (!trimmed) return trimmed;
1648
1665
  if (trimmed.startsWith("~")) {
1649
- const expanded = trimmed.replace(/^~(?=$|[\\/])/, os.homedir());
1666
+ const expanded = trimmed.replace(/^~(?=$|[\\/])/, () => os.homedir());
1650
1667
  return path.resolve(expanded);
1651
1668
  }
1652
1669
  return path.resolve(trimmed);
@@ -1686,6 +1703,7 @@ var CallManager = class {
1686
1703
  this.transcriptWaiters = /* @__PURE__ */ new Map();
1687
1704
  this.maxDurationTimers = /* @__PURE__ */ new Map();
1688
1705
  this.initialMessageInFlight = /* @__PURE__ */ new Set();
1706
+ this.autoResponseOwners = /* @__PURE__ */ new WeakMap();
1689
1707
  this.config = config;
1690
1708
  this.coreSession = coreSession;
1691
1709
  this.storePath = resolveDefaultStoreBase(config, storePath);
@@ -1857,6 +1875,7 @@ var CallManager = class {
1857
1875
  transcriptWaiters: this.transcriptWaiters,
1858
1876
  maxDurationTimers: this.maxDurationTimers,
1859
1877
  initialMessageInFlight: this.initialMessageInFlight,
1878
+ onCallerSpeech: (call) => this.invalidateAutoResponse(call),
1860
1879
  onCallAnswered: (call) => {
1861
1880
  this.maybeSpeakInitialMessageOnAnswered(call);
1862
1881
  },
@@ -1869,6 +1888,19 @@ var CallManager = class {
1869
1888
  processEvent(event) {
1870
1889
  return processEvent(this.getContext(), event);
1871
1890
  }
1891
+ createAutoResponseGuard(call) {
1892
+ const owner = Symbol("automatic response");
1893
+ this.autoResponseOwners.set(call, owner);
1894
+ return {
1895
+ isCurrent: () => this.activeCalls.get(call.callId) === call && !TerminalStates.has(call.state) && this.autoResponseOwners.get(call) === owner,
1896
+ release: () => {
1897
+ if (this.autoResponseOwners.get(call) === owner) this.autoResponseOwners.delete(call);
1898
+ }
1899
+ };
1900
+ }
1901
+ invalidateAutoResponse(call) {
1902
+ this.autoResponseOwners.delete(call);
1903
+ }
1872
1904
  shouldDeferConversationInitialMessageUntilStreamConnect() {
1873
1905
  if (!this.provider || this.provider.name !== "twilio" || !this.config.streaming.enabled) return false;
1874
1906
  const streamAwareProvider = this.provider;
@@ -1913,8 +1945,7 @@ var CallManager = class {
1913
1945
  async getCallFromMemoryOrStore(callId) {
1914
1946
  const active = this.getCall(callId) ?? this.getCallByProviderCallId(callId);
1915
1947
  if (active) return active;
1916
- const persisted = await findCallMatchesInStore(this.storePath, callId);
1917
- return persisted.byCallId ?? persisted.byProviderCallId;
1948
+ return findCallInStore(this.storePath, callId);
1918
1949
  }
1919
1950
  /**
1920
1951
  * Get call history (from persisted logs).
@@ -2106,7 +2137,7 @@ function formatBoundedChildOutput(output) {
2106
2137
  }
2107
2138
  //#endregion
2108
2139
  //#region extensions/voice-call/src/webhook/tailscale.ts
2109
- const TAILSCALE_COMMAND_STDOUT_MAX_BYTES = 4 * 1024 * 1024;
2140
+ const TAILSCALE_COMMAND_STDOUT_MAX_BYTES = 4194304;
2110
2141
  const DEFAULT_TAILSCALE_HTTPS_PORT = 443;
2111
2142
  function buildTailscaleExposureArgs(opts) {
2112
2143
  if (!opts.localUrl && opts.port === DEFAULT_TAILSCALE_HTTPS_PORT) return [
@@ -2240,7 +2271,6 @@ async function cleanupTailscaleExposure(config) {
2240
2271
  //#region extensions/voice-call/src/tunnel.ts
2241
2272
  const NGROK_LOG_BUFFER_MAX_CHARS = 16384;
2242
2273
  const NGROK_ERROR_MARKER = "ERR_NGROK";
2243
- const TUNNEL_COMMAND_OUTPUT_MAX_BYTES = 16384;
2244
2274
  const NGROK_STOP_GRACE_MS = 2e3;
2245
2275
  const NGROK_FORCE_KILL_WAIT_MS = 1e3;
2246
2276
  async function terminateNgrokProcess(proc, isClosed) {
@@ -2280,11 +2310,6 @@ function listenForChildStreamErrors(proc, onError) {
2280
2310
  * // Later: await tunnel.stop();
2281
2311
  */
2282
2312
  async function startNgrokTunnel(config) {
2283
- if (config.authToken) await runNgrokCommand([
2284
- "config",
2285
- "add-authtoken",
2286
- config.authToken
2287
- ]);
2288
2313
  const args = [
2289
2314
  "http",
2290
2315
  String(config.port),
@@ -2295,11 +2320,17 @@ async function startNgrokTunnel(config) {
2295
2320
  ];
2296
2321
  if (config.domain) args.push("--domain", config.domain);
2297
2322
  return new Promise((resolve, reject) => {
2298
- const proc = spawn("ngrok", args, { stdio: [
2299
- "ignore",
2300
- "pipe",
2301
- "pipe"
2302
- ] });
2323
+ const proc = spawn("ngrok", args, {
2324
+ stdio: [
2325
+ "ignore",
2326
+ "pipe",
2327
+ "pipe"
2328
+ ],
2329
+ ...config.authToken ? { env: {
2330
+ ...process.env,
2331
+ NGROK_AUTHTOKEN: config.authToken
2332
+ } } : {}
2333
+ });
2303
2334
  let startupSettled = false;
2304
2335
  let childClosed = false;
2305
2336
  let publicUrl = null;
@@ -2372,27 +2403,6 @@ async function startNgrokTunnel(config) {
2372
2403
  });
2373
2404
  }
2374
2405
  /**
2375
- * Run an ngrok command and wait for completion.
2376
- */
2377
- async function runNgrokCommand(args) {
2378
- const result = await runCommandWithTimeout(["ngrok", ...args], {
2379
- killProcessTree: true,
2380
- maxOutputBytes: TUNNEL_COMMAND_OUTPUT_MAX_BYTES,
2381
- outputCapture: "tail",
2382
- timeoutMs: 3e4
2383
- });
2384
- if (result.termination === "timeout") throw new Error("ngrok command timed out");
2385
- if (result.code === 0) return result.stdout;
2386
- const output = result.stderr ? {
2387
- text: result.stderr,
2388
- truncated: Boolean(result.stderrTruncatedBytes)
2389
- } : {
2390
- text: result.stdout,
2391
- truncated: Boolean(result.stdoutTruncatedBytes)
2392
- };
2393
- throw new Error(`ngrok command failed: ${formatBoundedChildOutput(output)}`);
2394
- }
2395
- /**
2396
2406
  * Start a Tailscale serve/funnel tunnel.
2397
2407
  */
2398
2408
  async function startTailscaleTunnel(config) {
@@ -2527,8 +2537,8 @@ const DEFAULT_PRE_START_TIMEOUT_MS = 5e3;
2527
2537
  const DEFAULT_MAX_PENDING_CONNECTIONS = 32;
2528
2538
  const DEFAULT_MAX_PENDING_CONNECTIONS_PER_IP = 4;
2529
2539
  const DEFAULT_MAX_CONNECTIONS = 128;
2530
- const MAX_INBOUND_MESSAGE_BYTES = 64 * 1024;
2531
- const MAX_WS_BUFFERED_BYTES = 1024 * 1024;
2540
+ const MAX_INBOUND_MESSAGE_BYTES = 65536;
2541
+ const MAX_WS_BUFFERED_BYTES = 1048576;
2532
2542
  const MAX_PENDING_TTS_OPERATIONS_PER_STREAM = 8;
2533
2543
  const MAX_IGNORED_PLAYBACK_MARKS_PER_STREAM = 64;
2534
2544
  const PLAYBACK_MARK_TIMEOUT_GRACE_MS = 2e3;
@@ -2691,10 +2701,7 @@ var MediaStreamHandler = class {
2691
2701
  session = null;
2692
2702
  }
2693
2703
  break;
2694
- case "mark":
2695
- if (session && message.mark?.name) this.acknowledgePlaybackMark(session.streamSid, message.mark.name);
2696
- break;
2697
- case "clear": break;
2704
+ case "mark": if (session && message.mark?.name) this.acknowledgePlaybackMark(session.streamSid, message.mark.name);
2698
2705
  }
2699
2706
  } catch (error) {
2700
2707
  console.error("[MediaStream] Error processing message:", error);
@@ -2756,7 +2763,7 @@ var MediaStreamHandler = class {
2756
2763
  role: "user"
2757
2764
  }
2758
2765
  });
2759
- this.config.onPartialTranscript?.(callSid, partial);
2766
+ this.config.onPartialTranscript?.(callSid, partial, streamSid);
2760
2767
  },
2761
2768
  onTranscript: (transcript) => {
2762
2769
  const session = this.sessions.get(streamSid);
@@ -2783,12 +2790,12 @@ var MediaStreamHandler = class {
2783
2790
  }
2784
2791
  });
2785
2792
  }
2786
- this.config.onTranscript?.(callSid, transcript);
2793
+ this.config.onTranscript?.(callSid, transcript, streamSid);
2787
2794
  },
2788
2795
  onSpeechStart: () => {
2789
2796
  const session = this.sessions.get(streamSid);
2790
2797
  if (session) this.ensureActiveTurn(session);
2791
- this.config.onSpeechStart?.(callSid);
2798
+ this.config.onSpeechStart?.(callSid, streamSid);
2792
2799
  },
2793
2800
  onError: (error) => {
2794
2801
  console.warn("[MediaStream] Transcription session error:", error.message);
@@ -3362,7 +3369,7 @@ const MAX_WEBHOOK_BODY_BYTES = WEBHOOK_BODY_READ_DEFAULTS.preAuth.maxBytes;
3362
3369
  const WEBHOOK_BODY_TIMEOUT_MS = WEBHOOK_BODY_READ_DEFAULTS.preAuth.timeoutMs;
3363
3370
  const MISSING_REMOTE_ADDRESS_IN_FLIGHT_KEY = "__voice_call_no_remote__";
3364
3371
  const loadRealtimeTranscriptionRuntime = createLazyRuntimeModule(() => import("./realtime-transcription.runtime-CbJAs5t_.js"));
3365
- const loadResponseGeneratorModule = createLazyRuntimeModule(() => import("./response-generator-tjyX4tDq.js"));
3372
+ const loadResponseGeneratorModule = createLazyRuntimeModule(() => import("./response-generator-BPKgHnuI.js"));
3366
3373
  function appendRecentTalkEventMetadata(call, event) {
3367
3374
  const metadata = call.metadata ?? {};
3368
3375
  const recent = Array.isArray(metadata.recentTalkEvents) ? metadata.recentTalkEvents.filter((entry) => Boolean(entry) && typeof entry === "object" && !Array.isArray(entry)) : [];
@@ -3419,7 +3426,7 @@ function buildTwilioReplayTwiML() {
3419
3426
  body: "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Response></Response>"
3420
3427
  };
3421
3428
  }
3422
- const WEBHOOK_REPLAY_RESPONSE_TTL_MS = 600 * 1e3;
3429
+ const WEBHOOK_REPLAY_RESPONSE_TTL_MS = 6e5;
3423
3430
  const WEBHOOK_REPLAY_RESPONSE_MAX_ENTRIES = 1e4;
3424
3431
  const WEBHOOK_REPLAY_RESPONSE_PRUNE_INTERVAL = 64;
3425
3432
  function cloneWebhookResponsePayload(payload) {
@@ -3516,6 +3523,21 @@ var VoiceCallWebhookServer = class {
3516
3523
  if ((call.metadata?.mode ?? "conversation") !== "conversation") return false;
3517
3524
  return (normalizeOptionalString(call.metadata?.initialMessage) ?? "").length > 0;
3518
3525
  }
3526
+ getCurrentStream(providerCallId, streamSid) {
3527
+ if (this.provider.name !== "twilio") return;
3528
+ const provider = this.provider;
3529
+ const call = this.manager.getCallByProviderCallId(providerCallId);
3530
+ return call && provider.hasRegisteredStream(providerCallId, streamSid) ? {
3531
+ call,
3532
+ provider
3533
+ } : void 0;
3534
+ }
3535
+ interruptStreamReply(providerCallId, streamSid) {
3536
+ const current = this.getCurrentStream(providerCallId, streamSid);
3537
+ if (!current || this.shouldSuppressBargeInForInitialMessage(current.call)) return;
3538
+ this.manager.invalidateAutoResponse(current.call);
3539
+ current.provider.clearTtsQueue(providerCallId);
3540
+ }
3519
3541
  /**
3520
3542
  * Initialize media streaming with the selected realtime transcription provider.
3521
3543
  */
@@ -3529,7 +3551,7 @@ var VoiceCallWebhookServer = class {
3529
3551
  cfg: pluginConfig,
3530
3552
  cfgForResolve: pluginConfig ?? {},
3531
3553
  getConfiguredProvider: (providerId) => getRealtimeTranscriptionProvider(providerId, pluginConfig),
3532
- listProviders: () => listRealtimeTranscriptionProviders(pluginConfig),
3554
+ listProviders: () => listRealtimeTranscriptionProviders(pluginConfig, Object.keys(streaming.providers)),
3533
3555
  resolveProviderConfig: ({ provider, cfg, rawConfig }) => provider.resolveConfig?.({
3534
3556
  cfg,
3535
3557
  rawConfig
@@ -3572,18 +3594,19 @@ var VoiceCallWebhookServer = class {
3572
3594
  }
3573
3595
  return true;
3574
3596
  },
3575
- onTranscript: (providerCallId, transcript) => {
3597
+ onTranscript: (providerCallId, transcript, streamSid) => {
3576
3598
  this.logger.info(`Transcript received ${providerCallId} chars=${transcript.length}`);
3577
- const call = this.manager.getCallByProviderCallId(providerCallId);
3578
- if (!call) {
3579
- this.logger.warn(`No active call found for provider ID: ${providerCallId}`);
3599
+ const current = this.getCurrentStream(providerCallId, streamSid);
3600
+ if (!current) {
3601
+ this.logger.info(`Ignoring transcript from inactive stream ${streamSid} (${providerCallId})`);
3580
3602
  return;
3581
3603
  }
3604
+ const { call, provider: streamProvider } = current;
3582
3605
  if (this.shouldSuppressBargeInForInitialMessage(call)) {
3583
3606
  this.logger.info(`Ignoring barge transcript while initial message is still playing (${providerCallId})`);
3584
3607
  return;
3585
3608
  }
3586
- if (this.provider.name === "twilio") this.provider.clearTtsQueue(providerCallId);
3609
+ streamProvider.clearTtsQueue(providerCallId);
3587
3610
  const event = {
3588
3611
  id: `stream-transcript-${Date.now()}`,
3589
3612
  type: "call.speech",
@@ -3595,32 +3618,36 @@ var VoiceCallWebhookServer = class {
3595
3618
  };
3596
3619
  this.processEventWithAutoResponse(event);
3597
3620
  },
3598
- onSpeechStart: (providerCallId) => {
3599
- if (this.provider.name !== "twilio") return;
3600
- const call = this.manager.getCallByProviderCallId(providerCallId);
3601
- if (this.shouldSuppressBargeInForInitialMessage(call)) return;
3602
- this.provider.clearTtsQueue(providerCallId);
3603
- },
3604
- onPartialTranscript: (callId, partial) => {
3621
+ onSpeechStart: (providerCallId, streamSid) => this.interruptStreamReply(providerCallId, streamSid),
3622
+ onPartialTranscript: (callId, partial, streamSid) => {
3605
3623
  this.logger.info(`Partial transcript ${callId} chars=${partial.length}`);
3624
+ this.interruptStreamReply(callId, streamSid);
3606
3625
  },
3607
- onTalkEvent: (providerCallId, _streamSid, event) => {
3608
- const call = this.manager.getCallByProviderCallId(providerCallId);
3609
- if (call) appendRecentTalkEventMetadata(call, event);
3626
+ onTalkEvent: (providerCallId, streamSid, event) => {
3627
+ const current = this.getCurrentStream(providerCallId, streamSid);
3628
+ if (current) appendRecentTalkEventMetadata(current.call, event);
3610
3629
  },
3611
3630
  onConnect: (callId, streamSid) => {
3612
3631
  this.logger.info(`Media stream connected: ${callId} -> ${streamSid}`);
3613
3632
  this.streamDisconnectGrace.connect(callId, streamSid);
3633
+ const call = this.manager.getCallByProviderCallId(callId);
3634
+ if (call) this.manager.invalidateAutoResponse(call);
3614
3635
  if (this.provider.name === "twilio") this.provider.registerCallStream(callId, streamSid);
3615
3636
  },
3616
- onTranscriptionReady: (callId) => {
3637
+ onTranscriptionReady: (callId, streamSid) => {
3638
+ if (!this.getCurrentStream(callId, streamSid)) return;
3617
3639
  this.manager.speakInitialMessage(callId).catch((err) => {
3618
3640
  this.logger.warn(`Failed to speak initial message: ${String(err)}`);
3619
3641
  });
3620
3642
  },
3621
3643
  onDisconnect: (callId, streamSid) => {
3622
3644
  this.logger.info(`Media stream disconnected: ${callId} (${streamSid})`);
3623
- if (this.provider.name === "twilio") this.provider.unregisterCallStream(callId, streamSid);
3645
+ if (this.provider.name === "twilio") {
3646
+ const twilio = this.provider;
3647
+ twilio.unregisterCallStream(callId, streamSid);
3648
+ const call = this.manager.getCallByProviderCallId(callId);
3649
+ if (call && !twilio.hasRegisteredStream(callId)) this.manager.invalidateAutoResponse(call);
3650
+ }
3624
3651
  this.streamDisconnectGrace.disconnect(callId, streamSid);
3625
3652
  }
3626
3653
  };
@@ -4018,8 +4045,18 @@ var VoiceCallWebhookServer = class {
4018
4045
  this.logger.warn("Agent runtime missing; skipping auto-response");
4019
4046
  return;
4020
4047
  }
4048
+ const response = this.manager.createAutoResponseGuard(call);
4049
+ const speakResponse = async (text) => {
4050
+ if (!response.isCurrent()) {
4051
+ this.logger.info(`Discarding superseded automatic reply ${callId}`);
4052
+ return false;
4053
+ }
4054
+ this.logger.info(`AI response queued ${callId} chars=${text.length}`);
4055
+ return (await this.manager.speak(callId, text, { listenAfterPlayback: true })).success;
4056
+ };
4021
4057
  try {
4022
4058
  const { generateVoiceResponse } = await loadResponseGeneratorModule();
4059
+ if (!response.isCurrent()) return;
4023
4060
  const numberRouteKey = resolveVoiceCallNumberRouteKeyForCall(call);
4024
4061
  const effectiveConfig = resolveVoiceCallEffectiveConfig(this.config, numberRouteKey).config;
4025
4062
  const result = await generateVoiceResponse({
@@ -4033,21 +4070,17 @@ var VoiceCallWebhookServer = class {
4033
4070
  agentId: resolveCallAgentId(call, effectiveConfig),
4034
4071
  transcript: call.transcript,
4035
4072
  userMessage,
4036
- onEarlyText: async (text) => {
4037
- this.logger.info(`Early AI response queued ${callId} chars=${text.length}`);
4038
- return (await this.manager.speak(callId, text, { listenAfterPlayback: true })).success;
4039
- }
4073
+ onEarlyText: speakResponse
4040
4074
  });
4041
4075
  if (result.error) {
4042
4076
  this.logger.error(`Response generation error: ${result.error}`);
4043
4077
  return;
4044
4078
  }
4045
- if (result.text && !result.deliveredEarly) {
4046
- this.logger.info(`AI response delivered ${callId} chars=${result.text.length}`);
4047
- await this.manager.speak(callId, result.text, { listenAfterPlayback: true });
4048
- }
4079
+ if (result.text && !result.deliveredEarly) await speakResponse(result.text);
4049
4080
  } catch (err) {
4050
4081
  this.logger.error(`Auto-response error: ${String(err)}`);
4082
+ } finally {
4083
+ response.release();
4051
4084
  }
4052
4085
  }
4053
4086
  };
@@ -4061,12 +4094,12 @@ const REALTIME_VOICE_CONSULT_SYSTEM_PROMPT = [
4061
4094
  "Do not print secret values or dump environment variables; only check whether required configuration is present.",
4062
4095
  "Be accurate, brief, and speakable."
4063
4096
  ].join(" ");
4064
- const loadTelnyxProvider = createLazyRuntimeModule(() => import("./telnyx-DO8dFQcb.js"));
4065
- const loadTwilioProvider = createLazyRuntimeModule(() => import("./twilio-JDt_1nlz.js"));
4066
- const loadPlivoProvider = createLazyRuntimeModule(() => import("./plivo-BbVnahnQ.js"));
4067
- const loadMockProvider = createLazyRuntimeModule(() => import("./mock-Dv7KxYGt.js"));
4097
+ const loadTelnyxProvider = createLazyRuntimeModule(() => import("./telnyx-BWmhDBtd.js"));
4098
+ const loadTwilioProvider = createLazyRuntimeModule(() => import("./twilio-78mzH8Lk.js"));
4099
+ const loadPlivoProvider = createLazyRuntimeModule(() => import("./plivo-CgYWuWVr.js"));
4100
+ const loadMockProvider = createLazyRuntimeModule(() => import("./mock-C57qj18W.js"));
4068
4101
  const loadRealtimeVoiceRuntime = createLazyRuntimeModule(() => import("./realtime-voice.runtime-vtdCOWg-.js"));
4069
- const loadRealtimeHandler = createLazyRuntimeModule(() => import("./realtime-handler-BaMEKbmJ.js"));
4102
+ const loadRealtimeHandler = createLazyRuntimeModule(() => import("./realtime-handler-oehCkuE2.js"));
4070
4103
  function resolveVoiceCallConsultSessionKey(call) {
4071
4104
  return resolveVoiceCallSessionKey({
4072
4105
  config: call.config,
@@ -1,2 +1,2 @@
1
- import { t as createVoiceCallRuntime } from "./runtime-entry-DK8kPCGC.js";
1
+ import { t as createVoiceCallRuntime } from "./runtime-entry-CJkbbEgg.js";
2
2
  export { createVoiceCallRuntime };
package/dist/setup-api.js CHANGED
@@ -1,19 +1,6 @@
1
1
  import { asFiniteNumber, asOptionalRecord, isRecord, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
2
2
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
3
3
  //#region extensions/voice-call/src/config-migration.ts
4
- /** Merge legacy provider-specific values into the canonical providers map. */
5
- function mergeProviderConfig(providersValue, providerId, compatValues) {
6
- if (Object.keys(compatValues).length === 0) return asOptionalRecord(providersValue);
7
- const providers = asOptionalRecord(providersValue) ?? {};
8
- const existing = asOptionalRecord(providers[providerId]) ?? {};
9
- return {
10
- ...providers,
11
- [providerId]: {
12
- ...existing,
13
- ...compatValues
14
- }
15
- };
16
- }
17
4
  /** Migrate legacy voice-call config input to the current canonical shape. */
18
5
  function migrateVoiceCallLegacyConfigInput(params) {
19
6
  const raw = asOptionalRecord(params.value) ?? {};
@@ -22,28 +9,71 @@ function migrateVoiceCallLegacyConfigInput(params) {
22
9
  const twilio = asOptionalRecord(raw.twilio);
23
10
  const streaming = asOptionalRecord(raw.streaming);
24
11
  const configPathPrefix = params.configPathPrefix ?? "plugins.entries.voice-call.config";
25
- const legacyStreamingOpenAICompat = {};
26
- const streamingOpenAIApiKey = readStringField(streaming, "openaiApiKey");
27
- if (streamingOpenAIApiKey) legacyStreamingOpenAICompat.apiKey = streamingOpenAIApiKey;
28
- const streamingSttModel = readStringField(streaming, "sttModel");
29
- if (streamingSttModel) legacyStreamingOpenAICompat.model = streamingSttModel;
30
- const streamingSilenceDurationMs = asFiniteNumber(streaming?.silenceDurationMs);
31
- if (streamingSilenceDurationMs !== void 0) legacyStreamingOpenAICompat.silenceDurationMs = streamingSilenceDurationMs;
32
- const streamingVadThreshold = asFiniteNumber(streaming?.vadThreshold);
33
- if (streamingVadThreshold !== void 0) legacyStreamingOpenAICompat.vadThreshold = streamingVadThreshold;
12
+ const changes = [];
13
+ if (raw.provider === "log") changes.push(`Moved ${configPathPrefix}.provider "log" → "mock".`);
14
+ if (typeof twilio?.from === "string") {
15
+ const source = `${configPathPrefix}.twilio.from`;
16
+ const target = `${configPathPrefix}.fromNumber`;
17
+ changes.push(raw.fromNumber != null ? `Removed ${source} (kept ${target}).` : `Moved ${source} → ${target}.`);
18
+ }
34
19
  const streamingProvider = readStringField(streaming, "provider");
35
20
  const legacyStreamingProvider = readStringField(streaming, "sttProvider");
36
21
  const normalizedStreaming = streaming ? {
37
22
  ...streaming,
38
- provider: streamingProvider ?? legacyStreamingProvider,
39
- providers: mergeProviderConfig(streaming.providers, "openai", legacyStreamingOpenAICompat)
23
+ provider: streamingProvider ?? legacyStreamingProvider
40
24
  } : void 0;
41
25
  if (normalizedStreaming) {
42
26
  delete normalizedStreaming.sttProvider;
43
- delete normalizedStreaming.openaiApiKey;
44
- delete normalizedStreaming.sttModel;
45
- delete normalizedStreaming.silenceDurationMs;
46
- delete normalizedStreaming.vadThreshold;
27
+ if (legacyStreamingProvider !== void 0) {
28
+ const source = `${configPathPrefix}.streaming.sttProvider`;
29
+ const target = `${configPathPrefix}.streaming.provider`;
30
+ changes.push(streamingProvider !== void 0 ? `Removed ${source} (kept ${target}).` : `Moved ${source} → ${target}.`);
31
+ }
32
+ for (const [legacyKey, canonicalKey, value] of [
33
+ [
34
+ "openaiApiKey",
35
+ "apiKey",
36
+ readStringField(streaming, "openaiApiKey")
37
+ ],
38
+ [
39
+ "sttModel",
40
+ "model",
41
+ readStringField(streaming, "sttModel")
42
+ ],
43
+ [
44
+ "silenceDurationMs",
45
+ "silenceDurationMs",
46
+ asFiniteNumber(streaming?.silenceDurationMs)
47
+ ],
48
+ [
49
+ "vadThreshold",
50
+ "vadThreshold",
51
+ asFiniteNumber(streaming?.vadThreshold)
52
+ ]
53
+ ]) {
54
+ if (!Object.hasOwn(normalizedStreaming, legacyKey)) continue;
55
+ delete normalizedStreaming[legacyKey];
56
+ const source = `${configPathPrefix}.streaming.${legacyKey}`;
57
+ if (value === void 0 || value === "") {
58
+ changes.push(`Removed invalid ${source}.`);
59
+ continue;
60
+ }
61
+ const providers = asOptionalRecord(normalizedStreaming.providers);
62
+ const existing = asOptionalRecord(providers?.openai);
63
+ const target = `${configPathPrefix}.streaming.providers.openai.${canonicalKey}`;
64
+ if (existing?.[canonicalKey] !== void 0) {
65
+ changes.push(`Removed ${source} (kept ${target}).`);
66
+ continue;
67
+ }
68
+ normalizedStreaming.providers = {
69
+ ...providers,
70
+ openai: {
71
+ ...existing,
72
+ [canonicalKey]: value
73
+ }
74
+ };
75
+ changes.push(`Moved ${source} → ${target}.`);
76
+ }
47
77
  }
48
78
  const normalizedTwilio = twilio ? { ...twilio } : void 0;
49
79
  if (normalizedTwilio) delete normalizedTwilio.from;
@@ -61,16 +91,6 @@ function migrateVoiceCallLegacyConfigInput(params) {
61
91
  streaming: normalizedStreaming,
62
92
  realtime: normalizedRealtime
63
93
  };
64
- const changes = [];
65
- if (raw.provider === "log") changes.push(`Moved ${configPathPrefix}.provider "log" → "mock".`);
66
- if (typeof twilio?.from === "string" && typeof raw.fromNumber !== "string") changes.push(`Moved ${configPathPrefix}.twilio.from → ${configPathPrefix}.fromNumber.`);
67
- if (typeof streaming?.sttProvider === "string") changes.push(`Moved ${configPathPrefix}.streaming.sttProvider → ${configPathPrefix}.streaming.provider.`);
68
- if (typeof streaming?.openaiApiKey === "string") changes.push(`Moved ${configPathPrefix}.streaming.openaiApiKey → ${configPathPrefix}.streaming.providers.openai.apiKey.`);
69
- if (typeof streaming?.sttModel === "string") changes.push(`Moved ${configPathPrefix}.streaming.sttModel → ${configPathPrefix}.streaming.providers.openai.model.`);
70
- if (asFiniteNumber(streaming?.silenceDurationMs) !== void 0) changes.push(`Moved ${configPathPrefix}.streaming.silenceDurationMs → ${configPathPrefix}.streaming.providers.openai.silenceDurationMs.`);
71
- else if (typeof streaming?.silenceDurationMs === "number") changes.push(`Removed invalid ${configPathPrefix}.streaming.silenceDurationMs.`);
72
- if (asFiniteNumber(streaming?.vadThreshold) !== void 0) changes.push(`Moved ${configPathPrefix}.streaming.vadThreshold → ${configPathPrefix}.streaming.providers.openai.vadThreshold.`);
73
- else if (typeof streaming?.vadThreshold === "number") changes.push(`Removed invalid ${configPathPrefix}.streaming.vadThreshold.`);
74
94
  if (realtimeAgentContext && Object.hasOwn(realtimeAgentContext, "includeSystemPrompt")) changes.push(`Removed ${configPathPrefix}.realtime.agentContext.includeSystemPrompt.`);
75
95
  return {
76
96
  config,
@@ -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. */
@@ -189,7 +182,7 @@ const MAX_CALL_RECORD_EVENTS = 1e3;
189
182
  const CALL_RECORD_EVENT_META_MAX_ENTRIES = 1100;
190
183
  const CALL_RECORD_CHUNK_MAX_ENTRIES = 48048;
191
184
  /** Raw UTF-8 bytes stored per call record chunk before base64 encoding. */
192
- const RAW_CALL_RECORD_CHUNK_BYTES = 47 * 1024;
185
+ const RAW_CALL_RECORD_CHUNK_BYTES = 48128;
193
186
  let callRecordEventSequence = 0;
194
187
  /** Return the pre-SQLite JSONL call log path for migration/compat checks. */
195
188
  function resolveVoiceCallLegacyCallLogPath(storePath) {
@@ -205,7 +198,7 @@ function resolvePluginStateEnv(storePath) {
205
198
  /** Open the plugin state stores when the runtime is available. */
206
199
  function createCallRecordStateStores(storePath) {
207
200
  const runtime = getOptionalVoiceCallStateRuntime();
208
- if (!runtime) return null;
201
+ if (!runtime) throw new Error("Voice Call state runtime not initialized");
209
202
  const env = resolvePluginStateEnv(storePath);
210
203
  return {
211
204
  events: runtime.state.openSyncKeyedStore({
@@ -392,7 +385,6 @@ function readCallRecordEvents(stores) {
392
385
  function persistCallRecord(storePath, call) {
393
386
  try {
394
387
  const stores = createCallRecordStateStores(storePath);
395
- if (!stores) throw new Error("Voice Call state runtime not initialized");
396
388
  const order = nextCallRecordOrder();
397
389
  registerCallRecordEvent(stores, buildNewEventKey(order), call, order);
398
390
  } catch (err) {
@@ -444,13 +436,11 @@ function readCallHistoryFromStore(storePath) {
444
436
  }
445
437
  return [];
446
438
  }
447
- /** Find the newest retained snapshots matching each call identifier namespace. */
448
- async function findCallMatchesInStore(storePath, callId) {
449
- const calls = readCallHistoryFromStore(storePath);
450
- return {
451
- byCallId: calls.findLast((call) => call.callId === callId),
452
- byProviderCallId: calls.findLast((call) => call.providerCallId === callId)
453
- };
439
+ /** Resolve an internal ID or retained provider alias to its newest logical call snapshot. */
440
+ function findCallInStore(storePath, callId) {
441
+ const calls = readCallRecordEvents(createCallRecordStateStores(storePath));
442
+ const match = calls.findLast((call) => call.callId === callId) ?? calls.findLast((call) => call.providerCallId === callId);
443
+ return match ? calls.findLast((call) => call.callId === match.callId) : void 0;
454
444
  }
455
445
  /** Return the newest persisted call history rows up to the requested limit. */
456
446
  async function getCallHistoryFromStore(storePath, limit = 50) {
@@ -464,4 +454,4 @@ function resolveDefaultVoiceCallStoreDir(env = process.env) {
464
454
  return path.join(resolveStateDir(env), "voice-calls");
465
455
  }
466
456
  //#endregion
467
- export { releaseRejectedProviderCall as _, CALL_RECORD_EVENT_META_MAX_ENTRIES as a, setVoiceCallStateRuntime as b, buildVoiceCallLegacyJsonlEventKey as c, loadActiveCallsFromStore as d, parseVoiceCallRecordLine as f, appendCallReplayKey as g, resolveVoiceCallLegacyCallLogPath as h, CALL_RECORD_EVENT_CHUNKS_NAMESPACE as i, findCallMatchesInStore as l, prepareVoiceCallRecordForStorage as m, CALL_RECORD_CHUNK_MAX_ENTRIES as n, MAX_CALL_RECORD_EVENTS as o, persistCallRecord as p, CALL_RECORD_EVENTS_NAMESPACE as r, RAW_CALL_RECORD_CHUNK_BYTES as s, resolveDefaultVoiceCallStoreDir as t, getCallHistoryFromStore as u, rememberManagerReplayKey as v, TerminalStates as x, reserveRejectedProviderCall as y };
457
+ export { releaseRejectedProviderCall as _, CALL_RECORD_EVENT_META_MAX_ENTRIES as a, setVoiceCallStateRuntime as b, buildVoiceCallLegacyJsonlEventKey as c, loadActiveCallsFromStore as d, parseVoiceCallRecordLine as f, appendCallReplayKey as g, resolveVoiceCallLegacyCallLogPath as h, CALL_RECORD_EVENT_CHUNKS_NAMESPACE as i, findCallInStore as l, prepareVoiceCallRecordForStorage as m, CALL_RECORD_CHUNK_MAX_ENTRIES as n, MAX_CALL_RECORD_EVENTS as o, persistCallRecord as p, CALL_RECORD_EVENTS_NAMESPACE as r, RAW_CALL_RECORD_CHUNK_BYTES as s, resolveDefaultVoiceCallStoreDir as t, getCallHistoryFromStore as u, rememberManagerReplayKey as v, TerminalStates as x, reserveRejectedProviderCall as y };