refactor: trim voice call helper exports

This commit is contained in:
Peter Steinberger
2026-05-01 17:42:21 +01:00
parent 53593f0683
commit 496bf38fcf
9 changed files with 43 additions and 46 deletions

View File

@@ -4,7 +4,7 @@ import { VoiceCallConfigSchema } from "./config.js";
export const VOICE_CALL_LEGACY_CONFIG_REMOVAL_VERSION = "2026.6.0";
export type VoiceCallLegacyConfigIssue = {
type VoiceCallLegacyConfigIssue = {
path: string;
replacement: string;
message: string;

View File

@@ -6,12 +6,10 @@ import {
type SecretInput,
} from "openclaw/plugin-sdk/secret-input";
import { z } from "openclaw/plugin-sdk/zod";
import { TtsAutoSchema, TtsConfigSchema, TtsModeSchema, TtsProviderSchema } from "../api.js";
import { TtsConfigSchema } from "../api.js";
import { deepMergeDefined } from "./deep-merge.js";
import { DEFAULT_VOICE_CALL_REALTIME_INSTRUCTIONS } from "./realtime-defaults.js";
export { DEFAULT_VOICE_CALL_REALTIME_INSTRUCTIONS } from "./realtime-defaults.js";
// -----------------------------------------------------------------------------
// Phone Number Validation
// -----------------------------------------------------------------------------
@@ -20,7 +18,7 @@ export { DEFAULT_VOICE_CALL_REALTIME_INSTRUCTIONS } from "./realtime-defaults.js
* E.164 phone number format: +[country code][number]
* Examples use 555 prefix (reserved for fictional numbers)
*/
export const E164Schema = z
const E164Schema = z
.string()
.regex(/^\+[1-9]\d{1,14}$/, "Expected E.164 format, e.g. +15550001234");
@@ -35,7 +33,7 @@ export const E164Schema = z
* - "pairing": Unknown callers can request pairing (future)
* - "open": Accept all inbound calls (dangerous!)
*/
export const InboundPolicySchema = z.enum(["disabled", "allowlist", "pairing", "open"]);
const InboundPolicySchema = z.enum(["disabled", "allowlist", "pairing", "open"]);
// -----------------------------------------------------------------------------
// Provider-Specific Configuration
@@ -43,7 +41,7 @@ export const InboundPolicySchema = z.enum(["disabled", "allowlist", "pairing", "
const SecretInputSchema = buildSecretInputSchema();
export const TelnyxConfigSchema = z
const TelnyxConfigSchema = z
.object({
/** Telnyx API v2 key */
apiKey: z.string().min(1).optional(),
@@ -55,7 +53,7 @@ export const TelnyxConfigSchema = z
.strict();
export type TelnyxConfig = z.infer<typeof TelnyxConfigSchema>;
export const TwilioConfigSchema = z
const TwilioConfigSchema = z
.object({
/** Twilio Account SID */
accountSid: z.string().min(1).optional(),
@@ -64,7 +62,7 @@ export const TwilioConfigSchema = z
})
.strict();
export const PlivoConfigSchema = z
const PlivoConfigSchema = z
.object({
/** Plivo Auth ID (starts with MA/SA) */
authId: z.string().min(1).optional(),
@@ -74,14 +72,13 @@ export const PlivoConfigSchema = z
.strict();
export type PlivoConfig = z.infer<typeof PlivoConfigSchema>;
export { TtsAutoSchema, TtsConfigSchema, TtsModeSchema, TtsProviderSchema };
export type VoiceCallTtsConfig = z.infer<typeof TtsConfigSchema>;
// -----------------------------------------------------------------------------
// Webhook Server Configuration
// -----------------------------------------------------------------------------
export const VoiceCallServeConfigSchema = z
const VoiceCallServeConfigSchema = z
.object({
/** Port to listen on */
port: z.number().int().positive().default(3334),
@@ -93,7 +90,7 @@ export const VoiceCallServeConfigSchema = z
.strict()
.default({ port: 3334, bind: "127.0.0.1", path: "/voice/webhook" });
export const VoiceCallTailscaleConfigSchema = z
const VoiceCallTailscaleConfigSchema = z
.object({
/**
* Tailscale exposure mode:
@@ -112,7 +109,7 @@ export const VoiceCallTailscaleConfigSchema = z
// Tunnel Configuration (unified ngrok/tailscale)
// -----------------------------------------------------------------------------
export const VoiceCallTunnelConfigSchema = z
const VoiceCallTunnelConfigSchema = z
.object({
/**
* Tunnel provider:
@@ -142,7 +139,7 @@ export const VoiceCallTunnelConfigSchema = z
// Webhook Security Configuration
// -----------------------------------------------------------------------------
export const VoiceCallWebhookSecurityConfigSchema = z
const VoiceCallWebhookSecurityConfigSchema = z
.object({
/**
* Allowed hostnames for webhook URL reconstruction.
@@ -173,10 +170,10 @@ export type WebhookSecurityConfig = z.infer<typeof VoiceCallWebhookSecurityConfi
* - "notify": Deliver message and auto-hangup after delay (one-way notification)
* - "conversation": Stay open for back-and-forth until explicit end or timeout
*/
export const CallModeSchema = z.enum(["notify", "conversation"]);
const CallModeSchema = z.enum(["notify", "conversation"]);
export type CallMode = z.infer<typeof CallModeSchema>;
export const OutboundConfigSchema = z
const OutboundConfigSchema = z
.object({
/** Default call mode for outbound calls */
defaultMode: CallModeSchema.default("notify"),
@@ -190,7 +187,7 @@ export const OutboundConfigSchema = z
// Realtime Voice Configuration
// -----------------------------------------------------------------------------
export const RealtimeToolSchema = z
const RealtimeToolSchema = z
.object({
type: z.literal("function"),
name: z.string().min(1),
@@ -202,17 +199,17 @@ export const RealtimeToolSchema = z
}),
})
.strict();
export type RealtimeToolConfig = z.infer<typeof RealtimeToolSchema>;
type RealtimeToolConfig = z.infer<typeof RealtimeToolSchema>;
export const VoiceCallRealtimeProvidersConfigSchema = z
const VoiceCallRealtimeProvidersConfigSchema = z
.record(z.string(), z.record(z.string(), z.unknown()))
.default({});
export const VoiceCallRealtimeToolPolicySchema = z.enum(REALTIME_VOICE_AGENT_CONSULT_TOOL_POLICIES);
const VoiceCallRealtimeToolPolicySchema = z.enum(REALTIME_VOICE_AGENT_CONSULT_TOOL_POLICIES);
export const VoiceCallRealtimeFastContextSourceSchema = z.enum(["memory", "sessions"]);
const VoiceCallRealtimeFastContextSourceSchema = z.enum(["memory", "sessions"]);
export const VoiceCallRealtimeFastContextConfigSchema = z
const VoiceCallRealtimeFastContextConfigSchema = z
.object({
/** Enable bounded memory/session lookup before the full consult agent. */
enabled: z.boolean().default(false),
@@ -240,11 +237,11 @@ export type VoiceCallRealtimeFastContextConfig = z.infer<
typeof VoiceCallRealtimeFastContextConfigSchema
>;
export const VoiceCallStreamingProvidersConfigSchema = z
const VoiceCallStreamingProvidersConfigSchema = z
.record(z.string(), z.record(z.string(), z.unknown()))
.default({});
export const VoiceCallRealtimeConfigSchema = z
const VoiceCallRealtimeConfigSchema = z
.object({
/** Enable realtime voice-to-voice mode. */
enabled: z.boolean().default(false),
@@ -284,7 +281,7 @@ export type VoiceCallRealtimeConfig = z.infer<typeof VoiceCallRealtimeConfigSche
// Streaming Configuration (Realtime Transcription)
// -----------------------------------------------------------------------------
export const VoiceCallStreamingConfigSchema = z
const VoiceCallStreamingConfigSchema = z
.object({
/** Enable real-time audio streaming (requires WebSocket support) */
enabled: z.boolean().default(false),

View File

@@ -35,13 +35,13 @@ type VoiceCallContinueOperation =
error: string;
};
export type VoiceCallContinueOperationStartPayload = {
type VoiceCallContinueOperationStartPayload = {
operationId: string;
status: "pending";
pollTimeoutMs: number;
};
export type VoiceCallContinueOperationResultPayload =
type VoiceCallContinueOperationResultPayload =
| {
operationId: string;
status: "pending";
@@ -58,7 +58,7 @@ export type VoiceCallContinueOperationResultPayload =
error: string;
};
export type VoiceCallContinueOperationRequest = {
type VoiceCallContinueOperationRequest = {
rt: VoiceCallRuntime;
callId: string;
message: string;

View File

@@ -1,6 +1,6 @@
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
export type HttpHeaderMap = Record<string, string | string[] | undefined>;
type HttpHeaderMap = Record<string, string | string[] | undefined>;
export function getHeader(headers: HttpHeaderMap, name: string): string | undefined {
const target = normalizeLowercaseStringOrEmpty(name);

View File

@@ -2,14 +2,14 @@ import type { VoiceCallConfig } from "../config.js";
import type { VoiceCallProvider } from "../providers/base.js";
import type { CallId, CallRecord } from "../types.js";
export type TranscriptWaiter = {
type TranscriptWaiter = {
resolve: (text: string) => void;
reject: (err: Error) => void;
timeout: NodeJS.Timeout;
turnToken?: string;
};
export type CallManagerRuntimeState = {
type CallManagerRuntimeState = {
activeCalls: Map<CallId, CallRecord>;
providerCallIdMap: Map<string, CallId>;
processedEventIds: Set<string>;
@@ -17,21 +17,21 @@ export type CallManagerRuntimeState = {
rejectedProviderCallIds: Set<string>;
};
export type CallManagerRuntimeDeps = {
type CallManagerRuntimeDeps = {
provider: VoiceCallProvider | null;
config: VoiceCallConfig;
storePath: string;
webhookUrl: string | null;
};
export type CallManagerTransientState = {
type CallManagerTransientState = {
activeTurnCalls: Set<CallId>;
transcriptWaiters: Map<CallId, TranscriptWaiter>;
maxDurationTimers: Map<CallId, NodeJS.Timeout>;
initialMessageInFlight: Set<CallId>;
};
export type CallManagerHooks = {
type CallManagerHooks = {
/** Optional runtime hook invoked after an event transitions a call into answered state. */
onCallAnswered?: (call: CallRecord) => void;
};

View File

@@ -1,7 +1,7 @@
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
import type { WebhookContext } from "../../types.js";
export type TwimlRequestView = {
type TwimlRequestView = {
callStatus: string | null;
direction: string | null;
isStatusCallback: boolean;
@@ -9,14 +9,14 @@ export type TwimlRequestView = {
callIdFromQuery?: string;
};
export type TwimlPolicyInput = TwimlRequestView & {
type TwimlPolicyInput = TwimlRequestView & {
hasStoredTwiml: boolean;
isNotifyCall: boolean;
hasActiveStreams: boolean;
canStream: boolean;
};
export type TwimlDecision =
type TwimlDecision =
| {
kind: "empty" | "pause" | "queue";
consumeStoredTwimlCallId?: string;

View File

@@ -25,7 +25,7 @@ type FastContextLookupResult =
| { status: "unavailable"; error?: string }
| { status: "hits"; hits: MemorySearchHit[] };
export type RealtimeFastContextConsultResult =
type RealtimeFastContextConsultResult =
| { handled: false }
| { handled: true; result: RealtimeVoiceAgentConsultResult };

View File

@@ -5,7 +5,7 @@ import type { CallMode } from "./config.js";
// Provider Identifiers
// -----------------------------------------------------------------------------
export const ProviderNameSchema = z.enum(["telnyx", "twilio", "plivo", "mock"]);
const ProviderNameSchema = z.enum(["telnyx", "twilio", "plivo", "mock"]);
export type ProviderName = z.infer<typeof ProviderNameSchema>;
// -----------------------------------------------------------------------------
@@ -16,13 +16,13 @@ export type ProviderName = z.infer<typeof ProviderNameSchema>;
export type CallId = string;
/** Provider-specific call identifier */
export type ProviderCallId = string;
type ProviderCallId = string;
// -----------------------------------------------------------------------------
// Call Lifecycle States
// -----------------------------------------------------------------------------
export const CallStateSchema = z.enum([
const CallStateSchema = z.enum([
// Non-terminal states
"initiated",
"ringing",
@@ -55,7 +55,7 @@ export const TerminalStates = new Set<CallState>([
"voicemail",
]);
export const EndReasonSchema = z.enum([
const EndReasonSchema = z.enum([
"completed",
"hangup-user",
"hangup-bot",
@@ -87,7 +87,7 @@ const BaseEventSchema = z.object({
to: z.string().optional(),
});
export const NormalizedEventSchema = z.discriminatedUnion("type", [
const NormalizedEventSchema = z.discriminatedUnion("type", [
BaseEventSchema.extend({
type: z.literal("call.initiated"),
}),
@@ -134,13 +134,13 @@ export type NormalizedEvent = z.infer<typeof NormalizedEventSchema>;
// Call Direction
// -----------------------------------------------------------------------------
export const CallDirectionSchema = z.enum(["outbound", "inbound"]);
const CallDirectionSchema = z.enum(["outbound", "inbound"]);
// -----------------------------------------------------------------------------
// Call Record
// -----------------------------------------------------------------------------
export const TranscriptEntrySchema = z.object({
const TranscriptEntrySchema = z.object({
timestamp: z.number(),
speaker: z.enum(["bot", "user"]),
text: z.string(),

View File

@@ -1,7 +1,7 @@
import { spawn } from "node:child_process";
import type { VoiceCallConfig } from "../config.js";
export type TailscaleSelfInfo = {
type TailscaleSelfInfo = {
dnsName: string | null;
nodeId: string | null;
};