@lawpath-tech/openclaw 2026.2.21-19 → 2026.2.21-21

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 (183) hide show
  1. package/dist/{agents-C9ij96eI.js → agents-C83RLKX2.js} +4 -4
  2. package/dist/{agents.config-PFAfbBID.js → agents.config-BVjazvnK.js} +1 -1
  3. package/dist/{agents.config-CBvMKGLh.js → agents.config-dnXbFv5H.js} +1 -1
  4. package/dist/{audio-preflight-omzaYFGD.js → audio-preflight-CLqoZYgI.js} +4 -4
  5. package/dist/{audio-preflight-CODznzqu.js → audio-preflight-Da7vejCH.js} +4 -4
  6. package/dist/{auth-choice-DH2T1E9c.js → auth-choice-BJNaxS-L.js} +1 -1
  7. package/dist/{auth-choice-CLiY8PdF.js → auth-choice-Hb6530su.js} +1 -1
  8. package/dist/{banner-CqQBBjKH.js → banner-dFAx4eE_.js} +1 -1
  9. package/dist/build-info.json +3 -3
  10. package/dist/bundled/boot-md/handler.js +6 -6
  11. package/dist/bundled/session-memory/handler.js +6 -6
  12. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  13. package/dist/{channel-options-e7Z9Smf2.js → channel-options--M8F-DHl.js} +1 -1
  14. package/dist/{channel-options-CLS27y1A.js → channel-options-D0Qjq83D.js} +1 -1
  15. package/dist/{channel-web-DHMbpU2p.js → channel-web-D8hSmcPF.js} +1 -1
  16. package/dist/{channels-cli-BDHLWbaU.js → channels-cli-6uwOEBM-.js} +4 -4
  17. package/dist/{channels-cli-CDwQerVi.js → channels-cli-BtZHPcBX.js} +4 -4
  18. package/dist/{chrome-BAv9fEiv.js → chrome-Bqbv_ZSj.js} +7 -7
  19. package/dist/{chrome-lRUqqgSD.js → chrome-CZuniMYN.js} +7 -7
  20. package/dist/{cli-BGJFLB0I.js → cli-CIImVBCM.js} +1 -1
  21. package/dist/{cli-C1nypCKP.js → cli-CijjdR37.js} +1 -1
  22. package/dist/{command-registry-pGuORBuB.js → command-registry-BtDC5Ll-.js} +9 -9
  23. package/dist/{completion-cli-Bf-Ojq_a.js → completion-cli-Bw5nZmTm.js} +2 -2
  24. package/dist/{completion-cli-C79-bpAx.js → completion-cli-CI6LWHg3.js} +1 -1
  25. package/dist/{config-cli-DLdH6zIF.js → config-cli-CgjgjWri.js} +1 -1
  26. package/dist/{config-cli-BcNHb4_E.js → config-cli-Dc0TQ1yE.js} +1 -1
  27. package/dist/{configure-BSt9S0yk.js → configure-CGqsSqXB.js} +3 -3
  28. package/dist/{configure-h1d7nrgR.js → configure-GUYUjx8A.js} +3 -3
  29. package/dist/{deliver-B2d2N8OJ.js → deliver-0ThKlzQo.js} +1 -1
  30. package/dist/{deliver-CLJRPnfx.js → deliver-CKH_FhS0.js} +1 -1
  31. package/dist/{doctor-completion-BB1f594d.js → doctor-completion-DgPc1rDZ.js} +1 -1
  32. package/dist/{doctor-completion-deY10x7w.js → doctor-completion-DipoVPSi.js} +1 -1
  33. package/dist/entry.js +1 -1
  34. package/dist/extensionAPI.js +6 -6
  35. package/dist/{gateway-cli-IziQis2t.js → gateway-cli-Ds3oN72Y.js} +8 -8
  36. package/dist/{gateway-cli-B-XwzYp2.js → gateway-cli-DuMINk2B.js} +8 -8
  37. package/dist/{health-DMrTgW8V.js → health-DQjqH1zU.js} +1 -1
  38. package/dist/{health-UmuJyu1Q.js → health-Dg0bAIKS.js} +1 -1
  39. package/dist/{hooks-cli-BparoMpt.js → hooks-cli-CEtCwdza.js} +2 -2
  40. package/dist/{hooks-cli-B6WCYLyG.js → hooks-cli-Cac2Ru8r.js} +2 -2
  41. package/dist/{image--DDZnw-F.js → image-88q3KE-C.js} +1 -1
  42. package/dist/{image-Ci28h-op.js → image-CxPjVob-.js} +1 -1
  43. package/dist/index.js +6 -6
  44. package/dist/llm-slug-generator.js +6 -6
  45. package/dist/{models-nhERae29.js → models-DnhANpnd.js} +2 -2
  46. package/dist/{models-cli-bfKNgoHD.js → models-cli-03noBf8W.js} +3 -3
  47. package/dist/{models-cli-BnbeEOCU.js → models-cli-CaP_rt4y.js} +2 -2
  48. package/dist/{onboard-D_VxXTXD.js → onboard-BMc-yUv5.js} +2 -2
  49. package/dist/{onboard-C3pJ0NU-.js → onboard-BZg2c27h.js} +2 -2
  50. package/dist/{onboard-channels-CyeyvIfw.js → onboard-channels-Lt2gG1e2.js} +1 -1
  51. package/dist/{onboard-channels-YvwszRaQ.js → onboard-channels-O6KvizE3.js} +1 -1
  52. package/dist/{onboarding-0zwx3Yv7.js → onboarding-CTgBlanm.js} +3 -3
  53. package/dist/{onboarding-D0_Q5m2W.js → onboarding-G28sv5Ms.js} +3 -3
  54. package/dist/{onboarding.finalize-CYb0_Q0z.js → onboarding.finalize-Cpt9Ymcn.js} +5 -5
  55. package/dist/{onboarding.finalize-CVeI23_g.js → onboarding.finalize-D2sylHDB.js} +6 -6
  56. package/dist/{pi-embedded-D7qD6fo-.js → pi-embedded-Dz24QZz9.js} +96 -17
  57. package/dist/{pi-embedded-CwQsXrVT.js → pi-embedded-ZvazjIyF.js} +96 -17
  58. package/dist/{pi-embedded-helpers-Cd0S0WfR.js → pi-embedded-helpers-B0Kht0I2.js} +4 -4
  59. package/dist/{pi-embedded-helpers-Ll4Lztu1.js → pi-embedded-helpers-CSE0v99A.js} +4 -4
  60. package/dist/{plugin-registry-83ThmVHf.js → plugin-registry-1pF4_jNl.js} +1 -1
  61. package/dist/{plugin-registry-D57lc9ob.js → plugin-registry-CHOacv01.js} +1 -1
  62. package/dist/plugin-sdk/{accounts-DUBJHEgi.js → accounts-BcQo4OUu.js} +1 -1
  63. package/dist/plugin-sdk/{accounts-wSu5JyDM.js → accounts-DEsAwvaZ.js} +1 -1
  64. package/dist/plugin-sdk/{accounts-Ciwy7Yxd.js → accounts-fnLsjAOi.js} +3 -3
  65. package/dist/plugin-sdk/{active-listener-Bnau4oiu.js → active-listener-CeaKSm29.js} +1 -1
  66. package/dist/plugin-sdk/{agent-scope-cT7IxdHY.js → agent-scope-D5PlT0Z0.js} +2 -2
  67. package/dist/plugin-sdk/{api-key-rotation-ElOMCP9r.js → api-key-rotation-B3O1qn0m.js} +1 -1
  68. package/dist/plugin-sdk/{audio-preflight-DtozH3sJ.js → audio-preflight-CYSy3uuZ.js} +24 -24
  69. package/dist/plugin-sdk/{bindings-CKqdifyU.js → bindings-C0XEX0yK.js} +2 -2
  70. package/dist/plugin-sdk/{channel-activity-CCnr-9bS.js → channel-activity-WgE8Z4Bb.js} +1 -1
  71. package/dist/plugin-sdk/{channel-web-XYtgc8aq.js → channel-web-Nw-bUgtV.js} +22 -22
  72. package/dist/plugin-sdk/{chrome-BO7SUTgY.js → chrome-C1xnOQsc.js} +3 -3
  73. package/dist/plugin-sdk/{chunk-DyzO7IVo.js → chunk-CQk9vawA.js} +1 -1
  74. package/dist/plugin-sdk/{command-format-DcBXOX9Z.js → command-format-DHXa0Clc.js} +1 -1
  75. package/dist/plugin-sdk/{commands-registry-BsSKPwgj.js → commands-registry-yTZdwtLj.js} +4 -4
  76. package/dist/plugin-sdk/{config-9OZuRn0C.js → config--kW4R0el.js} +9 -9
  77. package/dist/plugin-sdk/{deliver-BO_wt0wd.js → deliver-C2gjA_UH.js} +10 -10
  78. package/dist/plugin-sdk/{diagnostic-DKyVS3IK.js → diagnostic-Up1aMyxZ.js} +1 -1
  79. package/dist/plugin-sdk/{image-C31B_R8P.js → image-Bcc99tVA.js} +4 -4
  80. package/dist/plugin-sdk/{image-ops-B8aJIly2.js → image-ops-C6RLbtSJ.js} +1 -1
  81. package/dist/plugin-sdk/index.js +53 -53
  82. package/dist/plugin-sdk/{ir-CD5VqKGg.js → ir-Cwi1KVG5.js} +4 -4
  83. package/dist/plugin-sdk/{local-roots-D4PCK2jj.js → local-roots-6BSl8AoA.js} +3 -3
  84. package/dist/plugin-sdk/{login-CW17h3xU.js → login-Cau5eVVj.js} +7 -7
  85. package/dist/plugin-sdk/{login-qr-CMdk06CS.js → login-qr-CLWtfA9m.js} +9 -9
  86. package/dist/plugin-sdk/{manager-CZPPxI6R.js → manager-C3Eb_8Hz.js} +8 -8
  87. package/dist/plugin-sdk/{manifest-registry-sgHvjA6E.js → manifest-registry-DvEm2HHf.js} +1 -1
  88. package/dist/plugin-sdk/{markdown-tables-Bo-r4msS.js → markdown-tables-cJqvQe9j.js} +1 -1
  89. package/dist/plugin-sdk/{message-channel-DmB4bGFh.js → message-channel-DY9wVB2A.js} +1 -1
  90. package/dist/plugin-sdk/{model-selection-CXooYLb2.js → model-selection-BjVTBZiY.js} +4 -4
  91. package/dist/plugin-sdk/{outbound-dAcQHyyr.js → outbound-Co1QSkdS.js} +7 -7
  92. package/dist/plugin-sdk/{outbound-attachment-CCxMtk8m.js → outbound-attachment-CsmRyZPI.js} +2 -2
  93. package/dist/plugin-sdk/{pi-auth-json-C-euHc3x.js → pi-auth-json-DfEOhoTK.js} +5 -5
  94. package/dist/plugin-sdk/{pi-embedded-helpers-DU0nLxRU.js → pi-embedded-helpers-D3EACnFn.js} +17 -17
  95. package/dist/plugin-sdk/plugins/runtime/types.d.ts +2 -0
  96. package/dist/plugin-sdk/{plugins-DIzw7Ncy.js → plugins-Dyz98ZJX.js} +4 -4
  97. package/dist/plugin-sdk/{pw-ai-CYmDAwXD.js → pw-ai-smoXiGqZ.js} +8 -8
  98. package/dist/plugin-sdk/{qmd-manager-DQiT4WTr.js → qmd-manager-CUhJ7WG2.js} +4 -4
  99. package/dist/plugin-sdk/{registry-O1yza0Vh.js → registry-DL-33c9X.js} +2 -2
  100. package/dist/plugin-sdk/{replies-Bnu7HnvW.js → replies-Bkaifx0j.js} +3 -3
  101. package/dist/plugin-sdk/{reply-BSOpvBX4.js → reply-Cb54ddUA.js} +158 -79
  102. package/dist/plugin-sdk/{reply-prefix-BF0omhZE.js → reply-prefix-5vdbga0R.js} +1 -1
  103. package/dist/plugin-sdk/{resolve-outbound-target-DzZIT1V1.js → resolve-outbound-target-CP7QDN14.js} +2 -2
  104. package/dist/plugin-sdk/{resolve-route-B63ztF-V.js → resolve-route-eopifBrM.js} +3 -3
  105. package/dist/plugin-sdk/{retry-JFNwfUew.js → retry-5P3d0rYw.js} +1 -1
  106. package/dist/plugin-sdk/{runner-DztRiWhh.js → runner-DqwPn56n.js} +9 -9
  107. package/dist/plugin-sdk/{send-DEV4iiFj.js → send-C2NQjVF9.js} +10 -10
  108. package/dist/plugin-sdk/{send-D-spKwjf.js → send-D_X6p9H0.js} +10 -10
  109. package/dist/plugin-sdk/{send-CK3DCPv-.js → send-DnFB-v5n.js} +6 -6
  110. package/dist/plugin-sdk/{send-D1C2sJG1.js → send-PPtjKj-0.js} +7 -7
  111. package/dist/plugin-sdk/{send-DrYvH25B.js → send-_uj_NuFI.js} +6 -6
  112. package/dist/plugin-sdk/{session-DRJl5tKC.js → session-CZngPTYk.js} +4 -4
  113. package/dist/plugin-sdk/{skill-commands-Dv5AeOZQ.js → skill-commands-Cr54qPdy.js} +5 -5
  114. package/dist/plugin-sdk/{skills-5hEda4_Y.js → skills-Dcu4o-9l.js} +7 -7
  115. package/dist/plugin-sdk/{sqlite-DUu0dwic.js → sqlite-ulpcowfN.js} +1 -1
  116. package/dist/plugin-sdk/{store-DoRXD0QB.js → store-EUYCRBvs.js} +2 -2
  117. package/dist/plugin-sdk/{subsystem-C4Rh0kdL.js → subsystem-C9WnrKN8.js} +1 -1
  118. package/dist/plugin-sdk/{tables-C_81Ve2l.js → tables-CMdQECje.js} +1 -1
  119. package/dist/plugin-sdk/{target-errors-C638yp2-.js → target-errors-CrR2lAF7.js} +2 -2
  120. package/dist/plugin-sdk/{thinking-DexKPSsI.js → thinking-C7zYA2s9.js} +5 -5
  121. package/dist/plugin-sdk/{tokens-K9ITCatc.js → tokens-2475WU2Z.js} +1 -1
  122. package/dist/plugin-sdk/{tool-images-BvjD_HnR.js → tool-images-DCvC8yL7.js} +2 -2
  123. package/dist/plugin-sdk/{tool-loop-detection-BBRtoox8.js → tool-loop-detection-D0ADW0h2.js} +2 -2
  124. package/dist/plugin-sdk/tts/tts-core.d.ts +15 -0
  125. package/dist/plugin-sdk/tts/tts.d.ts +16 -0
  126. package/dist/plugin-sdk/web-BVkjyY4A.js +65 -0
  127. package/dist/plugin-sdk/{whatsapp-actions-D8i2vCxS.js → whatsapp-actions-DC4RRNqv.js} +21 -21
  128. package/dist/{plugins-cli-BymsOcmR.js → plugins-cli-CflNTqmB.js} +2 -2
  129. package/dist/{plugins-cli-DOWA2F0z.js → plugins-cli-DW0TLzrq.js} +2 -2
  130. package/dist/{program-T05XIetU.js → program-DUkjf6lq.js} +7 -7
  131. package/dist/{program-context-CDmctW1L.js → program-context-C0-LzXoV.js} +17 -17
  132. package/dist/{prompt-select-styled-cR4-U64b.js → prompt-select-styled-CQUE1vow.js} +4 -4
  133. package/dist/{prompt-select-styled-BbzQhWF7.js → prompt-select-styled-CWNxdPrL.js} +4 -4
  134. package/dist/{provider-auth-helpers-BbnDoytN.js → provider-auth-helpers-D39L7fZC.js} +1 -1
  135. package/dist/{provider-auth-helpers-C4RNIRyy.js → provider-auth-helpers-bYUBBkmL.js} +1 -1
  136. package/dist/{push-apns-Dli4pG4y.js → push-apns-DGIqfAg5.js} +1 -1
  137. package/dist/{push-apns-DUeNpqyF.js → push-apns-Qfohz-Hs.js} +1 -1
  138. package/dist/{pw-ai-KrN0mqVH.js → pw-ai-D-_aGzdQ.js} +1 -1
  139. package/dist/{pw-ai-C-kqYO4L.js → pw-ai-S3cpSYOy.js} +1 -1
  140. package/dist/{register.agent-aY_6enDc.js → register.agent-BEM0_4uG.js} +5 -5
  141. package/dist/{register.agent-DNAtU5WP.js → register.agent-WQgVmACk.js} +6 -6
  142. package/dist/{register.configure-CwlwOMZ3.js → register.configure-CCpFmFPt.js} +6 -6
  143. package/dist/{register.configure-C_n9kIWJ.js → register.configure-CdW3aECy.js} +6 -6
  144. package/dist/{register.maintenance-DC40nnCm.js → register.maintenance-B8RZLSOe.js} +8 -8
  145. package/dist/{register.maintenance-Dddndcoo.js → register.maintenance-CM3QEdFz.js} +7 -7
  146. package/dist/{register.message-DiR6desB.js → register.message-BRLYHuVS.js} +2 -2
  147. package/dist/{register.message-CpEf0b15.js → register.message-XYm9NyDT.js} +2 -2
  148. package/dist/{register.onboard-bOeA39xj.js → register.onboard-BzAJbRpP.js} +4 -4
  149. package/dist/{register.onboard-VzVmqpEA.js → register.onboard-EwGHFUsd.js} +4 -4
  150. package/dist/{register.setup-CNLQzxl8.js → register.setup-33shhZje.js} +4 -4
  151. package/dist/{register.setup-D6iO5Xqa.js → register.setup-DpdZyiMv.js} +4 -4
  152. package/dist/{register.status-health-sessions-CoiaeduR.js → register.status-health-sessions-C8-iqepo.js} +3 -3
  153. package/dist/{register.status-health-sessions-BtJeTZnN.js → register.status-health-sessions-CK4f2nj_.js} +3 -3
  154. package/dist/{register.subclis-CFTYYdAQ.js → register.subclis-Mn68G5tp.js} +9 -9
  155. package/dist/{reply-Bv-RvRzs.js → reply-DuVUTFfT.js} +82 -3
  156. package/dist/{run-main-EzFOCEdp.js → run-main-CNB3qjRM.js} +14 -14
  157. package/dist/{runner-ChBxge-W.js → runner-D1eXJZ8T.js} +1 -1
  158. package/dist/{runner-Dq-qfrq7.js → runner-a43IsYad.js} +1 -1
  159. package/dist/{server-node-events-B9TqPvCI.js → server-node-events-B3o3P600.js} +2 -2
  160. package/dist/{server-node-events-syk21TN6.js → server-node-events-BPFwUGbS.js} +2 -2
  161. package/dist/{session-dirs-BAcQuXpY.js → session-dirs-Cw2efkey.js} +1 -1
  162. package/dist/{session-dirs-t0YpmrIL.js → session-dirs-DsU4kvIK.js} +1 -1
  163. package/dist/{status-keCWmejk.js → status-BSJIuIlN.js} +1 -1
  164. package/dist/{status-05251w21.js → status-BhHpKHQP.js} +2 -2
  165. package/dist/{status-BqDiXxwL.js → status-C2PvVLaW.js} +1 -1
  166. package/dist/{status-B8UVm7P3.js → status-CLKmcFCC.js} +2 -2
  167. package/dist/{subagent-registry-Lm4ps45z.js → subagent-registry-Da0a_Vka.js} +82 -3
  168. package/dist/{update-cli-BTF6TXGD.js → update-cli-BW3q5tFm.js} +7 -7
  169. package/dist/{update-cli-aW6jVJIJ.js → update-cli-D4EUMrX6.js} +8 -8
  170. package/dist/{web-BYZfljIx.js → web-BTbIFuWM.js} +2 -2
  171. package/dist/{web-BjxYv-hA.js → web-C7TQSVU0.js} +1 -1
  172. package/dist/{web-DPHo2cWX.js → web-CVSol55V.js} +6 -6
  173. package/dist/{web-Cs0IP-ZO.js → web-Y49Dumye.js} +6 -6
  174. package/extensions/voice-call/package.json +2 -0
  175. package/extensions/voice-call/src/config.ts +16 -3
  176. package/extensions/voice-call/src/providers/realtime-session.ts +210 -0
  177. package/extensions/voice-call/src/providers/twilio.ts +31 -13
  178. package/extensions/voice-call/src/response-generator.ts +3 -3
  179. package/extensions/voice-call/src/telephony-audio.ts +54 -0
  180. package/extensions/voice-call/src/telephony-tts.ts +40 -2
  181. package/extensions/voice-call/src/webhook.ts +174 -8
  182. package/package.json +1 -1
  183. package/dist/plugin-sdk/web-DiLYLsml.js +0 -65
