@framers/agentos 0.1.111 → 0.1.112
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/dist/voice/CallManager.d.ts.map +1 -1
- package/dist/voice/CallManager.js +9 -1
- package/dist/voice/CallManager.js.map +1 -1
- package/dist/voice/MediaStreamParser.d.ts +115 -6
- package/dist/voice/MediaStreamParser.d.ts.map +1 -1
- package/dist/voice/MediaStreamParser.js +44 -0
- package/dist/voice/MediaStreamParser.js.map +1 -1
- package/dist/voice/TelephonyStreamTransport.d.ts +112 -20
- package/dist/voice/TelephonyStreamTransport.d.ts.map +1 -1
- package/dist/voice/TelephonyStreamTransport.js +136 -30
- package/dist/voice/TelephonyStreamTransport.js.map +1 -1
- package/dist/voice/parsers/PlivoMediaStreamParser.d.ts +64 -6
- package/dist/voice/parsers/PlivoMediaStreamParser.d.ts.map +1 -1
- package/dist/voice/parsers/PlivoMediaStreamParser.js +67 -6
- package/dist/voice/parsers/PlivoMediaStreamParser.js.map +1 -1
- package/dist/voice/parsers/TelnyxMediaStreamParser.d.ts +55 -8
- package/dist/voice/parsers/TelnyxMediaStreamParser.d.ts.map +1 -1
- package/dist/voice/parsers/TelnyxMediaStreamParser.js +60 -9
- package/dist/voice/parsers/TelnyxMediaStreamParser.js.map +1 -1
- package/dist/voice/parsers/TwilioMediaStreamParser.d.ts +73 -11
- package/dist/voice/parsers/TwilioMediaStreamParser.d.ts.map +1 -1
- package/dist/voice/parsers/TwilioMediaStreamParser.js +81 -12
- package/dist/voice/parsers/TwilioMediaStreamParser.js.map +1 -1
- package/dist/voice/providers/plivo.d.ts +108 -12
- package/dist/voice/providers/plivo.d.ts.map +1 -1
- package/dist/voice/providers/plivo.js +106 -9
- package/dist/voice/providers/plivo.js.map +1 -1
- package/dist/voice/providers/telnyx.d.ts +110 -20
- package/dist/voice/providers/telnyx.d.ts.map +1 -1
- package/dist/voice/providers/telnyx.js +111 -20
- package/dist/voice/providers/telnyx.js.map +1 -1
- package/dist/voice/providers/twilio.d.ts +91 -13
- package/dist/voice/providers/twilio.d.ts.map +1 -1
- package/dist/voice/providers/twilio.js +94 -14
- package/dist/voice/providers/twilio.js.map +1 -1
- package/dist/voice/twiml.d.ts +70 -12
- package/dist/voice/twiml.d.ts.map +1 -1
- package/dist/voice/twiml.js +70 -12
- package/dist/voice/twiml.js.map +1 -1
- package/dist/voice/types.d.ts +142 -15
- package/dist/voice/types.d.ts.map +1 -1
- package/dist/voice/types.js +34 -3
- package/dist/voice/types.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"telnyx.js","sourceRoot":"","sources":["../../../src/voice/providers/telnyx.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"telnyx.js","sourceRoot":"","sources":["../../../src/voice/providers/telnyx.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,20 +2,60 @@
|
|
|
2
2
|
* @fileoverview Twilio telephony provider for AgentOS voice calls.
|
|
3
3
|
*
|
|
4
4
|
* Implements {@link IVoiceCallProvider} using the Twilio REST API v2010-04-01.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
|
+
* ## REST API contract
|
|
7
|
+
*
|
|
8
|
+
* | Operation | Method | Endpoint | Body format |
|
|
9
|
+
* |----------------|--------|---------------------------------------------------|-----------------|
|
|
10
|
+
* | Initiate call | POST | `/2010-04-01/Accounts/{sid}/Calls.json` | form-encoded |
|
|
11
|
+
* | Hangup call | POST | `/2010-04-01/Accounts/{sid}/Calls/{callSid}.json` | form-encoded |
|
|
12
|
+
* | Play TTS | POST | `/2010-04-01/Accounts/{sid}/Calls/{callSid}.json` | form-encoded |
|
|
13
|
+
*
|
|
14
|
+
* All requests use HTTP Basic authentication: `Authorization: Basic base64(accountSid:authToken)`.
|
|
15
|
+
* Request bodies are `application/x-www-form-urlencoded` (not JSON), which is
|
|
16
|
+
* Twilio's legacy convention for the 2010-04-01 API.
|
|
17
|
+
*
|
|
18
|
+
* ## Webhook verification: HMAC-SHA1
|
|
19
|
+
*
|
|
20
|
+
* Twilio signs every webhook request using HMAC-SHA1. The verification algorithm:
|
|
21
|
+
*
|
|
22
|
+
* 1. Start with the **full request URL** (including scheme, host, path, and any query string).
|
|
23
|
+
* 2. Parse the POST body as form-encoded key-value pairs.
|
|
24
|
+
* 3. Sort the parameters **alphabetically by key name**.
|
|
25
|
+
* 4. Concatenate each key+value pair (no separator) directly to the URL string.
|
|
26
|
+
* 5. Compute `HMAC-SHA1(authToken, concatenatedString)`.
|
|
27
|
+
* 6. Base64-encode the HMAC digest.
|
|
28
|
+
* 7. Compare the result with the `X-Twilio-Signature` request header.
|
|
29
|
+
*
|
|
30
|
+
* If the computed signature matches the header, the request is authentic.
|
|
31
|
+
*
|
|
32
|
+
* ## Event mapping table
|
|
33
|
+
*
|
|
34
|
+
* | Twilio `CallStatus` | Normalised `kind` |
|
|
35
|
+
* |---------------------|----------------------|
|
|
36
|
+
* | `ringing` | `call-ringing` |
|
|
37
|
+
* | `in-progress` | `call-answered` |
|
|
38
|
+
* | `completed` | `call-completed` |
|
|
39
|
+
* | `failed` | `call-failed` |
|
|
40
|
+
* | `busy` | `call-busy` |
|
|
41
|
+
* | `no-answer` | `call-no-answer` |
|
|
42
|
+
* | `canceled` | `call-hangup-user` |
|
|
43
|
+
* | (+ `Digits` param) | `call-dtmf` |
|
|
6
44
|
*
|
|
7
45
|
* @module @framers/agentos/voice/providers/twilio
|
|
8
46
|
*/
|
|
9
47
|
import type { IVoiceCallProvider, InitiateCallInput, InitiateCallResult, HangupCallInput, PlayTtsInput } from '../IVoiceCallProvider.js';
|
|
10
48
|
import type { WebhookContext, WebhookVerificationResult, WebhookParseResult } from '../types.js';
|
|
11
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Configuration for {@link TwilioVoiceProvider}.
|
|
51
|
+
*/
|
|
12
52
|
export interface TwilioVoiceProviderConfig {
|
|
13
53
|
/** Twilio Account SID (starts with "AC"). */
|
|
14
54
|
accountSid: string;
|
|
15
|
-
/** Twilio Auth Token. */
|
|
55
|
+
/** Twilio Auth Token (used for both API auth and webhook HMAC verification). */
|
|
16
56
|
authToken: string;
|
|
17
57
|
/**
|
|
18
|
-
* Optional fetch override
|
|
58
|
+
* Optional fetch implementation override -- inject a mock in tests.
|
|
19
59
|
* Defaults to the global `fetch`.
|
|
20
60
|
*/
|
|
21
61
|
fetchImpl?: typeof fetch;
|
|
@@ -35,44 +75,82 @@ export interface TwilioVoiceProviderConfig {
|
|
|
35
75
|
* ```
|
|
36
76
|
*/
|
|
37
77
|
export declare class TwilioVoiceProvider implements IVoiceCallProvider {
|
|
78
|
+
/** Provider identifier, always `'twilio'`. */
|
|
38
79
|
readonly name: "twilio";
|
|
80
|
+
/** Immutable configuration snapshot. */
|
|
39
81
|
private readonly config;
|
|
82
|
+
/** Base URL for the Twilio REST API (2010-04-01 version). */
|
|
40
83
|
private readonly baseUrl;
|
|
84
|
+
/** Pre-computed `Authorization: Basic ...` header value. */
|
|
41
85
|
private readonly authHeader;
|
|
86
|
+
/** HTTP fetch implementation (injectable for testing). */
|
|
42
87
|
private readonly fetch;
|
|
88
|
+
/**
|
|
89
|
+
* @param config - Twilio credentials and optional overrides.
|
|
90
|
+
*/
|
|
43
91
|
constructor(config: TwilioVoiceProviderConfig);
|
|
44
92
|
/**
|
|
45
93
|
* Verify an incoming Twilio webhook request using HMAC-SHA1.
|
|
46
94
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
95
|
+
* ## Algorithm (step by step)
|
|
96
|
+
*
|
|
97
|
+
* 1. Extract the `X-Twilio-Signature` header from the request.
|
|
98
|
+
* 2. Parse the request body as URL-encoded form data.
|
|
99
|
+
* 3. Sort all key-value pairs alphabetically by key.
|
|
100
|
+
* 4. Build the signed string: start with the full URL, then append each
|
|
101
|
+
* key + value (no delimiters between pairs).
|
|
102
|
+
* 5. Compute `HMAC-SHA1` of the signed string using the auth token as the key.
|
|
103
|
+
* 6. Base64-encode the digest and compare it to the header value.
|
|
104
|
+
*
|
|
105
|
+
* @param ctx - Raw webhook request context.
|
|
106
|
+
* @returns Verification result with `valid: true` if the signature matches.
|
|
49
107
|
*/
|
|
50
108
|
verifyWebhook(ctx: WebhookContext): WebhookVerificationResult;
|
|
51
109
|
/**
|
|
52
110
|
* Parse a Twilio webhook body into normalized {@link NormalizedCallEvent}s.
|
|
53
111
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
112
|
+
* Twilio sends webhooks with a form-encoded body containing `CallSid`,
|
|
113
|
+
* `CallStatus`, and optionally `Digits` (for DTMF input from `<Gather>`).
|
|
114
|
+
* Each webhook may produce one or two events (status + optional DTMF).
|
|
115
|
+
*
|
|
116
|
+
* @param ctx - Raw webhook request context.
|
|
117
|
+
* @returns Parsed result containing zero or more normalized events.
|
|
56
118
|
*/
|
|
57
119
|
parseWebhookEvent(ctx: WebhookContext): WebhookParseResult;
|
|
58
120
|
/**
|
|
59
121
|
* Initiate an outbound call via the Twilio Calls API.
|
|
60
122
|
*
|
|
61
|
-
* Posts to `/Accounts/{accountSid}/Calls.json` with form-encoded body
|
|
62
|
-
*
|
|
63
|
-
* are
|
|
123
|
+
* Posts to `/Accounts/{accountSid}/Calls.json` with a **form-encoded** body
|
|
124
|
+
* (not JSON -- this is Twilio's 2010-era API convention). All four status
|
|
125
|
+
* callback events (`initiated`, `ringing`, `answered`, `completed`) are
|
|
126
|
+
* requested so the {@link CallManager} receives the full state progression.
|
|
127
|
+
*
|
|
128
|
+
* @param input - Call initiation parameters (from/to numbers, webhook URLs).
|
|
129
|
+
* @returns Result containing the Twilio `CallSid` on success.
|
|
130
|
+
* @throws Never throws; returns `{ success: false, error: '...' }` on failure.
|
|
64
131
|
*/
|
|
65
132
|
initiateCall(input: InitiateCallInput): Promise<InitiateCallResult>;
|
|
66
133
|
/**
|
|
67
|
-
* Hang up an active call by
|
|
134
|
+
* Hang up an active call by POSTing `Status=completed`.
|
|
135
|
+
*
|
|
136
|
+
* Twilio uses the same Calls resource endpoint for both querying and
|
|
137
|
+
* modifying a call. Setting `Status=completed` instructs Twilio to
|
|
138
|
+
* immediately terminate the call.
|
|
139
|
+
*
|
|
140
|
+
* @param input - Contains the Twilio `CallSid` to hang up.
|
|
68
141
|
*/
|
|
69
142
|
hangupCall(input: HangupCallInput): Promise<void>;
|
|
70
143
|
/**
|
|
71
144
|
* Inject TTS into a live call using a TwiML `<Say>` verb.
|
|
72
145
|
*
|
|
73
|
-
* Sends a `Twiml` parameter containing a minimal `<Response><Say>`
|
|
146
|
+
* Sends a `Twiml` form parameter containing a minimal `<Response><Say>`
|
|
147
|
+
* document. Twilio will parse the TwiML, synthesise the speech, and play
|
|
148
|
+
* it to the caller in real-time.
|
|
149
|
+
*
|
|
74
150
|
* The optional `voice` attribute maps to Twilio's built-in voice names
|
|
75
151
|
* (e.g., `alice`, `Polly.Joanna`).
|
|
152
|
+
*
|
|
153
|
+
* @param input - TTS parameters (text, voice, call ID).
|
|
76
154
|
*/
|
|
77
155
|
playTts(input: PlayTtsInput): Promise<void>;
|
|
78
156
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"twilio.d.ts","sourceRoot":"","sources":["../../../src/voice/providers/twilio.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"twilio.d.ts","sourceRoot":"","sources":["../../../src/voice/providers/twilio.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;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;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,6CAA6C;IAC7C,UAAU,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAMD;;;;;;;;;;;;;GAaG;AACH,qBAAa,mBAAoB,YAAW,kBAAkB;IAC5D,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAG,QAAQ,CAAU;IAElC,wCAAwC;IACxC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4B;IAEnD,6DAA6D;IAC7D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IAEjC,4DAA4D;IAC5D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,0DAA0D;IAC1D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IAErC;;OAEG;gBACS,MAAM,EAAE,yBAAyB;IAY7C;;;;;;;;;;;;;;;OAeG;IACH,aAAa,CAAC,GAAG,EAAE,cAAc,GAAG,yBAAyB;IA0B7D;;;;;;;;;OASG;IACH,iBAAiB,CAAC,GAAG,EAAE,cAAc,GAAG,kBAAkB;IA8D1D;;;;;;;;;;;OAWG;IACG,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAiCzE;;;;;;;;OAQG;IACG,UAAU,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAavD;;;;;;;;;;;OAWG;IACG,OAAO,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAelD"}
|
|
@@ -2,7 +2,45 @@
|
|
|
2
2
|
* @fileoverview Twilio telephony provider for AgentOS voice calls.
|
|
3
3
|
*
|
|
4
4
|
* Implements {@link IVoiceCallProvider} using the Twilio REST API v2010-04-01.
|
|
5
|
-
*
|
|
5
|
+
*
|
|
6
|
+
* ## REST API contract
|
|
7
|
+
*
|
|
8
|
+
* | Operation | Method | Endpoint | Body format |
|
|
9
|
+
* |----------------|--------|---------------------------------------------------|-----------------|
|
|
10
|
+
* | Initiate call | POST | `/2010-04-01/Accounts/{sid}/Calls.json` | form-encoded |
|
|
11
|
+
* | Hangup call | POST | `/2010-04-01/Accounts/{sid}/Calls/{callSid}.json` | form-encoded |
|
|
12
|
+
* | Play TTS | POST | `/2010-04-01/Accounts/{sid}/Calls/{callSid}.json` | form-encoded |
|
|
13
|
+
*
|
|
14
|
+
* All requests use HTTP Basic authentication: `Authorization: Basic base64(accountSid:authToken)`.
|
|
15
|
+
* Request bodies are `application/x-www-form-urlencoded` (not JSON), which is
|
|
16
|
+
* Twilio's legacy convention for the 2010-04-01 API.
|
|
17
|
+
*
|
|
18
|
+
* ## Webhook verification: HMAC-SHA1
|
|
19
|
+
*
|
|
20
|
+
* Twilio signs every webhook request using HMAC-SHA1. The verification algorithm:
|
|
21
|
+
*
|
|
22
|
+
* 1. Start with the **full request URL** (including scheme, host, path, and any query string).
|
|
23
|
+
* 2. Parse the POST body as form-encoded key-value pairs.
|
|
24
|
+
* 3. Sort the parameters **alphabetically by key name**.
|
|
25
|
+
* 4. Concatenate each key+value pair (no separator) directly to the URL string.
|
|
26
|
+
* 5. Compute `HMAC-SHA1(authToken, concatenatedString)`.
|
|
27
|
+
* 6. Base64-encode the HMAC digest.
|
|
28
|
+
* 7. Compare the result with the `X-Twilio-Signature` request header.
|
|
29
|
+
*
|
|
30
|
+
* If the computed signature matches the header, the request is authentic.
|
|
31
|
+
*
|
|
32
|
+
* ## Event mapping table
|
|
33
|
+
*
|
|
34
|
+
* | Twilio `CallStatus` | Normalised `kind` |
|
|
35
|
+
* |---------------------|----------------------|
|
|
36
|
+
* | `ringing` | `call-ringing` |
|
|
37
|
+
* | `in-progress` | `call-answered` |
|
|
38
|
+
* | `completed` | `call-completed` |
|
|
39
|
+
* | `failed` | `call-failed` |
|
|
40
|
+
* | `busy` | `call-busy` |
|
|
41
|
+
* | `no-answer` | `call-no-answer` |
|
|
42
|
+
* | `canceled` | `call-hangup-user` |
|
|
43
|
+
* | (+ `Digits` param) | `call-dtmf` |
|
|
6
44
|
*
|
|
7
45
|
* @module @framers/agentos/voice/providers/twilio
|
|
8
46
|
*/
|
|
@@ -26,10 +64,15 @@ import { randomUUID } from 'node:crypto';
|
|
|
26
64
|
* ```
|
|
27
65
|
*/
|
|
28
66
|
export class TwilioVoiceProvider {
|
|
67
|
+
/**
|
|
68
|
+
* @param config - Twilio credentials and optional overrides.
|
|
69
|
+
*/
|
|
29
70
|
constructor(config) {
|
|
71
|
+
/** Provider identifier, always `'twilio'`. */
|
|
30
72
|
this.name = 'twilio';
|
|
31
73
|
this.config = config;
|
|
32
74
|
this.baseUrl = 'https://api.twilio.com/2010-04-01';
|
|
75
|
+
// Twilio uses HTTP Basic auth with accountSid:authToken.
|
|
33
76
|
this.authHeader =
|
|
34
77
|
'Basic ' +
|
|
35
78
|
Buffer.from(`${config.accountSid}:${config.authToken}`).toString('base64');
|
|
@@ -39,21 +82,32 @@ export class TwilioVoiceProvider {
|
|
|
39
82
|
/**
|
|
40
83
|
* Verify an incoming Twilio webhook request using HMAC-SHA1.
|
|
41
84
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
85
|
+
* ## Algorithm (step by step)
|
|
86
|
+
*
|
|
87
|
+
* 1. Extract the `X-Twilio-Signature` header from the request.
|
|
88
|
+
* 2. Parse the request body as URL-encoded form data.
|
|
89
|
+
* 3. Sort all key-value pairs alphabetically by key.
|
|
90
|
+
* 4. Build the signed string: start with the full URL, then append each
|
|
91
|
+
* key + value (no delimiters between pairs).
|
|
92
|
+
* 5. Compute `HMAC-SHA1` of the signed string using the auth token as the key.
|
|
93
|
+
* 6. Base64-encode the digest and compare it to the header value.
|
|
94
|
+
*
|
|
95
|
+
* @param ctx - Raw webhook request context.
|
|
96
|
+
* @returns Verification result with `valid: true` if the signature matches.
|
|
44
97
|
*/
|
|
45
98
|
verifyWebhook(ctx) {
|
|
46
99
|
const signature = ctx.headers['x-twilio-signature'];
|
|
47
100
|
if (!signature || Array.isArray(signature)) {
|
|
48
101
|
return { valid: false, error: 'Missing x-twilio-signature header' };
|
|
49
102
|
}
|
|
50
|
-
//
|
|
103
|
+
// Step 2-4: Parse form body, sort params, build signed data string.
|
|
51
104
|
const bodyParams = new URLSearchParams(ctx.body.toString());
|
|
52
105
|
const sorted = [...bodyParams.entries()].sort(([a], [b]) => a.localeCompare(b));
|
|
53
106
|
let data = ctx.url;
|
|
54
107
|
for (const [key, value] of sorted) {
|
|
55
108
|
data += key + value;
|
|
56
109
|
}
|
|
110
|
+
// Step 5-6: HMAC-SHA1 with auth token, compare base64 digest.
|
|
57
111
|
const expected = createHmac('sha1', this.config.authToken)
|
|
58
112
|
.update(data)
|
|
59
113
|
.digest('base64');
|
|
@@ -66,8 +120,12 @@ export class TwilioVoiceProvider {
|
|
|
66
120
|
/**
|
|
67
121
|
* Parse a Twilio webhook body into normalized {@link NormalizedCallEvent}s.
|
|
68
122
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
123
|
+
* Twilio sends webhooks with a form-encoded body containing `CallSid`,
|
|
124
|
+
* `CallStatus`, and optionally `Digits` (for DTMF input from `<Gather>`).
|
|
125
|
+
* Each webhook may produce one or two events (status + optional DTMF).
|
|
126
|
+
*
|
|
127
|
+
* @param ctx - Raw webhook request context.
|
|
128
|
+
* @returns Parsed result containing zero or more normalized events.
|
|
71
129
|
*/
|
|
72
130
|
parseWebhookEvent(ctx) {
|
|
73
131
|
const params = new URLSearchParams(ctx.body.toString());
|
|
@@ -76,12 +134,13 @@ export class TwilioVoiceProvider {
|
|
|
76
134
|
const digits = params.get('Digits');
|
|
77
135
|
const timestamp = Date.now();
|
|
78
136
|
const events = [];
|
|
79
|
-
/** Helper: shared base fields. */
|
|
137
|
+
/** Helper: shared base fields with a unique event ID for idempotency. */
|
|
80
138
|
const base = () => ({
|
|
81
139
|
eventId: randomUUID(),
|
|
82
140
|
providerCallId: callSid,
|
|
83
141
|
timestamp,
|
|
84
142
|
});
|
|
143
|
+
// Map Twilio CallStatus values to normalized event kinds.
|
|
85
144
|
switch (callStatus) {
|
|
86
145
|
case 'ringing':
|
|
87
146
|
events.push({ ...base(), kind: 'call-ringing' });
|
|
@@ -102,13 +161,17 @@ export class TwilioVoiceProvider {
|
|
|
102
161
|
events.push({ ...base(), kind: 'call-no-answer' });
|
|
103
162
|
break;
|
|
104
163
|
case 'canceled':
|
|
164
|
+
// Twilio uses "canceled" when the caller hangs up before the callee answers.
|
|
105
165
|
events.push({ ...base(), kind: 'call-hangup-user' });
|
|
106
166
|
break;
|
|
107
167
|
default:
|
|
108
|
-
// initiated / queued / etc.
|
|
168
|
+
// initiated / queued / etc. -- no normalized event emitted.
|
|
169
|
+
// These are transient Twilio-internal states that don't map to
|
|
170
|
+
// meaningful call lifecycle events.
|
|
109
171
|
break;
|
|
110
172
|
}
|
|
111
|
-
// DTMF digit input
|
|
173
|
+
// DTMF digit input (from <Gather> TwiML verb callback).
|
|
174
|
+
// This can co-occur with a CallStatus update in the same webhook.
|
|
112
175
|
if (digits != null && digits !== '') {
|
|
113
176
|
events.push({
|
|
114
177
|
...base(),
|
|
@@ -122,12 +185,18 @@ export class TwilioVoiceProvider {
|
|
|
122
185
|
/**
|
|
123
186
|
* Initiate an outbound call via the Twilio Calls API.
|
|
124
187
|
*
|
|
125
|
-
* Posts to `/Accounts/{accountSid}/Calls.json` with form-encoded body
|
|
126
|
-
*
|
|
127
|
-
* are
|
|
188
|
+
* Posts to `/Accounts/{accountSid}/Calls.json` with a **form-encoded** body
|
|
189
|
+
* (not JSON -- this is Twilio's 2010-era API convention). All four status
|
|
190
|
+
* callback events (`initiated`, `ringing`, `answered`, `completed`) are
|
|
191
|
+
* requested so the {@link CallManager} receives the full state progression.
|
|
192
|
+
*
|
|
193
|
+
* @param input - Call initiation parameters (from/to numbers, webhook URLs).
|
|
194
|
+
* @returns Result containing the Twilio `CallSid` on success.
|
|
195
|
+
* @throws Never throws; returns `{ success: false, error: '...' }` on failure.
|
|
128
196
|
*/
|
|
129
197
|
async initiateCall(input) {
|
|
130
198
|
const url = `${this.baseUrl}/Accounts/${this.config.accountSid}/Calls.json`;
|
|
199
|
+
// Build form-encoded body. Twilio expects this format, not JSON.
|
|
131
200
|
const body = [
|
|
132
201
|
`To=${encodeURIComponent(input.toNumber)}`,
|
|
133
202
|
`From=${encodeURIComponent(input.fromNumber)}`,
|
|
@@ -154,7 +223,13 @@ export class TwilioVoiceProvider {
|
|
|
154
223
|
return { providerCallId: data.sid, success: true };
|
|
155
224
|
}
|
|
156
225
|
/**
|
|
157
|
-
* Hang up an active call by
|
|
226
|
+
* Hang up an active call by POSTing `Status=completed`.
|
|
227
|
+
*
|
|
228
|
+
* Twilio uses the same Calls resource endpoint for both querying and
|
|
229
|
+
* modifying a call. Setting `Status=completed` instructs Twilio to
|
|
230
|
+
* immediately terminate the call.
|
|
231
|
+
*
|
|
232
|
+
* @param input - Contains the Twilio `CallSid` to hang up.
|
|
158
233
|
*/
|
|
159
234
|
async hangupCall(input) {
|
|
160
235
|
const url = `${this.baseUrl}/Accounts/${this.config.accountSid}/Calls/${input.providerCallId}.json`;
|
|
@@ -170,9 +245,14 @@ export class TwilioVoiceProvider {
|
|
|
170
245
|
/**
|
|
171
246
|
* Inject TTS into a live call using a TwiML `<Say>` verb.
|
|
172
247
|
*
|
|
173
|
-
* Sends a `Twiml` parameter containing a minimal `<Response><Say>`
|
|
248
|
+
* Sends a `Twiml` form parameter containing a minimal `<Response><Say>`
|
|
249
|
+
* document. Twilio will parse the TwiML, synthesise the speech, and play
|
|
250
|
+
* it to the caller in real-time.
|
|
251
|
+
*
|
|
174
252
|
* The optional `voice` attribute maps to Twilio's built-in voice names
|
|
175
253
|
* (e.g., `alice`, `Polly.Joanna`).
|
|
254
|
+
*
|
|
255
|
+
* @param input - TTS parameters (text, voice, call ID).
|
|
176
256
|
*/
|
|
177
257
|
async playTts(input) {
|
|
178
258
|
const url = `${this.baseUrl}/Accounts/${this.config.accountSid}/Calls/${input.providerCallId}.json`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"twilio.js","sourceRoot":"","sources":["../../../src/voice/providers/twilio.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"twilio.js","sourceRoot":"","sources":["../../../src/voice/providers/twilio.ts"],"names":[],"mappings":"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"}
|
package/dist/voice/twiml.d.ts
CHANGED
|
@@ -2,8 +2,26 @@
|
|
|
2
2
|
* @fileoverview TwiML and XML generation helpers for telephony providers.
|
|
3
3
|
*
|
|
4
4
|
* Generates provider-specific XML/TwiML response payloads for Twilio, Telnyx,
|
|
5
|
-
* and Plivo. All text content and attribute values are XML-escaped
|
|
6
|
-
* injection or malformed markup.
|
|
5
|
+
* and Plivo. All text content and attribute values are XML-escaped via
|
|
6
|
+
* {@link escapeXml} to prevent injection or malformed markup.
|
|
7
|
+
*
|
|
8
|
+
* ## XSS / injection prevention
|
|
9
|
+
*
|
|
10
|
+
* Every dynamic value (URLs, text content, voice names) passes through
|
|
11
|
+
* {@link escapeXml} before being interpolated into the XML template. This
|
|
12
|
+
* replaces the five XML-sensitive characters with their named entity
|
|
13
|
+
* equivalents:
|
|
14
|
+
*
|
|
15
|
+
* | Character | Entity |
|
|
16
|
+
* |-----------|-----------|
|
|
17
|
+
* | `<` | `<` |
|
|
18
|
+
* | `>` | `>` |
|
|
19
|
+
* | `&` | `&` |
|
|
20
|
+
* | `"` | `"` |
|
|
21
|
+
* | `'` | `'` |
|
|
22
|
+
*
|
|
23
|
+
* This ensures that user-controlled input (caller names, agent-generated
|
|
24
|
+
* messages, etc.) cannot break out of an attribute or inject child elements.
|
|
7
25
|
*
|
|
8
26
|
* @module @framers/agentos/voice/twiml
|
|
9
27
|
*/
|
|
@@ -11,27 +29,44 @@
|
|
|
11
29
|
* Generate TwiML for Twilio conversation mode using a bidirectional media stream.
|
|
12
30
|
*
|
|
13
31
|
* The returned markup instructs Twilio to open a WebSocket to `streamUrl` and
|
|
14
|
-
* stream audio in both directions for the duration of the call.
|
|
32
|
+
* stream audio in both directions for the duration of the call. Twilio will
|
|
33
|
+
* send mu-law 8 kHz audio chunks as JSON `media` events on the WebSocket.
|
|
15
34
|
*
|
|
16
|
-
* @param streamUrl - WebSocket URL Twilio should connect to (e.g. `wss
|
|
17
|
-
* @param token - Optional bearer token appended as a `?token=` query parameter
|
|
35
|
+
* @param streamUrl - WebSocket URL Twilio should connect to (e.g. `wss://api.example.com/stream`).
|
|
36
|
+
* @param token - Optional bearer token appended as a `?token=` query parameter
|
|
37
|
+
* for authenticating the WebSocket connection.
|
|
18
38
|
* @returns A complete TwiML XML document string.
|
|
19
39
|
*
|
|
20
40
|
* @example
|
|
21
41
|
* ```typescript
|
|
22
|
-
*
|
|
42
|
+
* // Without auth token:
|
|
43
|
+
* twilioConversationTwiml('wss://api.example.com/call');
|
|
44
|
+
* // => '<?xml version="1.0" encoding="UTF-8"?>\n<Response><Connect><Stream url="wss://api.example.com/call" /></Connect></Response>'
|
|
45
|
+
*
|
|
46
|
+
* // With auth token:
|
|
47
|
+
* twilioConversationTwiml('wss://api.example.com/call', 'jwt-token-here');
|
|
48
|
+
* // => '<?xml version="1.0" ...><Response><Connect><Stream url="wss://api.example.com/call?token=jwt-token-here" /></Connect></Response>'
|
|
23
49
|
* ```
|
|
24
50
|
*/
|
|
25
51
|
export declare function twilioConversationTwiml(streamUrl: string, token?: string): string;
|
|
26
52
|
/**
|
|
27
|
-
* Generate TwiML for Twilio notify mode
|
|
53
|
+
* Generate TwiML for Twilio notify mode -- synthesise `text` over the call then hang up.
|
|
28
54
|
*
|
|
29
55
|
* Useful for delivering one-shot announcements (e.g. voicemail greetings, error
|
|
30
|
-
* messages) without establishing a full media stream.
|
|
56
|
+
* messages, appointment reminders) without establishing a full media stream.
|
|
31
57
|
*
|
|
32
|
-
* @param text - The message to speak to the caller.
|
|
58
|
+
* @param text - The message to speak to the caller. XML-escaped automatically.
|
|
33
59
|
* @param voice - Optional Twilio voice name (e.g. `'Polly.Joanna'`, `'alice'`).
|
|
34
60
|
* @returns A complete TwiML XML document string.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* twilioNotifyTwiml('Your appointment is confirmed.');
|
|
65
|
+
* // => '<?xml version="1.0" encoding="UTF-8"?>\n<Response><Say>Your appointment is confirmed.</Say><Hangup/></Response>'
|
|
66
|
+
*
|
|
67
|
+
* twilioNotifyTwiml('Hello', 'Polly.Joanna');
|
|
68
|
+
* // => '...><Say voice="Polly.Joanna">Hello</Say><Hangup/></Response>'
|
|
69
|
+
* ```
|
|
35
70
|
*/
|
|
36
71
|
export declare function twilioNotifyTwiml(text: string, voice?: string): string;
|
|
37
72
|
/**
|
|
@@ -43,27 +78,50 @@ export declare function twilioNotifyTwiml(text: string, voice?: string): string;
|
|
|
43
78
|
*
|
|
44
79
|
* @param streamUrl - WebSocket URL Telnyx should stream audio to.
|
|
45
80
|
* @returns A complete XML document string.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* telnyxStreamXml('wss://api.example.com/telnyx-stream');
|
|
85
|
+
* // => '<?xml version="1.0" encoding="UTF-8"?>\n<Response><Stream url="wss://api.example.com/telnyx-stream" /></Response>'
|
|
86
|
+
* ```
|
|
46
87
|
*/
|
|
47
88
|
export declare function telnyxStreamXml(streamUrl: string): string;
|
|
48
89
|
/**
|
|
49
90
|
* Generate Plivo bidirectional streaming XML.
|
|
50
91
|
*
|
|
51
92
|
* Instructs Plivo to open a bidirectional WebSocket stream and keep the call
|
|
52
|
-
* alive for the duration of the stream session.
|
|
93
|
+
* alive for the duration of the stream session. The stream URL is placed as
|
|
94
|
+
* element text content (not an attribute) per the Plivo XML spec.
|
|
53
95
|
*
|
|
54
96
|
* @param streamUrl - WebSocket URL Plivo should connect to.
|
|
55
97
|
* @returns A complete Plivo XML document string.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* plivoStreamXml('wss://api.example.com/plivo-stream');
|
|
102
|
+
* // => '<?xml version="1.0" encoding="UTF-8"?>\n<Response><Stream bidirectional="true" keepCallAlive="true">wss://api.example.com/plivo-stream</Stream></Response>'
|
|
103
|
+
* ```
|
|
56
104
|
*/
|
|
57
105
|
export declare function plivoStreamXml(streamUrl: string): string;
|
|
58
106
|
/**
|
|
59
107
|
* Generate Plivo speak + hangup XML.
|
|
60
108
|
*
|
|
61
109
|
* Synthesises `text` to the caller using Plivo's TTS engine and immediately
|
|
62
|
-
* hangs up after playback completes.
|
|
110
|
+
* hangs up after playback completes. Equivalent to Twilio's `<Say>...<Hangup/>`
|
|
111
|
+
* but uses Plivo's `<Speak>` element name.
|
|
63
112
|
*
|
|
64
|
-
* @param text - The message to speak to the caller.
|
|
113
|
+
* @param text - The message to speak to the caller. XML-escaped automatically.
|
|
65
114
|
* @param voice - Optional Plivo voice name (e.g. `'WOMAN'`, `'Polly.Joanna'`).
|
|
66
115
|
* @returns A complete Plivo XML document string.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* plivoNotifyXml('Your order has shipped.');
|
|
120
|
+
* // => '<?xml version="1.0" encoding="UTF-8"?>\n<Response><Speak>Your order has shipped.</Speak><Hangup/></Response>'
|
|
121
|
+
*
|
|
122
|
+
* plivoNotifyXml('Hello', 'WOMAN');
|
|
123
|
+
* // => '...><Speak voice="WOMAN">Hello</Speak><Hangup/></Response>'
|
|
124
|
+
* ```
|
|
67
125
|
*/
|
|
68
126
|
export declare function plivoNotifyXml(text: string, voice?: string): string;
|
|
69
127
|
//# sourceMappingURL=twiml.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"twiml.d.ts","sourceRoot":"","sources":["../../src/voice/twiml.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"twiml.d.ts","sourceRoot":"","sources":["../../src/voice/twiml.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAQH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGjF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGtE;AAMD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzD;AAMD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAGnE"}
|