@openclaw/voice-call 2026.1.29 → 2026.2.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.
- package/CHANGELOG.md +31 -0
- package/README.md +13 -9
- package/index.ts +45 -49
- package/openclaw.plugin.json +11 -53
- package/package.json +6 -3
- package/src/cli.ts +80 -113
- package/src/config.test.ts +1 -4
- package/src/config.ts +88 -110
- package/src/core-bridge.ts +14 -12
- package/src/manager/context.ts +1 -1
- package/src/manager/events.ts +18 -9
- package/src/manager/lookup.ts +3 -1
- package/src/manager/outbound.ts +46 -19
- package/src/manager/state.ts +4 -6
- package/src/manager/store.ts +6 -3
- package/src/manager/timers.ts +11 -8
- package/src/manager.test.ts +7 -10
- package/src/manager.ts +53 -75
- package/src/media-stream.test.ts +0 -1
- package/src/media-stream.ts +12 -26
- package/src/providers/mock.ts +13 -16
- package/src/providers/plivo.test.ts +0 -1
- package/src/providers/plivo.ts +27 -29
- package/src/providers/stt-openai-realtime.ts +8 -8
- package/src/providers/telnyx.ts +5 -11
- package/src/providers/tts-openai.ts +9 -14
- package/src/providers/twilio/api.ts +9 -12
- package/src/providers/twilio/webhook.ts +2 -4
- package/src/providers/twilio.test.ts +1 -5
- package/src/providers/twilio.ts +34 -46
- package/src/response-generator.ts +7 -20
- package/src/runtime.ts +12 -25
- package/src/telephony-audio.ts +14 -12
- package/src/telephony-tts.ts +21 -12
- package/src/tunnel.ts +7 -24
- package/src/types.ts +0 -1
- package/src/utils.ts +3 -1
- package/src/voice-mapping.ts +3 -1
- package/src/webhook-security.test.ts +12 -21
- package/src/webhook-security.ts +25 -29
- package/src/webhook.ts +22 -57
package/CHANGELOG.md
CHANGED
|
@@ -1,13 +1,33 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026.2.1
|
|
4
|
+
|
|
5
|
+
### Changes
|
|
6
|
+
|
|
7
|
+
- Version alignment with core OpenClaw release numbers.
|
|
8
|
+
|
|
9
|
+
## 2026.1.31
|
|
10
|
+
|
|
11
|
+
### Changes
|
|
12
|
+
|
|
13
|
+
- Version alignment with core OpenClaw release numbers.
|
|
14
|
+
|
|
15
|
+
## 2026.1.30
|
|
16
|
+
|
|
17
|
+
### Changes
|
|
18
|
+
|
|
19
|
+
- Version alignment with core OpenClaw release numbers.
|
|
20
|
+
|
|
3
21
|
## 2026.1.29
|
|
4
22
|
|
|
5
23
|
### Changes
|
|
24
|
+
|
|
6
25
|
- Version alignment with core OpenClaw release numbers.
|
|
7
26
|
|
|
8
27
|
## 2026.1.26
|
|
9
28
|
|
|
10
29
|
### Changes
|
|
30
|
+
|
|
11
31
|
- Breaking: voice-call TTS now uses core `messages.tts` (plugin TTS config deep‑merges with core).
|
|
12
32
|
- Telephony TTS supports OpenAI + ElevenLabs; Edge TTS is ignored for calls.
|
|
13
33
|
- Removed legacy `tts.model`/`tts.voice`/`tts.instructions` plugin fields.
|
|
@@ -16,54 +36,65 @@
|
|
|
16
36
|
## 2026.1.23
|
|
17
37
|
|
|
18
38
|
### Changes
|
|
39
|
+
|
|
19
40
|
- Version alignment with core OpenClaw release numbers.
|
|
20
41
|
|
|
21
42
|
## 2026.1.22
|
|
22
43
|
|
|
23
44
|
### Changes
|
|
45
|
+
|
|
24
46
|
- Version alignment with core OpenClaw release numbers.
|
|
25
47
|
|
|
26
48
|
## 2026.1.21
|
|
27
49
|
|
|
28
50
|
### Changes
|
|
51
|
+
|
|
29
52
|
- Version alignment with core OpenClaw release numbers.
|
|
30
53
|
|
|
31
54
|
## 2026.1.20
|
|
32
55
|
|
|
33
56
|
### Changes
|
|
57
|
+
|
|
34
58
|
- Version alignment with core OpenClaw release numbers.
|
|
35
59
|
|
|
36
60
|
## 2026.1.17-1
|
|
37
61
|
|
|
38
62
|
### Changes
|
|
63
|
+
|
|
39
64
|
- Version alignment with core OpenClaw release numbers.
|
|
40
65
|
|
|
41
66
|
## 2026.1.17
|
|
42
67
|
|
|
43
68
|
### Changes
|
|
69
|
+
|
|
44
70
|
- Version alignment with core OpenClaw release numbers.
|
|
45
71
|
|
|
46
72
|
## 2026.1.16
|
|
47
73
|
|
|
48
74
|
### Changes
|
|
75
|
+
|
|
49
76
|
- Version alignment with core OpenClaw release numbers.
|
|
50
77
|
|
|
51
78
|
## 2026.1.15
|
|
52
79
|
|
|
53
80
|
### Changes
|
|
81
|
+
|
|
54
82
|
- Version alignment with core OpenClaw release numbers.
|
|
55
83
|
|
|
56
84
|
## 2026.1.14
|
|
57
85
|
|
|
58
86
|
### Changes
|
|
87
|
+
|
|
59
88
|
- Version alignment with core OpenClaw release numbers.
|
|
60
89
|
|
|
61
90
|
## 0.1.0
|
|
62
91
|
|
|
63
92
|
### Highlights
|
|
93
|
+
|
|
64
94
|
- First public release of the @openclaw/voice-call plugin.
|
|
65
95
|
|
|
66
96
|
### Features
|
|
97
|
+
|
|
67
98
|
- Providers: Twilio (Programmable Voice + Media Streams), Telnyx (Call Control v2), and mock provider for local dev.
|
|
68
99
|
- Call flows: outbound notify vs. conversation modes, configurable auto‑hangup, and multi‑turn continuation.
|
|
69
100
|
- Inbound handling: policy controls (disabled/allowlist/open), allowlist matching, and inbound greeting.
|
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Official Voice Call plugin for **OpenClaw**.
|
|
4
4
|
|
|
5
5
|
Providers:
|
|
6
|
+
|
|
6
7
|
- **Twilio** (Programmable Voice + Media Streams)
|
|
7
8
|
- **Telnyx** (Call Control v2)
|
|
8
9
|
- **Plivo** (Voice API + XML transfer + GetInput speech)
|
|
@@ -41,18 +42,18 @@ Put under `plugins.entries.voice-call.config`:
|
|
|
41
42
|
|
|
42
43
|
twilio: {
|
|
43
44
|
accountSid: "ACxxxxxxxx",
|
|
44
|
-
authToken: "your_token"
|
|
45
|
+
authToken: "your_token",
|
|
45
46
|
},
|
|
46
47
|
|
|
47
48
|
plivo: {
|
|
48
49
|
authId: "MAxxxxxxxxxxxxxxxxxxxx",
|
|
49
|
-
authToken: "your_token"
|
|
50
|
+
authToken: "your_token",
|
|
50
51
|
},
|
|
51
52
|
|
|
52
53
|
// Webhook server
|
|
53
54
|
serve: {
|
|
54
55
|
port: 3334,
|
|
55
|
-
path: "/voice/webhook"
|
|
56
|
+
path: "/voice/webhook",
|
|
56
57
|
},
|
|
57
58
|
|
|
58
59
|
// Public exposure (pick one):
|
|
@@ -61,17 +62,18 @@ Put under `plugins.entries.voice-call.config`:
|
|
|
61
62
|
// tailscale: { mode: "funnel", path: "/voice/webhook" }
|
|
62
63
|
|
|
63
64
|
outbound: {
|
|
64
|
-
defaultMode: "notify" // or "conversation"
|
|
65
|
+
defaultMode: "notify", // or "conversation"
|
|
65
66
|
},
|
|
66
67
|
|
|
67
68
|
streaming: {
|
|
68
69
|
enabled: true,
|
|
69
|
-
streamPath: "/voice/stream"
|
|
70
|
-
}
|
|
70
|
+
streamPath: "/voice/stream",
|
|
71
|
+
},
|
|
71
72
|
}
|
|
72
73
|
```
|
|
73
74
|
|
|
74
75
|
Notes:
|
|
76
|
+
|
|
75
77
|
- Twilio/Telnyx/Plivo require a **publicly reachable** webhook URL.
|
|
76
78
|
- `mock` is a local dev provider (no network calls).
|
|
77
79
|
- `tunnel.allowNgrokFreeTierLoopbackBypass: true` allows Twilio webhooks with invalid signatures **only** when `tunnel.provider="ngrok"` and `serve.bind` is loopback (ngrok local agent). Use for local dev only.
|
|
@@ -87,13 +89,14 @@ same shape — overrides deep-merge with `messages.tts`.
|
|
|
87
89
|
tts: {
|
|
88
90
|
provider: "openai",
|
|
89
91
|
openai: {
|
|
90
|
-
voice: "alloy"
|
|
91
|
-
}
|
|
92
|
-
}
|
|
92
|
+
voice: "alloy",
|
|
93
|
+
},
|
|
94
|
+
},
|
|
93
95
|
}
|
|
94
96
|
```
|
|
95
97
|
|
|
96
98
|
Notes:
|
|
99
|
+
|
|
97
100
|
- Edge TTS is ignored for voice calls (telephony audio needs PCM; Edge output is unreliable).
|
|
98
101
|
- Core TTS is used when Twilio media streaming is enabled; otherwise calls fall back to provider native voices.
|
|
99
102
|
|
|
@@ -114,6 +117,7 @@ openclaw voicecall expose --mode funnel
|
|
|
114
117
|
Tool name: `voice_call`
|
|
115
118
|
|
|
116
119
|
Actions:
|
|
120
|
+
|
|
117
121
|
- `initiate_call` (message, to?, mode?)
|
|
118
122
|
- `continue_call` (callId, message)
|
|
119
123
|
- `speak_to_user` (callId, message)
|
package/index.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Type } from "@sinclair/typebox";
|
|
2
2
|
import type { CoreConfig } from "./src/core-bridge.js";
|
|
3
|
+
import { registerVoiceCallCli } from "./src/cli.js";
|
|
3
4
|
import {
|
|
4
5
|
VoiceCallConfigSchema,
|
|
5
6
|
resolveVoiceCallConfig,
|
|
6
7
|
validateProviderConfig,
|
|
7
8
|
type VoiceCallConfig,
|
|
8
9
|
} from "./src/config.js";
|
|
9
|
-
import { registerVoiceCallCli } from "./src/cli.js";
|
|
10
10
|
import { createVoiceCallRuntime, type VoiceCallRuntime } from "./src/runtime.js";
|
|
11
11
|
|
|
12
12
|
const voiceCallConfigSchema = {
|
|
@@ -145,23 +145,17 @@ const voiceCallPlugin = {
|
|
|
145
145
|
description: "Voice-call plugin with Telnyx/Twilio/Plivo providers",
|
|
146
146
|
configSchema: voiceCallConfigSchema,
|
|
147
147
|
register(api) {
|
|
148
|
-
const config = resolveVoiceCallConfig(
|
|
149
|
-
voiceCallConfigSchema.parse(api.pluginConfig),
|
|
150
|
-
);
|
|
148
|
+
const config = resolveVoiceCallConfig(voiceCallConfigSchema.parse(api.pluginConfig));
|
|
151
149
|
const validation = validateProviderConfig(config);
|
|
152
150
|
|
|
153
151
|
if (api.pluginConfig && typeof api.pluginConfig === "object") {
|
|
154
152
|
const raw = api.pluginConfig as Record<string, unknown>;
|
|
155
153
|
const twilio = raw.twilio as Record<string, unknown> | undefined;
|
|
156
154
|
if (raw.provider === "log") {
|
|
157
|
-
api.logger.warn(
|
|
158
|
-
"[voice-call] provider \"log\" is deprecated; use \"mock\" instead",
|
|
159
|
-
);
|
|
155
|
+
api.logger.warn('[voice-call] provider "log" is deprecated; use "mock" instead');
|
|
160
156
|
}
|
|
161
157
|
if (typeof twilio?.from === "string") {
|
|
162
|
-
api.logger.warn(
|
|
163
|
-
"[voice-call] twilio.from is deprecated; use fromNumber instead",
|
|
164
|
-
);
|
|
158
|
+
api.logger.warn("[voice-call] twilio.from is deprecated; use fromNumber instead");
|
|
165
159
|
}
|
|
166
160
|
}
|
|
167
161
|
|
|
@@ -175,7 +169,9 @@ const voiceCallPlugin = {
|
|
|
175
169
|
if (!validation.valid) {
|
|
176
170
|
throw new Error(validation.errors.join("; "));
|
|
177
171
|
}
|
|
178
|
-
if (runtime)
|
|
172
|
+
if (runtime) {
|
|
173
|
+
return runtime;
|
|
174
|
+
}
|
|
179
175
|
if (!runtimePromise) {
|
|
180
176
|
runtimePromise = createVoiceCallRuntime({
|
|
181
177
|
config,
|
|
@@ -194,8 +190,7 @@ const voiceCallPlugin = {
|
|
|
194
190
|
|
|
195
191
|
api.registerGatewayMethod("voicecall.initiate", async ({ params, respond }) => {
|
|
196
192
|
try {
|
|
197
|
-
const message =
|
|
198
|
-
typeof params?.message === "string" ? params.message.trim() : "";
|
|
193
|
+
const message = typeof params?.message === "string" ? params.message.trim() : "";
|
|
199
194
|
if (!message) {
|
|
200
195
|
respond(false, { error: "message required" });
|
|
201
196
|
return;
|
|
@@ -210,9 +205,7 @@ const voiceCallPlugin = {
|
|
|
210
205
|
return;
|
|
211
206
|
}
|
|
212
207
|
const mode =
|
|
213
|
-
params?.mode === "notify" || params?.mode === "conversation"
|
|
214
|
-
? params.mode
|
|
215
|
-
: undefined;
|
|
208
|
+
params?.mode === "notify" || params?.mode === "conversation" ? params.mode : undefined;
|
|
216
209
|
const result = await rt.manager.initiateCall(to, undefined, {
|
|
217
210
|
message,
|
|
218
211
|
mode,
|
|
@@ -229,10 +222,8 @@ const voiceCallPlugin = {
|
|
|
229
222
|
|
|
230
223
|
api.registerGatewayMethod("voicecall.continue", async ({ params, respond }) => {
|
|
231
224
|
try {
|
|
232
|
-
const callId =
|
|
233
|
-
|
|
234
|
-
const message =
|
|
235
|
-
typeof params?.message === "string" ? params.message.trim() : "";
|
|
225
|
+
const callId = typeof params?.callId === "string" ? params.callId.trim() : "";
|
|
226
|
+
const message = typeof params?.message === "string" ? params.message.trim() : "";
|
|
236
227
|
if (!callId || !message) {
|
|
237
228
|
respond(false, { error: "callId and message required" });
|
|
238
229
|
return;
|
|
@@ -251,10 +242,8 @@ const voiceCallPlugin = {
|
|
|
251
242
|
|
|
252
243
|
api.registerGatewayMethod("voicecall.speak", async ({ params, respond }) => {
|
|
253
244
|
try {
|
|
254
|
-
const callId =
|
|
255
|
-
|
|
256
|
-
const message =
|
|
257
|
-
typeof params?.message === "string" ? params.message.trim() : "";
|
|
245
|
+
const callId = typeof params?.callId === "string" ? params.callId.trim() : "";
|
|
246
|
+
const message = typeof params?.message === "string" ? params.message.trim() : "";
|
|
258
247
|
if (!callId || !message) {
|
|
259
248
|
respond(false, { error: "callId and message required" });
|
|
260
249
|
return;
|
|
@@ -273,8 +262,7 @@ const voiceCallPlugin = {
|
|
|
273
262
|
|
|
274
263
|
api.registerGatewayMethod("voicecall.end", async ({ params, respond }) => {
|
|
275
264
|
try {
|
|
276
|
-
const callId =
|
|
277
|
-
typeof params?.callId === "string" ? params.callId.trim() : "";
|
|
265
|
+
const callId = typeof params?.callId === "string" ? params.callId.trim() : "";
|
|
278
266
|
if (!callId) {
|
|
279
267
|
respond(false, { error: "callId required" });
|
|
280
268
|
return;
|
|
@@ -304,8 +292,7 @@ const voiceCallPlugin = {
|
|
|
304
292
|
return;
|
|
305
293
|
}
|
|
306
294
|
const rt = await ensureRuntime();
|
|
307
|
-
const call =
|
|
308
|
-
rt.manager.getCall(raw) || rt.manager.getCallByProviderCallId(raw);
|
|
295
|
+
const call = rt.manager.getCall(raw) || rt.manager.getCallByProviderCallId(raw);
|
|
309
296
|
if (!call) {
|
|
310
297
|
respond(true, { found: false });
|
|
311
298
|
return;
|
|
@@ -319,8 +306,7 @@ const voiceCallPlugin = {
|
|
|
319
306
|
api.registerGatewayMethod("voicecall.start", async ({ params, respond }) => {
|
|
320
307
|
try {
|
|
321
308
|
const to = typeof params?.to === "string" ? params.to.trim() : "";
|
|
322
|
-
const message =
|
|
323
|
-
typeof params?.message === "string" ? params.message.trim() : "";
|
|
309
|
+
const message = typeof params?.message === "string" ? params.message.trim() : "";
|
|
324
310
|
if (!to) {
|
|
325
311
|
respond(false, { error: "to required" });
|
|
326
312
|
return;
|
|
@@ -342,14 +328,11 @@ const voiceCallPlugin = {
|
|
|
342
328
|
api.registerTool({
|
|
343
329
|
name: "voice_call",
|
|
344
330
|
label: "Voice Call",
|
|
345
|
-
description:
|
|
346
|
-
"Make phone calls and have voice conversations via the voice-call plugin.",
|
|
331
|
+
description: "Make phone calls and have voice conversations via the voice-call plugin.",
|
|
347
332
|
parameters: VoiceCallToolSchema,
|
|
348
333
|
async execute(_toolCallId, params) {
|
|
349
334
|
const json = (payload: unknown) => ({
|
|
350
|
-
content: [
|
|
351
|
-
{ type: "text", text: JSON.stringify(payload, null, 2) },
|
|
352
|
-
],
|
|
335
|
+
content: [{ type: "text", text: JSON.stringify(payload, null, 2) }],
|
|
353
336
|
details: payload,
|
|
354
337
|
});
|
|
355
338
|
|
|
@@ -360,12 +343,16 @@ const voiceCallPlugin = {
|
|
|
360
343
|
switch (params.action) {
|
|
361
344
|
case "initiate_call": {
|
|
362
345
|
const message = String(params.message || "").trim();
|
|
363
|
-
if (!message)
|
|
346
|
+
if (!message) {
|
|
347
|
+
throw new Error("message required");
|
|
348
|
+
}
|
|
364
349
|
const to =
|
|
365
350
|
typeof params.to === "string" && params.to.trim()
|
|
366
351
|
? params.to.trim()
|
|
367
352
|
: rt.config.toNumber;
|
|
368
|
-
if (!to)
|
|
353
|
+
if (!to) {
|
|
354
|
+
throw new Error("to required");
|
|
355
|
+
}
|
|
369
356
|
const result = await rt.manager.initiateCall(to, undefined, {
|
|
370
357
|
message,
|
|
371
358
|
mode:
|
|
@@ -404,7 +391,9 @@ const voiceCallPlugin = {
|
|
|
404
391
|
}
|
|
405
392
|
case "end_call": {
|
|
406
393
|
const callId = String(params.callId || "").trim();
|
|
407
|
-
if (!callId)
|
|
394
|
+
if (!callId) {
|
|
395
|
+
throw new Error("callId required");
|
|
396
|
+
}
|
|
408
397
|
const result = await rt.manager.endCall(callId);
|
|
409
398
|
if (!result.success) {
|
|
410
399
|
throw new Error(result.error || "end failed");
|
|
@@ -413,10 +402,11 @@ const voiceCallPlugin = {
|
|
|
413
402
|
}
|
|
414
403
|
case "get_status": {
|
|
415
404
|
const callId = String(params.callId || "").trim();
|
|
416
|
-
if (!callId)
|
|
405
|
+
if (!callId) {
|
|
406
|
+
throw new Error("callId required");
|
|
407
|
+
}
|
|
417
408
|
const call =
|
|
418
|
-
rt.manager.getCall(callId) ||
|
|
419
|
-
rt.manager.getCallByProviderCallId(callId);
|
|
409
|
+
rt.manager.getCall(callId) || rt.manager.getCallByProviderCallId(callId);
|
|
420
410
|
return json(call ? { found: true, call } : { found: false });
|
|
421
411
|
}
|
|
422
412
|
}
|
|
@@ -424,11 +414,11 @@ const voiceCallPlugin = {
|
|
|
424
414
|
|
|
425
415
|
const mode = params?.mode ?? "call";
|
|
426
416
|
if (mode === "status") {
|
|
427
|
-
const sid =
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
417
|
+
const sid = typeof params.sid === "string" ? params.sid.trim() : "";
|
|
418
|
+
if (!sid) {
|
|
419
|
+
throw new Error("sid required for status");
|
|
420
|
+
}
|
|
421
|
+
const call = rt.manager.getCall(sid) || rt.manager.getCallByProviderCallId(sid);
|
|
432
422
|
return json(call ? { found: true, call } : { found: false });
|
|
433
423
|
}
|
|
434
424
|
|
|
@@ -436,7 +426,9 @@ const voiceCallPlugin = {
|
|
|
436
426
|
typeof params.to === "string" && params.to.trim()
|
|
437
427
|
? params.to.trim()
|
|
438
428
|
: rt.config.toNumber;
|
|
439
|
-
if (!to)
|
|
429
|
+
if (!to) {
|
|
430
|
+
throw new Error("to required for call");
|
|
431
|
+
}
|
|
440
432
|
const result = await rt.manager.initiateCall(to, undefined, {
|
|
441
433
|
message:
|
|
442
434
|
typeof params.message === "string" && params.message.trim()
|
|
@@ -469,7 +461,9 @@ const voiceCallPlugin = {
|
|
|
469
461
|
api.registerService({
|
|
470
462
|
id: "voicecall",
|
|
471
463
|
start: async () => {
|
|
472
|
-
if (!config.enabled)
|
|
464
|
+
if (!config.enabled) {
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
473
467
|
try {
|
|
474
468
|
await ensureRuntime();
|
|
475
469
|
} catch (err) {
|
|
@@ -481,7 +475,9 @@ const voiceCallPlugin = {
|
|
|
481
475
|
}
|
|
482
476
|
},
|
|
483
477
|
stop: async () => {
|
|
484
|
-
if (!runtimePromise)
|
|
478
|
+
if (!runtimePromise) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
485
481
|
try {
|
|
486
482
|
const rt = await runtimePromise;
|
|
487
483
|
await rt.stop();
|
package/openclaw.plugin.json
CHANGED
|
@@ -168,12 +168,7 @@
|
|
|
168
168
|
},
|
|
169
169
|
"provider": {
|
|
170
170
|
"type": "string",
|
|
171
|
-
"enum": [
|
|
172
|
-
"telnyx",
|
|
173
|
-
"twilio",
|
|
174
|
-
"plivo",
|
|
175
|
-
"mock"
|
|
176
|
-
]
|
|
171
|
+
"enum": ["telnyx", "twilio", "plivo", "mock"]
|
|
177
172
|
},
|
|
178
173
|
"telnyx": {
|
|
179
174
|
"type": "object",
|
|
@@ -224,12 +219,7 @@
|
|
|
224
219
|
},
|
|
225
220
|
"inboundPolicy": {
|
|
226
221
|
"type": "string",
|
|
227
|
-
"enum": [
|
|
228
|
-
"disabled",
|
|
229
|
-
"allowlist",
|
|
230
|
-
"pairing",
|
|
231
|
-
"open"
|
|
232
|
-
]
|
|
222
|
+
"enum": ["disabled", "allowlist", "pairing", "open"]
|
|
233
223
|
},
|
|
234
224
|
"allowFrom": {
|
|
235
225
|
"type": "array",
|
|
@@ -247,10 +237,7 @@
|
|
|
247
237
|
"properties": {
|
|
248
238
|
"defaultMode": {
|
|
249
239
|
"type": "string",
|
|
250
|
-
"enum": [
|
|
251
|
-
"notify",
|
|
252
|
-
"conversation"
|
|
253
|
-
]
|
|
240
|
+
"enum": ["notify", "conversation"]
|
|
254
241
|
},
|
|
255
242
|
"notifyHangupDelaySec": {
|
|
256
243
|
"type": "integer",
|
|
@@ -300,11 +287,7 @@
|
|
|
300
287
|
"properties": {
|
|
301
288
|
"mode": {
|
|
302
289
|
"type": "string",
|
|
303
|
-
"enum": [
|
|
304
|
-
"off",
|
|
305
|
-
"serve",
|
|
306
|
-
"funnel"
|
|
307
|
-
]
|
|
290
|
+
"enum": ["off", "serve", "funnel"]
|
|
308
291
|
},
|
|
309
292
|
"path": {
|
|
310
293
|
"type": "string"
|
|
@@ -317,12 +300,7 @@
|
|
|
317
300
|
"properties": {
|
|
318
301
|
"provider": {
|
|
319
302
|
"type": "string",
|
|
320
|
-
"enum": [
|
|
321
|
-
"none",
|
|
322
|
-
"ngrok",
|
|
323
|
-
"tailscale-serve",
|
|
324
|
-
"tailscale-funnel"
|
|
325
|
-
]
|
|
303
|
+
"enum": ["none", "ngrok", "tailscale-serve", "tailscale-funnel"]
|
|
326
304
|
},
|
|
327
305
|
"ngrokAuthToken": {
|
|
328
306
|
"type": "string"
|
|
@@ -344,9 +322,7 @@
|
|
|
344
322
|
},
|
|
345
323
|
"sttProvider": {
|
|
346
324
|
"type": "string",
|
|
347
|
-
"enum": [
|
|
348
|
-
"openai-realtime"
|
|
349
|
-
]
|
|
325
|
+
"enum": ["openai-realtime"]
|
|
350
326
|
},
|
|
351
327
|
"openaiApiKey": {
|
|
352
328
|
"type": "string"
|
|
@@ -380,9 +356,7 @@
|
|
|
380
356
|
"properties": {
|
|
381
357
|
"provider": {
|
|
382
358
|
"type": "string",
|
|
383
|
-
"enum": [
|
|
384
|
-
"openai"
|
|
385
|
-
]
|
|
359
|
+
"enum": ["openai"]
|
|
386
360
|
},
|
|
387
361
|
"model": {
|
|
388
362
|
"type": "string"
|
|
@@ -395,30 +369,18 @@
|
|
|
395
369
|
"properties": {
|
|
396
370
|
"auto": {
|
|
397
371
|
"type": "string",
|
|
398
|
-
"enum": [
|
|
399
|
-
"off",
|
|
400
|
-
"always",
|
|
401
|
-
"inbound",
|
|
402
|
-
"tagged"
|
|
403
|
-
]
|
|
372
|
+
"enum": ["off", "always", "inbound", "tagged"]
|
|
404
373
|
},
|
|
405
374
|
"enabled": {
|
|
406
375
|
"type": "boolean"
|
|
407
376
|
},
|
|
408
377
|
"mode": {
|
|
409
378
|
"type": "string",
|
|
410
|
-
"enum": [
|
|
411
|
-
"final",
|
|
412
|
-
"all"
|
|
413
|
-
]
|
|
379
|
+
"enum": ["final", "all"]
|
|
414
380
|
},
|
|
415
381
|
"provider": {
|
|
416
382
|
"type": "string",
|
|
417
|
-
"enum": [
|
|
418
|
-
"openai",
|
|
419
|
-
"elevenlabs",
|
|
420
|
-
"edge"
|
|
421
|
-
]
|
|
383
|
+
"enum": ["openai", "elevenlabs", "edge"]
|
|
422
384
|
},
|
|
423
385
|
"summaryModel": {
|
|
424
386
|
"type": "string"
|
|
@@ -476,11 +438,7 @@
|
|
|
476
438
|
},
|
|
477
439
|
"applyTextNormalization": {
|
|
478
440
|
"type": "string",
|
|
479
|
-
"enum": [
|
|
480
|
-
"auto",
|
|
481
|
-
"on",
|
|
482
|
-
"off"
|
|
483
|
-
]
|
|
441
|
+
"enum": ["auto", "on", "off"]
|
|
484
442
|
},
|
|
485
443
|
"languageCode": {
|
|
486
444
|
"type": "string"
|
package/package.json
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/voice-call",
|
|
3
|
-
"version": "2026.1
|
|
4
|
-
"type": "module",
|
|
3
|
+
"version": "2026.2.1",
|
|
5
4
|
"description": "OpenClaw voice-call plugin",
|
|
5
|
+
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
7
|
-
"@sinclair/typebox": "0.34.
|
|
7
|
+
"@sinclair/typebox": "0.34.48",
|
|
8
8
|
"ws": "^8.19.0",
|
|
9
9
|
"zod": "^4.3.6"
|
|
10
10
|
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"openclaw": "workspace:*"
|
|
13
|
+
},
|
|
11
14
|
"openclaw": {
|
|
12
15
|
"extensions": [
|
|
13
16
|
"./index.ts"
|