@fonoster/voice 0.27.0 → 0.31.0

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 (64) hide show
  1. package/README.md +32 -419
  2. package/dist/Session.d.ts +161 -0
  3. package/dist/Session.js +576 -0
  4. package/dist/VoiceServer.d.ts +70 -5
  5. package/dist/VoiceServer.js +149 -43
  6. package/dist/{verbs/Dial.d.ts → errors.d.ts} +9 -13
  7. package/dist/{verbs/Answer.js → errors.js} +18 -7
  8. package/dist/index.d.ts +4 -6
  9. package/dist/index.js +4 -15
  10. package/dist/signature.d.ts +31 -0
  11. package/dist/signature.js +93 -0
  12. package/dist/types.d.ts +88 -10
  13. package/dist/types.js +0 -21
  14. package/package.json +13 -19
  15. package/session.d.ts +1 -0
  16. package/session.js +2 -0
  17. package/dist/VoiceResponse.d.ts +0 -304
  18. package/dist/VoiceResponse.js +0 -461
  19. package/dist/createSession.d.ts +0 -22
  20. package/dist/createSession.js +0 -66
  21. package/dist/defaultServerConfig.d.ts +0 -21
  22. package/dist/defaultServerConfig.js +0 -10
  23. package/dist/demo.d.ts +0 -1
  24. package/dist/demo.js +0 -83
  25. package/dist/filters.d.ts +0 -44
  26. package/dist/filters.js +0 -17
  27. package/dist/serviceDefinition.d.ts +0 -2
  28. package/dist/serviceDefinition.js +0 -29
  29. package/dist/verbs/Answer.d.ts +0 -24
  30. package/dist/verbs/Dial.js +0 -60
  31. package/dist/verbs/Gather.d.ts +0 -25
  32. package/dist/verbs/Gather.js +0 -57
  33. package/dist/verbs/Hangup.d.ts +0 -24
  34. package/dist/verbs/Hangup.js +0 -29
  35. package/dist/verbs/Mute.d.ts +0 -25
  36. package/dist/verbs/Mute.js +0 -36
  37. package/dist/verbs/Play.d.ts +0 -25
  38. package/dist/verbs/Play.js +0 -33
  39. package/dist/verbs/PlayDtmf.d.ts +0 -25
  40. package/dist/verbs/PlayDtmf.js +0 -32
  41. package/dist/verbs/PlaybackControl.d.ts +0 -25
  42. package/dist/verbs/PlaybackControl.js +0 -35
  43. package/dist/verbs/Record.d.ts +0 -25
  44. package/dist/verbs/Record.js +0 -49
  45. package/dist/verbs/Say.d.ts +0 -25
  46. package/dist/verbs/Say.js +0 -33
  47. package/dist/verbs/SetAudioFilters.d.ts +0 -25
  48. package/dist/verbs/SetAudioFilters.js +0 -16
  49. package/dist/verbs/StopSay.d.ts +0 -25
  50. package/dist/verbs/StopSay.js +0 -11
  51. package/dist/verbs/Stream.d.ts +0 -39
  52. package/dist/verbs/Stream.js +0 -80
  53. package/dist/verbs/StreamGather.d.ts +0 -37
  54. package/dist/verbs/StreamGather.js +0 -66
  55. package/dist/verbs/Unmute.d.ts +0 -22
  56. package/dist/verbs/Unmute.js +0 -26
  57. package/dist/verbs/Verb.d.ts +0 -28
  58. package/dist/verbs/Verb.js +0 -103
  59. package/dist/verbs/getExpectedContent.d.ts +0 -2
  60. package/dist/verbs/getExpectedContent.js +0 -32
  61. package/dist/verbs/index.d.ts +0 -35
  62. package/dist/verbs/index.js +0 -51
  63. package/dist/verbs/validateRequest.d.ts +0 -20
  64. package/dist/verbs/validateRequest.js +0 -13
