@openclaw/voice-call 2026.7.2-beta.5 → 2026.7.33

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 (49) hide show
  1. package/README.md +3 -2
  2. package/dist/.setup/config-BBpso-fA.mjs +674 -0
  3. package/dist/.setup/config-compat-CYRE-SSo.mjs +156 -0
  4. package/dist/{guarded-json-api-Dxhe25Zi.js → .setup/guarded-json-api-BukKSVNW.mjs} +50 -24
  5. package/dist/{mock-YsvBTX-7.js → .setup/mock-DY4yTqR2.mjs} +1 -1
  6. package/dist/{plivo-B-KvPVji.js → .setup/plivo-DFHh72MH.mjs} +3 -2
  7. package/dist/{realtime-handler-hIVPEZxN.js → .setup/realtime-handler-DWLxvZuO.mjs} +197 -128
  8. package/dist/{response-generator-z6M9229t.js → .setup/response-generator-DoC6lfcm.mjs} +6 -21
  9. package/dist/{runtime-entry-CQtitvRd.js → .setup/runtime-entry-CH-CV0Sf.mjs} +384 -868
  10. package/dist/{store-path-nYL-yM0S.js → .setup/store-1UfyY3HS.mjs} +6 -13
  11. package/dist/{telnyx-BG56lL7z.js → .setup/telnyx-Bb8X-nin.mjs} +1 -1
  12. package/dist/{twilio-B3FQ_P9x.js → .setup/twilio-Bsbf3O9A.mjs} +11 -15
  13. package/dist/{webhook-replay-aaP79dI8.js → .setup/webhook-replay-g3cDA5wr.mjs} +1 -1
  14. package/dist/cli-metadata.js +1 -5
  15. package/dist/doctor-contract-api.js +14 -36
  16. package/dist/index.js +38 -28
  17. package/dist/runtime-entry.js +1 -1
  18. package/dist/setup-api.js +2 -86
  19. package/node_modules/commander/LICENSE +22 -0
  20. package/node_modules/commander/Readme.md +1172 -0
  21. package/node_modules/commander/index.js +21 -0
  22. package/node_modules/commander/lib/argument.js +147 -0
  23. package/node_modules/commander/lib/command.js +2790 -0
  24. package/node_modules/commander/lib/error.js +36 -0
  25. package/node_modules/commander/lib/help.js +731 -0
  26. package/node_modules/commander/lib/option.js +377 -0
  27. package/node_modules/commander/lib/suggestSimilar.js +99 -0
  28. package/node_modules/commander/package-support.json +19 -0
  29. package/node_modules/commander/package.json +64 -0
  30. package/node_modules/commander/typings/index.d.ts +1113 -0
  31. package/node_modules/typebox/build/compile/validator.d.mts +4 -1
  32. package/node_modules/typebox/build/guard/guard.d.mts +1 -1
  33. package/node_modules/typebox/build/guard/guard.mjs +2 -5
  34. package/node_modules/typebox/build/guard/string.mjs +4 -16
  35. package/node_modules/typebox/build/schema/engine/_functions.mjs +15 -34
  36. package/node_modules/typebox/build/type/action/readonly_object.d.mts +0 -5
  37. package/node_modules/typebox/build/type/action/readonly_object.mjs +0 -8
  38. package/node_modules/typebox/build/typebox.d.mts +1 -1
  39. package/node_modules/typebox/build/typebox.mjs +1 -1
  40. package/node_modules/typebox/package.json +1 -1
  41. package/node_modules/typebox/readme.md +32 -29
  42. package/node_modules/ws/lib/permessage-deflate.js +3 -1
  43. package/node_modules/ws/package.json +1 -1
  44. package/openclaw.plugin.json +15 -2
  45. package/package.json +8 -6
  46. package/dist/cli-output-mode.js +0 -27
  47. package/dist/twilio-region-4fkgz3UG.js +0 -25
  48. /package/dist/{realtime-transcription.runtime-CbJAs5t_.js → .setup/realtime-transcription.runtime-CbJAs5t_.mjs} +0 -0
  49. /package/dist/{realtime-voice.runtime-vtdCOWg-.js → .setup/realtime-voice.runtime-vtdCOWg-.mjs} +0 -0
