@framers/agentos 0.1.75 → 0.1.76
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 +139 -34
- 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,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Plivo telephony provider for AgentOS voice calls.
|
|
3
|
+
*
|
|
4
|
+
* Implements {@link IVoiceCallProvider} using the Plivo Voice REST API v1.
|
|
5
|
+
* Webhook verification uses HMAC-SHA256 with the v3 signature scheme.
|
|
6
|
+
*
|
|
7
|
+
* @module @framers/agentos/voice/providers/plivo
|
|
8
|
+
*/
|
|
9
|
+
import { createHmac, randomUUID } from 'node:crypto';
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// PlivoVoiceProvider
|
|
12
|
+
// ============================================================================
|
|
13
|
+
/**
|
|
14
|
+
* Plivo voice call provider.
|
|
15
|
+
*
|
|
16
|
+
* Uses the Plivo REST API v1 for outbound call control and HMAC-SHA256
|
|
17
|
+
* for inbound webhook signature verification (v3 signature scheme).
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* const provider = new PlivoVoiceProvider({
|
|
22
|
+
* authId: process.env.PLIVO_AUTH_ID!,
|
|
23
|
+
* authToken: process.env.PLIVO_AUTH_TOKEN!,
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export class PlivoVoiceProvider {
|
|
28
|
+
constructor(config) {
|
|
29
|
+
this.name = 'plivo';
|
|
30
|
+
this.config = config;
|
|
31
|
+
this.baseUrl = 'https://api.plivo.com/v1';
|
|
32
|
+
this.authHeader =
|
|
33
|
+
'Basic ' + Buffer.from(`${config.authId}:${config.authToken}`).toString('base64');
|
|
34
|
+
this.fetch = config.fetchImpl ?? globalThis.fetch;
|
|
35
|
+
}
|
|
36
|
+
// ── Webhook ───────────────────────────────────────────────────────────────
|
|
37
|
+
/**
|
|
38
|
+
* Verify an incoming Plivo webhook request using HMAC-SHA256 (v3 scheme).
|
|
39
|
+
*
|
|
40
|
+
* Plivo signs the concatenation of the full request URL and the nonce
|
|
41
|
+
* header value. The resulting base64-encoded digest is compared against
|
|
42
|
+
* the `x-plivo-signature-v3` header.
|
|
43
|
+
*/
|
|
44
|
+
verifyWebhook(ctx) {
|
|
45
|
+
const nonce = ctx.headers['x-plivo-signature-v3-nonce'];
|
|
46
|
+
const signature = ctx.headers['x-plivo-signature-v3'];
|
|
47
|
+
if (Array.isArray(nonce) || Array.isArray(signature)) {
|
|
48
|
+
return { valid: false, error: 'Duplicate Plivo signature headers' };
|
|
49
|
+
}
|
|
50
|
+
const nonceStr = nonce ?? '';
|
|
51
|
+
const data = ctx.url + nonceStr;
|
|
52
|
+
const expected = createHmac('sha256', this.config.authToken)
|
|
53
|
+
.update(data)
|
|
54
|
+
.digest('base64');
|
|
55
|
+
const valid = expected === signature;
|
|
56
|
+
return { valid };
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Parse a Plivo webhook body into normalized {@link NormalizedCallEvent}s.
|
|
60
|
+
*
|
|
61
|
+
* Supports both URL-encoded and JSON request bodies. Maps `CallStatus`
|
|
62
|
+
* parameter values to call lifecycle events.
|
|
63
|
+
*/
|
|
64
|
+
parseWebhookEvent(ctx) {
|
|
65
|
+
const body = ctx.body.toString();
|
|
66
|
+
let params;
|
|
67
|
+
// Plivo sends URL-encoded bodies for most events; try JSON as fallback.
|
|
68
|
+
try {
|
|
69
|
+
if (body.trimStart().startsWith('{')) {
|
|
70
|
+
const obj = JSON.parse(body);
|
|
71
|
+
params = new URLSearchParams(obj);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
params = new URLSearchParams(body);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
params = new URLSearchParams(body);
|
|
79
|
+
}
|
|
80
|
+
const callUuid = params.get('CallUUID') ?? params.get('call_uuid') ?? '';
|
|
81
|
+
const callStatus = params.get('CallStatus') ?? params.get('call_status') ?? '';
|
|
82
|
+
const digits = params.get('Digits');
|
|
83
|
+
const timestamp = Date.now();
|
|
84
|
+
const events = [];
|
|
85
|
+
const base = () => ({
|
|
86
|
+
eventId: randomUUID(),
|
|
87
|
+
providerCallId: callUuid,
|
|
88
|
+
timestamp,
|
|
89
|
+
});
|
|
90
|
+
switch (callStatus) {
|
|
91
|
+
case 'ringing':
|
|
92
|
+
events.push({ ...base(), kind: 'call-ringing' });
|
|
93
|
+
break;
|
|
94
|
+
case 'in-progress':
|
|
95
|
+
events.push({ ...base(), kind: 'call-answered' });
|
|
96
|
+
break;
|
|
97
|
+
case 'completed':
|
|
98
|
+
events.push({ ...base(), kind: 'call-completed' });
|
|
99
|
+
break;
|
|
100
|
+
case 'busy':
|
|
101
|
+
events.push({ ...base(), kind: 'call-busy' });
|
|
102
|
+
break;
|
|
103
|
+
case 'no-answer':
|
|
104
|
+
events.push({ ...base(), kind: 'call-no-answer' });
|
|
105
|
+
break;
|
|
106
|
+
case 'failed':
|
|
107
|
+
events.push({ ...base(), kind: 'call-failed' });
|
|
108
|
+
break;
|
|
109
|
+
default:
|
|
110
|
+
// initiated / queued / etc. — no normalized event emitted
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
if (digits != null && digits !== '') {
|
|
114
|
+
events.push({ ...base(), kind: 'call-dtmf', digit: digits });
|
|
115
|
+
}
|
|
116
|
+
return { events };
|
|
117
|
+
}
|
|
118
|
+
// ── Call Control ──────────────────────────────────────────────────────────
|
|
119
|
+
/**
|
|
120
|
+
* Initiate an outbound call via the Plivo Call API.
|
|
121
|
+
*
|
|
122
|
+
* POSTs a JSON body to `/Account/{authId}/Call/` with the caller, callee,
|
|
123
|
+
* and answer URL. Returns the `request_uuid` as the provider call ID.
|
|
124
|
+
*/
|
|
125
|
+
async initiateCall(input) {
|
|
126
|
+
const url = `${this.baseUrl}/Account/${this.config.authId}/Call/`;
|
|
127
|
+
const response = await this.fetch(url, {
|
|
128
|
+
method: 'POST',
|
|
129
|
+
headers: {
|
|
130
|
+
Authorization: this.authHeader,
|
|
131
|
+
'Content-Type': 'application/json',
|
|
132
|
+
},
|
|
133
|
+
body: JSON.stringify({
|
|
134
|
+
from: input.fromNumber,
|
|
135
|
+
to: input.toNumber,
|
|
136
|
+
answer_url: input.webhookUrl,
|
|
137
|
+
answer_method: 'POST',
|
|
138
|
+
}),
|
|
139
|
+
});
|
|
140
|
+
if (!response.ok) {
|
|
141
|
+
const text = await response.text().catch(() => String(response.status));
|
|
142
|
+
return { providerCallId: '', success: false, error: `Plivo error ${response.status}: ${text}` };
|
|
143
|
+
}
|
|
144
|
+
const data = (await response.json());
|
|
145
|
+
return { providerCallId: data.request_uuid, success: true };
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Hang up an active call using the Plivo Call DELETE endpoint.
|
|
149
|
+
*/
|
|
150
|
+
async hangupCall(input) {
|
|
151
|
+
const url = `${this.baseUrl}/Account/${this.config.authId}/Call/${input.providerCallId}/`;
|
|
152
|
+
await this.fetch(url, {
|
|
153
|
+
method: 'DELETE',
|
|
154
|
+
headers: {
|
|
155
|
+
Authorization: this.authHeader,
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Speak text into a live call using the Plivo Speak API.
|
|
161
|
+
*
|
|
162
|
+
* Defaults to `WOMAN` voice and `en-US` language when not specified.
|
|
163
|
+
*/
|
|
164
|
+
async playTts(input) {
|
|
165
|
+
const url = `${this.baseUrl}/Account/${this.config.authId}/Call/${input.providerCallId}/Speak/`;
|
|
166
|
+
await this.fetch(url, {
|
|
167
|
+
method: 'POST',
|
|
168
|
+
headers: {
|
|
169
|
+
Authorization: this.authHeader,
|
|
170
|
+
'Content-Type': 'application/json',
|
|
171
|
+
},
|
|
172
|
+
body: JSON.stringify({
|
|
173
|
+
text: input.text,
|
|
174
|
+
voice: input.voice ?? 'WOMAN',
|
|
175
|
+
language: 'en-US',
|
|
176
|
+
}),
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=plivo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plivo.js","sourceRoot":"","sources":["../../../src/voice/providers/plivo.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Telnyx telephony provider for AgentOS voice calls.
|
|
3
|
+
*
|
|
4
|
+
* Implements {@link IVoiceCallProvider} using the Telnyx Call Control v2 API.
|
|
5
|
+
* Webhook verification uses Ed25519 public key verification as specified in
|
|
6
|
+
* the Telnyx security documentation.
|
|
7
|
+
*
|
|
8
|
+
* @module @framers/agentos/voice/providers/telnyx
|
|
9
|
+
*/
|
|
10
|
+
import type { IVoiceCallProvider, InitiateCallInput, InitiateCallResult, HangupCallInput, PlayTtsInput } from '../IVoiceCallProvider.js';
|
|
11
|
+
import type { WebhookContext, WebhookVerificationResult, WebhookParseResult } from '../types.js';
|
|
12
|
+
/** Configuration for {@link TelnyxVoiceProvider}. */
|
|
13
|
+
export interface TelnyxVoiceProviderConfig {
|
|
14
|
+
/** Telnyx API key (starts with "KEY"). */
|
|
15
|
+
apiKey: string;
|
|
16
|
+
/** Telnyx connection/application ID for call routing. */
|
|
17
|
+
connectionId: string;
|
|
18
|
+
/**
|
|
19
|
+
* Base64-encoded DER-encoded SPKI Ed25519 public key for webhook verification.
|
|
20
|
+
* When omitted, webhook verification is skipped.
|
|
21
|
+
*/
|
|
22
|
+
publicKey?: string;
|
|
23
|
+
/**
|
|
24
|
+
* Optional fetch override — inject a mock in tests.
|
|
25
|
+
* Defaults to the global `fetch`.
|
|
26
|
+
*/
|
|
27
|
+
fetchImpl?: typeof fetch;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Telnyx voice call provider.
|
|
31
|
+
*
|
|
32
|
+
* Uses the Telnyx Call Control v2 API for outbound call initiation and
|
|
33
|
+
* in-call actions (hangup, speak). Webhook verification uses Ed25519 public
|
|
34
|
+
* key signing.
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* ```typescript
|
|
38
|
+
* const provider = new TelnyxVoiceProvider({
|
|
39
|
+
* apiKey: process.env.TELNYX_API_KEY!,
|
|
40
|
+
* connectionId: process.env.TELNYX_CONNECTION_ID!,
|
|
41
|
+
* publicKey: process.env.TELNYX_PUBLIC_KEY, // optional
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare class TelnyxVoiceProvider implements IVoiceCallProvider {
|
|
46
|
+
readonly name: "telnyx";
|
|
47
|
+
private readonly config;
|
|
48
|
+
private readonly baseUrl;
|
|
49
|
+
private readonly authHeader;
|
|
50
|
+
private readonly fetch;
|
|
51
|
+
constructor(config: TelnyxVoiceProviderConfig);
|
|
52
|
+
/**
|
|
53
|
+
* Verify an incoming Telnyx webhook using Ed25519 signature verification.
|
|
54
|
+
*
|
|
55
|
+
* Telnyx signs `{timestamp}|{rawBody}` with the configured Ed25519 private key.
|
|
56
|
+
* The matching public key is provided as a base64-encoded DER SPKI blob.
|
|
57
|
+
*
|
|
58
|
+
* If no public key is configured, the check is skipped and `valid: true`
|
|
59
|
+
* is returned so the provider can operate without cryptographic validation
|
|
60
|
+
* during initial development.
|
|
61
|
+
*/
|
|
62
|
+
verifyWebhook(ctx: WebhookContext): WebhookVerificationResult;
|
|
63
|
+
/**
|
|
64
|
+
* Parse a Telnyx webhook JSON body into normalized {@link NormalizedCallEvent}s.
|
|
65
|
+
*
|
|
66
|
+
* Handles Telnyx Call Control event types:
|
|
67
|
+
* - `call.initiated` → `call-ringing`
|
|
68
|
+
* - `call.answered` → `call-answered`
|
|
69
|
+
* - `call.hangup` → `call-completed` or `call-hangup-user` based on hangup_cause
|
|
70
|
+
* - `call.dtmf.received` → `call-dtmf`
|
|
71
|
+
* - `call.machine.detection.ended` with `result === 'machine'` → `call-voicemail`
|
|
72
|
+
*/
|
|
73
|
+
parseWebhookEvent(ctx: WebhookContext): WebhookParseResult;
|
|
74
|
+
/**
|
|
75
|
+
* Initiate an outbound call via the Telnyx Call Control v2 API.
|
|
76
|
+
*
|
|
77
|
+
* POSTs to `/calls` with a JSON body. The `mediaStreamUrl` (if provided)
|
|
78
|
+
* is stored internally for use after the call is answered — it is NOT sent
|
|
79
|
+
* in the initial call creation request.
|
|
80
|
+
*/
|
|
81
|
+
initiateCall(input: InitiateCallInput): Promise<InitiateCallResult>;
|
|
82
|
+
/**
|
|
83
|
+
* Hang up an active call via the Telnyx Call Control hangup action.
|
|
84
|
+
*/
|
|
85
|
+
hangupCall(input: HangupCallInput): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Speak text into a live call using Telnyx's text-to-speech speak action.
|
|
88
|
+
*
|
|
89
|
+
* Defaults to `female` voice and `en-US` language when not specified.
|
|
90
|
+
*/
|
|
91
|
+
playTts(input: PlayTtsInput): Promise<void>;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=telnyx.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telnyx.d.ts","sourceRoot":"","sources":["../../../src/voice/providers/telnyx.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACb,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EACV,cAAc,EACd,yBAAyB,EACzB,kBAAkB,EAEnB,MAAM,aAAa,CAAC;AAMrB,qDAAqD;AACrD,MAAM,WAAW,yBAAyB;IACxC,0CAA0C;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,YAAY,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAyBD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,mBAAoB,YAAW,kBAAkB;IAC5D,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAU;IAElC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAEzB,MAAM,EAAE,yBAAyB;IAS7C;;;;;;;;;OASG;IACH,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,yBAAyB;IAgC7D;;;;;;;;;OASG;IACH,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,kBAAkB;IAyD1D;;;;;;OAMG;IACG,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA0BzE;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAavD;;;;OAIG;IACG,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAgBlD"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Telnyx telephony provider for AgentOS voice calls.
|
|
3
|
+
*
|
|
4
|
+
* Implements {@link IVoiceCallProvider} using the Telnyx Call Control v2 API.
|
|
5
|
+
* Webhook verification uses Ed25519 public key verification as specified in
|
|
6
|
+
* the Telnyx security documentation.
|
|
7
|
+
*
|
|
8
|
+
* @module @framers/agentos/voice/providers/telnyx
|
|
9
|
+
*/
|
|
10
|
+
import { verify } from 'node:crypto';
|
|
11
|
+
import { randomUUID } from 'node:crypto';
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// TelnyxVoiceProvider
|
|
14
|
+
// ============================================================================
|
|
15
|
+
/**
|
|
16
|
+
* Telnyx voice call provider.
|
|
17
|
+
*
|
|
18
|
+
* Uses the Telnyx Call Control v2 API for outbound call initiation and
|
|
19
|
+
* in-call actions (hangup, speak). Webhook verification uses Ed25519 public
|
|
20
|
+
* key signing.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const provider = new TelnyxVoiceProvider({
|
|
25
|
+
* apiKey: process.env.TELNYX_API_KEY!,
|
|
26
|
+
* connectionId: process.env.TELNYX_CONNECTION_ID!,
|
|
27
|
+
* publicKey: process.env.TELNYX_PUBLIC_KEY, // optional
|
|
28
|
+
* });
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export class TelnyxVoiceProvider {
|
|
32
|
+
constructor(config) {
|
|
33
|
+
this.name = 'telnyx';
|
|
34
|
+
this.config = config;
|
|
35
|
+
this.baseUrl = 'https://api.telnyx.com/v2';
|
|
36
|
+
this.authHeader = `Bearer ${config.apiKey}`;
|
|
37
|
+
this.fetch = config.fetchImpl ?? globalThis.fetch;
|
|
38
|
+
}
|
|
39
|
+
// ── Webhook ───────────────────────────────────────────────────────────────
|
|
40
|
+
/**
|
|
41
|
+
* Verify an incoming Telnyx webhook using Ed25519 signature verification.
|
|
42
|
+
*
|
|
43
|
+
* Telnyx signs `{timestamp}|{rawBody}` with the configured Ed25519 private key.
|
|
44
|
+
* The matching public key is provided as a base64-encoded DER SPKI blob.
|
|
45
|
+
*
|
|
46
|
+
* If no public key is configured, the check is skipped and `valid: true`
|
|
47
|
+
* is returned so the provider can operate without cryptographic validation
|
|
48
|
+
* during initial development.
|
|
49
|
+
*/
|
|
50
|
+
verifyWebhook(ctx) {
|
|
51
|
+
if (!this.config.publicKey) {
|
|
52
|
+
return { valid: true };
|
|
53
|
+
}
|
|
54
|
+
const timestamp = ctx.headers['x-telnyx-timestamp'];
|
|
55
|
+
const sigHeader = ctx.headers['x-telnyx-signature-ed25519'];
|
|
56
|
+
if (!timestamp || Array.isArray(timestamp) || !sigHeader || Array.isArray(sigHeader)) {
|
|
57
|
+
return { valid: false, error: 'Missing Telnyx signature headers' };
|
|
58
|
+
}
|
|
59
|
+
const signature = Buffer.from(sigHeader, 'base64');
|
|
60
|
+
const payload = Buffer.from(`${timestamp}|${ctx.body.toString()}`);
|
|
61
|
+
try {
|
|
62
|
+
const valid = verify(null, payload, {
|
|
63
|
+
key: Buffer.from(this.config.publicKey, 'base64'),
|
|
64
|
+
format: 'der',
|
|
65
|
+
type: 'spki',
|
|
66
|
+
}, signature);
|
|
67
|
+
return { valid };
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return { valid: false, error: 'Verification failed' };
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Parse a Telnyx webhook JSON body into normalized {@link NormalizedCallEvent}s.
|
|
75
|
+
*
|
|
76
|
+
* Handles Telnyx Call Control event types:
|
|
77
|
+
* - `call.initiated` → `call-ringing`
|
|
78
|
+
* - `call.answered` → `call-answered`
|
|
79
|
+
* - `call.hangup` → `call-completed` or `call-hangup-user` based on hangup_cause
|
|
80
|
+
* - `call.dtmf.received` → `call-dtmf`
|
|
81
|
+
* - `call.machine.detection.ended` with `result === 'machine'` → `call-voicemail`
|
|
82
|
+
*/
|
|
83
|
+
parseWebhookEvent(ctx) {
|
|
84
|
+
let parsed;
|
|
85
|
+
try {
|
|
86
|
+
parsed = JSON.parse(ctx.body.toString());
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return { events: [] };
|
|
90
|
+
}
|
|
91
|
+
const { event_type, payload } = parsed.data;
|
|
92
|
+
const providerCallId = payload.call_control_id ?? '';
|
|
93
|
+
const timestamp = Date.now();
|
|
94
|
+
const events = [];
|
|
95
|
+
const base = () => ({ eventId: randomUUID(), providerCallId, timestamp });
|
|
96
|
+
switch (event_type) {
|
|
97
|
+
case 'call.initiated':
|
|
98
|
+
events.push({ ...base(), kind: 'call-ringing' });
|
|
99
|
+
break;
|
|
100
|
+
case 'call.answered':
|
|
101
|
+
events.push({ ...base(), kind: 'call-answered' });
|
|
102
|
+
break;
|
|
103
|
+
case 'call.hangup': {
|
|
104
|
+
// Distinguish user-initiated hangup from normal completion
|
|
105
|
+
const cause = payload.hangup_cause ?? '';
|
|
106
|
+
const kind = cause === 'user_busy' || cause === 'normal_clearing' || cause === 'originator_cancel'
|
|
107
|
+
? 'call-hangup-user'
|
|
108
|
+
: 'call-completed';
|
|
109
|
+
events.push({ ...base(), kind });
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case 'call.dtmf.received': {
|
|
113
|
+
const digit = payload.digit ?? '';
|
|
114
|
+
events.push({ ...base(), kind: 'call-dtmf', digit });
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
case 'call.machine.detection.ended':
|
|
118
|
+
if (payload.result === 'machine') {
|
|
119
|
+
events.push({ ...base(), kind: 'call-voicemail' });
|
|
120
|
+
}
|
|
121
|
+
break;
|
|
122
|
+
default:
|
|
123
|
+
// Unrecognized event type — emit nothing
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
return { events };
|
|
127
|
+
}
|
|
128
|
+
// ── Call Control ──────────────────────────────────────────────────────────
|
|
129
|
+
/**
|
|
130
|
+
* Initiate an outbound call via the Telnyx Call Control v2 API.
|
|
131
|
+
*
|
|
132
|
+
* POSTs to `/calls` with a JSON body. The `mediaStreamUrl` (if provided)
|
|
133
|
+
* is stored internally for use after the call is answered — it is NOT sent
|
|
134
|
+
* in the initial call creation request.
|
|
135
|
+
*/
|
|
136
|
+
async initiateCall(input) {
|
|
137
|
+
const url = `${this.baseUrl}/calls`;
|
|
138
|
+
const response = await this.fetch(url, {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: {
|
|
141
|
+
Authorization: this.authHeader,
|
|
142
|
+
'Content-Type': 'application/json',
|
|
143
|
+
},
|
|
144
|
+
body: JSON.stringify({
|
|
145
|
+
connection_id: this.config.connectionId,
|
|
146
|
+
to: input.toNumber,
|
|
147
|
+
from: input.fromNumber,
|
|
148
|
+
webhook_url: input.webhookUrl,
|
|
149
|
+
}),
|
|
150
|
+
});
|
|
151
|
+
if (!response.ok) {
|
|
152
|
+
const text = await response.text().catch(() => String(response.status));
|
|
153
|
+
return { providerCallId: '', success: false, error: `Telnyx error ${response.status}: ${text}` };
|
|
154
|
+
}
|
|
155
|
+
const data = (await response.json());
|
|
156
|
+
return { providerCallId: data.data.call_control_id, success: true };
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Hang up an active call via the Telnyx Call Control hangup action.
|
|
160
|
+
*/
|
|
161
|
+
async hangupCall(input) {
|
|
162
|
+
const url = `${this.baseUrl}/calls/${input.providerCallId}/actions/hangup`;
|
|
163
|
+
await this.fetch(url, {
|
|
164
|
+
method: 'POST',
|
|
165
|
+
headers: {
|
|
166
|
+
Authorization: this.authHeader,
|
|
167
|
+
'Content-Type': 'application/json',
|
|
168
|
+
},
|
|
169
|
+
body: JSON.stringify({}),
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Speak text into a live call using Telnyx's text-to-speech speak action.
|
|
174
|
+
*
|
|
175
|
+
* Defaults to `female` voice and `en-US` language when not specified.
|
|
176
|
+
*/
|
|
177
|
+
async playTts(input) {
|
|
178
|
+
const url = `${this.baseUrl}/calls/${input.providerCallId}/actions/speak`;
|
|
179
|
+
await this.fetch(url, {
|
|
180
|
+
method: 'POST',
|
|
181
|
+
headers: {
|
|
182
|
+
Authorization: this.authHeader,
|
|
183
|
+
'Content-Type': 'application/json',
|
|
184
|
+
},
|
|
185
|
+
body: JSON.stringify({
|
|
186
|
+
payload: input.text,
|
|
187
|
+
voice: input.voice ?? 'female',
|
|
188
|
+
language: 'en-US',
|
|
189
|
+
}),
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=telnyx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telnyx.js","sourceRoot":"","sources":["../../../src/voice/providers/telnyx.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
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 type { IVoiceCallProvider, InitiateCallInput, InitiateCallResult, HangupCallInput, PlayTtsInput } from '../IVoiceCallProvider.js';
|
|
10
|
+
import type { WebhookContext, WebhookVerificationResult, WebhookParseResult } from '../types.js';
|
|
11
|
+
/** Configuration for {@link TwilioVoiceProvider}. */
|
|
12
|
+
export interface TwilioVoiceProviderConfig {
|
|
13
|
+
/** Twilio Account SID (starts with "AC"). */
|
|
14
|
+
accountSid: string;
|
|
15
|
+
/** Twilio Auth Token. */
|
|
16
|
+
authToken: string;
|
|
17
|
+
/**
|
|
18
|
+
* Optional fetch override — inject a mock in tests.
|
|
19
|
+
* Defaults to the global `fetch`.
|
|
20
|
+
*/
|
|
21
|
+
fetchImpl?: typeof fetch;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Twilio voice call provider.
|
|
25
|
+
*
|
|
26
|
+
* Uses the Twilio REST API 2010-04-01 for outbound call control and
|
|
27
|
+
* HMAC-SHA1 for inbound webhook signature verification.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const provider = new TwilioVoiceProvider({
|
|
32
|
+
* accountSid: process.env.TWILIO_ACCOUNT_SID!,
|
|
33
|
+
* authToken: process.env.TWILIO_AUTH_TOKEN!,
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare class TwilioVoiceProvider implements IVoiceCallProvider {
|
|
38
|
+
readonly name: "twilio";
|
|
39
|
+
private readonly config;
|
|
40
|
+
private readonly baseUrl;
|
|
41
|
+
private readonly authHeader;
|
|
42
|
+
private readonly fetch;
|
|
43
|
+
constructor(config: TwilioVoiceProviderConfig);
|
|
44
|
+
/**
|
|
45
|
+
* Verify an incoming Twilio webhook request using HMAC-SHA1.
|
|
46
|
+
*
|
|
47
|
+
* Twilio constructs the signed data by appending all POST params (sorted
|
|
48
|
+
* alphabetically) as key+value pairs directly to the full request URL.
|
|
49
|
+
*/
|
|
50
|
+
verifyWebhook(ctx: WebhookContext): WebhookVerificationResult;
|
|
51
|
+
/**
|
|
52
|
+
* Parse a Twilio webhook body into normalized {@link NormalizedCallEvent}s.
|
|
53
|
+
*
|
|
54
|
+
* Handles call status transitions (ringing, in-progress, completed, failed,
|
|
55
|
+
* busy, no-answer, canceled) and DTMF input.
|
|
56
|
+
*/
|
|
57
|
+
parseWebhookEvent(ctx: WebhookContext): WebhookParseResult;
|
|
58
|
+
/**
|
|
59
|
+
* Initiate an outbound call via the Twilio Calls API.
|
|
60
|
+
*
|
|
61
|
+
* Posts to `/Accounts/{accountSid}/Calls.json` with form-encoded body.
|
|
62
|
+
* All four status callback events (initiated, ringing, answered, completed)
|
|
63
|
+
* are requested.
|
|
64
|
+
*/
|
|
65
|
+
initiateCall(input: InitiateCallInput): Promise<InitiateCallResult>;
|
|
66
|
+
/**
|
|
67
|
+
* Hang up an active call by posting `Status=completed`.
|
|
68
|
+
*/
|
|
69
|
+
hangupCall(input: HangupCallInput): Promise<void>;
|
|
70
|
+
/**
|
|
71
|
+
* Inject TTS into a live call using a TwiML `<Say>` verb.
|
|
72
|
+
*
|
|
73
|
+
* Sends a `Twiml` parameter containing a minimal `<Response><Say>` document.
|
|
74
|
+
* The optional `voice` attribute maps to Twilio's built-in voice names
|
|
75
|
+
* (e.g., `alice`, `Polly.Joanna`).
|
|
76
|
+
*/
|
|
77
|
+
playTts(input: PlayTtsInput): Promise<void>;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=twilio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"twilio.d.ts","sourceRoot":"","sources":["../../../src/voice/providers/twilio.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,KAAK,EACV,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACb,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EACV,cAAc,EACd,yBAAyB,EACzB,kBAAkB,EAEnB,MAAM,aAAa,CAAC;AAMrB,qDAAqD;AACrD,MAAM,WAAW,yBAAyB;IACxC,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAMD;;;;;;;;;;;;;GAaG;AACH,qBAAa,mBAAoB,YAAW,kBAAkB;IAC5D,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAU;IAElC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IACnD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAEzB,MAAM,EAAE,yBAAyB;IAW7C;;;;;OAKG;IACH,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,yBAAyB;IAyB7D;;;;;OAKG;IACH,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,kBAAkB;IAyD1D;;;;;;OAMG;IACG,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAgCzE;;OAEG;IACG,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAavD;;;;;;OAMG;IACG,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAelD"}
|