@openclaw/voice-call 2026.7.1 → 2026.7.2-beta.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 (31) hide show
  1. package/README.md +1 -2
  2. package/dist/doctor-contract-api.js +34 -12
  3. package/dist/{guarded-json-api-CLVDU_h1.js → guarded-json-api-BKOgUU1q.js} +32 -41
  4. package/dist/index.js +12 -28
  5. package/dist/{plivo-D5wI4-pi.js → plivo-ByAIoPFp.js} +2 -2
  6. package/dist/{realtime-handler-Bz-MNWtD.js → realtime-handler-DJk-eYyf.js} +31 -20
  7. package/dist/{response-generator-D3YO-WtN.js → response-generator-Cdj8EuLg.js} +20 -4
  8. package/dist/{runtime-entry-DwMgbq2p.js → runtime-entry-DSgyqIwE.js} +820 -274
  9. package/dist/runtime-entry.js +1 -1
  10. package/dist/setup-api.js +86 -2
  11. package/dist/{store-DtewuuOb.js → store-path-nYL-yM0S.js} +12 -5
  12. package/dist/{telnyx-DksS6q0n.js → telnyx-DMbLYwj4.js} +1 -1
  13. package/dist/{twilio-BYcsdUkj.js → twilio-BTKKlkLT.js} +7 -10
  14. package/dist/twilio-region-4fkgz3UG.js +25 -0
  15. package/npm-shrinkwrap.json +3 -13
  16. package/openclaw.plugin.json +1 -0
  17. package/package.json +4 -6
  18. package/dist/config-CDSeuGp3.js +0 -674
  19. package/dist/config-compat-D8fcjOHp.js +0 -156
  20. package/node_modules/commander/LICENSE +0 -22
  21. package/node_modules/commander/Readme.md +0 -1172
  22. package/node_modules/commander/index.js +0 -21
  23. package/node_modules/commander/lib/argument.js +0 -147
  24. package/node_modules/commander/lib/command.js +0 -2790
  25. package/node_modules/commander/lib/error.js +0 -36
  26. package/node_modules/commander/lib/help.js +0 -731
  27. package/node_modules/commander/lib/option.js +0 -377
  28. package/node_modules/commander/lib/suggestSimilar.js +0 -99
  29. package/node_modules/commander/package-support.json +0 -19
  30. package/node_modules/commander/package.json +0 -64
  31. package/node_modules/commander/typings/index.d.ts +0 -1113
@@ -1,2 +1,2 @@
1
- import { t as createVoiceCallRuntime } from "./runtime-entry-DwMgbq2p.js";
1
+ import { t as createVoiceCallRuntime } from "./runtime-entry-DSgyqIwE.js";
2
2
  export { createVoiceCallRuntime };
