@openclaw/voice-call 2026.1.29

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 (44) hide show
  1. package/CHANGELOG.md +78 -0
  2. package/README.md +135 -0
  3. package/index.ts +497 -0
  4. package/openclaw.plugin.json +601 -0
  5. package/package.json +16 -0
  6. package/src/cli.ts +312 -0
  7. package/src/config.test.ts +204 -0
  8. package/src/config.ts +502 -0
  9. package/src/core-bridge.ts +198 -0
  10. package/src/manager/context.ts +21 -0
  11. package/src/manager/events.ts +177 -0
  12. package/src/manager/lookup.ts +33 -0
  13. package/src/manager/outbound.ts +248 -0
  14. package/src/manager/state.ts +50 -0
  15. package/src/manager/store.ts +88 -0
  16. package/src/manager/timers.ts +86 -0
  17. package/src/manager/twiml.ts +9 -0
  18. package/src/manager.test.ts +108 -0
  19. package/src/manager.ts +888 -0
  20. package/src/media-stream.test.ts +97 -0
  21. package/src/media-stream.ts +393 -0
  22. package/src/providers/base.ts +67 -0
  23. package/src/providers/index.ts +10 -0
  24. package/src/providers/mock.ts +168 -0
  25. package/src/providers/plivo.test.ts +28 -0
  26. package/src/providers/plivo.ts +504 -0
  27. package/src/providers/stt-openai-realtime.ts +311 -0
  28. package/src/providers/telnyx.ts +364 -0
  29. package/src/providers/tts-openai.ts +264 -0
  30. package/src/providers/twilio/api.ts +45 -0
  31. package/src/providers/twilio/webhook.ts +30 -0
  32. package/src/providers/twilio.test.ts +64 -0
  33. package/src/providers/twilio.ts +595 -0
  34. package/src/response-generator.ts +171 -0
  35. package/src/runtime.ts +217 -0
  36. package/src/telephony-audio.ts +88 -0
  37. package/src/telephony-tts.ts +95 -0
  38. package/src/tunnel.ts +331 -0
  39. package/src/types.ts +273 -0
  40. package/src/utils.ts +12 -0
  41. package/src/voice-mapping.ts +65 -0
  42. package/src/webhook-security.test.ts +260 -0
  43. package/src/webhook-security.ts +469 -0
  44. package/src/webhook.ts +491 -0
