@kodelyth/voice-call 2026.5.39 → 2026.5.42

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.
Files changed (137) hide show
  1. package/README.md +167 -0
  2. package/api.ts +16 -0
  3. package/cli-metadata.ts +10 -0
  4. package/config-api.ts +12 -0
  5. package/dist/api.js +2 -0
  6. package/dist/cli-metadata.js +12 -0
  7. package/dist/config-DAwbG2aw.js +621 -0
  8. package/dist/config-compat-BYfJ5ueI.js +129 -0
  9. package/dist/guarded-json-api-xAIbFPZh.js +591 -0
  10. package/dist/index.js +1341 -0
  11. package/dist/mock-jtSdKDQN.js +135 -0
  12. package/dist/plivo-L-JTeuEc.js +392 -0
  13. package/dist/realtime-handler-5pSItXxX.js +1227 -0
  14. package/dist/realtime-transcription.runtime-CAbQKwCN.js +2 -0
  15. package/dist/realtime-voice.runtime-vCpCAutg.js +2 -0
  16. package/dist/response-generator-B-MjbtsM.js +199 -0
  17. package/dist/runtime-api.js +6 -0
  18. package/dist/runtime-entry-ohPMJR46.js +3435 -0
  19. package/dist/runtime-entry.js +2 -0
  20. package/dist/setup-api.js +37 -0
  21. package/dist/telnyx-BWr9EZ4x.js +278 -0
  22. package/dist/twilio-D9B0zY1k.js +679 -0
  23. package/index.test.ts +1075 -0
  24. package/index.ts +863 -0
  25. package/klaw.plugin.json +30 -133
  26. package/package.json +3 -3
  27. package/runtime-api.ts +20 -0
  28. package/runtime-entry.ts +1 -0
  29. package/setup-api.ts +47 -0
  30. package/src/allowlist.test.ts +18 -0
  31. package/src/allowlist.ts +19 -0
  32. package/src/cli.test.ts +12 -0
  33. package/src/cli.ts +866 -0
  34. package/src/config-compat.test.ts +130 -0
  35. package/src/config-compat.ts +227 -0
  36. package/src/config.test.ts +542 -0
  37. package/src/config.ts +883 -0
  38. package/src/core-bridge.ts +14 -0
  39. package/src/deep-merge.test.ts +40 -0
  40. package/src/deep-merge.ts +23 -0
  41. package/src/gateway-continue-operation.ts +200 -0
  42. package/src/http-headers.test.ts +16 -0
  43. package/src/http-headers.ts +15 -0
  44. package/src/manager/context.ts +50 -0
  45. package/src/manager/events.test.ts +578 -0
  46. package/src/manager/events.ts +332 -0
  47. package/src/manager/lifecycle.ts +53 -0
  48. package/src/manager/lookup.test.ts +52 -0
  49. package/src/manager/lookup.ts +35 -0
  50. package/src/manager/outbound.test.ts +629 -0
  51. package/src/manager/outbound.ts +508 -0
  52. package/src/manager/state.ts +48 -0
  53. package/src/manager/store.ts +107 -0
  54. package/src/manager/timers.test.ts +127 -0
  55. package/src/manager/timers.ts +113 -0
  56. package/src/manager/twiml.test.ts +13 -0
  57. package/src/manager/twiml.ts +17 -0
  58. package/src/manager.closed-loop.test.ts +259 -0
  59. package/src/manager.inbound-allowlist.test.ts +183 -0
  60. package/src/manager.notify.test.ts +390 -0
  61. package/src/manager.restore.test.ts +310 -0
  62. package/src/manager.test-harness.ts +127 -0
  63. package/src/manager.ts +441 -0
  64. package/src/media-stream.test.ts +953 -0
  65. package/src/media-stream.ts +876 -0
  66. package/src/providers/base.ts +99 -0
  67. package/src/providers/mock.test.ts +86 -0
  68. package/src/providers/mock.ts +185 -0
  69. package/src/providers/plivo.test.ts +93 -0
  70. package/src/providers/plivo.ts +601 -0
  71. package/src/providers/shared/call-status.test.ts +24 -0
  72. package/src/providers/shared/call-status.ts +24 -0
  73. package/src/providers/shared/guarded-json-api.test.ts +127 -0
  74. package/src/providers/shared/guarded-json-api.ts +49 -0
  75. package/src/providers/telnyx.test.ts +489 -0
  76. package/src/providers/telnyx.ts +419 -0
  77. package/src/providers/twilio/api.test.ts +184 -0
  78. package/src/providers/twilio/api.ts +100 -0
  79. package/src/providers/twilio/twiml-policy.test.ts +84 -0
  80. package/src/providers/twilio/twiml-policy.ts +87 -0
  81. package/src/providers/twilio/webhook.ts +34 -0
  82. package/src/providers/twilio.test.ts +607 -0
  83. package/src/providers/twilio.ts +861 -0
  84. package/src/providers/twilio.types.ts +17 -0
  85. package/src/realtime-agent-context.test.ts +101 -0
  86. package/src/realtime-agent-context.ts +149 -0
  87. package/src/realtime-defaults.ts +3 -0
  88. package/src/realtime-fast-context.test.ts +74 -0
  89. package/src/realtime-fast-context.ts +27 -0
  90. package/src/realtime-transcription.runtime.ts +4 -0
  91. package/src/realtime-voice.runtime.ts +5 -0
  92. package/src/response-generator.test.ts +385 -0
  93. package/src/response-generator.ts +348 -0
  94. package/src/response-model.test.ts +71 -0
  95. package/src/response-model.ts +23 -0
  96. package/src/runtime.test.ts +625 -0
  97. package/src/runtime.ts +528 -0
  98. package/src/telephony-audio.test.ts +61 -0
  99. package/src/telephony-audio.ts +12 -0
  100. package/src/telephony-tts.test.ts +196 -0
  101. package/src/telephony-tts.ts +235 -0
  102. package/src/test-fixtures.ts +82 -0
  103. package/src/tts-provider-voice.test.ts +34 -0
  104. package/src/tts-provider-voice.ts +21 -0
  105. package/src/tunnel.test.ts +173 -0
  106. package/src/tunnel.ts +314 -0
  107. package/src/types.ts +311 -0
  108. package/src/utils.test.ts +17 -0
  109. package/src/utils.ts +14 -0
  110. package/src/voice-mapping.test.ts +32 -0
  111. package/src/voice-mapping.ts +65 -0
  112. package/src/webhook/realtime-audio-pacer.test.ts +146 -0
  113. package/src/webhook/realtime-audio-pacer.ts +204 -0
  114. package/src/webhook/realtime-handler.test.ts +1450 -0
  115. package/src/webhook/realtime-handler.ts +1382 -0
  116. package/src/webhook/stale-call-reaper.test.ts +89 -0
  117. package/src/webhook/stale-call-reaper.ts +38 -0
  118. package/src/webhook/stream-frame-adapter.test.ts +187 -0
  119. package/src/webhook/stream-frame-adapter.ts +219 -0
  120. package/src/webhook/tailscale.test.ts +216 -0
  121. package/src/webhook/tailscale.ts +129 -0
  122. package/src/webhook-exposure.test.ts +33 -0
  123. package/src/webhook-exposure.ts +84 -0
  124. package/src/webhook-security.test.ts +813 -0
  125. package/src/webhook-security.ts +982 -0
  126. package/src/webhook.hangup-once.lifecycle.test.ts +179 -0
  127. package/src/webhook.test.ts +1615 -0
  128. package/src/webhook.ts +933 -0
  129. package/src/webhook.types.ts +5 -0
  130. package/src/websocket-test-support.ts +72 -0
  131. package/tsconfig.json +16 -0
  132. package/api.js +0 -7
  133. package/cli-metadata.js +0 -7
  134. package/index.js +0 -7
  135. package/runtime-api.js +0 -7
  136. package/runtime-entry.js +0 -7
  137. package/setup-api.js +0 -7
