@framers/agentos 0.1.74 → 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.
Files changed (138) hide show
  1. package/README.md +139 -34
  2. package/dist/core/agency/AgentCommunicationBus.d.ts +1 -0
  3. package/dist/core/agency/AgentCommunicationBus.d.ts.map +1 -1
  4. package/dist/core/agency/AgentCommunicationBus.js +62 -8
  5. package/dist/core/agency/AgentCommunicationBus.js.map +1 -1
  6. package/dist/core/agency/IAgentCommunicationBus.d.ts +1 -1
  7. package/dist/core/agency/IAgentCommunicationBus.d.ts.map +1 -1
  8. package/dist/orchestration/builders/index.d.ts +1 -1
  9. package/dist/orchestration/builders/index.d.ts.map +1 -1
  10. package/dist/orchestration/builders/index.js +1 -1
  11. package/dist/orchestration/builders/index.js.map +1 -1
  12. package/dist/orchestration/builders/nodes.d.ts +15 -0
  13. package/dist/orchestration/builders/nodes.d.ts.map +1 -1
  14. package/dist/orchestration/builders/nodes.js +33 -0
  15. package/dist/orchestration/builders/nodes.js.map +1 -1
  16. package/dist/orchestration/runtime/LoopController.d.ts +10 -10
  17. package/dist/orchestration/runtime/LoopController.d.ts.map +1 -1
  18. package/dist/orchestration/runtime/LoopController.js +1 -1
  19. package/dist/orchestration/runtime/LoopController.js.map +1 -1
  20. package/dist/orchestration/runtime/index.d.ts +1 -1
  21. package/dist/orchestration/runtime/index.d.ts.map +1 -1
  22. package/dist/orchestration/runtime/index.js.map +1 -1
  23. package/dist/speech/FallbackProxy.d.ts +104 -0
  24. package/dist/speech/FallbackProxy.d.ts.map +1 -0
  25. package/dist/speech/FallbackProxy.js +151 -0
  26. package/dist/speech/FallbackProxy.js.map +1 -0
  27. package/dist/speech/SpeechProviderResolver.d.ts +103 -0
  28. package/dist/speech/SpeechProviderResolver.d.ts.map +1 -0
  29. package/dist/speech/SpeechProviderResolver.js +256 -0
  30. package/dist/speech/SpeechProviderResolver.js.map +1 -0
  31. package/dist/speech/SpeechRuntime.d.ts +23 -1
  32. package/dist/speech/SpeechRuntime.d.ts.map +1 -1
  33. package/dist/speech/SpeechRuntime.js +82 -8
  34. package/dist/speech/SpeechRuntime.js.map +1 -1
  35. package/dist/speech/index.d.ts +6 -0
  36. package/dist/speech/index.d.ts.map +1 -1
  37. package/dist/speech/index.js +6 -0
  38. package/dist/speech/index.js.map +1 -1
  39. package/dist/speech/providerCatalog.d.ts.map +1 -1
  40. package/dist/speech/providerCatalog.js +15 -1
  41. package/dist/speech/providerCatalog.js.map +1 -1
  42. package/dist/speech/providers/AssemblyAISTTProvider.d.ts +49 -0
  43. package/dist/speech/providers/AssemblyAISTTProvider.d.ts.map +1 -0
  44. package/dist/speech/providers/AssemblyAISTTProvider.js +151 -0
  45. package/dist/speech/providers/AssemblyAISTTProvider.js.map +1 -0
  46. package/dist/speech/providers/AzureSpeechSTTProvider.d.ts +48 -0
  47. package/dist/speech/providers/AzureSpeechSTTProvider.d.ts.map +1 -0
  48. package/dist/speech/providers/AzureSpeechSTTProvider.js +90 -0
  49. package/dist/speech/providers/AzureSpeechSTTProvider.js.map +1 -0
  50. package/dist/speech/providers/AzureSpeechTTSProvider.d.ts +60 -0
  51. package/dist/speech/providers/AzureSpeechTTSProvider.d.ts.map +1 -0
  52. package/dist/speech/providers/AzureSpeechTTSProvider.js +127 -0
  53. package/dist/speech/providers/AzureSpeechTTSProvider.js.map +1 -0
  54. package/dist/speech/providers/DeepgramBatchSTTProvider.d.ts +55 -0
  55. package/dist/speech/providers/DeepgramBatchSTTProvider.d.ts.map +1 -0
  56. package/dist/speech/providers/DeepgramBatchSTTProvider.js +102 -0
  57. package/dist/speech/providers/DeepgramBatchSTTProvider.js.map +1 -0
  58. package/dist/speech/types.d.ts +35 -0
  59. package/dist/speech/types.d.ts.map +1 -1
  60. package/dist/voice/CallManager.d.ts +1 -1
  61. package/dist/voice/CallManager.d.ts.map +1 -1
  62. package/dist/voice/CallManager.js +9 -0
  63. package/dist/voice/CallManager.js.map +1 -1
  64. package/dist/voice/MediaStreamParser.d.ts +83 -0
  65. package/dist/voice/MediaStreamParser.d.ts.map +1 -0
  66. package/dist/voice/MediaStreamParser.js +2 -0
  67. package/dist/voice/MediaStreamParser.js.map +1 -0
  68. package/dist/voice/TelephonyStreamTransport.d.ts +112 -0
  69. package/dist/voice/TelephonyStreamTransport.d.ts.map +1 -0
  70. package/dist/voice/TelephonyStreamTransport.js +208 -0
  71. package/dist/voice/TelephonyStreamTransport.js.map +1 -0
  72. package/dist/voice/index.d.ts +10 -0
  73. package/dist/voice/index.d.ts.map +1 -1
  74. package/dist/voice/index.js +11 -0
  75. package/dist/voice/index.js.map +1 -1
  76. package/dist/voice/parsers/PlivoMediaStreamParser.d.ts +43 -0
  77. package/dist/voice/parsers/PlivoMediaStreamParser.d.ts.map +1 -0
  78. package/dist/voice/parsers/PlivoMediaStreamParser.js +92 -0
  79. package/dist/voice/parsers/PlivoMediaStreamParser.js.map +1 -0
  80. package/dist/voice/parsers/TelnyxMediaStreamParser.d.ts +51 -0
  81. package/dist/voice/parsers/TelnyxMediaStreamParser.d.ts.map +1 -0
  82. package/dist/voice/parsers/TelnyxMediaStreamParser.js +103 -0
  83. package/dist/voice/parsers/TelnyxMediaStreamParser.js.map +1 -0
  84. package/dist/voice/parsers/TwilioMediaStreamParser.d.ts +50 -0
  85. package/dist/voice/parsers/TwilioMediaStreamParser.d.ts.map +1 -0
  86. package/dist/voice/parsers/TwilioMediaStreamParser.js +144 -0
  87. package/dist/voice/parsers/TwilioMediaStreamParser.js.map +1 -0
  88. package/dist/voice/providers/plivo.d.ts +77 -0
  89. package/dist/voice/providers/plivo.d.ts.map +1 -0
  90. package/dist/voice/providers/plivo.js +180 -0
  91. package/dist/voice/providers/plivo.js.map +1 -0
  92. package/dist/voice/providers/telnyx.d.ts +93 -0
  93. package/dist/voice/providers/telnyx.d.ts.map +1 -0
  94. package/dist/voice/providers/telnyx.js +193 -0
  95. package/dist/voice/providers/telnyx.js.map +1 -0
  96. package/dist/voice/providers/twilio.d.ts +79 -0
  97. package/dist/voice/providers/twilio.d.ts.map +1 -0
  98. package/dist/voice/providers/twilio.js +191 -0
  99. package/dist/voice/providers/twilio.js.map +1 -0
  100. package/dist/voice/twiml.d.ts +69 -0
  101. package/dist/voice/twiml.d.ts.map +1 -0
  102. package/dist/voice/twiml.js +92 -0
  103. package/dist/voice/twiml.js.map +1 -0
  104. package/dist/voice/types.d.ts +9 -1
  105. package/dist/voice/types.d.ts.map +1 -1
  106. package/dist/voice-pipeline/AcousticEndpointDetector.d.ts +90 -0
  107. package/dist/voice-pipeline/AcousticEndpointDetector.d.ts.map +1 -0
  108. package/dist/voice-pipeline/AcousticEndpointDetector.js +123 -0
  109. package/dist/voice-pipeline/AcousticEndpointDetector.js.map +1 -0
  110. package/dist/voice-pipeline/HardCutBargeinHandler.d.ts +67 -0
  111. package/dist/voice-pipeline/HardCutBargeinHandler.d.ts.map +1 -0
  112. package/dist/voice-pipeline/HardCutBargeinHandler.js +55 -0
  113. package/dist/voice-pipeline/HardCutBargeinHandler.js.map +1 -0
  114. package/dist/voice-pipeline/HeuristicEndpointDetector.d.ts +128 -0
  115. package/dist/voice-pipeline/HeuristicEndpointDetector.d.ts.map +1 -0
  116. package/dist/voice-pipeline/HeuristicEndpointDetector.js +240 -0
  117. package/dist/voice-pipeline/HeuristicEndpointDetector.js.map +1 -0
  118. package/dist/voice-pipeline/SoftFadeBargeinHandler.d.ts +96 -0
  119. package/dist/voice-pipeline/SoftFadeBargeinHandler.d.ts.map +1 -0
  120. package/dist/voice-pipeline/SoftFadeBargeinHandler.js +69 -0
  121. package/dist/voice-pipeline/SoftFadeBargeinHandler.js.map +1 -0
  122. package/dist/voice-pipeline/VoicePipelineOrchestrator.d.ts +122 -0
  123. package/dist/voice-pipeline/VoicePipelineOrchestrator.d.ts.map +1 -0
  124. package/dist/voice-pipeline/VoicePipelineOrchestrator.js +317 -0
  125. package/dist/voice-pipeline/VoicePipelineOrchestrator.js.map +1 -0
  126. package/dist/voice-pipeline/WebSocketStreamTransport.d.ts +148 -0
  127. package/dist/voice-pipeline/WebSocketStreamTransport.d.ts.map +1 -0
  128. package/dist/voice-pipeline/WebSocketStreamTransport.js +207 -0
  129. package/dist/voice-pipeline/WebSocketStreamTransport.js.map +1 -0
  130. package/dist/voice-pipeline/index.d.ts +13 -0
  131. package/dist/voice-pipeline/index.d.ts.map +1 -0
  132. package/dist/voice-pipeline/index.js +13 -0
  133. package/dist/voice-pipeline/index.js.map +1 -0
  134. package/dist/voice-pipeline/types.d.ts +905 -0
  135. package/dist/voice-pipeline/types.d.ts.map +1 -0
  136. package/dist/voice-pipeline/types.js +23 -0
  137. package/dist/voice-pipeline/types.js.map +1 -0
  138. package/package.json +6 -1
@@ -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"}
@@ -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"}