package/README.md CHANGED
@@ -6,11 +6,14 @@ voice
6
6
  [![Downloads/week](https://img.shields.io/npm/dw/@fonoster/voice.svg)](https://npmjs.org/package/@fonoster/voice)
7
7
  [![License](https://img.shields.io/npm/l/@fonoster/voice.svg)](https://github.com/fonoster/fonoster/blob/main/package.json)
8
8
 
9
- The Voice module is a library for creating voice applications using the Fonoster API. It provides a simple way to create voice applications that can interact with the calling party using DTMF or speech recognition combined with simple verbs.
9
+ The SDK for Fonoster **streams**: your own WebSocket server, speaking the `fonoster` protocol. For
10
+ every call, Fonoster opens one signed WebSocket **session** to your stream; `VoiceServer` accepts it
11
+ and runs your handler with the session. The protocol is in `docs/session-protocol.md`, the full SDK guide in
12
+ `docs/voice-sdk.md`, and runnable examples in `examples/`.
10
13
 
11
14
  * [Installation](#installation)
12
15
  * [Example](#example)
13
- * [Voice Response](#VoiceResponse)
16
+ * [The session](#the-session)
14
17
 
15
18
  ## Installation
16
19
 
@@ -20,431 +23,41 @@ $ npm install --save @fonoster/voice
20
23
 
21
24
  ## Example
22
25
 
23
- A Voice Application is a server that controls a call's flow. A Voice Application can use any combination of the following verbs:
24
-
25
- - `Answer` - Accepts an incoming call
26
- - `Dial` - Passes the call to an Agent or a Number at the PSTN
27
- - `Hangup` - Closes the call
28
- - `Play` - Takes a URL or file and streams the sound back to the calling party
29
- - `Say` - Takes a text, synthesizes the text into audio, and streams back the result
30
- - `Gather` - Waits for DTMF or speech events and returns back the result
31
- - `SGather` - Returns a stream for future DTMF and speech results
32
- - `Stream` - Starts a stream to read and write audio into the call
33
- - `Record` - It records the voice of the calling party and saves the audio on the Storage sub-system
34
- - `Mute` - It tells the channel to stop sending media, effectively muting the channel
35
- - `Unmute` - It tells the channel to allow media flow
36
- - `SetAudioFilters` - Filters the caller's audio in the Media Server before it reaches speech recognition and your media stream
37
-
38
- ### Audio filters
39
-
40
- Background voices — a television, a radio, someone else in the room — get transcribed too, and
41
- your application ends up answering them. Ask the Media Server to filter the caller's audio before
42
- it reaches speech recognition and your media stream:
43
-
44
26
  ```typescript
45
- const { aiCoustics } = require("@fonoster/voice");
46
-
47
- new VoiceServer().listen(async (req: VoiceRequest, voice: VoiceResponse) => {
48
- // Set filters before answering; they apply for the rest of the session
49
- await voice.setAudioFilters([aiCoustics({ enhancementLevel: 0.8 })]);
50
-
51
- await voice.answer();
52
- // ...
53
- });
54
- ```
55
-
56
- Notes:
57
-
58
- - The filter runs in the Media Server, which needs the optional
59
- `@ai-coustics/aic-sdk` package and an `AIC_SDK_LICENSE` key
60
- ([developers.ai-coustics.com](https://developers.ai-coustics.com)). Credentials never leave the
61
- Media Server, so your application never handles them.
62
- - `enhancementLevel` goes from 0 to 1; higher values also suppress competing speech.
63
- - If the filter cannot be applied, `setAudioFilters` throws and **the call continues with
64
- unfiltered audio**. Catch it if a failure should not interrupt your flow.
65
- - Filtering adds about 30 ms of delay to the audio your application hears.
66
- - Sending a new list replaces the previous one, and an empty list removes all filters.
67
-
68
- Voice Application Example:
69
-
70
- ```typescript
71
- const VoiceServer = require("@fonoster/voice").default;
72
- const {
73
- GatherSource,
74
- VoiceRequest,
75
- VoiceResponse
76
- } = require("@fonoster/voice");
77
-
78
- new VoiceServer().listen(async (req: VoiceRequest, voice: VoiceResponse) => {
79
- const { ingressNumber, sessionRef, appRef } = req;
80
-
81
- await voice.answer();
82
-
83
- await voice.say("Hi there! What's your name?");
84
-
85
- const { speech: name } = await res.gather({
86
- source: GatherSource.SPEECH
87
- });
88
-
89
- await voice.say("Nice to meet you " + name + "!");
90
-
91
- await voice.say("Please enter your 4 digit pin.");
92
-
93
- const { digits } = await voice.gather({
94
- maxDigits: 4,
95
- finishOnKey: "#"
96
- });
97
-
98
- await voice.say("Your pin is " + digits);
99
-
100
- await voice.hangup();
101
- });
102
-
103
- // Your app will live at tcp://127.0.0.1:50061
104
- // and you can easily publish it to the Internet with:
105
- // ngrok tcp 50061
106
- ```
107
-
108
- <a name="VoiceResponse"></a>
109
-
110
- ## VoiceResponse ⇐ <code>Verb</code>
111
- Use the VoiceResponse object, to construct advance Interactive
112
- Voice Response (IVR) applications.
113
-
114
- **Kind**: global class
115
- **Extends**: <code>Verb</code>
116
- **See**: module:core:APIClient
117
-
118
- * [VoiceResponse](#VoiceResponse) ⇐ <code>Verb</code>
119
- * [new VoiceResponse(request, voice)](#new_VoiceResponse_new)
120
- * [.answer()](#VoiceResponse+answer)
121
- * [.hangup()](#VoiceResponse+hangup)
122
- * [.play(url, options)](#VoiceResponse+play)
123
- * [.playDtmf(digits)](#VoiceResponse+playDtmf)
124
- * [.playbackControl(playbackRef, action)](#VoiceResponse+playbackControl)
125
- * [.gather(options)](#VoiceResponse+gather)
126
- * [.say(text, options)](#VoiceResponse+say)
127
- * [.record(options)](#VoiceResponse+record) ⇒ <code>RecordResponse</code>
128
- * [.dial(destination, options)](#VoiceResponse+dial) ⇒ <code>Promise.&lt;DialStatusStream&gt;</code>
129
- * [.stream(options)](#VoiceResponse+stream) ⇒ <code>Promise.&lt;Stream&gt;</code>
130
- * [.sgather(options)](#VoiceResponse+sgather) ⇒ <code>Promise.&lt;StreamGatherStream&gt;</code>
131
- * [.mute(options)](#VoiceResponse+mute)
132
- * [.unmute(options)](#VoiceResponse+unmute)
133
- * [.on(event, listener)](#VoiceResponse+on)
134
-
135
- <a name="new_VoiceResponse_new"></a>
136
-
137
- ### new VoiceResponse(request, voice)
138
- Constructs a new VoiceResponse object.
139
-
140
-
141
- | Param | Type | Description |
142
- | --- | --- | --- |
143
- | request | <code>VoiceRequest</code> | Options to indicate the objects endpoint |
144
- | voice | <code>VoiceSessionStream</code> | The voice session stream |
145
-
146
- **Example**
147
- ```js
148
27
  import { VoiceServer } from "@fonoster/voice";
149
28
 
150
- async function handler (request, response) {
151
- await response.answer();
152
- await response.play("https://soundsserver:9000/sounds/hello-world.wav");
153
- }
154
-
155
- new VoiceServer().listen(handler, { port: 3000 })
156
- ```
157
- <a name="VoiceResponse+answer"></a>
158
-
159
- ### voiceResponse.answer()
160
- Answer the call. Before running any other verb you
161
- must run the answer command.
162
-
163
- **Kind**: instance method of [<code>VoiceResponse</code>](#VoiceResponse)
164
- **Example**
165
- ```js
166
- async function handler (request, response) {
167
- await response.answer();
168
- }
169
- ```
170
- <a name="VoiceResponse+hangup"></a>
171
-
172
- ### voiceResponse.hangup()
173
- Hangup the call.
174
-
175
- **Kind**: instance method of [<code>VoiceResponse</code>](#VoiceResponse)
176
- **Example**
177
- ```js
178
- async function handler (request, response) {
179
- await response.hangup();
180
- }
181
- ```
182
- <a name="VoiceResponse+play"></a>
183
-
184
- ### voiceResponse.play(url, options)
185
- Play an audio in the call.
186
-
187
- **Kind**: instance method of [<code>VoiceResponse</code>](#VoiceResponse)
188
- **See**: Playback
189
-
190
- | Param | Type | Description |
191
- | --- | --- | --- |
192
- | url | <code>string</code> | The URL of the media to play |
193
- | options | <code>PlayOptions</code> | Options to control the playback |
194
- | options.playbackRef | <code>string</code> | Playback identifier to use in Playback operations |
195
-
196
- **Example**
197
- ```js
198
- async function handler (request, response) {
199
- await response.answer();
200
- await response.play("https://soundsserver:9000/sounds/hello-world.wav");
201
- }
202
- ```
203
- <a name="VoiceResponse+playDtmf"></a>
204
-
205
- ### voiceResponse.playDtmf(digits)
206
- Play a series of DTMF digits in a call.
207
-
208
- **Kind**: instance method of [<code>VoiceResponse</code>](#VoiceResponse)
209
-
210
- | Param | Type | Description |
211
- | --- | --- | --- |
212
- | digits | <code>string</code> | The DTMF digits to play (0-9, #, or *) |
213
-
214
- **Example**
215
- ```js
216
- async function handler (request, response) {
217
- await response.answer();
218
- await response.playDtmf("1234");
219
- }
220
- ```
221
- <a name="VoiceResponse+playbackControl"></a>
222
-
223
- ### voiceResponse.playbackControl(playbackRef, action)
224
- Control the playback of the currently playing media.
225
-
226
- **Kind**: instance method of [<code>VoiceResponse</code>](#VoiceResponse)
227
- **See**: play
228
-
229
- | Param | Type | Description |
230
- | --- | --- | --- |
231
- | playbackRef | <code>string</code> | The playback identifier |
232
- | action | <code>PlaybackControlAction</code> | The action to perform (STOP, RESTART, PAUSE, UNPAUSE, FORWARD) |
29
+ // AppBridge signs every connection with the app's signing secret.
30
+ new VoiceServer({ port: 3000, signingSecret: process.env.APP_SIGNING_SECRET }).listen(
31
+ async (session) => {
32
+ await session.say("Hi there! What's your name?");
33
+ const { speech: name } = await session.gather();
34
+ await session.say(`Nice to meet you ${name}!`);
233
35
 
234
- **Example**
235
- ```js
236
- async function handler (request, response) {
237
- await response.answer();
238
- await response.play("https://s3.fonoster.io/uuid/hello-world.wav", { playbackRef: "playback-01" });
36
+ await session.say("Please enter your 4 digit pin.");
37
+ const { digits } = await session.gather({ maxDigits: 4, finishOnKey: "#" });
38
+ await session.say(`Your pin is ${digits}`);
239
39
 
240
- // Pause the media
241
- await response.playbackControl("playback-01", PlaybackControlAction.PAUSE);
242
- }
40
+ await session.hangup();
41
+ }
42
+ );
243
43
  ```
244
- <a name="VoiceResponse+gather"></a>
245
-
246
- ### voiceResponse.gather(options)
247
- Waits for data entry from the user's keypad or from a speech provider.
248
-
249
- **Kind**: instance method of [<code>VoiceResponse</code>](#VoiceResponse)
250
- **Note**: When including `SPEECH` the default timeout is 10000 (10s).
251
-
252
- | Param | Type | Description |
253
- | --- | --- | --- |
254
- | options | <code>GatherOptions</code> | Options to select the maximum number of digits, final character, and timeout |
255
- | options.maxDigits | <code>number</code> | Maximum number of digits to collect. Defaults to 1 |
256
- | options.timeout | <code>number</code> | Milliseconds to wait before timeout. Defaults to 4000. Use zero for no timeout. |
257
- | options.finishOnKey | <code>string</code> | Optional last character to wait for. Defaults to '#'. It will not be included in the returned digits |
258
- | options.source | <code>GatherSource</code> | Where to listen as input source. This option accepts `DTMF` and `SPEECH`. A speech provider must be configure when including the `SPEECH` source. You might include both with `SPEECH_AND_DTMF`. Defaults to `SPEECH_AND_DTMF` |
259
-
260
- **Example**
261
- ```js
262
- async function handler (request, response) {
263
- await response.answer();
264
- const speech = await response.gather({ source: GatherSource.SPEECH, numDigits: 3 });
265
- console.log("speech: " + speech);
266
- await response.hangup();
267
- }
268
- ```
269
- <a name="VoiceResponse+say"></a>
270
-
271
- ### voiceResponse.say(text, options)
272
- Send a text for a TTS engine to convert to speech.
273
-
274
- **Kind**: instance method of [<code>VoiceResponse</code>](#VoiceResponse)
275
- **See**: Say
276
-
277
- | Param | Type | Description |
278
- | --- | --- | --- |
279
- | text | <code>string</code> | The text to convert to speech |
280
- | options | <code>SayOptions</code> | Options to control the TTS engine |
281
- | options.playbackRef | <code>string</code> | Playback identifier to use in Playback operations |
282
- | options.ttsOptions | <code>TTSOptions</code> | Options to control the TTS engine (specific to the TTS engine) |
283
-
284
- **Example**
285
- ```js
286
- async function handler (request, response) {
287
- await response.answer();
288
- const playbackRef = await response.say("Hello World");
289
-
290
- // Like the play verb, you can control the playback
291
- await response.playbackControl(playbackRef, PlaybackControlAction.STOP);
292
- }
293
- ```
294
- <a name="VoiceResponse+record"></a>
295
-
296
- ### voiceResponse.record(options) ⇒ <code>RecordResponse</code>
297
- Record the audio of the call.
298
-
299
- **Kind**: instance method of [<code>VoiceResponse</code>](#VoiceResponse)
300
- **Returns**: <code>RecordResponse</code> - The record response
301
-
302
- | Param | Type | Description |
303
- | --- | --- | --- |
304
- | options | <code>RecordOptions</code> | Options to control the record operation |
305
- | options.maxDuration | <code>number</code> | The maximum duration of the recording in seconds. Default is 60 |
306
- | options.maxSilence | <code>number</code> | The maximum duration of silence in seconds. Default is 5 |
307
- | options.beep | <code>boolean</code> | Play a beep before recording. Default is true |
308
- | options.finishOnKey | <code>string</code> | Stop recording when a DTMF digit is received. Default is '#' |
309
-
310
- **Example**
311
- ```js
312
- async function handler (request, response) {
313
- await response.answer();
314
- const record = await response.record();
315
- console.log("Recording: %s", record.name);
316
- }
317
- ```
318
- <a name="VoiceResponse+dial"></a>
319
-
320
- ### voiceResponse.dial(destination, options) ⇒ <code>Promise.&lt;DialStatusStream&gt;</code>
321
- Dials a destination and returns a stream of status.
322
-
323
- **Kind**: instance method of [<code>VoiceResponse</code>](#VoiceResponse)
324
- **Returns**: <code>Promise.&lt;DialStatusStream&gt;</code> - The dial status stream
325
-
326
- | Param | Type | Description |
327
- | --- | --- | --- |
328
- | destination | <code>string</code> | The destination to dial |
329
- | options | <code>DialOptions</code> | Options to control the dial operation |
330
- | options.timeout | <code>number</code> | The timeout in seconds. Default is 60 |
331
- | options.recordDirection | <code>RecordDirection</code> | The direction to record the call (IN, OUT, BOTH). Default is BOTH |
332
-
333
- <a name="VoiceResponse+stream"></a>
334
-
335
- ### voiceResponse.stream(options) ⇒ <code>Promise.&lt;Stream&gt;</code>
336
- Starts a bidirectional audio stream between the call and the application.
337
44
 
338
- **Kind**: instance method of [<code>VoiceResponse</code>](#VoiceResponse)
339
- **Returns**: <code>Promise.&lt;Stream&gt;</code> - The stream object
45
+ `attach(server, handler)` mounts the same server on an existing HTTP or Express server.
340
46
 
341
- | Param | Type | Description |
342
- | --- | --- | --- |
343
- | options | <code>StreamOptions</code> | Options to control the stream operation |
344
- | options.direction | <code>StreamDirection</code> | The direction to stream the audio (IN, OUT, BOTH). Default is BOTH |
345
- | options.format | <code>StreamAudioFormat</code> | The audio format to stream (WAV). Default is WAV |
47
+ ## The session
346
48
 
347
- **Example**
348
- ```js
349
- async function handler (request, response) {
350
- await response.answer();
49
+ | Method | Does |
50
+ | --- | --- |
51
+ | `say(text \| asyncIterable, options?)` | Speaks; resolves with `{ interrupted, heardText, heardMs }` |
52
+ | `gather(options?)` | Waits for speech or keypresses (`maxDigits`, `finishOnKey`, `timeout`) |
53
+ | `play(url)`, `playDtmf(digits)` | Plays media or DTMF tones |
54
+ | `transfer(to, { mode })`, `completeTransfer()` | Cold, warm or `refer` transfer |
55
+ | `hold()`, `resume()`, `handoff(app, payload)`, `hangup()` | Call control |
56
+ | `audio.onFrame`, `audio.send`, `audio.mark`, `audio.clear` | Raw call audio |
57
+ | `on(event, cb)`, `subscribe(events)` | Turns, partial transcripts, keypresses |
351
58
 
352
- const stream = await response.stream({
353
- direction: StreamDirection.BOTH
354
- });
355
-
356
- stream.onPayload((payload) => {
357
- // Use the payload
358
- });
359
-
360
- // Or write to the stream
361
- // stream.write({ type: StreamMessageType.AUDIO_OUT, payload: "\x00\x01\x02" });
362
- }
363
- ```
364
- <a name="VoiceResponse+sgather"></a>
365
-
366
- ### voiceResponse.sgather(options) ⇒ <code>Promise.&lt;StreamGatherStream&gt;</code>
367
- Starts a server-side stream gather operation which sends transcription data to the voice server.
368
-
369
- **Kind**: instance method of [<code>VoiceResponse</code>](#VoiceResponse)
370
- **Returns**: <code>Promise.&lt;StreamGatherStream&gt;</code> - The stream gather object
371
- **See**: Gather
372
-
373
- | Param | Type | Description |
374
- | --- | --- | --- |
375
- | options | <code>StreamGatherOptions</code> | Options to control the stream gather operation |
376
- | options.source | <code>StreamGatherSource</code> | The source to gather data from (DTMF, SPEECH, SPEECH_AND_DTMF). Default is SPEECH |
377
-
378
- **Example**
379
- ```js
380
- async function handler (request, response) {
381
- await response.answer();
382
- const sGather = await response.sgather({ source: StreamGatherSource.SPEECH });
383
- sGather.onPayload((payload) => {
384
- console.log("Payload: %s", payload);
385
- });
386
- }
387
- ```
388
- <a name="VoiceResponse+mute"></a>
389
-
390
- ### voiceResponse.mute(options)
391
- Mutes a call.
392
-
393
- **Kind**: instance method of [<code>VoiceResponse</code>](#VoiceResponse)
394
- **See**: unmute
395
-
396
- | Param | Type | Description |
397
- | --- | --- | --- |
398
- | options | <code>MuteOptions</code> | Options to control the mute operation |
399
- | options.direction | <code>MuteDirection</code> | The direction to mute the channel (IN, OUT, BOTH). Default is BOTH |
400
-
401
- **Example**
402
- ```js
403
- async function handler (request, response) {
404
- await response.answer();
405
- await response.mute(); // Will mute both directions
406
- }
407
- ```
408
- <a name="VoiceResponse+unmute"></a>
409
-
410
- ### voiceResponse.unmute(options)
411
- Unmute a call.
412
-
413
- **Kind**: instance method of [<code>VoiceResponse</code>](#VoiceResponse)
414
- **See**: mute
415
-
416
- | Param | Type | Description |
417
- | --- | --- | --- |
418
- | options | <code>MuteOptions</code> | Options to control the unmute operation |
419
- | options.direction | <code>MuteDirection</code> | The direction to unmute the call (IN, OUT, BOTH). Default is BOTH |
420
-
421
- **Example**
422
- ```js
423
- async function handler (request, response) {
424
- await response.answer();
425
- await response.unmute(); // Will unmute both directions
426
- }
427
- ```
428
- <a name="VoiceResponse+on"></a>
429
-
430
- ### voiceResponse.on(event, listener)
431
- Register a listener for the given event.
432
-
433
- **Kind**: instance method of [<code>VoiceResponse</code>](#VoiceResponse)
434
-
435
- | Param | Type | Description |
436
- | --- | --- | --- |
437
- | event | <code>StreamEvent</code> | The event to listen for |
438
- | listener | <code>function</code> | The callback function |
439
-
440
- **Example**
441
- ```js
442
- async function handler (request, response) {
443
- ...
444
-
445
- response.on(StreamEvent.END, () => {
446
- console.log("Call ended");
447
- });
448
- }
449
- ```
59
+ Fields: `callRef`, `appRef`, `from`, `to`, `direction`, `metadata`.
450
60
 
61
+ Coming from the gRPC SDK (`(req, res)` handlers, `VoiceResponse`, `stream`, `sgather`, `dial`)?
62
+ Those are gone; see `docs/migrating-to-sessions.md`. `@fonoster/voice/session` resolves to this
63
+ package for one more release.
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Copyright (C) 2025 by Fonoster Inc (https://fonoster.com)
3
+ * http://github.com/fonoster/fonoster
4
+ *
5
+ * This file is part of Fonoster
6
+ *
7
+ * Licensed under the MIT License (the "License");
8
+ * you may not use this file except in compliance with
9
+ * the License. You may obtain a copy of the License at
10
+ *
11
+ * https://opensource.org/licenses/MIT
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+ import { SessionStart } from "@fonoster/common";
20
+ import { GatherOptions, GatherResult, PlayOptions, PlayResult, SayOptions, SayResult, SessionEventMap, SessionEventName, TransferOptions } from "./types";
21
+ /** What the session needs from its socket. */
22
+ export type SessionTransport = {
23
+ sendJson(message: Record<string, unknown>): void;
24
+ sendBinary(data: Buffer): void;
25
+ close(): void;
26
+ };
27
+ /**
28
+ * One call's session, built on the fonoster.session.v1 messages (voice-sdk S8). Verbs are
29
+ * conveniences: each sends one command and resolves on the event that answers it.
30
+ */
31
+ export declare class Session {
32
+ private readonly transport;
33
+ readonly sessionRef: string;
34
+ readonly callRef: string;
35
+ readonly appRef: string;
36
+ readonly direction: SessionStart["direction"];
37
+ readonly from: string;
38
+ readonly to: string;
39
+ readonly metadata: Record<string, unknown>;
40
+ /** The W3C traceparent of the call's span (session.start `traceparent`), when sent. */
41
+ readonly traceparent?: string;
42
+ /** The payload of the handoff that started this session (session.start `handoff`), or null. */
43
+ readonly handoffPayload: Record<string, unknown> | null;
44
+ readonly speech: SessionStart["speech"];
45
+ readonly audioFormat: SessionStart["audio"];
46
+ readonly tracks: SessionStart["tracks"];
47
+ /**
48
+ * The app and workspace AppBridge's app-bound signature named, when the server verified one
49
+ * (VoiceServer); undefined otherwise.
50
+ */
51
+ readonly signedFor?: {
52
+ appRef: string;
53
+ workspaceRef: string;
54
+ };
55
+ /** Raw audio (the Audio layer). */
56
+ readonly audio: {
57
+ onFrame(cb: (frame: Buffer, trackId: number) => void): void;
58
+ send(frame: Buffer): void;
59
+ mark(name: string): Promise<{
60
+ cleared: boolean;
61
+ }>;
62
+ clear(): void;
63
+ };
64
+ private seq;
65
+ private outputs;
66
+ private commands;
67
+ private marks;
68
+ private endWaiters;
69
+ private listeners;
70
+ private frameListeners;
71
+ private gatherState?;
72
+ private buffered;
73
+ private lastWarmTransfer?;
74
+ /** A pending completeTransfer(), rejected when AppBridge refuses it. */
75
+ private completing?;
76
+ private endedReason?;
77
+ private listening;
78
+ constructor(start: SessionStart, transport: SessionTransport, signedFor?: {
79
+ appRef: string;
80
+ workspaceRef: string;
81
+ });
82
+ get ended(): boolean;
83
+ /** Speaks text, or a stream of text pieces as one utterance, and reports what was heard. */
84
+ say(input: string | AsyncIterable<string>, options?: SayOptions): Promise<SayResult>;
85
+ /** Cuts the agent off: every pending say and play resolves as interrupted. */
86
+ stopSay(): Promise<void>;
87
+ play(url: string, options?: PlayOptions): Promise<PlayResult>;
88
+ /** Waits for speech (turn.ended) or digits (dtmf), whichever comes first. */
89
+ gather(options?: GatherOptions): Promise<GatherResult>;
90
+ /**
91
+ * Starts listening or changes how AppBridge listens (`listen.update`): recognition language,
92
+ * hints, and the turn-taking patience knob (`low`, `normal` or `high`).
93
+ */
94
+ listen(options?: {
95
+ language?: string;
96
+ hints?: string[];
97
+ patience?: "low" | "normal" | "high";
98
+ }): Promise<void>;
99
+ /** Requests opt-in events: "turn.estimate", "transcript.partial". */
100
+ subscribe(events: ("turn.estimate" | "transcript.partial")[]): Promise<void>;
101
+ on<E extends SessionEventName>(event: E, cb: (data: SessionEventMap[E]) => void): this;
102
+ off<E extends SessionEventName>(event: E, cb: (data: SessionEventMap[E]) => void): this;
103
+ playDtmf(digits: string): Promise<void>;
104
+ /** Transfers the call; resolves when AppBridge ends the session with reason transfer. */
105
+ transfer(to: string, options?: TransferOptions): Promise<void>;
106
+ /**
107
+ * Completes the pending warm transfer: the agent leaves, caller and new party stay. Resolves
108
+ * when the session ends with reason transfer; rejects with a SessionError
109
+ * (`transfer_not_answered`) when the new party has not answered yet, which leaves the transfer
110
+ * pending: wait for the `transfer.answered` event and call it again.
111
+ */
112
+ completeTransfer(): Promise<void>;
113
+ hold(options?: {
114
+ music?: string;
115
+ }): Promise<void>;
116
+ resume(): Promise<void>;
117
+ /** Hands the call to another app with a payload; resolves when this session ends. */
118
+ handoff(app: string, payload?: Record<string, unknown>): Promise<void>;
119
+ /** Hangs up; resolves when the session ends. */
120
+ hangup(): Promise<void>;
121
+ /**
122
+ * Adds a note to the call record (record.note).
123
+ *
124
+ * @internal Reserved for Fonoster's own apps (Autopilot); not part of the public SDK and may change.
125
+ */
126
+ note(data: Record<string, unknown>): Promise<void>;
127
+ /**
128
+ * Adds model token usage to the call (usage.report, add-usage-metering).
129
+ *
130
+ * @internal Reserved for Fonoster's own Autopilot; AppBridge refuses it from other apps.
131
+ */
132
+ reportUsage(lines: Record<string, unknown>[]): Promise<void>;
133
+ /** Resolves at once: AppBridge answers the call on connect (voice-sdk S2). */
134
+ answer(): Promise<void>;
135
+ /** Feeds one frame from the socket. */
136
+ receive(data: Buffer, isBinary: boolean): void;
137
+ /** The socket closed. */
138
+ closed(): void;
139
+ private onMessage;
140
+ private onError;
141
+ private input;
142
+ private feedGather;
143
+ private armGatherTimer;
144
+ private endGather;
145
+ private nextId;
146
+ private send;
147
+ /** Sends a command whose only answer is a possible error (logged as a session error). */
148
+ private command;
149
+ private awaitOutput;
150
+ /**
151
+ * The app owns interruption (appbridge B22): when the caller starts talking over an interruptible
152
+ * say or play, the SDK cuts it off with clear; a non-interruptible one keeps playing.
153
+ */
154
+ private bargeIn;
155
+ private take;
156
+ private untilEnd;
157
+ private finish;
158
+ private emit;
159
+ private compact;
160
+ private without;
161
+ }