@@ -6,12 +6,18 @@ import {
6
6
  readRequestBodyWithLimit,
7
7
  requestBodyErrorToText,
8
8
  } from "openclaw/plugin-sdk";
9
+ import type { WebSocket as NodeWebSocket } from "ws";
10
+ import { WebSocketServer } from "ws";
9
11
  import type { VoiceCallConfig } from "./config.js";
10
12
  import type { CoreConfig } from "./core-bridge.js";
11
13
  import type { CallManager } from "./manager.js";
12
14
  import type { MediaStreamConfig } from "./media-stream.js";
13
15
  import { MediaStreamHandler } from "./media-stream.js";
14
16
  import type { VoiceCallProvider } from "./providers/base.js";
17
+ import {
18
+ createRealtimeCallSession,
19
+ type RealtimeCallSession,
20
+ } from "./providers/realtime-session.js";
15
21
  import { OpenAIRealtimeSTTProvider } from "./providers/stt-openai-realtime.js";
16
22
  import type { TwilioProvider } from "./providers/twilio.js";
17
23
  import type { NormalizedEvent, WebhookContext } from "./types.js";
@@ -30,9 +36,18 @@ export class VoiceCallWebhookServer {
30
36
  private coreConfig: CoreConfig | null;
31
37
  private staleCallReaperInterval: ReturnType<typeof setInterval> | null = null;
32
38
 
33
- /** Media stream handler for bidirectional audio (when streaming enabled) */
39
+ /** Media stream handler for bidirectional audio (when streaming enabled, embedded mode) */
34
40
  private mediaStreamHandler: MediaStreamHandler | null = null;
35
41
 
42
+ /** Whether we're using the Realtime API for speech-to-speech (bypasses STT/TTS chain) */
43
+ private isRealtimeMode = false;
44
+
45
+ /** Realtime WebSocket server for direct Twilio → OpenAI Realtime API bridging */
46
+ private realtimeWss: WebSocketServer | null = null;
47
+
48
+ /** Active realtime sessions keyed by call SID */
49
+ private realtimeSessions = new Map<string, RealtimeCallSession>();
50
+
36
51
  constructor(
37
52
  config: VoiceCallConfig,
38
53
  manager: CallManager,
@@ -43,10 +58,14 @@ export class VoiceCallWebhookServer {
43
58
  this.manager = manager;
44
59
  this.provider = provider;
45
60
  this.coreConfig = coreConfig ?? null;
61
+ this.isRealtimeMode = config.responseMode === "realtime";
46
62
 
47
- // Initialize media stream handler if streaming is enabled
48
63
  if (config.streaming?.enabled) {
49
- this.initializeMediaStreaming();
64
+ if (this.isRealtimeMode) {
65
+ this.initializeRealtimeStreaming();
66
+ } else {
67
+ this.initializeMediaStreaming();
68
+ }
50
69
  }
51
70
  }
52
71
 
@@ -57,6 +76,122 @@ export class VoiceCallWebhookServer {
57
76
  return this.mediaStreamHandler;
58
77
  }
59
78
 
79
+ /**
80
+ * Initialize realtime streaming mode.
81
+ * In this mode, each Twilio WebSocket connection is bridged directly to the
82
+ * OpenAI Realtime API via TwilioRealtimeTransportLayer for speech-to-speech.
83
+ */
84
+ private initializeRealtimeStreaming(): void {
85
+ const apiKey = this.config.streaming?.openaiApiKey || process.env.OPENAI_API_KEY;
86
+ if (!apiKey) {
87
+ console.warn("[voice-call] Realtime mode enabled but no OpenAI API key found");
88
+ return;
89
+ }
90
+ console.log("[voice-call] Realtime speech-to-speech mode initialized");
91
+ }
92
+
93
+ /**
94
+ * Handle WebSocket upgrade for realtime mode.
95
+ * Creates a RealtimeCallSession that bridges Twilio <-> OpenAI Realtime API.
96
+ */
97
+ private handleRealtimeUpgrade(
98
+ request: http.IncomingMessage,
99
+ socket: import("node:stream").Duplex,
100
+ head: Buffer,
101
+ ): void {
102
+ if (!this.realtimeWss) {
103
+ this.realtimeWss = new WebSocketServer({ noServer: true });
104
+ this.realtimeWss.on("connection", (ws, req) => {
105
+ this.handleRealtimeConnection(ws as unknown as NodeWebSocket, req);
106
+ });
107
+ }
108
+ this.realtimeWss.handleUpgrade(request, socket, head, (ws) => {
109
+ this.realtimeWss?.emit("connection", ws, request);
110
+ });
111
+ }
112
+
113
+ /**
114
+ * Handle a new Twilio WebSocket connection in realtime mode.
115
+ * Waits for the Twilio "start" message to get the callSid, then creates
116
+ * a RealtimeCallSession that bridges directly to the OpenAI Realtime API.
117
+ */
118
+ private handleRealtimeConnection(ws: NodeWebSocket, _request: http.IncomingMessage): void {
119
+ let callSid: string | null = null;
120
+ let sessionCreated = false;
121
+
122
+ ws.on("message", (data: Buffer) => {
123
+ try {
124
+ const message = JSON.parse(data.toString());
125
+
126
+ if (message.event === "start" && !sessionCreated) {
127
+ callSid = message.start?.callSid || null;
128
+ if (!callSid) {
129
+ console.warn("[voice-call] [realtime] Missing callSid in start message");
130
+ ws.close(1008, "Missing callSid");
131
+ return;
132
+ }
133
+
134
+ const call = this.manager.getCallByProviderCallId(callSid);
135
+ const from = call?.from || message.start?.customParameters?.from || "unknown";
136
+
137
+ if (this.provider.name === "twilio") {
138
+ const twilio = this.provider as TwilioProvider;
139
+ const token = message.start?.customParameters?.token;
140
+ if (!twilio.isValidStreamToken(callSid, token)) {
141
+ console.warn(`[voice-call] [realtime] Invalid stream token for ${callSid}`);
142
+ ws.close(1008, "Unauthorized");
143
+ return;
144
+ }
145
+ const streamSid = message.streamSid || "";
146
+ twilio.registerCallStream(callSid, streamSid);
147
+ }
148
+
149
+ console.log(`[voice-call] [realtime] Creating session for call ${callSid} from ${from}`);
150
+ sessionCreated = true;
151
+
152
+ try {
153
+ const realtimeSession = createRealtimeCallSession({
154
+ twilioWebSocket: ws,
155
+ voiceConfig: this.config,
156
+ callId: call?.callId || callSid,
157
+ from,
158
+ });
159
+ this.realtimeSessions.set(callSid, realtimeSession);
160
+ } catch (err) {
161
+ console.error(`[voice-call] [realtime] Failed to create session:`, err);
162
+ ws.close(1011, "Session creation failed");
163
+ }
164
+ }
165
+ } catch (err) {
166
+ // Non-JSON messages are normal (binary audio frames in some edge cases)
167
+ }
168
+ });
169
+
170
+ ws.on("close", () => {
171
+ if (callSid) {
172
+ console.log(`[voice-call] [realtime] WebSocket closed for call ${callSid}`);
173
+ const session = this.realtimeSessions.get(callSid);
174
+ if (session) {
175
+ session.close();
176
+ this.realtimeSessions.delete(callSid);
177
+ }
178
+ if (this.provider.name === "twilio") {
179
+ (this.provider as TwilioProvider).unregisterCallStream(callSid);
180
+ }
181
+ const call = this.manager.getCallByProviderCallId(callSid);
182
+ if (call) {
183
+ void this.manager.endCall(call.callId).catch((err) => {
184
+ console.warn(`[voice-call] [realtime] Failed to end call ${call.callId}:`, err);
185
+ });
186
+ }
187
+ }
188
+ });
189
+
190
+ ws.on("error", (error) => {
191
+ console.error("[voice-call] [realtime] WebSocket error:", error);
192
+ });
193
+ }
194
+
60
195
  /**
61
196
  * Initialize media streaming with OpenAI Realtime STT.
62
197
  */
@@ -190,13 +325,18 @@ export class VoiceCallWebhookServer {
190
325
  });
191
326
 
192
327
  // Handle WebSocket upgrades for media streams
193
- if (this.mediaStreamHandler) {
328
+ if (this.isRealtimeMode || this.mediaStreamHandler) {
194
329
  this.server.on("upgrade", (request, socket, head) => {
195
330
  const url = new URL(request.url || "/", `http://${request.headers.host}`);
196
331
 
197
332
  if (url.pathname === streamPath) {
198
- console.log("[voice-call] WebSocket upgrade for media stream");
199
- this.mediaStreamHandler?.handleUpgrade(request, socket, head);
333
+ if (this.isRealtimeMode) {
334
+ console.log("[voice-call] WebSocket upgrade for realtime session");
335
+ this.handleRealtimeUpgrade(request, socket, head);
336
+ } else {
337
+ console.log("[voice-call] WebSocket upgrade for media stream");
338
+ this.mediaStreamHandler?.handleUpgrade(request, socket, head);
339
+ }
200
340
  } else {
201
341
  socket.destroy();
202
342
  }
@@ -208,7 +348,11 @@ export class VoiceCallWebhookServer {
208
348
  this.server.listen(port, bind, () => {
209
349
  const url = `http://${bind}:${port}${webhookPath}`;
210
350
  console.log(`[voice-call] Webhook server listening on ${url}`);
211
- if (this.mediaStreamHandler) {
351
+ if (this.isRealtimeMode) {
352
+ console.log(
353
+ `[voice-call] Realtime speech-to-speech WebSocket on ws://${bind}:${port}${streamPath}`,
354
+ );
355
+ } else if (this.mediaStreamHandler) {
212
356
  console.log(`[voice-call] Media stream WebSocket on ws://${bind}:${port}${streamPath}`);
213
357
  }
214
358
  resolve(url);
@@ -257,6 +401,10 @@ export class VoiceCallWebhookServer {
257
401
  clearInterval(this.staleCallReaperInterval);
258
402
  this.staleCallReaperInterval = null;
259
403
  }
404
+ for (const session of this.realtimeSessions.values()) {
405
+ session.close();
406
+ }
407
+ this.realtimeSessions.clear();
260
408
  return new Promise((resolve) => {
261
409
  if (this.server) {
262
410
  this.server.close(() => {
@@ -365,14 +513,26 @@ export class VoiceCallWebhookServer {
365
513
  return readRequestBodyWithLimit(req, { maxBytes, timeoutMs });
366
514
  }
367
515
 
516
+ private static readonly TOOL_HINT_PATTERNS = [
517
+ /\b(stripe|revenue|charges|gross|mtd|sales|refund|subscription)\b/i,
518
+ /\b(check|look up|pull|get|show|what(?:'s| is| are| was| were))\b.*\b(number|metric|data|status|uptime|error|log)\b/i,
519
+ /\b(yesterday|today|this week|this month|last month)\b.*\b(performance|revenue|gross|net)\b/i,
520
+ /\b(search|google|find out|look into|news|happening|weather)\b/i,
521
+ ];
522
+
523
+ private looksLikeToolQuery(message: string): boolean {
524
+ return VoiceCallWebhookServer.TOOL_HINT_PATTERNS.some((p) => p.test(message));
525
+ }
526
+
368
527
  /**
369
528
  * Handle auto-response for inbound calls using the agent system.
370
529
  * Supports tool calling for richer voice interactions.
530
+ * When a tool-heavy query is detected, speaks filler audio immediately
531
+ * so the caller knows we're working on it.
371
532
  */
372
533
  private async handleInboundResponse(callId: string, userMessage: string): Promise<void> {
373
534
  console.log(`[voice-call] Auto-responding to inbound call ${callId}: "${userMessage}"`);
374
535
 
375
- // Get call context for conversation history
376
536
  const call = this.manager.getCall(callId);
377
537
  if (!call) {
378
538
  console.warn(`[voice-call] Call ${callId} not found for auto-response`);
@@ -384,6 +544,12 @@ export class VoiceCallWebhookServer {
384
544
  return;
385
545
  }
386
546
 
547
+ // Speak filler audio immediately for queries that likely need tools,
548
+ // so the caller doesn't wait in silence while scripts run.
549
+ if (this.looksLikeToolQuery(userMessage)) {
550
+ this.manager.speak(callId, "Gimme a sec, let me check that for ya.").catch(() => {});
551
+ }
552
+
387
553
  try {
388
554
  const { generateVoiceResponse } = await import("./response-generator.js");
389
555
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lawpath-tech/openclaw",
3
- "version": "2026.2.21-19",
3
+ "version": "2026.2.21-21",
4
4
  "description": "Multi-channel AI gateway with extensible messaging integrations",
5
5
  "keywords": [],
6
6
  "homepage": "https://github.com/openclaw/openclaw#readme",
@@ -1,65 +0,0 @@
1
- import { a as WA_WEB_AUTH_DIR, g as webAuthExists, s as logWebSelfId, u as pickWebChannel } from "./accounts-Ciwy7Yxd.js";
2
- import "./reply-BSOpvBX4.js";
3
- import "./paths-DVWx7USN.js";
4
- import "./github-copilot-token-Cg0YPPSu.js";
5
- import "./plugins-DIzw7Ncy.js";
6
- import "./fetch-b2Zm8y7X.js";
7
- import "./registry-O1yza0Vh.js";
8
- import "./retry-JFNwfUew.js";
9
- import "./send-DEV4iiFj.js";
10
- import "./config-9OZuRn0C.js";
11
- import "./subsystem-C4Rh0kdL.js";
12
- import "./command-format-DcBXOX9Z.js";
13
- import "./model-selection-CXooYLb2.js";
14
- import "./agent-scope-cT7IxdHY.js";
15
- import "./manifest-registry-sgHvjA6E.js";
16
- import "./redact-DPnDWsnT.js";
17
- import "./errors-Bv8oZiTO.js";
18
- import "./channel-activity-CCnr-9bS.js";
19
- import "./image-ops-B8aJIly2.js";
20
- import "./ssrf-DKZ8eBrk.js";
21
- import "./local-roots-D4PCK2jj.js";
22
- import "./ir-CD5VqKGg.js";
23
- import "./chunk-DyzO7IVo.js";
24
- import "./message-channel-DmB4bGFh.js";
25
- import "./bindings-CKqdifyU.js";
26
- import "./markdown-tables-Bo-r4msS.js";
27
- import "./render-BiJZ5W4Z.js";
28
- import "./tables-C_81Ve2l.js";
29
- import "./send-D1C2sJG1.js";
30
- import "./tool-images-BvjD_HnR.js";
31
- import "./target-errors-C638yp2-.js";
32
- import "./send-CK3DCPv-.js";
33
- import "./send-D-spKwjf.js";
34
- import "./runner-DztRiWhh.js";
35
- import "./reply-prefix-BF0omhZE.js";
36
- import "./tokens-K9ITCatc.js";
37
- import "./skill-commands-Dv5AeOZQ.js";
38
- import "./skills-5hEda4_Y.js";
39
- import "./chrome-BO7SUTgY.js";
40
- import "./thinking-DexKPSsI.js";
41
- import "./accounts-wSu5JyDM.js";
42
- import "./accounts-DUBJHEgi.js";
43
- import "./deliver-BO_wt0wd.js";
44
- import "./pi-embedded-helpers-DU0nLxRU.js";
45
- import "./paths-BNQjLbn7.js";
46
- import "./diagnostic-DKyVS3IK.js";
47
- import "./store-DoRXD0QB.js";
48
- import { n as monitorWebInbox, t as monitorWebChannel } from "./channel-web-XYtgc8aq.js";
49
- import "./image-C31B_R8P.js";
50
- import "./pi-model-discovery-LbcEa65a.js";
51
- import "./api-key-rotation-ElOMCP9r.js";
52
- import "./sqlite-DUu0dwic.js";
53
- import "./diagnostic-session-state-Wd5tNeQG.js";
54
- import "./manager-CZPPxI6R.js";
55
- import "./commands-registry-BsSKPwgj.js";
56
- import "./send-DrYvH25B.js";
57
- import "./proxy-MquBDehr.js";
58
- import "./resolve-route-B63ztF-V.js";
59
- import "./replies-Bnu7HnvW.js";
60
- import "./outbound-attachment-CCxMtk8m.js";
61
- import { n as sendMessageWhatsApp } from "./outbound-dAcQHyyr.js";
62
- import { i as waitForWaConnection, t as createWaSocket } from "./session-DRJl5tKC.js";
63
- import { t as loginWeb } from "./login-CW17h3xU.js";
64
-
65
- export { WA_WEB_AUTH_DIR, createWaSocket, logWebSelfId, loginWeb, monitorWebChannel, monitorWebInbox, pickWebChannel, sendMessageWhatsApp, waitForWaConnection, webAuthExists };