@openclaw/voice-call 2026.9.8 → 2026.10.1-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 (41) hide show
  1. package/dist/.setup/{cli-D6uwMXyS.mjs → cli-Cn1leykM.mjs} +57 -37
  2. package/dist/.setup/{config-Ds7xoZbI.mjs → config-CX9J-053.mjs} +18 -119
  3. package/dist/.setup/{guarded-json-api-Bqc6QS8A.mjs → guarded-json-api-x55yqHvW.mjs} +39 -94
  4. package/dist/.setup/{mock-ClWW6Ufu.mjs → mock-eLv_Dc4o.mjs} +3 -6
  5. package/dist/.setup/{plivo-69ObrQPk.mjs → plivo-CpPIENu4.mjs} +48 -101
  6. package/dist/.setup/{realtime-handler-BgDPcPZt.mjs → realtime-handler-Do7Sz7Gh.mjs} +123 -235
  7. package/dist/.setup/{response-generator-BzKrJMEb.mjs → response-generator-DAhbXEta.mjs} +6 -25
  8. package/dist/.setup/{runtime-entry-BNOQRXii.mjs → runtime-entry-CbSKxSKk.mjs} +554 -425
  9. package/dist/.setup/state-retention-api-ONgqE-_V.mjs +201 -0
  10. package/dist/.setup/store-path-Cfwff6j7.mjs +33 -0
  11. package/dist/.setup/{telnyx-DjFGCfy1.mjs → telnyx-DPilutCH.mjs} +10 -29
  12. package/dist/.setup/{twilio-Bp3gfmUB.mjs → twilio-BWFNGyVx.mjs} +78 -201
  13. package/dist/.setup/{twilio-region-4fkgz3UG.mjs → twilio-region-wBOx-8sz.mjs} +1 -4
  14. package/dist/.setup/voice-mapping-B_iAUkER.mjs +31 -0
  15. package/dist/.setup/{webhook-replay-CCQSoBNF.mjs → webhook-replay-B7RJNWNG.mjs} +2 -5
  16. package/dist/doctor-contract-api.js +7 -330
  17. package/dist/index.js +18 -16
  18. package/dist/runtime-entry.js +1 -1
  19. package/dist/state-retention-api.js +2 -0
  20. package/openclaw.plugin.json +1 -1
  21. package/package.json +4 -6
  22. package/dist/.setup/utils-B5pnM_Ij.mjs +0 -437
  23. package/node_modules/ws/LICENSE +0 -20
  24. package/node_modules/ws/README.md +0 -548
  25. package/node_modules/ws/browser.js +0 -8
  26. package/node_modules/ws/index.js +0 -22
  27. package/node_modules/ws/lib/buffer-util.js +0 -131
  28. package/node_modules/ws/lib/constants.js +0 -19
  29. package/node_modules/ws/lib/event-target.js +0 -292
  30. package/node_modules/ws/lib/extension.js +0 -203
  31. package/node_modules/ws/lib/limiter.js +0 -55
  32. package/node_modules/ws/lib/permessage-deflate.js +0 -530
  33. package/node_modules/ws/lib/receiver.js +0 -743
  34. package/node_modules/ws/lib/sender.js +0 -607
  35. package/node_modules/ws/lib/stream.js +0 -161
  36. package/node_modules/ws/lib/subprotocol.js +0 -62
  37. package/node_modules/ws/lib/validation.js +0 -152
  38. package/node_modules/ws/lib/websocket-server.js +0 -562
  39. package/node_modules/ws/lib/websocket.js +0 -1407
  40. package/node_modules/ws/package.json +0 -74
  41. package/node_modules/ws/wrapper.mjs +0 -21