@@ -1,8 +1,7 @@
1
1
  import { z } from "zod";
2
- import { createHash, randomUUID } from "node:crypto";
3
2
  import path from "node:path";
3
+ import { createHash, randomUUID } from "node:crypto";
4
4
  import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
5
- import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
6
5
  //#region extensions/voice-call/src/types.ts
7
6
  const ProviderNameSchema = z.enum([
8
7
  "telnyx",
@@ -125,7 +124,7 @@ const CallRecordSchema = z.object({
125
124
  });
126
125
  //#endregion
127
126
  //#region extensions/voice-call/src/runtime-state.ts
128
- const { setRuntime: setVoiceCallStateRuntime, tryGetRuntime: getOptionalVoiceCallStateRuntime } = createPluginRuntimeStore({
127
+ const { setRuntime: setVoiceCallStateRuntime, clearRuntime: clearVoiceCallStateRuntime, tryGetRuntime: getOptionalVoiceCallStateRuntime } = createPluginRuntimeStore({
129
128
  pluginId: "voice-call-state",
130
129
  errorMessage: "Voice Call state runtime not initialized"
131
130
  });
@@ -141,7 +140,7 @@ const MAX_CALL_RECORD_EVENTS = 1e3;
141
140
  const CALL_RECORD_EVENT_META_MAX_ENTRIES = 1100;
142
141
  const CALL_RECORD_CHUNK_MAX_ENTRIES = 48048;
143
142
  /** Raw UTF-8 bytes stored per call record chunk before base64 encoding. */
144
- const RAW_CALL_RECORD_CHUNK_BYTES = 47 * 1024;
143
+ const RAW_CALL_RECORD_CHUNK_BYTES = 48128;
145
144
  let callRecordEventSequence = 0;
146
145
  /** Return the pre-SQLite JSONL call log path for migration/compat checks. */
147
146
  function resolveVoiceCallLegacyCallLogPath(storePath) {
@@ -204,8 +203,8 @@ function buildNewEventKey(order) {
204
203
  }
205
204
  /** Recover the sequence segment from newer event keys. */
206
205
  function parseEventKeySequence(key) {
207
- const sequence = /^event:[^:]+:(\d+):/.exec(key)?.[1];
208
- return sequence ? Number.parseInt(sequence, 10) : 0;
206
+ const match = /^event:[^:]+:(\d+):/.exec(key);
207
+ return match ? Number.parseInt(match[1], 10) : 0;
209
208
  }
210
209
  /** Parse a stored call record line from v2 envelope or legacy raw-call JSON. */
211
210
  function parseVoiceCallRecordLine(line, sequence = 0) {
@@ -401,10 +400,4 @@ async function getCallHistoryFromStore(storePath, limit = 50) {
401
400
  return readCallHistoryFromStore(storePath).slice(-limit);
402
401
  }
403
402
  //#endregion
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 };
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 };
@@ -1,4 +1,4 @@
1
- import { s as verifyTelnyxWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-Dxhe25Zi.js";
1
+ import { s as verifyTelnyxWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-BukKSVNW.mjs";
2
2
  import crypto from "node:crypto";
3
3
  //#region extensions/voice-call/src/providers/telnyx.ts
4
4
  function normalizeTelnyxDirection(direction) {
@@ -1,10 +1,9 @@
1
- import { fetchWithSsrFGuard } from "./runtime-api.js";
2
- import "./api.js";
3
- import { n as requireSupportedTwilioApiHostname, r as resolveTwilioApiBaseUrl } from "./twilio-region-4fkgz3UG.js";
4
- import { _ as escapeXml, a as normalizeProviderStatus, i as mapProviderStatusToEndReason, p as chunkAudio, r as isProviderStatusTerminal, s as getHeader, v as mapVoiceToPolly } from "./runtime-entry-CQtitvRd.js";
5
- import { c as verifyTwilioWebhook, i as readProviderJsonResponseText, n as cancelProviderResponseBody, r as readProviderErrorResponseSnippet, t as guardedJsonApiRequest } from "./guarded-json-api-Dxhe25Zi.js";
1
+ import { fetchWithSsrFGuard } from "../runtime-api.js";
2
+ import "../api.js";
3
+ import { l as requireSupportedTwilioApiHostname, u as resolveTwilioApiBaseUrl } from "./config-BBpso-fA.mjs";
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-CH-CV0Sf.mjs";
5
+ import { c as verifyTwilioWebhook, i as readProviderJsonResponseText, n as cancelProviderResponseBody, r as readProviderErrorResponseSnippet, t as guardedJsonApiRequest } from "./guarded-json-api-BukKSVNW.mjs";
6
6
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
7
- import { sleepWithAbort } from "openclaw/plugin-sdk/runtime-env";
8
7
  import crypto from "node:crypto";
9
8
  import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
10
9
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
@@ -179,9 +178,9 @@ var TwilioProvider = class TwilioProvider {
179
178
  deleteStoredTwimlForProviderCall(providerCallId) {
180
179
  const webhookUrl = this.callWebhookUrls.get(providerCallId);
181
180
  if (!webhookUrl) return;
182
- const callId = webhookUrl.match(/callId=([^&]+)/)?.[1];
183
- if (!callId) return;
184
- this.deleteStoredTwiml(callId);
181
+ const callIdMatch = webhookUrl.match(/callId=([^&]+)/);
182
+ if (!callIdMatch) return;
183
+ this.deleteStoredTwiml(callIdMatch[1]);
185
184
  this.streamAuthTokens.delete(providerCallId);
186
185
  }
187
186
  constructor(config, options = {}) {
@@ -629,12 +628,9 @@ var TwilioProvider = class TwilioProvider {
629
628
  chunkAttempts += 1;
630
629
  if (sendAudioChunk(chunk).sent) chunkDelivered += 1;
631
630
  const waitMs = nextChunkDueAt - Date.now();
632
- if (waitMs > 0) try {
633
- await sleepWithAbort(Math.ceil(waitMs), signal);
634
- } catch (error) {
635
- if (!signal.aborted) throw error;
636
- break;
637
- }
631
+ if (waitMs > 0) await new Promise((resolve) => {
632
+ setTimeout(resolve, Math.ceil(waitMs));
633
+ });
638
634
  nextChunkDueAt += CHUNK_DELAY_MS;
639
635
  if (signal.aborted) break;
640
636
  }
@@ -1,6 +1,6 @@
1
1
  import { isFutureDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
2
2
  //#region extensions/voice-call/src/webhook-replay.ts
3
- const REPLAY_WINDOW_MS = 600 * 1e3;
3
+ const REPLAY_WINDOW_MS = 6e5;
4
4
  const REPLAY_CACHE_MAX_ENTRIES = 1e4;
5
5
  const REPLAY_CACHE_PRUNE_INTERVAL = 64;
6
6
  function createWebhookReplayCache() {
@@ -1,4 +1,3 @@
1
- import { VOICE_CALL_CLI_DESCRIPTOR } from "./cli-output-mode.js";
2
1
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
3
2
  //#region extensions/voice-call/cli-metadata.ts
4
3
  var cli_metadata_default = definePluginEntry({
@@ -6,10 +5,7 @@ var cli_metadata_default = definePluginEntry({
6
5
  name: "Voice Call",
7
6
  description: "Voice call channel plugin",
8
7
  register(api) {
9
- api.registerCli(() => {}, {
10
- commands: ["voicecall"],
11
- descriptors: [VOICE_CALL_CLI_DESCRIPTOR]
12
- });
8
+ api.registerCli(() => {}, { commands: ["voicecall"] });
13
9
  }
14
10
  });
15
11
  //#endregion
@@ -1,9 +1,9 @@
1
- import { a as CALL_RECORD_EVENT_META_MAX_ENTRIES, c as buildVoiceCallLegacyJsonlEventKey, f as parseVoiceCallRecordLine, h as resolveVoiceCallLegacyCallLogPath, i as CALL_RECORD_EVENT_CHUNKS_NAMESPACE, m as prepareVoiceCallRecordForStorage, n as CALL_RECORD_CHUNK_MAX_ENTRIES, o as MAX_CALL_RECORD_EVENTS, r as CALL_RECORD_EVENTS_NAMESPACE, s as RAW_CALL_RECORD_CHUNK_BYTES, t as resolveDefaultVoiceCallStoreDir } from "./store-path-nYL-yM0S.js";
1
+ import { a as MAX_CALL_RECORD_EVENTS, d as parseVoiceCallRecordLine, i as CALL_RECORD_EVENT_META_MAX_ENTRIES, m as resolveVoiceCallLegacyCallLogPath, n as CALL_RECORD_EVENTS_NAMESPACE, o as RAW_CALL_RECORD_CHUNK_BYTES, p as prepareVoiceCallRecordForStorage, r as CALL_RECORD_EVENT_CHUNKS_NAMESPACE, s as buildVoiceCallLegacyJsonlEventKey, t as CALL_RECORD_CHUNK_MAX_ENTRIES } from "./.setup/store-1UfyY3HS.mjs";
2
2
  import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
3
- import path from "node:path";
4
3
  import os from "node:os";
4
+ import path from "node:path";
5
5
  import fs from "node:fs/promises";
6
- import { archiveLegacyStateSource, detectOpenClawStateDatabaseSchemaMigrations, repairOpenClawStateDatabaseSchema } from "openclaw/plugin-sdk/runtime-doctor";
6
+ import { archiveLegacyStateSource } from "openclaw/plugin-sdk/runtime-doctor";
7
7
  //#region extensions/voice-call/doctor-contract-api.ts
8
8
  /** Resolve home from doctor env with OS fallback. */
9
9
  function resolveHome(env) {
@@ -50,23 +50,7 @@ function resolveSessionStoreAgentIds(params) {
50
50
  function resolveVoiceCallStorePath(params) {
51
51
  const configuredStore = getVoiceCallConfigStore(params.config);
52
52
  if (configuredStore) return resolveUserPath(configuredStore, params.env);
53
- return resolveDefaultVoiceCallStoreDir(params.env);
54
- }
55
- function resolveVoiceCallStateDatabaseEnv(params) {
56
- return {
57
- ...params.env,
58
- OPENCLAW_STATE_DIR: resolveVoiceCallStorePath(params)
59
- };
60
- }
61
- function describeVoiceCallSchemaMigration(migration) {
62
- switch (migration.kind) {
63
- case "agent-databases-composite-primary-key": return "agent database registry primary key -> agent_id,path";
64
- case "audit-events-v2": return "audit event ledger -> versioned message lifecycle schema";
65
- case "operator-approvals-system-agent": return "operator approvals -> OpenClaw system changes";
66
- case "session-watch-cursor-provenance-v4": return "session watch cursors -> provenance column";
67
- case "strict-tables-v3": return "tables -> SQLite STRICT typing";
68
- }
69
- return migration.kind;
53
+ return path.join(resolveHome(params.env), ".openclaw", "voice-calls");
70
54
  }
71
55
  /** Return true when a path exists and is a file. */
72
56
  /** Build the plugin state key for one migrated event chunk. */
@@ -185,32 +169,26 @@ const stateMigrations = [{
185
169
  id: "voice-call-calls-jsonl-to-plugin-state",
186
170
  label: "Voice Call call log",
187
171
  async detectLegacyState(params) {
188
- const { entries } = await readLegacyCallRecords(resolveVoiceCallLegacyCallLogPath(resolveVoiceCallStorePath(params)));
189
- const schemaMigrations = detectOpenClawStateDatabaseSchemaMigrations({ env: resolveVoiceCallStateDatabaseEnv(params) });
190
- if (entries.length === 0 && schemaMigrations.length === 0) return null;
191
- return { preview: [...schemaMigrations.map((migration) => `- Voice Call SQLite schema: ${describeVoiceCallSchemaMigration(migration)}`), ...entries.length > 0 ? [`- Voice Call call log: ${entries.length} ${entries.length === 1 ? "record" : "records"} -> plugin state (${CALL_RECORD_EVENTS_NAMESPACE})`] : []] };
172
+ const storePath = resolveVoiceCallStorePath(params);
173
+ const { entries } = await readLegacyCallRecords(resolveVoiceCallLegacyCallLogPath(storePath));
174
+ if (entries.length === 0) return null;
175
+ return { preview: [`- Voice Call call log: ${entries.length} ${entries.length === 1 ? "record" : "records"} -> plugin state (${CALL_RECORD_EVENTS_NAMESPACE})`] };
192
176
  },
193
177
  async migrateLegacyState(params) {
194
178
  const changes = [];
195
179
  const warnings = [];
196
- const filePath = resolveVoiceCallLegacyCallLogPath(resolveVoiceCallStorePath(params));
180
+ const storePath = resolveVoiceCallStorePath(params);
181
+ const filePath = resolveVoiceCallLegacyCallLogPath(storePath);
197
182
  const { entries, warnings: readWarnings } = await readLegacyCallRecords(filePath);
198
183
  warnings.push(...readWarnings);
199
- const stateDatabaseEnv = resolveVoiceCallStateDatabaseEnv(params);
200
- if (detectOpenClawStateDatabaseSchemaMigrations({ env: stateDatabaseEnv }).length > 0) {
201
- const repaired = repairOpenClawStateDatabaseSchema({ env: stateDatabaseEnv });
202
- warnings.push(...repaired.warnings);
203
- if (repaired.warnings.length > 0) return {
204
- changes,
205
- warnings
206
- };
207
- changes.push(...repaired.changes.map((change) => change.replace(/^Migrated shared state /, "Migrated Voice Call SQLite ").replaceAll("→", "->")));
208
- }
209
184
  if (entries.length === 0) return {
210
185
  changes,
211
186
  warnings
212
187
  };
213
- const env = stateDatabaseEnv;
188
+ const env = {
189
+ ...params.env,
190
+ OPENCLAW_STATE_DIR: storePath
191
+ };
214
192
  const imported = await importLegacyCallRecords({
215
193
  entries,
216
194
  eventStore: params.context.openPluginStateKeyedStore({
package/dist/index.js CHANGED
@@ -1,24 +1,27 @@
1
1
  import { definePluginEntry, sleep } from "./runtime-api.js";
2
2
  import "./api.js";
3
- import { VOICE_CALL_CLI_DESCRIPTOR } from "./cli-output-mode.js";
4
- import { S as validateProviderConfig, b as resolveVoiceCallConfig, c as resolveWebhookExposureStatus, d as setupTailscaleExposureRoute, g as resolveUserPath, l as cleanupTailscaleExposureRoute, t as createVoiceCallRuntime, u as getTailscaleSelfInfo, y as VoiceCallConfigSchema } from "./runtime-entry-CQtitvRd.js";
5
- import { g as setVoiceCallStateRuntime, t as resolveDefaultVoiceCallStoreDir, u as getCallHistoryFromStore } from "./store-path-nYL-yM0S.js";
3
+ import { c as validateProviderConfig, i as resolveVoiceCallConfig } from "./.setup/config-BBpso-fA.mjs";
4
+ import { c as cleanupTailscaleExposureRoute, h as resolveUserPath, l as getTailscaleSelfInfo, s as resolveWebhookExposureStatus, t as createVoiceCallRuntime, u as setupTailscaleExposureRoute } from "./.setup/runtime-entry-CH-CV0Sf.mjs";
5
+ import { h as setVoiceCallStateRuntime, l as getCallHistoryFromStore } from "./.setup/store-1UfyY3HS.mjs";
6
+ import { i as parseVoiceCallPluginConfig, r as normalizeVoiceCallLegacyConfigInput, t as formatVoiceCallLegacyConfigWarnings } from "./.setup/config-compat-CYRE-SSo.mjs";
6
7
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
7
8
  import { ErrorCodes, callGatewayFromCli, errorShape } from "openclaw/plugin-sdk/gateway-runtime";
8
9
  import { MAX_TCP_PORT, MAX_TIMER_TIMEOUT_MS, addTimerTimeoutGraceMs, clampTimerTimeoutMs, parseStrictNonNegativeInteger, resolveTimerTimeoutMs, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
9
10
  import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
10
- import { asOptionalRecord, isRecord, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
11
+ import { isRecord, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
11
12
  import { jsonResult } from "openclaw/plugin-sdk/tool-results";
12
13
  import { Type } from "typebox";
13
14
  import fs from "node:fs";
14
- import { randomUUID } from "node:crypto";
15
+ import os from "node:os";
15
16
  import path from "node:path";
17
+ import { randomUUID } from "node:crypto";
16
18
  import { format } from "node:util";
17
19
  //#region extensions/voice-call/src/cli.ts
18
20
  const VOICE_CALL_GATEWAY_DEFAULT_TIMEOUT_MS = 5e3;
19
21
  const VOICE_CALL_GATEWAY_OPERATION_TIMEOUT_MS = 3e4;
20
22
  const VOICE_CALL_GATEWAY_TRANSCRIPT_BUFFER_MS = 1e4;
21
23
  const VOICE_CALL_GATEWAY_POLL_INTERVAL_MS = 1e3;
24
+ const voiceCallCliDeps = { callGatewayFromCli };
22
25
  function writeStdoutLine(...values) {
23
26
  process.stdout.write(`${format(...values)}\n`);
24
27
  }
@@ -27,7 +30,8 @@ function writeStdoutJson(value) {
27
30
  }
28
31
  function parseVoiceCallIntOption(raw, optionName, opts) {
29
32
  const min = opts?.min ?? 0;
30
- const parsed = parseStrictNonNegativeInteger(raw?.trim() ?? "");
33
+ const value = raw?.trim() ?? "";
34
+ const parsed = parseStrictNonNegativeInteger(value);
31
35
  if (parsed === void 0 || parsed < min || opts?.max !== void 0 && parsed > opts.max) throw new Error(`Invalid numeric value for ${optionName}: ${raw ?? ""}`);
32
36
  return parsed;
33
37
  }
@@ -40,7 +44,7 @@ async function callVoiceCallGateway(method, params, opts) {
40
44
  const timeoutMs = typeof opts?.timeoutMs === "number" && Number.isFinite(opts.timeoutMs) ? Math.max(1, Math.ceil(opts.timeoutMs)) : VOICE_CALL_GATEWAY_DEFAULT_TIMEOUT_MS;
41
45
  return {
42
46
  ok: true,
43
- payload: await callGatewayFromCli(method, {
47
+ payload: await voiceCallCliDeps.callGatewayFromCli(method, {
44
48
  json: true,
45
49
  timeout: String(timeoutMs)
46
50
  }, params, { progress: false })
@@ -88,17 +92,13 @@ function readCompletedContinueResult(payload) {
88
92
  }
89
93
  async function pollVoiceCallContinueGateway(params) {
90
94
  const deadlineMs = resolveVoiceCallDeadlineMs(params.timeoutMs);
91
- for (;;) {
92
- const remainingMs = deadlineMs - Date.now();
93
- if (remainingMs <= 0) break;
94
- const gateway = await callVoiceCallGateway("voicecall.continue.result", { operationId: params.operationId }, { timeoutMs: Math.min(VOICE_CALL_GATEWAY_DEFAULT_TIMEOUT_MS, remainingMs) });
95
+ while (Date.now() <= deadlineMs) {
96
+ const gateway = await callVoiceCallGateway("voicecall.continue.result", { operationId: params.operationId }, { timeoutMs: VOICE_CALL_GATEWAY_DEFAULT_TIMEOUT_MS });
95
97
  if (!gateway.ok) throw new Error(`gateway unavailable while waiting for voicecall continue result: ${formatErrorMessage(gateway.error)}`);
96
98
  const result = readCompletedContinueResult(gateway.payload);
97
99
  if (result.status === "completed") return result.result;
98
100
  if (result.status === "failed") throw new Error(result.error);
99
- const sleepMs = Math.min(VOICE_CALL_GATEWAY_POLL_INTERVAL_MS, deadlineMs - Date.now());
100
- if (sleepMs <= 0) break;
101
- await sleep(sleepMs);
101
+ await sleep(Math.min(VOICE_CALL_GATEWAY_POLL_INTERVAL_MS, Math.max(1, deadlineMs - Date.now())));
102
102
  }
103
103
  throw new Error("voicecall continue timed out waiting for gateway operation");
104
104
  }
@@ -108,7 +108,16 @@ function resolveMode(input) {
108
108
  return "funnel";
109
109
  }
110
110
  function resolveDefaultStorePath(config) {
111
- const base = config.store?.trim() ? resolveUserPath(config.store) : resolveDefaultVoiceCallStoreDir();
111
+ const preferred = path.join(os.homedir(), ".openclaw", "voice-calls");
112
+ const resolvedPreferred = resolveUserPath(preferred);
113
+ const existing = [resolvedPreferred].find((dir) => {
114
+ try {
115
+ return fs.existsSync(path.join(dir, "calls.jsonl")) || fs.existsSync(dir);
116
+ } catch {
117
+ return false;
118
+ }
119
+ }) ?? resolvedPreferred;
120
+ const base = config.store?.trim() ? resolveUserPath(config.store) : existing;
112
121
  return path.join(base, "calls.jsonl");
113
122
  }
114
123
  function percentile(values, p) {
@@ -533,12 +542,12 @@ function registerVoiceCallCli(params) {
533
542
  //#endregion
534
543
  //#region extensions/voice-call/src/gateway-continue-operation.ts
535
544
  const VOICE_CALL_CONTINUE_OPERATION_BUFFER_MS = 3e4;
536
- const VOICE_CALL_CONTINUE_OPERATION_CLEANUP_MS = 300 * 1e3;
545
+ const VOICE_CALL_CONTINUE_OPERATION_CLEANUP_MS = 3e5;
537
546
  /** Create a process-local operation store for gateway continue-call polling. */
538
547
  function createVoiceCallContinueOperationStore(params) {
539
548
  const operations = /* @__PURE__ */ new Map();
540
549
  const resolvePollTimeoutMs = (rt) => {
541
- const ttsTimeoutMs = rt.config.tts?.timeoutMs ?? params.config.tts?.timeoutMs ?? params.coreConfig.tts?.timeoutMs ?? 8e3;
550
+ const ttsTimeoutMs = rt.config.tts?.timeoutMs ?? params.config.tts?.timeoutMs ?? params.coreConfig.messages?.tts?.timeoutMs ?? 8e3;
542
551
  return resolveTimerTimeoutMs((rt.config.transcriptTimeoutMs ?? params.config.transcriptTimeoutMs) + ttsTimeoutMs + VOICE_CALL_CONTINUE_OPERATION_BUFFER_MS, VOICE_CALL_CONTINUE_OPERATION_BUFFER_MS);
543
552
  };
544
553
  const scheduleCleanup = (operationId) => {
@@ -651,12 +660,12 @@ const VOICE_CALL_WRITE_METHOD_SCOPE = { scope: "operator.write" };
651
660
  const VOICE_CALL_READ_METHOD_SCOPE = { scope: "operator.read" };
652
661
  const voiceCallConfigSchema = {
653
662
  parse(value) {
654
- const config = asOptionalRecord(value) ?? {};
655
- const enabled = typeof config.enabled === "boolean" ? config.enabled : true;
656
- return VoiceCallConfigSchema.parse({
657
- ...config,
663
+ const normalized = normalizeVoiceCallLegacyConfigInput(value);
664
+ const enabled = typeof normalized.enabled === "boolean" ? normalized.enabled : true;
665
+ return parseVoiceCallPluginConfig({
666
+ ...normalized,
658
667
  enabled,
659
- provider: config.provider ?? (enabled ? "mock" : void 0)
668
+ provider: normalized.provider ?? (enabled ? "mock" : void 0)
660
669
  });
661
670
  },
662
671
  uiHints: {
@@ -736,7 +745,6 @@ const voiceCallConfigSchema = {
736
745
  },
737
746
  "streaming.enabled": {
738
747
  label: "Enable Streaming",
739
- help: "Classic streaming transcription currently requires the Twilio call provider.",
740
748
  advanced: true
741
749
  },
742
750
  "streaming.provider": {
@@ -827,7 +835,7 @@ const voiceCallConfigSchema = {
827
835
  },
828
836
  "tts.provider": {
829
837
  label: "TTS Provider Override",
830
- help: "Deep-merges with tts (Microsoft is ignored for calls).",
838
+ help: "Deep-merges with messages.tts (Microsoft is ignored for calls).",
831
839
  advanced: true
832
840
  },
833
841
  "tts.providers": {
@@ -946,6 +954,11 @@ var voice_call_default = definePluginEntry({
946
954
  register(api) {
947
955
  const config = resolveVoiceCallConfig(voiceCallConfigSchema.parse(api.pluginConfig));
948
956
  const validation = validateProviderConfig(config);
957
+ if (api.pluginConfig && typeof api.pluginConfig === "object") for (const warning of formatVoiceCallLegacyConfigWarnings({
958
+ value: api.pluginConfig,
959
+ configPathPrefix: "plugins.entries.voice-call.config",
960
+ doctorFixCommand: "openclaw doctor --fix"
961
+ })) api.logger.warn(warning);
949
962
  const runtimeState = getVoiceCallRuntimeGlobalState();
950
963
  const continueOperationStore = createVoiceCallContinueOperationStore({
951
964
  config,
@@ -1350,10 +1363,7 @@ var voice_call_default = definePluginEntry({
1350
1363
  ensureRuntime,
1351
1364
  stateRuntime: api.runtime.state,
1352
1365
  logger: api.logger
1353
- }), {
1354
- commands: ["voicecall"],
1355
- descriptors: [VOICE_CALL_CLI_DESCRIPTOR]
1356
- });
1366
+ }), { commands: ["voicecall"] });
1357
1367
  api.registerService({
1358
1368
  id: "voicecall",
1359
1369
  start: () => {
@@ -1,2 +1,2 @@
1
- import { t as createVoiceCallRuntime } from "./runtime-entry-CQtitvRd.js";
1
+ import { t as createVoiceCallRuntime } from "./.setup/runtime-entry-CH-CV0Sf.mjs";
2
2
  export { createVoiceCallRuntime };
package/dist/setup-api.js CHANGED
@@ -1,90 +1,6 @@
1
- import { asOptionalRecord, isRecord, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
1
+ import { n as migrateVoiceCallLegacyConfigInput } from "./.setup/config-compat-CYRE-SSo.mjs";
2
+ import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
2
3
  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
88
4
  //#region extensions/voice-call/setup-api.ts
89
5
  /** Migrate voice-call plugin config inside the full OpenClaw config object. */
90
6
  function migrateVoiceCallPluginConfig(config) {
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.