@voctiv/agent-sdk 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +258 -6
- package/dist/define-script.d.ts +31 -2
- package/dist/define-script.d.ts.map +1 -1
- package/dist/define-script.js +30 -3
- package/dist/define-script.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types/call-options.d.ts +53 -0
- package/dist/types/call-options.d.ts.map +1 -0
- package/dist/types/call-options.js +3 -0
- package/dist/types/call-options.js.map +1 -0
- package/dist/types/llm.d.ts +82 -2
- package/dist/types/llm.d.ts.map +1 -1
- package/dist/types/script-context.d.ts +30 -1
- package/dist/types/script-context.d.ts.map +1 -1
- package/dist/types/script-context.js +13 -2
- package/dist/types/script-context.js.map +1 -1
- package/examples/README.md +3 -0
- package/examples/outbound-from-invite.ts +30 -0
- package/examples/outbound-on-answer.ts +18 -0
- package/examples/outbound-script-dial.ts +20 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -70,7 +70,15 @@ export default defineScript(async ({ channel, logger, context }) => {
|
|
|
70
70
|
});
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
+
This snippet assumes a **live answered** channel: inbound after you `answer()`, or a campaign
|
|
74
|
+
outbound that started on `'on_answer'` (the default). It is not how you pick up ringing, 1xx, or
|
|
75
|
+
a 4xx. Those are two different outbound stories:
|
|
73
76
|
|
|
77
|
+
- **Campaign / `platform.call`** — `defineScript(fn, { outboundCallMode })` decides *when* the
|
|
78
|
+
host starts this session. See [Campaign Outbound And `outboundCallMode`](#campaign-outbound-and-outboundcallmode).
|
|
79
|
+
- **A second leg from an already-running script** — `channel.sip.makeCall()`, then
|
|
80
|
+
`waitForEarly()` / `waitForAnswer()` on the **returned** channel. See
|
|
81
|
+
[B-leg outbound (already in session)](#b-leg-outbound-already-in-session).
|
|
74
82
|
|
|
75
83
|
## Examples
|
|
76
84
|
|
|
@@ -79,6 +87,9 @@ The `[examples/](./examples/)` folder contains copy-paste-ready scripts:
|
|
|
79
87
|
|
|
80
88
|
| Example | Description |
|
|
81
89
|
| --------------------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
90
|
+
| [outbound-on-answer.ts](./examples/outbound-on-answer.ts) | Campaign outbound on `200 OK` (`outboundCallMode: 'on_answer'`, the default) |
|
|
91
|
+
| [outbound-from-invite.ts](./examples/outbound-from-invite.ts) | Campaign outbound from INVITE (`outboundCallMode: 'from_invite'`) |
|
|
92
|
+
| [outbound-script-dial.ts](./examples/outbound-script-dial.ts) | Script dials the campaign leg (`outboundCallMode: 'script_dial'`) |
|
|
82
93
|
| [outbound-with-recall.ts](./examples/outbound-with-recall.ts) | Outbound with `recallCount` / `recallDelay` (automatic redial on failure) |
|
|
83
94
|
| [recall-routing-by-attempt.ts](./examples/recall-routing-by-attempt.ts) | Online recall: branch on `context.attempt` (`getScriptPhase` → `'online'`) |
|
|
84
95
|
| [schedule-call-with-defaults.ts](./examples/schedule-call-with-defaults.ts) | `platform.call()` without explicit recall — CMS defaults from `context` |
|
|
@@ -130,6 +141,11 @@ const maxAttempts = parseRecallCount(context.dialogParams?.recall_count);
|
|
|
130
141
|
Ship it in a **sibling** module (`media-providers/`), not in the sandboxed script entry — see
|
|
131
142
|
[Custom ASR / TTS Providers](#custom-asr--tts-providers).
|
|
132
143
|
|
|
144
|
+
`defineScript(fn, options)` additionally declares `ScriptCallOptions`, whose `outboundCallMode`
|
|
145
|
+
(`OutboundCallMode`: `'on_answer'` | `'from_invite'` | `'script_dial'`) the host reads
|
|
146
|
+
**before** the script runs to decide when the session starts — see
|
|
147
|
+
[Campaign Outbound And `outboundCallMode`](#campaign-outbound-and-outboundcallmode).
|
|
148
|
+
|
|
133
149
|
**Three different “context” names:**
|
|
134
150
|
|
|
135
151
|
|
|
@@ -172,6 +188,11 @@ The important states are:
|
|
|
172
188
|
- `active`: final 200 OK has been received or sent.
|
|
173
189
|
- `terminated`: the call ended and no more audio is possible.
|
|
174
190
|
|
|
191
|
+
`channel.sip.state === 'early'` is **media** (RTP before `200 OK`). It is not
|
|
192
|
+
`getScriptPhase(context) === 'early'`. That phase is a snapshot of *when the live session started*
|
|
193
|
+
(campaign `'from_invite'` or inbound pre-answer) and **does not flip** when the call answers — use
|
|
194
|
+
`channel.sip.isAnswered` / `answered$` for the live state.
|
|
195
|
+
|
|
175
196
|
|
|
176
197
|
|
|
177
198
|
### How Pre-Answer Works
|
|
@@ -196,9 +217,20 @@ For outbound calls, pre-answer is controlled by the remote side. If the remote e
|
|
|
196
217
|
|
|
197
218
|
`early` is a media-ready state, not a final answer state. `answer()` is still the explicit transition that sends final `200 OK` for inbound calls. External billing behavior depends on the carrier.
|
|
198
219
|
|
|
199
|
-
###
|
|
220
|
+
### Two outbound stories
|
|
221
|
+
|
|
222
|
+
Do not mix these up. Both can see 183 / early media, but they start in different places.
|
|
223
|
+
|
|
224
|
+
| You want… | Use | Conversation on |
|
|
225
|
+
| --------- | --- | --------------- |
|
|
226
|
+
| The **campaign** (or `platform.call`) to start this script at 200 OK, at INVITE, or with no leg yet | `defineScript(fn, { outboundCallMode })` — [below](#campaign-outbound-and-outboundcallmode) | `channel`, except `'script_dial'` which talks on the **returned** `leg` |
|
|
227
|
+
| A **second** outbound from a script that is already running (transfer target, IVR, operator) | `channel.sip.makeCall()` — next subsection | the **returned** B-leg |
|
|
228
|
+
|
|
229
|
+
`outboundCallMode` never changes inbound or a B-leg you opened yourself.
|
|
230
|
+
|
|
231
|
+
### B-leg outbound (already in session)
|
|
200
232
|
|
|
201
|
-
|
|
233
|
+
Early media on a B-leg starts when the remote side sends a provisional response with SDP, usually `183 Session Progress`. This is useful for IVRs that speak before answering. The parent script is already live; `outboundCallMode` does not apply here.
|
|
202
234
|
|
|
203
235
|
```ts
|
|
204
236
|
const bLeg = await channel.sip.makeCall({
|
|
@@ -217,6 +249,175 @@ asr.result$.subscribe((text) => {
|
|
|
217
249
|
|
|
218
250
|
`waitForEarly()` resolves when the call reaches either `early` or `active`. If a carrier skips early media and answers directly, it resolves on the final answer.
|
|
219
251
|
|
|
252
|
+
### Campaign Outbound And `outboundCallMode`
|
|
253
|
+
|
|
254
|
+
This is **when the platform starts the script** for a campaign / `platform.call` row — not how you
|
|
255
|
+
open a B-leg from inside a live session ([that is `makeCall`](#b-leg-outbound-already-in-session)).
|
|
256
|
+
|
|
257
|
+
`OutboundCallMode` (`'on_answer'` | `'from_invite'` | `'script_dial'`) is the second argument of
|
|
258
|
+
`defineScript`. The host reads it **before** the session starts, because it decides *when* (and
|
|
259
|
+
whether) the platform dials. The names describe that moment.
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
import { defineScript } from '@voctiv/agent-sdk';
|
|
263
|
+
|
|
264
|
+
defineScript(async () => undefined); // same as 'on_answer'
|
|
265
|
+
defineScript(async () => undefined, { outboundCallMode: 'on_answer' });
|
|
266
|
+
defineScript(async () => undefined, { outboundCallMode: 'from_invite' });
|
|
267
|
+
defineScript(async () => undefined, { outboundCallMode: 'script_dial' });
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
A script that passes nothing stays on `'on_answer'` — the Basic Script above is that path.
|
|
271
|
+
Inbound legs and `channel.sip.makeCall()` B-legs are unchanged: they already hand the script a
|
|
272
|
+
channel before the final answer.
|
|
273
|
+
|
|
274
|
+
`answer_date` and billing never move: both come from the `200 OK`, never from `183`.
|
|
275
|
+
`getScriptPhase` is a snapshot of how the run **started** and does not flip when the answer arrives
|
|
276
|
+
— use `channel.sip.isAnswered` or `answered$` for the live state. (`defineScript` stores the
|
|
277
|
+
options on the function as `callOptions` — type `ScriptFnWithCallOptions` — and the host copies
|
|
278
|
+
that into `context.startedBeforeAnswer` / `context.startedWithoutLeg`.)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
| `outboundCallMode` | Who dials | Script starts on | `getScriptPhase` at start | Conversation channel |
|
|
282
|
+
| ----------------------- | ----------------------- | ----------------------------------- | ------------------------------------------------------ | ---------------------- |
|
|
283
|
+
| `'on_answer'` (default) | host | the `200 OK` | `'online'` | `channel` |
|
|
284
|
+
| `'from_invite'` | host (after script up) | INVITE — session is already running | `'early'` (stays `'early'` after answer) | `channel` |
|
|
285
|
+
| `'script_dial'` | script (`sip.makeCall`) | nothing — no leg exists yet | `'dialing'` (stays `'dialing'` after the script dials) | the **returned** `leg` |
|
|
286
|
+
|
|
287
|
+
Copy-paste: [outbound-on-answer.ts](./examples/outbound-on-answer.ts),
|
|
288
|
+
[outbound-from-invite.ts](./examples/outbound-from-invite.ts),
|
|
289
|
+
[outbound-script-dial.ts](./examples/outbound-script-dial.ts).
|
|
290
|
+
|
|
291
|
+
#### `'on_answer'` — start on answer (default)
|
|
292
|
+
|
|
293
|
+
The host sends the INVITE and **holds the script** until the remote party answers (`200 OK`). Busy,
|
|
294
|
+
no-answer, reject, or the dialer timeout never reach the live handler: the campaign writes
|
|
295
|
+
`call.result` and may run `after_call_failed` / recall instead.
|
|
296
|
+
|
|
297
|
+
Use this when the script is a conversation: greeting, turn watchdog, max-duration timer. They all
|
|
298
|
+
assume a human is already on the line. Existing agents that do not pass options keep this behavior.
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
import { defineScript, getScriptPhase } from '@voctiv/agent-sdk';
|
|
302
|
+
|
|
303
|
+
export default defineScript(async ({ channel, context }) => {
|
|
304
|
+
// Live outbound: always 'online'. Headless before_call / after_call_* still apply.
|
|
305
|
+
if (getScriptPhase(context) !== 'online') return;
|
|
306
|
+
|
|
307
|
+
await channel.audio.say('Hello!');
|
|
308
|
+
const asr = await channel.createAsr();
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
How to work with it:
|
|
313
|
+
|
|
314
|
+
- Talk on `channel` — audio, ASR, SIP waiters are already live and answered.
|
|
315
|
+
- Do **not** wait for `183` / `progress$`: the script was not running then.
|
|
316
|
+
- Do **not** call `channel.sip.makeCall()` for the campaign number; the host already dialed it.
|
|
317
|
+
- Inbound is unchanged: the same export still sees `'online'` (or `'early'` only if you also opted
|
|
318
|
+
into `'from_invite'`).
|
|
319
|
+
|
|
320
|
+
#### `'from_invite'` — script first, host dials
|
|
321
|
+
|
|
322
|
+
The host starts the script **before** any INVITE, then automatically dials the campaign number on
|
|
323
|
+
the same `channel`. Subscribe first, then wait: 180/183 arrive on `progress$` / `sipSignal$`, and a
|
|
324
|
+
4xx/5xx rejects `waitForAnswer()` with the SIP result in the message (`486 Busy Here`). The
|
|
325
|
+
greeting still belongs after the answer — otherwise it plays into ringback.
|
|
326
|
+
|
|
327
|
+
```ts
|
|
328
|
+
import { defineScript, getScriptPhase } from '@voctiv/agent-sdk';
|
|
329
|
+
|
|
330
|
+
export default defineScript(
|
|
331
|
+
async ({ channel, logger, context }) => {
|
|
332
|
+
if (getScriptPhase(context) === 'early') {
|
|
333
|
+
channel.sip.progress$.subscribe(({ statusCode }) => {
|
|
334
|
+
logger.log('progress', { statusCode });
|
|
335
|
+
});
|
|
336
|
+
try {
|
|
337
|
+
await channel.sip.waitForAnswer();
|
|
338
|
+
} catch (err) {
|
|
339
|
+
logger.warn('outbound failed', { err });
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
await channel.audio.say('Hello!');
|
|
345
|
+
},
|
|
346
|
+
{ outboundCallMode: 'from_invite' },
|
|
347
|
+
);
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
How to work with it:
|
|
351
|
+
|
|
352
|
+
- Talk on `channel` — the host already dialed; do **not** call `makeCall()` for the campaign number.
|
|
353
|
+
- `getScriptPhase` is `'early'` for the whole live run (it does not flip on `200 OK`). Read
|
|
354
|
+
`channel.sip.isAnswered` / `answered$` for the live state.
|
|
355
|
+
- `audio.say` / `createAsr` wait until the leg has media, then run on this channel.
|
|
356
|
+
- On inbound, `'from_invite'` still reports `'early'` when the session starts before the answer.
|
|
357
|
+
- Unlike `'on_answer'`, a 4xx/5xx **is** visible in this handler — catch `waitForAnswer()` or subscribe
|
|
358
|
+
to `sipSignal$` / `events.terminated$`.
|
|
359
|
+
|
|
360
|
+
#### `'script_dial'` — dial the leg yourself
|
|
361
|
+
|
|
362
|
+
The host does **not** send an INVITE. The script starts with no leg: `sip.state === 'idle'`, no
|
|
363
|
+
audio, phase `'dialing'`. It opens the leg itself and talks on the channel that **comes back** —
|
|
364
|
+
the same shape as a `makeCall()` B-leg. The starting `channel` can only dial.
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
import { defineScript, getScriptPhase } from '@voctiv/agent-sdk';
|
|
368
|
+
|
|
369
|
+
export default defineScript(
|
|
370
|
+
async ({ channel, logger, context }) => {
|
|
371
|
+
if (getScriptPhase(context) !== 'dialing') return;
|
|
372
|
+
|
|
373
|
+
// Empty options: campaign msisdn + the call row's trunk.
|
|
374
|
+
const leg = await channel.sip.makeCall({});
|
|
375
|
+
|
|
376
|
+
leg.sip.progress$.subscribe(({ statusCode }) => {
|
|
377
|
+
logger.log('progress', { statusCode });
|
|
378
|
+
});
|
|
379
|
+
await leg.sip.waitForAnswer();
|
|
380
|
+
|
|
381
|
+
await leg.audio.say('Hello!');
|
|
382
|
+
const asr = await leg.createAsr();
|
|
383
|
+
},
|
|
384
|
+
{ outboundCallMode: 'script_dial' },
|
|
385
|
+
);
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
How to work with it:
|
|
389
|
+
|
|
390
|
+
- **Conversation is on `leg`, not on `channel`.** `channel.audio.say` / `createAsr` before
|
|
391
|
+
`makeCall` are no-ops with a warning. `channel.sip.waitForAnswer()` rejects — there is no leg.
|
|
392
|
+
- `makeCall({})` dials the campaign number over the row's trunk. Pass `msisdn` (and `channel` for a
|
|
393
|
+
different trunk) to dial somewhere else, exactly as in a live session.
|
|
394
|
+
- `makeCall` **resolves on media** (183/200) and **rejects** when the leg fails (`486 Busy Here`) or
|
|
395
|
+
times out. A retry loop is `try` / `catch`, not a race on `terminated$`.
|
|
396
|
+
- The **first** successful `makeCall` is the one the platform reports on: `call.result`,
|
|
397
|
+
`answer_date`, duration, recording, transcription. Later legs are B-legs. A run that never dials
|
|
398
|
+
leaves the row with `-ERR Script dialed no call`.
|
|
399
|
+
- The dialer's answer timeout no longer applies to the campaign row. The worker slot is held from
|
|
400
|
+
the moment the row is claimed until the script returns. Returning hangs up any leg still up.
|
|
401
|
+
- `getScriptPhase` stays `'dialing'` after the script opens a leg. Gate on it to decide *whether*
|
|
402
|
+
to dial; use `leg.sip.isAnswered` for *whether someone picked up*.
|
|
403
|
+
|
|
404
|
+
An existing agent that already talks on `ctx.channel` does not need a rewrite: take the returned
|
|
405
|
+
leg once, then keep the rest of the script as-is.
|
|
406
|
+
|
|
407
|
+
```ts
|
|
408
|
+
import { defineScript, getScriptPhase } from '@voctiv/agent-sdk';
|
|
409
|
+
|
|
410
|
+
export default defineScript(
|
|
411
|
+
async (ctx) => {
|
|
412
|
+
const phase = getScriptPhase(ctx.context);
|
|
413
|
+
const channel =
|
|
414
|
+
phase === 'dialing' ? await ctx.channel.sip.makeCall({}) : ctx.channel;
|
|
415
|
+
await channel.audio.say('Hello!');
|
|
416
|
+
},
|
|
417
|
+
{ outboundCallMode: 'script_dial' },
|
|
418
|
+
);
|
|
419
|
+
```
|
|
420
|
+
|
|
220
421
|
### Inbound Pre-Answer
|
|
221
422
|
|
|
222
423
|
For inbound calls, call `channel.sip.sendProgress()` to send `183 Session Progress` with SDP. This enters `early` state and enables full-duplex audio before the final answer.
|
|
@@ -867,12 +1068,16 @@ the full surface and IDEs autocomplete correctly:
|
|
|
867
1068
|
| Interface | Role |
|
|
868
1069
|
| --- | --- |
|
|
869
1070
|
| `ScriptAsrConnector` | Custom STT connector returned by an `asr` factory |
|
|
1071
|
+
| `ScriptAsrConnectorError` | Error payload on `ScriptAsrConnector.error$` |
|
|
870
1072
|
| `ScriptTtsConnector` | Unified batch + optional streaming TTS connector |
|
|
1073
|
+
| `ScriptTtsSynthesisContext` | Synthesis context passed into a TTS factory (`TtsSynthesisContext`) |
|
|
871
1074
|
| `MediaConnectorContext` | `id`, flattened `config`, `dialogUuid`, `debug$` passed into factories |
|
|
872
1075
|
| `MediaProviderShared` | Optional per-dialog object from `createShared` |
|
|
1076
|
+
| `AsrProviderFactory` / `TtsProviderFactory` | Factory functions in `MediaProvidersDefinition` (`asr` / `tts` maps) |
|
|
873
1077
|
|
|
874
1078
|
`defineMediaProviders` is an identity helper for typing. Factories must return objects that
|
|
875
|
-
satisfy those interfaces (classes with `implements` recommended).
|
|
1079
|
+
satisfy those interfaces (classes with `implements` recommended). The argument is a
|
|
1080
|
+
`MediaProvidersDefinition`.
|
|
876
1081
|
|
|
877
1082
|
### Export shape (host module)
|
|
878
1083
|
|
|
@@ -1286,11 +1491,18 @@ create ASR or want one subscription for the whole channel).
|
|
|
1286
1491
|
| `ask(message, options?)` | `Promise<string>` | Single-shot completion (consumes SSE stream). |
|
|
1287
1492
|
| `stream(message, options?)` | `Observable<LlmStreamChunk>` | Token/chunk stream; use `chunk.content` for TTS. |
|
|
1288
1493
|
| `extract(options?)` | `Promise<Record<string, any>>` | Structured extraction via Omni extract API. |
|
|
1494
|
+
| `createDialog(options)` | `Promise<DialogInfo>` | Create dialog (`CreateDialogOptions`, optional `payload`) (HTTP). |
|
|
1495
|
+
| `updateDialog(options)` | `Promise<DialogInfo>` | Patch dialog (`UpdateDialogOptions` / `payload`) (HTTP `/meta`). |
|
|
1289
1496
|
| `makePersistentStream(options?)` | `PersistentLlmStreamHandle` | Long-lived stream for multi-turn chat. |
|
|
1290
1497
|
|
|
1498
|
+
`send()` returns a `PersistentLlmSendHandle` (`requestId`, per-turn `stream$`, `abort()`). Several sends may run in parallel on one socket.
|
|
1499
|
+
|
|
1500
|
+
Persistent handle also exposes `createDialog` / `updateDialog` over Socket.IO (`dialog.create` / `dialog.update`).
|
|
1501
|
+
|
|
1291
1502
|
|
|
1292
1503
|
Common `LlmOptions`: `dialogUuid`, `agentUuid`, `role`, `hidden`, `name` (LLM speaker label — **not**
|
|
1293
|
-
the TTS credential `name`), `payload`, `debug`, `agentAliasFilter`, `currentAgentAlias
|
|
1504
|
+
the TTS credential `name`), `payload`, `history`, `debug`, `agentAliasFilter`, `currentAgentAlias`,
|
|
1505
|
+
`pseudoReasoning` (manual same-turn hint; skips the assigned reasoner LLM), `requestId`.
|
|
1294
1506
|
|
|
1295
1507
|
```ts
|
|
1296
1508
|
const answer = await channel.llm.ask('Summarize the user request', {
|
|
@@ -1326,18 +1538,46 @@ const fields = await channel.llm.extract({
|
|
|
1326
1538
|
});
|
|
1327
1539
|
```
|
|
1328
1540
|
|
|
1329
|
-
Persistent multi-turn stream:
|
|
1541
|
+
Persistent multi-turn stream (one Socket.IO connection, multiplexed by `requestId`):
|
|
1330
1542
|
|
|
1331
1543
|
```ts
|
|
1544
|
+
// HTTP: create / update dialog payload for prompt templates
|
|
1545
|
+
const created = await channel.llm.createDialog({
|
|
1546
|
+
agentUuid: context.agentUuid,
|
|
1547
|
+
payload: { customerName: 'Alex', language: 'ru' },
|
|
1548
|
+
});
|
|
1549
|
+
await channel.llm.updateDialog({
|
|
1550
|
+
dialogUuid: context.dialogUuid,
|
|
1551
|
+
payload: { ...created.payload, balance: 1200 },
|
|
1552
|
+
});
|
|
1553
|
+
|
|
1332
1554
|
const chat = channel.llm.makePersistentStream({
|
|
1333
1555
|
agentUuid: context.agentUuid,
|
|
1334
1556
|
dialogUuid: context.dialogUuid,
|
|
1335
1557
|
});
|
|
1336
1558
|
|
|
1559
|
+
// Same over the persistent socket
|
|
1560
|
+
await chat.createDialog({
|
|
1561
|
+
agentUuid: context.agentUuid,
|
|
1562
|
+
payload: { customerName: 'Alex' },
|
|
1563
|
+
});
|
|
1564
|
+
await chat.updateDialog({ payload: { customerName: 'Alex', tier: 'gold' } });
|
|
1565
|
+
|
|
1337
1566
|
chat.stream$.pipe(map((c) => c.content)).subscribe((text) => logger.debug('LLM chunk', { text }));
|
|
1338
1567
|
|
|
1339
1568
|
chat.send('What is my balance?');
|
|
1340
1569
|
chat.send('And my last payment date?');
|
|
1570
|
+
|
|
1571
|
+
// Parallel agents on the same socket:
|
|
1572
|
+
const realtime = chat.send(userText, {
|
|
1573
|
+
currentAgentAlias: 'fast',
|
|
1574
|
+
pseudoReasoning: 'User wants to cancel. Confirm identity first.',
|
|
1575
|
+
});
|
|
1576
|
+
const deep = chat.send(userText, { currentAgentAlias: 'researcher' });
|
|
1577
|
+
realtime.stream$.subscribe((c) => { /* TTS */ });
|
|
1578
|
+
deep.stream$.subscribe((c) => { /* notes / later TTS */ });
|
|
1579
|
+
// deep.abort(); // cancel only the deep turn
|
|
1580
|
+
|
|
1341
1581
|
chat.disconnect();
|
|
1342
1582
|
```
|
|
1343
1583
|
|
|
@@ -1375,6 +1615,8 @@ The return value is the `ScriptPhase` union — one of the phases in the table b
|
|
|
1375
1615
|
| Phase | `context.headless` | When | Typical `context.entryPoint` |
|
|
1376
1616
|
| -------------------- | ------------------ | ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
|
|
1377
1617
|
| `before_call` | `true` | Dialog queue / bulk outbound **before** the first platform call (often schedules `platform.call`) | empty, `main`, `default` |
|
|
1618
|
+
| `dialing` | `false` | Live session with **no leg yet** — script opted into `outboundCallMode: 'script_dial'` and must `makeCall()` | any (ignored for phase; snapshot stays `'dialing'`) |
|
|
1619
|
+
| `early` | `false` | Live session that started **before** the `200 OK` (campaign `'from_invite'` or inbound pre-answer). Not `channel.sip.state === 'early'` (RTP). Snapshot stays `'early'` after answer. | any (ignored for phase) |
|
|
1378
1620
|
| `online` | `false` | Live SIP session (inbound, outbound, **and automatic recall redials**) | any (ignored for phase) |
|
|
1379
1621
|
| `after_call_success` | `true` | Headless run **after** a successful call | `on_success_call`, `after_call_success`, `on_done_call` |
|
|
1380
1622
|
| `after_call_failed` | `true` | Headless run **after** failed attempts (when `onFailedCall` was configured) | `on_failed_call`, `after_call_failed` |
|
|
@@ -1419,6 +1661,16 @@ export default defineScript(async ({ channel, context, logger, platform }) => {
|
|
|
1419
1661
|
logger.log('Inbound message', { text: context.inboundMessage?.payload });
|
|
1420
1662
|
return { output: { phase } };
|
|
1421
1663
|
|
|
1664
|
+
case 'dialing': {
|
|
1665
|
+
const leg = await channel.sip.makeCall({});
|
|
1666
|
+
await leg.sip.waitForAnswer();
|
|
1667
|
+
await leg.audio.say('Hello.');
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
case 'early':
|
|
1672
|
+
await channel.sip.waitForAnswer();
|
|
1673
|
+
// fall through to the conversation
|
|
1422
1674
|
case 'online':
|
|
1423
1675
|
channel.sip.answer();
|
|
1424
1676
|
if ((context.attempt ?? 0) > 0) {
|
|
@@ -1694,7 +1946,7 @@ These APIs are available and are the intended tools for offline scripts:
|
|
|
1694
1946
|
- `platform.messaging.send()` for outbound messages through the configured platform messaging transport.
|
|
1695
1947
|
- `platform.call()` for scheduling outbound platform-managed calls.
|
|
1696
1948
|
- `platform.dialog.entryPoint` and `platform.dialog.result` for updating dialog routing and outcome.
|
|
1697
|
-
- `channel.llm.ask()`, `channel.llm.stream()`, and `channel.llm.
|
|
1949
|
+
- `channel.llm.ask()`, `channel.llm.stream()`, `channel.llm.extract()`, `channel.llm.createDialog()`, and `channel.llm.updateDialog()` for Omni LLM operations.
|
|
1698
1950
|
- `logger` for structured logs.
|
|
1699
1951
|
|
|
1700
1952
|
Audio and telephony APIs are intentionally inert:
|
package/dist/define-script.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { MediaChannel } from './types/media-channel';
|
|
2
2
|
import type { ScriptLogger } from './types/logger';
|
|
3
|
+
import type { ScriptCallOptions } from './types/call-options';
|
|
3
4
|
import type { ScriptDialogContext, ScriptResult } from './types/script-context';
|
|
4
5
|
import type { PlatformApi } from './types/platform';
|
|
5
6
|
/**
|
|
@@ -42,6 +43,15 @@ export interface ScriptContext {
|
|
|
42
43
|
* Return {@link ScriptResult} to persist output (and optional error), or void.
|
|
43
44
|
*/
|
|
44
45
|
export type ScriptFn = (ctx: ScriptContext) => void | ScriptResult | Promise<void | ScriptResult>;
|
|
46
|
+
/**
|
|
47
|
+
* A {@link ScriptFn} carrying the {@link ScriptCallOptions} passed to
|
|
48
|
+
* {@link defineScript}. The host reads them off the default export **before**
|
|
49
|
+
* the script runs, which is why they live on the function rather than in the
|
|
50
|
+
* returned {@link ScriptResult}.
|
|
51
|
+
*/
|
|
52
|
+
export type ScriptFnWithCallOptions = ScriptFn & {
|
|
53
|
+
readonly callOptions?: ScriptCallOptions;
|
|
54
|
+
};
|
|
45
55
|
/**
|
|
46
56
|
* Mark the default export as a typed script entry point (identity wrapper; no runtime transform).
|
|
47
57
|
*
|
|
@@ -53,7 +63,11 @@ export type ScriptFn = (ctx: ScriptContext) => void | ScriptResult | Promise<voi
|
|
|
53
63
|
* - Use **`channel`** for ASR/TTS (see {@link import('./types/asr-handle').AsrConfig.name},
|
|
54
64
|
* {@link import('./types/mixer').PlayOptions.name} for LE **`key_storage.name`** selection).
|
|
55
65
|
* - Use **`context.dialogParams`** / **`context.dialogUuid`** for routing; **`platform`** for NLU, calls, messaging.
|
|
56
|
-
* @
|
|
66
|
+
* @param options - Optional {@link ScriptCallOptions}. Unlike everything else in the
|
|
67
|
+
* SDK these are read by the host **before** the script runs — they decide *when*
|
|
68
|
+
* the session starts — so they are attached to the returned function instead of
|
|
69
|
+
* being read from the handler.
|
|
70
|
+
* @returns The same **`fn`** reference, with `options` attached as `callOptions`.
|
|
57
71
|
*
|
|
58
72
|
* @example
|
|
59
73
|
* ```ts
|
|
@@ -67,6 +81,21 @@ export type ScriptFn = (ctx: ScriptContext) => void | ScriptResult | Promise<voi
|
|
|
67
81
|
* });
|
|
68
82
|
* });
|
|
69
83
|
* ```
|
|
84
|
+
*
|
|
85
|
+
* @example Pick up the outbound leg from the INVITE — see {@link ScriptCallOptions.outboundCallMode}
|
|
86
|
+
* ```ts
|
|
87
|
+
* import { defineScript, getScriptPhase } from '@voctiv/agent-sdk';
|
|
88
|
+
*
|
|
89
|
+
* export default defineScript(
|
|
90
|
+
* async ({ channel, context }) => {
|
|
91
|
+
* if (getScriptPhase(context) === 'early') {
|
|
92
|
+
* await channel.sip.waitForAnswer();
|
|
93
|
+
* }
|
|
94
|
+
* await channel.audio.say('Hello!');
|
|
95
|
+
* },
|
|
96
|
+
* { outboundCallMode: 'from_invite' },
|
|
97
|
+
* );
|
|
98
|
+
* ```
|
|
70
99
|
*/
|
|
71
|
-
export declare function defineScript(fn: ScriptFn):
|
|
100
|
+
export declare function defineScript(fn: ScriptFn, options?: ScriptCallOptions): ScriptFnWithCallOptions;
|
|
72
101
|
//# sourceMappingURL=define-script.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-script.d.ts","sourceRoot":"","sources":["../src/define-script.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,OAAO,EAAE,YAAY,CAAC;IACtB,0FAA0F;IAC1F,MAAM,EAAE,YAAY,CAAC;IACrB,wFAAwF;IACxF,OAAO,EAAE,mBAAmB,CAAC;IAC7B,mEAAmE;IACnE,QAAQ,EAAE,WAAW,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,CACrB,GAAG,EAAE,aAAa,KACf,IAAI,GAAG,YAAY,GAAG,OAAO,CAAC,IAAI,GAAG,YAAY,CAAC,CAAC;AAExD
|
|
1
|
+
{"version":3,"file":"define-script.d.ts","sourceRoot":"","sources":["../src/define-script.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAChF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,OAAO,EAAE,YAAY,CAAC;IACtB,0FAA0F;IAC1F,MAAM,EAAE,YAAY,CAAC;IACrB,wFAAwF;IACxF,OAAO,EAAE,mBAAmB,CAAC;IAC7B,mEAAmE;IACnE,QAAQ,EAAE,WAAW,CAAC;CACvB;AAED;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,CACrB,GAAG,EAAE,aAAa,KACf,IAAI,GAAG,YAAY,GAAG,OAAO,CAAC,IAAI,GAAG,YAAY,CAAC,CAAC;AAExD;;;;;GAKG;AACH,MAAM,MAAM,uBAAuB,GAAG,QAAQ,GAAG;IAC/C,QAAQ,CAAC,WAAW,CAAC,EAAE,iBAAiB,CAAC;CAC1C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,wBAAgB,YAAY,CAC1B,EAAE,EAAE,QAAQ,EACZ,OAAO,CAAC,EAAE,iBAAiB,GAC1B,uBAAuB,CASzB"}
|
package/dist/define-script.js
CHANGED
|
@@ -12,7 +12,11 @@ exports.defineScript = defineScript;
|
|
|
12
12
|
* - Use **`channel`** for ASR/TTS (see {@link import('./types/asr-handle').AsrConfig.name},
|
|
13
13
|
* {@link import('./types/mixer').PlayOptions.name} for LE **`key_storage.name`** selection).
|
|
14
14
|
* - Use **`context.dialogParams`** / **`context.dialogUuid`** for routing; **`platform`** for NLU, calls, messaging.
|
|
15
|
-
* @
|
|
15
|
+
* @param options - Optional {@link ScriptCallOptions}. Unlike everything else in the
|
|
16
|
+
* SDK these are read by the host **before** the script runs — they decide *when*
|
|
17
|
+
* the session starts — so they are attached to the returned function instead of
|
|
18
|
+
* being read from the handler.
|
|
19
|
+
* @returns The same **`fn`** reference, with `options` attached as `callOptions`.
|
|
16
20
|
*
|
|
17
21
|
* @example
|
|
18
22
|
* ```ts
|
|
@@ -26,8 +30,31 @@ exports.defineScript = defineScript;
|
|
|
26
30
|
* });
|
|
27
31
|
* });
|
|
28
32
|
* ```
|
|
33
|
+
*
|
|
34
|
+
* @example Pick up the outbound leg from the INVITE — see {@link ScriptCallOptions.outboundCallMode}
|
|
35
|
+
* ```ts
|
|
36
|
+
* import { defineScript, getScriptPhase } from '@voctiv/agent-sdk';
|
|
37
|
+
*
|
|
38
|
+
* export default defineScript(
|
|
39
|
+
* async ({ channel, context }) => {
|
|
40
|
+
* if (getScriptPhase(context) === 'early') {
|
|
41
|
+
* await channel.sip.waitForAnswer();
|
|
42
|
+
* }
|
|
43
|
+
* await channel.audio.say('Hello!');
|
|
44
|
+
* },
|
|
45
|
+
* { outboundCallMode: 'from_invite' },
|
|
46
|
+
* );
|
|
47
|
+
* ```
|
|
29
48
|
*/
|
|
30
|
-
function defineScript(fn) {
|
|
31
|
-
|
|
49
|
+
function defineScript(fn, options) {
|
|
50
|
+
if (!options)
|
|
51
|
+
return fn;
|
|
52
|
+
// Non-enumerable so the export keeps looking like a plain function to
|
|
53
|
+
// anything that reflects over it (worker bridges, test doubles).
|
|
54
|
+
return Object.defineProperty(fn, 'callOptions', {
|
|
55
|
+
value: { ...options },
|
|
56
|
+
enumerable: false,
|
|
57
|
+
configurable: true,
|
|
58
|
+
});
|
|
32
59
|
}
|
|
33
60
|
//# sourceMappingURL=define-script.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-script.js","sourceRoot":"","sources":["../src/define-script.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"define-script.js","sourceRoot":"","sources":["../src/define-script.ts"],"names":[],"mappings":";;AAyGA,oCAYC;AAzDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,SAAgB,YAAY,CAC1B,EAAY,EACZ,OAA2B;IAE3B,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACxB,sEAAsE;IACtE,iEAAiE;IACjE,OAAO,MAAM,CAAC,cAAc,CAAC,EAAE,EAAE,aAAa,EAAE;QAC9C,KAAK,EAAE,EAAE,GAAG,OAAO,EAAE;QACrB,UAAU,EAAE,KAAK;QACjB,YAAY,EAAE,IAAI;KACnB,CAA4B,CAAC;AAChC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
* and live SIP INFO ({@link import('./types/events').SipInfo} on `sipInfo$`). See **`README.md`** → SIP Signalling Metadata.
|
|
30
30
|
*/
|
|
31
31
|
export { defineScript } from './define-script';
|
|
32
|
-
export type { ScriptContext, ScriptFn } from './define-script';
|
|
32
|
+
export type { ScriptContext, ScriptFn, ScriptFnWithCallOptions, } from './define-script';
|
|
33
|
+
export type { ScriptCallOptions, OutboundCallMode, } from './types/call-options';
|
|
33
34
|
export { defineMediaProviders } from './types/media-providers';
|
|
34
35
|
export type { MediaProvidersDefinition, MediaConnectorContext, MediaProviderShared, AsrProviderFactory, TtsProviderFactory, ScriptAsrConnector, ScriptAsrConnectorError, ScriptTtsConnector, TtsSynthesisContext as ScriptTtsSynthesisContext, } from './types/media-providers';
|
|
35
36
|
export type { ScriptDialogContext, ScriptPhase, ScriptRunTime, ScriptResult, PersistedScriptResult, ScriptError, AgentContext, AgentEnvSetOptions, StorageContextApi, } from './types/script-context';
|
|
@@ -38,7 +39,7 @@ export type { NluScriptApi, PlatformApi, DialogApi, ScheduleCallOptions, SendMes
|
|
|
38
39
|
export type { ScriptLogger } from './types/logger';
|
|
39
40
|
export { TranscriptionRole } from './types/logger';
|
|
40
41
|
export type { MediaChannel, ChannelAudio, ChannelEvents, } from './types/media-channel';
|
|
41
|
-
export type { ChannelLlm, LlmOptions, LlmStreamChunk, ExtractOptions, PersistentLlmStreamHandle, } from './types/llm';
|
|
42
|
+
export type { ChannelLlm, LlmOptions, LlmStreamChunk, ExtractOptions, PersistentLlmStreamHandle, PersistentLlmSendHandle, CreateDialogOptions, UpdateDialogOptions, DialogInfo, } from './types/llm';
|
|
42
43
|
export type { ChannelSip, SipState, SipProgressEvent, SipInviteHeaders, ParsedSdpDetails, } from './types/sip';
|
|
43
44
|
export type { MediaError } from './types/errors';
|
|
44
45
|
export type { AsrHandle, AsrConfig, AsrVadConfig, AsrSmartTurnConfig } from './types/asr-handle';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,YAAY,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,YAAY,EACV,aAAa,EACb,QAAQ,EACR,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,YAAY,EACV,wBAAwB,EACxB,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,uBAAuB,EACvB,kBAAkB,EAClB,mBAAmB,IAAI,yBAAyB,GACjD,MAAM,yBAAyB,CAAC;AAEjC,YAAY,EACV,mBAAmB,EACnB,WAAW,EACX,aAAa,EACb,YAAY,EACZ,qBAAqB,EACrB,WAAW,EACX,YAAY,EACZ,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,YAAY,EACV,YAAY,EACZ,WAAW,EACX,SAAS,EACT,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,aAAa,GACd,MAAM,uBAAuB,CAAC;AAE/B,YAAY,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,cAAc,EACd,yBAAyB,EACzB,uBAAuB,EACvB,mBAAmB,EACnB,mBAAmB,EACnB,UAAU,GACX,MAAM,aAAa,CAAC;AAErB,YAAY,EACV,UAAU,EACV,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACjG,YAAY,EACV,SAAS,EACT,SAAS,EACT,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,wBAAwB,GACzB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,iBAAiB,EAAE,WAAW,EAAE,aAAa,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC3H,YAAY,EACV,kBAAkB,EAClB,sBAAsB,EACtB,YAAY,GACb,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AACpD,YAAY,EACV,SAAS,EACT,OAAO,EACP,SAAS,EACT,WAAW,GACZ,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;;;AAEH,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;;;AAEH,iDAA+C;AAAtC,6GAAA,YAAY,OAAA;AAWrB,2DAA+D;AAAtD,uHAAA,oBAAoB,OAAA;AAyB7B,yDAAwD;AAA/C,gHAAA,cAAc,OAAA;AAavB,yCAAmD;AAA1C,2GAAA,iBAAiB,OAAA;AA4C1B,uDAG+B;AAF7B,2HAAA,0BAA0B,OAAA;AAC1B,qHAAA,oBAAoB,OAAA;AAUtB,+CAA2E;AAAlE,gHAAA,gBAAgB,OAAA;AAAE,uHAAA,uBAAuB,OAAA"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When the host hands a platform-dialed outbound leg to the script.
|
|
3
|
+
*
|
|
4
|
+
* - `'on_answer'` — the host dials and starts the script on the `200 OK`. The script
|
|
5
|
+
* always gets an answered call, which is what a greeting, a turn watchdog and a
|
|
6
|
+
* max-duration timer assume.
|
|
7
|
+
* - `'from_invite'` — the host starts the script **first**, then automatically
|
|
8
|
+
* dials the campaign number on the same `channel`. The session is already
|
|
9
|
+
* running when the INVITE goes out, so `progress$` / `sipSignal$` see 1xx and
|
|
10
|
+
* a 4xx/5xx rejects `waitForAnswer()`. Phase is `'early'`; gate the greeting
|
|
11
|
+
* on `channel.sip.waitForAnswer()`.
|
|
12
|
+
* - `'script_dial'` — the host does **not** dial. The script starts with no leg at all
|
|
13
|
+
* (phase `'dialing'`) and opens one itself:
|
|
14
|
+
*
|
|
15
|
+
* ```ts
|
|
16
|
+
* const leg = await channel.sip.makeCall({ msisdn: context.calledNumber });
|
|
17
|
+
* await leg.sip.waitForAnswer();
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* The leg it gets back is the conversation channel — the one the script plays and
|
|
21
|
+
* listens on. The channel the script starts with can only dial: it has no audio.
|
|
22
|
+
* The first leg opened this way is the one the platform reports on (result,
|
|
23
|
+
* `answer_date`, duration, recording), so a script that never dials leaves the
|
|
24
|
+
* call row with no answer.
|
|
25
|
+
*
|
|
26
|
+
* Inbound legs are unaffected — they always start before the answer. `'from_invite'`
|
|
27
|
+
* additionally lets an inbound run report `'early'` instead of `'online'`.
|
|
28
|
+
*
|
|
29
|
+
* `answer_date` and billing never move: both come from the `200 OK`.
|
|
30
|
+
*/
|
|
31
|
+
export type OutboundCallMode = 'on_answer' | 'from_invite' | 'script_dial';
|
|
32
|
+
/**
|
|
33
|
+
* Call-handling options a script declares for itself, passed as the second
|
|
34
|
+
* argument of {@link import('../define-script').defineScript}:
|
|
35
|
+
*
|
|
36
|
+
* ```ts
|
|
37
|
+
* export default defineScript(handler, { outboundCallMode: 'from_invite' });
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* The host reads them off the default export **before** the session starts,
|
|
41
|
+
* because they decide *when* it starts. A script that passes nothing gets the
|
|
42
|
+
* documented defaults.
|
|
43
|
+
*/
|
|
44
|
+
export interface ScriptCallOptions {
|
|
45
|
+
/**
|
|
46
|
+
* Where the script picks up a platform-dialed outbound leg (campaign dialer,
|
|
47
|
+
* `platform.call`). See {@link OutboundCallMode} for what each mode implies.
|
|
48
|
+
*
|
|
49
|
+
* @defaultValue 'on_answer'
|
|
50
|
+
*/
|
|
51
|
+
outboundCallMode?: OutboundCallMode;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=call-options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-options.d.ts","sourceRoot":"","sources":["../../src/types/call-options.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,MAAM,gBAAgB,GAAG,WAAW,GAAG,aAAa,GAAG,aAAa,CAAC;AAE3E;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"call-options.js","sourceRoot":"","sources":["../../src/types/call-options.ts"],"names":[],"mappings":""}
|
package/dist/types/llm.d.ts
CHANGED
|
@@ -22,6 +22,58 @@ export interface LlmOptions {
|
|
|
22
22
|
debug?: boolean;
|
|
23
23
|
/** Restrict which agent aliases may handle the request. */
|
|
24
24
|
agentAliasFilter?: string[];
|
|
25
|
+
/**
|
|
26
|
+
* Manual same-turn pseudo-reasoning hint for the answering agent.
|
|
27
|
+
* When set, Omni skips the assigned reasoner LLM call and injects this text
|
|
28
|
+
* as a hidden reasoning message before the user turn.
|
|
29
|
+
*/
|
|
30
|
+
pseudoReasoning?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Multiplex id for persistent WS turns. Generated by `send()` when omitted.
|
|
33
|
+
* Echoed on every `stream.chunk` / `stream.complete` / `stream.error`.
|
|
34
|
+
*/
|
|
35
|
+
requestId?: string;
|
|
36
|
+
}
|
|
37
|
+
/** Create an Omni dialog (HTTP or persistent `dialog.create`). */
|
|
38
|
+
export interface CreateDialogOptions {
|
|
39
|
+
/** Omni agent UUID (required). */
|
|
40
|
+
agentUuid: string;
|
|
41
|
+
/** Optional fixed dialog UUID; Omni generates one when omitted. */
|
|
42
|
+
uuid?: string;
|
|
43
|
+
/** Extra dialog prompt appended after agent system prompt. */
|
|
44
|
+
prompt?: string;
|
|
45
|
+
/** Human-readable dialog name. */
|
|
46
|
+
name?: string;
|
|
47
|
+
/** Template variables persisted on the dialog (`dialog.payload`). */
|
|
48
|
+
payload?: Record<string, any>;
|
|
49
|
+
/** Optional seed history. */
|
|
50
|
+
history?: Array<{
|
|
51
|
+
role?: string;
|
|
52
|
+
content: string;
|
|
53
|
+
hidden?: boolean;
|
|
54
|
+
name?: string;
|
|
55
|
+
}>;
|
|
56
|
+
}
|
|
57
|
+
/** Patch Omni dialog fields (HTTP `/meta` or persistent `dialog.update`). */
|
|
58
|
+
export interface UpdateDialogOptions {
|
|
59
|
+
/** Target dialog UUID; defaults to the channel / persistent stream dialog. */
|
|
60
|
+
dialogUuid?: string;
|
|
61
|
+
/** Required when the dialog may need to be created on first update. */
|
|
62
|
+
agentUuid?: string;
|
|
63
|
+
prompt?: string;
|
|
64
|
+
name?: string;
|
|
65
|
+
/** Replaces stored `dialog.payload` when set. */
|
|
66
|
+
payload?: Record<string, any>;
|
|
67
|
+
metadata?: Record<string, any>;
|
|
68
|
+
currentAgentAlias?: string;
|
|
69
|
+
}
|
|
70
|
+
/** Result of {@link ChannelLlm.createDialog} / {@link PersistentLlmStreamHandle.createDialog}. */
|
|
71
|
+
export interface DialogInfo {
|
|
72
|
+
uuid: string;
|
|
73
|
+
agentUuid?: string;
|
|
74
|
+
payload?: Record<string, any> | null;
|
|
75
|
+
prompt?: string | null;
|
|
76
|
+
currentAgentAlias?: string | null;
|
|
25
77
|
}
|
|
26
78
|
/** Options for **`channel.llm.extract`**. All fields except `dialogUuid` are forwarded to Omni extract. */
|
|
27
79
|
export interface ExtractOptions {
|
|
@@ -48,14 +100,38 @@ export interface LlmStreamChunk {
|
|
|
48
100
|
event: any;
|
|
49
101
|
toolMessages?: any[];
|
|
50
102
|
raw: Record<string, any>;
|
|
103
|
+
/** Set on persistent WS chunks so parallel sends on one socket can be demuxed. */
|
|
104
|
+
requestId?: string;
|
|
105
|
+
/** Alias that produced this chunk when the backend includes it. */
|
|
106
|
+
currentAgentAlias?: string;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* One in-flight turn on a {@link PersistentLlmStreamHandle}.
|
|
110
|
+
* Several sends may run in parallel on the same Socket.IO connection.
|
|
111
|
+
*/
|
|
112
|
+
export interface PersistentLlmSendHandle {
|
|
113
|
+
readonly requestId: string;
|
|
114
|
+
readonly stream$: Observable<LlmStreamChunk>;
|
|
115
|
+
/** Cancel this turn only (`stream.abort`); other in-flight sends keep running. */
|
|
116
|
+
abort(): void;
|
|
51
117
|
}
|
|
52
118
|
/**
|
|
53
119
|
* Long-lived LLM Socket.IO stream — amortizes connection setup across many user turns.
|
|
54
120
|
*/
|
|
55
121
|
export interface PersistentLlmStreamHandle {
|
|
122
|
+
/** Merged chunks from every in-flight `send` (filter by `chunk.requestId` if needed). */
|
|
56
123
|
readonly stream$: Observable<LlmStreamChunk>;
|
|
57
|
-
/**
|
|
58
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Send one turn over the existing socket.
|
|
126
|
+
* Returns a per-turn stream so several agents can run in parallel on one connection.
|
|
127
|
+
*/
|
|
128
|
+
send(message: string, options?: LlmOptions): PersistentLlmSendHandle;
|
|
129
|
+
/** Create a dialog (and optional payload) over the persistent socket (`dialog.create`). */
|
|
130
|
+
createDialog(options: CreateDialogOptions): Promise<DialogInfo>;
|
|
131
|
+
/** Update dialog fields / payload over the persistent socket (`dialog.update`). */
|
|
132
|
+
updateDialog(options: UpdateDialogOptions): Promise<DialogInfo>;
|
|
133
|
+
/** Cancel one in-flight turn by `requestId` (`stream.abort`). */
|
|
134
|
+
abort(requestId: string): void;
|
|
59
135
|
/** Close the underlying Socket.IO stream without destroying the handle object. */
|
|
60
136
|
disconnect(): void;
|
|
61
137
|
/** Re-open the underlying Socket.IO stream after {@link disconnect}. */
|
|
@@ -69,6 +145,10 @@ export interface ChannelLlm {
|
|
|
69
145
|
stream(message: string, options?: LlmOptions): Observable<LlmStreamChunk>;
|
|
70
146
|
/** Structured extraction / JSON-style fill from conversation context via Omni extract. */
|
|
71
147
|
extract(options?: ExtractOptions): Promise<Record<string, any>>;
|
|
148
|
+
/** Create an Omni dialog with optional payload (HTTP `POST /v1/chat`). */
|
|
149
|
+
createDialog(options: CreateDialogOptions): Promise<DialogInfo>;
|
|
150
|
+
/** Update Omni dialog payload / prompt / alias (HTTP `POST /v1/chat/:uuid/meta`). */
|
|
151
|
+
updateDialog(options: UpdateDialogOptions): Promise<DialogInfo>;
|
|
72
152
|
/**
|
|
73
153
|
* Open a persistent Omni chat stream.
|
|
74
154
|
* @param options - Default **`agentUuid`**, **`dialogUuid`**, etc.; per-**`send`** overrides allowed.
|
package/dist/types/llm.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../../src/types/llm.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAEvC,qFAAqF;AACrF,MAAM,WAAW,UAAU;IACzB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wFAAwF;IACxF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8CAA8C;IAC9C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,wDAAwD;IACxD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9B,uCAAuC;IACvC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2DAA2D;IAC3D,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../../src/types/llm.ts"],"names":[],"mappings":"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"}
|
|
@@ -168,6 +168,27 @@ export interface ScriptDialogContext {
|
|
|
168
168
|
* {@link import('./platform').DialogApi.entryPoint} (`platform.dialog.entryPoint`).
|
|
169
169
|
*/
|
|
170
170
|
entryPoint?: string;
|
|
171
|
+
/**
|
|
172
|
+
* True when this live session started **before** the final 200 OK — the leg had
|
|
173
|
+
* media (183 with SDP) but was not answered yet. Snapshot at script start: it
|
|
174
|
+
* describes how the run began and does not flip when the answer arrives; use
|
|
175
|
+
* `channel.sip.isAnswered` / `answered$` for the live state.
|
|
176
|
+
*
|
|
177
|
+
* Set by the host only for scripts that opted in via
|
|
178
|
+
* {@link import('./call-options').ScriptCallOptions.outboundCallMode} `'from_invite'`,
|
|
179
|
+
* and surfaced as {@link ScriptPhase} `'early'` by {@link getScriptPhase}.
|
|
180
|
+
*/
|
|
181
|
+
startedBeforeAnswer?: boolean;
|
|
182
|
+
/**
|
|
183
|
+
* True when this live session started with **no leg at all** — the script dials one
|
|
184
|
+
* itself via `channel.sip.makeCall()`. Set only for scripts that opted in via
|
|
185
|
+
* {@link import('./call-options').ScriptCallOptions.outboundCallMode} `'script_dial'`, and
|
|
186
|
+
* surfaced as {@link ScriptPhase} `'dialing'` by {@link getScriptPhase}.
|
|
187
|
+
*
|
|
188
|
+
* Like {@link startedBeforeAnswer}, a snapshot of how the run began: it stays true after
|
|
189
|
+
* the script opens a leg.
|
|
190
|
+
*/
|
|
191
|
+
startedWithoutLeg?: boolean;
|
|
171
192
|
/** Current recall attempt number for this dialog (from `dialog.params.attempt`, starts at 0). */
|
|
172
193
|
attempt?: number;
|
|
173
194
|
/**
|
|
@@ -231,11 +252,19 @@ export interface ScriptRunTime {
|
|
|
231
252
|
* High-level phase of a script execution lifecycle. Useful for branching logic
|
|
232
253
|
* between pre-call preparation, the live call, and post-call processing.
|
|
233
254
|
*/
|
|
234
|
-
export type ScriptPhase = 'before_call' | 'online' | 'after_call_success' | 'after_call_failed' | 'messaging' | 'recall' | 'headless_other';
|
|
255
|
+
export type ScriptPhase = 'before_call' | 'dialing' | 'early' | 'online' | 'after_call_success' | 'after_call_failed' | 'messaging' | 'recall' | 'headless_other';
|
|
235
256
|
/**
|
|
236
257
|
* Determines which phase of the dialog lifecycle the script is currently executing in.
|
|
237
258
|
*
|
|
238
259
|
* - `before_call` — headless run that typically schedules an outbound call.
|
|
260
|
+
* - `dialing` — live session that started with no leg: the script opens one itself with
|
|
261
|
+
* `channel.sip.makeCall()` and talks on the channel that returns. Only scripts that opt
|
|
262
|
+
* in via {@link import('./call-options').ScriptCallOptions.outboundCallMode} `'script_dial'`
|
|
263
|
+
* ever see it.
|
|
264
|
+
* - `early` — live SIP/media session that started before the final answer. Only scripts
|
|
265
|
+
* that opt in via {@link import('./call-options').ScriptCallOptions.outboundCallMode}
|
|
266
|
+
* `'from_invite'` ever see it; await `channel.sip.waitForAnswer()` to reach the
|
|
267
|
+
* conversation.
|
|
239
268
|
* - `online` — live SIP/media session (voice call active).
|
|
240
269
|
* - `after_call_success` — headless run after a successful call completion.
|
|
241
270
|
* - `after_call_failed` — headless run after call failure (no answer, reject, etc.).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"script-context.d.ts","sourceRoot":"","sources":["../../src/types/script-context.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"script-context.d.ts","sourceRoot":"","sources":["../../src/types/script-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,14 @@ const RECALL_ENTRY_POINTS = new Set(['on_recall', 'recall']);
|
|
|
15
15
|
* Determines which phase of the dialog lifecycle the script is currently executing in.
|
|
16
16
|
*
|
|
17
17
|
* - `before_call` — headless run that typically schedules an outbound call.
|
|
18
|
+
* - `dialing` — live session that started with no leg: the script opens one itself with
|
|
19
|
+
* `channel.sip.makeCall()` and talks on the channel that returns. Only scripts that opt
|
|
20
|
+
* in via {@link import('./call-options').ScriptCallOptions.outboundCallMode} `'script_dial'`
|
|
21
|
+
* ever see it.
|
|
22
|
+
* - `early` — live SIP/media session that started before the final answer. Only scripts
|
|
23
|
+
* that opt in via {@link import('./call-options').ScriptCallOptions.outboundCallMode}
|
|
24
|
+
* `'from_invite'` ever see it; await `channel.sip.waitForAnswer()` to reach the
|
|
25
|
+
* conversation.
|
|
18
26
|
* - `online` — live SIP/media session (voice call active).
|
|
19
27
|
* - `after_call_success` — headless run after a successful call completion.
|
|
20
28
|
* - `after_call_failed` — headless run after call failure (no answer, reject, etc.).
|
|
@@ -23,8 +31,11 @@ const RECALL_ENTRY_POINTS = new Set(['on_recall', 'recall']);
|
|
|
23
31
|
* - `headless_other` — any other headless invocation not matching the above.
|
|
24
32
|
*/
|
|
25
33
|
function getScriptPhase(context) {
|
|
26
|
-
if (!context.headless)
|
|
27
|
-
|
|
34
|
+
if (!context.headless) {
|
|
35
|
+
if (context.startedWithoutLeg)
|
|
36
|
+
return 'dialing';
|
|
37
|
+
return context.startedBeforeAnswer ? 'early' : 'online';
|
|
38
|
+
}
|
|
28
39
|
const ep = context.entryPoint?.trim().toLowerCase() ?? '';
|
|
29
40
|
if (AFTER_CALL_SUCCESS_ENTRY_POINTS.has(ep))
|
|
30
41
|
return 'after_call_success';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"script-context.js","sourceRoot":"","sources":["../../src/types/script-context.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"script-context.js","sourceRoot":"","sources":["../../src/types/script-context.ts"],"names":[],"mappings":";;AA2TA,wCAiBC;AA/CD,MAAM,+BAA+B,GAAG,IAAI,GAAG,CAAC;IAC9C,iBAAiB;IACjB,oBAAoB;IACpB,cAAc;CACf,CAAC,CAAC;AACH,MAAM,8BAA8B,GAAG,IAAI,GAAG,CAAC;IAC7C,gBAAgB;IAChB,mBAAmB;CACpB,CAAC,CAAC;AACH,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;AAE7D;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAgB,cAAc,CAAC,OAA4B;IACzD,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtB,IAAI,OAAO,CAAC,iBAAiB;YAAE,OAAO,SAAS,CAAC;QAChD,OAAO,OAAO,CAAC,mBAAmB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC1D,CAAC;IAED,MAAM,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,IAAI,EAAE,CAAC;IAE1D,IAAI,+BAA+B,CAAC,GAAG,CAAC,EAAE,CAAC;QAAE,OAAO,oBAAoB,CAAC;IACzE,IAAI,8BAA8B,CAAC,GAAG,CAAC,EAAE,CAAC;QAAE,OAAO,mBAAmB,CAAC;IACvE,IAAI,mBAAmB,CAAC,GAAG,CAAC,EAAE,CAAC;QAAE,OAAO,QAAQ,CAAC;IACjD,IAAI,EAAE,KAAK,yBAAyB,IAAI,OAAO,CAAC,cAAc;QAC5D,OAAO,WAAW,CAAC;IAErB,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,MAAM,IAAI,EAAE,KAAK,SAAS;QAAE,OAAO,aAAa,CAAC;IAEnE,OAAO,gBAAgB,CAAC;AAC1B,CAAC"}
|
package/examples/README.md
CHANGED
|
@@ -6,6 +6,9 @@ Each file exports one `defineScript` handler. The host **always** invokes that s
|
|
|
6
6
|
|
|
7
7
|
| File | Topic |
|
|
8
8
|
|------|--------|
|
|
9
|
+
| [outbound-on-answer.ts](./outbound-on-answer.ts) | Campaign outbound on `200 OK` (`outboundCallMode: 'on_answer'`, the default) |
|
|
10
|
+
| [outbound-from-invite.ts](./outbound-from-invite.ts) | Campaign outbound from INVITE (`outboundCallMode: 'from_invite'`) |
|
|
11
|
+
| [outbound-script-dial.ts](./outbound-script-dial.ts) | Script dials the campaign leg (`outboundCallMode: 'script_dial'`) |
|
|
9
12
|
| [outbound-with-recall.ts](./outbound-with-recall.ts) | Outbound with `recallCount` / `recallDelay` |
|
|
10
13
|
| [recall-routing-by-attempt.ts](./recall-routing-by-attempt.ts) | Branch on `context.attempt` during online recall legs |
|
|
11
14
|
| [schedule-call-with-defaults.ts](./schedule-call-with-defaults.ts) | `platform.call()` using CMS defaults from `context` |
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Campaign outbound that starts at INVITE (`outboundCallMode: 'from_invite'`).
|
|
3
|
+
*
|
|
4
|
+
* The host raises the script first, then dials the campaign number on the same
|
|
5
|
+
* `channel`. 1xx lands on `progress$`; a 4xx/5xx rejects `waitForAnswer()`.
|
|
6
|
+
* Gate the greeting on the answer so it does not play into ringback.
|
|
7
|
+
*
|
|
8
|
+
* `getScriptPhase` stays `'early'` for the whole live run. Read
|
|
9
|
+
* `channel.sip.isAnswered` for the live state. Billing stays on the 200 OK.
|
|
10
|
+
*/
|
|
11
|
+
import { defineScript, getScriptPhase } from '@voctiv/agent-sdk';
|
|
12
|
+
|
|
13
|
+
export default defineScript(
|
|
14
|
+
async ({ channel, logger, context }) => {
|
|
15
|
+
if (getScriptPhase(context) === 'early') {
|
|
16
|
+
channel.sip.progress$.subscribe(({ statusCode }) => {
|
|
17
|
+
logger.log('progress', { statusCode });
|
|
18
|
+
});
|
|
19
|
+
try {
|
|
20
|
+
await channel.sip.waitForAnswer();
|
|
21
|
+
} catch (err) {
|
|
22
|
+
logger.warn('outbound failed', { err });
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
await channel.audio.say('Hello!');
|
|
28
|
+
},
|
|
29
|
+
{ outboundCallMode: 'from_invite' },
|
|
30
|
+
);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Campaign outbound that starts on the 200 OK (`outboundCallMode: 'on_answer'`).
|
|
3
|
+
*
|
|
4
|
+
* This is the default: omit the second argument of `defineScript` and you get
|
|
5
|
+
* the same timing. The host dials first and holds the script until answer.
|
|
6
|
+
* Busy / no-answer / 4xx never reach this handler.
|
|
7
|
+
*
|
|
8
|
+
* Talk on `channel`. Do not `makeCall()` the campaign number — the host
|
|
9
|
+
* already did. Billing stays on the 200 OK.
|
|
10
|
+
*/
|
|
11
|
+
import { defineScript, getScriptPhase } from '@voctiv/agent-sdk';
|
|
12
|
+
|
|
13
|
+
export default defineScript(async ({ channel, context }) => {
|
|
14
|
+
// Live outbound: always 'online'. Headless before_call / after_call_* still apply.
|
|
15
|
+
if (getScriptPhase(context) !== 'online') return;
|
|
16
|
+
|
|
17
|
+
await channel.audio.say('Hello!');
|
|
18
|
+
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Campaign outbound where the script opens the leg (`outboundCallMode: 'script_dial'`).
|
|
3
|
+
*
|
|
4
|
+
* The host does not send an INVITE. Phase is `'dialing'`; the starting `channel`
|
|
5
|
+
* can only dial. Conversation is on the channel `makeCall()` returns.
|
|
6
|
+
* `makeCall({})` uses the campaign msisdn + the call row's trunk.
|
|
7
|
+
*
|
|
8
|
+
* The first successful dial is the campaign row (result, `answer_date`, recording).
|
|
9
|
+
*/
|
|
10
|
+
import { defineScript, getScriptPhase } from '@voctiv/agent-sdk';
|
|
11
|
+
|
|
12
|
+
export default defineScript(
|
|
13
|
+
async (ctx) => {
|
|
14
|
+
const phase = getScriptPhase(ctx.context);
|
|
15
|
+
const channel =
|
|
16
|
+
phase === 'dialing' ? await ctx.channel.sip.makeCall({}) : ctx.channel;
|
|
17
|
+
await channel.audio.say('Hello!');
|
|
18
|
+
},
|
|
19
|
+
{ outboundCallMode: 'script_dial' },
|
|
20
|
+
);
|
package/package.json
CHANGED