@@ -0,0 +1,601 @@
1
+ {
2
+ "id": "voice-call",
3
+ "uiHints": {
4
+ "provider": {
5
+ "label": "Provider",
6
+ "help": "Use twilio, telnyx, or mock for dev/no-network."
7
+ },
8
+ "fromNumber": {
9
+ "label": "From Number",
10
+ "placeholder": "+15550001234"
11
+ },
12
+ "toNumber": {
13
+ "label": "Default To Number",
14
+ "placeholder": "+15550001234"
15
+ },
16
+ "inboundPolicy": {
17
+ "label": "Inbound Policy"
18
+ },
19
+ "allowFrom": {
20
+ "label": "Inbound Allowlist"
21
+ },
22
+ "inboundGreeting": {
23
+ "label": "Inbound Greeting",
24
+ "advanced": true
25
+ },
26
+ "telnyx.apiKey": {
27
+ "label": "Telnyx API Key",
28
+ "sensitive": true
29
+ },
30
+ "telnyx.connectionId": {
31
+ "label": "Telnyx Connection ID"
32
+ },
33
+ "telnyx.publicKey": {
34
+ "label": "Telnyx Public Key",
35
+ "sensitive": true
36
+ },
37
+ "twilio.accountSid": {
38
+ "label": "Twilio Account SID"
39
+ },
40
+ "twilio.authToken": {
41
+ "label": "Twilio Auth Token",
42
+ "sensitive": true
43
+ },
44
+ "outbound.defaultMode": {
45
+ "label": "Default Call Mode"
46
+ },
47
+ "outbound.notifyHangupDelaySec": {
48
+ "label": "Notify Hangup Delay (sec)",
49
+ "advanced": true
50
+ },
51
+ "serve.port": {
52
+ "label": "Webhook Port"
53
+ },
54
+ "serve.bind": {
55
+ "label": "Webhook Bind"
56
+ },
57
+ "serve.path": {
58
+ "label": "Webhook Path"
59
+ },
60
+ "tailscale.mode": {
61
+ "label": "Tailscale Mode",
62
+ "advanced": true
63
+ },
64
+ "tailscale.path": {
65
+ "label": "Tailscale Path",
66
+ "advanced": true
67
+ },
68
+ "tunnel.provider": {
69
+ "label": "Tunnel Provider",
70
+ "advanced": true
71
+ },
72
+ "tunnel.ngrokAuthToken": {
73
+ "label": "ngrok Auth Token",
74
+ "sensitive": true,
75
+ "advanced": true
76
+ },
77
+ "tunnel.ngrokDomain": {
78
+ "label": "ngrok Domain",
79
+ "advanced": true
80
+ },
81
+ "tunnel.allowNgrokFreeTierLoopbackBypass": {
82
+ "label": "Allow ngrok Free Tier (Loopback Bypass)",
83
+ "advanced": true
84
+ },
85
+ "streaming.enabled": {
86
+ "label": "Enable Streaming",
87
+ "advanced": true
88
+ },
89
+ "streaming.openaiApiKey": {
90
+ "label": "OpenAI Realtime API Key",
91
+ "sensitive": true,
92
+ "advanced": true
93
+ },
94
+ "streaming.sttModel": {
95
+ "label": "Realtime STT Model",
96
+ "advanced": true
97
+ },
98
+ "streaming.streamPath": {
99
+ "label": "Media Stream Path",
100
+ "advanced": true
101
+ },
102
+ "tts.provider": {
103
+ "label": "TTS Provider Override",
104
+ "help": "Deep-merges with messages.tts (Edge is ignored for calls).",
105
+ "advanced": true
106
+ },
107
+ "tts.openai.model": {
108
+ "label": "OpenAI TTS Model",
109
+ "advanced": true
110
+ },
111
+ "tts.openai.voice": {
112
+ "label": "OpenAI TTS Voice",
113
+ "advanced": true
114
+ },
115
+ "tts.openai.apiKey": {
116
+ "label": "OpenAI API Key",
117
+ "sensitive": true,
118
+ "advanced": true
119
+ },
120
+ "tts.elevenlabs.modelId": {
121
+ "label": "ElevenLabs Model ID",
122
+ "advanced": true
123
+ },
124
+ "tts.elevenlabs.voiceId": {
125
+ "label": "ElevenLabs Voice ID",
126
+ "advanced": true
127
+ },
128
+ "tts.elevenlabs.apiKey": {
129
+ "label": "ElevenLabs API Key",
130
+ "sensitive": true,
131
+ "advanced": true
132
+ },
133
+ "tts.elevenlabs.baseUrl": {
134
+ "label": "ElevenLabs Base URL",
135
+ "advanced": true
136
+ },
137
+ "publicUrl": {
138
+ "label": "Public Webhook URL",
139
+ "advanced": true
140
+ },
141
+ "skipSignatureVerification": {
142
+ "label": "Skip Signature Verification",
143
+ "advanced": true
144
+ },
145
+ "store": {
146
+ "label": "Call Log Store Path",
147
+ "advanced": true
148
+ },
149
+ "responseModel": {
150
+ "label": "Response Model",
151
+ "advanced": true
152
+ },
153
+ "responseSystemPrompt": {
154
+ "label": "Response System Prompt",
155
+ "advanced": true
156
+ },
157
+ "responseTimeoutMs": {
158
+ "label": "Response Timeout (ms)",
159
+ "advanced": true
160
+ }
161
+ },
162
+ "configSchema": {
163
+ "type": "object",
164
+ "additionalProperties": false,
165
+ "properties": {
166
+ "enabled": {
167
+ "type": "boolean"
168
+ },
169
+ "provider": {
170
+ "type": "string",
171
+ "enum": [
172
+ "telnyx",
173
+ "twilio",
174
+ "plivo",
175
+ "mock"
176
+ ]
177
+ },
178
+ "telnyx": {
179
+ "type": "object",
180
+ "additionalProperties": false,
181
+ "properties": {
182
+ "apiKey": {
183
+ "type": "string"
184
+ },
185
+ "connectionId": {
186
+ "type": "string"
187
+ },
188
+ "publicKey": {
189
+ "type": "string"
190
+ }
191
+ }
192
+ },
193
+ "twilio": {
194
+ "type": "object",
195
+ "additionalProperties": false,
196
+ "properties": {
197
+ "accountSid": {
198
+ "type": "string"
199
+ },
200
+ "authToken": {
201
+ "type": "string"
202
+ }
203
+ }
204
+ },
205
+ "plivo": {
206
+ "type": "object",
207
+ "additionalProperties": false,
208
+ "properties": {
209
+ "authId": {
210
+ "type": "string"
211
+ },
212
+ "authToken": {
213
+ "type": "string"
214
+ }
215
+ }
216
+ },
217
+ "fromNumber": {
218
+ "type": "string",
219
+ "pattern": "^\\+[1-9]\\d{1,14}$"
220
+ },
221
+ "toNumber": {
222
+ "type": "string",
223
+ "pattern": "^\\+[1-9]\\d{1,14}$"
224
+ },
225
+ "inboundPolicy": {
226
+ "type": "string",
227
+ "enum": [
228
+ "disabled",
229
+ "allowlist",
230
+ "pairing",
231
+ "open"
232
+ ]
233
+ },
234
+ "allowFrom": {
235
+ "type": "array",
236
+ "items": {
237
+ "type": "string",
238
+ "pattern": "^\\+[1-9]\\d{1,14}$"
239
+ }
240
+ },
241
+ "inboundGreeting": {
242
+ "type": "string"
243
+ },
244
+ "outbound": {
245
+ "type": "object",
246
+ "additionalProperties": false,
247
+ "properties": {
248
+ "defaultMode": {
249
+ "type": "string",
250
+ "enum": [
251
+ "notify",
252
+ "conversation"
253
+ ]
254
+ },
255
+ "notifyHangupDelaySec": {
256
+ "type": "integer",
257
+ "minimum": 0
258
+ }
259
+ }
260
+ },
261
+ "maxDurationSeconds": {
262
+ "type": "integer",
263
+ "minimum": 1
264
+ },
265
+ "silenceTimeoutMs": {
266
+ "type": "integer",
267
+ "minimum": 1
268
+ },
269
+ "transcriptTimeoutMs": {
270
+ "type": "integer",
271
+ "minimum": 1
272
+ },
273
+ "ringTimeoutMs": {
274
+ "type": "integer",
275
+ "minimum": 1
276
+ },
277
+ "maxConcurrentCalls": {
278
+ "type": "integer",
279
+ "minimum": 1
280
+ },
281
+ "serve": {
282
+ "type": "object",
283
+ "additionalProperties": false,
284
+ "properties": {
285
+ "port": {
286
+ "type": "integer",
287
+ "minimum": 1
288
+ },
289
+ "bind": {
290
+ "type": "string"
291
+ },
292
+ "path": {
293
+ "type": "string"
294
+ }
295
+ }
296
+ },
297
+ "tailscale": {
298
+ "type": "object",
299
+ "additionalProperties": false,
300
+ "properties": {
301
+ "mode": {
302
+ "type": "string",
303
+ "enum": [
304
+ "off",
305
+ "serve",
306
+ "funnel"
307
+ ]
308
+ },
309
+ "path": {
310
+ "type": "string"
311
+ }
312
+ }
313
+ },
314
+ "tunnel": {
315
+ "type": "object",
316
+ "additionalProperties": false,
317
+ "properties": {
318
+ "provider": {
319
+ "type": "string",
320
+ "enum": [
321
+ "none",
322
+ "ngrok",
323
+ "tailscale-serve",
324
+ "tailscale-funnel"
325
+ ]
326
+ },
327
+ "ngrokAuthToken": {
328
+ "type": "string"
329
+ },
330
+ "ngrokDomain": {
331
+ "type": "string"
332
+ },
333
+ "allowNgrokFreeTierLoopbackBypass": {
334
+ "type": "boolean"
335
+ }
336
+ }
337
+ },
338
+ "streaming": {
339
+ "type": "object",
340
+ "additionalProperties": false,
341
+ "properties": {
342
+ "enabled": {
343
+ "type": "boolean"
344
+ },
345
+ "sttProvider": {
346
+ "type": "string",
347
+ "enum": [
348
+ "openai-realtime"
349
+ ]
350
+ },
351
+ "openaiApiKey": {
352
+ "type": "string"
353
+ },
354
+ "sttModel": {
355
+ "type": "string"
356
+ },
357
+ "silenceDurationMs": {
358
+ "type": "integer",
359
+ "minimum": 1
360
+ },
361
+ "vadThreshold": {
362
+ "type": "number",
363
+ "minimum": 0,
364
+ "maximum": 1
365
+ },
366
+ "streamPath": {
367
+ "type": "string"
368
+ }
369
+ }
370
+ },
371
+ "publicUrl": {
372
+ "type": "string"
373
+ },
374
+ "skipSignatureVerification": {
375
+ "type": "boolean"
376
+ },
377
+ "stt": {
378
+ "type": "object",
379
+ "additionalProperties": false,
380
+ "properties": {
381
+ "provider": {
382
+ "type": "string",
383
+ "enum": [
384
+ "openai"
385
+ ]
386
+ },
387
+ "model": {
388
+ "type": "string"
389
+ }
390
+ }
391
+ },
392
+ "tts": {
393
+ "type": "object",
394
+ "additionalProperties": false,
395
+ "properties": {
396
+ "auto": {
397
+ "type": "string",
398
+ "enum": [
399
+ "off",
400
+ "always",
401
+ "inbound",
402
+ "tagged"
403
+ ]
404
+ },
405
+ "enabled": {
406
+ "type": "boolean"
407
+ },
408
+ "mode": {
409
+ "type": "string",
410
+ "enum": [
411
+ "final",
412
+ "all"
413
+ ]
414
+ },
415
+ "provider": {
416
+ "type": "string",
417
+ "enum": [
418
+ "openai",
419
+ "elevenlabs",
420
+ "edge"
421
+ ]
422
+ },
423
+ "summaryModel": {
424
+ "type": "string"
425
+ },
426
+ "modelOverrides": {
427
+ "type": "object",
428
+ "additionalProperties": false,
429
+ "properties": {
430
+ "enabled": {
431
+ "type": "boolean"
432
+ },
433
+ "allowText": {
434
+ "type": "boolean"
435
+ },
436
+ "allowProvider": {
437
+ "type": "boolean"
438
+ },
439
+ "allowVoice": {
440
+ "type": "boolean"
441
+ },
442
+ "allowModelId": {
443
+ "type": "boolean"
444
+ },
445
+ "allowVoiceSettings": {
446
+ "type": "boolean"
447
+ },
448
+ "allowNormalization": {
449
+ "type": "boolean"
450
+ },
451
+ "allowSeed": {
452
+ "type": "boolean"
453
+ }
454
+ }
455
+ },
456
+ "elevenlabs": {
457
+ "type": "object",
458
+ "additionalProperties": false,
459
+ "properties": {
460
+ "apiKey": {
461
+ "type": "string"
462
+ },
463
+ "baseUrl": {
464
+ "type": "string"
465
+ },
466
+ "voiceId": {
467
+ "type": "string"
468
+ },
469
+ "modelId": {
470
+ "type": "string"
471
+ },
472
+ "seed": {
473
+ "type": "integer",
474
+ "minimum": 0,
475
+ "maximum": 4294967295
476
+ },
477
+ "applyTextNormalization": {
478
+ "type": "string",
479
+ "enum": [
480
+ "auto",
481
+ "on",
482
+ "off"
483
+ ]
484
+ },
485
+ "languageCode": {
486
+ "type": "string"
487
+ },
488
+ "voiceSettings": {
489
+ "type": "object",
490
+ "additionalProperties": false,
491
+ "properties": {
492
+ "stability": {
493
+ "type": "number",
494
+ "minimum": 0,
495
+ "maximum": 1
496
+ },
497
+ "similarityBoost": {
498
+ "type": "number",
499
+ "minimum": 0,
500
+ "maximum": 1
501
+ },
502
+ "style": {
503
+ "type": "number",
504
+ "minimum": 0,
505
+ "maximum": 1
506
+ },
507
+ "useSpeakerBoost": {
508
+ "type": "boolean"
509
+ },
510
+ "speed": {
511
+ "type": "number",
512
+ "minimum": 0.5,
513
+ "maximum": 2
514
+ }
515
+ }
516
+ }
517
+ }
518
+ },
519
+ "openai": {
520
+ "type": "object",
521
+ "additionalProperties": false,
522
+ "properties": {
523
+ "apiKey": {
524
+ "type": "string"
525
+ },
526
+ "model": {
527
+ "type": "string"
528
+ },
529
+ "voice": {
530
+ "type": "string"
531
+ }
532
+ }
533
+ },
534
+ "edge": {
535
+ "type": "object",
536
+ "additionalProperties": false,
537
+ "properties": {
538
+ "enabled": {
539
+ "type": "boolean"
540
+ },
541
+ "voice": {
542
+ "type": "string"
543
+ },
544
+ "lang": {
545
+ "type": "string"
546
+ },
547
+ "outputFormat": {
548
+ "type": "string"
549
+ },
550
+ "pitch": {
551
+ "type": "string"
552
+ },
553
+ "rate": {
554
+ "type": "string"
555
+ },
556
+ "volume": {
557
+ "type": "string"
558
+ },
559
+ "saveSubtitles": {
560
+ "type": "boolean"
561
+ },
562
+ "proxy": {
563
+ "type": "string"
564
+ },
565
+ "timeoutMs": {
566
+ "type": "integer",
567
+ "minimum": 1000,
568
+ "maximum": 120000
569
+ }
570
+ }
571
+ },
572
+ "prefsPath": {
573
+ "type": "string"
574
+ },
575
+ "maxTextLength": {
576
+ "type": "integer",
577
+ "minimum": 1
578
+ },
579
+ "timeoutMs": {
580
+ "type": "integer",
581
+ "minimum": 1000,
582
+ "maximum": 120000
583
+ }
584
+ }
585
+ },
586
+ "store": {
587
+ "type": "string"
588
+ },
589
+ "responseModel": {
590
+ "type": "string"
591
+ },
592
+ "responseSystemPrompt": {
593
+ "type": "string"
594
+ },
595
+ "responseTimeoutMs": {
596
+ "type": "integer",
597
+ "minimum": 1
598
+ }
599
+ }
600
+ }
601
+ }
package/package.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@openclaw/voice-call",
3
+ "version": "2026.1.29",
4
+ "type": "module",
5
+ "description": "OpenClaw voice-call plugin",
6
+ "dependencies": {
7
+ "@sinclair/typebox": "0.34.47",
8
+ "ws": "^8.19.0",
9
+ "zod": "^4.3.6"
10
+ },
11
+ "openclaw": {
12
+ "extensions": [
13
+ "./index.ts"
14
+ ]
15
+ }
16
+ }