@@ -0,0 +1,17 @@
1
+ import type { WebhookSecurityConfig } from "../config.js";
2
+
3
+ /**
4
+ * Twilio Voice API provider options.
5
+ */
6
+ export interface TwilioProviderOptions {
7
+ /** Allow ngrok free tier compatibility mode (loopback only, less secure) */
8
+ allowNgrokFreeTierLoopbackBypass?: boolean;
9
+ /** Override public URL for signature verification */
10
+ publicUrl?: string;
11
+ /** Path for media stream WebSocket (e.g., /voice/stream) */
12
+ streamPath?: string;
13
+ /** Skip webhook signature verification (development only) */
14
+ skipVerification?: boolean;
15
+ /** Webhook security options (forwarded headers/allowlist) */
16
+ webhookSecurity?: WebhookSecurityConfig;
17
+ }
@@ -0,0 +1,101 @@
1
+ import { mkdtemp, rm, writeFile } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import path from "node:path";
4
+ import { afterEach, describe, expect, it, vi } from "vitest";
5
+ import type { VoiceCallConfig } from "./config.js";
6
+ import type { CoreAgentDeps, CoreConfig } from "./core-bridge.js";
7
+ import { buildRealtimeVoiceInstructions } from "./realtime-agent-context.js";
8
+ import { createVoiceCallBaseConfig } from "./test-fixtures.js";
9
+
10
+ const tempDirs: string[] = [];
11
+
12
+ afterEach(async () => {
13
+ await Promise.all(tempDirs.splice(0).map((dir) => rm(dir, { recursive: true, force: true })));
14
+ });
15
+
16
+ async function createWorkspace(): Promise<string> {
17
+ const workspaceDir = await mkdtemp(path.join(tmpdir(), "klaw-voice-context-"));
18
+ tempDirs.push(workspaceDir);
19
+ return workspaceDir;
20
+ }
21
+
22
+ function createConfig(overrides?: Partial<VoiceCallConfig["realtime"]>): VoiceCallConfig {
23
+ const config = createVoiceCallBaseConfig();
24
+ config.agentId = "voice";
25
+ config.realtime.enabled = true;
26
+ config.realtime.instructions = "Base voice instructions.";
27
+ config.realtime = {
28
+ ...config.realtime,
29
+ ...overrides,
30
+ fastContext: {
31
+ ...config.realtime.fastContext,
32
+ ...overrides?.fastContext,
33
+ sources: overrides?.fastContext?.sources ?? config.realtime.fastContext.sources,
34
+ },
35
+ agentContext: {
36
+ ...config.realtime.agentContext,
37
+ ...overrides?.agentContext,
38
+ files: overrides?.agentContext?.files ?? config.realtime.agentContext.files,
39
+ },
40
+ tools: overrides?.tools ?? config.realtime.tools,
41
+ providers: overrides?.providers ?? config.realtime.providers,
42
+ };
43
+ return config;
44
+ }
45
+
46
+ function createAgentRuntime(workspaceDir: string): CoreAgentDeps {
47
+ return {
48
+ resolveAgentIdentity: vi.fn(() => ({
49
+ name: "Claw Voice",
50
+ emoji: ":claw:",
51
+ theme: "bright",
52
+ vibe: "snappy",
53
+ creature: "operator",
54
+ })),
55
+ resolveAgentWorkspaceDir: vi.fn(() => workspaceDir),
56
+ } as unknown as CoreAgentDeps;
57
+ }
58
+
59
+ describe("buildRealtimeVoiceInstructions", () => {
60
+ it("injects bounded identity, system prompt, and workspace context", async () => {
61
+ const workspaceDir = await createWorkspace();
62
+ await writeFile(path.join(workspaceDir, "SOUL.md"), "Stay quick, direct, and warm.\n");
63
+ await writeFile(path.join(workspaceDir, "IDENTITY.md"), "Name: Claw Voice\nVibe: snappy\n");
64
+ await writeFile(path.join(workspaceDir, "SECRET.md"), "do not include\n");
65
+
66
+ const coreConfig = {
67
+ agents: {
68
+ list: [{ id: "voice", systemPromptOverride: "Keep spoken answers short." }],
69
+ },
70
+ } as CoreConfig;
71
+
72
+ const instructions = await buildRealtimeVoiceInstructions({
73
+ baseInstructions: "Base voice instructions.",
74
+ config: createConfig({
75
+ consultPolicy: "substantive",
76
+ agentContext: {
77
+ enabled: true,
78
+ maxChars: 2000,
79
+ includeIdentity: true,
80
+ includeSystemPrompt: true,
81
+ includeWorkspaceFiles: true,
82
+ files: ["SOUL.md", "IDENTITY.md", "../SECRET.md"],
83
+ },
84
+ }),
85
+ coreConfig,
86
+ agentRuntime: createAgentRuntime(workspaceDir),
87
+ });
88
+
89
+ expect(instructions).toContain("Klaw agent voice context:");
90
+ expect(instructions).toContain("Consult behavior:");
91
+ expect(instructions).toContain("Call klaw_agent_consult before answering requests");
92
+ expect(instructions).toContain("- Agent id: voice");
93
+ expect(instructions).toContain("- Name: Claw Voice");
94
+ expect(instructions).toContain("- Vibe: snappy");
95
+ expect(instructions).toContain("Keep spoken answers short.");
96
+ expect(instructions).toContain("### SOUL.md");
97
+ expect(instructions).toContain("Stay quick, direct, and warm.");
98
+ expect(instructions).toContain("### IDENTITY.md");
99
+ expect(instructions).not.toContain("do not include");
100
+ });
101
+ });
@@ -0,0 +1,149 @@
1
+ import type { KlawConfig } from "klaw/plugin-sdk/config-contracts";
2
+ import { buildRealtimeVoiceAgentConsultPolicyInstructions } from "klaw/plugin-sdk/realtime-voice";
3
+ import { root } from "klaw/plugin-sdk/security-runtime";
4
+ import type { VoiceCallConfig } from "./config.js";
5
+ import type { CoreAgentDeps, CoreConfig } from "./core-bridge.js";
6
+
7
+ type AgentEntryLike = {
8
+ id?: unknown;
9
+ systemPromptOverride?: unknown;
10
+ };
11
+
12
+ type VoiceIdentityLike = {
13
+ name?: unknown;
14
+ emoji?: unknown;
15
+ theme?: unknown;
16
+ creature?: unknown;
17
+ vibe?: unknown;
18
+ };
19
+
20
+ function normalizeString(value: unknown): string | undefined {
21
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
22
+ }
23
+
24
+ function readAgentEntries(cfg: CoreConfig): AgentEntryLike[] {
25
+ const agents = (cfg as { agents?: { list?: unknown } }).agents;
26
+ return Array.isArray(agents?.list)
27
+ ? agents.list.filter((entry): entry is AgentEntryLike =>
28
+ Boolean(entry && typeof entry === "object"),
29
+ )
30
+ : [];
31
+ }
32
+
33
+ function resolveAgentSystemPromptOverride(cfg: CoreConfig, agentId: string): string | undefined {
34
+ const entries = readAgentEntries(cfg);
35
+ const entry = entries.find((candidate) => normalizeString(candidate.id) === agentId);
36
+ return (
37
+ normalizeString(entry?.systemPromptOverride) ??
38
+ normalizeString(
39
+ (cfg as { agents?: { defaults?: { systemPromptOverride?: unknown } } }).agents?.defaults
40
+ ?.systemPromptOverride,
41
+ )
42
+ );
43
+ }
44
+
45
+ function limitText(text: string, maxChars: number): string {
46
+ if (text.length <= maxChars) {
47
+ return text;
48
+ }
49
+ return `${text.slice(0, Math.max(0, maxChars - 32)).trimEnd()}\n[truncated]`;
50
+ }
51
+
52
+ async function readWorkspaceVoiceContextFiles(params: {
53
+ workspaceDir: string;
54
+ files: readonly string[];
55
+ maxChars: number;
56
+ }): Promise<string[]> {
57
+ const sections: string[] = [];
58
+ let remaining = params.maxChars;
59
+ const workspaceRoot = await root(params.workspaceDir).catch(() => null);
60
+ if (!workspaceRoot) {
61
+ return sections;
62
+ }
63
+ for (const file of params.files) {
64
+ if (remaining <= 0) {
65
+ continue;
66
+ }
67
+ const content = await workspaceRoot.readText(file).catch(() => undefined);
68
+ const trimmed = content?.trim();
69
+ if (!trimmed) {
70
+ continue;
71
+ }
72
+ const body = limitText(trimmed, Math.max(0, remaining - file.length - 16));
73
+ const section = `### ${file}\n${body}`;
74
+ sections.push(section);
75
+ remaining -= section.length;
76
+ }
77
+ return sections;
78
+ }
79
+
80
+ export async function buildRealtimeVoiceInstructions(params: {
81
+ baseInstructions: string;
82
+ config: VoiceCallConfig;
83
+ coreConfig: CoreConfig;
84
+ agentRuntime: CoreAgentDeps;
85
+ }): Promise<string> {
86
+ const { config } = params;
87
+ const sections: string[] = [params.baseInstructions];
88
+ const consultGuidance = buildRealtimeVoiceAgentConsultPolicyInstructions(config.realtime);
89
+ if (consultGuidance) {
90
+ sections.push(consultGuidance);
91
+ }
92
+
93
+ const contextConfig = config.realtime.agentContext;
94
+ if (!contextConfig.enabled) {
95
+ return sections.filter(Boolean).join("\n\n");
96
+ }
97
+
98
+ const agentId = config.agentId ?? "main";
99
+ const capsule: string[] = [
100
+ "Klaw agent voice context:",
101
+ `- Agent id: ${agentId}`,
102
+ "- Use this context to match the Klaw agent's personality and standing preferences on fast voice turns.",
103
+ "- Treat this as compact context only; call klaw_agent_consult when the caller needs the full agent brain, tools, memory, or workspace state.",
104
+ ];
105
+
106
+ if (contextConfig.includeIdentity) {
107
+ const identity = params.agentRuntime.resolveAgentIdentity(
108
+ params.coreConfig as KlawConfig,
109
+ agentId,
110
+ ) as VoiceIdentityLike | undefined;
111
+ const identityLines = [
112
+ normalizeString(identity?.name) ? `- Name: ${normalizeString(identity?.name)}` : undefined,
113
+ normalizeString(identity?.emoji) ? `- Emoji: ${normalizeString(identity?.emoji)}` : undefined,
114
+ normalizeString(identity?.vibe) ? `- Vibe: ${normalizeString(identity?.vibe)}` : undefined,
115
+ normalizeString(identity?.theme) ? `- Theme: ${normalizeString(identity?.theme)}` : undefined,
116
+ normalizeString(identity?.creature)
117
+ ? `- Creature/persona: ${normalizeString(identity?.creature)}`
118
+ : undefined,
119
+ ].filter(Boolean);
120
+ if (identityLines.length > 0) {
121
+ capsule.push(`Configured identity:\n${identityLines.join("\n")}`);
122
+ }
123
+ }
124
+
125
+ if (contextConfig.includeSystemPrompt) {
126
+ const systemPrompt = resolveAgentSystemPromptOverride(params.coreConfig, agentId);
127
+ if (systemPrompt) {
128
+ capsule.push(`Configured system prompt override:\n${systemPrompt}`);
129
+ }
130
+ }
131
+
132
+ if (contextConfig.includeWorkspaceFiles) {
133
+ const workspaceDir = params.agentRuntime.resolveAgentWorkspaceDir(
134
+ params.coreConfig as KlawConfig,
135
+ agentId,
136
+ );
137
+ const fileSections = await readWorkspaceVoiceContextFiles({
138
+ workspaceDir,
139
+ files: contextConfig.files,
140
+ maxChars: contextConfig.maxChars,
141
+ });
142
+ if (fileSections.length > 0) {
143
+ capsule.push(`Workspace voice context:\n${fileSections.join("\n\n")}`);
144
+ }
145
+ }
146
+
147
+ sections.push(limitText(capsule.join("\n\n"), contextConfig.maxChars));
148
+ return sections.filter(Boolean).join("\n\n");
149
+ }
@@ -0,0 +1,3 @@
1
+ import { REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME } from "klaw/plugin-sdk/realtime-voice";
2
+
3
+ export const DEFAULT_VOICE_CALL_REALTIME_INSTRUCTIONS = `You are Klaw's phone-call realtime voice interface. Keep spoken replies brief and natural. When a question needs deeper reasoning, current information, or tools, call ${REALTIME_VOICE_AGENT_CONSULT_TOOL_NAME} before answering.`;
@@ -0,0 +1,74 @@
1
+ import type { KlawConfig } from "klaw/plugin-sdk/config-contracts";
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
3
+ import type { VoiceCallRealtimeFastContextConfig } from "./config.js";
4
+
5
+ const mocks = vi.hoisted(() => ({
6
+ resolveRealtimeVoiceFastContextConsult: vi.fn(),
7
+ }));
8
+
9
+ vi.mock("klaw/plugin-sdk/realtime-voice", () => ({
10
+ resolveRealtimeVoiceFastContextConsult: mocks.resolveRealtimeVoiceFastContextConsult,
11
+ }));
12
+
13
+ import { resolveRealtimeFastContextConsult } from "./realtime-fast-context.js";
14
+
15
+ const cfg = {} as KlawConfig;
16
+
17
+ function createFastContextConfig(
18
+ overrides: Partial<VoiceCallRealtimeFastContextConfig> = {},
19
+ ): VoiceCallRealtimeFastContextConfig {
20
+ return {
21
+ enabled: true,
22
+ timeoutMs: 800,
23
+ maxResults: 3,
24
+ sources: ["memory", "sessions"],
25
+ fallbackToConsult: false,
26
+ ...overrides,
27
+ };
28
+ }
29
+
30
+ function createLogger() {
31
+ return {
32
+ debug: vi.fn(),
33
+ warn: vi.fn(),
34
+ };
35
+ }
36
+
37
+ describe("resolveRealtimeFastContextConsult", () => {
38
+ beforeEach(() => {
39
+ mocks.resolveRealtimeVoiceFastContextConsult.mockReset();
40
+ });
41
+
42
+ afterEach(() => {
43
+ vi.useRealTimers();
44
+ });
45
+
46
+ it("passes voice-call labels into the SDK fast context resolver", async () => {
47
+ const logger = createLogger();
48
+ mocks.resolveRealtimeVoiceFastContextConsult.mockResolvedValue({ handled: false });
49
+
50
+ await expect(
51
+ resolveRealtimeFastContextConsult({
52
+ cfg,
53
+ agentId: "main",
54
+ sessionKey: "voice:15550001234",
55
+ config: createFastContextConfig({ fallbackToConsult: true }),
56
+ args: { question: "What do you remember?" },
57
+ logger,
58
+ }),
59
+ ).resolves.toEqual({ handled: false });
60
+
61
+ expect(mocks.resolveRealtimeVoiceFastContextConsult).toHaveBeenCalledWith({
62
+ cfg,
63
+ agentId: "main",
64
+ sessionKey: "voice:15550001234",
65
+ config: createFastContextConfig({ fallbackToConsult: true }),
66
+ args: { question: "What do you remember?" },
67
+ logger,
68
+ labels: {
69
+ audienceLabel: "caller",
70
+ contextName: "Klaw memory or session context",
71
+ },
72
+ });
73
+ });
74
+ });
@@ -0,0 +1,27 @@
1
+ import type { KlawConfig } from "klaw/plugin-sdk/config-contracts";
2
+ import {
3
+ resolveRealtimeVoiceFastContextConsult,
4
+ type RealtimeVoiceFastContextConsultResult,
5
+ type RealtimeVoiceFastContextConfig,
6
+ } from "klaw/plugin-sdk/realtime-voice";
7
+
8
+ type Logger = {
9
+ debug?: (message: string) => void;
10
+ };
11
+
12
+ export async function resolveRealtimeFastContextConsult(params: {
13
+ cfg: KlawConfig;
14
+ agentId: string;
15
+ sessionKey: string;
16
+ config: RealtimeVoiceFastContextConfig;
17
+ args: unknown;
18
+ logger: Logger;
19
+ }): Promise<RealtimeVoiceFastContextConsultResult> {
20
+ return await resolveRealtimeVoiceFastContextConsult({
21
+ ...params,
22
+ labels: {
23
+ audienceLabel: "caller",
24
+ contextName: "Klaw memory or session context",
25
+ },
26
+ });
27
+ }
@@ -0,0 +1,4 @@
1
+ export {
2
+ getRealtimeTranscriptionProvider,
3
+ listRealtimeTranscriptionProviders,
4
+ } from "klaw/plugin-sdk/realtime-transcription";
@@ -0,0 +1,5 @@
1
+ export {
2
+ getRealtimeVoiceProvider,
3
+ listRealtimeVoiceProviders,
4
+ resolveConfiguredRealtimeVoiceProvider,
5
+ } from "klaw/plugin-sdk/realtime-voice";