@@ -1,204 +1,9 @@
1
- import { a as CALL_RECORD_EVENT_CHUNKS_NAMESPACE, c as buildChunkKey, g as resolveVoiceCallLegacyCallLogPath, i as CALL_RECORD_EVENTS_NAMESPACE, l as buildVoiceCallLegacyJsonlEventKey, m as parseVoiceCallRecordLine, n as resolveDefaultVoiceCallStoreDir, o as CALL_RECORD_EVENT_META_MAX_ENTRIES, r as CALL_RECORD_CHUNK_MAX_ENTRIES, s as MAX_CALL_RECORD_EVENTS, t as resolveUserPath, u as encodeCallRecordEvent } from "./.setup/utils-B5pnM_Ij.mjs";
2
- import "node:module";
1
+ import { n as stateMigrations$1 } from "./.setup/state-retention-api-ONgqE-_V.mjs";
2
+ import { n as resolveVoiceCallStorePath } from "./.setup/store-path-Cfwff6j7.mjs";
3
3
  import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
4
4
  import { asOptionalRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
5
5
  import { existsSync } from "node:fs";
6
- import os from "node:os";
7
- import fs$1 from "node:fs/promises";
8
- //#region \0rolldown/runtime.js
9
- var __defProp = Object.defineProperty;
10
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
11
- var __getOwnPropNames = Object.getOwnPropertyNames;
12
- var __hasOwnProp = Object.prototype.hasOwnProperty;
13
- var __exportAll = (all, no_symbols) => {
14
- let target = {};
15
- for (var name in all) __defProp(target, name, {
16
- get: all[name],
17
- enumerable: true
18
- });
19
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
20
- return target;
21
- };
22
- var __copyProps = (to, from, except, desc) => {
23
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
24
- key = keys[i];
25
- if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
26
- get: ((k) => from[k]).bind(null, key),
27
- enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
28
- });
29
- }
30
- return to;
31
- };
32
- var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
33
- //#endregion
34
- //#region packages/normalization-core/src/record-coerce.ts
35
- /** Type guard for non-array object records at browser-safe boundaries. */
36
- function isRecord$1(value) {
37
- return value !== null && typeof value === "object" && !Array.isArray(value);
38
- }
39
- /** Returns a non-array record or null. */
40
- function asNullableRecord$1(value) {
41
- return isRecord$1(value) ? value : null;
42
- }
43
- //#endregion
44
- //#region src/config/channel-config-normalization.ts
45
- /** Applies one channel-specific doctor migration to every object-shaped account. */
46
- function normalizeChannelAccounts(params) {
47
- const rawAccounts = asNullableRecord$1(params.entry.accounts);
48
- if (!rawAccounts) return {
49
- entry: params.entry,
50
- changed: false
51
- };
52
- let changed = false;
53
- const accounts = { ...rawAccounts };
54
- for (const [accountId, value] of Object.entries(rawAccounts)) {
55
- const account = asNullableRecord$1(value);
56
- if (!account) continue;
57
- const normalized = params.normalizeAccount({
58
- account,
59
- accountId,
60
- pathPrefix: `${params.pathPrefix}.accounts.${accountId}`,
61
- changes: params.changes
62
- });
63
- if (normalized.changed) {
64
- accounts[accountId] = normalized.entry;
65
- changed = true;
66
- }
67
- }
68
- return changed ? {
69
- entry: {
70
- ...params.entry,
71
- accounts
72
- },
73
- changed: true
74
- } : {
75
- entry: params.entry,
76
- changed: false
77
- };
78
- }
79
- /** Applies the same channel-specific doctor migration at root and account scope. */
80
- function normalizeChannelConfigEntries(params) {
81
- const changes = params.changes ?? [];
82
- const channels = params.cfg.channels;
83
- const entry = asNullableRecord$1(channels?.[params.channelId]);
84
- if (!entry) return {
85
- config: params.cfg,
86
- changes
87
- };
88
- const channelPath = `channels.${params.channelId}`;
89
- const root = params.normalizeEntry({
90
- entry,
91
- pathPrefix: channelPath,
92
- changes
93
- });
94
- const accounts = normalizeChannelAccounts({
95
- entry: root.entry,
96
- pathPrefix: channelPath,
97
- changes,
98
- normalizeAccount: (accountParams) => params.normalizeEntry({
99
- entry: accountParams.account,
100
- accountId: accountParams.accountId,
101
- pathPrefix: accountParams.pathPrefix,
102
- changes: accountParams.changes
103
- })
104
- });
105
- if (!root.changed && !accounts.changed) return {
106
- config: params.cfg,
107
- changes
108
- };
109
- return {
110
- config: {
111
- ...params.cfg,
112
- channels: {
113
- ...channels,
114
- [params.channelId]: accounts.entry
115
- }
116
- },
117
- changes
118
- };
119
- }
120
- //#endregion
121
- //#region src/plugin-sdk/legacy-webhook-listener-migration.ts
122
- /** Preserve explicitly configured webhook listeners while moving ingress onto Gateway routes. */
123
- function createLegacyWebhookListenerDoctorContract(params) {
124
- const portKey = params.portKey ?? "webhookPort";
125
- const hostKey = params.hostKey === void 0 ? "webhookHost" : params.hostKey;
126
- const source = (entry) => params.webhookKey ? asNullableRecord$1(entry[params.webhookKey]) : entry;
127
- const hasLegacy = (value) => {
128
- const entry = asNullableRecord$1(value);
129
- const listener = entry && source(entry);
130
- return Boolean(listener && (Object.hasOwn(listener, portKey) || hostKey && Object.hasOwn(listener, hostKey)));
131
- };
132
- const prefix = `channels.${params.channelKey}`;
133
- return {
134
- legacyConfigRules: [{
135
- path: ["channels", params.channelKey],
136
- message: `${prefix} webhook listeners moved to Gateway routes. Run "openclaw doctor --fix" to preserve explicitly configured listener settings as legacyWebhook.`,
137
- match: (value) => {
138
- const accounts = asNullableRecord$1(asNullableRecord$1(value)?.accounts);
139
- return hasLegacy(value) || Object.values(accounts ?? {}).some(hasLegacy);
140
- }
141
- }],
142
- normalizeCompatibilityConfig: ({ cfg }) => {
143
- const root = asNullableRecord$1(asNullableRecord$1(cfg.channels)?.[params.channelKey]);
144
- const inherited = root && source(root);
145
- const canonicalRoot = asNullableRecord$1(root?.legacyWebhook);
146
- return normalizeChannelConfigEntries({
147
- cfg,
148
- channelId: params.channelKey,
149
- normalizeEntry: ({ entry, accountId, pathPrefix, changes }) => {
150
- const listener = source(entry);
151
- if (!listener || !hasLegacy(entry)) return {
152
- entry,
153
- changed: false
154
- };
155
- const next = { ...entry };
156
- const port = Object.hasOwn(listener, portKey) ? listener[portKey] : (accountId ? canonicalRoot?.port ?? inherited?.[portKey] : void 0) ?? params.defaultPort;
157
- const inheritedHost = accountId ? canonicalRoot ? canonicalRoot.host : (hostKey ? inherited?.[hostKey] : void 0) ?? params.defaultHost : params.defaultHost;
158
- const host = hostKey ? listener[hostKey] ?? inheritedHost : inheritedHost;
159
- const legacyPath = [pathPrefix, params.webhookKey].filter(Boolean).join(".");
160
- if (Object.hasOwn(entry, "legacyWebhook")) changes.push(`Removed ${legacyPath} legacy listener keys; ${pathPrefix}.legacyWebhook is already configured.`);
161
- else if (accountId && root?.legacyWebhook === false) changes.push(`Removed ${legacyPath} legacy listener keys; ${prefix}.legacyWebhook: false keeps this account's inherited listener disabled.`);
162
- else {
163
- next.legacyWebhook = {
164
- port,
165
- ...host !== void 0 ? { host } : {}
166
- };
167
- changes.push(`Moved ${legacyPath} listener settings to ${pathPrefix}.legacyWebhook. Point the external callback or reverse proxy at the Gateway port and webhook path, verify delivery, then set legacyWebhook: false to disable legacy forwarding.`);
168
- }
169
- const updated = params.webhookKey ? { ...listener } : next;
170
- delete updated[portKey];
171
- if (hostKey) delete updated[hostKey];
172
- if (params.webhookKey) {
173
- if (Object.keys(updated).length) next[params.webhookKey] = updated;
174
- else delete next[params.webhookKey];
175
- }
176
- return {
177
- entry: next,
178
- changed: true
179
- };
180
- }
181
- });
182
- }
183
- };
184
- }
185
- //#endregion
186
- //#region \0openclaw:bundled-sdk:openclaw/plugin-sdk/runtime-doctor-migrations
187
- var runtime_doctor_migrations_exports = /* @__PURE__ */ __exportAll({ createLegacyWebhookListenerDoctorContract: () => createLegacyWebhookListenerDoctorContract });
188
- import * as import_openclaw_plugin_sdk_runtime_doctor_migrations from "openclaw/plugin-sdk/runtime-doctor-migrations";
189
- __reExport(runtime_doctor_migrations_exports, import_openclaw_plugin_sdk_runtime_doctor_migrations);
190
- //#endregion
191
6
  //#region extensions/voice-call/doctor-contract-api.ts
