supafone-labs 0.3.1 → 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/dist/index.js CHANGED
@@ -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,6 +354,9 @@ 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() {
@@ -396,6 +452,18 @@ class LabsAgentsNamespace {
396
452
  const suffix = q.toString() ? `?${q}` : "";
397
453
  return this.sm.requestSupafoneApi("GET", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}${suffix}`);
398
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}`);
466
+ }
399
467
  }
400
468
  class LabsPresetsNamespace {
401
469
  sm;
@@ -431,6 +499,75 @@ class LabsVoicesNamespace {
431
499
  return this.sm.requestSupafoneApi("GET", `/api/v1/labs/voices${suffix}`);
432
500
  }
433
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}`);
569
+ }
570
+ }
434
571
  class LabsPhoneNumbersNamespace {
435
572
  sm;
436
573
  constructor(sm) {
@@ -461,6 +598,27 @@ class LabsPhoneNumbersNamespace {
461
598
  assign(numberId, input = {}) {
462
599
  return this.sm.requestSupafoneApi("POST", `/api/v1/labs/phone-numbers/${encodeURIComponent(numberId)}/assign`, phoneNumberAssignPayload(input));
463
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));
621
+ }
464
622
  /**
465
623
  * Search if needed, buy the first matching Supafone-managed number, and assign
466
624
  * it to the supplied agent. This is the zero-Twilio-account happy path.
@@ -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 ? providerKeysPayload(input.byok) : undefined,
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);