@openclaw/voice-call 2026.5.28 → 2026.5.31-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.
@@ -433,7 +433,7 @@ function normalizePhoneRouteKey(phone) {
433
433
  function resolveVoiceCallNumberRouteKey(config, phone) {
434
434
  const routes = config.numbers;
435
435
  if (!routes) return;
436
- if (phone && Object.prototype.hasOwnProperty.call(routes, phone)) return phone;
436
+ if (phone && Object.hasOwn(routes, phone)) return phone;
437
437
  const normalizedPhone = normalizePhoneRouteKey(phone);
438
438
  if (!normalizedPhone) return;
439
439
  return Object.keys(routes).find((routeKey) => normalizePhoneRouteKey(routeKey) === normalizedPhone);
@@ -1,4 +1,4 @@
1
- import { t as VoiceCallConfigSchema } from "./config-U-rgixyY.js";
1
+ import { t as VoiceCallConfigSchema } from "./config-BKyRNKHF.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
  const VOICE_CALL_LEGACY_CONFIG_REMOVAL_VERSION = "2026.6.0";
@@ -61,7 +61,7 @@ function collectVoiceCallLegacyConfigIssues(value) {
61
61
  replacement: "streaming.providers.openai.vadThreshold",
62
62
  message: "Move streaming.vadThreshold to streaming.providers.openai.vadThreshold."
63
63
  });
64
- if (realtimeAgentContext && Object.prototype.hasOwnProperty.call(realtimeAgentContext, "includeSystemPrompt")) issues.push({
64
+ if (realtimeAgentContext && Object.hasOwn(realtimeAgentContext, "includeSystemPrompt")) issues.push({
65
65
  path: "realtime.agentContext.includeSystemPrompt",
66
66
  replacement: "realtime.agentContext",
67
67
  message: "Remove realtime.agentContext.includeSystemPrompt; realtime context now uses the generated agent prompt."
@@ -130,7 +130,7 @@ function migrateVoiceCallLegacyConfigInput(params) {
130
130
  else if (typeof streaming?.silenceDurationMs === "number") changes.push(`Removed invalid ${configPathPrefix}.streaming.silenceDurationMs.`);
131
131
  if (getNumber(streaming, "vadThreshold") !== void 0) changes.push(`Moved ${configPathPrefix}.streaming.vadThreshold → ${configPathPrefix}.streaming.providers.openai.vadThreshold.`);
132
132
  else if (typeof streaming?.vadThreshold === "number") changes.push(`Removed invalid ${configPathPrefix}.streaming.vadThreshold.`);
133
- if (realtimeAgentContext && Object.prototype.hasOwnProperty.call(realtimeAgentContext, "includeSystemPrompt")) changes.push(`Removed ${configPathPrefix}.realtime.agentContext.includeSystemPrompt.`);
133
+ if (realtimeAgentContext && Object.hasOwn(realtimeAgentContext, "includeSystemPrompt")) changes.push(`Removed ${configPathPrefix}.realtime.agentContext.includeSystemPrompt.`);
134
134
  return {
135
135
  config,
136
136
  changes,
@@ -1,8 +1,9 @@
1
1
  import { fetchWithSsrFGuard } from "./runtime-api.js";
2
2
  import "./api.js";
3
- import { a as getHeader } from "./runtime-entry-DwBgs2Sq.js";
3
+ import { a as getHeader } from "./runtime-entry-DXf7kdXt.js";
4
4
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
5
5
  import { isLoopbackHost } from "openclaw/plugin-sdk/gateway-runtime";
6
+ import { isFutureDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
6
7
  import { normalizeLowercaseStringOrEmpty, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
7
8
  import crypto from "node:crypto";
8
9
  import { safeEqualSecret } from "openclaw/plugin-sdk/security-runtime";
@@ -29,7 +30,7 @@ function createSkippedVerificationReplayKey(provider, ctx) {
29
30
  return `${provider}:skip:${sha256Hex(`${ctx.method}\n${ctx.url}\n${ctx.rawBody}`)}`;
30
31
  }
31
32
  function pruneReplayCache(cache, now) {
32
- for (const [key, expiresAt] of cache.seenUntil) if (expiresAt <= now) cache.seenUntil.delete(key);
33
+ for (const [key, expiresAt] of cache.seenUntil) if (!isFutureDateTimestampMs(expiresAt, { nowMs: now })) cache.seenUntil.delete(key);
33
34
  while (cache.seenUntil.size > REPLAY_CACHE_MAX_ENTRIES) {
34
35
  const oldest = cache.seenUntil.keys().next().value;
35
36
  if (!oldest) break;
@@ -41,8 +42,9 @@ function markReplay(cache, replayKey) {
41
42
  cache.calls += 1;
42
43
  if (cache.calls % REPLAY_CACHE_PRUNE_INTERVAL === 0) pruneReplayCache(cache, now);
43
44
  const existing = cache.seenUntil.get(replayKey);
44
- if (existing && existing > now) return true;
45
- cache.seenUntil.set(replayKey, now + REPLAY_WINDOW_MS);
45
+ if (existing !== void 0 && isFutureDateTimestampMs(existing, { nowMs: now })) return true;
46
+ const expiresAt = resolveExpiresAtMsFromDurationMs(REPLAY_WINDOW_MS, { nowMs: now });
47
+ if (expiresAt !== void 0) cache.seenUntil.set(replayKey, expiresAt);
46
48
  if (cache.seenUntil.size > REPLAY_CACHE_MAX_ENTRIES) pruneReplayCache(cache, now);
47
49
  return false;
48
50
  }
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { definePluginEntry, sleep } from "./runtime-api.js";
2
2
  import "./api.js";
3
- import { i as resolveVoiceCallConfig, s as validateProviderConfig } from "./config-U-rgixyY.js";
4
- import { c as getTailscaleSelfInfo, l as setupTailscaleExposureRoute, o as resolveWebhookExposureStatus, p as resolveUserPath, s as cleanupTailscaleExposureRoute, t as createVoiceCallRuntime } from "./runtime-entry-DwBgs2Sq.js";
5
- import { i as parseVoiceCallPluginConfig, r as normalizeVoiceCallLegacyConfigInput, t as formatVoiceCallLegacyConfigWarnings } from "./config-compat-CokN3Zzr.js";
3
+ import { i as resolveVoiceCallConfig, s as validateProviderConfig } from "./config-BKyRNKHF.js";
4
+ import { _ as setVoiceCallStateRuntime, c as getTailscaleSelfInfo, g as getCallHistoryFromStore, l as setupTailscaleExposureRoute, o as resolveWebhookExposureStatus, p as resolveUserPath, s as cleanupTailscaleExposureRoute, t as createVoiceCallRuntime } from "./runtime-entry-DXf7kdXt.js";
5
+ import { i as parseVoiceCallPluginConfig, r as normalizeVoiceCallLegacyConfigInput, t as formatVoiceCallLegacyConfigWarnings } from "./config-compat-BLdRz9GR.js";
6
6
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
7
7
  import { ErrorCodes, callGatewayFromCli, errorShape } from "openclaw/plugin-sdk/gateway-runtime";
8
+ import { MAX_TCP_PORT, MAX_TIMER_TIMEOUT_MS, addTimerTimeoutGraceMs, clampTimerTimeoutMs, parseStrictNonNegativeInteger, resolveTimerTimeoutMs, timestampMsToIsoString } from "openclaw/plugin-sdk/number-runtime";
8
9
  import { isRecord, normalizeOptionalLowercaseString, normalizeOptionalString } from "openclaw/plugin-sdk/string-coerce-runtime";
9
10
  import { Type } from "typebox";
10
11
  import fs from "node:fs";
@@ -12,7 +13,6 @@ import os from "node:os";
12
13
  import path from "node:path";
13
14
  import { randomUUID } from "node:crypto";
14
15
  import { format } from "node:util";
15
- import { MAX_TCP_PORT, parseStrictNonNegativeInteger } from "openclaw/plugin-sdk/number-runtime";
16
16
  //#region extensions/voice-call/src/cli.ts
17
17
  const VOICE_CALL_GATEWAY_DEFAULT_TIMEOUT_MS = 5e3;
18
18
  const VOICE_CALL_GATEWAY_OPERATION_TIMEOUT_MS = 3e4;
@@ -54,10 +54,13 @@ async function callVoiceCallGateway(method, params, opts) {
54
54
  }
55
55
  }
56
56
  function resolveGatewayOperationTimeoutMs(config) {
57
- return Math.max(VOICE_CALL_GATEWAY_OPERATION_TIMEOUT_MS, config.ringTimeoutMs + 5e3);
57
+ return Math.max(VOICE_CALL_GATEWAY_OPERATION_TIMEOUT_MS, addTimerTimeoutGraceMs(config.ringTimeoutMs) ?? 1);
58
58
  }
59
59
  function resolveGatewayContinueTimeoutMs(config) {
60
- return config.transcriptTimeoutMs + VOICE_CALL_GATEWAY_OPERATION_TIMEOUT_MS + VOICE_CALL_GATEWAY_TRANSCRIPT_BUFFER_MS;
60
+ return clampTimerTimeoutMs(config.transcriptTimeoutMs + VOICE_CALL_GATEWAY_OPERATION_TIMEOUT_MS + VOICE_CALL_GATEWAY_TRANSCRIPT_BUFFER_MS) ?? 1;
61
+ }
62
+ function resolveVoiceCallDeadlineMs(timeoutMs, nowMs = Date.now()) {
63
+ return nowMs + (clampTimerTimeoutMs(timeoutMs) ?? MAX_TIMER_TIMEOUT_MS);
61
64
  }
62
65
  function isUnknownGatewayMethod(err, method) {
63
66
  return formatErrorMessage(err).includes(`unknown method: ${method}`);
@@ -67,7 +70,7 @@ function readGatewayOperationId(payload) {
67
70
  throw new Error("voicecall gateway response missing operationId");
68
71
  }
69
72
  function readGatewayPollTimeoutMs(payload, fallbackTimeoutMs) {
70
- if (isRecord(payload) && typeof payload.pollTimeoutMs === "number") return Math.max(1, Math.ceil(payload.pollTimeoutMs));
73
+ if (isRecord(payload) && typeof payload.pollTimeoutMs === "number") return clampTimerTimeoutMs(payload.pollTimeoutMs) ?? fallbackTimeoutMs;
71
74
  return fallbackTimeoutMs;
72
75
  }
73
76
  function readCompletedContinueResult(payload) {
@@ -84,7 +87,7 @@ function readCompletedContinueResult(payload) {
84
87
  throw new Error("voicecall gateway response has unknown operation status");
85
88
  }
86
89
  async function pollVoiceCallContinueGateway(params) {
87
- const deadlineMs = Date.now() + params.timeoutMs;
90
+ const deadlineMs = resolveVoiceCallDeadlineMs(params.timeoutMs);
88
91
  while (Date.now() <= deadlineMs) {
89
92
  const gateway = await callVoiceCallGateway("voicecall.continue.result", { operationId: params.operationId }, { timeoutMs: VOICE_CALL_GATEWAY_DEFAULT_TIMEOUT_MS });
90
93
  if (!gateway.ok) throw new Error(`gateway unavailable while waiting for voicecall continue result: ${formatErrorMessage(gateway.error)}`);
@@ -218,7 +221,10 @@ async function initiateCallViaGatewayOrRuntime(params) {
218
221
  });
219
222
  }
220
223
  function registerVoiceCallCli(params) {
221
- const { program, config, ensureRuntime, logger } = params;
224
+ const { program, config, ensureRuntime, stateRuntime } = params;
225
+ const ensureHistoryStateRuntime = () => {
226
+ if (stateRuntime) setVoiceCallStateRuntime({ state: stateRuntime });
227
+ };
222
228
  const root = program.command("voicecall").description("Voice call utilities").addHelpText("after", () => `\nDocs: https://docs.openclaw.ai/cli/voicecall\n`);
223
229
  root.command("setup").description("Show Voice Call provider and webhook setup status").option("--json", "Print machine-readable JSON").action((options) => {
224
230
  const status = buildSetupStatus(config);
@@ -408,54 +414,81 @@ function registerVoiceCallCli(params) {
408
414
  const file = options.file;
409
415
  const since = parseVoiceCallIntOption(options.since, "--since", { min: 0 });
410
416
  const pollMs = parseVoiceCallIntOption(options.poll, "--poll", { min: 50 });
411
- if (!fs.existsSync(file)) {
412
- logger.error(`No log file at ${file}`);
413
- process.exit(1);
414
- }
415
- const initial = fs.readFileSync(file, "utf8");
416
- const lines = initial.split("\n").filter(Boolean);
417
- for (const line of lines.slice(Math.max(0, lines.length - since))) writeStdoutLine(line);
418
- let offset = Buffer.byteLength(initial, "utf8");
419
- for (;;) {
420
- try {
421
- const stat = fs.statSync(file);
422
- if (stat.size < offset) offset = 0;
423
- if (stat.size > offset) {
424
- const fd = fs.openSync(file, "r");
425
- try {
426
- const buf = Buffer.alloc(stat.size - offset);
427
- fs.readSync(fd, buf, 0, buf.length, offset);
428
- offset = stat.size;
429
- const text = buf.toString("utf8");
430
- for (const line of text.split("\n").filter(Boolean)) writeStdoutLine(line);
431
- } finally {
432
- fs.closeSync(fd);
433
- }
417
+ const tailSqliteHistory = async (initialLimit) => {
418
+ ensureHistoryStateRuntime();
419
+ const seen = /* @__PURE__ */ new Set();
420
+ const printCall = (call) => {
421
+ const line = JSON.stringify(call);
422
+ if (!seen.has(line)) {
423
+ seen.add(line);
424
+ writeStdoutLine(line);
434
425
  }
435
- } catch {}
436
- await sleep(pollMs);
437
- }
426
+ };
427
+ if (initialLimit > 0) for (const call of await getCallHistoryFromStore(path.dirname(file), initialLimit)) printCall(call);
428
+ for (;;) {
429
+ try {
430
+ for (const call of await getCallHistoryFromStore(path.dirname(file), 1e3)) printCall(call);
431
+ } catch {}
432
+ await sleep(pollMs);
433
+ }
434
+ };
435
+ if (fs.existsSync(file) && path.basename(file) !== "calls.jsonl") {
436
+ const initial = fs.readFileSync(file, "utf8");
437
+ const lines = initial.split("\n").filter(Boolean);
438
+ for (const line of lines.slice(Math.max(0, lines.length - since))) writeStdoutLine(line);
439
+ let offset = Buffer.byteLength(initial, "utf8");
440
+ for (;;) {
441
+ try {
442
+ const stat = fs.statSync(file);
443
+ if (stat.size < offset) offset = 0;
444
+ if (stat.size > offset) {
445
+ const fd = fs.openSync(file, "r");
446
+ try {
447
+ const buf = Buffer.alloc(stat.size - offset);
448
+ fs.readSync(fd, buf, 0, buf.length, offset);
449
+ offset = stat.size;
450
+ const text = buf.toString("utf8");
451
+ for (const line of text.split("\n").filter(Boolean)) writeStdoutLine(line);
452
+ } finally {
453
+ fs.closeSync(fd);
454
+ }
455
+ }
456
+ } catch {}
457
+ await sleep(pollMs);
458
+ }
459
+ } else await tailSqliteHistory(since);
438
460
  });
439
461
  root.command("latency").description("Summarize turn latency metrics from voice-call JSONL logs").option("--file <path>", "Path to calls.jsonl", resolveDefaultStorePath(config)).option("--last <n>", "Analyze last N records", "200").action(async (options) => {
440
462
  const file = options.file;
441
463
  const last = parseVoiceCallIntOption(options.last, "--last", { min: 1 });
442
- if (!fs.existsSync(file)) throw new Error("No log file at " + file);
443
- const lines = fs.readFileSync(file, "utf8").split("\n").filter(Boolean).slice(-last);
464
+ if (fs.existsSync(file) && path.basename(file) !== "calls.jsonl") writeVoiceCallLatencySummary(fs.readFileSync(file, "utf8").split("\n").filter(Boolean).slice(-last).map((line) => {
465
+ try {
466
+ const parsed = JSON.parse(line);
467
+ return parsed.call ?? parsed;
468
+ } catch {
469
+ return null;
470
+ }
471
+ }).filter((call) => call !== null));
472
+ else {
473
+ ensureHistoryStateRuntime();
474
+ writeVoiceCallLatencySummary(await getCallHistoryFromStore(path.dirname(file), last));
475
+ }
476
+ });
477
+ function writeVoiceCallLatencySummary(calls) {
444
478
  const turnLatencyMs = [];
445
479
  const listenWaitMs = [];
446
- for (const line of lines) try {
447
- const parsed = JSON.parse(line);
448
- const latency = parsed.metadata?.lastTurnLatencyMs;
449
- const listenWait = parsed.metadata?.lastTurnListenWaitMs;
480
+ for (const call of calls) {
481
+ const latency = call.metadata?.lastTurnLatencyMs;
482
+ const listenWait = call.metadata?.lastTurnListenWaitMs;
450
483
  if (typeof latency === "number" && Number.isFinite(latency)) turnLatencyMs.push(latency);
451
484
  if (typeof listenWait === "number" && Number.isFinite(listenWait)) listenWaitMs.push(listenWait);
452
- } catch {}
485
+ }
453
486
  writeStdoutJson({
454
- recordsScanned: lines.length,
487
+ recordsScanned: calls.length,
455
488
  turnLatency: summarizeSeries(turnLatencyMs),
456
489
  listenWait: summarizeSeries(listenWaitMs)
457
490
  });
458
- });
491
+ }
459
492
  root.command("expose").description("Enable/disable Tailscale serve/funnel for the webhook").option("--mode <mode>", "off | serve (tailnet) | funnel (public)", "funnel").option("--path <path>", "Tailscale path to expose (recommend matching serve.path)").option("--port <port>", "Local webhook port").option("--serve-path <path>", "Local webhook path").action(async (options) => {
460
493
  const mode = resolveMode(options.mode ?? "funnel");
461
494
  const servePort = parseVoiceCallIntOption(options.port ?? String(config.serve.port ?? 3334), "--port", {
@@ -509,7 +542,7 @@ function createVoiceCallContinueOperationStore(params) {
509
542
  const operations = /* @__PURE__ */ new Map();
510
543
  const resolvePollTimeoutMs = (rt) => {
511
544
  const ttsTimeoutMs = rt.config.tts?.timeoutMs ?? params.config.tts?.timeoutMs ?? params.coreConfig.messages?.tts?.timeoutMs ?? 8e3;
512
- return (rt.config.transcriptTimeoutMs ?? params.config.transcriptTimeoutMs) + ttsTimeoutMs + VOICE_CALL_CONTINUE_OPERATION_BUFFER_MS;
545
+ return resolveTimerTimeoutMs((rt.config.transcriptTimeoutMs ?? params.config.transcriptTimeoutMs) + ttsTimeoutMs + VOICE_CALL_CONTINUE_OPERATION_BUFFER_MS, VOICE_CALL_CONTINUE_OPERATION_BUFFER_MS);
513
546
  };
514
547
  const scheduleCleanup = (operationId) => {
515
548
  setTimeout(() => {
@@ -925,6 +958,7 @@ var voice_call_default = definePluginEntry({
925
958
  coreConfig: api.config,
926
959
  fullConfig: api.config,
927
960
  agentRuntime: api.runtime.agent,
961
+ stateRuntime: api.runtime.state,
928
962
  ttsRuntime: api.runtime.tts,
929
963
  logger: api.logger
930
964
  });
@@ -954,10 +988,11 @@ var voice_call_default = definePluginEntry({
954
988
  const describeHistoricalCall = async (rt, callId) => {
955
989
  const call = (await rt.manager.getCallHistory(100)).toReversed().find((candidate) => candidate.callId === callId || candidate.providerCallId === callId);
956
990
  if (!call) return;
991
+ const endedAt = timestampMsToIsoString(call.endedAt);
957
992
  return `call is not active (${[
958
993
  `last state=${call.state}`,
959
994
  call.endReason ? `endReason=${call.endReason}` : void 0,
960
- call.endedAt ? `endedAt=${new Date(call.endedAt).toISOString()}` : void 0
995
+ endedAt ? `endedAt=${endedAt}` : void 0
961
996
  ].filter(Boolean).join(", ")})`;
962
997
  };
963
998
  const resolveCallMessageRequest = async (params) => {
@@ -1296,6 +1331,7 @@ var voice_call_default = definePluginEntry({
1296
1331
  program,
1297
1332
  config,
1298
1333
  ensureRuntime,
1334
+ stateRuntime: api.runtime.state,
1299
1335
  logger: api.logger
1300
1336
  }), { commands: ["voicecall"] });
1301
1337
  api.registerService({
@@ -1,5 +1,5 @@
1
- import { a as getHeader, m as escapeXml } from "./runtime-entry-DwBgs2Sq.js";
2
- import { n as reconstructWebhookUrl, r as verifyPlivoWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-hXuXY1dk.js";
1
+ import { a as getHeader, m as escapeXml } from "./runtime-entry-DXf7kdXt.js";
2
+ import { n as reconstructWebhookUrl, r as verifyPlivoWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-mCcsLRBb.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,5 @@
1
1
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
2
+ import { isFutureDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "openclaw/plugin-sdk/number-runtime";
2
3
  import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env";
3
4
  import { REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME, buildRealtimeVoiceAgentConsultWorkingResponse, createRealtimeVoiceBridgeSession, createRealtimeVoiceForcedConsultCoordinator, createTalkSessionController, readRealtimeVoiceConsultQuestion, readSpeakableRealtimeVoiceToolResult, recordTalkObservabilityEvent } from "openclaw/plugin-sdk/realtime-voice";
4
5
  import { randomUUID } from "node:crypto";
@@ -99,8 +100,8 @@ var RealtimeAudioPacer = class {
99
100
  function calculateMulawRms(muLaw) {
100
101
  if (muLaw.length === 0) return 0;
101
102
  let sum = 0;
102
- for (let i = 0; i < muLaw.length; i += 1) {
103
- const normalized = (MULAW_LINEAR_SAMPLES[muLaw[i] ?? 0] ?? 0) / PCM16_MAX_AMPLITUDE;
103
+ for (const sample of muLaw) {
104
+ const normalized = (MULAW_LINEAR_SAMPLES[sample] ?? 0) / PCM16_MAX_AMPLITUDE;
104
105
  sum += normalized * normalized;
105
106
  }
106
107
  return Math.sqrt(sum / muLaw.length);
@@ -578,18 +579,20 @@ var RealtimeCallHandler = class {
578
579
  }
579
580
  issueStreamToken(meta = {}) {
580
581
  const token = randomUUID();
581
- this.pendingStreamTokens.set(token, {
582
- expiry: Date.now() + STREAM_TOKEN_TTL_MS,
582
+ const now = Date.now();
583
+ const expiry = resolveExpiresAtMsFromDurationMs(STREAM_TOKEN_TTL_MS, { nowMs: now });
584
+ if (expiry !== void 0) this.pendingStreamTokens.set(token, {
585
+ expiry,
583
586
  ...meta
584
587
  });
585
- for (const [candidate, entry] of this.pendingStreamTokens) if (Date.now() > entry.expiry) this.pendingStreamTokens.delete(candidate);
588
+ for (const [candidate, entry] of this.pendingStreamTokens) if (!isFutureDateTimestampMs(entry.expiry, { nowMs: now })) this.pendingStreamTokens.delete(candidate);
586
589
  return token;
587
590
  }
588
591
  consumeStreamToken(token) {
589
592
  const entry = this.pendingStreamTokens.get(token);
590
593
  if (!entry) return null;
591
594
  this.pendingStreamTokens.delete(token);
592
- if (Date.now() > entry.expiry) return null;
595
+ if (!isFutureDateTimestampMs(entry.expiry)) return null;
593
596
  return {
594
597
  from: entry.from,
595
598
  to: entry.to,
@@ -1,5 +1,5 @@
1
- import { o as resolveVoiceCallSessionKey } from "./config-U-rgixyY.js";
2
- import { f as resolveVoiceResponseModel } from "./runtime-entry-DwBgs2Sq.js";
1
+ import { o as resolveVoiceCallSessionKey } from "./config-BKyRNKHF.js";
2
+ import { f as resolveVoiceResponseModel } from "./runtime-entry-DXf7kdXt.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";
@@ -1,16 +1,18 @@
1
1
  import { isBlockedHostnameOrIp, isRequestBodyLimitError, readRequestBodyWithLimit, requestBodyErrorToText } from "./runtime-api.js";
2
2
  import "./api.js";
3
- import { a as resolveVoiceCallEffectiveConfig, c as deepMergeDefined, i as resolveVoiceCallConfig, n as normalizeVoiceCallConfig, o as resolveVoiceCallSessionKey, r as resolveTwilioAuthToken, s as validateProviderConfig } from "./config-U-rgixyY.js";
3
+ import { a as resolveVoiceCallEffectiveConfig, c as deepMergeDefined, i as resolveVoiceCallConfig, n as normalizeVoiceCallConfig, o as resolveVoiceCallSessionKey, r as resolveTwilioAuthToken, s as validateProviderConfig } from "./config-BKyRNKHF.js";
4
4
  import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
5
5
  import { isLoopbackHost } from "openclaw/plugin-sdk/gateway-runtime";
6
+ import { MAX_TIMER_TIMEOUT_MS, asDateTimestampMs, resolveExpiresAtMsFromDurationMs, resolveTimerTimeoutMs } from "openclaw/plugin-sdk/number-runtime";
6
7
  import { normalizeLowercaseStringOrEmpty, normalizeOptionalLowercaseString, normalizeOptionalString, normalizeStringEntries } from "openclaw/plugin-sdk/string-coerce-runtime";
7
8
  import { REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME, buildRealtimeVoiceAgentConsultPolicyInstructions, consultRealtimeVoiceAgent, convertPcmToMulaw8k, createTalkSessionController, recordTalkObservabilityEvent, resolveRealtimeVoiceAgentConsultTools, resolveRealtimeVoiceAgentConsultToolsAllow, resolveRealtimeVoiceFastContextConsult } from "openclaw/plugin-sdk/realtime-voice";
8
9
  import { z } from "zod";
9
10
  import fs from "node:fs";
10
11
  import os from "node:os";
11
12
  import path from "node:path";
12
- import crypto from "node:crypto";
13
+ import crypto, { createHash, randomUUID } from "node:crypto";
13
14
  import { appendRegularFile, privateFileStore, privateFileStoreSync, root } from "openclaw/plugin-sdk/security-runtime";
15
+ import { createPluginRuntimeStore } from "openclaw/plugin-sdk/runtime-store";
14
16
  import { parseTtsDirectives } from "openclaw/plugin-sdk/speech";
15
17
  import { spawn } from "node:child_process";
16
18
  import http from "node:http";
@@ -179,12 +181,224 @@ function addTranscriptEntry(call, speaker, text) {
179
181
  call.transcript.push(entry);
180
182
  }
181
183
  //#endregion
184
+ //#region extensions/voice-call/src/runtime-state.ts
185
+ const { setRuntime: setVoiceCallStateRuntime, clearRuntime: clearVoiceCallStateRuntime, tryGetRuntime: getOptionalVoiceCallStateRuntime } = createPluginRuntimeStore({
186
+ pluginId: "voice-call-state",
187
+ errorMessage: "Voice Call state runtime not initialized"
188
+ });
189
+ //#endregion
182
190
  //#region extensions/voice-call/src/manager/store.ts
183
191
  const pendingPersistWrites = /* @__PURE__ */ new Set();
192
+ const CALL_RECORD_EVENTS_NAMESPACE = "call-record-events";
193
+ const CALL_RECORD_EVENT_CHUNKS_NAMESPACE = "call-record-event-chunks";
194
+ const CALL_RECORD_MIGRATIONS_NAMESPACE = "call-record-migrations";
195
+ const CALL_RECORD_JSONL_MIGRATION_KEY = "calls-jsonl-v1";
196
+ const MAX_CALL_RECORD_EVENTS = 1e3;
197
+ const CALL_RECORD_EVENT_META_MAX_ENTRIES = 1100;
198
+ const MAX_CHUNKS_PER_CALL_RECORD_EVENT = 48;
199
+ const CALL_RECORD_CHUNK_MAX_ENTRIES = 48048;
200
+ const RAW_CHUNK_BYTES = 36 * 1024;
201
+ let callRecordEventSequence = 0;
202
+ function resolveCallLogPath(storePath) {
203
+ return path.join(storePath, "calls.jsonl");
204
+ }
205
+ function resolvePluginStateEnv(storePath) {
206
+ return {
207
+ ...process.env,
208
+ OPENCLAW_STATE_DIR: storePath
209
+ };
210
+ }
211
+ function createCallRecordStateStores(storePath) {
212
+ const runtime = getOptionalVoiceCallStateRuntime();
213
+ if (!runtime) return null;
214
+ const env = resolvePluginStateEnv(storePath);
215
+ return {
216
+ events: runtime.state.openSyncKeyedStore({
217
+ namespace: CALL_RECORD_EVENTS_NAMESPACE,
218
+ maxEntries: CALL_RECORD_EVENT_META_MAX_ENTRIES,
219
+ env
220
+ }),
221
+ chunks: runtime.state.openSyncKeyedStore({
222
+ namespace: CALL_RECORD_EVENT_CHUNKS_NAMESPACE,
223
+ maxEntries: CALL_RECORD_CHUNK_MAX_ENTRIES,
224
+ env
225
+ }),
226
+ migrations: runtime.state.openSyncKeyedStore({
227
+ namespace: CALL_RECORD_MIGRATIONS_NAMESPACE,
228
+ maxEntries: 100,
229
+ env
230
+ })
231
+ };
232
+ }
233
+ function tryCreateCallRecordStateStores(storePath) {
234
+ try {
235
+ return createCallRecordStateStores(storePath);
236
+ } catch (err) {
237
+ console.error("[voice-call] Failed to open SQLite call record store:", err);
238
+ return null;
239
+ }
240
+ }
241
+ function buildChunkKey(eventKey, index) {
242
+ return `${eventKey}:chunk:${String(index).padStart(4, "0")}`;
243
+ }
244
+ function buildJsonlEventKey(line, index) {
245
+ return `jsonl:${String(index).padStart(8, "0")}:${createHash("sha256").update(line).digest("hex")}`;
246
+ }
247
+ function nextCallRecordOrder() {
248
+ const sequence = callRecordEventSequence;
249
+ callRecordEventSequence = (callRecordEventSequence + 1) % 1e6;
250
+ return {
251
+ persistedAt: Date.now(),
252
+ sequence
253
+ };
254
+ }
255
+ function buildNewEventKey(order) {
256
+ return `event:${order.persistedAt.toString(36)}:${String(order.sequence).padStart(6, "0")}:${randomUUID()}`;
257
+ }
258
+ function parseEventKeySequence(key) {
259
+ const match = /^event:[^:]+:(\d+):/.exec(key);
260
+ return match ? Number.parseInt(match[1], 10) : 0;
261
+ }
262
+ function parseCallRecordLine(line, sequence = 0) {
263
+ if (!line.trim()) return null;
264
+ try {
265
+ const parsed = JSON.parse(line);
266
+ if (parsed && typeof parsed === "object" && parsed.version === 2) {
267
+ const envelope = parsed;
268
+ return {
269
+ call: CallRecordSchema.parse(envelope.call),
270
+ persistedAt: typeof envelope.persistedAt === "number" && Number.isFinite(envelope.persistedAt) ? envelope.persistedAt : 0,
271
+ sequence: typeof envelope.sequence === "number" && Number.isFinite(envelope.sequence) ? envelope.sequence : sequence,
272
+ orderKey: ""
273
+ };
274
+ }
275
+ return {
276
+ call: CallRecordSchema.parse(parsed),
277
+ persistedAt: 0,
278
+ sequence,
279
+ orderKey: ""
280
+ };
281
+ } catch {
282
+ return null;
283
+ }
284
+ }
285
+ function registerCallRecordEvent(stores, eventKey, call, order) {
286
+ const serialized = JSON.stringify(call);
287
+ const buffer = Buffer.from(serialized, "utf8");
288
+ const chunkCount = Math.max(1, Math.ceil(buffer.byteLength / RAW_CHUNK_BYTES));
289
+ if (chunkCount > MAX_CHUNKS_PER_CALL_RECORD_EVENT) throw new Error(`voice-call record exceeds SQLite chunk limit (${chunkCount}/${MAX_CHUNKS_PER_CALL_RECORD_EVENT})`);
290
+ for (let index = 0; index < chunkCount; index += 1) {
291
+ const chunk = buffer.subarray(index * RAW_CHUNK_BYTES, (index + 1) * RAW_CHUNK_BYTES);
292
+ stores.chunks.register(buildChunkKey(eventKey, index), {
293
+ index,
294
+ dataBase64: chunk.toString("base64")
295
+ });
296
+ }
297
+ stores.events.register(eventKey, {
298
+ chunkCount,
299
+ byteLength: buffer.byteLength,
300
+ persistedAt: order?.persistedAt,
301
+ sequence: order?.sequence
302
+ });
303
+ pruneCallRecordEvents(stores);
304
+ }
305
+ function deleteCallRecordEventRows(stores, eventKey) {
306
+ const meta = stores.events.lookup(eventKey);
307
+ stores.events.delete(eventKey);
308
+ if (!meta) return;
309
+ for (let index = 0; index < meta.chunkCount; index += 1) stores.chunks.delete(buildChunkKey(eventKey, index));
310
+ }
311
+ function pruneCallRecordEvents(stores) {
312
+ const rows = stores.events.entries();
313
+ if (rows.length <= MAX_CALL_RECORD_EVENTS) return;
314
+ const sorted = rows.toSorted((a, b) => a.createdAt - b.createdAt || a.key.localeCompare(b.key));
315
+ for (const row of sorted.slice(0, rows.length - MAX_CALL_RECORD_EVENTS)) deleteCallRecordEventRows(stores, row.key);
316
+ }
317
+ function registerCallRecordEventIfAbsent(stores, eventKey, record) {
318
+ if (!stores.events.lookup(eventKey)) registerCallRecordEvent(stores, eventKey, record.call, {
319
+ persistedAt: record.persistedAt,
320
+ sequence: record.sequence
321
+ });
322
+ }
323
+ function readCallRecordEvent(stores, eventKey) {
324
+ const meta = stores.events.lookup(eventKey);
325
+ if (!meta) return null;
326
+ const chunks = [];
327
+ for (let index = 0; index < meta.chunkCount; index += 1) {
328
+ const chunk = stores.chunks.lookup(buildChunkKey(eventKey, index));
329
+ if (!chunk || chunk.index !== index) return null;
330
+ chunks.push(Buffer.from(chunk.dataBase64, "base64"));
331
+ }
332
+ return parseCallRecordLine(Buffer.concat(chunks, meta.byteLength).toString("utf8"))?.call ?? null;
333
+ }
334
+ function ensureLegacyCallLogImported(storePath, stores) {
335
+ const imported = stores.migrations.lookup(CALL_RECORD_JSONL_MIGRATION_KEY) !== void 0;
336
+ const logPath = resolveCallLogPath(storePath);
337
+ const content = privateFileStoreSync(storePath).readTextIfExists(path.basename(logPath));
338
+ if (content === null) {
339
+ if (!imported) stores.migrations.register(CALL_RECORD_JSONL_MIGRATION_KEY, { importedAt: (/* @__PURE__ */ new Date()).toISOString() });
340
+ return [];
341
+ }
342
+ const fallbackCalls = [];
343
+ {
344
+ let index = 0;
345
+ let importFailed = false;
346
+ for (const line of content.split("\n")) {
347
+ const parsed = parseCallRecordLine(line, index);
348
+ if (!parsed) {
349
+ index += 1;
350
+ continue;
351
+ }
352
+ try {
353
+ registerCallRecordEventIfAbsent(stores, buildJsonlEventKey(line, index), parsed);
354
+ } catch (err) {
355
+ importFailed = true;
356
+ fallbackCalls.push({
357
+ ...parsed,
358
+ orderKey: `jsonl:${String(index).padStart(8, "0")}`
359
+ });
360
+ console.error("[voice-call] Failed to import persisted call record:", err);
361
+ }
362
+ index += 1;
363
+ }
364
+ if (!importFailed) try {
365
+ fs.rmSync(logPath, { force: true });
366
+ } catch {}
367
+ }
368
+ if (!imported) stores.migrations.register(CALL_RECORD_JSONL_MIGRATION_KEY, { importedAt: (/* @__PURE__ */ new Date()).toISOString() });
369
+ return fallbackCalls;
370
+ }
371
+ function readCallRecordEvents(storePath, stores) {
372
+ const fallbackCalls = ensureLegacyCallLogImported(storePath, stores);
373
+ return [...stores.events.entries().toSorted((a, b) => a.createdAt - b.createdAt || a.key.localeCompare(b.key)).map((entry) => {
374
+ const call = readCallRecordEvent(stores, entry.key);
375
+ return call ? {
376
+ call,
377
+ persistedAt: entry.value.persistedAt ?? entry.createdAt,
378
+ sequence: entry.value.sequence ?? parseEventKeySequence(entry.key),
379
+ orderKey: entry.key
380
+ } : null;
381
+ }).filter((entry) => entry !== null), ...fallbackCalls].toSorted((a, b) => a.persistedAt - b.persistedAt || a.sequence - b.sequence || a.orderKey.localeCompare(b.orderKey)).map((entry) => entry.call);
382
+ }
184
383
  function persistCallRecord(storePath, call) {
384
+ const stores = tryCreateCallRecordStateStores(storePath);
385
+ if (stores) try {
386
+ ensureLegacyCallLogImported(storePath, stores);
387
+ const order = nextCallRecordOrder();
388
+ registerCallRecordEvent(stores, buildNewEventKey(order), call, order);
389
+ return;
390
+ } catch (err) {
391
+ console.error("[voice-call] Failed to persist call record:", err);
392
+ }
393
+ const logPath = resolveCallLogPath(storePath);
394
+ const order = nextCallRecordOrder();
185
395
  const write = appendRegularFile({
186
- filePath: path.join(storePath, "calls.jsonl"),
187
- content: `${JSON.stringify(call)}\n`,
396
+ filePath: logPath,
397
+ content: `${JSON.stringify({
398
+ version: 2,
399
+ ...order,
400
+ call
401
+ })}\n`,
188
402
  rejectSymlinkParents: true
189
403
  }).catch((err) => {
190
404
  console.error("[voice-call] Failed to persist call record:", err);
@@ -194,23 +408,22 @@ function persistCallRecord(storePath, call) {
194
408
  pendingPersistWrites.add(write);
195
409
  }
196
410
  function loadActiveCallsFromStore(storePath) {
197
- const logPath = path.join(storePath, "calls.jsonl");
198
- const content = privateFileStoreSync(storePath).readTextIfExists(path.basename(logPath));
199
- if (content === null) return {
411
+ const stores = tryCreateCallRecordStateStores(storePath);
412
+ let calls;
413
+ try {
414
+ calls = stores ? readCallRecordEvents(storePath, stores) : readCallRecordsFromLegacyLog(storePath);
415
+ } catch (err) {
416
+ console.error("[voice-call] Failed to read SQLite call records:", err);
417
+ calls = readCallRecordsFromLegacyLog(storePath);
418
+ }
419
+ if (calls.length === 0) return {
200
420
  activeCalls: /* @__PURE__ */ new Map(),
201
421
  providerCallIdMap: /* @__PURE__ */ new Map(),
202
422
  processedEventIds: /* @__PURE__ */ new Set(),
203
423
  rejectedProviderCallIds: /* @__PURE__ */ new Set()
204
424
  };
205
- const lines = content.split("\n");
206
425
  const callMap = /* @__PURE__ */ new Map();
207
- for (const line of lines) {
208
- if (!line.trim()) continue;
209
- try {
210
- const call = CallRecordSchema.parse(JSON.parse(line));
211
- callMap.set(call.callId, call);
212
- } catch {}
213
- }
426
+ for (const call of calls) callMap.set(call.callId, call);
214
427
  const activeCalls = /* @__PURE__ */ new Map();
215
428
  const providerCallIdMap = /* @__PURE__ */ new Map();
216
429
  const processedEventIds = /* @__PURE__ */ new Set();
@@ -229,17 +442,40 @@ function loadActiveCallsFromStore(storePath) {
229
442
  };
230
443
  }
231
444
  async function getCallHistoryFromStore(storePath, limit = 50) {
232
- const logPath = path.join(storePath, "calls.jsonl");
445
+ if (limit <= 0) return [];
446
+ const stores = tryCreateCallRecordStateStores(storePath);
447
+ if (stores) try {
448
+ return readCallRecordEvents(storePath, stores).slice(-limit);
449
+ } catch (err) {
450
+ console.error("[voice-call] Failed to read SQLite call history:", err);
451
+ }
452
+ const logPath = resolveCallLogPath(storePath);
233
453
  const content = await privateFileStore(storePath).readTextIfExists(path.basename(logPath));
234
454
  if (content === null) return [];
235
455
  const lines = content.trim().split("\n").filter(Boolean);
236
456
  const calls = [];
237
- for (const line of lines.slice(-limit)) try {
238
- const parsed = CallRecordSchema.parse(JSON.parse(line));
239
- calls.push(parsed);
240
- } catch {}
457
+ for (const [index, line] of lines.slice(-limit).entries()) {
458
+ const parsed = parseCallRecordLine(line, index);
459
+ if (parsed) calls.push(parsed.call);
460
+ }
241
461
  return calls;
242
462
  }
463
+ function readCallRecordsFromLegacyLog(storePath) {
464
+ const logPath = resolveCallLogPath(storePath);
465
+ const content = privateFileStoreSync(storePath).readTextIfExists(path.basename(logPath));
466
+ if (content === null) return [];
467
+ return content.split("\n").map((line, index) => parseCallRecordLine(line, index)?.call ?? null).filter((call) => call !== null);
468
+ }
469
+ //#endregion
470
+ //#region extensions/voice-call/src/manager/timer-delays.ts
471
+ function resolveVoiceCallSecondsTimerDelayMs(seconds, minMs = 1) {
472
+ if (!Number.isFinite(seconds)) return resolveTimerTimeoutMs(MAX_TIMER_TIMEOUT_MS, MAX_TIMER_TIMEOUT_MS, minMs);
473
+ const timeoutMs = Math.floor(seconds * 1e3);
474
+ return resolveTimerTimeoutMs(Number.isFinite(timeoutMs) ? timeoutMs : MAX_TIMER_TIMEOUT_MS, minMs, minMs);
475
+ }
476
+ function resolveVoiceCallTimerDelayMs(timeoutMs, fallbackMs = 1) {
477
+ return resolveTimerTimeoutMs(timeoutMs, fallbackMs);
478
+ }
243
479
  //#endregion
244
480
  //#region extensions/voice-call/src/manager/timers.ts
245
481
  function clearMaxDurationTimer(ctx, callId) {
@@ -251,7 +487,7 @@ function clearMaxDurationTimer(ctx, callId) {
251
487
  }
252
488
  function startMaxDurationTimer(params) {
253
489
  clearMaxDurationTimer(params.ctx, params.callId);
254
- const maxDurationMs = params.timeoutMs ?? params.ctx.config.maxDurationSeconds * 1e3;
490
+ const maxDurationMs = params.timeoutMs === void 0 ? resolveVoiceCallSecondsTimerDelayMs(params.ctx.config.maxDurationSeconds) : resolveVoiceCallTimerDelayMs(params.timeoutMs);
255
491
  console.log(`[voice-call] Starting max duration timer (${Math.ceil(maxDurationMs / 1e3)}s) for call ${params.callId}`);
256
492
  const timer = setTimeout(async () => {
257
493
  params.ctx.maxDurationTimers.delete(params.callId);
@@ -287,7 +523,7 @@ function resolveTranscriptWaiter(ctx, callId, transcript, turnToken) {
287
523
  }
288
524
  function waitForFinalTranscript(ctx, callId, turnToken) {
289
525
  if (ctx.transcriptWaiters.has(callId)) return Promise.reject(/* @__PURE__ */ new Error("Already waiting for transcript"));
290
- const timeoutMs = ctx.config.transcriptTimeoutMs;
526
+ const timeoutMs = resolveVoiceCallTimerDelayMs(ctx.config.transcriptTimeoutMs);
291
527
  return new Promise((resolve, reject) => {
292
528
  const timeout = setTimeout(() => {
293
529
  ctx.transcriptWaiters.delete(callId);
@@ -654,6 +890,7 @@ async function speakInitialMessage(ctx, providerCallId) {
654
890
  }
655
891
  if (mode === "notify") {
656
892
  const delaySec = ctx.config.outbound.notifyHangupDelaySec;
893
+ const delayMs = resolveVoiceCallSecondsTimerDelayMs(delaySec, 0);
657
894
  console.log(`[voice-call] Notify mode: auto-hangup in ${delaySec}s for call ${call.callId}`);
658
895
  setTimeout(async () => {
659
896
  const currentCall = ctx.activeCalls.get(call.callId);
@@ -661,7 +898,7 @@ async function speakInitialMessage(ctx, providerCallId) {
661
898
  console.log(`[voice-call] Notify mode: hanging up call ${call.callId}`);
662
899
  await endCall(ctx, call.callId);
663
900
  }
664
- }, delaySec * 1e3);
901
+ }, delayMs);
665
902
  } else if (mode === "conversation" && ctx.provider && shouldStartListeningAfterInitialMessage(ctx)) {
666
903
  transitionState(call, "listening");
667
904
  persistCallRecord(ctx.storePath, call);
@@ -1047,7 +1284,7 @@ var CallManager = class {
1047
1284
  let skippedAlreadyElapsedTimers = 0;
1048
1285
  for (const [callId, call] of verified) if (call.answeredAt && !TerminalStates.has(call.state)) {
1049
1286
  const elapsed = Date.now() - call.answeredAt;
1050
- const maxDurationMs = this.config.maxDurationSeconds * 1e3;
1287
+ const maxDurationMs = resolveVoiceCallSecondsTimerDelayMs(this.config.maxDurationSeconds);
1051
1288
  if (elapsed >= maxDurationMs) {
1052
1289
  verified.delete(callId);
1053
1290
  if (call.providerCallId) this.providerCallIdMap.delete(call.providerCallId);
@@ -1074,7 +1311,7 @@ var CallManager = class {
1074
1311
  */
1075
1312
  async verifyRestoredCalls(provider, candidates) {
1076
1313
  if (candidates.size === 0) return /* @__PURE__ */ new Map();
1077
- const maxAgeMs = this.config.maxDurationSeconds * 1e3;
1314
+ const maxAgeMs = resolveVoiceCallSecondsTimerDelayMs(this.config.maxDurationSeconds);
1078
1315
  const now = Date.now();
1079
1316
  const verified = /* @__PURE__ */ new Map();
1080
1317
  const verifyTasks = [];
@@ -1348,7 +1585,7 @@ function createTelephonyTtsProvider(params) {
1348
1585
  const providerConfigs = collectTelephonyProviderConfigs(ttsConfig);
1349
1586
  const activeProvider = normalizeProviderId(ttsConfig?.provider);
1350
1587
  return {
1351
- synthesisTimeoutMs: mergedConfig.messages?.tts?.timeoutMs ?? 8e3,
1588
+ synthesisTimeoutMs: resolveTimerTimeoutMs(mergedConfig.messages?.tts?.timeoutMs, TELEPHONY_DEFAULT_TTS_TIMEOUT_MS),
1352
1589
  synthesizeForTelephony: async (text) => {
1353
1590
  const directives = parseTtsDirectives(text, modelOverrides, {
1354
1591
  cfg: mergedConfig,
@@ -1506,7 +1743,6 @@ function runTailscaleCommand(args, timeoutMs = 2500) {
1506
1743
  text: ""
1507
1744
  };
1508
1745
  let settled = false;
1509
- let timer;
1510
1746
  const finish = (result) => {
1511
1747
  if (settled) return;
1512
1748
  settled = true;
@@ -1523,7 +1759,7 @@ function runTailscaleCommand(args, timeoutMs = 2500) {
1523
1759
  });
1524
1760
  }
1525
1761
  });
1526
- timer = setTimeout(() => {
1762
+ const timer = setTimeout(() => {
1527
1763
  proc.kill("SIGKILL");
1528
1764
  finish({
1529
1765
  code: -1,
@@ -1938,7 +2174,7 @@ var MediaStreamHandler = class {
1938
2174
  this.ttsPlaying = /* @__PURE__ */ new Map();
1939
2175
  this.ttsActiveControllers = /* @__PURE__ */ new Map();
1940
2176
  this.config = config;
1941
- this.preStartTimeoutMs = config.preStartTimeoutMs ?? DEFAULT_PRE_START_TIMEOUT_MS;
2177
+ this.preStartTimeoutMs = resolveTimerTimeoutMs(config.preStartTimeoutMs, DEFAULT_PRE_START_TIMEOUT_MS);
1942
2178
  this.maxPendingConnections = config.maxPendingConnections ?? DEFAULT_MAX_PENDING_CONNECTIONS;
1943
2179
  this.maxPendingConnectionsPerIp = config.maxPendingConnectionsPerIp ?? DEFAULT_MAX_PENDING_CONNECTIONS_PER_IP;
1944
2180
  this.maxConnections = config.maxConnections ?? DEFAULT_MAX_CONNECTIONS;
@@ -2588,7 +2824,7 @@ function loadRealtimeTranscriptionRuntime() {
2588
2824
  return realtimeTranscriptionRuntimePromise;
2589
2825
  }
2590
2826
  function loadResponseGeneratorModule() {
2591
- responseGeneratorModulePromise ??= import("./response-generator-C8EHsqMw.js");
2827
+ responseGeneratorModulePromise ??= import("./response-generator-Cu4sOiUO.js");
2592
2828
  return responseGeneratorModulePromise;
2593
2829
  }
2594
2830
  function sanitizeTranscriptForLog(value) {
@@ -2598,7 +2834,7 @@ function sanitizeTranscriptForLog(value) {
2598
2834
  }
2599
2835
  function appendRecentTalkEventMetadata(call, event) {
2600
2836
  const metadata = call.metadata ?? {};
2601
- const recent = Array.isArray(metadata.recentTalkEvents) ? metadata.recentTalkEvents.filter((entry) => !!entry && typeof entry === "object" && !Array.isArray(entry)) : [];
2837
+ const recent = Array.isArray(metadata.recentTalkEvents) ? metadata.recentTalkEvents.filter((entry) => Boolean(entry) && typeof entry === "object" && !Array.isArray(entry)) : [];
2602
2838
  recent.push({
2603
2839
  at: event.timestamp,
2604
2840
  type: event.type,
@@ -3094,8 +3330,11 @@ var VoiceCallWebhookServer = class {
3094
3330
  this.webhookInFlightLimiter.release(inFlightKey);
3095
3331
  }
3096
3332
  }
3097
- pruneReplayResponses(now) {
3098
- for (const [key, entry] of this.replayResponses) if (entry.expiresAt <= now) this.replayResponses.delete(key);
3333
+ pruneReplayResponses(rawNow) {
3334
+ const now = asDateTimestampMs(rawNow);
3335
+ if (now !== void 0) {
3336
+ for (const [key, entry] of this.replayResponses) if (entry.expiresAt <= now) this.replayResponses.delete(key);
3337
+ }
3099
3338
  while (this.replayResponses.size > WEBHOOK_REPLAY_RESPONSE_MAX_ENTRIES) {
3100
3339
  const oldest = this.replayResponses.keys().next().value;
3101
3340
  if (!oldest) break;
@@ -3103,9 +3342,9 @@ var VoiceCallWebhookServer = class {
3103
3342
  }
3104
3343
  }
3105
3344
  async getCachedReplayResponse(key) {
3106
- const now = Date.now();
3345
+ const now = asDateTimestampMs(Date.now());
3107
3346
  const entry = this.replayResponses.get(key);
3108
- if (!entry) return null;
3347
+ if (!entry || now === void 0) return null;
3109
3348
  if (entry.expiresAt <= now) {
3110
3349
  this.replayResponses.delete(key);
3111
3350
  return null;
@@ -3114,14 +3353,15 @@ var VoiceCallWebhookServer = class {
3114
3353
  }
3115
3354
  async cacheReplayResponse(key, buildResponse) {
3116
3355
  const now = Date.now();
3356
+ const expiresAt = resolveExpiresAtMsFromDurationMs(WEBHOOK_REPLAY_RESPONSE_TTL_MS, { nowMs: now });
3117
3357
  this.replayResponseCacheCalls += 1;
3118
3358
  if (this.replayResponseCacheCalls % WEBHOOK_REPLAY_RESPONSE_PRUNE_INTERVAL === 0) this.pruneReplayResponses(now);
3119
3359
  const response = buildResponse().then(cloneWebhookResponsePayload).catch((err) => {
3120
3360
  this.replayResponses.delete(key);
3121
3361
  throw err;
3122
3362
  });
3123
- this.replayResponses.set(key, {
3124
- expiresAt: now + WEBHOOK_REPLAY_RESPONSE_TTL_MS,
3363
+ if (expiresAt !== void 0) this.replayResponses.set(key, {
3364
+ expiresAt,
3125
3365
  response
3126
3366
  });
3127
3367
  if (this.replayResponses.size > WEBHOOK_REPLAY_RESPONSE_MAX_ENTRIES) this.pruneReplayResponses(now);
@@ -3268,15 +3508,15 @@ let mockProviderPromise;
3268
3508
  let realtimeVoiceRuntimePromise;
3269
3509
  let realtimeHandlerPromise;
3270
3510
  function loadTelnyxProvider() {
3271
- telnyxProviderPromise ??= import("./telnyx-C8sgJugJ.js");
3511
+ telnyxProviderPromise ??= import("./telnyx-CxEuPBOJ.js");
3272
3512
  return telnyxProviderPromise;
3273
3513
  }
3274
3514
  function loadTwilioProvider() {
3275
- twilioProviderPromise ??= import("./twilio-CpBg6Ir5.js");
3515
+ twilioProviderPromise ??= import("./twilio-DzQmSUQP.js");
3276
3516
  return twilioProviderPromise;
3277
3517
  }
3278
3518
  function loadPlivoProvider() {
3279
- plivoProviderPromise ??= import("./plivo-CVgE_V_c.js");
3519
+ plivoProviderPromise ??= import("./plivo-Dpf08ZVJ.js");
3280
3520
  return plivoProviderPromise;
3281
3521
  }
3282
3522
  function loadMockProvider() {
@@ -3288,7 +3528,7 @@ function loadRealtimeVoiceRuntime() {
3288
3528
  return realtimeVoiceRuntimePromise;
3289
3529
  }
3290
3530
  function loadRealtimeHandler() {
3291
- realtimeHandlerPromise ??= import("./realtime-handler-DQDCDHv2.js");
3531
+ realtimeHandlerPromise ??= import("./realtime-handler-Crs3kty2.js");
3292
3532
  return realtimeHandlerPromise;
3293
3533
  }
3294
3534
  function resolveVoiceCallConsultSessionKey(call) {
@@ -3394,7 +3634,7 @@ async function resolveRealtimeProvider(params) {
3394
3634
  });
3395
3635
  }
3396
3636
  async function createVoiceCallRuntime(params) {
3397
- const { config: rawConfig, coreConfig, fullConfig, agentRuntime, ttsRuntime, logger } = params;
3637
+ const { config: rawConfig, coreConfig, fullConfig, agentRuntime, stateRuntime, ttsRuntime, logger } = params;
3398
3638
  const log = logger ?? {
3399
3639
  info: console.log,
3400
3640
  warn: console.warn,
@@ -3408,6 +3648,7 @@ async function createVoiceCallRuntime(params) {
3408
3648
  const validation = validateProviderConfig(config);
3409
3649
  if (!validation.valid) throw new Error(`Invalid voice-call config: ${validation.errors.join("; ")}`);
3410
3650
  const provider = await resolveProvider(config);
3651
+ if (stateRuntime) setVoiceCallStateRuntime({ state: stateRuntime });
3411
3652
  const manager = new CallManager(config);
3412
3653
  const realtimeProvider = config.realtime.enabled ? await resolveRealtimeProvider({
3413
3654
  config,
@@ -3552,4 +3793,4 @@ async function createVoiceCallRuntime(params) {
3552
3793
  }
3553
3794
  }
3554
3795
  //#endregion
3555
- export { getHeader as a, getTailscaleSelfInfo as c, chunkAudio as d, resolveVoiceResponseModel as f, mapVoiceToPolly as h, normalizeProviderStatus as i, setupTailscaleExposureRoute as l, escapeXml as m, isProviderStatusTerminal as n, resolveWebhookExposureStatus as o, resolveUserPath as p, mapProviderStatusToEndReason as r, cleanupTailscaleExposureRoute as s, createVoiceCallRuntime as t, TELEPHONY_DEFAULT_TTS_TIMEOUT_MS as u };
3796
+ export { setVoiceCallStateRuntime as _, getHeader as a, getTailscaleSelfInfo as c, chunkAudio as d, resolveVoiceResponseModel as f, getCallHistoryFromStore as g, mapVoiceToPolly as h, normalizeProviderStatus as i, setupTailscaleExposureRoute as l, escapeXml as m, isProviderStatusTerminal as n, resolveWebhookExposureStatus as o, resolveUserPath as p, mapProviderStatusToEndReason as r, cleanupTailscaleExposureRoute as s, createVoiceCallRuntime as t, TELEPHONY_DEFAULT_TTS_TIMEOUT_MS as u };
@@ -1,2 +1,2 @@
1
- import { t as createVoiceCallRuntime } from "./runtime-entry-DwBgs2Sq.js";
1
+ import { t as createVoiceCallRuntime } from "./runtime-entry-DXf7kdXt.js";
2
2
  export { createVoiceCallRuntime };
package/dist/setup-api.js CHANGED
@@ -1,4 +1,4 @@
1
- import { n as migrateVoiceCallLegacyConfigInput } from "./config-compat-CokN3Zzr.js";
1
+ import { n as migrateVoiceCallLegacyConfigInput } from "./config-compat-BLdRz9GR.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 { i as verifyTelnyxWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-hXuXY1dk.js";
1
+ import { i as verifyTelnyxWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-mCcsLRBb.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-DwBgs2Sq.js";
4
- import { a as verifyTwilioWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-hXuXY1dk.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-DXf7kdXt.js";
4
+ import { a as verifyTwilioWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-mCcsLRBb.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";
@@ -164,10 +164,13 @@ export type TExtendsMapping<Input extends [unknown, unknown, unknown, unknown, u
164
164
  export declare function ExtendsMapping(input: [unknown, unknown, unknown, unknown, unknown, unknown] | []): unknown;
165
165
  export type TBaseMapping<Input extends [unknown, unknown, unknown] | unknown> = (Input extends ['(', infer Type extends T.TSchema, ')'] ? Type : Input extends infer Type extends T.TSchema ? Type : never);
166
166
  export declare function BaseMapping(input: [unknown, unknown, unknown] | unknown): unknown;
167
+ export type TWithMapping<Input extends [unknown, unknown] | []> = (Input extends ['with', infer Options extends Record<PropertyKey, unknown>] ? Options : []);
168
+ export declare function WithMapping(input: [unknown, unknown] | []): unknown;
167
169
  type TFactorIndexArray<Type extends T.TSchema, IndexArray extends unknown[]> = (IndexArray extends [infer Left extends T.TSchema[], ...infer Right extends unknown[]] ? (Left extends [infer Indexer extends T.TSchema] ? TFactorIndexArray<C.TIndexDeferred<Type, Indexer>, Right> : Left extends [] ? TFactorIndexArray<T.TArray<Type>, Right> : T.TNever) : Type);
168
170
  type TFactorExtends<Type extends T.TSchema, Extends extends unknown[]> = (Extends extends [infer Right extends T.TSchema, infer True extends T.TSchema, infer False extends T.TSchema] ? C.TConditionalDeferred<Type, Right, True, False> : Type);
169
- export type TFactorMapping<Input extends [unknown, unknown, unknown, unknown]> = (Input extends [infer KeyOf extends boolean, infer Type extends T.TSchema, infer IndexArray extends unknown[], infer Extend extends unknown[]] ? KeyOf extends true ? TFactorExtends<C.TKeyOfDeferred<TFactorIndexArray<Type, IndexArray>>, Extend> : TFactorExtends<TFactorIndexArray<Type, IndexArray>, Extend> : never);
170
- export declare function FactorMapping(input: [unknown, unknown, unknown, unknown]): unknown;
171
+ type TFactorWith<Type extends T.TSchema, With extends unknown> = (With extends Record<PropertyKey, unknown> ? C.TOptionsDeferred<Type, With> : Type);
172
+ export type TFactorMapping<Input extends [unknown, unknown, unknown, unknown, unknown]> = (Input extends [infer KeyOf extends boolean, infer Type extends T.TSchema, infer IndexArray extends unknown[], infer Extend extends unknown[], infer WithClause extends unknown] ? TFactorWith<KeyOf extends true ? TFactorExtends<C.TKeyOfDeferred<TFactorIndexArray<Type, IndexArray>>, Extend> : TFactorExtends<TFactorIndexArray<Type, IndexArray>, Extend>, WithClause> : never);
173
+ export declare function FactorMapping(input: [unknown, unknown, unknown, unknown, unknown]): unknown;
171
174
  type TExprBinaryMapping<Left extends T.TSchema, Rest extends unknown[]> = (Rest extends [infer Operator extends unknown, infer Right extends T.TSchema, infer Next extends unknown[]] ? (TExprBinaryMapping<Right, Next> extends infer Schema extends T.TSchema ? (Operator extends '&' ? (Schema extends T.TIntersect<infer Types extends T.TSchema[]> ? T.TIntersect<[Left, ...Types]> : T.TIntersect<[Left, Schema]>) : Operator extends '|' ? (Schema extends T.TUnion<infer Types extends T.TSchema[]> ? T.TUnion<[Left, ...Types]> : T.TUnion<[Left, Schema]>) : never) : never) : Left);
172
175
  export type TExprTermTailMapping<Input extends [unknown, unknown, unknown] | []> = (Input);
173
176
  export declare function ExprTermTailMapping(input: [unknown, unknown, unknown] | []): unknown;
@@ -185,27 +185,34 @@ export function BaseMapping(input) {
185
185
  ? input[1]
186
186
  : input;
187
187
  }
188
+ export function WithMapping(input) {
189
+ return Guard.IsEqual(input.length, 2) ? input[1] : [];
190
+ }
188
191
  // deno-coverage-ignore-start
189
- // ...
190
- const FactorIndexArray = (Type, indexArray) => {
192
+ function FactorIndexArray(Type, indexArray) {
191
193
  return indexArray.reduce((result, left) => {
192
194
  const _left = left;
193
195
  return (Guard.IsEqual(_left.length, 1) ? C.IndexDeferred(result, _left[0]) :
194
196
  Guard.IsEqual(_left.length, 0) ? T.Array(result) :
195
197
  Unreachable());
196
198
  }, Type);
197
- };
199
+ }
198
200
  // deno-coverage-ignore-stop
199
- const FactorExtends = (type, extend) => {
201
+ function FactorExtends(type, extend) {
200
202
  return Guard.IsEqual(extend.length, 3)
201
203
  ? C.ConditionalDeferred(type, extend[0], extend[1], extend[2])
202
204
  : type;
203
- };
205
+ }
206
+ function FactorWith(type, withClause) {
207
+ return Guard.IsArray(withClause) && Guard.IsEqual(withClause.length, 0)
208
+ ? type
209
+ : C.OptionsDeferred(type, withClause);
210
+ }
204
211
  export function FactorMapping(input) {
205
- const [keyOf, type, indexArray, extend] = input;
206
- return keyOf
212
+ const [keyOf, type, indexArray, extend, withClause] = input;
213
+ return FactorWith(keyOf
207
214
  ? FactorExtends(C.KeyOfDeferred(FactorIndexArray(type, indexArray)), extend)
208
- : FactorExtends(FactorIndexArray(type, indexArray), extend);
215
+ : FactorExtends(FactorIndexArray(type, indexArray), extend), withClause);
209
216
  }
210
217
  // deno-coverage-ignore-start
211
218
  function ExprBinaryMapping(left, rest) {
@@ -43,7 +43,8 @@ export type TIndexArray_0<Input extends string, Result extends unknown[] = []> =
43
43
  export type TIndexArray<Input extends string> = TIndexArray_0<Input> extends [infer _0 extends ([unknown, unknown, unknown] | [unknown, unknown])[], infer Input extends string] ? [S.TIndexArrayMapping<_0>, Input] : [];
44
44
  export type TExtends<Input extends string> = ((Token.TConst<'extends', Input> extends [infer _0, infer Input extends string] ? (TType<Input> extends [infer _1, infer Input extends string] ? (Token.TConst<'?', Input> extends [infer _2, infer Input extends string] ? (TType<Input> extends [infer _3, infer Input extends string] ? (Token.TConst<':', Input> extends [infer _4, infer Input extends string] ? (TType<Input> extends [infer _5, infer Input extends string] ? [[_0, _1, _2, _3, _4, _5], Input] : []) : []) : []) : []) : []) : []) extends [infer _0, infer Input extends string] ? [_0, Input] : [[], Input] extends [infer _0, infer Input extends string] ? [_0, Input] : []) extends [infer _0 extends [unknown, unknown, unknown, unknown, unknown, unknown] | [], infer Input extends string] ? [S.TExtendsMapping<_0>, Input] : [];
45
45
  export type TBase<Input extends string> = ((Token.TConst<'(', Input> extends [infer _0, infer Input extends string] ? (TType<Input> extends [infer _1, infer Input extends string] ? (Token.TConst<')', Input> extends [infer _2, infer Input extends string] ? [[_0, _1, _2], Input] : []) : []) : []) extends [infer _0, infer Input extends string] ? [_0, Input] : TKeyword<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : T_Object_<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TTuple<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TTemplateLiteral<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TLiteral<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TConstructor<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : T_Function_<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TMapped<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TOptions<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TGenericCall<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : TReference<Input> extends [infer _0, infer Input extends string] ? [_0, Input] : []) extends [infer _0 extends [unknown, unknown, unknown] | unknown, infer Input extends string] ? [S.TBaseMapping<_0>, Input] : [];
46
- export type TFactor<Input extends string> = (TKeyOf<Input> extends [infer _0, infer Input extends string] ? (TBase<Input> extends [infer _1, infer Input extends string] ? (TIndexArray<Input> extends [infer _2, infer Input extends string] ? (TExtends<Input> extends [infer _3, infer Input extends string] ? [[_0, _1, _2, _3], Input] : []) : []) : []) : []) extends [infer _0 extends [unknown, unknown, unknown, unknown], infer Input extends string] ? [S.TFactorMapping<_0>, Input] : [];
46
+ export type TWith<Input extends string> = ((Token.TConst<'with', Input> extends [infer _0, infer Input extends string] ? (TJsonObject<Input> extends [infer _1, infer Input extends string] ? [[_0, _1], Input] : []) : []) extends [infer _0, infer Input extends string] ? [_0, Input] : [[], Input] extends [infer _0, infer Input extends string] ? [_0, Input] : []) extends [infer _0 extends [unknown, unknown] | [], infer Input extends string] ? [S.TWithMapping<_0>, Input] : [];
47
+ export type TFactor<Input extends string> = (TKeyOf<Input> extends [infer _0, infer Input extends string] ? (TBase<Input> extends [infer _1, infer Input extends string] ? (TIndexArray<Input> extends [infer _2, infer Input extends string] ? (TExtends<Input> extends [infer _3, infer Input extends string] ? (TWith<Input> extends [infer _4, infer Input extends string] ? [[_0, _1, _2, _3, _4], Input] : []) : []) : []) : []) : []) extends [infer _0 extends [unknown, unknown, unknown, unknown, unknown], infer Input extends string] ? [S.TFactorMapping<_0>, Input] : [];
47
48
  export type TExprTermTail<Input extends string> = ((Token.TConst<'&', Input> extends [infer _0, infer Input extends string] ? (TFactor<Input> extends [infer _1, infer Input extends string] ? (TExprTermTail<Input> extends [infer _2, infer Input extends string] ? [[_0, _1, _2], Input] : []) : []) : []) extends [infer _0, infer Input extends string] ? [_0, Input] : [[], Input] extends [infer _0, infer Input extends string] ? [_0, Input] : []) extends [infer _0 extends [unknown, unknown, unknown] | [], infer Input extends string] ? [S.TExprTermTailMapping<_0>, Input] : [];
48
49
  export type TExprTerm<Input extends string> = (TFactor<Input> extends [infer _0, infer Input extends string] ? (TExprTermTail<Input> extends [infer _1, infer Input extends string] ? [[_0, _1], Input] : []) : []) extends [infer _0 extends [unknown, unknown], infer Input extends string] ? [S.TExprTermMapping<_0>, Input] : [];
49
50
  export type TExprTail<Input extends string> = ((Token.TConst<'|', Input> extends [infer _0, infer Input extends string] ? (TExprTerm<Input> extends [infer _1, infer Input extends string] ? (TExprTail<Input> extends [infer _2, infer Input extends string] ? [[_0, _1, _2], Input] : []) : []) : []) extends [infer _0, infer Input extends string] ? [_0, Input] : [[], Input] extends [infer _0, infer Input extends string] ? [_0, Input] : []) extends [infer _0 extends [unknown, unknown, unknown] | [], infer Input extends string] ? [S.TExprTailMapping<_0>, Input] : [];
@@ -172,6 +173,7 @@ export declare const IndexArray_0: (input: string, result?: unknown[]) => [unkno
172
173
  export declare const IndexArray: (input: string) => [unknown, string] | [];
173
174
  export declare const Extends: (input: string) => [unknown, string] | [];
174
175
  export declare const Base: (input: string) => [unknown, string] | [];
176
+ export declare const With: (input: string) => [unknown, string] | [];
175
177
  export declare const Factor: (input: string) => [unknown, string] | [];
176
178
  export declare const ExprTermTail: (input: string) => [unknown, string] | [];
177
179
  export declare const ExprTerm: (input: string) => [unknown, string] | [];
@@ -47,7 +47,8 @@ export const IndexArray_0 = (input, result = []) => If(If(If(Token.Const('[', in
47
47
  export const IndexArray = (input) => If(IndexArray_0(input), ([_0, input]) => [S.IndexArrayMapping(_0), input]);
48
48
  export const Extends = (input) => If(If(If(Token.Const('extends', input), ([_0, input]) => If(Type(input), ([_1, input]) => If(Token.Const('?', input), ([_2, input]) => If(Type(input), ([_3, input]) => If(Token.Const(':', input), ([_4, input]) => If(Type(input), ([_5, input]) => [[_0, _1, _2, _3, _4, _5], input])))))), ([_0, input]) => [_0, input], () => If([[], input], ([_0, input]) => [_0, input], () => [])), ([_0, input]) => [S.ExtendsMapping(_0), input]);
49
49
  export const Base = (input) => If(If(If(Token.Const('(', input), ([_0, input]) => If(Type(input), ([_1, input]) => If(Token.Const(')', input), ([_2, input]) => [[_0, _1, _2], input]))), ([_0, input]) => [_0, input], () => If(Keyword(input), ([_0, input]) => [_0, input], () => If(_Object_(input), ([_0, input]) => [_0, input], () => If(Tuple(input), ([_0, input]) => [_0, input], () => If(TemplateLiteral(input), ([_0, input]) => [_0, input], () => If(Literal(input), ([_0, input]) => [_0, input], () => If(Constructor(input), ([_0, input]) => [_0, input], () => If(_Function_(input), ([_0, input]) => [_0, input], () => If(Mapped(input), ([_0, input]) => [_0, input], () => If(Options(input), ([_0, input]) => [_0, input], () => If(GenericCall(input), ([_0, input]) => [_0, input], () => If(Reference(input), ([_0, input]) => [_0, input], () => [])))))))))))), ([_0, input]) => [S.BaseMapping(_0), input]);
50
- export const Factor = (input) => If(If(KeyOf(input), ([_0, input]) => If(Base(input), ([_1, input]) => If(IndexArray(input), ([_2, input]) => If(Extends(input), ([_3, input]) => [[_0, _1, _2, _3], input])))), ([_0, input]) => [S.FactorMapping(_0), input]);
50
+ export const With = (input) => If(If(If(Token.Const('with', input), ([_0, input]) => If(JsonObject(input), ([_1, input]) => [[_0, _1], input])), ([_0, input]) => [_0, input], () => If([[], input], ([_0, input]) => [_0, input], () => [])), ([_0, input]) => [S.WithMapping(_0), input]);
51
+ export const Factor = (input) => If(If(KeyOf(input), ([_0, input]) => If(Base(input), ([_1, input]) => If(IndexArray(input), ([_2, input]) => If(Extends(input), ([_3, input]) => If(With(input), ([_4, input]) => [[_0, _1, _2, _3, _4], input]))))), ([_0, input]) => [S.FactorMapping(_0), input]);
51
52
  export const ExprTermTail = (input) => If(If(If(Token.Const('&', input), ([_0, input]) => If(Factor(input), ([_1, input]) => If(ExprTermTail(input), ([_2, input]) => [[_0, _1, _2], input]))), ([_0, input]) => [_0, input], () => If([[], input], ([_0, input]) => [_0, input], () => [])), ([_0, input]) => [S.ExprTermTailMapping(_0), input]);
52
53
  export const ExprTerm = (input) => If(If(Factor(input), ([_0, input]) => If(ExprTermTail(input), ([_1, input]) => [[_0, _1], input])), ([_0, input]) => [S.ExprTermMapping(_0), input]);
53
54
  export const ExprTail = (input) => If(If(If(Token.Const('|', input), ([_0, input]) => If(ExprTerm(input), ([_1, input]) => If(ExprTail(input), ([_2, input]) => [[_0, _1, _2], input]))), ([_0, input]) => [_0, input], () => If([[], input], ([_0, input]) => [_0, input], () => [])), ([_0, input]) => [S.ExprTailMapping(_0), input]);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "typebox",
3
3
  "description": "Json Schema Type Builder with Static Type Resolution for TypeScript",
4
- "version": "1.1.38",
4
+ "version": "1.1.39",
5
5
  "keywords": [
6
6
  "typescript",
7
7
  "jsonschema"
@@ -16,22 +16,6 @@
16
16
  "types": "./build/index.d.mts",
17
17
  "module": "./build/index.mjs",
18
18
  "exports": {
19
- "./guard": {
20
- "import": "./build/guard/index.mjs",
21
- "default": "./build/guard/index.mjs"
22
- },
23
- "./error": {
24
- "import": "./build/error/index.mjs",
25
- "default": "./build/error/index.mjs"
26
- },
27
- "./compile": {
28
- "import": "./build/compile/index.mjs",
29
- "default": "./build/compile/index.mjs"
30
- },
31
- "./system": {
32
- "import": "./build/system/index.mjs",
33
- "default": "./build/system/index.mjs"
34
- },
35
19
  "./format": {
36
20
  "import": "./build/format/index.mjs",
37
21
  "default": "./build/format/index.mjs"
@@ -44,10 +28,26 @@
44
28
  "import": "./build/schema/index.mjs",
45
29
  "default": "./build/schema/index.mjs"
46
30
  },
31
+ "./compile": {
32
+ "import": "./build/compile/index.mjs",
33
+ "default": "./build/compile/index.mjs"
34
+ },
47
35
  "./value": {
48
36
  "import": "./build/value/index.mjs",
49
37
  "default": "./build/value/index.mjs"
50
38
  },
39
+ "./guard": {
40
+ "import": "./build/guard/index.mjs",
41
+ "default": "./build/guard/index.mjs"
42
+ },
43
+ "./system": {
44
+ "import": "./build/system/index.mjs",
45
+ "default": "./build/system/index.mjs"
46
+ },
47
+ "./error": {
48
+ "import": "./build/error/index.mjs",
49
+ "default": "./build/error/index.mjs"
50
+ },
51
51
  ".": {
52
52
  "import": "./build/index.mjs",
53
53
  "default": "./build/index.mjs"
@@ -55,18 +55,6 @@
55
55
  },
56
56
  "typesVersions": {
57
57
  "*": {
58
- "guard": [
59
- "./build/guard/index.d.mts"
60
- ],
61
- "error": [
62
- "./build/error/index.d.mts"
63
- ],
64
- "compile": [
65
- "./build/compile/index.d.mts"
66
- ],
67
- "system": [
68
- "./build/system/index.d.mts"
69
- ],
70
58
  "format": [
71
59
  "./build/format/index.d.mts"
72
60
  ],
@@ -76,9 +64,21 @@
76
64
  "schema": [
77
65
  "./build/schema/index.d.mts"
78
66
  ],
67
+ "compile": [
68
+ "./build/compile/index.d.mts"
69
+ ],
79
70
  "value": [
80
71
  "./build/value/index.d.mts"
81
72
  ],
73
+ "guard": [
74
+ "./build/guard/index.d.mts"
75
+ ],
76
+ "system": [
77
+ "./build/system/index.d.mts"
78
+ ],
79
+ "error": [
80
+ "./build/error/index.d.mts"
81
+ ],
82
82
  ".": [
83
83
  "./build/index.d.mts"
84
84
  ]
@@ -1,20 +1,20 @@
1
1
  {
2
2
  "name": "@openclaw/voice-call",
3
- "version": "2026.5.28",
3
+ "version": "2026.5.31-beta.1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@openclaw/voice-call",
9
- "version": "2026.5.28",
9
+ "version": "2026.5.31-beta.1",
10
10
  "dependencies": {
11
11
  "commander": "14.0.3",
12
- "typebox": "1.1.38",
12
+ "typebox": "1.1.39",
13
13
  "ws": "8.21.0",
14
14
  "zod": "4.4.3"
15
15
  },
16
16
  "peerDependencies": {
17
- "openclaw": ">=2026.5.28"
17
+ "openclaw": ">=2026.5.31-beta.1"
18
18
  },
19
19
  "peerDependenciesMeta": {
20
20
  "openclaw": {
@@ -32,9 +32,9 @@
32
32
  }
33
33
  },
34
34
  "node_modules/typebox": {
35
- "version": "1.1.38",
36
- "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.1.38.tgz",
37
- "integrity": "sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==",
35
+ "version": "1.1.39",
36
+ "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.1.39.tgz",
37
+ "integrity": "sha512-vj0afVtOfLQvv0GR0VxVagYxsXN64btL7Z9XoaG0ZggH3mruMMkOO6hXdgMsjCY3shZgEvooAWVeznQVs5c43w==",
38
38
  "license": "MIT"
39
39
  },
40
40
  "node_modules/ws": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/voice-call",
3
- "version": "2026.5.28",
3
+ "version": "2026.5.31-beta.1",
4
4
  "description": "OpenClaw voice-call plugin for Twilio, Telnyx, and Plivo phone calls.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -9,12 +9,12 @@
9
9
  "type": "module",
10
10
  "dependencies": {
11
11
  "commander": "14.0.3",
12
- "typebox": "1.1.38",
12
+ "typebox": "1.1.39",
13
13
  "ws": "8.21.0",
14
14
  "zod": "4.4.3"
15
15
  },
16
16
  "peerDependencies": {
17
- "openclaw": ">=2026.5.28"
17
+ "openclaw": ">=2026.5.31-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.5.28"
34
+ "pluginApi": ">=2026.5.31-beta.1"
35
35
  },
36
36
  "build": {
37
- "openclawVersion": "2026.5.28"
37
+ "openclawVersion": "2026.5.31-beta.1"
38
38
  },
39
39
  "release": {
40
40
  "publishToClawHub": true,