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/index.js
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Works in Node 18+ (native fetch/WebSocket) and the browser.
|
|
11
11
|
*
|
|
12
|
-
* npm i
|
|
12
|
+
* npm i supafone-labs
|
|
13
13
|
*
|
|
14
|
-
* import { Supafone } from "
|
|
14
|
+
* import { Supafone } from "supafone-labs";
|
|
15
15
|
* const supafone = new Supafone({ apiKey: process.env.SUPAFONE_API_KEY! });
|
|
16
16
|
* const agent = await supafone.labs.agents.createInboundWithNumber({
|
|
17
17
|
* agentKey: "northline-intake",
|
|
@@ -98,7 +98,7 @@ export class SupafoneLabs {
|
|
|
98
98
|
clearTimeout(timer);
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
-
/** @internal Authenticated JSON request to the Supafone app API (`/api/
|
|
101
|
+
/** @internal Authenticated JSON request to the Supafone app API (`/api/v1/labs/*`). */
|
|
102
102
|
async requestSupafoneApi(method, path, body) {
|
|
103
103
|
const ctrl = new AbortController();
|
|
104
104
|
const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
|
|
@@ -166,6 +166,10 @@ export class SupafoneLabs {
|
|
|
166
166
|
clearTimeout(timer);
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
|
+
/** Convenience alias for voice preview UI/buttons. */
|
|
170
|
+
previewVoice(voice = "supafone-labs-calm-en", text = "Hi, this is your Supafone agent voice preview.") {
|
|
171
|
+
return this.tts(text, voice);
|
|
172
|
+
}
|
|
169
173
|
/** Hosted STT for a finished audio clip — returns transcript + language tags. */
|
|
170
174
|
async stt(audio, opts = {}) {
|
|
171
175
|
const bytes = audio instanceof ArrayBuffer ? new Uint8Array(audio) : audio;
|
|
@@ -212,6 +216,48 @@ export class SupafoneLabs {
|
|
|
212
216
|
logs(limit = 100) {
|
|
213
217
|
return this.request("GET", `/v1/logs?limit=${limit}`);
|
|
214
218
|
}
|
|
219
|
+
/** Live audit-log stream. Works in Node 18+ and browsers via fetch streaming. */
|
|
220
|
+
async *streamLogs(opts = {}) {
|
|
221
|
+
const q = new URLSearchParams({
|
|
222
|
+
limit: String(opts.limit ?? 100),
|
|
223
|
+
poll_ms: String(opts.pollMs ?? 1000),
|
|
224
|
+
snapshot: String(opts.snapshot ?? true),
|
|
225
|
+
});
|
|
226
|
+
if (opts.afterId !== undefined)
|
|
227
|
+
q.set("after_id", String(opts.afterId));
|
|
228
|
+
const res = await fetch(`${this.baseUrl}/v1/logs/stream?${q}`, {
|
|
229
|
+
method: "GET",
|
|
230
|
+
signal: opts.signal,
|
|
231
|
+
headers: { Authorization: `Bearer ${this.apiKey}` },
|
|
232
|
+
});
|
|
233
|
+
if (!res.ok)
|
|
234
|
+
throw new SupafoneLabsError(`streamLogs: ${await res.text()}`, res.status);
|
|
235
|
+
if (!res.body)
|
|
236
|
+
throw new SupafoneLabsError("streamLogs: response body is not readable");
|
|
237
|
+
const reader = res.body.getReader();
|
|
238
|
+
const decoder = new TextDecoder();
|
|
239
|
+
let buffer = "";
|
|
240
|
+
try {
|
|
241
|
+
for (;;) {
|
|
242
|
+
const { done, value } = await reader.read();
|
|
243
|
+
if (done)
|
|
244
|
+
break;
|
|
245
|
+
buffer += decoder.decode(value, { stream: true });
|
|
246
|
+
let split = buffer.indexOf("\n\n");
|
|
247
|
+
while (split >= 0) {
|
|
248
|
+
const raw = buffer.slice(0, split);
|
|
249
|
+
buffer = buffer.slice(split + 2);
|
|
250
|
+
const parsed = parseSseLog(raw);
|
|
251
|
+
if (parsed)
|
|
252
|
+
yield parsed;
|
|
253
|
+
split = buffer.indexOf("\n\n");
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
finally {
|
|
258
|
+
reader.releaseLock();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
215
261
|
/** The structured whisper feed (what the console shows). */
|
|
216
262
|
nudges(limit = 50) {
|
|
217
263
|
return this.request("GET", `/v1/nudges?limit=${limit}`);
|
|
@@ -238,6 +284,10 @@ export class SupafoneLabs {
|
|
|
238
284
|
const d = await this.request("GET", "/v1/voices");
|
|
239
285
|
return d.voices.map((v) => (typeof v === "string" ? v : (v.voice ?? v.id ?? ""))).filter(Boolean);
|
|
240
286
|
}
|
|
287
|
+
/** Full hosted voice catalog with provider live/configured flags. */
|
|
288
|
+
voiceCatalog() {
|
|
289
|
+
return this.request("GET", "/v1/voices");
|
|
290
|
+
}
|
|
241
291
|
}
|
|
242
292
|
class LiveTranscription {
|
|
243
293
|
ws;
|
|
@@ -293,6 +343,9 @@ class LabsNamespace {
|
|
|
293
343
|
voices;
|
|
294
344
|
phoneNumbers;
|
|
295
345
|
telephony;
|
|
346
|
+
calls;
|
|
347
|
+
recordings;
|
|
348
|
+
transcripts;
|
|
296
349
|
constructor(sm) {
|
|
297
350
|
this.sm = sm;
|
|
298
351
|
this.agents = new LabsAgentsNamespace(sm);
|
|
@@ -301,10 +354,13 @@ class LabsNamespace {
|
|
|
301
354
|
this.voices = new LabsVoicesNamespace(sm);
|
|
302
355
|
this.phoneNumbers = new LabsPhoneNumbersNamespace(sm);
|
|
303
356
|
this.telephony = new LabsTelephonyNamespace(sm);
|
|
357
|
+
this.calls = new LabsCallsNamespace(sm);
|
|
358
|
+
this.recordings = new LabsRecordingsNamespace(sm);
|
|
359
|
+
this.transcripts = new LabsTranscriptsNamespace(sm);
|
|
304
360
|
}
|
|
305
361
|
/** Discover the Supafone convenience layer over Ultravox. */
|
|
306
362
|
capabilities() {
|
|
307
|
-
return this.sm.requestSupafoneApi("GET", "/api/
|
|
363
|
+
return this.sm.requestSupafoneApi("GET", "/api/v1/labs/capabilities");
|
|
308
364
|
}
|
|
309
365
|
}
|
|
310
366
|
class LabsAgentsNamespace {
|
|
@@ -314,7 +370,7 @@ class LabsAgentsNamespace {
|
|
|
314
370
|
}
|
|
315
371
|
/** Spawn a durable hosted Supafone agent backed by Ultravox and Supafone-managed providers. */
|
|
316
372
|
create(input) {
|
|
317
|
-
return this.sm.requestSupafoneApi("POST", "/api/
|
|
373
|
+
return this.sm.requestSupafoneApi("POST", "/api/v1/labs/agents", labsAgentPayload(input));
|
|
318
374
|
}
|
|
319
375
|
/** Create an inbound receptionist/intake agent. No Twilio account is required. */
|
|
320
376
|
createInbound(input) {
|
|
@@ -384,7 +440,7 @@ class LabsAgentsNamespace {
|
|
|
384
440
|
if (opts.style)
|
|
385
441
|
q.set("style", opts.style);
|
|
386
442
|
const suffix = q.toString() ? `?${q}` : "";
|
|
387
|
-
return this.sm.requestSupafoneApi("GET", `/api/
|
|
443
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/agents${suffix}`);
|
|
388
444
|
}
|
|
389
445
|
/** Fetch one durable agent by key. */
|
|
390
446
|
get(agentKey, opts = {}) {
|
|
@@ -394,7 +450,19 @@ class LabsAgentsNamespace {
|
|
|
394
450
|
if (opts.agentType)
|
|
395
451
|
q.set("agent_type", opts.agentType);
|
|
396
452
|
const suffix = q.toString() ? `?${q}` : "";
|
|
397
|
-
return this.sm.requestSupafoneApi("GET", `/api/
|
|
453
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}${suffix}`);
|
|
454
|
+
}
|
|
455
|
+
/** Delete an agent. Optionally ask the backend to release assigned numbers. */
|
|
456
|
+
delete(agentKey, opts = {}) {
|
|
457
|
+
const q = new URLSearchParams();
|
|
458
|
+
const agencyId = opts.agency_id ?? opts.agencyId;
|
|
459
|
+
const releaseNumbers = opts.release_numbers ?? opts.releaseNumbers;
|
|
460
|
+
if (agencyId)
|
|
461
|
+
q.set("agency_id", agencyId);
|
|
462
|
+
if (releaseNumbers !== undefined)
|
|
463
|
+
q.set("release_numbers", String(releaseNumbers));
|
|
464
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
465
|
+
return this.sm.requestSupafoneApi("DELETE", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}${suffix}`);
|
|
398
466
|
}
|
|
399
467
|
}
|
|
400
468
|
class LabsPresetsNamespace {
|
|
@@ -404,7 +472,7 @@ class LabsPresetsNamespace {
|
|
|
404
472
|
}
|
|
405
473
|
/** Out-of-the-box multistage agent presets. */
|
|
406
474
|
list() {
|
|
407
|
-
return this.sm.requestSupafoneApi("GET", "/api/
|
|
475
|
+
return this.sm.requestSupafoneApi("GET", "/api/v1/labs/presets");
|
|
408
476
|
}
|
|
409
477
|
}
|
|
410
478
|
class LabsToolsNamespace {
|
|
@@ -414,7 +482,7 @@ class LabsToolsNamespace {
|
|
|
414
482
|
}
|
|
415
483
|
/** Built-in tools Supafone agents can use. */
|
|
416
484
|
list() {
|
|
417
|
-
return this.sm.requestSupafoneApi("GET", "/api/
|
|
485
|
+
return this.sm.requestSupafoneApi("GET", "/api/v1/labs/tools");
|
|
418
486
|
}
|
|
419
487
|
}
|
|
420
488
|
class LabsVoicesNamespace {
|
|
@@ -428,7 +496,76 @@ class LabsVoicesNamespace {
|
|
|
428
496
|
if (opts.provider)
|
|
429
497
|
q.set("provider", opts.provider);
|
|
430
498
|
const suffix = q.toString() ? `?${q}` : "";
|
|
431
|
-
return this.sm.requestSupafoneApi("GET", `/api/
|
|
499
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/voices${suffix}`);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
class LabsCallsNamespace {
|
|
503
|
+
sm;
|
|
504
|
+
constructor(sm) {
|
|
505
|
+
this.sm = sm;
|
|
506
|
+
}
|
|
507
|
+
list(opts = {}) {
|
|
508
|
+
const q = hostedListQuery(opts);
|
|
509
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
510
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/calls${suffix}`);
|
|
511
|
+
}
|
|
512
|
+
get(callId, opts = {}) {
|
|
513
|
+
const q = new URLSearchParams();
|
|
514
|
+
if (opts.agencyId)
|
|
515
|
+
q.set("agency_id", opts.agencyId);
|
|
516
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
517
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/calls/${encodeURIComponent(callId)}${suffix}`);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
class LabsRecordingsNamespace {
|
|
521
|
+
sm;
|
|
522
|
+
constructor(sm) {
|
|
523
|
+
this.sm = sm;
|
|
524
|
+
}
|
|
525
|
+
list(opts = {}) {
|
|
526
|
+
const q = hostedListQuery(opts);
|
|
527
|
+
const callId = opts.call_id ?? opts.callId;
|
|
528
|
+
if (callId)
|
|
529
|
+
q.set("call_id", callId);
|
|
530
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
531
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/recordings${suffix}`);
|
|
532
|
+
}
|
|
533
|
+
get(recordingId, opts = {}) {
|
|
534
|
+
const q = new URLSearchParams();
|
|
535
|
+
if (opts.agencyId)
|
|
536
|
+
q.set("agency_id", opts.agencyId);
|
|
537
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
538
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/recordings/${encodeURIComponent(recordingId)}${suffix}`);
|
|
539
|
+
}
|
|
540
|
+
delete(recordingId, opts = {}) {
|
|
541
|
+
const q = new URLSearchParams();
|
|
542
|
+
if (opts.agencyId)
|
|
543
|
+
q.set("agency_id", opts.agencyId);
|
|
544
|
+
if (opts.reason)
|
|
545
|
+
q.set("reason", opts.reason);
|
|
546
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
547
|
+
return this.sm.requestSupafoneApi("DELETE", `/api/v1/labs/recordings/${encodeURIComponent(recordingId)}${suffix}`);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
class LabsTranscriptsNamespace {
|
|
551
|
+
sm;
|
|
552
|
+
constructor(sm) {
|
|
553
|
+
this.sm = sm;
|
|
554
|
+
}
|
|
555
|
+
list(opts = {}) {
|
|
556
|
+
const q = hostedListQuery(opts);
|
|
557
|
+
const callId = opts.call_id ?? opts.callId;
|
|
558
|
+
if (callId)
|
|
559
|
+
q.set("call_id", callId);
|
|
560
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
561
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/transcripts${suffix}`);
|
|
562
|
+
}
|
|
563
|
+
get(transcriptId, opts = {}) {
|
|
564
|
+
const q = new URLSearchParams();
|
|
565
|
+
if (opts.agencyId)
|
|
566
|
+
q.set("agency_id", opts.agencyId);
|
|
567
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
568
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/transcripts/${encodeURIComponent(transcriptId)}${suffix}`);
|
|
432
569
|
}
|
|
433
570
|
}
|
|
434
571
|
class LabsPhoneNumbersNamespace {
|
|
@@ -444,22 +581,43 @@ class LabsPhoneNumbersNamespace {
|
|
|
444
581
|
if (opts.activeOnly !== undefined)
|
|
445
582
|
q.set("active_only", String(opts.activeOnly));
|
|
446
583
|
const suffix = q.toString() ? `?${q}` : "";
|
|
447
|
-
return this.sm.requestSupafoneApi("GET", `/api/
|
|
584
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/phone-numbers${suffix}`);
|
|
448
585
|
}
|
|
449
586
|
/** Search Supafone-managed inventory. This uses Supafone's master telephony account. */
|
|
450
587
|
search(opts = {}) {
|
|
451
|
-
return this.sm.requestSupafoneApi("POST", "/api/
|
|
588
|
+
return this.sm.requestSupafoneApi("POST", "/api/v1/labs/phone-numbers/search", phoneNumberSearchPayload(opts));
|
|
452
589
|
}
|
|
453
590
|
/** Buy a Supafone-managed number. Developers do not need a Twilio account. */
|
|
454
591
|
buy(input) {
|
|
455
|
-
return this.sm.requestSupafoneApi("POST", "/api/
|
|
592
|
+
return this.sm.requestSupafoneApi("POST", "/api/v1/labs/phone-numbers", phoneNumberProvisionPayload({
|
|
456
593
|
...input,
|
|
457
594
|
telephony: input.telephony ?? { mode: "supafone_managed", provider: "supafone" },
|
|
458
595
|
}));
|
|
459
596
|
}
|
|
460
597
|
/** Attach an existing Supafone number to an inbound or outbound agent. */
|
|
461
598
|
assign(numberId, input = {}) {
|
|
462
|
-
return this.sm.requestSupafoneApi("POST", `/api/
|
|
599
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/labs/phone-numbers/${encodeURIComponent(numberId)}/assign`, phoneNumberAssignPayload(input));
|
|
600
|
+
}
|
|
601
|
+
/** Unassign a number from an agent but keep it reserved on the account. */
|
|
602
|
+
unassign(numberId, input = {}) {
|
|
603
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/labs/phone-numbers/${encodeURIComponent(numberId)}/unassign`, phoneNumberReleasePayload(input));
|
|
604
|
+
}
|
|
605
|
+
/** Give a number back to the shared pool or release the reservation. */
|
|
606
|
+
release(numberId, input = {}) {
|
|
607
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/labs/phone-numbers/${encodeURIComponent(numberId)}/release`, phoneNumberReleasePayload({ ...input, returnToPool: input.returnToPool ?? input.return_to_pool ?? true }));
|
|
608
|
+
}
|
|
609
|
+
/** Alias for release(numberId, { returnToPool: true }). */
|
|
610
|
+
returnToPool(numberId, input = {}) {
|
|
611
|
+
return this.release(numberId, { ...input, returnToPool: true });
|
|
612
|
+
}
|
|
613
|
+
/** Delete/release a number reservation. Backend policy decides whether this is allowed. */
|
|
614
|
+
delete(numberId, input = {}) {
|
|
615
|
+
const q = new URLSearchParams();
|
|
616
|
+
const agencyId = input.agency_id ?? input.agencyId;
|
|
617
|
+
if (agencyId)
|
|
618
|
+
q.set("agency_id", agencyId);
|
|
619
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
620
|
+
return this.sm.requestSupafoneApi("DELETE", `/api/v1/labs/phone-numbers/${encodeURIComponent(numberId)}${suffix}`, phoneNumberReleasePayload(input));
|
|
463
621
|
}
|
|
464
622
|
/**
|
|
465
623
|
* Search if needed, buy the first matching Supafone-managed number, and assign
|
|
@@ -496,11 +654,11 @@ class LabsTelephonyNamespace {
|
|
|
496
654
|
if (opts.agencyId)
|
|
497
655
|
q.set("agency_id", opts.agencyId);
|
|
498
656
|
const suffix = q.toString() ? `?${q}` : "";
|
|
499
|
-
return this.sm.requestSupafoneApi("GET", `/api/
|
|
657
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/telephony${suffix}`);
|
|
500
658
|
}
|
|
501
659
|
/** Configure advanced BYOK telephony, or reset back to Supafone-managed. */
|
|
502
660
|
configure(input) {
|
|
503
|
-
return this.sm.requestSupafoneApi("PUT", "/api/
|
|
661
|
+
return this.sm.requestSupafoneApi("PUT", "/api/v1/labs/telephony", telephonyPayload(input));
|
|
504
662
|
}
|
|
505
663
|
/** Reset to the seamless default where Supafone buys and routes numbers. */
|
|
506
664
|
useSupafoneManaged(agencyId) {
|
|
@@ -561,6 +719,10 @@ class OptimizerNamespace {
|
|
|
561
719
|
standing(agent = "builder") {
|
|
562
720
|
return this.sm.request("GET", `/v1/optimizer/standing?agent=${encodeURIComponent(agent)}`);
|
|
563
721
|
}
|
|
722
|
+
/** SSR grade distribution: five nominal levels folded into a real score distribution. */
|
|
723
|
+
distribution(agent = "builder", limit = 500) {
|
|
724
|
+
return this.sm.request("GET", `/v1/objective/distribution?agent=${encodeURIComponent(agent)}&limit=${limit}`);
|
|
725
|
+
}
|
|
564
726
|
/** List the post-call reports behind the optimizer. */
|
|
565
727
|
reports(agent = "builder", limit = 40) {
|
|
566
728
|
return this.sm.request("GET", `/v1/optimizer/reports?agent=${encodeURIComponent(agent)}&limit=${limit}`);
|
|
@@ -578,17 +740,26 @@ function labsAgentPayload(input) {
|
|
|
578
740
|
industry: input.industry,
|
|
579
741
|
website_url: input.website_url ?? input.websiteUrl,
|
|
580
742
|
phone_number: input.phone_number ?? input.phoneNumber,
|
|
743
|
+
number_strategy: input.number_strategy ?? input.numberStrategy,
|
|
744
|
+
number_pool: input.number_pool ?? input.numberPool,
|
|
745
|
+
premium: input.premium,
|
|
581
746
|
direction: input.direction,
|
|
582
747
|
preset_key: input.preset_key ?? input.presetKey,
|
|
583
748
|
runtime_mode: input.runtime_mode ?? input.runtimeMode,
|
|
749
|
+
call_stages: callStagesPayload(input),
|
|
584
750
|
goal: input.goal,
|
|
585
751
|
greeting: input.greeting,
|
|
586
752
|
system_prompt: input.system_prompt ?? input.systemPrompt,
|
|
587
753
|
language: input.language,
|
|
588
754
|
voice: input.voice ? voicePayload(input.voice) : undefined,
|
|
589
755
|
provider_keys: input.provider_keys ?? (input.providerKeys ? providerKeysPayload(input.providerKeys) : undefined),
|
|
590
|
-
byok: input.byok ?
|
|
756
|
+
byok: input.byok ? byokPayload(input.byok) : undefined,
|
|
591
757
|
telephony: input.telephony ? telephonyPayload(input.telephony) : undefined,
|
|
758
|
+
custom_sip: customSipPayload(input.custom_sip ?? input.customSip ?? input.sip),
|
|
759
|
+
recording: input.recording ? recordingPayload(input.recording) : undefined,
|
|
760
|
+
transcription: input.transcription ? transcriptionPayload(input.transcription) : undefined,
|
|
761
|
+
artifacts: input.artifacts ? artifactsPayload(input.artifacts) : undefined,
|
|
762
|
+
compliance: input.compliance,
|
|
592
763
|
tools: input.tools ? toolsPayload(input.tools) : undefined,
|
|
593
764
|
labs: input.labs ? labsPayload(input.labs) : undefined,
|
|
594
765
|
ultravox: input.ultravox ? ultravoxPayload(input.ultravox) : undefined,
|
|
@@ -597,13 +768,30 @@ function labsAgentPayload(input) {
|
|
|
597
768
|
metadata: input.metadata,
|
|
598
769
|
});
|
|
599
770
|
}
|
|
771
|
+
function hostedListQuery(opts) {
|
|
772
|
+
const q = new URLSearchParams();
|
|
773
|
+
if (opts.agencyId)
|
|
774
|
+
q.set("agency_id", opts.agencyId);
|
|
775
|
+
const agentKey = opts.agent_key ?? opts.agentKey;
|
|
776
|
+
if (agentKey)
|
|
777
|
+
q.set("agent_key", agentKey);
|
|
778
|
+
if (opts.limit !== undefined)
|
|
779
|
+
q.set("limit", String(opts.limit));
|
|
780
|
+
return q;
|
|
781
|
+
}
|
|
600
782
|
function telephonyPayload(input) {
|
|
601
783
|
return compact({
|
|
602
784
|
agency_id: input.agency_id ?? input.agencyId,
|
|
603
785
|
mode: input.mode,
|
|
604
786
|
provider: input.provider,
|
|
787
|
+
number_strategy: input.number_strategy ?? input.numberStrategy,
|
|
788
|
+
number_pool: input.number_pool ?? input.numberPool,
|
|
789
|
+
number_id: input.number_id ?? input.numberId,
|
|
790
|
+
premium: input.premium,
|
|
605
791
|
label: input.label,
|
|
606
792
|
credentials: input.credentials ? telephonyCredentialsPayload(input.credentials) : undefined,
|
|
793
|
+
provider_settings: input.provider_settings ?? input.providerSettings,
|
|
794
|
+
custom_sip: customSipPayload(input.custom_sip ?? input.customSip),
|
|
607
795
|
metadata: input.metadata,
|
|
608
796
|
});
|
|
609
797
|
}
|
|
@@ -621,11 +809,22 @@ function telephonyCredentialsPayload(input) {
|
|
|
621
809
|
username: input.username,
|
|
622
810
|
password: input.password,
|
|
623
811
|
webhook_secret: input.webhook_secret ?? input.webhookSecret,
|
|
812
|
+
telnyx_connection_id: input.telnyx_connection_id ?? input.telnyxConnectionId,
|
|
813
|
+
signalwire_space_url: input.signalwire_space_url ?? input.signalwireSpaceUrl,
|
|
814
|
+
project_id: input.project_id ?? input.projectId,
|
|
815
|
+
application_id: input.application_id ?? input.applicationId,
|
|
816
|
+
trunk_id: input.trunk_id ?? input.trunkId,
|
|
817
|
+
endpoint_id: input.endpoint_id ?? input.endpointId,
|
|
818
|
+
token: input.token,
|
|
819
|
+
secret: input.secret,
|
|
820
|
+
custom_sip: customSipPayload(input.custom_sip ?? input.customSip),
|
|
624
821
|
});
|
|
625
822
|
}
|
|
626
823
|
function phoneNumberSearchPayload(input) {
|
|
627
824
|
return compact({
|
|
628
825
|
agency_id: input.agency_id ?? input.agencyId,
|
|
826
|
+
number_pool: input.number_pool ?? input.numberPool,
|
|
827
|
+
number_strategy: input.number_strategy ?? input.numberStrategy,
|
|
629
828
|
country_code: input.country_code ?? input.countryCode,
|
|
630
829
|
area_code: input.area_code ?? input.areaCode,
|
|
631
830
|
postal_code: input.postal_code ?? input.postalCode,
|
|
@@ -646,6 +845,9 @@ function phoneNumberProvisionPayload(input) {
|
|
|
646
845
|
agent_id: input.agent_id ?? input.agentId,
|
|
647
846
|
agent_name: input.agent_name ?? input.agentName,
|
|
648
847
|
preset_key: input.preset_key ?? input.presetKey,
|
|
848
|
+
number_strategy: input.number_strategy ?? input.numberStrategy,
|
|
849
|
+
number_pool: input.number_pool ?? input.numberPool,
|
|
850
|
+
premium: input.premium,
|
|
649
851
|
style: input.agent_style ?? input.agentStyle ?? input.style,
|
|
650
852
|
direction: input.direction,
|
|
651
853
|
telephony: input.telephony ? telephonyPayload(input.telephony) : undefined,
|
|
@@ -660,12 +862,99 @@ function phoneNumberAssignPayload(input) {
|
|
|
660
862
|
agent_name: input.agent_name ?? input.agentName,
|
|
661
863
|
friendly_name: input.friendly_name ?? input.friendlyName,
|
|
662
864
|
preset_key: input.preset_key ?? input.presetKey,
|
|
865
|
+
number_strategy: input.number_strategy ?? input.numberStrategy,
|
|
866
|
+
number_pool: input.number_pool ?? input.numberPool,
|
|
867
|
+
premium: input.premium,
|
|
663
868
|
style: input.agent_style ?? input.agentStyle ?? input.style,
|
|
664
869
|
direction: input.direction,
|
|
665
870
|
telephony: input.telephony ? telephonyPayload(input.telephony) : undefined,
|
|
666
871
|
metadata: input.metadata,
|
|
667
872
|
});
|
|
668
873
|
}
|
|
874
|
+
function phoneNumberReleasePayload(input) {
|
|
875
|
+
return compact({
|
|
876
|
+
agency_id: input.agency_id ?? input.agencyId,
|
|
877
|
+
reason: input.reason,
|
|
878
|
+
return_to_pool: input.return_to_pool ?? input.returnToPool,
|
|
879
|
+
metadata: input.metadata,
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
function callStagesPayload(input) {
|
|
883
|
+
const explicit = input.call_stages ?? input.callStages ?? input.stages;
|
|
884
|
+
const auto = input.auto_call_stages ?? input.autoCallStages;
|
|
885
|
+
if (Array.isArray(explicit))
|
|
886
|
+
return explicit.map(callStagePayload);
|
|
887
|
+
if (explicit === false || auto === false)
|
|
888
|
+
return undefined;
|
|
889
|
+
return generateCallStages(input).map(callStagePayload);
|
|
890
|
+
}
|
|
891
|
+
function callStagePayload(input) {
|
|
892
|
+
return compact({
|
|
893
|
+
key: input.key ?? input.id,
|
|
894
|
+
name: input.name,
|
|
895
|
+
goal: input.goal,
|
|
896
|
+
instructions: input.instructions,
|
|
897
|
+
exit_criteria: input.exit_criteria ?? input.exitCriteria,
|
|
898
|
+
tools: input.tools,
|
|
899
|
+
metadata: input.metadata,
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
export function generateCallStages(input) {
|
|
903
|
+
const direction = String(input.direction ?? input.agent_style ?? input.agentStyle ?? input.style ?? "inbound").toLowerCase();
|
|
904
|
+
const haystack = [
|
|
905
|
+
input.name,
|
|
906
|
+
input.assistant_name ?? input.assistantName,
|
|
907
|
+
input.business_name ?? input.businessName,
|
|
908
|
+
input.industry,
|
|
909
|
+
input.goal,
|
|
910
|
+
input.system_prompt ?? input.systemPrompt,
|
|
911
|
+
input.preset_key ?? input.presetKey,
|
|
912
|
+
].filter(Boolean).join(" ").toLowerCase();
|
|
913
|
+
const meta = { auto_generated: true, source: "supafone-labs-sdk" };
|
|
914
|
+
if (direction === "outbound" || haystack.includes("sales") || haystack.includes("lead")) {
|
|
915
|
+
return [
|
|
916
|
+
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),
|
|
917
|
+
stage("qualification", "Qualification", "Confirm fit, urgency, decision process, and the best next step.", ["Need and timeline are clear"], meta),
|
|
918
|
+
stage("offer", "Offer", "Explain the next step in plain language without unsupported claims.", ["Caller understands the next step"], meta),
|
|
919
|
+
stage("booking", "Booking", "Book or route the caller only after confirming required details.", ["Next step is confirmed by a tool or human handoff"], meta),
|
|
920
|
+
stage("close", "Close", "Summarize what will happen next and end politely.", ["Caller knows the follow-up path"], meta),
|
|
921
|
+
];
|
|
922
|
+
}
|
|
923
|
+
if (haystack.includes("legal") || haystack.includes("law") || haystack.includes("injury") || haystack.includes("intake")) {
|
|
924
|
+
return [
|
|
925
|
+
stage("greeting", "Greeting", "Open warmly and acknowledge the caller before logistics.", ["Caller need is understood"], meta),
|
|
926
|
+
stage("incident", "Incident details", "Collect what happened, when it happened, injuries, insurance, and contact details.", ["Core facts are collected"], meta),
|
|
927
|
+
stage("screening", "Screening", "Identify urgency, jurisdiction, conflicts, and whether human escalation is required.", ["Escalation decision is clear"], meta),
|
|
928
|
+
stage("booking", "Consult booking", "Book the right next step without quoting fees or inventing availability.", ["Booking or handoff is tool-confirmed"], meta),
|
|
929
|
+
stage("close", "Close", "Summarize the next step and set expectations accurately.", ["Caller knows exactly what happens next"], meta),
|
|
930
|
+
];
|
|
931
|
+
}
|
|
932
|
+
if (haystack.includes("medical") || haystack.includes("clinic") || haystack.includes("patient") || haystack.includes("health")) {
|
|
933
|
+
return [
|
|
934
|
+
stage("greeting", "Greeting", "Identify the caller need and keep the tone calm and concise.", ["Caller need is understood"], meta),
|
|
935
|
+
stage("patient_context", "Patient context", "Collect non-sensitive scheduling context and avoid medical advice.", ["Required scheduling context is collected"], meta),
|
|
936
|
+
stage("routing", "Routing", "Route urgent, billing, clinical, and scheduling requests correctly.", ["Correct route is selected"], meta),
|
|
937
|
+
stage("appointment", "Appointment", "Book or request the appointment only after confirming details.", ["Appointment path is confirmed"], meta),
|
|
938
|
+
stage("close", "Close", "Recap next steps and any confirmed timing.", ["Caller knows the next step"], meta),
|
|
939
|
+
];
|
|
940
|
+
}
|
|
941
|
+
return [
|
|
942
|
+
stage("greeting", "Greeting", "Open naturally, identify the caller need, and set a helpful tone.", ["Caller need is understood"], meta),
|
|
943
|
+
stage("discovery", "Discovery", "Ask one question at a time until the key details are clear.", ["Required details are collected"], meta),
|
|
944
|
+
stage("resolution", "Resolution", "Answer approved questions or route to the right workflow.", ["Resolution path is selected"], meta),
|
|
945
|
+
stage("action", "Action", "Use tools for booking, routing, messaging, or handoff before claiming success.", ["Action is confirmed by a tool or handoff"], meta),
|
|
946
|
+
stage("close", "Close", "Summarize the outcome and next step accurately.", ["Caller knows what happens next"], meta),
|
|
947
|
+
];
|
|
948
|
+
}
|
|
949
|
+
function stage(key, name, instructions, exitCriteria, metadata) {
|
|
950
|
+
return {
|
|
951
|
+
key,
|
|
952
|
+
name,
|
|
953
|
+
instructions,
|
|
954
|
+
exitCriteria,
|
|
955
|
+
metadata,
|
|
956
|
+
};
|
|
957
|
+
}
|
|
669
958
|
function voicePayload(input) {
|
|
670
959
|
return compact({
|
|
671
960
|
provider: input.provider,
|
|
@@ -677,6 +966,30 @@ function providerKeysPayload(input) {
|
|
|
677
966
|
return compact({
|
|
678
967
|
ultravox: input.ultravox,
|
|
679
968
|
ultravox_api_key: input.ultravox_api_key ?? input.ultravoxApiKey,
|
|
969
|
+
retell: input.retell,
|
|
970
|
+
retell_api_key: input.retell_api_key ?? input.retellApiKey,
|
|
971
|
+
vapi: input.vapi,
|
|
972
|
+
vapi_api_key: input.vapi_api_key ?? input.vapiApiKey,
|
|
973
|
+
bland: input.bland,
|
|
974
|
+
bland_api_key: input.bland_api_key ?? input.blandApiKey,
|
|
975
|
+
livekit: input.livekit,
|
|
976
|
+
livekit_api_key: input.livekit_api_key ?? input.livekitApiKey,
|
|
977
|
+
livekit_api_secret: input.livekit_api_secret ?? input.livekitApiSecret,
|
|
978
|
+
pipecat: input.pipecat,
|
|
979
|
+
pipecat_api_key: input.pipecat_api_key ?? input.pipecatApiKey,
|
|
980
|
+
twilio: input.twilio,
|
|
981
|
+
twilio_account_sid: input.twilio_account_sid ?? input.twilioAccountSid,
|
|
982
|
+
twilio_auth_token: input.twilio_auth_token ?? input.twilioAuthToken,
|
|
983
|
+
twilio_api_key_sid: input.twilio_api_key_sid ?? input.twilioApiKeySid,
|
|
984
|
+
twilio_api_key_secret: input.twilio_api_key_secret ?? input.twilioApiKeySecret,
|
|
985
|
+
telnyx: input.telnyx,
|
|
986
|
+
telnyx_api_key: input.telnyx_api_key ?? input.telnyxApiKey,
|
|
987
|
+
plivo: input.plivo,
|
|
988
|
+
plivo_auth_id: input.plivo_auth_id ?? input.plivoAuthId,
|
|
989
|
+
plivo_auth_token: input.plivo_auth_token ?? input.plivoAuthToken,
|
|
990
|
+
signalwire: input.signalwire,
|
|
991
|
+
signalwire_api_token: input.signalwire_api_token ?? input.signalwireApiToken,
|
|
992
|
+
signalwire_project_id: input.signalwire_project_id ?? input.signalwireProjectId,
|
|
680
993
|
elevenlabs: input.elevenlabs,
|
|
681
994
|
elevenlabs_api_key: input.elevenlabs_api_key ?? input.elevenlabsApiKey,
|
|
682
995
|
cartesia: input.cartesia,
|
|
@@ -685,6 +998,56 @@ function providerKeysPayload(input) {
|
|
|
685
998
|
inworld_api_key: input.inworld_api_key ?? input.inworldApiKey,
|
|
686
999
|
deepgram: input.deepgram,
|
|
687
1000
|
deepgram_api_key: input.deepgram_api_key ?? input.deepgramApiKey,
|
|
1001
|
+
anthropic: input.anthropic,
|
|
1002
|
+
anthropic_api_key: input.anthropic_api_key ?? input.anthropicApiKey,
|
|
1003
|
+
openai: input.openai,
|
|
1004
|
+
openai_api_key: input.openai_api_key ?? input.openaiApiKey,
|
|
1005
|
+
xai: input.xai,
|
|
1006
|
+
xai_api_key: input.xai_api_key ?? input.xaiApiKey,
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
function byokPayload(input) {
|
|
1010
|
+
const structured = input;
|
|
1011
|
+
if (structured.agentProvider ||
|
|
1012
|
+
structured.agent_provider ||
|
|
1013
|
+
structured.runtime ||
|
|
1014
|
+
structured.telephony ||
|
|
1015
|
+
structured.tts ||
|
|
1016
|
+
structured.stt ||
|
|
1017
|
+
structured.llm ||
|
|
1018
|
+
structured.providerKeys ||
|
|
1019
|
+
structured.provider_keys ||
|
|
1020
|
+
structured.customSip ||
|
|
1021
|
+
structured.custom_sip ||
|
|
1022
|
+
structured.sip) {
|
|
1023
|
+
return compact({
|
|
1024
|
+
provider_keys: providerKeysPayload(structured.provider_keys ?? structured.providerKeys ?? {}),
|
|
1025
|
+
agent_provider: providerConfigPayload(structured.agent_provider ?? structured.agentProvider ?? structured.runtime),
|
|
1026
|
+
telephony: structured.telephony ? telephonyPayload(structured.telephony) : undefined,
|
|
1027
|
+
tts: providerConfigPayload(structured.tts),
|
|
1028
|
+
stt: providerConfigPayload(structured.stt),
|
|
1029
|
+
llm: providerConfigPayload(structured.llm),
|
|
1030
|
+
custom_sip: customSipPayload(structured.custom_sip ?? structured.customSip ?? structured.sip),
|
|
1031
|
+
});
|
|
1032
|
+
}
|
|
1033
|
+
return providerKeysPayload(input);
|
|
1034
|
+
}
|
|
1035
|
+
function providerConfigPayload(input) {
|
|
1036
|
+
if (!input)
|
|
1037
|
+
return undefined;
|
|
1038
|
+
const out = { ...input };
|
|
1039
|
+
delete out.apiKey;
|
|
1040
|
+
delete out.api_key;
|
|
1041
|
+
delete out.voiceId;
|
|
1042
|
+
delete out.voice_id;
|
|
1043
|
+
return compact({
|
|
1044
|
+
...out,
|
|
1045
|
+
provider: input.provider,
|
|
1046
|
+
api_key: input.api_key ?? input.apiKey,
|
|
1047
|
+
credentials: input.credentials,
|
|
1048
|
+
settings: input.settings,
|
|
1049
|
+
model: input.model,
|
|
1050
|
+
voice_id: input.voice_id ?? input.voiceId,
|
|
688
1051
|
});
|
|
689
1052
|
}
|
|
690
1053
|
function toolsPayload(input) {
|
|
@@ -701,11 +1064,54 @@ function toolsPayload(input) {
|
|
|
701
1064
|
custom_tools: input.custom_tools ?? input.customTools,
|
|
702
1065
|
});
|
|
703
1066
|
}
|
|
1067
|
+
function recordingPayload(input) {
|
|
1068
|
+
return compact({
|
|
1069
|
+
enabled: input.enabled,
|
|
1070
|
+
record_audio: input.record_audio ?? input.recordAudio,
|
|
1071
|
+
consent_required: input.consent_required ?? input.consentRequired,
|
|
1072
|
+
announcement: input.announcement,
|
|
1073
|
+
retention_days: input.retention_days ?? input.retentionDays,
|
|
1074
|
+
storage: input.storage,
|
|
1075
|
+
redact_pii: input.redact_pii ?? input.redactPii,
|
|
1076
|
+
metadata: input.metadata,
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
function transcriptionPayload(input) {
|
|
1080
|
+
return compact({
|
|
1081
|
+
enabled: input.enabled,
|
|
1082
|
+
provider: input.provider,
|
|
1083
|
+
model: input.model,
|
|
1084
|
+
language: input.language,
|
|
1085
|
+
redact_pii: input.redact_pii ?? input.redactPii,
|
|
1086
|
+
diarization: input.diarization,
|
|
1087
|
+
timestamps: input.timestamps,
|
|
1088
|
+
metadata: input.metadata,
|
|
1089
|
+
});
|
|
1090
|
+
}
|
|
1091
|
+
function artifactsPayload(input) {
|
|
1092
|
+
return compact({
|
|
1093
|
+
recordings: input.recordings,
|
|
1094
|
+
transcripts: input.transcripts,
|
|
1095
|
+
summaries: input.summaries,
|
|
1096
|
+
qa_reports: input.qa_reports ?? input.qaReports,
|
|
1097
|
+
logs: input.logs,
|
|
1098
|
+
webhooks: input.webhooks,
|
|
1099
|
+
retention_days: input.retention_days ?? input.retentionDays,
|
|
1100
|
+
metadata: input.metadata,
|
|
1101
|
+
});
|
|
1102
|
+
}
|
|
704
1103
|
function labsPayload(input) {
|
|
705
1104
|
return compact({
|
|
706
1105
|
enabled: input.enabled,
|
|
707
1106
|
voice_watcher: input.voice_watcher ?? input.voiceWatcher,
|
|
1107
|
+
api_key: input.api_key ?? input.apiKey,
|
|
708
1108
|
model: input.model,
|
|
1109
|
+
mode: input.mode,
|
|
1110
|
+
managed_infrastructure: input.managed_infrastructure ?? input.managedInfrastructure,
|
|
1111
|
+
stt: input.stt,
|
|
1112
|
+
llm: input.llm,
|
|
1113
|
+
tts: input.tts,
|
|
1114
|
+
provider_keys: input.provider_keys ?? input.providerKeys,
|
|
709
1115
|
label: input.label,
|
|
710
1116
|
});
|
|
711
1117
|
}
|
|
@@ -734,6 +1140,24 @@ function ultravoxPayload(input) {
|
|
|
734
1140
|
voiceOverrides: input.voiceOverrides ?? input.voice_overrides,
|
|
735
1141
|
retentionPolicy: input.retentionPolicy ?? input.retention_policy,
|
|
736
1142
|
callTemplate: input.callTemplate ?? input.call_template,
|
|
1143
|
+
custom_sip: customSipPayload(input.custom_sip ?? input.customSip ?? input.sip),
|
|
1144
|
+
});
|
|
1145
|
+
}
|
|
1146
|
+
function customSipPayload(input) {
|
|
1147
|
+
if (!input)
|
|
1148
|
+
return undefined;
|
|
1149
|
+
return compact({
|
|
1150
|
+
sip_trunk_uri: input.sip_trunk_uri ?? input.sipTrunkUri,
|
|
1151
|
+
trunk_uri: input.trunk_uri ?? input.trunkUri,
|
|
1152
|
+
sip_host: input.sip_host ?? input.sipHost,
|
|
1153
|
+
from_number: input.from_number ?? input.fromNumber,
|
|
1154
|
+
username: input.username,
|
|
1155
|
+
password: input.password,
|
|
1156
|
+
transport: input.transport,
|
|
1157
|
+
headers: input.headers,
|
|
1158
|
+
codecs: input.codecs,
|
|
1159
|
+
dtmf_mode: input.dtmf_mode ?? input.dtmfMode,
|
|
1160
|
+
metadata: input.metadata,
|
|
737
1161
|
});
|
|
738
1162
|
}
|
|
739
1163
|
function compact(input) {
|
|
@@ -744,6 +1168,21 @@ function compact(input) {
|
|
|
744
1168
|
}
|
|
745
1169
|
return out;
|
|
746
1170
|
}
|
|
1171
|
+
function parseSseLog(raw) {
|
|
1172
|
+
const data = [];
|
|
1173
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
1174
|
+
if (!line || line.startsWith(":"))
|
|
1175
|
+
continue;
|
|
1176
|
+
if (line.startsWith("data:"))
|
|
1177
|
+
data.push(line.slice(5).trimStart());
|
|
1178
|
+
}
|
|
1179
|
+
if (!data.length)
|
|
1180
|
+
return null;
|
|
1181
|
+
const parsed = safeJson(data.join("\n"));
|
|
1182
|
+
if (!parsed || typeof parsed !== "object")
|
|
1183
|
+
return null;
|
|
1184
|
+
return parsed;
|
|
1185
|
+
}
|
|
747
1186
|
function safeJson(text) {
|
|
748
1187
|
try {
|
|
749
1188
|
return JSON.parse(text);
|