package/dist/setup-api.js CHANGED
@@ -1,6 +1,90 @@
1
- import { n as migrateVoiceCallLegacyConfigInput } from "./config-compat-D8fcjOHp.js";
2
- import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
1
+ import { asOptionalRecord, isRecord, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
3
2
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
3
+ //#region extensions/voice-call/src/config-migration.ts
4
+ const asObject = asOptionalRecord;
5
+ const getString = readStringField;
6
+ /** Read finite numeric config values. */
7
+ function getNumber(obj, key) {
8
+ const value = obj?.[key];
9
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
10
+ }
11
+ /** Merge legacy provider-specific values into the canonical providers map. */
12
+ function mergeProviderConfig(providersValue, providerId, compatValues) {
13
+ if (Object.keys(compatValues).length === 0) return asObject(providersValue);
14
+ const providers = asObject(providersValue) ?? {};
15
+ const existing = asObject(providers[providerId]) ?? {};
16
+ return {
17
+ ...providers,
18
+ [providerId]: {
19
+ ...existing,
20
+ ...compatValues
21
+ }
22
+ };
23
+ }
24
+ /** Migrate legacy voice-call config input to the current canonical shape. */
25
+ function migrateVoiceCallLegacyConfigInput(params) {
26
+ const raw = asObject(params.value) ?? {};
27
+ const realtime = asObject(raw.realtime);
28
+ const realtimeAgentContext = asObject(realtime?.agentContext);
29
+ const twilio = asObject(raw.twilio);
30
+ const streaming = asObject(raw.streaming);
31
+ const configPathPrefix = params.configPathPrefix ?? "plugins.entries.voice-call.config";
32
+ const legacyStreamingOpenAICompat = {};
33
+ const streamingOpenAIApiKey = getString(streaming, "openaiApiKey");
34
+ if (streamingOpenAIApiKey) legacyStreamingOpenAICompat.apiKey = streamingOpenAIApiKey;
35
+ const streamingSttModel = getString(streaming, "sttModel");
36
+ if (streamingSttModel) legacyStreamingOpenAICompat.model = streamingSttModel;
37
+ const streamingSilenceDurationMs = getNumber(streaming, "silenceDurationMs");
38
+ if (streamingSilenceDurationMs !== void 0) legacyStreamingOpenAICompat.silenceDurationMs = streamingSilenceDurationMs;
39
+ const streamingVadThreshold = getNumber(streaming, "vadThreshold");
40
+ if (streamingVadThreshold !== void 0) legacyStreamingOpenAICompat.vadThreshold = streamingVadThreshold;
41
+ const streamingProvider = getString(streaming, "provider");
42
+ const legacyStreamingProvider = getString(streaming, "sttProvider");
43
+ const normalizedStreaming = streaming ? {
44
+ ...streaming,
45
+ provider: streamingProvider ?? legacyStreamingProvider,
46
+ providers: mergeProviderConfig(streaming.providers, "openai", legacyStreamingOpenAICompat)
47
+ } : void 0;
48
+ if (normalizedStreaming) {
49
+ delete normalizedStreaming.sttProvider;
50
+ delete normalizedStreaming.openaiApiKey;
51
+ delete normalizedStreaming.sttModel;
52
+ delete normalizedStreaming.silenceDurationMs;
53
+ delete normalizedStreaming.vadThreshold;
54
+ }
55
+ const normalizedTwilio = twilio ? { ...twilio } : void 0;
56
+ if (normalizedTwilio) delete normalizedTwilio.from;
57
+ const normalizedRealtimeAgentContext = realtimeAgentContext ? { ...realtimeAgentContext } : void 0;
58
+ if (normalizedRealtimeAgentContext) delete normalizedRealtimeAgentContext.includeSystemPrompt;
59
+ const normalizedRealtime = realtime ? {
60
+ ...realtime,
61
+ agentContext: normalizedRealtimeAgentContext ?? realtime.agentContext
62
+ } : void 0;
63
+ const config = {
64
+ ...raw,
65
+ provider: raw.provider === "log" ? "mock" : raw.provider,
66
+ fromNumber: raw.fromNumber ?? (typeof twilio?.from === "string" ? twilio.from : void 0),
67
+ twilio: normalizedTwilio,
68
+ streaming: normalizedStreaming,
69
+ realtime: normalizedRealtime
70
+ };
71
+ const changes = [];
72
+ if (raw.provider === "log") changes.push(`Moved ${configPathPrefix}.provider "log" → "mock".`);
73
+ if (typeof twilio?.from === "string" && typeof raw.fromNumber !== "string") changes.push(`Moved ${configPathPrefix}.twilio.from → ${configPathPrefix}.fromNumber.`);
74
+ if (typeof streaming?.sttProvider === "string") changes.push(`Moved ${configPathPrefix}.streaming.sttProvider → ${configPathPrefix}.streaming.provider.`);
75
+ if (typeof streaming?.openaiApiKey === "string") changes.push(`Moved ${configPathPrefix}.streaming.openaiApiKey → ${configPathPrefix}.streaming.providers.openai.apiKey.`);
76
+ if (typeof streaming?.sttModel === "string") changes.push(`Moved ${configPathPrefix}.streaming.sttModel → ${configPathPrefix}.streaming.providers.openai.model.`);
77
+ if (getNumber(streaming, "silenceDurationMs") !== void 0) changes.push(`Moved ${configPathPrefix}.streaming.silenceDurationMs → ${configPathPrefix}.streaming.providers.openai.silenceDurationMs.`);
78
+ else if (typeof streaming?.silenceDurationMs === "number") changes.push(`Removed invalid ${configPathPrefix}.streaming.silenceDurationMs.`);
79
+ if (getNumber(streaming, "vadThreshold") !== void 0) changes.push(`Moved ${configPathPrefix}.streaming.vadThreshold → ${configPathPrefix}.streaming.providers.openai.vadThreshold.`);
80
+ else if (typeof streaming?.vadThreshold === "number") changes.push(`Removed invalid ${configPathPrefix}.streaming.vadThreshold.`);
81
+ if (realtimeAgentContext && Object.hasOwn(realtimeAgentContext, "includeSystemPrompt")) changes.push(`Removed ${configPathPrefix}.realtime.agentContext.includeSystemPrompt.`);
82
+ return {
83
+ config,
84
+ changes
85
+ };
86
+ }
87
+ //#endregion
4
88
  //#region extensions/voice-call/setup-api.ts
5
89
  /** Migrate voice-call plugin config inside the full OpenClaw config object. */
6
90
  function migrateVoiceCallPluginConfig(config) {
@@ -1,7 +1,8 @@
1
1
  import { z } from "zod";
2
- import path from "node:path";
3
2
  import { createHash, randomUUID } from "node:crypto";
3
+ import path from "node:path";
4
4
  import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
5
+ import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
5
6
  //#region extensions/voice-call/src/types.ts
6
7
  const ProviderNameSchema = z.enum([
7
8
  "telnyx",
@@ -124,7 +125,7 @@ const CallRecordSchema = z.object({
124
125
  });
125
126
  //#endregion
126
127
  //#region extensions/voice-call/src/runtime-state.ts
127
- const { setRuntime: setVoiceCallStateRuntime, clearRuntime: clearVoiceCallStateRuntime, tryGetRuntime: getOptionalVoiceCallStateRuntime } = createPluginRuntimeStore({
128
+ const { setRuntime: setVoiceCallStateRuntime, tryGetRuntime: getOptionalVoiceCallStateRuntime } = createPluginRuntimeStore({
128
129
  pluginId: "voice-call-state",
129
130
  errorMessage: "Voice Call state runtime not initialized"
130
131
  });
@@ -203,8 +204,8 @@ function buildNewEventKey(order) {
203
204
  }
204
205
  /** Recover the sequence segment from newer event keys. */
205
206
  function parseEventKeySequence(key) {
206
- const match = /^event:[^:]+:(\d+):/.exec(key);
207
- return match ? Number.parseInt(match[1], 10) : 0;
207
+ const sequence = /^event:[^:]+:(\d+):/.exec(key)?.[1];
208
+ return sequence ? Number.parseInt(sequence, 10) : 0;
208
209
  }
209
210
  /** Parse a stored call record line from v2 envelope or legacy raw-call JSON. */
210
211
  function parseVoiceCallRecordLine(line, sequence = 0) {
@@ -400,4 +401,10 @@ async function getCallHistoryFromStore(storePath, limit = 50) {
400
401
  return readCallHistoryFromStore(storePath).slice(-limit);
401
402
  }
402
403
  //#endregion
403
- export { MAX_CALL_RECORD_EVENTS as a, findCallMatchesInStore as c, parseVoiceCallRecordLine as d, persistCallRecord as f, TerminalStates as g, setVoiceCallStateRuntime as h, CALL_RECORD_EVENT_META_MAX_ENTRIES as i, getCallHistoryFromStore as l, resolveVoiceCallLegacyCallLogPath as m, CALL_RECORD_EVENTS_NAMESPACE as n, RAW_CALL_RECORD_CHUNK_BYTES as o, prepareVoiceCallRecordForStorage as p, CALL_RECORD_EVENT_CHUNKS_NAMESPACE as r, buildVoiceCallLegacyJsonlEventKey as s, CALL_RECORD_CHUNK_MAX_ENTRIES as t, loadActiveCallsFromStore as u };
404
+ //#region extensions/voice-call/src/store-path.ts
405
+ /** Resolve the plugin-owned store below OpenClaw's canonical state directory. */
406
+ function resolveDefaultVoiceCallStoreDir(env = process.env) {
407
+ return path.join(resolveStateDir(env), "voice-calls");
408
+ }
409
+ //#endregion
410
+ export { TerminalStates as _, CALL_RECORD_EVENT_META_MAX_ENTRIES as a, buildVoiceCallLegacyJsonlEventKey as c, loadActiveCallsFromStore as d, parseVoiceCallRecordLine as f, setVoiceCallStateRuntime 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 };
@@ -1,4 +1,4 @@
1
- import { s as verifyTelnyxWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-CLVDU_h1.js";
1
+ import { s as verifyTelnyxWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-BKOgUU1q.js";
2
2
  import crypto from "node:crypto";
3
3
  //#region extensions/voice-call/src/providers/telnyx.ts
4
4
  function normalizeTelnyxDirection(direction) {
@@ -1,8 +1,8 @@
1
1
  import { fetchWithSsrFGuard } from "./runtime-api.js";
2
2
  import "./api.js";
3
- import { l as requireSupportedTwilioApiHostname, u as resolveTwilioApiBaseUrl } from "./config-CDSeuGp3.js";
4
- import { _ as mapVoiceToPolly, a as normalizeProviderStatus, f as chunkAudio, g as escapeXml, i as mapProviderStatusToEndReason, o as getHeader, r as isProviderStatusTerminal } from "./runtime-entry-DwMgbq2p.js";
5
- import { c as verifyTwilioWebhook, i as readProviderJsonResponseText, n as cancelProviderResponseBody, r as readProviderErrorResponseSnippet, t as guardedJsonApiRequest } from "./guarded-json-api-CLVDU_h1.js";
3
+ import { n as requireSupportedTwilioApiHostname, r as resolveTwilioApiBaseUrl } from "./twilio-region-4fkgz3UG.js";
4
+ import { _ as mapVoiceToPolly, a as normalizeProviderStatus, f as chunkAudio, g as escapeXml, i as mapProviderStatusToEndReason, o as getHeader, r as isProviderStatusTerminal } from "./runtime-entry-DSgyqIwE.js";
5
+ import { c as verifyTwilioWebhook, i as readProviderJsonResponseText, n as cancelProviderResponseBody, r as readProviderErrorResponseSnippet, t as guardedJsonApiRequest } from "./guarded-json-api-BKOgUU1q.js";
6
6
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
7
  import crypto from "node:crypto";
8
8
  import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
@@ -132,10 +132,7 @@ function verifyTwilioProviderWebhook(params) {
132
132
  trustedProxyIPs: params.options.webhookSecurity?.trustedProxyIPs,
133
133
  remoteIP: params.ctx.remoteAddress
134
134
  });
135
- if (!result.ok) {
136
- console.warn(`[twilio] Webhook verification failed: ${result.reason}`);
137
- if (result.verificationUrl) console.warn(`[twilio] Verification URL: ${result.verificationUrl}`);
138
- }
135
+ if (!result.ok) console.warn(`[twilio] Webhook verification failed: ${result.reason}`);
139
136
  return {
140
137
  ok: result.ok,
141
138
  reason: result.reason,
@@ -181,9 +178,9 @@ var TwilioProvider = class TwilioProvider {
181
178
  deleteStoredTwimlForProviderCall(providerCallId) {
182
179
  const webhookUrl = this.callWebhookUrls.get(providerCallId);
183
180
  if (!webhookUrl) return;
184
- const callIdMatch = webhookUrl.match(/callId=([^&]+)/);
185
- if (!callIdMatch) return;
186
- this.deleteStoredTwiml(callIdMatch[1]);
181
+ const callId = webhookUrl.match(/callId=([^&]+)/)?.[1];
182
+ if (!callId) return;
183
+ this.deleteStoredTwiml(callId);
187
184
  this.streamAuthTokens.delete(providerCallId);
188
185
  }
189
186
  constructor(config, options = {}) {
@@ -0,0 +1,25 @@
1
+ //#region extensions/voice-call/src/providers/twilio-region.ts
2
+ const TWILIO_REGIONS = [
3
+ "us1",
4
+ "ie1",
5
+ "au1"
6
+ ];
7
+ const TWILIO_API_HOSTNAME_BY_REGION = {
8
+ us1: "api.twilio.com",
9
+ ie1: "api.dublin.ie1.twilio.com",
10
+ au1: "api.sydney.au1.twilio.com"
11
+ };
12
+ const TWILIO_API_HOSTNAMES = new Set(Object.values(TWILIO_API_HOSTNAME_BY_REGION));
13
+ function resolveTwilioApiHostname(region) {
14
+ return TWILIO_API_HOSTNAME_BY_REGION[region ?? "us1"];
15
+ }
16
+ function resolveTwilioApiBaseUrl(params) {
17
+ return `https://${resolveTwilioApiHostname(params.region)}/2010-04-01/Accounts/${params.accountSid}`;
18
+ }
19
+ function requireSupportedTwilioApiHostname(baseUrl) {
20
+ const hostname = new URL(baseUrl).hostname;
21
+ if (!TWILIO_API_HOSTNAMES.has(hostname)) throw new Error(`Unsupported Twilio API hostname: ${hostname}`);
22
+ return hostname;
23
+ }
24
+ //#endregion
25
+ export { requireSupportedTwilioApiHostname as n, resolveTwilioApiBaseUrl as r, TWILIO_REGIONS as t };
@@ -1,20 +1,19 @@
1
1
  {
2
2
  "name": "@openclaw/voice-call",
3
- "version": "2026.7.1",
3
+ "version": "2026.7.2-beta.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/voice-call",
9
- "version": "2026.7.1",
9
+ "version": "2026.7.2-beta.2",
10
10
  "dependencies": {
11
- "commander": "15.0.0",
12
11
  "typebox": "1.3.3",
13
12
  "ws": "8.21.0",
14
13
  "zod": "4.4.3"
15
14
  },
16
15
  "peerDependencies": {
17
- "openclaw": ">=2026.7.1"
16
+ "openclaw": ">=2026.7.2-beta.2"
18
17
  },
19
18
  "peerDependenciesMeta": {
20
19
  "openclaw": {
@@ -22,15 +21,6 @@
22
21
  }
23
22
  }
24
23
  },
25
- "node_modules/commander": {
26
- "version": "15.0.0",
27
- "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz",
28
- "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==",
29
- "license": "MIT",
30
- "engines": {
31
- "node": ">=22.12.0"
32
- }
33
- },
34
24
  "node_modules/typebox": {
35
25
  "version": "1.3.3",
36
26
  "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.3.tgz",
@@ -118,6 +118,7 @@
118
118
  },
119
119
  "streaming.enabled": {
120
120
  "label": "Enable Streaming",
121
+ "help": "Classic streaming transcription currently requires the Twilio call provider.",
121
122
  "advanced": true
122
123
  },
123
124
  "streaming.provider": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/voice-call",
3
- "version": "2026.7.1",
3
+ "version": "2026.7.2-beta.2",
4
4
  "description": "OpenClaw voice-call plugin for Twilio, Telnyx, and Plivo phone calls.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,13 +8,12 @@
8
8
  },
9
9
  "type": "module",
10
10
  "dependencies": {
11
- "commander": "15.0.0",
12
11
  "typebox": "1.3.3",
13
12
  "ws": "8.21.0",
14
13
  "zod": "4.4.3"
15
14
  },
16
15
  "peerDependencies": {
17
- "openclaw": ">=2026.7.1"
16
+ "openclaw": ">=2026.7.2-beta.2"
18
17
  },
19
18
  "peerDependenciesMeta": {
20
19
  "openclaw": {
@@ -31,10 +30,10 @@
31
30
  "minHostVersion": ">=2026.4.10"
32
31
  },
33
32
  "compat": {
34
- "pluginApi": ">=2026.7.1"
33
+ "pluginApi": ">=2026.7.2-beta.2"
35
34
  },
36
35
  "build": {
37
- "openclawVersion": "2026.7.1"
36
+ "openclawVersion": "2026.7.2-beta.2"
38
37
  },
39
38
  "release": {
40
39
  "publishToClawHub": true,
@@ -52,7 +51,6 @@
52
51
  "skills/**"
53
52
  ],
54
53
  "bundledDependencies": [
55
- "commander",
56
54
  "typebox",
57
55
  "ws",
58
56
  "zod"