@openclaw/voice-call 2026.6.11-beta.2 → 2026.7.1-beta.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.
@@ -2,7 +2,9 @@ import { TtsConfigSchema } from "./runtime-api.js";
2
2
  import "./api.js";
3
3
  import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  import { REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME, REALTIME_VOICE_AGENT_CONSULT_TOOL_POLICIES } from "openclaw/plugin-sdk/realtime-voice";
5
+ import { normalizeAgentId, parseAgentSessionKey } from "openclaw/plugin-sdk/routing";
5
6
  import { buildSecretInputSchema, hasConfiguredSecretInput, normalizeResolvedSecretInputString } from "openclaw/plugin-sdk/secret-input";
7
+ import { canonicalizeMainSessionAlias } from "openclaw/plugin-sdk/session-store-runtime";
6
8
  import { z } from "zod";
7
9
  //#region extensions/voice-call/src/deep-merge.ts
8
10
  const BLOCKED_MERGE_KEYS = new Set([
@@ -442,6 +444,13 @@ function resolveVoiceCallNumberRouteKey(config, phone) {
442
444
  if (!normalizedPhone) return;
443
445
  return Object.keys(routes).find((routeKey) => normalizePhoneRouteKey(routeKey) === normalizedPhone);
444
446
  }
447
+ /** Resolve inbound-only number routing from a persisted call record. */
448
+ function resolveVoiceCallNumberRouteKeyForCall(call) {
449
+ if (call.direction !== "inbound") return;
450
+ const storedRouteKey = call.metadata?.numberRouteKey;
451
+ if (typeof storedRouteKey === "string") return storedRouteKey;
452
+ return call.to;
453
+ }
445
454
  function resolveVoiceCallEffectiveConfig(config, phoneOrRouteKey) {
446
455
  const numberRouteKey = resolveVoiceCallNumberRouteKey(config, phoneOrRouteKey);
447
456
  if (!numberRouteKey) return { config };
@@ -538,10 +547,37 @@ function normalizeVoiceCallConfig(config) {
538
547
  }
539
548
  function resolveVoiceCallSessionKey(params) {
540
549
  const explicit = params.explicitSessionKey?.trim();
541
- if (explicit) return explicit;
542
- if (params.config.sessionScope === "per-call") return `voice:call:${params.callId}`;
550
+ if (explicit) return resolveVoiceCallAgentSessionKey({
551
+ config: params.config,
552
+ sessionKey: explicit,
553
+ coreSession: params.coreSession
554
+ });
555
+ const prefix = `agent:${normalizeAgentId(params.config.agentId)}:voice`;
556
+ if (params.config.sessionScope === "per-call") return `${prefix}:call:${params.callId}`.toLowerCase();
543
557
  const normalizedPhone = params.phone?.replace(/\D/g, "");
544
- return normalizedPhone ? `voice:${normalizedPhone}` : `voice:${params.callId}`;
558
+ return (normalizedPhone ? `${prefix}:${normalizedPhone}` : `${prefix}:${params.callId}`).toLowerCase();
559
+ }
560
+ /** Resolve persisted or integration-provided keys into the configured agent namespace. */
561
+ function resolveVoiceCallAgentSessionKey(params) {
562
+ const sessionKey = params.sessionKey.trim();
563
+ if (!sessionKey) throw new Error("Voice Call session key cannot be empty");
564
+ const lower = sessionKey.toLowerCase();
565
+ const agentId = normalizeAgentId(params.config.agentId);
566
+ if (lower === "global" || lower === "unknown") return lower;
567
+ const parsedInput = parseAgentSessionKey(sessionKey);
568
+ let normalizedScopedKey;
569
+ if (parsedInput && normalizeAgentId(parsedInput.agentId) === parsedInput.agentId && parsedInput.agentId === agentId) normalizedScopedKey = `agent:${parsedInput.agentId}:${parsedInput.rest}`;
570
+ else {
571
+ const wrappedInput = parseAgentSessionKey(`agent:${agentId}:${sessionKey}`);
572
+ if (!wrappedInput) throw new Error("Voice Call session key could not be normalized");
573
+ normalizedScopedKey = `agent:${agentId}:${wrappedInput.rest}`;
574
+ }
575
+ const canonicalMain = canonicalizeMainSessionAlias({
576
+ cfg: { session: params.coreSession },
577
+ agentId,
578
+ sessionKey: normalizedScopedKey
579
+ });
580
+ return canonicalMain === normalizedScopedKey ? normalizedScopedKey : canonicalMain;
545
581
  }
546
582
  /**
547
583
  * Resolves the configuration by merging environment variables into missing fields.
@@ -616,4 +652,4 @@ function validateProviderConfig(config) {
616
652
  };
617
653
  }
618
654
  //#endregion
619
- export { resolveVoiceCallEffectiveConfig as a, normalizePath as c, resolveVoiceCallConfig as i, deepMergeDefined as l, normalizeVoiceCallConfig as n, resolveVoiceCallSessionKey as o, resolveTwilioAuthToken as r, validateProviderConfig as s, VoiceCallConfigSchema as t };
655
+ export { resolveVoiceCallEffectiveConfig as a, validateProviderConfig as c, resolveVoiceCallConfig as i, normalizePath as l, normalizeVoiceCallConfig as n, resolveVoiceCallNumberRouteKeyForCall as o, resolveTwilioAuthToken as r, resolveVoiceCallSessionKey as s, VoiceCallConfigSchema as t, deepMergeDefined as u };
@@ -1,4 +1,4 @@
1
- import { t as VoiceCallConfigSchema } from "./config-BKDAAd8j.js";
1
+ import { t as VoiceCallConfigSchema } from "./config-DNtTB7Iw.js";
2
2
  import { asOptionalRecord, readStringField } from "openclaw/plugin-sdk/string-coerce-runtime";
3
3
  //#region extensions/voice-call/src/config-compat.ts
4
4
  /** Version where legacy voice-call config shape support is removed. */
@@ -1,4 +1,5 @@
1
1
  import { a as MAX_CALL_RECORD_EVENTS, f as prepareVoiceCallRecordForStorage, i as CALL_RECORD_EVENT_META_MAX_ENTRIES, n as CALL_RECORD_EVENTS_NAMESPACE, o as RAW_CALL_RECORD_CHUNK_BYTES, p as resolveVoiceCallLegacyCallLogPath, r as CALL_RECORD_EVENT_CHUNKS_NAMESPACE, s as buildVoiceCallLegacyJsonlEventKey, t as CALL_RECORD_CHUNK_MAX_ENTRIES, u as parseVoiceCallRecordLine } from "./store-8M2m4Isq.js";
2
+ import { normalizeAgentId } from "openclaw/plugin-sdk/routing";
2
3
  import os from "node:os";
3
4
  import path from "node:path";
4
5
  import fs from "node:fs/promises";
@@ -24,6 +25,26 @@ function getVoiceCallConfigStore(config) {
24
25
  }
25
26
  return "";
26
27
  }
28
+ function asRecord(value) {
29
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
30
+ }
31
+ /** Return Voice Call agents whose templated core session stores need migration. */
32
+ function resolveSessionStoreAgentIds(params) {
33
+ const agentIds = /* @__PURE__ */ new Set();
34
+ for (const pluginId of ["voice-call", "@openclaw/voice-call"]) {
35
+ const entry = params.cfg.plugins?.entries?.[pluginId];
36
+ if (!entry) continue;
37
+ const config = entry.config === void 0 ? {} : asRecord(entry.config);
38
+ if (!config) continue;
39
+ agentIds.add(normalizeAgentId(typeof config.agentId === "string" ? config.agentId : void 0));
40
+ const numbers = asRecord(config.numbers);
41
+ for (const route of Object.values(numbers ?? {})) {
42
+ const agentId = asRecord(route)?.agentId;
43
+ if (typeof agentId === "string") agentIds.add(normalizeAgentId(agentId));
44
+ }
45
+ }
46
+ return [...agentIds].toSorted();
47
+ }
27
48
  /** Resolve the voice-call store path used by legacy and plugin-state call records. */
28
49
  function resolveVoiceCallStorePath(params) {
29
50
  const configuredStore = getVoiceCallConfigStore(params.config);
@@ -220,4 +241,4 @@ const stateMigrations = [{
220
241
  }
221
242
  }];
222
243
  //#endregion
223
- export { stateMigrations };
244
+ export { resolveSessionStoreAgentIds, stateMigrations };
@@ -1,6 +1,6 @@
1
1
  import { fetchWithSsrFGuard } from "./runtime-api.js";
2
2
  import "./api.js";
3
- import { a as getHeader } from "./runtime-entry-DIAae7sB.js";
3
+ import { a as getHeader } from "./runtime-entry-Cwb6vEQf.js";
4
4
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
5
5
  import { isLoopbackHost } from "openclaw/plugin-sdk/gateway-runtime";
6
6
  import { isFutureDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { definePluginEntry, sleep } from "./runtime-api.js";
2
2
  import "./api.js";
3
- import { i as resolveVoiceCallConfig, s as validateProviderConfig } from "./config-BKDAAd8j.js";
4
- import { c as getTailscaleSelfInfo, l as setupTailscaleExposureRoute, o as resolveWebhookExposureStatus, p as resolveUserPath, s as cleanupTailscaleExposureRoute, t as createVoiceCallRuntime } from "./runtime-entry-DIAae7sB.js";
3
+ import { c as validateProviderConfig, i as resolveVoiceCallConfig } from "./config-DNtTB7Iw.js";
4
+ import { c as getTailscaleSelfInfo, l as setupTailscaleExposureRoute, o as resolveWebhookExposureStatus, p as resolveUserPath, s as cleanupTailscaleExposureRoute, t as createVoiceCallRuntime } from "./runtime-entry-Cwb6vEQf.js";
5
5
  import { c as getCallHistoryFromStore, m as setVoiceCallStateRuntime } from "./store-8M2m4Isq.js";
6
- import { i as parseVoiceCallPluginConfig, r as normalizeVoiceCallLegacyConfigInput, t as formatVoiceCallLegacyConfigWarnings } from "./config-compat-qDCcKTUt.js";
6
+ import { i as parseVoiceCallPluginConfig, r as normalizeVoiceCallLegacyConfigInput, t as formatVoiceCallLegacyConfigWarnings } from "./config-compat-CCEjHjAJ.js";
7
7
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
8
8
  import { ErrorCodes, callGatewayFromCli, errorShape } from "openclaw/plugin-sdk/gateway-runtime";
9
9
  import { MAX_TCP_PORT, MAX_TIMER_TIMEOUT_MS, addTimerTimeoutGraceMs, clampTimerTimeoutMs, parseStrictNonNegativeInteger, resolveTimerTimeoutMs, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
@@ -915,6 +915,19 @@ function asParamRecord(params) {
915
915
  function isCliOnlyProcess() {
916
916
  return process.env.OPENCLAW_CLI === "1" && !process.argv.slice(2).includes("gateway");
917
917
  }
918
+ function toVoiceCallStatus(call) {
919
+ return {
920
+ callId: call.callId,
921
+ ...call.providerCallId !== void 0 ? { providerCallId: call.providerCallId } : {},
922
+ provider: call.provider,
923
+ direction: call.direction,
924
+ state: call.state,
925
+ startedAt: call.startedAt,
926
+ ...call.answeredAt !== void 0 ? { answeredAt: call.answeredAt } : {},
927
+ ...call.endedAt !== void 0 ? { endedAt: call.endedAt } : {},
928
+ ...call.endReason !== void 0 ? { endReason: call.endReason } : {}
929
+ };
930
+ }
918
931
  const VOICE_CALL_RUNTIME_KEY = Symbol.for("openclaw.voice-call.runtime");
919
932
  const VOICE_CALL_RUNTIME_PROMISE_KEY = Symbol.for("openclaw.voice-call.runtimePromise");
920
933
  const VOICE_CALL_RUNTIME_STOP_PROMISE_KEY = Symbol.for("openclaw.voice-call.runtimeStopPromise");
@@ -1183,7 +1196,7 @@ var voice_call_default = definePluginEntry({
1183
1196
  if (!raw) {
1184
1197
  respond(true, {
1185
1198
  found: true,
1186
- calls: rt.manager.getActiveCalls()
1199
+ calls: rt.manager.getActiveCalls().map(toVoiceCallStatus)
1187
1200
  });
1188
1201
  return;
1189
1202
  }
@@ -1194,7 +1207,7 @@ var voice_call_default = definePluginEntry({
1194
1207
  }
1195
1208
  respond(true, {
1196
1209
  found: true,
1197
- call
1210
+ call: toVoiceCallStatus(call)
1198
1211
  });
1199
1212
  } catch (err) {
1200
1213
  sendError(respond, err);
@@ -1299,7 +1312,7 @@ var voice_call_default = definePluginEntry({
1299
1312
  const call = rt.manager.getCall(callId) || rt.manager.getCallByProviderCallId(callId);
1300
1313
  return json(call ? {
1301
1314
  found: true,
1302
- call
1315
+ call: toVoiceCallStatus(call)
1303
1316
  } : { found: false });
1304
1317
  }
1305
1318
  }
@@ -1309,7 +1322,7 @@ var voice_call_default = definePluginEntry({
1309
1322
  const call = rt.manager.getCall(sid) || rt.manager.getCallByProviderCallId(sid);
1310
1323
  return json(call ? {
1311
1324
  found: true,
1312
- call
1325
+ call: toVoiceCallStatus(call)
1313
1326
  } : { found: false });
1314
1327
  }
1315
1328
  const to = normalizeOptionalString(rawParams.to) ?? rt.config.toNumber;
@@ -1,5 +1,5 @@
1
- import { a as getHeader, m as escapeXml } from "./runtime-entry-DIAae7sB.js";
2
- import { a as reconstructWebhookUrl, o as verifyPlivoWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-BXeBxB7w.js";
1
+ import { a as getHeader, m as escapeXml } from "./runtime-entry-Cwb6vEQf.js";
2
+ import { a as reconstructWebhookUrl, o as verifyPlivoWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-Dnyy7xHB.js";
3
3
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  import crypto from "node:crypto";
5
5
  //#region extensions/voice-call/src/providers/plivo.ts
@@ -1,4 +1,4 @@
1
- import { c as normalizePath } from "./config-BKDAAd8j.js";
1
+ import { l as normalizePath } from "./config-DNtTB7Iw.js";
2
2
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
3
3
  import { isFutureDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
4
4
  import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env";
@@ -6,6 +6,7 @@ import { REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME, buildRealtimeVoiceAgentConsultW
6
6
  import { randomUUID } from "node:crypto";
7
7
  import "node:http";
8
8
  import WebSocket, { WebSocketServer } from "ws";
9
+ import { sliceUtf16Safe, truncateUtf16Safe } from "openclaw/plugin-sdk/text-utility-runtime";
9
10
  //#region extensions/voice-call/src/webhook/realtime-audio-pacer.ts
10
11
  const TELEPHONY_SAMPLE_RATE = 8e3;
11
12
  const TELEPHONY_CHUNK_BYTES = 160;
@@ -379,7 +380,7 @@ function appendTranscriptText(base, fragment) {
379
380
  }
380
381
  function limitPartialUserTranscript(text) {
381
382
  if (text.length <= MAX_PARTIAL_USER_TRANSCRIPT_CHARS) return text;
382
- const tail = text.slice(-1200);
383
+ const tail = sliceUtf16Safe(text, -1200);
383
384
  return tail.replace(/^\S+\s+/, "").trimStart() || tail.trimStart();
384
385
  }
385
386
  function withFallbackConsultQuestion(args, fallback) {
@@ -412,7 +413,7 @@ function buildForcedConsultSpeechPrompt(result) {
412
413
  "Internal OpenClaw consult result is ready.",
413
414
  "Do not call tools for this internal result.",
414
415
  "Speak the following answer to the caller now, briefly and naturally:",
415
- trimmed.length <= FORCED_CONSULT_RESULT_MAX_CHARS ? trimmed : `${trimmed.slice(0, FORCED_CONSULT_RESULT_MAX_CHARS - 16).trimEnd()} [truncated]`
416
+ trimmed.length <= FORCED_CONSULT_RESULT_MAX_CHARS ? trimmed : `${truncateUtf16Safe(trimmed, FORCED_CONSULT_RESULT_MAX_CHARS - 16).trimEnd()} [truncated]`
416
417
  ].join("\n");
417
418
  }
418
419
  function appendRecentTalkEventMetadata(call, event) {
@@ -1,5 +1,5 @@
1
- import { o as resolveVoiceCallSessionKey } from "./config-BKDAAd8j.js";
2
- import { f as resolveVoiceResponseModel } from "./runtime-entry-DIAae7sB.js";
1
+ import { s as resolveVoiceCallSessionKey } from "./config-DNtTB7Iw.js";
2
+ import { f as resolveVoiceResponseModel } from "./runtime-entry-Cwb6vEQf.js";
3
3
  import { isRecord, normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
4
4
  import crypto from "node:crypto";
5
5
  import { applyModelOverrideToSessionEntry } from "openclaw/plugin-sdk/model-session-runtime";
@@ -106,7 +106,8 @@ async function generateVoiceResponse(params) {
106
106
  config: voiceConfig,
107
107
  callId,
108
108
  phone: from,
109
- explicitSessionKey: sessionKey
109
+ explicitSessionKey: sessionKey,
110
+ coreSession: coreConfig.session
110
111
  });
111
112
  const agentId = voiceConfig.agentId ?? "main";
112
113
  const toolsAllow = resolveVoiceAgentToolsAllow(cfg, agentId);
@@ -154,7 +155,6 @@ async function generateVoiceResponse(params) {
154
155
  error: "Voice response session could not be initialized"
155
156
  };
156
157
  const sessionId = sessionEntry.sessionId;
157
- const sessionFile = agentRuntime.session.resolveSessionFilePath(sessionId, sessionEntry, { agentId });
158
158
  const thinkLevel = agentRuntime.resolveThinkingDefault({
159
159
  cfg,
160
160
  provider,
@@ -171,10 +171,15 @@ async function generateVoiceResponse(params) {
171
171
  const result = await agentRuntime.runEmbeddedAgent({
172
172
  sessionId,
173
173
  sessionKey: resolvedSessionKey,
174
+ sessionTarget: {
175
+ agentId,
176
+ sessionId,
177
+ sessionKey: resolvedSessionKey,
178
+ storePath
179
+ },
174
180
  sandboxSessionKey: resolveVoiceSandboxSessionKey(agentId, resolvedSessionKey),
175
181
  agentId,
176
182
  messageProvider: "voice",
177
- sessionFile,
178
183
  workspaceDir,
179
184
  config: cfg,
180
185
  prompt: userMessage,
@@ -1,6 +1,6 @@
1
1
  import { isBlockedHostnameOrIp, isRequestBodyLimitError, readRequestBodyWithLimit, requestBodyErrorToText } from "./runtime-api.js";
2
2
  import "./api.js";
3
- import { a as resolveVoiceCallEffectiveConfig, i as resolveVoiceCallConfig, l as deepMergeDefined, n as normalizeVoiceCallConfig, o as resolveVoiceCallSessionKey, r as resolveTwilioAuthToken, s as validateProviderConfig } from "./config-BKDAAd8j.js";
3
+ import { a as resolveVoiceCallEffectiveConfig, c as validateProviderConfig, i as resolveVoiceCallConfig, n as normalizeVoiceCallConfig, o as resolveVoiceCallNumberRouteKeyForCall, r as resolveTwilioAuthToken, s as resolveVoiceCallSessionKey, u as deepMergeDefined } from "./config-DNtTB7Iw.js";
4
4
  import { c as getCallHistoryFromStore, d as persistCallRecord, h as TerminalStates, l as loadActiveCallsFromStore, m as setVoiceCallStateRuntime } from "./store-8M2m4Isq.js";
5
5
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
6
6
  import { isLoopbackHost } from "openclaw/plugin-sdk/gateway-runtime";
@@ -356,7 +356,8 @@ async function initiateCall(ctx, to, sessionKey, options) {
356
356
  config: ctx.config,
357
357
  callId,
358
358
  phone: to,
359
- explicitSessionKey: sessionKey
359
+ explicitSessionKey: sessionKey,
360
+ coreSession: ctx.coreSession
360
361
  }),
361
362
  startedAt: Date.now(),
362
363
  transcript: [],
@@ -438,7 +439,7 @@ async function speak(ctx, callId, text) {
438
439
  });
439
440
  transitionState(call, "speaking");
440
441
  persistCallRecord(ctx.storePath, call);
441
- const numberRouteKey = typeof call.metadata?.numberRouteKey === "string" ? call.metadata.numberRouteKey : call.to;
442
+ const numberRouteKey = resolveVoiceCallNumberRouteKeyForCall(call);
442
443
  const voice = resolvePreferredTtsVoice(resolveVoiceCallEffectiveConfig(ctx.config, numberRouteKey).config);
443
444
  await provider.playTts({
444
445
  callId,
@@ -910,7 +911,7 @@ function resolveDefaultStoreBase(config, storePath) {
910
911
  * Manages voice calls: state ownership and delegation to manager helper modules.
911
912
  */
912
913
  var CallManager = class {
913
- constructor(config, storePath) {
914
+ constructor(config, storePath, coreSession) {
914
915
  this.activeCalls = /* @__PURE__ */ new Map();
915
916
  this.providerCallIdMap = /* @__PURE__ */ new Map();
916
917
  this.processedEventIds = /* @__PURE__ */ new Set();
@@ -922,6 +923,7 @@ var CallManager = class {
922
923
  this.maxDurationTimers = /* @__PURE__ */ new Map();
923
924
  this.initialMessageInFlight = /* @__PURE__ */ new Set();
924
925
  this.config = config;
926
+ this.coreSession = coreSession;
925
927
  this.storePath = resolveDefaultStoreBase(config, storePath);
926
928
  }
927
929
  /**
@@ -1085,6 +1087,7 @@ var CallManager = class {
1085
1087
  rejectedProviderCallIds: this.rejectedProviderCallIds,
1086
1088
  provider: this.provider,
1087
1089
  config: this.config,
1090
+ coreSession: this.coreSession,
1088
1091
  storePath: this.storePath,
1089
1092
  webhookUrl: this.webhookUrl,
1090
1093
  activeTurnCalls: this.activeTurnCalls,
@@ -2510,7 +2513,7 @@ function loadRealtimeTranscriptionRuntime() {
2510
2513
  return realtimeTranscriptionRuntimePromise;
2511
2514
  }
2512
2515
  function loadResponseGeneratorModule() {
2513
- responseGeneratorModulePromise ??= import("./response-generator-DBzGujH_.js");
2516
+ responseGeneratorModulePromise ??= import("./response-generator-DOdrErIH.js");
2514
2517
  return responseGeneratorModulePromise;
2515
2518
  }
2516
2519
  function sanitizeTranscriptForLog(value) {
@@ -3156,7 +3159,7 @@ var VoiceCallWebhookServer = class {
3156
3159
  }
3157
3160
  try {
3158
3161
  const { generateVoiceResponse } = await loadResponseGeneratorModule();
3159
- const numberRouteKey = typeof call.metadata?.numberRouteKey === "string" ? call.metadata.numberRouteKey : call.to;
3162
+ const numberRouteKey = resolveVoiceCallNumberRouteKeyForCall(call);
3160
3163
  const effectiveConfig = resolveVoiceCallEffectiveConfig(this.config, numberRouteKey).config;
3161
3164
  const result = await generateVoiceResponse({
3162
3165
  voiceConfig: effectiveConfig,
@@ -3198,15 +3201,15 @@ let mockProviderPromise;
3198
3201
  let realtimeVoiceRuntimePromise;
3199
3202
  let realtimeHandlerPromise;
3200
3203
  function loadTelnyxProvider() {
3201
- telnyxProviderPromise ??= import("./telnyx-CYS_IihO.js");
3204
+ telnyxProviderPromise ??= import("./telnyx-LSKUJimK.js");
3202
3205
  return telnyxProviderPromise;
3203
3206
  }
3204
3207
  function loadTwilioProvider() {
3205
- twilioProviderPromise ??= import("./twilio-Bn1ta3tQ.js");
3208
+ twilioProviderPromise ??= import("./twilio-GBk6YOP_.js");
3206
3209
  return twilioProviderPromise;
3207
3210
  }
3208
3211
  function loadPlivoProvider() {
3209
- plivoProviderPromise ??= import("./plivo-CPxvHw8k.js");
3212
+ plivoProviderPromise ??= import("./plivo-sCM3Jvjh.js");
3210
3213
  return plivoProviderPromise;
3211
3214
  }
3212
3215
  function loadMockProvider() {
@@ -3218,16 +3221,16 @@ function loadRealtimeVoiceRuntime() {
3218
3221
  return realtimeVoiceRuntimePromise;
3219
3222
  }
3220
3223
  function loadRealtimeHandler() {
3221
- realtimeHandlerPromise ??= import("./realtime-handler-rYbJUshl.js");
3224
+ realtimeHandlerPromise ??= import("./realtime-handler-DdViwdCW.js");
3222
3225
  return realtimeHandlerPromise;
3223
3226
  }
3224
3227
  function resolveVoiceCallConsultSessionKey(call) {
3225
- if (call.sessionKey) return call.sessionKey;
3226
- const phone = call.direction === "outbound" ? call.to : call.from;
3227
3228
  return resolveVoiceCallSessionKey({
3228
3229
  config: call.config,
3229
3230
  callId: call.callId,
3230
- phone
3231
+ phone: call.direction === "outbound" ? call.to : call.from,
3232
+ explicitSessionKey: call.sessionKey,
3233
+ coreSession: call.coreSession
3231
3234
  });
3232
3235
  }
3233
3236
  function mapVoiceCallConsultTranscript(call, context) {
@@ -3339,7 +3342,7 @@ async function createVoiceCallRuntime(params) {
3339
3342
  if (!validation.valid) throw new Error(`Invalid voice-call config: ${validation.errors.join("; ")}`);
3340
3343
  const provider = await resolveProvider(config);
3341
3344
  if (stateRuntime) setVoiceCallStateRuntime({ state: stateRuntime });
3342
- const manager = new CallManager(config);
3345
+ const manager = new CallManager(config, void 0, cfg.session);
3343
3346
  const realtimeProvider = config.realtime.enabled ? await resolveRealtimeProvider({
3344
3347
  config,
3345
3348
  fullConfig: cfg
@@ -3361,11 +3364,12 @@ async function createVoiceCallRuntime(params) {
3361
3364
  if (config.realtime.toolPolicy !== "none") realtimeHandler.registerToolHandler(REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME, async (args, callId, handlerContext) => {
3362
3365
  const call = manager.getCall(callId);
3363
3366
  if (!call) return { error: `Call "${callId}" not found` };
3364
- const effectiveConfig = resolveVoiceCallEffectiveConfig(config, typeof call.metadata?.numberRouteKey === "string" ? call.metadata.numberRouteKey : call.to).config;
3367
+ const effectiveConfig = resolveVoiceCallEffectiveConfig(config, resolveVoiceCallNumberRouteKeyForCall(call)).config;
3365
3368
  const agentId = effectiveConfig.agentId ?? "main";
3366
3369
  const sessionKey = resolveVoiceCallConsultSessionKey({
3367
3370
  ...call,
3368
- config: effectiveConfig
3371
+ config: effectiveConfig,
3372
+ coreSession: cfg.session
3369
3373
  });
3370
3374
  const requesterSessionKey = typeof call.metadata?.requesterSessionKey === "string" ? call.metadata.requesterSessionKey : void 0;
3371
3375
  const fastContext = await resolveRealtimeFastContextConsult({
@@ -1,2 +1,2 @@
1
- import { t as createVoiceCallRuntime } from "./runtime-entry-DIAae7sB.js";
1
+ import { t as createVoiceCallRuntime } from "./runtime-entry-Cwb6vEQf.js";
2
2
  export { createVoiceCallRuntime };
package/dist/setup-api.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as migrateVoiceCallLegacyConfigInput } from "./config-compat-qDCcKTUt.js";
1
+ import { n as migrateVoiceCallLegacyConfigInput } from "./config-compat-CCEjHjAJ.js";
2
2
  import { isRecord } from "openclaw/plugin-sdk/string-coerce-runtime";
3
3
  import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
4
4
  //#region extensions/voice-call/setup-api.ts
@@ -1,4 +1,4 @@
1
- import { s as verifyTelnyxWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-BXeBxB7w.js";
1
+ import { s as verifyTelnyxWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-Dnyy7xHB.js";
2
2
  import crypto from "node:crypto";
3
3
  //#region extensions/voice-call/src/providers/telnyx.ts
4
4
  function normalizeTelnyxDirection(direction) {
@@ -1,7 +1,7 @@
1
1
  import { fetchWithSsrFGuard } from "./runtime-api.js";
2
2
  import "./api.js";
3
- import { a as getHeader, d as chunkAudio, h as mapVoiceToPolly, i as normalizeProviderStatus, m as escapeXml, n as isProviderStatusTerminal, r as mapProviderStatusToEndReason } from "./runtime-entry-DIAae7sB.js";
4
- import { c as verifyTwilioWebhook, i as readProviderJsonResponseText, n as cancelProviderResponseBody, r as readProviderErrorResponseSnippet, t as guardedJsonApiRequest } from "./guarded-json-api-BXeBxB7w.js";
3
+ import { a as getHeader, d as chunkAudio, h as mapVoiceToPolly, i as normalizeProviderStatus, m as escapeXml, n as isProviderStatusTerminal, r as mapProviderStatusToEndReason } from "./runtime-entry-Cwb6vEQf.js";
4
+ import { c as verifyTwilioWebhook, i as readProviderJsonResponseText, n as cancelProviderResponseBody, r as readProviderErrorResponseSnippet, t as guardedJsonApiRequest } from "./guarded-json-api-Dnyy7xHB.js";
5
5
  import { normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
6
6
  import crypto from "node:crypto";
7
7
  import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@openclaw/voice-call",
3
- "version": "2026.6.11-beta.2",
3
+ "version": "2026.7.1-beta.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/voice-call",
9
- "version": "2026.6.11-beta.2",
9
+ "version": "2026.7.1-beta.1",
10
10
  "dependencies": {
11
11
  "commander": "14.0.3",
12
12
  "typebox": "1.1.39",
@@ -14,7 +14,7 @@
14
14
  "zod": "4.4.3"
15
15
  },
16
16
  "peerDependencies": {
17
- "openclaw": ">=2026.6.11-beta.2"
17
+ "openclaw": ">=2026.7.1-beta.1"
18
18
  },
19
19
  "peerDependenciesMeta": {
20
20
  "openclaw": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/voice-call",
3
- "version": "2026.6.11-beta.2",
3
+ "version": "2026.7.1-beta.1",
4
4
  "description": "OpenClaw voice-call plugin for Twilio, Telnyx, and Plivo phone calls.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,7 +14,7 @@
14
14
  "zod": "4.4.3"
15
15
  },
16
16
  "peerDependencies": {
17
- "openclaw": ">=2026.6.11-beta.2"
17
+ "openclaw": ">=2026.7.1-beta.1"
18
18
  },
19
19
  "peerDependenciesMeta": {
20
20
  "openclaw": {
@@ -31,10 +31,10 @@
31
31
  "minHostVersion": ">=2026.4.10"
32
32
  },
33
33
  "compat": {
34
- "pluginApi": ">=2026.6.11-beta.2"
34
+ "pluginApi": ">=2026.7.1-beta.1"
35
35
  },
36
36
  "build": {
37
- "openclawVersion": "2026.6.11-beta.2"
37
+ "openclawVersion": "2026.7.1-beta.1"
38
38
  },
39
39
  "release": {
40
40
  "publishToClawHub": true,