@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.
- package/dist/{config-U-rgixyY.js → config-BKyRNKHF.js} +1 -1
- package/dist/{config-compat-CokN3Zzr.js → config-compat-BLdRz9GR.js} +3 -3
- package/dist/{guarded-json-api-hXuXY1dk.js → guarded-json-api-mCcsLRBb.js} +6 -4
- package/dist/index.js +82 -46
- package/dist/{plivo-CVgE_V_c.js → plivo-Dpf08ZVJ.js} +2 -2
- package/dist/{realtime-handler-DQDCDHv2.js → realtime-handler-Crs3kty2.js} +9 -6
- package/dist/{response-generator-C8EHsqMw.js → response-generator-Cu4sOiUO.js} +2 -2
- package/dist/{runtime-entry-DwBgs2Sq.js → runtime-entry-DXf7kdXt.js} +284 -43
- package/dist/runtime-entry.js +1 -1
- package/dist/setup-api.js +1 -1
- package/dist/{telnyx-C8sgJugJ.js → telnyx-CxEuPBOJ.js} +1 -1
- package/dist/{twilio-CpBg6Ir5.js → twilio-DzQmSUQP.js} +2 -2
- package/node_modules/typebox/build/type/script/mapping.d.mts +5 -2
- package/node_modules/typebox/build/type/script/mapping.mjs +15 -8
- package/node_modules/typebox/build/type/script/parser.d.mts +3 -1
- package/node_modules/typebox/build/type/script/parser.mjs +2 -1
- package/node_modules/typebox/package.json +29 -29
- package/npm-shrinkwrap.json +7 -7
- package/package.json +5 -5
|
@@ -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.
|
|
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-
|
|
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.
|
|
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.
|
|
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-
|
|
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
|
|
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
|
|
45
|
-
|
|
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-
|
|
4
|
-
import { c as getTailscaleSelfInfo, l as setupTailscaleExposureRoute, o as resolveWebhookExposureStatus, p as resolveUserPath, s as cleanupTailscaleExposureRoute, t as createVoiceCallRuntime } from "./runtime-entry-
|
|
5
|
-
import { i as parseVoiceCallPluginConfig, r as normalizeVoiceCallLegacyConfigInput, t as formatVoiceCallLegacyConfigWarnings } from "./config-compat-
|
|
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
|
|
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
|
|
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 =
|
|
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,
|
|
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
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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
|
-
}
|
|
436
|
-
await
|
|
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 (
|
|
443
|
-
|
|
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
|
|
447
|
-
const
|
|
448
|
-
const
|
|
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
|
-
}
|
|
485
|
+
}
|
|
453
486
|
writeStdoutJson({
|
|
454
|
-
recordsScanned:
|
|
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
|
-
|
|
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-
|
|
2
|
-
import { n as reconstructWebhookUrl, r as verifyPlivoWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-
|
|
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 (
|
|
103
|
-
const normalized = (MULAW_LINEAR_SAMPLES[
|
|
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
|
-
|
|
582
|
-
|
|
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 (
|
|
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 (
|
|
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-
|
|
2
|
-
import { f as resolveVoiceResponseModel } from "./runtime-entry-
|
|
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-
|
|
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:
|
|
187
|
-
content: `${JSON.stringify(
|
|
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
|
|
198
|
-
|
|
199
|
-
|
|
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
|
|
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
|
-
|
|
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))
|
|
238
|
-
const parsed =
|
|
239
|
-
calls.push(parsed);
|
|
240
|
-
}
|
|
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
|
|
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
|
-
},
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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-
|
|
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) =>
|
|
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(
|
|
3098
|
-
|
|
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
|
|
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-
|
|
3511
|
+
telnyxProviderPromise ??= import("./telnyx-CxEuPBOJ.js");
|
|
3272
3512
|
return telnyxProviderPromise;
|
|
3273
3513
|
}
|
|
3274
3514
|
function loadTwilioProvider() {
|
|
3275
|
-
twilioProviderPromise ??= import("./twilio-
|
|
3515
|
+
twilioProviderPromise ??= import("./twilio-DzQmSUQP.js");
|
|
3276
3516
|
return twilioProviderPromise;
|
|
3277
3517
|
}
|
|
3278
3518
|
function loadPlivoProvider() {
|
|
3279
|
-
plivoProviderPromise ??= import("./plivo-
|
|
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-
|
|
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 };
|
package/dist/runtime-entry.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as createVoiceCallRuntime } from "./runtime-entry-
|
|
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-
|
|
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-
|
|
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-
|
|
4
|
-
import { a as verifyTwilioWebhook, t as guardedJsonApiRequest } from "./guarded-json-api-
|
|
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
|
-
|
|
170
|
-
export
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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
|
]
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/voice-call",
|
|
3
|
-
"version": "2026.5.
|
|
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.
|
|
9
|
+
"version": "2026.5.31-beta.1",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"commander": "14.0.3",
|
|
12
|
-
"typebox": "1.1.
|
|
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.
|
|
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.
|
|
36
|
-
"resolved": "https://registry.npmjs.org/typebox/-/typebox-1.1.
|
|
37
|
-
"integrity": "sha512-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
34
|
+
"pluginApi": ">=2026.5.31-beta.1"
|
|
35
35
|
},
|
|
36
36
|
"build": {
|
|
37
|
-
"openclawVersion": "2026.5.
|
|
37
|
+
"openclawVersion": "2026.5.31-beta.1"
|
|
38
38
|
},
|
|
39
39
|
"release": {
|
|
40
40
|
"publishToClawHub": true,
|