@framers/agentos 0.1.75 → 0.1.77
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/README.md +148 -38
- package/dist/api/agent.d.ts +13 -3
- package/dist/api/agent.d.ts.map +1 -1
- package/dist/api/agent.js +1 -0
- package/dist/api/agent.js.map +1 -1
- package/dist/api/generateImage.d.ts +11 -2
- package/dist/api/generateImage.d.ts.map +1 -1
- package/dist/api/generateImage.js +2 -2
- package/dist/api/generateImage.js.map +1 -1
- package/dist/api/generateText.d.ts +13 -3
- package/dist/api/generateText.d.ts.map +1 -1
- package/dist/api/generateText.js +2 -2
- package/dist/api/generateText.js.map +1 -1
- package/dist/api/model.d.ts +53 -0
- package/dist/api/model.d.ts.map +1 -1
- package/dist/api/model.js +58 -0
- package/dist/api/model.js.map +1 -1
- package/dist/api/provider-defaults.d.ts +40 -0
- package/dist/api/provider-defaults.d.ts.map +1 -0
- package/dist/api/provider-defaults.js +73 -0
- package/dist/api/provider-defaults.js.map +1 -0
- package/dist/api/streamText.js +2 -2
- package/dist/api/streamText.js.map +1 -1
- package/dist/core/agency/AgentCommunicationBus.d.ts +1 -0
- package/dist/core/agency/AgentCommunicationBus.d.ts.map +1 -1
- package/dist/core/agency/AgentCommunicationBus.js +62 -8
- package/dist/core/agency/AgentCommunicationBus.js.map +1 -1
- package/dist/core/agency/IAgentCommunicationBus.d.ts +1 -1
- package/dist/core/agency/IAgentCommunicationBus.d.ts.map +1 -1
- package/dist/orchestration/runtime/LoopController.d.ts +10 -10
- package/dist/orchestration/runtime/LoopController.d.ts.map +1 -1
- package/dist/orchestration/runtime/LoopController.js +1 -1
- package/dist/orchestration/runtime/LoopController.js.map +1 -1
- package/dist/orchestration/runtime/index.d.ts +1 -1
- package/dist/orchestration/runtime/index.d.ts.map +1 -1
- package/dist/orchestration/runtime/index.js.map +1 -1
- package/dist/speech/FallbackProxy.d.ts +104 -0
- package/dist/speech/FallbackProxy.d.ts.map +1 -0
- package/dist/speech/FallbackProxy.js +151 -0
- package/dist/speech/FallbackProxy.js.map +1 -0
- package/dist/speech/SpeechProviderResolver.d.ts +103 -0
- package/dist/speech/SpeechProviderResolver.d.ts.map +1 -0
- package/dist/speech/SpeechProviderResolver.js +256 -0
- package/dist/speech/SpeechProviderResolver.js.map +1 -0
- package/dist/speech/SpeechRuntime.d.ts +23 -1
- package/dist/speech/SpeechRuntime.d.ts.map +1 -1
- package/dist/speech/SpeechRuntime.js +82 -8
- package/dist/speech/SpeechRuntime.js.map +1 -1
- package/dist/speech/index.d.ts +6 -0
- package/dist/speech/index.d.ts.map +1 -1
- package/dist/speech/index.js +6 -0
- package/dist/speech/index.js.map +1 -1
- package/dist/speech/providerCatalog.d.ts.map +1 -1
- package/dist/speech/providerCatalog.js +15 -1
- package/dist/speech/providerCatalog.js.map +1 -1
- package/dist/speech/providers/AssemblyAISTTProvider.d.ts +49 -0
- package/dist/speech/providers/AssemblyAISTTProvider.d.ts.map +1 -0
- package/dist/speech/providers/AssemblyAISTTProvider.js +151 -0
- package/dist/speech/providers/AssemblyAISTTProvider.js.map +1 -0
- package/dist/speech/providers/AzureSpeechSTTProvider.d.ts +48 -0
- package/dist/speech/providers/AzureSpeechSTTProvider.d.ts.map +1 -0
- package/dist/speech/providers/AzureSpeechSTTProvider.js +90 -0
- package/dist/speech/providers/AzureSpeechSTTProvider.js.map +1 -0
- package/dist/speech/providers/AzureSpeechTTSProvider.d.ts +60 -0
- package/dist/speech/providers/AzureSpeechTTSProvider.d.ts.map +1 -0
- package/dist/speech/providers/AzureSpeechTTSProvider.js +127 -0
- package/dist/speech/providers/AzureSpeechTTSProvider.js.map +1 -0
- package/dist/speech/providers/DeepgramBatchSTTProvider.d.ts +55 -0
- package/dist/speech/providers/DeepgramBatchSTTProvider.d.ts.map +1 -0
- package/dist/speech/providers/DeepgramBatchSTTProvider.js +102 -0
- package/dist/speech/providers/DeepgramBatchSTTProvider.js.map +1 -0
- package/dist/speech/types.d.ts +35 -0
- package/dist/speech/types.d.ts.map +1 -1
- package/dist/voice/CallManager.d.ts +1 -1
- package/dist/voice/CallManager.d.ts.map +1 -1
- package/dist/voice/CallManager.js +9 -0
- package/dist/voice/CallManager.js.map +1 -1
- package/dist/voice/MediaStreamParser.d.ts +83 -0
- package/dist/voice/MediaStreamParser.d.ts.map +1 -0
- package/dist/voice/MediaStreamParser.js +2 -0
- package/dist/voice/MediaStreamParser.js.map +1 -0
- package/dist/voice/TelephonyStreamTransport.d.ts +112 -0
- package/dist/voice/TelephonyStreamTransport.d.ts.map +1 -0
- package/dist/voice/TelephonyStreamTransport.js +208 -0
- package/dist/voice/TelephonyStreamTransport.js.map +1 -0
- package/dist/voice/index.d.ts +10 -0
- package/dist/voice/index.d.ts.map +1 -1
- package/dist/voice/index.js +11 -0
- package/dist/voice/index.js.map +1 -1
- package/dist/voice/parsers/PlivoMediaStreamParser.d.ts +43 -0
- package/dist/voice/parsers/PlivoMediaStreamParser.d.ts.map +1 -0
- package/dist/voice/parsers/PlivoMediaStreamParser.js +92 -0
- package/dist/voice/parsers/PlivoMediaStreamParser.js.map +1 -0
- package/dist/voice/parsers/TelnyxMediaStreamParser.d.ts +51 -0
- package/dist/voice/parsers/TelnyxMediaStreamParser.d.ts.map +1 -0
- package/dist/voice/parsers/TelnyxMediaStreamParser.js +103 -0
- package/dist/voice/parsers/TelnyxMediaStreamParser.js.map +1 -0
- package/dist/voice/parsers/TwilioMediaStreamParser.d.ts +50 -0
- package/dist/voice/parsers/TwilioMediaStreamParser.d.ts.map +1 -0
- package/dist/voice/parsers/TwilioMediaStreamParser.js +144 -0
- package/dist/voice/parsers/TwilioMediaStreamParser.js.map +1 -0
- package/dist/voice/providers/plivo.d.ts +77 -0
- package/dist/voice/providers/plivo.d.ts.map +1 -0
- package/dist/voice/providers/plivo.js +180 -0
- package/dist/voice/providers/plivo.js.map +1 -0
- package/dist/voice/providers/telnyx.d.ts +93 -0
- package/dist/voice/providers/telnyx.d.ts.map +1 -0
- package/dist/voice/providers/telnyx.js +193 -0
- package/dist/voice/providers/telnyx.js.map +1 -0
- package/dist/voice/providers/twilio.d.ts +79 -0
- package/dist/voice/providers/twilio.d.ts.map +1 -0
- package/dist/voice/providers/twilio.js +191 -0
- package/dist/voice/providers/twilio.js.map +1 -0
- package/dist/voice/twiml.d.ts +69 -0
- package/dist/voice/twiml.d.ts.map +1 -0
- package/dist/voice/twiml.js +92 -0
- package/dist/voice/twiml.js.map +1 -0
- package/dist/voice/types.d.ts +9 -1
- package/dist/voice/types.d.ts.map +1 -1
- package/dist/voice-pipeline/AcousticEndpointDetector.d.ts +90 -0
- package/dist/voice-pipeline/AcousticEndpointDetector.d.ts.map +1 -0
- package/dist/voice-pipeline/AcousticEndpointDetector.js +123 -0
- package/dist/voice-pipeline/AcousticEndpointDetector.js.map +1 -0
- package/dist/voice-pipeline/HardCutBargeinHandler.d.ts +67 -0
- package/dist/voice-pipeline/HardCutBargeinHandler.d.ts.map +1 -0
- package/dist/voice-pipeline/HardCutBargeinHandler.js +55 -0
- package/dist/voice-pipeline/HardCutBargeinHandler.js.map +1 -0
- package/dist/voice-pipeline/HeuristicEndpointDetector.d.ts +128 -0
- package/dist/voice-pipeline/HeuristicEndpointDetector.d.ts.map +1 -0
- package/dist/voice-pipeline/HeuristicEndpointDetector.js +240 -0
- package/dist/voice-pipeline/HeuristicEndpointDetector.js.map +1 -0
- package/dist/voice-pipeline/SoftFadeBargeinHandler.d.ts +96 -0
- package/dist/voice-pipeline/SoftFadeBargeinHandler.d.ts.map +1 -0
- package/dist/voice-pipeline/SoftFadeBargeinHandler.js +69 -0
- package/dist/voice-pipeline/SoftFadeBargeinHandler.js.map +1 -0
- package/dist/voice-pipeline/VoicePipelineOrchestrator.d.ts +122 -0
- package/dist/voice-pipeline/VoicePipelineOrchestrator.d.ts.map +1 -0
- package/dist/voice-pipeline/VoicePipelineOrchestrator.js +317 -0
- package/dist/voice-pipeline/VoicePipelineOrchestrator.js.map +1 -0
- package/dist/voice-pipeline/WebSocketStreamTransport.d.ts +148 -0
- package/dist/voice-pipeline/WebSocketStreamTransport.d.ts.map +1 -0
- package/dist/voice-pipeline/WebSocketStreamTransport.js +207 -0
- package/dist/voice-pipeline/WebSocketStreamTransport.js.map +1 -0
- package/dist/voice-pipeline/index.d.ts +13 -0
- package/dist/voice-pipeline/index.d.ts.map +1 -0
- package/dist/voice-pipeline/index.js +13 -0
- package/dist/voice-pipeline/index.js.map +1 -0
- package/dist/voice-pipeline/types.d.ts +905 -0
- package/dist/voice-pipeline/types.d.ts.map +1 -0
- package/dist/voice-pipeline/types.js +23 -0
- package/dist/voice-pipeline/types.js.map +1 -0
- package/package.json +6 -1
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Twilio telephony provider for AgentOS voice calls.
|
|
3
|
+
*
|
|
4
|
+
* Implements {@link IVoiceCallProvider} using the Twilio REST API v2010-04-01.
|
|
5
|
+
* Webhook verification uses HMAC-SHA1 per Twilio's security documentation.
|
|
6
|
+
*
|
|
7
|
+
* @module @framers/agentos/voice/providers/twilio
|
|
8
|
+
*/
|
|
9
|
+
import { createHmac } from 'node:crypto';
|
|
10
|
+
import { randomUUID } from 'node:crypto';
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// TwilioVoiceProvider
|
|
13
|
+
// ============================================================================
|
|
14
|
+
/**
|
|
15
|
+
* Twilio voice call provider.
|
|
16
|
+
*
|
|
17
|
+
* Uses the Twilio REST API 2010-04-01 for outbound call control and
|
|
18
|
+
* HMAC-SHA1 for inbound webhook signature verification.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const provider = new TwilioVoiceProvider({
|
|
23
|
+
* accountSid: process.env.TWILIO_ACCOUNT_SID!,
|
|
24
|
+
* authToken: process.env.TWILIO_AUTH_TOKEN!,
|
|
25
|
+
* });
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class TwilioVoiceProvider {
|
|
29
|
+
constructor(config) {
|
|
30
|
+
this.name = 'twilio';
|
|
31
|
+
this.config = config;
|
|
32
|
+
this.baseUrl = 'https://api.twilio.com/2010-04-01';
|
|
33
|
+
this.authHeader =
|
|
34
|
+
'Basic ' +
|
|
35
|
+
Buffer.from(`${config.accountSid}:${config.authToken}`).toString('base64');
|
|
36
|
+
this.fetch = config.fetchImpl ?? globalThis.fetch;
|
|
37
|
+
}
|
|
38
|
+
// ── Webhook ───────────────────────────────────────────────────────────────
|
|
39
|
+
/**
|
|
40
|
+
* Verify an incoming Twilio webhook request using HMAC-SHA1.
|
|
41
|
+
*
|
|
42
|
+
* Twilio constructs the signed data by appending all POST params (sorted
|
|
43
|
+
* alphabetically) as key+value pairs directly to the full request URL.
|
|
44
|
+
*/
|
|
45
|
+
verifyWebhook(ctx) {
|
|
46
|
+
const signature = ctx.headers['x-twilio-signature'];
|
|
47
|
+
if (!signature || Array.isArray(signature)) {
|
|
48
|
+
return { valid: false, error: 'Missing x-twilio-signature header' };
|
|
49
|
+
}
|
|
50
|
+
// Build the signed data string
|
|
51
|
+
const bodyParams = new URLSearchParams(ctx.body.toString());
|
|
52
|
+
const sorted = [...bodyParams.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
53
|
+
let data = ctx.url;
|
|
54
|
+
for (const [key, value] of sorted) {
|
|
55
|
+
data += key + value;
|
|
56
|
+
}
|
|
57
|
+
const expected = createHmac('sha1', this.config.authToken)
|
|
58
|
+
.update(data)
|
|
59
|
+
.digest('base64');
|
|
60
|
+
const valid = expected === signature;
|
|
61
|
+
return {
|
|
62
|
+
valid,
|
|
63
|
+
...(valid ? {} : { error: 'Signature mismatch' }),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Parse a Twilio webhook body into normalized {@link NormalizedCallEvent}s.
|
|
68
|
+
*
|
|
69
|
+
* Handles call status transitions (ringing, in-progress, completed, failed,
|
|
70
|
+
* busy, no-answer, canceled) and DTMF input.
|
|
71
|
+
*/
|
|
72
|
+
parseWebhookEvent(ctx) {
|
|
73
|
+
const params = new URLSearchParams(ctx.body.toString());
|
|
74
|
+
const callSid = params.get('CallSid') ?? '';
|
|
75
|
+
const callStatus = params.get('CallStatus') ?? '';
|
|
76
|
+
const digits = params.get('Digits');
|
|
77
|
+
const timestamp = Date.now();
|
|
78
|
+
const events = [];
|
|
79
|
+
/** Helper: shared base fields. */
|
|
80
|
+
const base = () => ({
|
|
81
|
+
eventId: randomUUID(),
|
|
82
|
+
providerCallId: callSid,
|
|
83
|
+
timestamp,
|
|
84
|
+
});
|
|
85
|
+
switch (callStatus) {
|
|
86
|
+
case 'ringing':
|
|
87
|
+
events.push({ ...base(), kind: 'call-ringing' });
|
|
88
|
+
break;
|
|
89
|
+
case 'in-progress':
|
|
90
|
+
events.push({ ...base(), kind: 'call-answered' });
|
|
91
|
+
break;
|
|
92
|
+
case 'completed':
|
|
93
|
+
events.push({ ...base(), kind: 'call-completed' });
|
|
94
|
+
break;
|
|
95
|
+
case 'failed':
|
|
96
|
+
events.push({ ...base(), kind: 'call-failed' });
|
|
97
|
+
break;
|
|
98
|
+
case 'busy':
|
|
99
|
+
events.push({ ...base(), kind: 'call-busy' });
|
|
100
|
+
break;
|
|
101
|
+
case 'no-answer':
|
|
102
|
+
events.push({ ...base(), kind: 'call-no-answer' });
|
|
103
|
+
break;
|
|
104
|
+
case 'canceled':
|
|
105
|
+
events.push({ ...base(), kind: 'call-hangup-user' });
|
|
106
|
+
break;
|
|
107
|
+
default:
|
|
108
|
+
// initiated / queued / etc. — no normalized event emitted
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
// DTMF digit input
|
|
112
|
+
if (digits != null && digits !== '') {
|
|
113
|
+
events.push({
|
|
114
|
+
...base(),
|
|
115
|
+
kind: 'call-dtmf',
|
|
116
|
+
digit: digits,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return { events };
|
|
120
|
+
}
|
|
121
|
+
// ── Call Control ──────────────────────────────────────────────────────────
|
|
122
|
+
/**
|
|
123
|
+
* Initiate an outbound call via the Twilio Calls API.
|
|
124
|
+
*
|
|
125
|
+
* Posts to `/Accounts/{accountSid}/Calls.json` with form-encoded body.
|
|
126
|
+
* All four status callback events (initiated, ringing, answered, completed)
|
|
127
|
+
* are requested.
|
|
128
|
+
*/
|
|
129
|
+
async initiateCall(input) {
|
|
130
|
+
const url = `${this.baseUrl}/Accounts/${this.config.accountSid}/Calls.json`;
|
|
131
|
+
const body = [
|
|
132
|
+
`To=${encodeURIComponent(input.toNumber)}`,
|
|
133
|
+
`From=${encodeURIComponent(input.fromNumber)}`,
|
|
134
|
+
`Url=${encodeURIComponent(input.webhookUrl)}`,
|
|
135
|
+
`StatusCallback=${encodeURIComponent(input.statusCallbackUrl ?? '')}`,
|
|
136
|
+
`StatusCallbackEvent=initiated`,
|
|
137
|
+
`StatusCallbackEvent=ringing`,
|
|
138
|
+
`StatusCallbackEvent=answered`,
|
|
139
|
+
`StatusCallbackEvent=completed`,
|
|
140
|
+
].join('&');
|
|
141
|
+
const response = await this.fetch(url, {
|
|
142
|
+
method: 'POST',
|
|
143
|
+
headers: {
|
|
144
|
+
Authorization: this.authHeader,
|
|
145
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
146
|
+
},
|
|
147
|
+
body,
|
|
148
|
+
});
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
const text = await response.text().catch(() => String(response.status));
|
|
151
|
+
return { providerCallId: '', success: false, error: `Twilio error ${response.status}: ${text}` };
|
|
152
|
+
}
|
|
153
|
+
const data = (await response.json());
|
|
154
|
+
return { providerCallId: data.sid, success: true };
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Hang up an active call by posting `Status=completed`.
|
|
158
|
+
*/
|
|
159
|
+
async hangupCall(input) {
|
|
160
|
+
const url = `${this.baseUrl}/Accounts/${this.config.accountSid}/Calls/${input.providerCallId}.json`;
|
|
161
|
+
await this.fetch(url, {
|
|
162
|
+
method: 'POST',
|
|
163
|
+
headers: {
|
|
164
|
+
Authorization: this.authHeader,
|
|
165
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
166
|
+
},
|
|
167
|
+
body: 'Status=completed',
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Inject TTS into a live call using a TwiML `<Say>` verb.
|
|
172
|
+
*
|
|
173
|
+
* Sends a `Twiml` parameter containing a minimal `<Response><Say>` document.
|
|
174
|
+
* The optional `voice` attribute maps to Twilio's built-in voice names
|
|
175
|
+
* (e.g., `alice`, `Polly.Joanna`).
|
|
176
|
+
*/
|
|
177
|
+
async playTts(input) {
|
|
178
|
+
const url = `${this.baseUrl}/Accounts/${this.config.accountSid}/Calls/${input.providerCallId}.json`;
|
|
179
|
+
const voiceAttr = input.voice ? ` voice="${input.voice}"` : '';
|
|
180
|
+
const twiml = `<Response><Say${voiceAttr}>${input.text}</Say></Response>`;
|
|
181
|
+
await this.fetch(url, {
|
|
182
|
+
method: 'POST',
|
|
183
|
+
headers: {
|
|
184
|
+
Authorization: this.authHeader,
|
|
185
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
186
|
+
},
|
|
187
|
+
body: `Twiml=${encodeURIComponent(twiml)}`,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=twilio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"twilio.js","sourceRoot":"","sources":["../../../src/voice/providers/twilio.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview TwiML and XML generation helpers for telephony providers.
|
|
3
|
+
*
|
|
4
|
+
* Generates provider-specific XML/TwiML response payloads for Twilio, Telnyx,
|
|
5
|
+
* and Plivo. All text content and attribute values are XML-escaped to prevent
|
|
6
|
+
* injection or malformed markup.
|
|
7
|
+
*
|
|
8
|
+
* @module @framers/agentos/voice/twiml
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Generate TwiML for Twilio conversation mode using a bidirectional media stream.
|
|
12
|
+
*
|
|
13
|
+
* The returned markup instructs Twilio to open a WebSocket to `streamUrl` and
|
|
14
|
+
* stream audio in both directions for the duration of the call.
|
|
15
|
+
*
|
|
16
|
+
* @param streamUrl - WebSocket URL Twilio should connect to (e.g. `wss://…/stream`).
|
|
17
|
+
* @param token - Optional bearer token appended as a `?token=` query parameter.
|
|
18
|
+
* @returns A complete TwiML XML document string.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* res.type('text/xml').send(twilioConversationTwiml('wss://api.example.com/call', jwtToken));
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export declare function twilioConversationTwiml(streamUrl: string, token?: string): string;
|
|
26
|
+
/**
|
|
27
|
+
* Generate TwiML for Twilio notify mode — synthesise `text` over the call then hang up.
|
|
28
|
+
*
|
|
29
|
+
* Useful for delivering one-shot announcements (e.g. voicemail greetings, error
|
|
30
|
+
* messages) without establishing a full media stream.
|
|
31
|
+
*
|
|
32
|
+
* @param text - The message to speak to the caller.
|
|
33
|
+
* @param voice - Optional Twilio voice name (e.g. `'Polly.Joanna'`, `'alice'`).
|
|
34
|
+
* @returns A complete TwiML XML document string.
|
|
35
|
+
*/
|
|
36
|
+
export declare function twilioNotifyTwiml(text: string, voice?: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Generate a Telnyx streaming XML response.
|
|
39
|
+
*
|
|
40
|
+
* Telnyx primarily uses a REST API for call control, but this helper produces
|
|
41
|
+
* an XML acknowledgment document wrapping the stream URL for webhook responses
|
|
42
|
+
* that require XML.
|
|
43
|
+
*
|
|
44
|
+
* @param streamUrl - WebSocket URL Telnyx should stream audio to.
|
|
45
|
+
* @returns A complete XML document string.
|
|
46
|
+
*/
|
|
47
|
+
export declare function telnyxStreamXml(streamUrl: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Generate Plivo bidirectional streaming XML.
|
|
50
|
+
*
|
|
51
|
+
* Instructs Plivo to open a bidirectional WebSocket stream and keep the call
|
|
52
|
+
* alive for the duration of the stream session.
|
|
53
|
+
*
|
|
54
|
+
* @param streamUrl - WebSocket URL Plivo should connect to.
|
|
55
|
+
* @returns A complete Plivo XML document string.
|
|
56
|
+
*/
|
|
57
|
+
export declare function plivoStreamXml(streamUrl: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Generate Plivo speak + hangup XML.
|
|
60
|
+
*
|
|
61
|
+
* Synthesises `text` to the caller using Plivo's TTS engine and immediately
|
|
62
|
+
* hangs up after playback completes.
|
|
63
|
+
*
|
|
64
|
+
* @param text - The message to speak to the caller.
|
|
65
|
+
* @param voice - Optional Plivo voice name (e.g. `'WOMAN'`, `'Polly.Joanna'`).
|
|
66
|
+
* @returns A complete Plivo XML document string.
|
|
67
|
+
*/
|
|
68
|
+
export declare function plivoNotifyXml(text: string, voice?: string): string;
|
|
69
|
+
//# sourceMappingURL=twiml.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"twiml.d.ts","sourceRoot":"","sources":["../../src/voice/twiml.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAQH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGjF;AAED;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGtE;AAMD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzD;AAMD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGnE"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview TwiML and XML generation helpers for telephony providers.
|
|
3
|
+
*
|
|
4
|
+
* Generates provider-specific XML/TwiML response payloads for Twilio, Telnyx,
|
|
5
|
+
* and Plivo. All text content and attribute values are XML-escaped to prevent
|
|
6
|
+
* injection or malformed markup.
|
|
7
|
+
*
|
|
8
|
+
* @module @framers/agentos/voice/twiml
|
|
9
|
+
*/
|
|
10
|
+
import { escapeXml } from './telephony-audio.js';
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// Twilio TwiML
|
|
13
|
+
// ============================================================================
|
|
14
|
+
/**
|
|
15
|
+
* Generate TwiML for Twilio conversation mode using a bidirectional media stream.
|
|
16
|
+
*
|
|
17
|
+
* The returned markup instructs Twilio to open a WebSocket to `streamUrl` and
|
|
18
|
+
* stream audio in both directions for the duration of the call.
|
|
19
|
+
*
|
|
20
|
+
* @param streamUrl - WebSocket URL Twilio should connect to (e.g. `wss://…/stream`).
|
|
21
|
+
* @param token - Optional bearer token appended as a `?token=` query parameter.
|
|
22
|
+
* @returns A complete TwiML XML document string.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* res.type('text/xml').send(twilioConversationTwiml('wss://api.example.com/call', jwtToken));
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export function twilioConversationTwiml(streamUrl, token) {
|
|
30
|
+
const url = token ? `${streamUrl}?token=${token}` : streamUrl;
|
|
31
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n<Response><Connect><Stream url="${escapeXml(url)}" /></Connect></Response>`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Generate TwiML for Twilio notify mode — synthesise `text` over the call then hang up.
|
|
35
|
+
*
|
|
36
|
+
* Useful for delivering one-shot announcements (e.g. voicemail greetings, error
|
|
37
|
+
* messages) without establishing a full media stream.
|
|
38
|
+
*
|
|
39
|
+
* @param text - The message to speak to the caller.
|
|
40
|
+
* @param voice - Optional Twilio voice name (e.g. `'Polly.Joanna'`, `'alice'`).
|
|
41
|
+
* @returns A complete TwiML XML document string.
|
|
42
|
+
*/
|
|
43
|
+
export function twilioNotifyTwiml(text, voice) {
|
|
44
|
+
const voiceAttr = voice ? ` voice="${escapeXml(voice)}"` : '';
|
|
45
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n<Response><Say${voiceAttr}>${escapeXml(text)}</Say><Hangup/></Response>`;
|
|
46
|
+
}
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// Telnyx XML
|
|
49
|
+
// ============================================================================
|
|
50
|
+
/**
|
|
51
|
+
* Generate a Telnyx streaming XML response.
|
|
52
|
+
*
|
|
53
|
+
* Telnyx primarily uses a REST API for call control, but this helper produces
|
|
54
|
+
* an XML acknowledgment document wrapping the stream URL for webhook responses
|
|
55
|
+
* that require XML.
|
|
56
|
+
*
|
|
57
|
+
* @param streamUrl - WebSocket URL Telnyx should stream audio to.
|
|
58
|
+
* @returns A complete XML document string.
|
|
59
|
+
*/
|
|
60
|
+
export function telnyxStreamXml(streamUrl) {
|
|
61
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n<Response><Stream url="${escapeXml(streamUrl)}" /></Response>`;
|
|
62
|
+
}
|
|
63
|
+
// ============================================================================
|
|
64
|
+
// Plivo XML
|
|
65
|
+
// ============================================================================
|
|
66
|
+
/**
|
|
67
|
+
* Generate Plivo bidirectional streaming XML.
|
|
68
|
+
*
|
|
69
|
+
* Instructs Plivo to open a bidirectional WebSocket stream and keep the call
|
|
70
|
+
* alive for the duration of the stream session.
|
|
71
|
+
*
|
|
72
|
+
* @param streamUrl - WebSocket URL Plivo should connect to.
|
|
73
|
+
* @returns A complete Plivo XML document string.
|
|
74
|
+
*/
|
|
75
|
+
export function plivoStreamXml(streamUrl) {
|
|
76
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n<Response><Stream bidirectional="true" keepCallAlive="true">${escapeXml(streamUrl)}</Stream></Response>`;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Generate Plivo speak + hangup XML.
|
|
80
|
+
*
|
|
81
|
+
* Synthesises `text` to the caller using Plivo's TTS engine and immediately
|
|
82
|
+
* hangs up after playback completes.
|
|
83
|
+
*
|
|
84
|
+
* @param text - The message to speak to the caller.
|
|
85
|
+
* @param voice - Optional Plivo voice name (e.g. `'WOMAN'`, `'Polly.Joanna'`).
|
|
86
|
+
* @returns A complete Plivo XML document string.
|
|
87
|
+
*/
|
|
88
|
+
export function plivoNotifyXml(text, voice) {
|
|
89
|
+
const voiceAttr = voice ? ` voice="${escapeXml(voice)}"` : '';
|
|
90
|
+
return `<?xml version="1.0" encoding="UTF-8"?>\n<Response><Speak${voiceAttr}>${escapeXml(text)}</Speak><Hangup/></Response>`;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=twiml.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"twiml.js","sourceRoot":"","sources":["../../src/voice/twiml.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEjD,+EAA+E;AAC/E,eAAe;AACf,+EAA+E;AAE/E;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,uBAAuB,CAAC,SAAiB,EAAE,KAAc;IACvE,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,UAAU,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9D,OAAO,2EAA2E,SAAS,CAAC,GAAG,CAAC,2BAA2B,CAAC;AAC9H,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY,EAAE,KAAc;IAC5D,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,WAAW,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,OAAO,yDAAyD,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC,4BAA4B,CAAC;AAC3H,CAAC;AAED,+EAA+E;AAC/E,aAAa;AACb,+EAA+E;AAE/E;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,OAAO,kEAAkE,SAAS,CAAC,SAAS,CAAC,iBAAiB,CAAC;AACjH,CAAC;AAED,+EAA+E;AAC/E,YAAY;AACZ,+EAA+E;AAE/E;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,OAAO,uGAAuG,SAAS,CAAC,SAAS,CAAC,sBAAsB,CAAC;AAC3J,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,KAAc;IACzD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,WAAW,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,OAAO,2DAA2D,SAAS,IAAI,SAAS,CAAC,IAAI,CAAC,8BAA8B,CAAC;AAC/H,CAAC"}
|
package/dist/voice/types.d.ts
CHANGED
|
@@ -99,7 +99,7 @@ export interface CallRecord {
|
|
|
99
99
|
* Normalized webhook event from any telephony provider.
|
|
100
100
|
* Uses a discriminated union on the `kind` field.
|
|
101
101
|
*/
|
|
102
|
-
export type NormalizedCallEvent = NormalizedCallRinging | NormalizedCallAnswered | NormalizedCallCompleted | NormalizedCallFailed | NormalizedCallBusy | NormalizedCallNoAnswer | NormalizedCallVoicemail | NormalizedCallHangupUser | NormalizedCallError | NormalizedTranscript | NormalizedSpeechStart | NormalizedMediaStreamConnected;
|
|
102
|
+
export type NormalizedCallEvent = NormalizedCallRinging | NormalizedCallAnswered | NormalizedCallCompleted | NormalizedCallFailed | NormalizedCallBusy | NormalizedCallNoAnswer | NormalizedCallVoicemail | NormalizedCallHangupUser | NormalizedCallError | NormalizedTranscript | NormalizedSpeechStart | NormalizedMediaStreamConnected | NormalizedDtmfReceived;
|
|
103
103
|
interface NormalizedEventBase {
|
|
104
104
|
/** Provider-assigned event ID for idempotency. */
|
|
105
105
|
eventId: string;
|
|
@@ -150,6 +150,14 @@ export interface NormalizedMediaStreamConnected extends NormalizedEventBase {
|
|
|
150
150
|
kind: 'media-stream-connected';
|
|
151
151
|
streamSid: string;
|
|
152
152
|
}
|
|
153
|
+
/** DTMF digit received during a call. */
|
|
154
|
+
export interface NormalizedDtmfReceived extends NormalizedEventBase {
|
|
155
|
+
kind: 'call-dtmf';
|
|
156
|
+
/** The digit pressed: '0'-'9', '*', '#' */
|
|
157
|
+
digit: string;
|
|
158
|
+
/** How long the key was pressed (ms), if available from provider */
|
|
159
|
+
durationMs?: number;
|
|
160
|
+
}
|
|
153
161
|
/** Raw webhook context passed to provider verification. */
|
|
154
162
|
export interface WebhookContext {
|
|
155
163
|
/** HTTP method (usually POST). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/voice/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAMH;;GAEG;AACH,MAAM,MAAM,iBAAiB,GACzB,QAAQ,GACR,QAAQ,GACR,OAAO,GACP,MAAM,GACN,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAMlB;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAEjB,WAAW,GACX,SAAS,GACT,UAAU,GACV,QAAQ,GAER,UAAU,GACV,WAAW,GAEX,WAAW,GACX,aAAa,GACb,YAAY,GACZ,SAAS,GACT,OAAO,GACP,QAAQ,GACR,WAAW,GACX,MAAM,GACN,WAAW,CAAC;AAEhB,0EAA0E;AAC1E,eAAO,MAAM,oBAAoB,gBAU/B,CAAC;AAEH,6DAA6D;AAC7D,eAAO,MAAM,mBAAmB,gBAAgD,CAAC;AAEjF,qEAAqE;AACrE,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAO3C,CAAC;AAMF;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,cAAc,CAAC;AAEjD;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,SAAS,CAAC;AAEnD;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,WAAW,GAAG,SAAS,GAAG,MAAM,CAAC;AAM1E,2CAA2C;AAC3C,MAAM,WAAW,eAAe;IAC9B,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB;IACjB,OAAO,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,uBAAuB;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,OAAO,EAAE,OAAO,CAAC;CAClB;AAMD,8BAA8B;AAC9B,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC;AAE5B;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,qCAAqC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,wDAAwD;IACxD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,4CAA4C;IAC5C,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,2CAA2C;IAC3C,KAAK,EAAE,SAAS,CAAC;IACjB,sBAAsB;IACtB,SAAS,EAAE,aAAa,CAAC;IACzB,6BAA6B;IAC7B,IAAI,EAAE,QAAQ,CAAC;IACf,wCAAwC;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,6DAA6D;IAC7D,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAMD;;;GAGG;AACH,MAAM,MAAM,mBAAmB,GAC3B,qBAAqB,GACrB,sBAAsB,GACtB,uBAAuB,GACvB,oBAAoB,GACpB,kBAAkB,GAClB,sBAAsB,GACtB,uBAAuB,GACvB,wBAAwB,GACxB,mBAAmB,GACnB,oBAAoB,GACpB,qBAAqB,GACrB,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/voice/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module voice-pipeline/AcousticEndpointDetector
|
|
3
|
+
*
|
|
4
|
+
* Acoustic-only endpoint detector that wraps {@link SilenceDetector} to convert
|
|
5
|
+
* VAD events into turn-completion decisions. It ignores transcript content entirely
|
|
6
|
+
* and relies solely on the duration of post-speech silence to decide when the user
|
|
7
|
+
* has finished speaking.
|
|
8
|
+
*
|
|
9
|
+
* Emits:
|
|
10
|
+
* - `'turn_complete'` ({@link TurnCompleteEvent}) — silence exceeded the configured
|
|
11
|
+
* `utteranceEndThresholdMs` after the most recent `speech_end` VAD event.
|
|
12
|
+
* - `'speech_start'` () — re-emitted when a `speech_start` VAD event is received.
|
|
13
|
+
*/
|
|
14
|
+
import { EventEmitter } from 'node:events';
|
|
15
|
+
import type { IEndpointDetector, VadEvent, TranscriptEvent } from './types.js';
|
|
16
|
+
/**
|
|
17
|
+
* Constructor options for {@link AcousticEndpointDetector}.
|
|
18
|
+
*/
|
|
19
|
+
export interface AcousticEndpointDetectorConfig {
|
|
20
|
+
/**
|
|
21
|
+
* Silence duration after speech (ms) that triggers a "significant pause"
|
|
22
|
+
* notification on the underlying {@link SilenceDetector}. Does not directly
|
|
23
|
+
* cause `turn_complete` to fire, but is forwarded to the SilenceDetector.
|
|
24
|
+
* @defaultValue 1500
|
|
25
|
+
*/
|
|
26
|
+
significantPauseThresholdMs?: number;
|
|
27
|
+
/**
|
|
28
|
+
* Silence duration after speech (ms) that triggers `turn_complete` with
|
|
29
|
+
* `reason: 'silence_timeout'`.
|
|
30
|
+
* @defaultValue 3000
|
|
31
|
+
*/
|
|
32
|
+
utteranceEndThresholdMs?: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Purely acoustic endpoint detector.
|
|
36
|
+
*
|
|
37
|
+
* Delegates silence timing to a {@link SilenceDetector} instance. VAD
|
|
38
|
+
* `speech_end` events start the silence clock; `speech_start` events cancel
|
|
39
|
+
* any pending turn-complete emission. Transcript content is completely ignored.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```ts
|
|
43
|
+
* const detector = new AcousticEndpointDetector({ utteranceEndThresholdMs: 2000 });
|
|
44
|
+
* detector.on('turn_complete', (event) => console.log('Turn done:', event));
|
|
45
|
+
* detector.pushVadEvent({ type: 'speech_end', timestamp: Date.now() });
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare class AcousticEndpointDetector extends EventEmitter implements IEndpointDetector {
|
|
49
|
+
/** @inheritdoc */
|
|
50
|
+
readonly mode: "acoustic";
|
|
51
|
+
/** Underlying silence-duration tracker. */
|
|
52
|
+
private readonly silenceDetector;
|
|
53
|
+
/**
|
|
54
|
+
* Timestamp (ms) when the current speech segment began. Tracked so that
|
|
55
|
+
* `durationMs` in the emitted {@link TurnCompleteEvent} can be computed.
|
|
56
|
+
*/
|
|
57
|
+
private speechStartTimeMs;
|
|
58
|
+
/**
|
|
59
|
+
* Timestamp (ms) when the most recent `speech_end` VAD event was received.
|
|
60
|
+
* Used to calculate `durationMs` for the turn-complete event.
|
|
61
|
+
*/
|
|
62
|
+
private speechEndTimeMs;
|
|
63
|
+
/**
|
|
64
|
+
* Creates a new AcousticEndpointDetector.
|
|
65
|
+
*
|
|
66
|
+
* @param config - Optional silence-threshold overrides.
|
|
67
|
+
*/
|
|
68
|
+
constructor(config?: AcousticEndpointDetectorConfig);
|
|
69
|
+
/**
|
|
70
|
+
* Converts a {@link VadEvent} into the SilenceDetector's expected API calls.
|
|
71
|
+
*
|
|
72
|
+
* - `speech_start` — resets silence state and re-emits `'speech_start'` on self.
|
|
73
|
+
* - `speech_end` — starts the silence clock.
|
|
74
|
+
* - `silence` — treated as ongoing non-speech frames.
|
|
75
|
+
*
|
|
76
|
+
* @param event - Incoming VAD event.
|
|
77
|
+
*/
|
|
78
|
+
pushVadEvent(event: VadEvent): void;
|
|
79
|
+
/**
|
|
80
|
+
* No-op — this detector is purely acoustic and does not use transcript content.
|
|
81
|
+
*
|
|
82
|
+
* @param _event - Ignored transcript event.
|
|
83
|
+
*/
|
|
84
|
+
pushTranscript(_event: TranscriptEvent): void;
|
|
85
|
+
/**
|
|
86
|
+
* Resets all internal state and timers. Call at the start of each new turn.
|
|
87
|
+
*/
|
|
88
|
+
reset(): void;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=AcousticEndpointDetector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AcousticEndpointDetector.d.ts","sourceRoot":"","sources":["../../src/voice-pipeline/AcousticEndpointDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,KAAK,EACV,iBAAiB,EACjB,QAAQ,EACR,eAAe,EAEhB,MAAM,YAAY,CAAC;AAMpB;;GAEG;AACH,MAAM,WAAW,8BAA8B;IAC7C;;;;;OAKG;IACH,2BAA2B,CAAC,EAAE,MAAM,CAAC;IAErC;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAC;CAClC;AAMD;;;;;;;;;;;;;GAaG;AACH,qBAAa,wBAAyB,SAAQ,YAAa,YAAW,iBAAiB;IACrF,kBAAkB;IAClB,SAAgB,IAAI,EAAG,UAAU,CAAU;IAE3C,2CAA2C;IAC3C,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAElD;;;OAGG;IACH,OAAO,CAAC,iBAAiB,CAAuB;IAEhD;;;OAGG;IACH,OAAO,CAAC,eAAe,CAAuB;IAI9C;;;;OAIG;gBACS,MAAM,GAAE,8BAAmC;IAgCvD;;;;;;;;OAQG;IACI,YAAY,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAyB1C;;;;OAIG;IACI,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAIpD;;OAEG;IACI,KAAK,IAAI,IAAI;CAKrB"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module voice-pipeline/AcousticEndpointDetector
|
|
3
|
+
*
|
|
4
|
+
* Acoustic-only endpoint detector that wraps {@link SilenceDetector} to convert
|
|
5
|
+
* VAD events into turn-completion decisions. It ignores transcript content entirely
|
|
6
|
+
* and relies solely on the duration of post-speech silence to decide when the user
|
|
7
|
+
* has finished speaking.
|
|
8
|
+
*
|
|
9
|
+
* Emits:
|
|
10
|
+
* - `'turn_complete'` ({@link TurnCompleteEvent}) — silence exceeded the configured
|
|
11
|
+
* `utteranceEndThresholdMs` after the most recent `speech_end` VAD event.
|
|
12
|
+
* - `'speech_start'` () — re-emitted when a `speech_start` VAD event is received.
|
|
13
|
+
*/
|
|
14
|
+
import { EventEmitter } from 'node:events';
|
|
15
|
+
import { SilenceDetector } from '../core/audio/SilenceDetector.js';
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
// Implementation
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
/**
|
|
20
|
+
* Purely acoustic endpoint detector.
|
|
21
|
+
*
|
|
22
|
+
* Delegates silence timing to a {@link SilenceDetector} instance. VAD
|
|
23
|
+
* `speech_end` events start the silence clock; `speech_start` events cancel
|
|
24
|
+
* any pending turn-complete emission. Transcript content is completely ignored.
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* const detector = new AcousticEndpointDetector({ utteranceEndThresholdMs: 2000 });
|
|
29
|
+
* detector.on('turn_complete', (event) => console.log('Turn done:', event));
|
|
30
|
+
* detector.pushVadEvent({ type: 'speech_end', timestamp: Date.now() });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export class AcousticEndpointDetector extends EventEmitter {
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
/**
|
|
36
|
+
* Creates a new AcousticEndpointDetector.
|
|
37
|
+
*
|
|
38
|
+
* @param config - Optional silence-threshold overrides.
|
|
39
|
+
*/
|
|
40
|
+
constructor(config = {}) {
|
|
41
|
+
super();
|
|
42
|
+
/** @inheritdoc */
|
|
43
|
+
this.mode = 'acoustic';
|
|
44
|
+
/**
|
|
45
|
+
* Timestamp (ms) when the current speech segment began. Tracked so that
|
|
46
|
+
* `durationMs` in the emitted {@link TurnCompleteEvent} can be computed.
|
|
47
|
+
*/
|
|
48
|
+
this.speechStartTimeMs = null;
|
|
49
|
+
/**
|
|
50
|
+
* Timestamp (ms) when the most recent `speech_end` VAD event was received.
|
|
51
|
+
* Used to calculate `durationMs` for the turn-complete event.
|
|
52
|
+
*/
|
|
53
|
+
this.speechEndTimeMs = null;
|
|
54
|
+
const sdConfig = {
|
|
55
|
+
significantPauseThresholdMs: config.significantPauseThresholdMs ?? 1500,
|
|
56
|
+
utteranceEndThresholdMs: config.utteranceEndThresholdMs ?? 3000,
|
|
57
|
+
};
|
|
58
|
+
this.silenceDetector = new SilenceDetector(sdConfig);
|
|
59
|
+
// When SilenceDetector decides the utterance has ended, fire turn_complete.
|
|
60
|
+
this.silenceDetector.on('utterance_end_detected', (_silenceDurationMs) => {
|
|
61
|
+
const durationMs = this.speechStartTimeMs !== null && this.speechEndTimeMs !== null
|
|
62
|
+
? this.speechEndTimeMs - this.speechStartTimeMs
|
|
63
|
+
: 0;
|
|
64
|
+
const event = {
|
|
65
|
+
transcript: '', // Acoustic mode has no transcript access
|
|
66
|
+
confidence: 0,
|
|
67
|
+
durationMs,
|
|
68
|
+
reason: 'silence_timeout',
|
|
69
|
+
};
|
|
70
|
+
this.emit('turn_complete', event);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
// IEndpointDetector
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
/**
|
|
77
|
+
* Converts a {@link VadEvent} into the SilenceDetector's expected API calls.
|
|
78
|
+
*
|
|
79
|
+
* - `speech_start` — resets silence state and re-emits `'speech_start'` on self.
|
|
80
|
+
* - `speech_end` — starts the silence clock.
|
|
81
|
+
* - `silence` — treated as ongoing non-speech frames.
|
|
82
|
+
*
|
|
83
|
+
* @param event - Incoming VAD event.
|
|
84
|
+
*/
|
|
85
|
+
pushVadEvent(event) {
|
|
86
|
+
// Minimal VADResult stub — SilenceDetector's public methods only use it as
|
|
87
|
+
// a pass-through parameter and don't inspect its contents.
|
|
88
|
+
const vadResultStub = { timestamp: event.timestamp };
|
|
89
|
+
switch (event.type) {
|
|
90
|
+
case 'speech_start':
|
|
91
|
+
this.speechStartTimeMs = event.timestamp;
|
|
92
|
+
this.speechEndTimeMs = null;
|
|
93
|
+
this.silenceDetector.handleSpeechStart(vadResultStub);
|
|
94
|
+
this.emit('speech_start');
|
|
95
|
+
break;
|
|
96
|
+
case 'speech_end':
|
|
97
|
+
this.speechEndTimeMs = event.timestamp;
|
|
98
|
+
this.silenceDetector.handleSpeechEnd(vadResultStub, 0);
|
|
99
|
+
break;
|
|
100
|
+
case 'silence':
|
|
101
|
+
// Periodic silence heartbeat — pass as a non-speech frame.
|
|
102
|
+
this.silenceDetector.handleNoVoiceActivity(vadResultStub);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* No-op — this detector is purely acoustic and does not use transcript content.
|
|
108
|
+
*
|
|
109
|
+
* @param _event - Ignored transcript event.
|
|
110
|
+
*/
|
|
111
|
+
pushTranscript(_event) {
|
|
112
|
+
// Intentional no-op: acoustic mode ignores linguistic content.
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Resets all internal state and timers. Call at the start of each new turn.
|
|
116
|
+
*/
|
|
117
|
+
reset() {
|
|
118
|
+
this.speechStartTimeMs = null;
|
|
119
|
+
this.speechEndTimeMs = null;
|
|
120
|
+
this.silenceDetector.reset();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=AcousticEndpointDetector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AcousticEndpointDetector.js","sourceRoot":"","sources":["../../src/voice-pipeline/AcousticEndpointDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,eAAe,EAA8B,MAAM,kCAAkC,CAAC;AAgC/F,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,wBAAyB,SAAQ,YAAY;IAmBxD,8EAA8E;IAE9E;;;;OAIG;IACH,YAAY,SAAyC,EAAE;QACrD,KAAK,EAAE,CAAC;QA1BV,kBAAkB;QACF,SAAI,GAAG,UAAmB,CAAC;QAK3C;;;WAGG;QACK,sBAAiB,GAAkB,IAAI,CAAC;QAEhD;;;WAGG;QACK,oBAAe,GAAkB,IAAI,CAAC;QAY5C,MAAM,QAAQ,GAA0B;YACtC,2BAA2B,EAAE,MAAM,CAAC,2BAA2B,IAAI,IAAI;YACvE,uBAAuB,EAAE,MAAM,CAAC,uBAAuB,IAAI,IAAI;SAChE,CAAC;QAEF,IAAI,CAAC,eAAe,GAAG,IAAI,eAAe,CAAC,QAAQ,CAAC,CAAC;QAErD,4EAA4E;QAC5E,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,wBAAwB,EAAE,CAAC,kBAA0B,EAAE,EAAE;YAC/E,MAAM,UAAU,GACd,IAAI,CAAC,iBAAiB,KAAK,IAAI,IAAI,IAAI,CAAC,eAAe,KAAK,IAAI;gBAC9D,CAAC,CAAC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,iBAAiB;gBAC/C,CAAC,CAAC,CAAC,CAAC;YAER,MAAM,KAAK,GAAsB;gBAC/B,UAAU,EAAE,EAAE,EAAI,yCAAyC;gBAC3D,UAAU,EAAE,CAAC;gBACb,UAAU;gBACV,MAAM,EAAE,iBAAiB;aAC1B,CAAC;YAEF,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,KAAK,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;IACL,CAAC;IAED,8EAA8E;IAC9E,oBAAoB;IACpB,8EAA8E;IAE9E;;;;;;;;OAQG;IACI,YAAY,CAAC,KAAe;QACjC,2EAA2E;QAC3E,2DAA2D;QAC3D,MAAM,aAAa,GAAG,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAW,CAAC;QAE9D,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,KAAK,cAAc;gBACjB,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC,SAAS,CAAC;gBACzC,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;gBAC5B,IAAI,CAAC,eAAe,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC;gBACtD,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;gBAC1B,MAAM;YAER,KAAK,YAAY;gBACf,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC,SAAS,CAAC;gBACvC,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC;gBACvD,MAAM;YAER,KAAK,SAAS;gBACZ,2DAA2D;gBAC3D,IAAI,CAAC,eAAe,CAAC,qBAAqB,CAAC,aAAa,CAAC,CAAC;gBAC1D,MAAM;QACV,CAAC;IACH,CAAC;IAED;;;;OAIG;IACI,cAAc,CAAC,MAAuB;QAC3C,+DAA+D;IACjE,CAAC;IAED;;OAEG;IACI,KAAK;QACV,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;QAC9B,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC;QAC5B,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;IAC/B,CAAC;CACF"}
|