192
- /** Read the configured voice-call store path from either package id. */
193
- function getVoiceCallConfigStore(config) {
194
- for (const pluginId of ["voice-call", "@openclaw/voice-call"]) {
195
- const rawConfig = config.plugins?.entries?.[pluginId]?.config;
196
- if (!rawConfig || typeof rawConfig !== "object" || Array.isArray(rawConfig)) continue;
197
- const store = rawConfig.store;
198
- if (typeof store === "string" && store.trim()) return store.trim();
199
- }
200
- return "";
201
- }
202
7
  /** Return Voice Call agents whose templated core session stores need migration. */
203
8
  function resolveSessionStoreAgentIds(params) {
204
9
  const agentIds = /* @__PURE__ */ new Set();
@@ -216,12 +21,6 @@ function resolveSessionStoreAgentIds(params) {
216
21
  }
217
22
  return [...agentIds].toSorted();
218
23
  }
219
- /** Resolve the voice-call store path used by legacy and plugin-state call records. */
220
- function resolveVoiceCallStorePath(params) {
221
- const configuredStore = getVoiceCallConfigStore(params.config);
222
- if (configuredStore) return resolveUserPath(configuredStore, () => params.env.HOME?.trim() || os.homedir());
223
- return resolveDefaultVoiceCallStoreDir(params.env);
224
- }
225
24
  function resolveVoiceCallStateDatabaseEnv(params) {
226
25
  return {
227
26
  ...params.env,
@@ -250,103 +49,17 @@ function describeVoiceCallSchemaMigration(migration) {
250
49
  }
251
50
  return migration.kind;
252
51
  }
253
- /** Read and prepare legacy JSONL call records, collecting line-level warnings. */
254
- async function readLegacyCallRecords(filePath) {
255
- let content;
256
- try {
257
- content = await fs$1.readFile(filePath, "utf8");
258
- } catch {
259
- return {
260
- entries: [],
261
- warnings: []
262
- };
263
- }
264
- const entries = [];
265
- const warnings = [];
266
- let index = 0;
267
- for (const line of content.split("\n")) {
268
- const parsed = parseVoiceCallRecordLine(line, index);
269
- if (!parsed) {
270
- if (line.trim()) warnings.push(`Skipped malformed Voice Call call-log line ${index + 1}`);
271
- index += 1;
272
- continue;
273
- }
274
- try {
275
- const prepared = encodeCallRecordEvent(parsed.call);
276
- const chunks = Array.from({ length: prepared.meta.chunkCount }, (_, chunkIndex) => prepared.chunk(chunkIndex));
277
- entries.push({
278
- eventKey: buildVoiceCallLegacyJsonlEventKey(line, index),
279
- lineNumber: index + 1,
280
- chunks,
281
- meta: {
282
- ...prepared.meta,
283
- persistedAt: parsed.persistedAt,
284
- sequence: parsed.sequence
285
- }
286
- });
287
- } catch (err) {
288
- warnings.push(`Skipped Voice Call call-log line ${index + 1}: ${String(err)}`);
289
- }
290
- index += 1;
291
- }
292
- return {
293
- entries,
294
- warnings
295
- };
296
- }
297
- /** Select newest missing records that fit remaining plugin state capacity. */
298
- async function selectEntriesForImport(params) {
299
- const existingEventKeys = new Set((await params.eventStore.entries()).map((entry) => entry.key));
300
- const missingEntries = params.entries.filter((entry) => !existingEventKeys.has(entry.eventKey));
301
- const existingChunks = await params.chunkStore.entries();
302
- let eventRoom = Math.max(0, MAX_CALL_RECORD_EVENTS - existingEventKeys.size);
303
- let chunkRoom = Math.max(0, CALL_RECORD_CHUNK_MAX_ENTRIES - existingChunks.length);
304
- const selected = [];
305
- let pruned = 0;
306
- for (const entry of missingEntries.toReversed()) {
307
- if (eventRoom <= 0 || entry.chunks.length > chunkRoom) {
308
- pruned++;
309
- continue;
310
- }
311
- selected.push(entry);
312
- eventRoom--;
313
- chunkRoom -= entry.chunks.length;
314
- }
315
- if (pruned > 0) params.warnings.push(`Pruned ${pruned} older Voice Call call-log ${pruned === 1 ? "record" : "records"} during migration because plugin state keeps the newest ${MAX_CALL_RECORD_EVENTS} records`);
316
- return {
317
- existingEventKeys,
318
- entries: selected.toReversed()
319
- };
320
- }
321
- /** Import prepared legacy call records into plugin state. */
322
- async function importLegacyCallRecords(params) {
323
- const selected = await selectEntriesForImport(params);
324
- let imported = 0;
325
- for (const entry of selected.entries) {
326
- if (selected.existingEventKeys.has(entry.eventKey)) continue;
327
- try {
328
- for (const chunk of entry.chunks) await params.chunkStore.register(buildChunkKey(entry.eventKey, chunk.index), chunk);
329
- await params.eventStore.register(entry.eventKey, entry.meta);
330
- selected.existingEventKeys.add(entry.eventKey);
331
- imported++;
332
- } catch (err) {
333
- params.warnings.push(`Failed migrating Voice Call call-log line ${entry.lineNumber}: ${String(err)}`);
334
- }
335
- }
336
- return imported;
337
- }
338
52
  /** Doctor migrations owned by the voice-call plugin. */
339
- const stateMigrations = [{
340
- id: "voice-call-calls-jsonl-to-plugin-state",
341
- label: "Voice Call call log",
53
+ const stateMigrations = [...stateMigrations$1, {
54
+ id: "voice-call-sqlite-schema",
55
+ label: "Voice Call SQLite schema",
342
56
  async detectLegacyState(params) {
343
57
  const storePath = resolveVoiceCallStorePath(params);
344
58
  if (!existsSync(storePath)) return null;
345
59
  const { detectOpenClawStateDatabaseSchemaMigrations } = await import("openclaw/plugin-sdk/doctor-repair-runtime");
346
- const { entries } = await readLegacyCallRecords(resolveVoiceCallLegacyCallLogPath(storePath));
347
60
  const schemaMigrations = detectOpenClawStateDatabaseSchemaMigrations({ env: resolveVoiceCallStateDatabaseEnv(params) });
348
- if (entries.length === 0 && schemaMigrations.length === 0) return null;
349
- 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})`] : []] };
61
+ if (schemaMigrations.length === 0) return null;
62
+ return { preview: schemaMigrations.map((migration) => `- Voice Call SQLite schema: ${describeVoiceCallSchemaMigration(migration)}`) };
350
63
  },
351
64
  async migrateLegacyState(params) {
352
65
  const changes = [];
@@ -357,9 +70,6 @@ const stateMigrations = [{
357
70
  warnings
358
71
  };
359
72
  const { detectOpenClawStateDatabaseSchemaMigrations, repairOpenClawStateDatabaseSchema } = await import("openclaw/plugin-sdk/doctor-repair-runtime");
360
- const filePath = resolveVoiceCallLegacyCallLogPath(storePath);
361
- const { entries, warnings: readWarnings } = await readLegacyCallRecords(filePath);
362
- warnings.push(...readWarnings);
363
73
  const stateDatabaseEnv = resolveVoiceCallStateDatabaseEnv(params);
364
74
  if (detectOpenClawStateDatabaseSchemaMigrations({ env: stateDatabaseEnv }).length > 0) {
365
75
  const repaired = repairOpenClawStateDatabaseSchema({ env: stateDatabaseEnv });
@@ -370,39 +80,6 @@ const stateMigrations = [{
370
80
  };
371
81
  changes.push(...repaired.changes.map((change) => change.replace(/^Migrated shared state /, "Migrated Voice Call SQLite ").replaceAll("→", "->")));
372
82
  }
373
- if (entries.length === 0) return {
374
- changes,
375
- warnings
376
- };
377
- const env = stateDatabaseEnv;
378
- const imported = await importLegacyCallRecords({
379
- entries,
380
- eventStore: params.context.openPluginStateKeyedStore({
381
- namespace: CALL_RECORD_EVENTS_NAMESPACE,
382
- maxEntries: CALL_RECORD_EVENT_META_MAX_ENTRIES,
383
- env
384
- }),
385
- chunkStore: params.context.openPluginStateKeyedStore({
386
- namespace: CALL_RECORD_EVENT_CHUNKS_NAMESPACE,
387
- maxEntries: CALL_RECORD_CHUNK_MAX_ENTRIES,
388
- env
389
- }),
390
- warnings
391
- });
392
- if (imported > 0) changes.push(`Migrated ${imported} Voice Call call-log ${imported === 1 ? "record" : "records"} -> plugin state`);
393
- if (warnings.some((warning) => warning.startsWith("Failed migrating Voice Call") || warning.startsWith("Skipped malformed Voice Call call-log line") || warning.startsWith("Skipped Voice Call call-log line") || warning.startsWith("Skipped Voice Call call-log migration"))) {
394
- warnings.push("Left Voice Call call-log source in place because migration was incomplete");
395
- return {
396
- changes,
397
- warnings
398
- };
399
- }
400
- await (0, runtime_doctor_migrations_exports.archiveLegacyStateSource)({
401
- filePath,
402
- label: "Voice Call call-log",
403
- changes,
404
- warnings
405
- });
406
83
  return {
407
84
  changes,
408
85
  warnings
package/dist/index.js CHANGED
@@ -1,11 +1,12 @@
1
1
  import { definePluginEntry } from "./api.js";
2
2
  import { VOICE_CALL_CLI_DESCRIPTOR } from "./cli-output-mode.js";
3
- import { i as resolveVoiceCallConfig, t as VoiceCallConfigSchema, u as validateProviderConfig } from "./.setup/config-Ds7xoZbI.mjs";
4
- import { t as createVoiceCallRuntime } from "./.setup/runtime-entry-BNOQRXii.mjs";
3
+ import { i as resolveVoiceCallConfig, t as VoiceCallConfigSchema, u as validateProviderConfig } from "./.setup/config-CX9J-053.mjs";
4
+ import { t as createVoiceCallRuntime } from "./.setup/runtime-entry-CbSKxSKk.mjs";
5
5
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
6
6
  import { ErrorCodes, errorShape } from "openclaw/plugin-sdk/gateway-runtime";
7
7
  import { resolveGlobalSingleton } from "openclaw/plugin-sdk/global-singleton";
8
8
  import { normalizeAgentId, parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
9
+ import { resolvePluginServiceScheduler } from "openclaw/plugin-sdk/runtime";
9
10
  import { asNonArrayRecord, asOptionalRecord, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
10
11
  import { jsonResult } from "openclaw/plugin-sdk/tool-results";
11
12
  import { Type } from "typebox";
@@ -140,11 +141,6 @@ function createVoiceCallContinueOperationStore(params) {
140
141
  const ttsTimeoutMs = rt.config.tts?.timeoutMs ?? params.config.tts?.timeoutMs ?? params.coreConfig.tts?.timeoutMs ?? 8e3;
141
142
  return resolveTimerTimeoutMs((rt.config.transcriptTimeoutMs ?? params.config.transcriptTimeoutMs) + ttsTimeoutMs + VOICE_CALL_CONTINUE_OPERATION_BUFFER_MS, VOICE_CALL_CONTINUE_OPERATION_BUFFER_MS);
142
143
  };
143
- const scheduleCleanup = (operationId) => {
144
- setTimeout(() => {
145
- operations.delete(operationId);
146
- }, VOICE_CALL_CONTINUE_OPERATION_CLEANUP_MS).unref?.();
147
- };
148
144
  const start = (request) => {
149
145
  const operationId = randomUUID();
150
146
  const pollTimeoutMs = resolvePollTimeoutMs(request.rt);
@@ -154,8 +150,6 @@ function createVoiceCallContinueOperationStore(params) {
154
150
  pollTimeoutMs
155
151
  });
156
152
  request.run().then((result) => {
157
- const current = operations.get(operationId);
158
- if (!current || current.status !== "pending") return;
159
153
  operations.set(operationId, {
160
154
  operationId,
161
155
  status: "completed",
@@ -165,15 +159,15 @@ function createVoiceCallContinueOperationStore(params) {
165
159
  }
166
160
  });
167
161
  }).catch((err) => {
168
- const current = operations.get(operationId);
169
- if (!current || current.status !== "pending") return;
170
162
  operations.set(operationId, {
171
163
  operationId,
172
164
  status: "failed",
173
165
  error: formatErrorMessage(err)
174
166
  });
175
167
  }).finally(() => {
176
- scheduleCleanup(operationId);
168
+ setTimeout(() => {
169
+ operations.delete(operationId);
170
+ }, VOICE_CALL_CONTINUE_OPERATION_CLEANUP_MS).unref?.();
177
171
  });
178
172
  return {
179
173
  operationId,
@@ -338,10 +332,13 @@ var voice_call_default = definePluginEntry({
338
332
  };
339
333
  return createdRuntime;
340
334
  }
335
+ const scheduler = runtimeGeneration.scheduler;
336
+ if (!scheduler || scheduler.signal.aborted) throw new VoiceCallRuntimeLifecycleError("Voice call service is not running; start the Gateway and retry");
341
337
  const startingSlot = {
342
338
  state: "starting",
343
339
  owner: runtimeGeneration,
344
340
  promise: createVoiceCallRuntime({
341
+ scheduler,
345
342
  config,
346
343
  coreConfig: api.config,
347
344
  fullConfig: api.config,
@@ -478,14 +475,16 @@ var voice_call_default = definePluginEntry({
478
475
  }
479
476
  }));
480
477
  api.registerCli(async ({ program }) => {
481
- const { registerVoiceCallCli } = await import("./.setup/cli-D6uwMXyS.mjs");
478
+ const { registerVoiceCallCli } = await import("./.setup/cli-Cn1leykM.mjs");
482
479
  registerVoiceCallCli({
483
480
  program,
484
481
  config,
485
482
  coreConfig: api.config,
486
- ensureRuntime,
487
- stateRuntime: api.runtime.state,
488
- logger: api.logger
483
+ ensureRuntime: () => {
484
+ runtimeRegistration.generation.scheduler = resolvePluginServiceScheduler();
485
+ return ensureRuntime();
486
+ },
487
+ stateRuntime: api.runtime.state
489
488
  });
490
489
  }, {
491
490
  commands: ["voicecall"],
@@ -493,6 +492,7 @@ var voice_call_default = definePluginEntry({
493
492
  });
494
493
  api.registerService({
495
494
  id: "voicecall",
495
+ apiVersion: 2,
496
496
  start: (ctx) => {
497
497
  if (isCliOnlyProcess()) return;
498
498
  try {
@@ -500,6 +500,7 @@ var voice_call_default = definePluginEntry({
500
500
  if (runtimeCoordinator.current !== runtimeRegistration) throw new VoiceCallRuntimeLifecycleError("Voice call runtime generation was superseded; use the current plugin registration");
501
501
  runtimeRegistration.generation = { retired: false };
502
502
  }
503
+ runtimeRegistration.generation.scheduler = ctx.scheduler;
503
504
  runtimeRegistration.generation.serviceHealth = ctx.serviceHealth;
504
505
  activateRuntimeGeneration(runtimeRegistration.generation);
505
506
  } catch (err) {
@@ -527,6 +528,7 @@ var voice_call_default = definePluginEntry({
527
528
  try {
528
529
  await stopVoiceCallRuntimeGeneration(runtimeCoordinator, runtimeGeneration);
529
530
  } finally {
531
+ runtimeGeneration.scheduler = void 0;
530
532
  runtimeGeneration.serviceHealth = void 0;
531
533
  }
532
534
  }
@@ -1,2 +1,2 @@
1
- import { t as createVoiceCallRuntime } from "./.setup/runtime-entry-BNOQRXii.mjs";
1
+ import { t as createVoiceCallRuntime } from "./.setup/runtime-entry-CbSKxSKk.mjs";
2
2
  export { createVoiceCallRuntime };
@@ -0,0 +1,2 @@
1
+ import { n as stateMigrations, t as packageName } from "./.setup/state-retention-api-ONgqE-_V.mjs";
2
+ export { packageName, stateMigrations };
@@ -3,7 +3,7 @@
3
3
  "categories": ["voice"],
4
4
  "doctorContract": {
5
5
  "resolveSessionStoreAgentIds": true,
6
- "stateMigrations": [{ "id": "voice-call-calls-jsonl-to-plugin-state" }]
6
+ "stateMigrations": [{ "id": "voice-call-calls-jsonl-to-plugin-state" }, { "id": "voice-call-sqlite-schema" }]
7
7
  },
8
8
  "name": "Voice Call",
9
9
  "description": "OpenClaw voice-call plugin for Twilio, Telnyx, and Plivo phone calls.",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/voice-call",
3
- "version": "2026.9.8",
3
+ "version": "2026.10.1-beta.2",
4
4
  "description": "OpenClaw voice-call plugin for Twilio, Telnyx, and Plivo phone calls.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -9,11 +9,10 @@
9
9
  "type": "module",
10
10
  "dependencies": {
11
11
  "typebox": "1.3.34",
12
- "ws": "8.21.3",
13
12
  "zod": "4.6.5"
14
13
  },
15
14
  "peerDependencies": {
16
- "openclaw": ">=2026.9.8"
15
+ "openclaw": ">=2026.10.1-beta.2"
17
16
  },
18
17
  "peerDependenciesMeta": {
19
18
  "openclaw": {
@@ -32,10 +31,10 @@
32
31
  "minHostVersion": ">=2026.4.10"
33
32
  },
34
33
  "compat": {
35
- "pluginApi": ">=2026.9.8"
34
+ "pluginApi": ">=2026.10.1-beta.2"
36
35
  },
37
36
  "build": {
38
- "openclawVersion": "2026.9.8"
37
+ "openclawVersion": "2026.10.1-beta.2"
39
38
  },
40
39
  "release": {
41
40
  "publishToClawHub": true,
@@ -56,7 +55,6 @@
56
55
  ],
57
56
  "bundledDependencies": [
58
57
  "typebox",
59
- "ws",
60
58
  "zod"
61
59
  ]
62
60
  }