@voctiv/agent-sdk 0.1.3 → 0.2.1
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/define-script.d.ts +155 -52
- package/dist/define-script.d.ts.map +1 -1
- package/dist/define-script.js +15 -3
- package/dist/define-script.js.map +1 -1
- package/dist/index.d.ts +27 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -1
- package/dist/index.js.map +1 -1
- package/dist/types/asr-handle.d.ts +94 -30
- package/dist/types/asr-handle.d.ts.map +1 -1
- package/dist/types/events.d.ts +7 -7
- package/dist/types/events.d.ts.map +1 -1
- package/dist/types/legacy-phrase.d.ts +45 -0
- package/dist/types/legacy-phrase.d.ts.map +1 -0
- package/dist/types/legacy-phrase.js +19 -0
- package/dist/types/legacy-phrase.js.map +1 -0
- package/dist/types/logger.d.ts +4 -2
- package/dist/types/logger.d.ts.map +1 -1
- package/dist/types/media-channel.d.ts +610 -84
- package/dist/types/media-channel.d.ts.map +1 -1
- package/dist/types/mixer.d.ts +97 -20
- package/dist/types/mixer.d.ts.map +1 -1
- package/dist/types/nlu.d.ts +20 -10
- package/dist/types/nlu.d.ts.map +1 -1
- package/dist/types/text-input.d.ts +26 -2
- package/dist/types/text-input.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -2,194 +2,720 @@ import { Observable } from 'rxjs';
|
|
|
2
2
|
import { AsrConfig, AsrHandle } from './asr-handle';
|
|
3
3
|
import { DtmfEvent, SipInfo, SipSignal, DataMessage } from './events';
|
|
4
4
|
import { MixerQueueControl, PlayOptions } from './mixer';
|
|
5
|
+
import type { LegacyPhraseRecord } from './legacy-phrase';
|
|
5
6
|
import { TextInput } from './text-input';
|
|
6
7
|
/**
|
|
7
|
-
* Audio playback API — TTS synthesis, file playback, and queue
|
|
8
|
+
* Audio playback API — TTS synthesis, file/URL playback, caching, and multi-queue mixing.
|
|
8
9
|
*
|
|
9
|
-
* Audio is routed through a multi-queue mixer. Each queue
|
|
10
|
-
*
|
|
10
|
+
* Audio is routed through a **multi-queue mixer** (indices **0–4**). Each queue has its own
|
|
11
|
+
* volume and lifecycle; use different **`queue`** values in {@link PlayOptions} so earcons,
|
|
12
|
+
* hold music, and agent TTS can be **`stop`**ped independently.
|
|
13
|
+
*
|
|
14
|
+
* ### Pre-answer behaviour (SIP)
|
|
15
|
+
*
|
|
16
|
+
* For SIP calls, `say()` and `play()` automatically wait for the RTP pipeline to be ready
|
|
17
|
+
* (early media or 200 OK) before starting playback. You do **not** need to call
|
|
18
|
+
* `sip.waitForEarly()` / `sip.waitForAnswer()` manually before speaking — the audio
|
|
19
|
+
* operation waits internally and starts as soon as the call enters `"early"` or `"active"` state.
|
|
20
|
+
*
|
|
21
|
+
* If the call terminates **before** any media is available, deferred `say`/`play` calls
|
|
22
|
+
* resolve as no-ops (they do not throw).
|
|
11
23
|
*/
|
|
12
24
|
export interface ChannelAudio {
|
|
13
25
|
/**
|
|
14
|
-
* Synthesize text
|
|
15
|
-
*
|
|
16
|
-
*
|
|
26
|
+
* Synthesize text with TTS and play the result on the selected mixer queue.
|
|
27
|
+
*
|
|
28
|
+
* On WS channels the runtime also emits text/audio progress events to the client
|
|
29
|
+
* (`TEXT_*`, `AUDIO_*`). On headless channels this is a no-op that only logs a warning.
|
|
30
|
+
*
|
|
31
|
+
* @param input - Plain string, or `Observable<string>` of chunks for streaming synthesis.
|
|
32
|
+
* @param options - Queue, alias, queue volume, {@link PlayOptions.ttsVendor},
|
|
33
|
+
* {@link PlayOptions.name} (LE `key_storage` row), {@link PlayOptions.ttsConfig},
|
|
34
|
+
* {@link PlayOptions.ttsStrategy}, etc.
|
|
35
|
+
* @returns Resolves when playback of this **`say`** invocation has finished (queue may still
|
|
36
|
+
* contain other items).
|
|
37
|
+
*
|
|
38
|
+
* On SIP channels, if the call has not yet reached `"early"`/`"active"` state when
|
|
39
|
+
* `say()` is invoked, the call automatically waits for media availability. The Promise
|
|
40
|
+
* resolves as a no-op if the call terminates before media starts flowing.
|
|
17
41
|
*/
|
|
18
42
|
say(input: string | Observable<string>, options?: PlayOptions): Promise<void>;
|
|
19
43
|
/**
|
|
20
|
-
* Play a
|
|
21
|
-
*
|
|
44
|
+
* Play audio from a URL/path string, or a {@link LegacyPhraseRecord} from the Voctiv platform phrase DB.
|
|
45
|
+
*
|
|
46
|
+
* The runtime decodes the full source to PCM and pushes it to the mixer. `ttsVendor`,
|
|
47
|
+
* `ttsConfig`, `ttsStrategy`, and `legacySavePhrase` do not affect raw playback.
|
|
48
|
+
*
|
|
49
|
+
* @param source - HTTP(S) URL, local path, or structured {@link LegacyPhraseRecord} (no TTS).
|
|
50
|
+
* @param options - Queue, alias, volume, loop; **`tts*`** fields apply only if the host
|
|
51
|
+
* ever wraps playback (normally ignored for raw audio).
|
|
52
|
+
*
|
|
53
|
+
* On SIP channels, automatically waits for early/active state before starting
|
|
54
|
+
* (same behaviour as `say()`).
|
|
55
|
+
*/
|
|
56
|
+
play(source: string | LegacyPhraseRecord, options?: PlayOptions): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Download/decode **`source`** through the audio player.
|
|
59
|
+
*
|
|
60
|
+
* In the current API runtime this warms the decoder/audio-player path for `play()`;
|
|
61
|
+
* it does not synthesize TTS and does not populate the TTS cache used by `presay()`.
|
|
62
|
+
* @param source - URL or file path accepted by the host’s fetch layer.
|
|
22
63
|
*/
|
|
23
|
-
play(source: string, options?: PlayOptions): Promise<void>;
|
|
24
|
-
/** Download, decode and cache audio (URL or file path) as PCM for instant playback. */
|
|
25
64
|
preload(source: string): Promise<void>;
|
|
26
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* Run TTS ahead of time and store PCM in the host TTS cache.
|
|
67
|
+
*
|
|
68
|
+
* Later `say()` calls with the same resolved TTS config and text can reuse the cached
|
|
69
|
+
* file. If the cache service is unavailable, the current runtime logs a warning and
|
|
70
|
+
* resolves without throwing.
|
|
71
|
+
* @param text - Full text to synthesize.
|
|
72
|
+
* @param options - Same TTS-related options as **`say`** (vendor, **`name`**, **`ttsConfig`**).
|
|
73
|
+
*/
|
|
27
74
|
presay(text: string, options?: PlayOptions): Promise<void>;
|
|
28
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* @param index - Mixer queue **0–4**.
|
|
77
|
+
* @returns Control handle for that queue’s volume and item lifecycle observables.
|
|
78
|
+
*/
|
|
29
79
|
queue(index: number): MixerQueueControl;
|
|
30
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* Remove one item by **`alias`**.
|
|
82
|
+
*
|
|
83
|
+
* When **`queue`** is omitted, the SIP/WS runtime searches all five queues. When set,
|
|
84
|
+
* only that queue is inspected. For sentence-split TTS, internal queue item aliases are
|
|
85
|
+
* suffixed as `alias-0`, `alias-1`, etc.; remove the concrete suffix if you need to
|
|
86
|
+
* cancel one synthesized sentence.
|
|
87
|
+
* @param alias - **`PlayOptions.alias`** of the item to drop.
|
|
88
|
+
* @param queue - Queue index; defaults to **0**.
|
|
89
|
+
*/
|
|
31
90
|
remove(alias: string, queue?: number): void;
|
|
32
|
-
/**
|
|
91
|
+
/**
|
|
92
|
+
* Stop playback and clear all pending items on a single queue.
|
|
93
|
+
*
|
|
94
|
+
* Also aborts in-flight sentence TTS generation for that queue in the current runtime.
|
|
95
|
+
* @param queue - Queue index **0–4**.
|
|
96
|
+
*/
|
|
33
97
|
stop(queue: number): void;
|
|
34
|
-
/** Stop and clear all queues. */
|
|
98
|
+
/** Stop and clear **all** queues; WS clients also receive an audio interrupt signal. */
|
|
35
99
|
stopAll(): void;
|
|
36
100
|
}
|
|
37
|
-
/**
|
|
101
|
+
/** Channel-level observables: VAD-related speech, barge-in, session end, and arbitrary data messages. */
|
|
38
102
|
export interface ChannelEvents {
|
|
39
|
-
/**
|
|
103
|
+
/** User started speaking (VAD speech start, socket speech event, or synthetic text input). */
|
|
40
104
|
readonly speechStart$: Observable<void>;
|
|
41
|
-
/**
|
|
105
|
+
/** User stopped speaking (VAD speech end, socket speech event, ASR final, or synthetic text input). */
|
|
42
106
|
readonly speechEnd$: Observable<void>;
|
|
43
|
-
/**
|
|
107
|
+
/** User speech caused an interrupt of bot audio (barge-in path; may be inert without VAD). */
|
|
44
108
|
readonly interrupt$: Observable<void>;
|
|
45
|
-
/**
|
|
109
|
+
/** Session is ending: hangup, client disconnect, or explicit **`channel.destroy()`**. */
|
|
46
110
|
readonly terminated$: Observable<void>;
|
|
47
|
-
/**
|
|
111
|
+
/** Structured messages from the WS client or bridge (event + payload). */
|
|
48
112
|
readonly message$: Observable<DataMessage>;
|
|
49
113
|
}
|
|
50
|
-
/** Options for
|
|
114
|
+
/** Options for **`channel.llm.ask`** / **`stream`** / **`makePersistentStream`**. */
|
|
51
115
|
export interface LlmOptions {
|
|
52
|
-
/** Override dialog UUID for LLM
|
|
116
|
+
/** Override dialog UUID for Omni/LLM tracing (defaults to script dialog). */
|
|
53
117
|
dialogUuid?: string;
|
|
54
|
-
/**
|
|
118
|
+
/** Chat role sent with the message, e.g. **`"assistant"`**, **`"user"`**. */
|
|
55
119
|
role?: string;
|
|
56
|
-
/**
|
|
120
|
+
/** When `true`, message may be hidden from user-visible transcript (host-dependent). */
|
|
57
121
|
hidden?: boolean;
|
|
58
|
-
/**
|
|
122
|
+
/**
|
|
123
|
+
* **LLM-only**: speaker / persona label in multi-party chat. **Not** related to
|
|
124
|
+
* {@link PlayOptions.name} (TTS **`key_storage.name`**).
|
|
125
|
+
*/
|
|
59
126
|
name?: string;
|
|
60
|
-
/**
|
|
127
|
+
/** Route the request to a specific Omni agent profile (UUID). */
|
|
61
128
|
agentUuid?: string;
|
|
62
|
-
/**
|
|
129
|
+
/** Multi-agent: current alias for routing. */
|
|
63
130
|
currentAgentAlias?: string;
|
|
64
|
-
/**
|
|
131
|
+
/** Opaque payload forwarded to the Omni LLM backend. */
|
|
65
132
|
payload?: Record<string, any>;
|
|
66
|
-
/**
|
|
133
|
+
/** Verbose logging on the LLM path. */
|
|
67
134
|
debug?: boolean;
|
|
68
|
-
/** Restrict
|
|
135
|
+
/** Restrict which agent aliases may handle the request. */
|
|
69
136
|
agentAliasFilter?: string[];
|
|
70
137
|
}
|
|
71
|
-
/** Options for
|
|
138
|
+
/** Options for **`channel.llm.extract`**. All fields except `dialogUuid` are forwarded to Omni extract. */
|
|
72
139
|
export interface ExtractOptions {
|
|
73
|
-
/** Override dialog UUID. */
|
|
140
|
+
/** Override dialog UUID for extraction context; defaults to the current script dialog. */
|
|
74
141
|
dialogUuid?: string;
|
|
75
|
-
/**
|
|
142
|
+
/** Optional extraction prompt/instruction forwarded to Omni. */
|
|
76
143
|
prompt?: string;
|
|
77
|
-
/**
|
|
144
|
+
/** Model name or id understood by the Omni backend. */
|
|
78
145
|
model?: string;
|
|
79
|
-
/**
|
|
146
|
+
/** Nucleus sampling value forwarded to Omni. */
|
|
80
147
|
topP?: number;
|
|
81
|
-
/** Temperature
|
|
148
|
+
/** Temperature forwarded to Omni. */
|
|
82
149
|
temperature?: number;
|
|
83
|
-
/** Custom model
|
|
150
|
+
/** Custom model descriptor forwarded as-is. */
|
|
84
151
|
customModel?: Record<string, any>;
|
|
85
152
|
[key: string]: unknown;
|
|
86
153
|
}
|
|
87
|
-
/**
|
|
154
|
+
/** One chunk from **`channel.llm.stream`**. */
|
|
88
155
|
export interface LlmStreamChunk {
|
|
89
|
-
/** Request ID. */
|
|
90
156
|
id: string;
|
|
91
|
-
/** Sequential chunk number. */
|
|
92
157
|
chunkId: number;
|
|
93
|
-
/** Text content of this chunk. */
|
|
94
158
|
content: string;
|
|
95
|
-
/** Non-null when the stream is done (e.g. `"stop"`, `"length"`). */
|
|
96
159
|
finishReason: string | null;
|
|
97
|
-
/** Raw SSE event data. */
|
|
98
160
|
event: any;
|
|
99
|
-
/** Tool call messages (function calling). */
|
|
100
161
|
toolMessages?: any[];
|
|
101
|
-
/** Full raw response object. */
|
|
102
162
|
raw: Record<string, any>;
|
|
103
163
|
}
|
|
104
164
|
/**
|
|
105
|
-
* Long-lived
|
|
165
|
+
* Long-lived LLM Socket.IO stream — amortizes connection setup across many user turns.
|
|
106
166
|
*/
|
|
107
167
|
export interface PersistentLlmStreamHandle {
|
|
108
168
|
readonly stream$: Observable<LlmStreamChunk>;
|
|
169
|
+
/** Send one user/assistant turn over the existing stream; options override defaults for this send only. */
|
|
109
170
|
send(message: string, options?: LlmOptions): void;
|
|
171
|
+
/** Close the underlying Socket.IO stream without destroying the handle object. */
|
|
110
172
|
disconnect(): void;
|
|
173
|
+
/** Re-open the underlying Socket.IO stream after {@link disconnect}. */
|
|
111
174
|
reconnect(): void;
|
|
112
175
|
}
|
|
113
|
-
/**
|
|
114
|
-
* LLM (Large Language Model) API — text generation and structured extraction.
|
|
115
|
-
*/
|
|
176
|
+
/** LLM facade on the media channel. */
|
|
116
177
|
export interface ChannelLlm {
|
|
117
|
-
/**
|
|
178
|
+
/** Single-shot completion for **`message`**; consumes the Omni SSE stream and returns concatenated text. */
|
|
118
179
|
ask(message: string, options?: LlmOptions): Promise<string>;
|
|
119
|
-
/**
|
|
180
|
+
/** Token/chunk stream for **`message`** parsed from Omni SSE `data:` events. */
|
|
120
181
|
stream(message: string, options?: LlmOptions): Observable<LlmStreamChunk>;
|
|
121
|
-
/** Structured
|
|
182
|
+
/** Structured extraction / JSON-style fill from conversation context via Omni extract. */
|
|
122
183
|
extract(options?: ExtractOptions): Promise<Record<string, any>>;
|
|
123
184
|
/**
|
|
124
|
-
* Open a persistent
|
|
125
|
-
*
|
|
185
|
+
* Open a persistent Omni chat stream (see host docs for URL and auth).
|
|
186
|
+
* @param options - Default **`agentUuid`**, **`dialogUuid`**, etc.; per-**`send`** overrides allowed.
|
|
126
187
|
*/
|
|
127
188
|
makePersistentStream(options?: LlmOptions): PersistentLlmStreamHandle;
|
|
128
189
|
}
|
|
129
190
|
/**
|
|
130
|
-
* SIP
|
|
191
|
+
* SIP call state visible to script developers.
|
|
192
|
+
*
|
|
193
|
+
* ### Lifecycle
|
|
194
|
+
*
|
|
195
|
+
* ```
|
|
196
|
+
* outbound 183 w/ SDP
|
|
197
|
+
* idle ──► ringing ─────────────────► early ──► active ──► terminated
|
|
198
|
+
* │ ▲ ▲
|
|
199
|
+
* │ inbound │ │
|
|
200
|
+
* │ sendProgress()───────┘ │
|
|
201
|
+
* └───────────────────────────────────┘ (early may be skipped)
|
|
202
|
+
* ```
|
|
203
|
+
*
|
|
204
|
+
* | State | Meaning | How you get here |
|
|
205
|
+
* |---|---|---|
|
|
206
|
+
* | **`idle`** | Call object exists but no SIP signalling has started yet. | Initial state. |
|
|
207
|
+
* | **`ringing`** | INVITE sent (outbound) or received (inbound); remote party is ringing. No media yet. | Automatic after INVITE. |
|
|
208
|
+
* | **`early`** | RTP pipeline is up — you can **send and receive audio**, run ASR, play TTS. This is the "pre-answer" phase. Billing typically has **not** started. | **Outbound:** remote sends 183 Session Progress with SDP. **Inbound:** your script calls `sip.sendProgress()`. |
|
|
209
|
+
* | **`active`** | 200 OK received/sent — the call is fully established. Billing starts. | **Outbound:** remote answers. **Inbound:** your script calls `sip.answer()`. |
|
|
210
|
+
* | **`holding`** | Local hold is active. | Your script calls `sip.hold()`. |
|
|
211
|
+
* | **`terminated`** | Call ended (BYE, CANCEL, or error). No further audio is possible. | Either side hangs up, or network error. |
|
|
212
|
+
*
|
|
213
|
+
* > **Note:** not every call goes through every state. A fast answer may jump
|
|
214
|
+
* > `ringing` → `active` without `early`. An unanswered outbound call may go
|
|
215
|
+
* > `ringing` → `terminated`. Inbound calls stay in `ringing` until you call
|
|
216
|
+
* > either `sendProgress()` (→ `early`) or `answer()` (→ `active`).
|
|
217
|
+
*/
|
|
218
|
+
export type SipState = 'idle' | 'ringing' | 'early' | 'active' | 'holding' | 'terminated';
|
|
219
|
+
/**
|
|
220
|
+
* A SIP 1xx provisional response forwarded to the script.
|
|
221
|
+
*
|
|
222
|
+
* Provisional responses are sent by the remote party **before** the final answer (200 OK).
|
|
223
|
+
* Common examples:
|
|
224
|
+
*
|
|
225
|
+
* | Code | Phrase | Typical meaning |
|
|
226
|
+
* |------|--------|-----------------|
|
|
227
|
+
* | 100 | Trying | Request received, processing |
|
|
228
|
+
* | 180 | Ringing | Remote phone is ringing (may carry SDP → early media) |
|
|
229
|
+
* | 183 | Session Progress | Early media available — SDP is present, RTP can flow |
|
|
230
|
+
*
|
|
231
|
+
* Subscribe to `sip.progress$` to track every provisional response. The `sdp` field
|
|
232
|
+
* is present only when the response carries a Session Description (media offer/answer).
|
|
233
|
+
*/
|
|
234
|
+
export interface SipProgressEvent {
|
|
235
|
+
/** SIP status code (100–199). */
|
|
236
|
+
statusCode: number;
|
|
237
|
+
/** Human-readable reason phrase, e.g. `"Ringing"`, `"Session Progress"`. */
|
|
238
|
+
statusPhrase: string;
|
|
239
|
+
/** Raw SDP body when the provisional response carries a media description. */
|
|
240
|
+
sdp?: string;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* SIP channel API — call-state observables, DTMF, INFO, hold/mute/hangup,
|
|
244
|
+
* outbound **`makeCall`**, **`bridge`**, and **pre-answer media**.
|
|
245
|
+
*
|
|
246
|
+
* ---
|
|
247
|
+
*
|
|
248
|
+
* ### Pre-answer media (early media)
|
|
249
|
+
*
|
|
250
|
+
* Both **inbound** and **outbound** calls support full-duplex audio **before** the
|
|
251
|
+
* 200 OK answer. In the `"early"` state the RTP pipeline is fully operational —
|
|
252
|
+
* ASR, TTS, `audio.say()`, `audio.play()`, and DTMF all work **exactly the same**
|
|
253
|
+
* as in the `"active"` state.
|
|
254
|
+
*
|
|
255
|
+
* #### Outbound calls
|
|
256
|
+
*
|
|
257
|
+
* The remote side may send **183 Session Progress** with SDP (e.g. an IVR greeting,
|
|
258
|
+
* ringback tone, or DTMF challenge). The call enters `"early"` automatically.
|
|
259
|
+
*
|
|
260
|
+
* #### Inbound calls
|
|
261
|
+
*
|
|
262
|
+
* Call `sip.sendProgress()` to send **183 Session Progress** to the caller. The call
|
|
263
|
+
* enters `"early"`, audio flows in both directions, and **billing has not started**
|
|
264
|
+
* (billing typically begins at 200 OK, depending on the carrier). Call `sip.answer()`
|
|
265
|
+
* later to complete the answer.
|
|
266
|
+
*
|
|
267
|
+
* ---
|
|
268
|
+
*
|
|
269
|
+
* ### Quick examples
|
|
270
|
+
*
|
|
271
|
+
* #### Outbound — interact with an IVR before answer
|
|
272
|
+
*
|
|
273
|
+
* ```ts
|
|
274
|
+
* const call = await channel.sip.makeCall({ sipUri: 'sip:+1234@trunk.example.com' });
|
|
275
|
+
*
|
|
276
|
+
* // Wait until RTP is ready (early media or full answer)
|
|
277
|
+
* await call.sip.waitForEarly();
|
|
278
|
+
* // Audio flows — start ASR to listen to the remote IVR
|
|
279
|
+
* const asr = await call.createAsr();
|
|
280
|
+
* asr.result$.subscribe((text) => {
|
|
281
|
+
* if (/press 1/i.test(text)) call.sip.sendDtmf('1');
|
|
282
|
+
* });
|
|
283
|
+
*
|
|
284
|
+
* // Optionally wait for the actual answer
|
|
285
|
+
* await call.sip.waitForAnswer();
|
|
286
|
+
* await call.audio.say('Hello! We are calling about your order.');
|
|
287
|
+
* ```
|
|
288
|
+
*
|
|
289
|
+
* #### Inbound — collect data before billing starts
|
|
290
|
+
*
|
|
291
|
+
* ```ts
|
|
292
|
+
* // Inbound call arrives — state is 'ringing'
|
|
293
|
+
* channel.sip.sendProgress();
|
|
294
|
+
* // State is now 'early' — full duplex audio, billing NOT started
|
|
295
|
+
*
|
|
296
|
+
* const asr = await channel.createAsr();
|
|
297
|
+
* await channel.audio.say('Please say your account number.');
|
|
298
|
+
* const account = await firstValueFrom(asr.result$);
|
|
299
|
+
*
|
|
300
|
+
* // Now answer — billing starts
|
|
301
|
+
* channel.sip.answer();
|
|
302
|
+
* await channel.audio.say(`Thank you! Looking up account ${account}…`);
|
|
303
|
+
* ```
|
|
304
|
+
*
|
|
305
|
+
* #### Inbound — simple answer (no pre-answer)
|
|
306
|
+
*
|
|
307
|
+
* ```ts
|
|
308
|
+
* // Inbound call — answer immediately
|
|
309
|
+
* channel.sip.answer();
|
|
310
|
+
* await channel.audio.say('Welcome!');
|
|
311
|
+
* ```
|
|
312
|
+
*
|
|
313
|
+
* #### Audio auto-buffering
|
|
314
|
+
*
|
|
315
|
+
* You do **not** need to manually wait for early/answer before calling `audio.say()`
|
|
316
|
+
* or `audio.play()`. These methods automatically defer until the RTP pipeline is ready
|
|
317
|
+
* and resolve as no-ops if the call terminates first. The explicit `waitForEarly()` /
|
|
318
|
+
* `waitForAnswer()` await points are useful when you need to **sequence logic** around
|
|
319
|
+
* call state (e.g. create ASR only after media is available).
|
|
131
320
|
*/
|
|
132
321
|
export interface ChannelSip {
|
|
133
|
-
/**
|
|
322
|
+
/**
|
|
323
|
+
* Emits every time the remote party sends a DTMF digit (RFC 2833 in-band or SIP INFO).
|
|
324
|
+
*
|
|
325
|
+
* ```ts
|
|
326
|
+
* sip.dtmf$.subscribe(({ digit, duration }) => {
|
|
327
|
+
* console.log(`User pressed ${digit}`);
|
|
328
|
+
* });
|
|
329
|
+
* ```
|
|
330
|
+
*/
|
|
134
331
|
readonly dtmf$: Observable<DtmfEvent>;
|
|
135
|
-
/**
|
|
332
|
+
/**
|
|
333
|
+
* Raw SIP INFO messages received on this call leg.
|
|
334
|
+
*
|
|
335
|
+
* Useful for carrier-specific signalling (e.g. `application/dtmf-relay`). On the
|
|
336
|
+
* current SIP runtime this stream is low-level and may not include parsed body
|
|
337
|
+
* details for every Sofia event; prefer `dtmf$` for DTMF.
|
|
338
|
+
*/
|
|
136
339
|
readonly sipInfo$: Observable<SipInfo>;
|
|
137
|
-
/**
|
|
340
|
+
/**
|
|
341
|
+
* Low-level SIP state-change events from the underlying Sofia stack.
|
|
342
|
+
*
|
|
343
|
+
* Prefer the higher-level **`state$`**, **`progress$`**, **`early$`**, **`answered$`**
|
|
344
|
+
* observables unless you need raw Sofia event details.
|
|
345
|
+
*/
|
|
138
346
|
readonly sipSignal$: Observable<SipSignal>;
|
|
139
|
-
/**
|
|
347
|
+
/**
|
|
348
|
+
* Live observable of the current call state.
|
|
349
|
+
*
|
|
350
|
+
* Emits the new {@link SipState} every time the call transitions. Starts with the
|
|
351
|
+
* state the call was in when the script began (typically `"ringing"` for inbound,
|
|
352
|
+
* `"idle"` or `"ringing"` for outbound).
|
|
353
|
+
*
|
|
354
|
+
* ```ts
|
|
355
|
+
* sip.state$.subscribe((state) => {
|
|
356
|
+
* console.log(`Call state → ${state}`);
|
|
357
|
+
* });
|
|
358
|
+
* ```
|
|
359
|
+
*
|
|
360
|
+
* > **Tip:** for one-shot checks use the synchronous `sip.state` getter instead.
|
|
361
|
+
*/
|
|
362
|
+
readonly state$: Observable<SipState>;
|
|
363
|
+
/**
|
|
364
|
+
* Current call state at the moment of access (synchronous).
|
|
365
|
+
*
|
|
366
|
+
* Returns one of: `'idle'`, `'ringing'`, `'early'`, `'active'`, `'holding'`, `'terminated'`.
|
|
367
|
+
*
|
|
368
|
+
* ```ts
|
|
369
|
+
* if (sip.state === 'active') {
|
|
370
|
+
* await audio.say('Call is live');
|
|
371
|
+
* }
|
|
372
|
+
* ```
|
|
373
|
+
*/
|
|
374
|
+
readonly state: SipState;
|
|
375
|
+
/**
|
|
376
|
+
* Whether the call has been answered — `true` after 200 OK is received (outbound)
|
|
377
|
+
* or sent (inbound via `sip.answer()`).
|
|
378
|
+
*
|
|
379
|
+
* This is a synchronous getter. For an awaitable version use {@link waitForAnswer}.
|
|
380
|
+
*
|
|
381
|
+
* ```ts
|
|
382
|
+
* if (!sip.isAnswered) {
|
|
383
|
+
* console.log('Still waiting for answer…');
|
|
384
|
+
* }
|
|
385
|
+
* ```
|
|
386
|
+
*/
|
|
387
|
+
readonly isAnswered: boolean;
|
|
388
|
+
/**
|
|
389
|
+
* Emits every SIP 1xx provisional response (180 Ringing, 183 Session Progress, etc.).
|
|
390
|
+
*
|
|
391
|
+
* Useful for tracking ringing state, ringback-tone detection, or reading SDP from
|
|
392
|
+
* early 183 responses. Each event is a {@link SipProgressEvent} with `statusCode`,
|
|
393
|
+
* `statusPhrase`, and optionally `sdp`.
|
|
394
|
+
*
|
|
395
|
+
* ```ts
|
|
396
|
+
* sip.progress$.subscribe(({ statusCode, statusPhrase }) => {
|
|
397
|
+
* console.log(`1xx: ${statusCode} ${statusPhrase}`);
|
|
398
|
+
* });
|
|
399
|
+
* ```
|
|
400
|
+
*
|
|
401
|
+
* > **Note:** `progress$` may emit **zero** events if the remote party answers
|
|
402
|
+
* > immediately (200 OK without any provisional).
|
|
403
|
+
*/
|
|
404
|
+
readonly progress$: Observable<SipProgressEvent>;
|
|
405
|
+
/**
|
|
406
|
+
* Emits **once** when early media becomes available — the RTP pipeline is up and
|
|
407
|
+
* audio can be sent/received **before** the call is formally answered.
|
|
408
|
+
*
|
|
409
|
+
* **Outbound:** fires automatically when the remote side sends a 1xx with SDP
|
|
410
|
+
* (typically **183 Session Progress**).
|
|
411
|
+
*
|
|
412
|
+
* **Inbound:** fires after your script calls `sip.sendProgress()`.
|
|
413
|
+
*
|
|
414
|
+
* After this event fires:
|
|
415
|
+
* - `audio.say()` / `audio.play()` will be heard by the remote party
|
|
416
|
+
* - `createAsr()` will receive remote audio
|
|
417
|
+
* - `dtmf$` will deliver in-band DTMF
|
|
418
|
+
* - Billing has typically **not** started yet (depends on carrier)
|
|
419
|
+
*
|
|
420
|
+
* If the call is answered without any early media (outbound: no 183 with SDP;
|
|
421
|
+
* inbound: `answer()` called directly), `early$` **may not emit at all** —
|
|
422
|
+
* use `answered$` or `waitForAnswer()` instead.
|
|
423
|
+
*
|
|
424
|
+
* ```ts
|
|
425
|
+
* sip.early$.subscribe(() => {
|
|
426
|
+
* console.log('Early media — RTP is flowing before answer');
|
|
427
|
+
* });
|
|
428
|
+
* ```
|
|
429
|
+
*/
|
|
430
|
+
readonly early$: Observable<void>;
|
|
431
|
+
/**
|
|
432
|
+
* Emits **once** when the call is answered (200 OK received for outbound,
|
|
433
|
+
* or sent for inbound after `sip.answer()`).
|
|
434
|
+
*
|
|
435
|
+
* After this event the call state is `"active"` and the call is fully established.
|
|
436
|
+
*
|
|
437
|
+
* ```ts
|
|
438
|
+
* sip.answered$.subscribe(() => {
|
|
439
|
+
* console.log('Call answered — full duplex');
|
|
440
|
+
* });
|
|
441
|
+
* ```
|
|
442
|
+
*
|
|
443
|
+
* > For the awaitable version see {@link waitForAnswer}.
|
|
444
|
+
*/
|
|
445
|
+
readonly answered$: Observable<void>;
|
|
446
|
+
/**
|
|
447
|
+
* Returns a `Promise` that resolves when the call is answered (200 OK).
|
|
448
|
+
*
|
|
449
|
+
* If the call is **already answered** at the time of calling, resolves immediately.
|
|
450
|
+
*
|
|
451
|
+
* Useful in outbound scenarios where you want to block until the remote party picks up:
|
|
452
|
+
*
|
|
453
|
+
* ```ts
|
|
454
|
+
* const call = await channel.sip.makeCall({ sipUri: destination });
|
|
455
|
+
* await call.sip.waitForAnswer();
|
|
456
|
+
* await call.audio.say('You have picked up!');
|
|
457
|
+
* ```
|
|
458
|
+
*
|
|
459
|
+
* > **Warning:** if the call is never answered (busy, timeout, rejection) and your
|
|
460
|
+
* > script does not handle `events.terminated$`, this Promise will remain pending
|
|
461
|
+
* > until the call terminates (at which point the script exits).
|
|
462
|
+
*/
|
|
463
|
+
waitForAnswer(): Promise<void>;
|
|
464
|
+
/**
|
|
465
|
+
* Returns a `Promise` that resolves as soon as the RTP pipeline is ready —
|
|
466
|
+
* either on **early media** or on **answer**, whichever comes first.
|
|
467
|
+
*
|
|
468
|
+
* If the call is already in `"early"` or `"active"` state, resolves immediately.
|
|
469
|
+
*
|
|
470
|
+
* #### Outbound usage
|
|
471
|
+
*
|
|
472
|
+
* The recommended await point when you want to start ASR / play audio
|
|
473
|
+
* **as early as possible** (including pre-answer):
|
|
474
|
+
*
|
|
475
|
+
* ```ts
|
|
476
|
+
* const call = await channel.sip.makeCall({ sipUri: destination });
|
|
477
|
+
*
|
|
478
|
+
* // Don't wait for full answer — start as soon as any media path exists
|
|
479
|
+
* await call.sip.waitForEarly();
|
|
480
|
+
*
|
|
481
|
+
* // ASR is already receiving remote audio (IVR prompts, ringback, etc.)
|
|
482
|
+
* const asr = await call.createAsr();
|
|
483
|
+
* asr.result$.subscribe((text) => console.log('Heard:', text));
|
|
484
|
+
*
|
|
485
|
+
* // Play DTMF to navigate an IVR — works even before 200 OK
|
|
486
|
+
* call.sip.sendDtmf('1');
|
|
487
|
+
* ```
|
|
488
|
+
*
|
|
489
|
+
* #### Inbound usage
|
|
490
|
+
*
|
|
491
|
+
* For inbound calls you must call `sendProgress()` **first** to trigger early media,
|
|
492
|
+
* then `waitForEarly()` resolves:
|
|
493
|
+
*
|
|
494
|
+
* ```ts
|
|
495
|
+
* channel.sip.sendProgress();
|
|
496
|
+
* await channel.sip.waitForEarly(); // resolves immediately after sendProgress()
|
|
497
|
+
* const asr = await channel.createAsr();
|
|
498
|
+
* ```
|
|
499
|
+
*
|
|
500
|
+
* > **Tip:** some carriers answer outbound calls without early media (180 without SDP).
|
|
501
|
+
* > In that case `waitForEarly()` resolves only when the 200 OK arrives.
|
|
502
|
+
*/
|
|
503
|
+
waitForEarly(): Promise<void>;
|
|
504
|
+
/**
|
|
505
|
+
* Send a DTMF digit to the remote party.
|
|
506
|
+
*
|
|
507
|
+
* SIP channels delegate to the telephony stack. WS channels validate one digit
|
|
508
|
+
* and emit `dtmf-send` to the connected client. Headless channels ignore it.
|
|
509
|
+
*
|
|
510
|
+
* @param digit - One of `0`–`9`, `*`, `#`.
|
|
511
|
+
* @param duration - Tone duration in milliseconds (default **250 ms**).
|
|
512
|
+
*
|
|
513
|
+
* ```ts
|
|
514
|
+
* // Navigate an IVR: press 1, wait, press 3
|
|
515
|
+
* sip.sendDtmf('1');
|
|
516
|
+
* await new Promise((r) => setTimeout(r, 2000));
|
|
517
|
+
* sip.sendDtmf('3');
|
|
518
|
+
* ```
|
|
519
|
+
*/
|
|
140
520
|
sendDtmf(digit: string, duration?: number): void;
|
|
141
|
-
/**
|
|
521
|
+
/**
|
|
522
|
+
* Send a SIP INFO request with an arbitrary content type and body on this call leg.
|
|
523
|
+
*
|
|
524
|
+
* No-op on WS and headless channels.
|
|
525
|
+
*
|
|
526
|
+
* @param contentType - MIME type, e.g. `"application/dtmf-relay"`.
|
|
527
|
+
* @param body - Raw text body of the INFO request.
|
|
528
|
+
*/
|
|
142
529
|
sendInfo(contentType: string, body: string): void;
|
|
143
|
-
/**
|
|
530
|
+
/**
|
|
531
|
+
* Put the SIP call on hold (sends re-INVITE with `a=sendonly`).
|
|
532
|
+
*
|
|
533
|
+
* The remote party hears silence (or hold music if your carrier supports it).
|
|
534
|
+
* Call {@link unhold} to resume.
|
|
535
|
+
*/
|
|
144
536
|
hold(): void;
|
|
145
|
-
/**
|
|
537
|
+
/**
|
|
538
|
+
* Resume a held SIP call (sends re-INVITE with `a=sendrecv`).
|
|
539
|
+
*/
|
|
146
540
|
unhold(): void;
|
|
147
|
-
/**
|
|
541
|
+
/**
|
|
542
|
+
* Suppress outgoing SIP audio locally — the remote party hears silence,
|
|
543
|
+
* but you still receive their audio.
|
|
544
|
+
*
|
|
545
|
+
* This does **not** send a re-INVITE; it simply stops feeding PCM
|
|
546
|
+
* to the RTP encoder. Call {@link unmute} to resume.
|
|
547
|
+
*/
|
|
148
548
|
mute(): void;
|
|
149
|
-
/**
|
|
549
|
+
/** Resume sending audio after {@link mute}. No-op on WS/headless channels. */
|
|
150
550
|
unmute(): void;
|
|
151
|
-
/**
|
|
551
|
+
/**
|
|
552
|
+
* Hang up the call.
|
|
553
|
+
*
|
|
554
|
+
* SIP channels send BYE. WS channels disconnect the socket. Headless channels destroy
|
|
555
|
+
* the synthetic channel. After this call `events.terminated$` will emit and the session ends.
|
|
556
|
+
*/
|
|
152
557
|
hangup(): void;
|
|
153
|
-
/**
|
|
558
|
+
/**
|
|
559
|
+
* Answer an **inbound** call (sends 200 OK with SDP).
|
|
560
|
+
*
|
|
561
|
+
* No-op if the call is already answered or if this is an outbound call. Also no-op
|
|
562
|
+
* on WS/headless channels. After answering, the state transitions to `"active"` and
|
|
563
|
+
* billing starts.
|
|
564
|
+
*
|
|
565
|
+
* If the call is in `"early"` state (after `sendProgress()`), `answer()` promotes it
|
|
566
|
+
* to `"active"` — audio was already flowing, billing now begins.
|
|
567
|
+
*
|
|
568
|
+
* ```ts
|
|
569
|
+
* // Simple: answer immediately
|
|
570
|
+
* channel.sip.answer();
|
|
571
|
+
* await channel.audio.say('Hello, how can I help?');
|
|
572
|
+
* ```
|
|
573
|
+
*
|
|
574
|
+
* ```ts
|
|
575
|
+
* // Advanced: pre-answer → answer
|
|
576
|
+
* channel.sip.sendProgress(); // early media, no billing
|
|
577
|
+
* await channel.audio.say('One moment…');
|
|
578
|
+
* channel.sip.answer(); // billing starts
|
|
579
|
+
* await channel.audio.say('How can I help?');
|
|
580
|
+
* ```
|
|
581
|
+
*/
|
|
154
582
|
answer(): void;
|
|
155
|
-
/**
|
|
583
|
+
/**
|
|
584
|
+
* Send **183 Session Progress** with SDP on an **inbound** call, enabling
|
|
585
|
+
* early media (full-duplex audio) **before** the 200 OK answer.
|
|
586
|
+
*
|
|
587
|
+
* After calling `sendProgress()`:
|
|
588
|
+
* - The call state transitions to `"early"` and `early$` emits.
|
|
589
|
+
* - `audio.say()`, `audio.play()`, `createAsr()`, `dtmf$` all work —
|
|
590
|
+
* exactly the same as in the `"active"` state.
|
|
591
|
+
* - The remote party hears your audio but the call is **not yet billed**
|
|
592
|
+
* (billing typically starts at 200 OK, depending on the carrier).
|
|
593
|
+
*
|
|
594
|
+
* Call `sip.answer()` later to send the final 200 OK and transition to `"active"`.
|
|
595
|
+
*
|
|
596
|
+
* No-op if the call is already in `"early"` or `"active"` state, or if this is
|
|
597
|
+
* an outbound / WS / headless channel.
|
|
598
|
+
*
|
|
599
|
+
* ### Typical use case: play a greeting before answering
|
|
600
|
+
*
|
|
601
|
+
* ```ts
|
|
602
|
+
* // Inbound call arrives — state is 'ringing'
|
|
603
|
+
* channel.sip.sendProgress();
|
|
604
|
+
* // State is now 'early' — audio flows, billing has NOT started
|
|
605
|
+
*
|
|
606
|
+
* await channel.audio.say('Please hold while we connect you…');
|
|
607
|
+
*
|
|
608
|
+
* // Now answer (billing starts)
|
|
609
|
+
* channel.sip.answer();
|
|
610
|
+
* await channel.audio.say('Hello! How can I help?');
|
|
611
|
+
* ```
|
|
612
|
+
*
|
|
613
|
+
* ### Typical use case: start ASR before answering
|
|
614
|
+
*
|
|
615
|
+
* ```ts
|
|
616
|
+
* channel.sip.sendProgress();
|
|
617
|
+
* const asr = await channel.createAsr();
|
|
618
|
+
* await channel.audio.say('Hi! What is your account number?');
|
|
619
|
+
* const result = await firstValueFrom(asr.result$);
|
|
620
|
+
* // Collected account number BEFORE billing started
|
|
621
|
+
* channel.sip.answer();
|
|
622
|
+
* ```
|
|
623
|
+
*/
|
|
624
|
+
sendProgress(): void;
|
|
625
|
+
/**
|
|
626
|
+
* Initiate an **outbound** SIP call to the given SIP URI.
|
|
627
|
+
*
|
|
628
|
+
* Returns a new {@link MediaChannel} representing the B-leg. The returned channel
|
|
629
|
+
* has its own `sip`, `audio`, `events`, etc. — use `waitForAnswer()` or
|
|
630
|
+
* `waitForEarly()` on the B-leg before sending audio.
|
|
631
|
+
*
|
|
632
|
+
* Supported only by the main-thread SIP channel. Worker-isolated, WS, and headless
|
|
633
|
+
* channels throw because the current worker bridge does not proxy nested call legs.
|
|
634
|
+
*
|
|
635
|
+
* @param opts.sipUri - Full SIP URI, e.g. `"sip:+12025551234@trunk.carrier.com"`.
|
|
636
|
+
* @param opts.fromUri - Optional caller-ID override SIP URI.
|
|
637
|
+
* @returns A new `MediaChannel` for the outbound leg.
|
|
638
|
+
*
|
|
639
|
+
* ```ts
|
|
640
|
+
* const bLeg = await channel.sip.makeCall({
|
|
641
|
+
* sipUri: 'sip:+12025551234@trunk.carrier.com',
|
|
642
|
+
* });
|
|
643
|
+
* await bLeg.sip.waitForAnswer();
|
|
644
|
+
* // Bridge both legs so callers hear each other
|
|
645
|
+
* const teardown = channel.sip.bridge(bLeg);
|
|
646
|
+
* ```
|
|
647
|
+
*/
|
|
156
648
|
makeCall(opts: {
|
|
157
649
|
sipUri: string;
|
|
158
650
|
fromUri?: string;
|
|
159
651
|
}): Promise<MediaChannel>;
|
|
160
|
-
/**
|
|
652
|
+
/**
|
|
653
|
+
* Cross-connect audio between this SIP call and another SIP call (conference bridge).
|
|
654
|
+
*
|
|
655
|
+
* Both parties hear each other in real time. Returns a teardown function
|
|
656
|
+
* that disconnects the bridge when called. The current runtime only bridges two
|
|
657
|
+
* `SipMediaChannel` instances directly; non-SIP channels return a no-op teardown
|
|
658
|
+
* or throw depending on the host.
|
|
659
|
+
*
|
|
660
|
+
* @param other - The other `MediaChannel` (e.g. an outbound B-leg).
|
|
661
|
+
* @returns A function to tear down the bridge.
|
|
662
|
+
*
|
|
663
|
+
* ```ts
|
|
664
|
+
* const teardown = channel.sip.bridge(bLeg);
|
|
665
|
+
* // ... later
|
|
666
|
+
* teardown(); // disconnect the bridge
|
|
667
|
+
* ```
|
|
668
|
+
*/
|
|
161
669
|
bridge(other: MediaChannel): () => void;
|
|
162
670
|
}
|
|
163
671
|
/**
|
|
164
|
-
*
|
|
672
|
+
* Primary script API for real-time voice: ASR, TTS/audio, SIP, LLM, and **`params`** from the host.
|
|
165
673
|
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
674
|
+
* **`type`** is **`"sip"`** for telephony or **`"ws"`** for WebSocket/script-manager sessions.
|
|
675
|
+
* Headless sessions currently also expose a `"ws"` typed synthetic channel; use
|
|
676
|
+
* {@link import('./define-script').ScriptDialogContext.headless} to distinguish them.
|
|
677
|
+
* In headless mode audio/SIP/text input are mostly no-ops while **`llm`** / **`platform`**
|
|
678
|
+
* still work.
|
|
168
679
|
*/
|
|
169
680
|
export interface MediaChannel {
|
|
170
|
-
/** Channel transport type: `"sip"` for phone calls, `"ws"` for WebSocket sessions. */
|
|
171
681
|
readonly type: 'sip' | 'ws';
|
|
172
|
-
/** Inbound caller ID (phone number or WS client ID). */
|
|
173
682
|
readonly callerId: string;
|
|
174
|
-
/** Called number / destination DID. */
|
|
175
683
|
readonly calledNumber: string;
|
|
176
|
-
/**
|
|
684
|
+
/**
|
|
685
|
+
* Merged session parameters from env, Omni, Voctiv platform defaults, and route.
|
|
686
|
+
*
|
|
687
|
+
* Relevant to ASR/TTS when **Voctiv platform** compatibility is on (host-dependent keys), for example:
|
|
688
|
+
* - **`asrVendor`**, **`ttsVendor`**, **`asrConfig`**, **`ttsConfig`**
|
|
689
|
+
* - **`defaultAsrName`**, **`defaultTtsName`**: default **`key_storage.name`** when the script
|
|
690
|
+
* omits **`name`** on {@link AsrConfig} / {@link PlayOptions}
|
|
691
|
+
* - **`authentication_data`**: may include **`legacyAsrKeysByName`**, **`legacyTtsKeysByName`**
|
|
692
|
+
* (built from LE DB for this dialog’s agent + company)
|
|
693
|
+
*
|
|
694
|
+
* Treat as read-only unless your integration explicitly documents mutable keys.
|
|
695
|
+
*/
|
|
177
696
|
readonly params: Record<string, unknown>;
|
|
178
|
-
/**
|
|
697
|
+
/**
|
|
698
|
+
* Create a speech recognizer for this session.
|
|
699
|
+
*
|
|
700
|
+
* SIP channels feed remote RTP audio. WS channels feed socket `audio` frames and can
|
|
701
|
+
* create a per-session VAD on first ASR creation. Headless channels return an inert
|
|
702
|
+
* handle with empty observables.
|
|
703
|
+
*
|
|
704
|
+
* @param config - Optional {@link AsrConfig}: **`vendor`**, **`name`** (storage row), **`language`**,
|
|
705
|
+
* **`data`** overlays, VAD / smart-turn tuning.
|
|
706
|
+
* @returns A handle you should {@link AsrHandle.destroy} when you no longer need recognition.
|
|
707
|
+
* If connector creation fails, SIP/WS return a degraded handle with VAD observables but no STT results.
|
|
708
|
+
*/
|
|
179
709
|
createAsr(config?: AsrConfig): Promise<AsrHandle>;
|
|
180
|
-
/**
|
|
710
|
+
/** Push synthetic ASR results (testing / WS debug). No-op on headless channels. */
|
|
181
711
|
readonly textInput: TextInput;
|
|
182
|
-
/** Audio playback and TTS. */
|
|
183
712
|
readonly audio: ChannelAudio;
|
|
184
|
-
/** Channel events (speech, termination, data messages). */
|
|
185
713
|
readonly events: ChannelEvents;
|
|
186
|
-
/** LLM text generation and extraction. */
|
|
187
714
|
readonly llm: ChannelLlm;
|
|
188
|
-
/** SIP telephony controls. */
|
|
189
715
|
readonly sip: ChannelSip;
|
|
190
|
-
/**
|
|
716
|
+
/** Emit a structured message to the remote peer. Implemented for WS; SIP ignores it and headless logs it. */
|
|
191
717
|
sendMessage(data: DataMessage): void;
|
|
192
|
-
/**
|
|
718
|
+
/** Release connectors, mixer, recordings, persistent LLM streams, and subscriptions. */
|
|
193
719
|
destroy(): void;
|
|
194
720
|
}
|
|
195
721
|
//# sourceMappingURL=media-channel.d.ts.map
|