@kodelyth/voice-call 2026.5.42 → 2026.6.2

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 (111) hide show
  1. package/package.json +18 -6
  2. package/api.ts +0 -16
  3. package/cli-metadata.ts +0 -10
  4. package/config-api.ts +0 -12
  5. package/index.test.ts +0 -1075
  6. package/index.ts +0 -863
  7. package/runtime-api.ts +0 -20
  8. package/runtime-entry.ts +0 -1
  9. package/setup-api.ts +0 -47
  10. package/src/allowlist.test.ts +0 -18
  11. package/src/allowlist.ts +0 -19
  12. package/src/cli.test.ts +0 -12
  13. package/src/cli.ts +0 -866
  14. package/src/config-compat.test.ts +0 -130
  15. package/src/config-compat.ts +0 -227
  16. package/src/config.test.ts +0 -542
  17. package/src/config.ts +0 -883
  18. package/src/core-bridge.ts +0 -14
  19. package/src/deep-merge.test.ts +0 -40
  20. package/src/deep-merge.ts +0 -23
  21. package/src/gateway-continue-operation.ts +0 -200
  22. package/src/http-headers.test.ts +0 -16
  23. package/src/http-headers.ts +0 -15
  24. package/src/manager/context.ts +0 -50
  25. package/src/manager/events.test.ts +0 -578
  26. package/src/manager/events.ts +0 -332
  27. package/src/manager/lifecycle.ts +0 -53
  28. package/src/manager/lookup.test.ts +0 -52
  29. package/src/manager/lookup.ts +0 -35
  30. package/src/manager/outbound.test.ts +0 -629
  31. package/src/manager/outbound.ts +0 -508
  32. package/src/manager/state.ts +0 -48
  33. package/src/manager/store.ts +0 -107
  34. package/src/manager/timers.test.ts +0 -127
  35. package/src/manager/timers.ts +0 -113
  36. package/src/manager/twiml.test.ts +0 -13
  37. package/src/manager/twiml.ts +0 -17
  38. package/src/manager.closed-loop.test.ts +0 -259
  39. package/src/manager.inbound-allowlist.test.ts +0 -183
  40. package/src/manager.notify.test.ts +0 -390
  41. package/src/manager.restore.test.ts +0 -310
  42. package/src/manager.test-harness.ts +0 -127
  43. package/src/manager.ts +0 -441
  44. package/src/media-stream.test.ts +0 -953
  45. package/src/media-stream.ts +0 -876
  46. package/src/providers/base.ts +0 -99
  47. package/src/providers/mock.test.ts +0 -86
  48. package/src/providers/mock.ts +0 -185
  49. package/src/providers/plivo.test.ts +0 -93
  50. package/src/providers/plivo.ts +0 -601
  51. package/src/providers/shared/call-status.test.ts +0 -24
  52. package/src/providers/shared/call-status.ts +0 -24
  53. package/src/providers/shared/guarded-json-api.test.ts +0 -127
  54. package/src/providers/shared/guarded-json-api.ts +0 -49
  55. package/src/providers/telnyx.test.ts +0 -489
  56. package/src/providers/telnyx.ts +0 -419
  57. package/src/providers/twilio/api.test.ts +0 -184
  58. package/src/providers/twilio/api.ts +0 -100
  59. package/src/providers/twilio/twiml-policy.test.ts +0 -84
  60. package/src/providers/twilio/twiml-policy.ts +0 -87
  61. package/src/providers/twilio/webhook.ts +0 -34
  62. package/src/providers/twilio.test.ts +0 -607
  63. package/src/providers/twilio.ts +0 -861
  64. package/src/providers/twilio.types.ts +0 -17
  65. package/src/realtime-agent-context.test.ts +0 -101
  66. package/src/realtime-agent-context.ts +0 -149
  67. package/src/realtime-defaults.ts +0 -3
  68. package/src/realtime-fast-context.test.ts +0 -74
  69. package/src/realtime-fast-context.ts +0 -27
  70. package/src/realtime-transcription.runtime.ts +0 -4
  71. package/src/realtime-voice.runtime.ts +0 -5
  72. package/src/response-generator.test.ts +0 -385
  73. package/src/response-generator.ts +0 -348
  74. package/src/response-model.test.ts +0 -71
  75. package/src/response-model.ts +0 -23
  76. package/src/runtime.test.ts +0 -625
  77. package/src/runtime.ts +0 -528
  78. package/src/telephony-audio.test.ts +0 -61
  79. package/src/telephony-audio.ts +0 -12
  80. package/src/telephony-tts.test.ts +0 -196
  81. package/src/telephony-tts.ts +0 -235
  82. package/src/test-fixtures.ts +0 -82
  83. package/src/tts-provider-voice.test.ts +0 -34
  84. package/src/tts-provider-voice.ts +0 -21
  85. package/src/tunnel.test.ts +0 -173
  86. package/src/tunnel.ts +0 -314
  87. package/src/types.ts +0 -311
  88. package/src/utils.test.ts +0 -17
  89. package/src/utils.ts +0 -14
  90. package/src/voice-mapping.test.ts +0 -32
  91. package/src/voice-mapping.ts +0 -65
  92. package/src/webhook/realtime-audio-pacer.test.ts +0 -146
  93. package/src/webhook/realtime-audio-pacer.ts +0 -204
  94. package/src/webhook/realtime-handler.test.ts +0 -1450
  95. package/src/webhook/realtime-handler.ts +0 -1382
  96. package/src/webhook/stale-call-reaper.test.ts +0 -89
  97. package/src/webhook/stale-call-reaper.ts +0 -38
  98. package/src/webhook/stream-frame-adapter.test.ts +0 -187
  99. package/src/webhook/stream-frame-adapter.ts +0 -219
  100. package/src/webhook/tailscale.test.ts +0 -216
  101. package/src/webhook/tailscale.ts +0 -129
  102. package/src/webhook-exposure.test.ts +0 -33
  103. package/src/webhook-exposure.ts +0 -84
  104. package/src/webhook-security.test.ts +0 -813
  105. package/src/webhook-security.ts +0 -982
  106. package/src/webhook.hangup-once.lifecycle.test.ts +0 -179
  107. package/src/webhook.test.ts +0 -1615
  108. package/src/webhook.ts +0 -933
  109. package/src/webhook.types.ts +0 -5
  110. package/src/websocket-test-support.ts +0 -72
  111. package/tsconfig.json +0 -16
