import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime"; import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime"; import {
consultRealtimeVoiceAgent,
REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME,
resolveRealtimeVoiceAgentConsultTools,
resolveRealtimeVoiceAgentConsultToolsAllow,
type RealtimeVoiceAgentConsultTranscriptEntry,
type ResolvedRealtimeVoiceProvider,
} from "openclaw/plugin-sdk/realtime-voice"; import type { VoiceCallConfig } from "./config.js"; import { resolveVoiceCallConfig, validateProviderConfig } from "./config.js"; import type { CoreAgentDeps, CoreConfig } from "./core-bridge.js"; import { CallManager } from "./manager.js"; import type { VoiceCallProvider } from "./providers/base.js"; import type { TwilioProvider } from "./providers/twilio.js"; import { resolveVoiceResponseModel } from "./response-model.js"; import type { TelephonyTtsRuntime } from "./telephony-tts.js"; import { createTelephonyTtsProvider } from "./telephony-tts.js"; import { startTunnel, type TunnelResult } from "./tunnel.js"; import { VoiceCallWebhookServer } from "./webhook.js"; import { cleanupTailscaleExposure, setupTailscaleExposure } from "./webhook/tailscale.js";
type ResolvedRealtimeProvider = ResolvedRealtimeVoiceProvider;
type TelnyxProviderModule = typeofimport("./providers/telnyx.js");
type TwilioProviderModule = typeofimport("./providers/twilio.js");
type PlivoProviderModule = typeofimport("./providers/plivo.js");
type MockProviderModule = typeofimport("./providers/mock.js");
type RealtimeVoiceRuntimeModule = typeofimport("./realtime-voice.runtime.js");
type RealtimeHandlerModule = typeofimport("./webhook/realtime-handler.js");
let telnyxProviderPromise: Promise<TelnyxProviderModule> | undefined;
let twilioProviderPromise: Promise<TwilioProviderModule> | undefined;
let plivoProviderPromise: Promise<PlivoProviderModule> | undefined;
let mockProviderPromise: Promise<MockProviderModule> | undefined;
let realtimeVoiceRuntimePromise: Promise<RealtimeVoiceRuntimeModule> | undefined;
let realtimeHandlerPromise: Promise<RealtimeHandlerModule> | undefined;
function loadTelnyxProvider(): Promise<TelnyxProviderModule> {
telnyxProviderPromise ??= import("./providers/telnyx.js"); return telnyxProviderPromise;
}
function loadTwilioProvider(): Promise<TwilioProviderModule> {
twilioProviderPromise ??= import("./providers/twilio.js"); return twilioProviderPromise;
}
function loadPlivoProvider(): Promise<PlivoProviderModule> {
plivoProviderPromise ??= import("./providers/plivo.js"); return plivoProviderPromise;
}
function loadMockProvider(): Promise<MockProviderModule> {
mockProviderPromise ??= import("./providers/mock.js"); return mockProviderPromise;
}
function loadRealtimeVoiceRuntime(): Promise<RealtimeVoiceRuntimeModule> {
realtimeVoiceRuntimePromise ??= import("./realtime-voice.runtime.js"); return realtimeVoiceRuntimePromise;
}
function loadRealtimeHandler(): Promise<RealtimeHandlerModule> {
realtimeHandlerPromise ??= import("./webhook/realtime-handler.js"); return realtimeHandlerPromise;
}
if (!config.enabled) { thrownew Error("Voice call disabled. Enable the plugin entry in config.");
}
if (config.skipSignatureVerification) {
log.warn( "[voice-call] SECURITY WARNING: skipSignatureVerification=true disables webhook signature verification (development only). Do not use in production.",
);
}
// Wrap remaining initialization in try/catch so the webhook server is // properly stopped if any subsequent step fails. Without this, the server // keeps the port bound while the runtime promise rejects, causing // EADDRINUSE on the next attempt. See: #32387 try { // Determine public URL - priority: config.publicUrl > tunnel > legacy tailscale
let publicUrl: string | null = config.publicUrl ?? null;
const mediaHandler = webhookServer.getMediaStreamHandler(); if (mediaHandler) {
twilioProvider.setMediaStreamHandler(mediaHandler);
log.info("[voice-call] Media stream handler wired to provider");
}
}
if (realtimeProvider) {
log.info(`[voice-call] Realtime voice provider: ${realtimeProvider.provider.id}`);
}
await manager.initialize(provider, webhookUrl);
const stop = async () => await lifecycle.stop();
log.info("[voice-call] Runtime initialized");
log.info(`[voice-call] Webhook URL: ${webhookUrl}`); if (publicUrl) {
log.info(`[voice-call] Public URL: ${publicUrl}`);
}
return {
config,
provider,
manager,
webhookServer,
webhookUrl,
publicUrl,
stop,
};
} catch (err) { // If any step after the server started fails, clean up every provisioned // resource (tunnel, tailscale exposure, and webhook server) so retries // don't leak processes or keep the port bound.
await lifecycle.stop({ suppressErrors: true }); throw err;
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-06-05)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.