supafone-labs 0.3.0 → 0.3.2
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 +87 -15
- package/dist/cjs/index.d.ts +365 -7
- package/dist/cjs/index.js +457 -17
- package/dist/index.d.ts +365 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +456 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +819 -21
package/dist/cjs/index.js
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Works in Node 18+ (native fetch/WebSocket) and the browser.
|
|
12
12
|
*
|
|
13
|
-
* npm i
|
|
13
|
+
* npm i supafone-labs
|
|
14
14
|
*
|
|
15
|
-
* import { Supafone } from "
|
|
15
|
+
* import { Supafone } from "supafone-labs";
|
|
16
16
|
* const supafone = new Supafone({ apiKey: process.env.SUPAFONE_API_KEY! });
|
|
17
17
|
* const agent = await supafone.labs.agents.createInboundWithNumber({
|
|
18
18
|
* agentKey: "northline-intake",
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
24
|
exports.Supafone = exports.SupafoneLabs = exports.SupafoneLabsError = void 0;
|
|
25
|
+
exports.generateCallStages = generateCallStages;
|
|
25
26
|
class SupafoneLabsError extends Error {
|
|
26
27
|
status;
|
|
27
28
|
body;
|
|
@@ -102,7 +103,7 @@ class SupafoneLabs {
|
|
|
102
103
|
clearTimeout(timer);
|
|
103
104
|
}
|
|
104
105
|
}
|
|
105
|
-
/** @internal Authenticated JSON request to the Supafone app API (`/api/
|
|
106
|
+
/** @internal Authenticated JSON request to the Supafone app API (`/api/v1/labs/*`). */
|
|
106
107
|
async requestSupafoneApi(method, path, body) {
|
|
107
108
|
const ctrl = new AbortController();
|
|
108
109
|
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
@@ -170,6 +171,10 @@ class SupafoneLabs {
|
|
|
170
171
|
clearTimeout(timer);
|
|
171
172
|
}
|
|
172
173
|
}
|
|
174
|
+
/** Convenience alias for voice preview UI/buttons. */
|
|
175
|
+
previewVoice(voice = "supafone-labs-calm-en", text = "Hi, this is your Supafone agent voice preview.") {
|
|
176
|
+
return this.tts(text, voice);
|
|
177
|
+
}
|
|
173
178
|
/** Hosted STT for a finished audio clip — returns transcript + language tags. */
|
|
174
179
|
async stt(audio, opts = {}) {
|
|
175
180
|
const bytes = audio instanceof ArrayBuffer ? new Uint8Array(audio) : audio;
|
|
@@ -216,6 +221,48 @@ class SupafoneLabs {
|
|
|
216
221
|
logs(limit = 100) {
|
|
217
222
|
return this.request("GET", `/v1/logs?limit=${limit}`);
|
|
218
223
|
}
|
|
224
|
+
/** Live audit-log stream. Works in Node 18+ and browsers via fetch streaming. */
|
|
225
|
+
async *streamLogs(opts = {}) {
|
|
226
|
+
const q = new URLSearchParams({
|
|
227
|
+
limit: String(opts.limit ?? 100),
|
|
228
|
+
poll_ms: String(opts.pollMs ?? 1000),
|
|
229
|
+
snapshot: String(opts.snapshot ?? true),
|
|
230
|
+
});
|
|
231
|
+
if (opts.afterId !== undefined)
|
|
232
|
+
q.set("after_id", String(opts.afterId));
|
|
233
|
+
const res = await fetch(`${this.baseUrl}/v1/logs/stream?${q}`, {
|
|
234
|
+
method: "GET",
|
|
235
|
+
signal: opts.signal,
|
|
236
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
237
|
+
});
|
|
238
|
+
if (!res.ok)
|
|
239
|
+
throw new SupafoneLabsError(`streamLogs: ${await res.text()}`, res.status);
|
|
240
|
+
if (!res.body)
|
|
241
|
+
throw new SupafoneLabsError("streamLogs: response body is not readable");
|
|
242
|
+
const reader = res.body.getReader();
|
|
243
|
+
const decoder = new TextDecoder();
|
|
244
|
+
let buffer = "";
|
|
245
|
+
try {
|
|
246
|
+
for (;;) {
|
|
247
|
+
const { done, value } = await reader.read();
|
|
248
|
+
if (done)
|
|
249
|
+
break;
|
|
250
|
+
buffer += decoder.decode(value, { stream: true });
|
|
251
|
+
let split = buffer.indexOf("\n\n");
|
|
252
|
+
while (split >= 0) {
|
|
253
|
+
const raw = buffer.slice(0, split);
|
|
254
|
+
buffer = buffer.slice(split + 2);
|
|
255
|
+
const parsed = parseSseLog(raw);
|
|
256
|
+
if (parsed)
|
|
257
|
+
yield parsed;
|
|
258
|
+
split = buffer.indexOf("\n\n");
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
finally {
|
|
263
|
+
reader.releaseLock();
|
|
264
|
+
}
|
|
265
|
+
}
|
|
219
266
|
/** The structured whisper feed (what the console shows). */
|
|
220
267
|
nudges(limit = 50) {
|
|
221
268
|
return this.request("GET", `/v1/nudges?limit=${limit}`);
|
|
@@ -242,6 +289,10 @@ class SupafoneLabs {
|
|
|
242
289
|
const d = await this.request("GET", "/v1/voices");
|
|
243
290
|
return d.voices.map((v) => (typeof v === "string" ? v : (v.voice ?? v.id ?? ""))).filter(Boolean);
|
|
244
291
|
}
|
|
292
|
+
/** Full hosted voice catalog with provider live/configured flags. */
|
|
293
|
+
voiceCatalog() {
|
|
294
|
+
return this.request("GET", "/v1/voices");
|
|
295
|
+
}
|
|
245
296
|
}
|
|
246
297
|
exports.SupafoneLabs = SupafoneLabs;
|
|
247
298
|
exports.Supafone = SupafoneLabs;
|
|
@@ -299,6 +350,9 @@ class LabsNamespace {
|
|
|
299
350
|
voices;
|
|
300
351
|
phoneNumbers;
|
|
301
352
|
telephony;
|
|
353
|
+
calls;
|
|
354
|
+
recordings;
|
|
355
|
+
transcripts;
|
|
302
356
|
constructor(sm) {
|
|
303
357
|
this.sm = sm;
|
|
304
358
|
this.agents = new LabsAgentsNamespace(sm);
|
|
@@ -307,10 +361,13 @@ class LabsNamespace {
|
|
|
307
361
|
this.voices = new LabsVoicesNamespace(sm);
|
|
308
362
|
this.phoneNumbers = new LabsPhoneNumbersNamespace(sm);
|
|
309
363
|
this.telephony = new LabsTelephonyNamespace(sm);
|
|
364
|
+
this.calls = new LabsCallsNamespace(sm);
|
|
365
|
+
this.recordings = new LabsRecordingsNamespace(sm);
|
|
366
|
+
this.transcripts = new LabsTranscriptsNamespace(sm);
|
|
310
367
|
}
|
|
311
368
|
/** Discover the Supafone convenience layer over Ultravox. */
|
|
312
369
|
capabilities() {
|
|
313
|
-
return this.sm.requestSupafoneApi("GET", "/api/
|
|
370
|
+
return this.sm.requestSupafoneApi("GET", "/api/v1/labs/capabilities");
|
|
314
371
|
}
|
|
315
372
|
}
|
|
316
373
|
class LabsAgentsNamespace {
|
|
@@ -320,7 +377,7 @@ class LabsAgentsNamespace {
|
|
|
320
377
|
}
|
|
321
378
|
/** Spawn a durable hosted Supafone agent backed by Ultravox and Supafone-managed providers. */
|
|
322
379
|
create(input) {
|
|
323
|
-
return this.sm.requestSupafoneApi("POST", "/api/
|
|
380
|
+
return this.sm.requestSupafoneApi("POST", "/api/v1/labs/agents", labsAgentPayload(input));
|
|
324
381
|
}
|
|
325
382
|
/** Create an inbound receptionist/intake agent. No Twilio account is required. */
|
|
326
383
|
createInbound(input) {
|
|
@@ -390,7 +447,7 @@ class LabsAgentsNamespace {
|
|
|
390
447
|
if (opts.style)
|
|
391
448
|
q.set("style", opts.style);
|
|
392
449
|
const suffix = q.toString() ? `?${q}` : "";
|
|
393
|
-
return this.sm.requestSupafoneApi("GET", `/api/
|
|
450
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/agents${suffix}`);
|
|
394
451
|
}
|
|
395
452
|
/** Fetch one durable agent by key. */
|
|
396
453
|
get(agentKey, opts = {}) {
|
|
@@ -400,7 +457,19 @@ class LabsAgentsNamespace {
|
|
|
400
457
|
if (opts.agentType)
|
|
401
458
|
q.set("agent_type", opts.agentType);
|
|
402
459
|
const suffix = q.toString() ? `?${q}` : "";
|
|
403
|
-
return this.sm.requestSupafoneApi("GET", `/api/
|
|
460
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}${suffix}`);
|
|
461
|
+
}
|
|
462
|
+
/** Delete an agent. Optionally ask the backend to release assigned numbers. */
|
|
463
|
+
delete(agentKey, opts = {}) {
|
|
464
|
+
const q = new URLSearchParams();
|
|
465
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
466
|
+
const releaseNumbers = opts.release_numbers ?? opts.releaseNumbers;
|
|
467
|
+
if (agencyId)
|
|
468
|
+
q.set("agency_id", agencyId);
|
|
469
|
+
if (releaseNumbers !== undefined)
|
|
470
|
+
q.set("release_numbers", String(releaseNumbers));
|
|
471
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
472
|
+
return this.sm.requestSupafoneApi("DELETE", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}${suffix}`);
|
|
404
473
|
}
|
|
405
474
|
}
|
|
406
475
|
class LabsPresetsNamespace {
|
|
@@ -410,7 +479,7 @@ class LabsPresetsNamespace {
|
|
|
410
479
|
}
|
|
411
480
|
/** Out-of-the-box multistage agent presets. */
|
|
412
481
|
list() {
|
|
413
|
-
return this.sm.requestSupafoneApi("GET", "/api/
|
|
482
|
+
return this.sm.requestSupafoneApi("GET", "/api/v1/labs/presets");
|
|
414
483
|
}
|
|
415
484
|
}
|
|
416
485
|
class LabsToolsNamespace {
|
|
@@ -420,7 +489,7 @@ class LabsToolsNamespace {
|
|
|
420
489
|
}
|
|
421
490
|
/** Built-in tools Supafone agents can use. */
|
|
422
491
|
list() {
|
|
423
|
-
return this.sm.requestSupafoneApi("GET", "/api/
|
|
492
|
+
return this.sm.requestSupafoneApi("GET", "/api/v1/labs/tools");
|
|
424
493
|
}
|
|
425
494
|
}
|
|
426
495
|
class LabsVoicesNamespace {
|
|
@@ -434,7 +503,76 @@ class LabsVoicesNamespace {
|
|
|
434
503
|
if (opts.provider)
|
|
435
504
|
q.set("provider", opts.provider);
|
|
436
505
|
const suffix = q.toString() ? `?${q}` : "";
|
|
437
|
-
return this.sm.requestSupafoneApi("GET", `/api/
|
|
506
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/voices${suffix}`);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
class LabsCallsNamespace {
|
|
510
|
+
sm;
|
|
511
|
+
constructor(sm) {
|
|
512
|
+
this.sm = sm;
|
|
513
|
+
}
|
|
514
|
+
list(opts = {}) {
|
|
515
|
+
const q = hostedListQuery(opts);
|
|
516
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
517
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/calls${suffix}`);
|
|
518
|
+
}
|
|
519
|
+
get(callId, opts = {}) {
|
|
520
|
+
const q = new URLSearchParams();
|
|
521
|
+
if (opts.agencyId)
|
|
522
|
+
q.set("agency_id", opts.agencyId);
|
|
523
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
524
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/calls/${encodeURIComponent(callId)}${suffix}`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
class LabsRecordingsNamespace {
|
|
528
|
+
sm;
|
|
529
|
+
constructor(sm) {
|
|
530
|
+
this.sm = sm;
|
|
531
|
+
}
|
|
532
|
+
list(opts = {}) {
|
|
533
|
+
const q = hostedListQuery(opts);
|
|
534
|
+
const callId = opts.call_id ?? opts.callId;
|
|
535
|
+
if (callId)
|
|
536
|
+
q.set("call_id", callId);
|
|
537
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
538
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/recordings${suffix}`);
|
|
539
|
+
}
|
|
540
|
+
get(recordingId, opts = {}) {
|
|
541
|
+
const q = new URLSearchParams();
|
|
542
|
+
if (opts.agencyId)
|
|
543
|
+
q.set("agency_id", opts.agencyId);
|
|
544
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
545
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/recordings/${encodeURIComponent(recordingId)}${suffix}`);
|
|
546
|
+
}
|
|
547
|
+
delete(recordingId, opts = {}) {
|
|
548
|
+
const q = new URLSearchParams();
|
|
549
|
+
if (opts.agencyId)
|
|
550
|
+
q.set("agency_id", opts.agencyId);
|
|
551
|
+
if (opts.reason)
|
|
552
|
+
q.set("reason", opts.reason);
|
|
553
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
554
|
+
return this.sm.requestSupafoneApi("DELETE", `/api/v1/labs/recordings/${encodeURIComponent(recordingId)}${suffix}`);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
class LabsTranscriptsNamespace {
|
|
558
|
+
sm;
|
|
559
|
+
constructor(sm) {
|
|
560
|
+
this.sm = sm;
|
|
561
|
+
}
|
|
562
|
+
list(opts = {}) {
|
|
563
|
+
const q = hostedListQuery(opts);
|
|
564
|
+
const callId = opts.call_id ?? opts.callId;
|
|
565
|
+
if (callId)
|
|
566
|
+
q.set("call_id", callId);
|
|
567
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
568
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/transcripts${suffix}`);
|
|
569
|
+
}
|
|
570
|
+
get(transcriptId, opts = {}) {
|
|
571
|
+
const q = new URLSearchParams();
|
|
572
|
+
if (opts.agencyId)
|
|
573
|
+
q.set("agency_id", opts.agencyId);
|
|
574
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
575
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/transcripts/${encodeURIComponent(transcriptId)}${suffix}`);
|
|
438
576
|
}
|
|
439
577
|
}
|
|
440
578
|
class LabsPhoneNumbersNamespace {
|
|
@@ -450,22 +588,43 @@ class LabsPhoneNumbersNamespace {
|
|
|
450
588
|
if (opts.activeOnly !== undefined)
|
|
451
589
|
q.set("active_only", String(opts.activeOnly));
|
|
452
590
|
const suffix = q.toString() ? `?${q}` : "";
|
|
453
|
-
return this.sm.requestSupafoneApi("GET", `/api/
|
|
591
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/phone-numbers${suffix}`);
|
|
454
592
|
}
|
|
455
593
|
/** Search Supafone-managed inventory. This uses Supafone's master telephony account. */
|
|
456
594
|
search(opts = {}) {
|
|
457
|
-
return this.sm.requestSupafoneApi("POST", "/api/
|
|
595
|
+
return this.sm.requestSupafoneApi("POST", "/api/v1/labs/phone-numbers/search", phoneNumberSearchPayload(opts));
|
|
458
596
|
}
|
|
459
597
|
/** Buy a Supafone-managed number. Developers do not need a Twilio account. */
|
|
460
598
|
buy(input) {
|
|
461
|
-
return this.sm.requestSupafoneApi("POST", "/api/
|
|
599
|
+
return this.sm.requestSupafoneApi("POST", "/api/v1/labs/phone-numbers", phoneNumberProvisionPayload({
|
|
462
600
|
...input,
|
|
463
601
|
telephony: input.telephony ?? { mode: "supafone_managed", provider: "supafone" },
|
|
464
602
|
}));
|
|
465
603
|
}
|
|
466
604
|
/** Attach an existing Supafone number to an inbound or outbound agent. */
|
|
467
605
|
assign(numberId, input = {}) {
|
|
468
|
-
return this.sm.requestSupafoneApi("POST", `/api/
|
|
606
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/labs/phone-numbers/${encodeURIComponent(numberId)}/assign`, phoneNumberAssignPayload(input));
|
|
607
|
+
}
|
|
608
|
+
/** Unassign a number from an agent but keep it reserved on the account. */
|
|
609
|
+
unassign(numberId, input = {}) {
|
|
610
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/labs/phone-numbers/${encodeURIComponent(numberId)}/unassign`, phoneNumberReleasePayload(input));
|
|
611
|
+
}
|
|
612
|
+
/** Give a number back to the shared pool or release the reservation. */
|
|
613
|
+
release(numberId, input = {}) {
|
|
614
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/labs/phone-numbers/${encodeURIComponent(numberId)}/release`, phoneNumberReleasePayload({ ...input, returnToPool: input.returnToPool ?? input.return_to_pool ?? true }));
|
|
615
|
+
}
|
|
616
|
+
/** Alias for release(numberId, { returnToPool: true }). */
|
|
617
|
+
returnToPool(numberId, input = {}) {
|
|
618
|
+
return this.release(numberId, { ...input, returnToPool: true });
|
|
619
|
+
}
|
|
620
|
+
/** Delete/release a number reservation. Backend policy decides whether this is allowed. */
|
|
621
|
+
delete(numberId, input = {}) {
|
|
622
|
+
const q = new URLSearchParams();
|
|
623
|
+
const agencyId = input.agency_id ?? input.agencyId;
|
|
624
|
+
if (agencyId)
|
|
625
|
+
q.set("agency_id", agencyId);
|
|
626
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
627
|
+
return this.sm.requestSupafoneApi("DELETE", `/api/v1/labs/phone-numbers/${encodeURIComponent(numberId)}${suffix}`, phoneNumberReleasePayload(input));
|
|
469
628
|
}
|
|
470
629
|
/**
|
|
471
630
|
* Search if needed, buy the first matching Supafone-managed number, and assign
|
|
@@ -502,11 +661,11 @@ class LabsTelephonyNamespace {
|
|
|
502
661
|
if (opts.agencyId)
|
|
503
662
|
q.set("agency_id", opts.agencyId);
|
|
504
663
|
const suffix = q.toString() ? `?${q}` : "";
|
|
505
|
-
return this.sm.requestSupafoneApi("GET", `/api/
|
|
664
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/telephony${suffix}`);
|
|
506
665
|
}
|
|
507
666
|
/** Configure advanced BYOK telephony, or reset back to Supafone-managed. */
|
|
508
667
|
configure(input) {
|
|
509
|
-
return this.sm.requestSupafoneApi("PUT", "/api/
|
|
668
|
+
return this.sm.requestSupafoneApi("PUT", "/api/v1/labs/telephony", telephonyPayload(input));
|
|
510
669
|
}
|
|
511
670
|
/** Reset to the seamless default where Supafone buys and routes numbers. */
|
|
512
671
|
useSupafoneManaged(agencyId) {
|
|
@@ -567,6 +726,10 @@ class OptimizerNamespace {
|
|
|
567
726
|
standing(agent = "builder") {
|
|
568
727
|
return this.sm.request("GET", `/v1/optimizer/standing?agent=${encodeURIComponent(agent)}`);
|
|
569
728
|
}
|
|
729
|
+
/** SSR grade distribution: five nominal levels folded into a real score distribution. */
|
|
730
|
+
distribution(agent = "builder", limit = 500) {
|
|
731
|
+
return this.sm.request("GET", `/v1/objective/distribution?agent=${encodeURIComponent(agent)}&limit=${limit}`);
|
|
732
|
+
}
|
|
570
733
|
/** List the post-call reports behind the optimizer. */
|
|
571
734
|
reports(agent = "builder", limit = 40) {
|
|
572
735
|
return this.sm.request("GET", `/v1/optimizer/reports?agent=${encodeURIComponent(agent)}&limit=${limit}`);
|
|
@@ -584,17 +747,26 @@ function labsAgentPayload(input) {
|
|
|
584
747
|
industry: input.industry,
|
|
585
748
|
website_url: input.website_url ?? input.websiteUrl,
|
|
586
749
|
phone_number: input.phone_number ?? input.phoneNumber,
|
|
750
|
+
number_strategy: input.number_strategy ?? input.numberStrategy,
|
|
751
|
+
number_pool: input.number_pool ?? input.numberPool,
|
|
752
|
+
premium: input.premium,
|
|
587
753
|
direction: input.direction,
|
|
588
754
|
preset_key: input.preset_key ?? input.presetKey,
|
|
589
755
|
runtime_mode: input.runtime_mode ?? input.runtimeMode,
|
|
756
|
+
call_stages: callStagesPayload(input),
|
|
590
757
|
goal: input.goal,
|
|
591
758
|
greeting: input.greeting,
|
|
592
759
|
system_prompt: input.system_prompt ?? input.systemPrompt,
|
|
593
760
|
language: input.language,
|
|
594
761
|
voice: input.voice ? voicePayload(input.voice) : undefined,
|
|
595
762
|
provider_keys: input.provider_keys ?? (input.providerKeys ? providerKeysPayload(input.providerKeys) : undefined),
|
|
596
|
-
byok: input.byok ?
|
|
763
|
+
byok: input.byok ? byokPayload(input.byok) : undefined,
|
|
597
764
|
telephony: input.telephony ? telephonyPayload(input.telephony) : undefined,
|
|
765
|
+
custom_sip: customSipPayload(input.custom_sip ?? input.customSip ?? input.sip),
|
|
766
|
+
recording: input.recording ? recordingPayload(input.recording) : undefined,
|
|
767
|
+
transcription: input.transcription ? transcriptionPayload(input.transcription) : undefined,
|
|
768
|
+
artifacts: input.artifacts ? artifactsPayload(input.artifacts) : undefined,
|
|
769
|
+
compliance: input.compliance,
|
|
598
770
|
tools: input.tools ? toolsPayload(input.tools) : undefined,
|
|
599
771
|
labs: input.labs ? labsPayload(input.labs) : undefined,
|
|
600
772
|
ultravox: input.ultravox ? ultravoxPayload(input.ultravox) : undefined,
|
|
@@ -603,13 +775,30 @@ function labsAgentPayload(input) {
|
|
|
603
775
|
metadata: input.metadata,
|
|
604
776
|
});
|
|
605
777
|
}
|
|
778
|
+
function hostedListQuery(opts) {
|
|
779
|
+
const q = new URLSearchParams();
|
|
780
|
+
if (opts.agencyId)
|
|
781
|
+
q.set("agency_id", opts.agencyId);
|
|
782
|
+
const agentKey = opts.agent_key ?? opts.agentKey;
|
|
783
|
+
if (agentKey)
|
|
784
|
+
q.set("agent_key", agentKey);
|
|
785
|
+
if (opts.limit !== undefined)
|
|
786
|
+
q.set("limit", String(opts.limit));
|
|
787
|
+
return q;
|
|
788
|
+
}
|
|
606
789
|
function telephonyPayload(input) {
|
|
607
790
|
return compact({
|
|
608
791
|
agency_id: input.agency_id ?? input.agencyId,
|
|
609
792
|
mode: input.mode,
|
|
610
793
|
provider: input.provider,
|
|
794
|
+
number_strategy: input.number_strategy ?? input.numberStrategy,
|
|
795
|
+
number_pool: input.number_pool ?? input.numberPool,
|
|
796
|
+
number_id: input.number_id ?? input.numberId,
|
|
797
|
+
premium: input.premium,
|
|
611
798
|
label: input.label,
|
|
612
799
|
credentials: input.credentials ? telephonyCredentialsPayload(input.credentials) : undefined,
|
|
800
|
+
provider_settings: input.provider_settings ?? input.providerSettings,
|
|
801
|
+
custom_sip: customSipPayload(input.custom_sip ?? input.customSip),
|
|
613
802
|
metadata: input.metadata,
|
|
614
803
|
});
|
|
615
804
|
}
|
|
@@ -627,11 +816,22 @@ function telephonyCredentialsPayload(input) {
|
|
|
627
816
|
username: input.username,
|
|
628
817
|
password: input.password,
|
|
629
818
|
webhook_secret: input.webhook_secret ?? input.webhookSecret,
|
|
819
|
+
telnyx_connection_id: input.telnyx_connection_id ?? input.telnyxConnectionId,
|
|
820
|
+
signalwire_space_url: input.signalwire_space_url ?? input.signalwireSpaceUrl,
|
|
821
|
+
project_id: input.project_id ?? input.projectId,
|
|
822
|
+
application_id: input.application_id ?? input.applicationId,
|
|
823
|
+
trunk_id: input.trunk_id ?? input.trunkId,
|
|
824
|
+
endpoint_id: input.endpoint_id ?? input.endpointId,
|
|
825
|
+
token: input.token,
|
|
826
|
+
secret: input.secret,
|
|
827
|
+
custom_sip: customSipPayload(input.custom_sip ?? input.customSip),
|
|
630
828
|
});
|
|
631
829
|
}
|
|
632
830
|
function phoneNumberSearchPayload(input) {
|
|
633
831
|
return compact({
|
|
634
832
|
agency_id: input.agency_id ?? input.agencyId,
|
|
833
|
+
number_pool: input.number_pool ?? input.numberPool,
|
|
834
|
+
number_strategy: input.number_strategy ?? input.numberStrategy,
|
|
635
835
|
country_code: input.country_code ?? input.countryCode,
|
|
636
836
|
area_code: input.area_code ?? input.areaCode,
|
|
637
837
|
postal_code: input.postal_code ?? input.postalCode,
|
|
@@ -652,6 +852,9 @@ function phoneNumberProvisionPayload(input) {
|
|
|
652
852
|
agent_id: input.agent_id ?? input.agentId,
|
|
653
853
|
agent_name: input.agent_name ?? input.agentName,
|
|
654
854
|
preset_key: input.preset_key ?? input.presetKey,
|
|
855
|
+
number_strategy: input.number_strategy ?? input.numberStrategy,
|
|
856
|
+
number_pool: input.number_pool ?? input.numberPool,
|
|
857
|
+
premium: input.premium,
|
|
655
858
|
style: input.agent_style ?? input.agentStyle ?? input.style,
|
|
656
859
|
direction: input.direction,
|
|
657
860
|
telephony: input.telephony ? telephonyPayload(input.telephony) : undefined,
|
|
@@ -666,12 +869,99 @@ function phoneNumberAssignPayload(input) {
|
|
|
666
869
|
agent_name: input.agent_name ?? input.agentName,
|
|
667
870
|
friendly_name: input.friendly_name ?? input.friendlyName,
|
|
668
871
|
preset_key: input.preset_key ?? input.presetKey,
|
|
872
|
+
number_strategy: input.number_strategy ?? input.numberStrategy,
|
|
873
|
+
number_pool: input.number_pool ?? input.numberPool,
|
|
874
|
+
premium: input.premium,
|
|
669
875
|
style: input.agent_style ?? input.agentStyle ?? input.style,
|
|
670
876
|
direction: input.direction,
|
|
671
877
|
telephony: input.telephony ? telephonyPayload(input.telephony) : undefined,
|
|
672
878
|
metadata: input.metadata,
|
|
673
879
|
});
|
|
674
880
|
}
|
|
881
|
+
function phoneNumberReleasePayload(input) {
|
|
882
|
+
return compact({
|
|
883
|
+
agency_id: input.agency_id ?? input.agencyId,
|
|
884
|
+
reason: input.reason,
|
|
885
|
+
return_to_pool: input.return_to_pool ?? input.returnToPool,
|
|
886
|
+
metadata: input.metadata,
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
function callStagesPayload(input) {
|
|
890
|
+
const explicit = input.call_stages ?? input.callStages ?? input.stages;
|
|
891
|
+
const auto = input.auto_call_stages ?? input.autoCallStages;
|
|
892
|
+
if (Array.isArray(explicit))
|
|
893
|
+
return explicit.map(callStagePayload);
|
|
894
|
+
if (explicit === false || auto === false)
|
|
895
|
+
return undefined;
|
|
896
|
+
return generateCallStages(input).map(callStagePayload);
|
|
897
|
+
}
|
|
898
|
+
function callStagePayload(input) {
|
|
899
|
+
return compact({
|
|
900
|
+
key: input.key ?? input.id,
|
|
901
|
+
name: input.name,
|
|
902
|
+
goal: input.goal,
|
|
903
|
+
instructions: input.instructions,
|
|
904
|
+
exit_criteria: input.exit_criteria ?? input.exitCriteria,
|
|
905
|
+
tools: input.tools,
|
|
906
|
+
metadata: input.metadata,
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
function generateCallStages(input) {
|
|
910
|
+
const direction = String(input.direction ?? input.agent_style ?? input.agentStyle ?? input.style ?? "inbound").toLowerCase();
|
|
911
|
+
const haystack = [
|
|
912
|
+
input.name,
|
|
913
|
+
input.assistant_name ?? input.assistantName,
|
|
914
|
+
input.business_name ?? input.businessName,
|
|
915
|
+
input.industry,
|
|
916
|
+
input.goal,
|
|
917
|
+
input.system_prompt ?? input.systemPrompt,
|
|
918
|
+
input.preset_key ?? input.presetKey,
|
|
919
|
+
].filter(Boolean).join(" ").toLowerCase();
|
|
920
|
+
const meta = { auto_generated: true, source: "supafone-labs-sdk" };
|
|
921
|
+
if (direction === "outbound" || haystack.includes("sales") || haystack.includes("lead")) {
|
|
922
|
+
return [
|
|
923
|
+
stage("intro_consent", "Intro and consent", "State who you are, why you are calling, and offer an immediate opt-out.", ["Caller understands purpose", "Opt-out is honored"], meta),
|
|
924
|
+
stage("qualification", "Qualification", "Confirm fit, urgency, decision process, and the best next step.", ["Need and timeline are clear"], meta),
|
|
925
|
+
stage("offer", "Offer", "Explain the next step in plain language without unsupported claims.", ["Caller understands the next step"], meta),
|
|
926
|
+
stage("booking", "Booking", "Book or route the caller only after confirming required details.", ["Next step is confirmed by a tool or human handoff"], meta),
|
|
927
|
+
stage("close", "Close", "Summarize what will happen next and end politely.", ["Caller knows the follow-up path"], meta),
|
|
928
|
+
];
|
|
929
|
+
}
|
|
930
|
+
if (haystack.includes("legal") || haystack.includes("law") || haystack.includes("injury") || haystack.includes("intake")) {
|
|
931
|
+
return [
|
|
932
|
+
stage("greeting", "Greeting", "Open warmly and acknowledge the caller before logistics.", ["Caller need is understood"], meta),
|
|
933
|
+
stage("incident", "Incident details", "Collect what happened, when it happened, injuries, insurance, and contact details.", ["Core facts are collected"], meta),
|
|
934
|
+
stage("screening", "Screening", "Identify urgency, jurisdiction, conflicts, and whether human escalation is required.", ["Escalation decision is clear"], meta),
|
|
935
|
+
stage("booking", "Consult booking", "Book the right next step without quoting fees or inventing availability.", ["Booking or handoff is tool-confirmed"], meta),
|
|
936
|
+
stage("close", "Close", "Summarize the next step and set expectations accurately.", ["Caller knows exactly what happens next"], meta),
|
|
937
|
+
];
|
|
938
|
+
}
|
|
939
|
+
if (haystack.includes("medical") || haystack.includes("clinic") || haystack.includes("patient") || haystack.includes("health")) {
|
|
940
|
+
return [
|
|
941
|
+
stage("greeting", "Greeting", "Identify the caller need and keep the tone calm and concise.", ["Caller need is understood"], meta),
|
|
942
|
+
stage("patient_context", "Patient context", "Collect non-sensitive scheduling context and avoid medical advice.", ["Required scheduling context is collected"], meta),
|
|
943
|
+
stage("routing", "Routing", "Route urgent, billing, clinical, and scheduling requests correctly.", ["Correct route is selected"], meta),
|
|
944
|
+
stage("appointment", "Appointment", "Book or request the appointment only after confirming details.", ["Appointment path is confirmed"], meta),
|
|
945
|
+
stage("close", "Close", "Recap next steps and any confirmed timing.", ["Caller knows the next step"], meta),
|
|
946
|
+
];
|
|
947
|
+
}
|
|
948
|
+
return [
|
|
949
|
+
stage("greeting", "Greeting", "Open naturally, identify the caller need, and set a helpful tone.", ["Caller need is understood"], meta),
|
|
950
|
+
stage("discovery", "Discovery", "Ask one question at a time until the key details are clear.", ["Required details are collected"], meta),
|
|
951
|
+
stage("resolution", "Resolution", "Answer approved questions or route to the right workflow.", ["Resolution path is selected"], meta),
|
|
952
|
+
stage("action", "Action", "Use tools for booking, routing, messaging, or handoff before claiming success.", ["Action is confirmed by a tool or handoff"], meta),
|
|
953
|
+
stage("close", "Close", "Summarize the outcome and next step accurately.", ["Caller knows what happens next"], meta),
|
|
954
|
+
];
|
|
955
|
+
}
|
|
956
|
+
function stage(key, name, instructions, exitCriteria, metadata) {
|
|
957
|
+
return {
|
|
958
|
+
key,
|
|
959
|
+
name,
|
|
960
|
+
instructions,
|
|
961
|
+
exitCriteria,
|
|
962
|
+
metadata,
|
|
963
|
+
};
|
|
964
|
+
}
|
|
675
965
|
function voicePayload(input) {
|
|
676
966
|
return compact({
|
|
677
967
|
provider: input.provider,
|
|
@@ -683,6 +973,30 @@ function providerKeysPayload(input) {
|
|
|
683
973
|
return compact({
|
|
684
974
|
ultravox: input.ultravox,
|
|
685
975
|
ultravox_api_key: input.ultravox_api_key ?? input.ultravoxApiKey,
|
|
976
|
+
retell: input.retell,
|
|
977
|
+
retell_api_key: input.retell_api_key ?? input.retellApiKey,
|
|
978
|
+
vapi: input.vapi,
|
|
979
|
+
vapi_api_key: input.vapi_api_key ?? input.vapiApiKey,
|
|
980
|
+
bland: input.bland,
|
|
981
|
+
bland_api_key: input.bland_api_key ?? input.blandApiKey,
|
|
982
|
+
livekit: input.livekit,
|
|
983
|
+
livekit_api_key: input.livekit_api_key ?? input.livekitApiKey,
|
|
984
|
+
livekit_api_secret: input.livekit_api_secret ?? input.livekitApiSecret,
|
|
985
|
+
pipecat: input.pipecat,
|
|
986
|
+
pipecat_api_key: input.pipecat_api_key ?? input.pipecatApiKey,
|
|
987
|
+
twilio: input.twilio,
|
|
988
|
+
twilio_account_sid: input.twilio_account_sid ?? input.twilioAccountSid,
|
|
989
|
+
twilio_auth_token: input.twilio_auth_token ?? input.twilioAuthToken,
|
|
990
|
+
twilio_api_key_sid: input.twilio_api_key_sid ?? input.twilioApiKeySid,
|
|
991
|
+
twilio_api_key_secret: input.twilio_api_key_secret ?? input.twilioApiKeySecret,
|
|
992
|
+
telnyx: input.telnyx,
|
|
993
|
+
telnyx_api_key: input.telnyx_api_key ?? input.telnyxApiKey,
|
|
994
|
+
plivo: input.plivo,
|
|
995
|
+
plivo_auth_id: input.plivo_auth_id ?? input.plivoAuthId,
|
|
996
|
+
plivo_auth_token: input.plivo_auth_token ?? input.plivoAuthToken,
|
|
997
|
+
signalwire: input.signalwire,
|
|
998
|
+
signalwire_api_token: input.signalwire_api_token ?? input.signalwireApiToken,
|
|
999
|
+
signalwire_project_id: input.signalwire_project_id ?? input.signalwireProjectId,
|
|
686
1000
|
elevenlabs: input.elevenlabs,
|
|
687
1001
|
elevenlabs_api_key: input.elevenlabs_api_key ?? input.elevenlabsApiKey,
|
|
688
1002
|
cartesia: input.cartesia,
|
|
@@ -691,6 +1005,56 @@ function providerKeysPayload(input) {
|
|
|
691
1005
|
inworld_api_key: input.inworld_api_key ?? input.inworldApiKey,
|
|
692
1006
|
deepgram: input.deepgram,
|
|
693
1007
|
deepgram_api_key: input.deepgram_api_key ?? input.deepgramApiKey,
|
|
1008
|
+
anthropic: input.anthropic,
|
|
1009
|
+
anthropic_api_key: input.anthropic_api_key ?? input.anthropicApiKey,
|
|
1010
|
+
openai: input.openai,
|
|
1011
|
+
openai_api_key: input.openai_api_key ?? input.openaiApiKey,
|
|
1012
|
+
xai: input.xai,
|
|
1013
|
+
xai_api_key: input.xai_api_key ?? input.xaiApiKey,
|
|
1014
|
+
});
|
|
1015
|
+
}
|
|
1016
|
+
function byokPayload(input) {
|
|
1017
|
+
const structured = input;
|
|
1018
|
+
if (structured.agentProvider ||
|
|
1019
|
+
structured.agent_provider ||
|
|
1020
|
+
structured.runtime ||
|
|
1021
|
+
structured.telephony ||
|
|
1022
|
+
structured.tts ||
|
|
1023
|
+
structured.stt ||
|
|
1024
|
+
structured.llm ||
|
|
1025
|
+
structured.providerKeys ||
|
|
1026
|
+
structured.provider_keys ||
|
|
1027
|
+
structured.customSip ||
|
|
1028
|
+
structured.custom_sip ||
|
|
1029
|
+
structured.sip) {
|
|
1030
|
+
return compact({
|
|
1031
|
+
provider_keys: providerKeysPayload(structured.provider_keys ?? structured.providerKeys ?? {}),
|
|
1032
|
+
agent_provider: providerConfigPayload(structured.agent_provider ?? structured.agentProvider ?? structured.runtime),
|
|
1033
|
+
telephony: structured.telephony ? telephonyPayload(structured.telephony) : undefined,
|
|
1034
|
+
tts: providerConfigPayload(structured.tts),
|
|
1035
|
+
stt: providerConfigPayload(structured.stt),
|
|
1036
|
+
llm: providerConfigPayload(structured.llm),
|
|
1037
|
+
custom_sip: customSipPayload(structured.custom_sip ?? structured.customSip ?? structured.sip),
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
return providerKeysPayload(input);
|
|
1041
|
+
}
|
|
1042
|
+
function providerConfigPayload(input) {
|
|
1043
|
+
if (!input)
|
|
1044
|
+
return undefined;
|
|
1045
|
+
const out = { ...input };
|
|
1046
|
+
delete out.apiKey;
|
|
1047
|
+
delete out.api_key;
|
|
1048
|
+
delete out.voiceId;
|
|
1049
|
+
delete out.voice_id;
|
|
1050
|
+
return compact({
|
|
1051
|
+
...out,
|
|
1052
|
+
provider: input.provider,
|
|
1053
|
+
api_key: input.api_key ?? input.apiKey,
|
|
1054
|
+
credentials: input.credentials,
|
|
1055
|
+
settings: input.settings,
|
|
1056
|
+
model: input.model,
|
|
1057
|
+
voice_id: input.voice_id ?? input.voiceId,
|
|
694
1058
|
});
|
|
695
1059
|
}
|
|
696
1060
|
function toolsPayload(input) {
|
|
@@ -707,11 +1071,54 @@ function toolsPayload(input) {
|
|
|
707
1071
|
custom_tools: input.custom_tools ?? input.customTools,
|
|
708
1072
|
});
|
|
709
1073
|
}
|
|
1074
|
+
function recordingPayload(input) {
|
|
1075
|
+
return compact({
|
|
1076
|
+
enabled: input.enabled,
|
|
1077
|
+
record_audio: input.record_audio ?? input.recordAudio,
|
|
1078
|
+
consent_required: input.consent_required ?? input.consentRequired,
|
|
1079
|
+
announcement: input.announcement,
|
|
1080
|
+
retention_days: input.retention_days ?? input.retentionDays,
|
|
1081
|
+
storage: input.storage,
|
|
1082
|
+
redact_pii: input.redact_pii ?? input.redactPii,
|
|
1083
|
+
metadata: input.metadata,
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
1086
|
+
function transcriptionPayload(input) {
|
|
1087
|
+
return compact({
|
|
1088
|
+
enabled: input.enabled,
|
|
1089
|
+
provider: input.provider,
|
|
1090
|
+
model: input.model,
|
|
1091
|
+
language: input.language,
|
|
1092
|
+
redact_pii: input.redact_pii ?? input.redactPii,
|
|
1093
|
+
diarization: input.diarization,
|
|
1094
|
+
timestamps: input.timestamps,
|
|
1095
|
+
metadata: input.metadata,
|
|
1096
|
+
});
|
|
1097
|
+
}
|
|
1098
|
+
function artifactsPayload(input) {
|
|
1099
|
+
return compact({
|
|
1100
|
+
recordings: input.recordings,
|
|
1101
|
+
transcripts: input.transcripts,
|
|
1102
|
+
summaries: input.summaries,
|
|
1103
|
+
qa_reports: input.qa_reports ?? input.qaReports,
|
|
1104
|
+
logs: input.logs,
|
|
1105
|
+
webhooks: input.webhooks,
|
|
1106
|
+
retention_days: input.retention_days ?? input.retentionDays,
|
|
1107
|
+
metadata: input.metadata,
|
|
1108
|
+
});
|
|
1109
|
+
}
|
|
710
1110
|
function labsPayload(input) {
|
|
711
1111
|
return compact({
|
|
712
1112
|
enabled: input.enabled,
|
|
713
1113
|
voice_watcher: input.voice_watcher ?? input.voiceWatcher,
|
|
1114
|
+
api_key: input.api_key ?? input.apiKey,
|
|
714
1115
|
model: input.model,
|
|
1116
|
+
mode: input.mode,
|
|
1117
|
+
managed_infrastructure: input.managed_infrastructure ?? input.managedInfrastructure,
|
|
1118
|
+
stt: input.stt,
|
|
1119
|
+
llm: input.llm,
|
|
1120
|
+
tts: input.tts,
|
|
1121
|
+
provider_keys: input.provider_keys ?? input.providerKeys,
|
|
715
1122
|
label: input.label,
|
|
716
1123
|
});
|
|
717
1124
|
}
|
|
@@ -740,6 +1147,24 @@ function ultravoxPayload(input) {
|
|
|
740
1147
|
voiceOverrides: input.voiceOverrides ?? input.voice_overrides,
|
|
741
1148
|
retentionPolicy: input.retentionPolicy ?? input.retention_policy,
|
|
742
1149
|
callTemplate: input.callTemplate ?? input.call_template,
|
|
1150
|
+
custom_sip: customSipPayload(input.custom_sip ?? input.customSip ?? input.sip),
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
function customSipPayload(input) {
|
|
1154
|
+
if (!input)
|
|
1155
|
+
return undefined;
|
|
1156
|
+
return compact({
|
|
1157
|
+
sip_trunk_uri: input.sip_trunk_uri ?? input.sipTrunkUri,
|
|
1158
|
+
trunk_uri: input.trunk_uri ?? input.trunkUri,
|
|
1159
|
+
sip_host: input.sip_host ?? input.sipHost,
|
|
1160
|
+
from_number: input.from_number ?? input.fromNumber,
|
|
1161
|
+
username: input.username,
|
|
1162
|
+
password: input.password,
|
|
1163
|
+
transport: input.transport,
|
|
1164
|
+
headers: input.headers,
|
|
1165
|
+
codecs: input.codecs,
|
|
1166
|
+
dtmf_mode: input.dtmf_mode ?? input.dtmfMode,
|
|
1167
|
+
metadata: input.metadata,
|
|
743
1168
|
});
|
|
744
1169
|
}
|
|
745
1170
|
function compact(input) {
|
|
@@ -750,6 +1175,21 @@ function compact(input) {
|
|
|
750
1175
|
}
|
|
751
1176
|
return out;
|
|
752
1177
|
}
|
|
1178
|
+
function parseSseLog(raw) {
|
|
1179
|
+
const data = [];
|
|
1180
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
1181
|
+
if (!line || line.startsWith(":"))
|
|
1182
|
+
continue;
|
|
1183
|
+
if (line.startsWith("data:"))
|
|
1184
|
+
data.push(line.slice(5).trimStart());
|
|
1185
|
+
}
|
|
1186
|
+
if (!data.length)
|
|
1187
|
+
return null;
|
|
1188
|
+
const parsed = safeJson(data.join("\n"));
|
|
1189
|
+
if (!parsed || typeof parsed !== "object")
|
|
1190
|
+
return null;
|
|
1191
|
+
return parsed;
|
|
1192
|
+
}
|
|
753
1193
|
function safeJson(text) {
|
|
754
1194
|
try {
|
|
755
1195
|
return JSON.parse(text);
|