@@ -1,130 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import {
3
- VOICE_CALL_LEGACY_CONFIG_REMOVAL_VERSION,
4
- collectVoiceCallLegacyConfigIssues,
5
- formatVoiceCallLegacyConfigWarnings,
6
- migrateVoiceCallLegacyConfigInput,
7
- normalizeVoiceCallLegacyConfigInput,
8
- parseVoiceCallPluginConfig,
9
- } from "./config-compat.js";
10
-
11
- describe("voice-call config compatibility", () => {
12
- it("maps deprecated provider and twilio.from fields into canonical config", () => {
13
- const parsed = parseVoiceCallPluginConfig({
14
- enabled: true,
15
- provider: "log",
16
- twilio: {
17
- from: "+15550001234",
18
- },
19
- });
20
-
21
- expect(parsed.provider).toBe("mock");
22
- expect(parsed.fromNumber).toBe("+15550001234");
23
- });
24
-
25
- it("moves legacy streaming OpenAI fields into streaming.providers.openai", () => {
26
- const normalized = normalizeVoiceCallLegacyConfigInput({
27
- streaming: {
28
- enabled: true,
29
- sttProvider: "openai",
30
- openaiApiKey: "sk-test", // pragma: allowlist secret
31
- sttModel: "gpt-4o-transcribe",
32
- silenceDurationMs: 700,
33
- vadThreshold: 0.4,
34
- },
35
- });
36
-
37
- const streaming = normalized.streaming as
38
- | {
39
- enabled?: boolean;
40
- provider?: string;
41
- providers?: {
42
- openai?: {
43
- apiKey?: string;
44
- model?: string;
45
- silenceDurationMs?: number;
46
- vadThreshold?: number;
47
- };
48
- };
49
- openaiApiKey?: unknown;
50
- sttModel?: unknown;
51
- }
52
- | undefined;
53
- expect(streaming?.enabled).toBe(true);
54
- expect(streaming?.provider).toBe("openai");
55
- expect(streaming?.providers?.openai).toEqual({
56
- apiKey: "sk-test",
57
- model: "gpt-4o-transcribe",
58
- silenceDurationMs: 700,
59
- vadThreshold: 0.4,
60
- });
61
- expect(streaming?.openaiApiKey).toBeUndefined();
62
- expect(streaming?.sttModel).toBeUndefined();
63
- });
64
-
65
- it("reports doctor-oriented legacy issues and warnings", () => {
66
- const raw = {
67
- provider: "log",
68
- twilio: {
69
- from: "+15550001234",
70
- },
71
- streaming: {
72
- sttProvider: "openai",
73
- openaiApiKey: "sk-test", // pragma: allowlist secret
74
- },
75
- };
76
-
77
- expect(collectVoiceCallLegacyConfigIssues(raw)).toEqual([
78
- {
79
- path: "provider",
80
- replacement: "provider",
81
- message: 'Replace provider "log" with "mock".',
82
- },
83
- {
84
- path: "twilio.from",
85
- replacement: "fromNumber",
86
- message: "Move twilio.from to fromNumber.",
87
- },
88
- {
89
- path: "streaming.sttProvider",
90
- replacement: "streaming.provider",
91
- message: "Move streaming.sttProvider to streaming.provider.",
92
- },
93
- {
94
- path: "streaming.openaiApiKey",
95
- replacement: "streaming.providers.openai.apiKey",
96
- message: "Move streaming.openaiApiKey to streaming.providers.openai.apiKey.",
97
- },
98
- ]);
99
- expect(
100
- formatVoiceCallLegacyConfigWarnings({
101
- value: raw,
102
- configPathPrefix: "plugins.entries.voice-call.config",
103
- doctorFixCommand: "klaw doctor --fix",
104
- }),
105
- ).toEqual([
106
- `[voice-call] legacy config keys detected under plugins.entries.voice-call.config; runtime loading will not rewrite them, and support for the legacy shape will be removed in ${VOICE_CALL_LEGACY_CONFIG_REMOVAL_VERSION}. Run "klaw doctor --fix".`,
107
- '[voice-call] plugins.entries.voice-call.config.provider: Replace provider "log" with "mock".',
108
- "[voice-call] plugins.entries.voice-call.config.twilio.from: Move twilio.from to fromNumber.",
109
- "[voice-call] plugins.entries.voice-call.config.streaming.sttProvider: Move streaming.sttProvider to streaming.provider.",
110
- "[voice-call] plugins.entries.voice-call.config.streaming.openaiApiKey: Move streaming.openaiApiKey to streaming.providers.openai.apiKey.",
111
- ]);
112
- });
113
-
114
- it("returns doctor migration change lines", () => {
115
- const migration = migrateVoiceCallLegacyConfigInput({
116
- value: {
117
- provider: "log",
118
- streaming: {
119
- sttProvider: "openai",
120
- },
121
- },
122
- configPathPrefix: "plugins.entries.voice-call.config",
123
- });
124
-
125
- expect(migration.changes).toEqual([
126
- 'Moved plugins.entries.voice-call.config.provider "log" → "mock".',
127
- "Moved plugins.entries.voice-call.config.streaming.sttProvider → plugins.entries.voice-call.config.streaming.provider.",
128
- ]);
129
- });
130
- });
@@ -1,227 +0,0 @@
1
- import { asOptionalRecord, readStringField } from "klaw/plugin-sdk/string-coerce-runtime";
2
- import type { VoiceCallConfig } from "./config.js";
3
- import { VoiceCallConfigSchema } from "./config.js";
4
-
5
- export const VOICE_CALL_LEGACY_CONFIG_REMOVAL_VERSION = "2026.6.0";
6
-
7
- type VoiceCallLegacyConfigIssue = {
8
- path: string;
9
- replacement: string;
10
- message: string;
11
- };
12
-
13
- const asObject = asOptionalRecord;
14
- const getString = readStringField;
15
-
16
- function getNumber(obj: Record<string, unknown> | undefined, key: string): number | undefined {
17
- const value = obj?.[key];
18
- return typeof value === "number" ? value : undefined;
19
- }
20
-
21
- function mergeProviderConfig(
22
- providersValue: unknown,
23
- providerId: string,
24
- compatValues: Record<string, unknown>,
25
- ): Record<string, unknown> | undefined {
26
- if (Object.keys(compatValues).length === 0) {
27
- return asObject(providersValue);
28
- }
29
-
30
- const providers = asObject(providersValue) ?? {};
31
- const existing = asObject(providers[providerId]) ?? {};
32
- return {
33
- ...providers,
34
- [providerId]: {
35
- ...existing,
36
- ...compatValues,
37
- },
38
- };
39
- }
40
-
41
- export function collectVoiceCallLegacyConfigIssues(value: unknown): VoiceCallLegacyConfigIssue[] {
42
- const raw = asObject(value) ?? {};
43
- const twilio = asObject(raw.twilio);
44
- const streaming = asObject(raw.streaming);
45
-
46
- const issues: VoiceCallLegacyConfigIssue[] = [];
47
- if (raw.provider === "log") {
48
- issues.push({
49
- path: "provider",
50
- replacement: "provider",
51
- message: 'Replace provider "log" with "mock".',
52
- });
53
- }
54
- if (typeof twilio?.from === "string") {
55
- issues.push({
56
- path: "twilio.from",
57
- replacement: "fromNumber",
58
- message: "Move twilio.from to fromNumber.",
59
- });
60
- }
61
- if (typeof streaming?.sttProvider === "string") {
62
- issues.push({
63
- path: "streaming.sttProvider",
64
- replacement: "streaming.provider",
65
- message: "Move streaming.sttProvider to streaming.provider.",
66
- });
67
- }
68
- if (typeof streaming?.openaiApiKey === "string") {
69
- issues.push({
70
- path: "streaming.openaiApiKey",
71
- replacement: "streaming.providers.openai.apiKey",
72
- message: "Move streaming.openaiApiKey to streaming.providers.openai.apiKey.",
73
- });
74
- }
75
- if (typeof streaming?.sttModel === "string") {
76
- issues.push({
77
- path: "streaming.sttModel",
78
- replacement: "streaming.providers.openai.model",
79
- message: "Move streaming.sttModel to streaming.providers.openai.model.",
80
- });
81
- }
82
- if (typeof streaming?.silenceDurationMs === "number") {
83
- issues.push({
84
- path: "streaming.silenceDurationMs",
85
- replacement: "streaming.providers.openai.silenceDurationMs",
86
- message: "Move streaming.silenceDurationMs to streaming.providers.openai.silenceDurationMs.",
87
- });
88
- }
89
- if (typeof streaming?.vadThreshold === "number") {
90
- issues.push({
91
- path: "streaming.vadThreshold",
92
- replacement: "streaming.providers.openai.vadThreshold",
93
- message: "Move streaming.vadThreshold to streaming.providers.openai.vadThreshold.",
94
- });
95
- }
96
-
97
- return issues;
98
- }
99
-
100
- export function formatVoiceCallLegacyConfigWarnings(params: {
101
- value: unknown;
102
- configPathPrefix: string;
103
- doctorFixCommand: string;
104
- }): string[] {
105
- const issues = collectVoiceCallLegacyConfigIssues(params.value);
106
- if (issues.length === 0) {
107
- return [];
108
- }
109
-
110
- return [
111
- `[voice-call] legacy config keys detected under ${params.configPathPrefix}; runtime loading will not rewrite them, and support for the legacy shape will be removed in ${VOICE_CALL_LEGACY_CONFIG_REMOVAL_VERSION}. Run "${params.doctorFixCommand}".`,
112
- ...issues.map(
113
- (issue) => `[voice-call] ${params.configPathPrefix}.${issue.path}: ${issue.message}`,
114
- ),
115
- ];
116
- }
117
-
118
- export function migrateVoiceCallLegacyConfigInput(params: {
119
- value: unknown;
120
- configPathPrefix?: string;
121
- }): {
122
- config: Record<string, unknown>;
123
- changes: string[];
124
- issues: VoiceCallLegacyConfigIssue[];
125
- } {
126
- const raw = asObject(params.value) ?? {};
127
- const twilio = asObject(raw.twilio);
128
- const streaming = asObject(raw.streaming);
129
- const configPathPrefix = params.configPathPrefix ?? "plugins.entries.voice-call.config";
130
- const issues = collectVoiceCallLegacyConfigIssues(raw);
131
-
132
- const legacyStreamingOpenAICompat: Record<string, unknown> = {};
133
- const streamingOpenAIApiKey = getString(streaming, "openaiApiKey");
134
- if (streamingOpenAIApiKey) {
135
- legacyStreamingOpenAICompat.apiKey = streamingOpenAIApiKey;
136
- }
137
- const streamingSttModel = getString(streaming, "sttModel");
138
- if (streamingSttModel) {
139
- legacyStreamingOpenAICompat.model = streamingSttModel;
140
- }
141
- const streamingSilenceDurationMs = getNumber(streaming, "silenceDurationMs");
142
- if (streamingSilenceDurationMs !== undefined) {
143
- legacyStreamingOpenAICompat.silenceDurationMs = streamingSilenceDurationMs;
144
- }
145
- const streamingVadThreshold = getNumber(streaming, "vadThreshold");
146
- if (streamingVadThreshold !== undefined) {
147
- legacyStreamingOpenAICompat.vadThreshold = streamingVadThreshold;
148
- }
149
- const streamingProvider = getString(streaming, "provider");
150
- const legacyStreamingProvider = getString(streaming, "sttProvider");
151
-
152
- const normalizedStreaming: Record<string, unknown> | undefined = streaming
153
- ? {
154
- ...streaming,
155
- provider: streamingProvider ?? legacyStreamingProvider,
156
- providers: mergeProviderConfig(streaming.providers, "openai", legacyStreamingOpenAICompat),
157
- }
158
- : undefined;
159
-
160
- if (normalizedStreaming) {
161
- delete normalizedStreaming.sttProvider;
162
- delete normalizedStreaming.openaiApiKey;
163
- delete normalizedStreaming.sttModel;
164
- delete normalizedStreaming.silenceDurationMs;
165
- delete normalizedStreaming.vadThreshold;
166
- }
167
-
168
- const normalizedTwilio = twilio
169
- ? {
170
- ...twilio,
171
- }
172
- : undefined;
173
- if (normalizedTwilio) {
174
- delete normalizedTwilio.from;
175
- }
176
-
177
- const config = {
178
- ...raw,
179
- provider: raw.provider === "log" ? "mock" : raw.provider,
180
- fromNumber: raw.fromNumber ?? (typeof twilio?.from === "string" ? twilio.from : undefined),
181
- twilio: normalizedTwilio,
182
- streaming: normalizedStreaming,
183
- };
184
-
185
- const changes: string[] = [];
186
- if (raw.provider === "log") {
187
- changes.push(`Moved ${configPathPrefix}.provider "log" → "mock".`);
188
- }
189
- if (typeof twilio?.from === "string" && typeof raw.fromNumber !== "string") {
190
- changes.push(`Moved ${configPathPrefix}.twilio.from → ${configPathPrefix}.fromNumber.`);
191
- }
192
- if (typeof streaming?.sttProvider === "string") {
193
- changes.push(
194
- `Moved ${configPathPrefix}.streaming.sttProvider → ${configPathPrefix}.streaming.provider.`,
195
- );
196
- }
197
- if (typeof streaming?.openaiApiKey === "string") {
198
- changes.push(
199
- `Moved ${configPathPrefix}.streaming.openaiApiKey → ${configPathPrefix}.streaming.providers.openai.apiKey.`,
200
- );
201
- }
202
- if (typeof streaming?.sttModel === "string") {
203
- changes.push(
204
- `Moved ${configPathPrefix}.streaming.sttModel → ${configPathPrefix}.streaming.providers.openai.model.`,
205
- );
206
- }
207
- if (typeof streaming?.silenceDurationMs === "number") {
208
- changes.push(
209
- `Moved ${configPathPrefix}.streaming.silenceDurationMs → ${configPathPrefix}.streaming.providers.openai.silenceDurationMs.`,
210
- );
211
- }
212
- if (typeof streaming?.vadThreshold === "number") {
213
- changes.push(
214
- `Moved ${configPathPrefix}.streaming.vadThreshold → ${configPathPrefix}.streaming.providers.openai.vadThreshold.`,
215
- );
216
- }
217
-
218
- return { config, changes, issues };
219
- }
220
-
221
- export function normalizeVoiceCallLegacyConfigInput(value: unknown): Record<string, unknown> {
222
- return migrateVoiceCallLegacyConfigInput({ value }).config;
223
- }
224
-
225
- export function parseVoiceCallPluginConfig(value: unknown): VoiceCallConfig {
226
- return VoiceCallConfigSchema.parse(normalizeVoiceCallLegacyConfigInput(value));
227
- }