supafone-labs 0.4.13 → 0.5.0
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 +38 -0
- package/dist/cjs/index.d.ts +156 -6
- package/dist/cjs/index.js +270 -6
- package/dist/index.d.ts +156 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +267 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +458 -5
package/src/index.ts
CHANGED
|
@@ -235,6 +235,14 @@ export interface LabsVoiceSelection {
|
|
|
235
235
|
model?: string;
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
+
/** Public Agent Factory preference for one routed language. */
|
|
239
|
+
export interface LabsLanguageVoiceProfile {
|
|
240
|
+
language: string;
|
|
241
|
+
languageHint?: string;
|
|
242
|
+
language_hint?: string;
|
|
243
|
+
voice?: LabsVoiceSelection;
|
|
244
|
+
}
|
|
245
|
+
|
|
238
246
|
export type LabsVoiceGender = "female" | "male" | "neutral";
|
|
239
247
|
|
|
240
248
|
export interface LabsVoiceLanguage {
|
|
@@ -654,6 +662,192 @@ export interface LabsUltravoxRuntime {
|
|
|
654
662
|
[extra: string]: unknown;
|
|
655
663
|
}
|
|
656
664
|
|
|
665
|
+
export interface OutboundCallModeObservability {
|
|
666
|
+
enabled?: boolean;
|
|
667
|
+
includeTrigger?: boolean;
|
|
668
|
+
includeTransitions?: boolean;
|
|
669
|
+
includeTerminationReason?: boolean;
|
|
670
|
+
metadata?: Record<string, unknown>;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/** Public, provider-neutral controls for temporary outbound IVR navigation. */
|
|
674
|
+
export interface OutboundCallModeConfig {
|
|
675
|
+
enabled?: boolean;
|
|
676
|
+
autoDetect?: boolean;
|
|
677
|
+
dtmfToolEnabled?: boolean;
|
|
678
|
+
maxDurationSeconds?: number;
|
|
679
|
+
maxKeypresses?: number;
|
|
680
|
+
repeatedMenuLimit?: number;
|
|
681
|
+
noProgressTimeoutSeconds?: number;
|
|
682
|
+
humanDetectionEnabled?: boolean;
|
|
683
|
+
resumeOnHuman?: boolean;
|
|
684
|
+
observability?: OutboundCallModeObservability;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/** Capabilities positively reported by the active telephony/runtime adapter. */
|
|
688
|
+
export interface OutboundCallModeCapabilities {
|
|
689
|
+
provider?: string;
|
|
690
|
+
dtmf?: boolean;
|
|
691
|
+
statePersistence?: boolean;
|
|
692
|
+
humanDetection?: boolean;
|
|
693
|
+
observability?: boolean;
|
|
694
|
+
metadata?: Record<string, unknown>;
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
export interface OutboundCallModeReadiness {
|
|
698
|
+
ready: boolean;
|
|
699
|
+
status: "disabled" | "ready" | "unknown" | "unsupported";
|
|
700
|
+
provider: string;
|
|
701
|
+
transportFamily: string;
|
|
702
|
+
requiredCapabilities: string[];
|
|
703
|
+
missingCapabilities: string[];
|
|
704
|
+
unsupportedCapabilities: string[];
|
|
705
|
+
reasons: string[];
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
export const OUTBOUND_CALL_MODE_BOUNDS = {
|
|
709
|
+
maxDurationSeconds: { default: 180, min: 30, max: 900 },
|
|
710
|
+
maxKeypresses: { default: 12, min: 1, max: 64 },
|
|
711
|
+
repeatedMenuLimit: { default: 3, min: 1, max: 10 },
|
|
712
|
+
noProgressTimeoutSeconds: { default: 30, min: 5, max: 120 },
|
|
713
|
+
} as const;
|
|
714
|
+
|
|
715
|
+
export const OUTBOUND_CALL_MODE_DEFAULTS = {
|
|
716
|
+
enabled: false,
|
|
717
|
+
autoDetect: true,
|
|
718
|
+
dtmfToolEnabled: true,
|
|
719
|
+
maxDurationSeconds: 180,
|
|
720
|
+
maxKeypresses: 12,
|
|
721
|
+
repeatedMenuLimit: 3,
|
|
722
|
+
noProgressTimeoutSeconds: 30,
|
|
723
|
+
humanDetectionEnabled: true,
|
|
724
|
+
resumeOnHuman: true,
|
|
725
|
+
observability: {
|
|
726
|
+
enabled: true,
|
|
727
|
+
includeTrigger: true,
|
|
728
|
+
includeTransitions: true,
|
|
729
|
+
includeTerminationReason: true,
|
|
730
|
+
},
|
|
731
|
+
} as const;
|
|
732
|
+
|
|
733
|
+
export interface OutboundCallModeProviderProfile {
|
|
734
|
+
contractSupported: boolean;
|
|
735
|
+
execution: "adapter_runtime_dependent";
|
|
736
|
+
capabilityPolicy: "fail_closed";
|
|
737
|
+
transportFamily: string;
|
|
738
|
+
providers: readonly string[];
|
|
739
|
+
requiredCapabilities: readonly string[];
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
const OUTBOUND_PROVIDER_PROFILE_BASE = {
|
|
743
|
+
contractSupported: true,
|
|
744
|
+
execution: "adapter_runtime_dependent",
|
|
745
|
+
capabilityPolicy: "fail_closed",
|
|
746
|
+
requiredCapabilities: ["dtmf", "state_persistence", "human_detection"],
|
|
747
|
+
} as const;
|
|
748
|
+
|
|
749
|
+
export const OUTBOUND_CALL_MODE_PROVIDER_MATRIX: Record<
|
|
750
|
+
string,
|
|
751
|
+
OutboundCallModeProviderProfile
|
|
752
|
+
> = {
|
|
753
|
+
supafoneManaged: {
|
|
754
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
755
|
+
transportFamily: "supafone_managed",
|
|
756
|
+
providers: ["supafone", "supafone_managed", "managed"],
|
|
757
|
+
},
|
|
758
|
+
twilio: {
|
|
759
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
760
|
+
transportFamily: "twilio",
|
|
761
|
+
providers: ["twilio", "byo_twilio"],
|
|
762
|
+
},
|
|
763
|
+
telnyx: {
|
|
764
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
765
|
+
transportFamily: "telnyx",
|
|
766
|
+
providers: ["telnyx", "byo_telnyx"],
|
|
767
|
+
},
|
|
768
|
+
plivo: {
|
|
769
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
770
|
+
transportFamily: "plivo",
|
|
771
|
+
providers: ["plivo", "byo_plivo"],
|
|
772
|
+
},
|
|
773
|
+
signalWire: {
|
|
774
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
775
|
+
transportFamily: "signalwire",
|
|
776
|
+
providers: ["signalwire", "signal_wire", "byo_signalwire"],
|
|
777
|
+
},
|
|
778
|
+
sipByoc: {
|
|
779
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
780
|
+
transportFamily: "sip_byoc",
|
|
781
|
+
providers: ["sip", "custom_sip", "byo_sip", "byoc", "sip_byoc"],
|
|
782
|
+
},
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
export function outboundCallModeProviderProfile(provider: string): OutboundCallModeProviderProfile {
|
|
786
|
+
const normalized = String(provider ?? "").trim().toLowerCase();
|
|
787
|
+
for (const profile of Object.values(OUTBOUND_CALL_MODE_PROVIDER_MATRIX)) {
|
|
788
|
+
if (profile.providers.includes(normalized)) {
|
|
789
|
+
return { ...profile, providers: [...profile.providers] };
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
return {
|
|
793
|
+
contractSupported: false,
|
|
794
|
+
execution: "adapter_runtime_dependent",
|
|
795
|
+
capabilityPolicy: "fail_closed",
|
|
796
|
+
transportFamily: "unknown",
|
|
797
|
+
providers: normalized ? [normalized] : [],
|
|
798
|
+
requiredCapabilities: ["dtmf", "state_persistence", "human_detection"],
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
export function outboundCallModeReadiness(
|
|
803
|
+
config?: OutboundCallModeConfig,
|
|
804
|
+
capabilities?: OutboundCallModeCapabilities,
|
|
805
|
+
): OutboundCallModeReadiness {
|
|
806
|
+
const provider = String(capabilities?.provider ?? "");
|
|
807
|
+
const profile = outboundCallModeProviderProfile(provider);
|
|
808
|
+
const normalized = outboundCallModePayload(config ?? {});
|
|
809
|
+
if (normalized.enabled !== true) {
|
|
810
|
+
return {
|
|
811
|
+
ready: true,
|
|
812
|
+
status: "disabled",
|
|
813
|
+
provider,
|
|
814
|
+
transportFamily: profile.transportFamily,
|
|
815
|
+
requiredCapabilities: [],
|
|
816
|
+
missingCapabilities: [],
|
|
817
|
+
unsupportedCapabilities: [],
|
|
818
|
+
reasons: [],
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
const requiredCapabilities = [...(normalized.required_capabilities as string[])];
|
|
823
|
+
const reported: Record<string, boolean | undefined> = {
|
|
824
|
+
dtmf: capabilities?.dtmf,
|
|
825
|
+
state_persistence: capabilities?.statePersistence,
|
|
826
|
+
human_detection: capabilities?.humanDetection,
|
|
827
|
+
observability: capabilities?.observability,
|
|
828
|
+
};
|
|
829
|
+
const missingCapabilities = requiredCapabilities.filter((key) => reported[key] === undefined);
|
|
830
|
+
const unsupportedCapabilities = requiredCapabilities.filter((key) => reported[key] === false);
|
|
831
|
+
const status = unsupportedCapabilities.length
|
|
832
|
+
? "unsupported"
|
|
833
|
+
: missingCapabilities.length
|
|
834
|
+
? "unknown"
|
|
835
|
+
: "ready";
|
|
836
|
+
const reasons = unsupportedCapabilities.length
|
|
837
|
+
? unsupportedCapabilities.map((key) => `Adapter reports ${key} is unsupported.`)
|
|
838
|
+
: missingCapabilities.map((key) => `Adapter did not report ${key} support.`);
|
|
839
|
+
return {
|
|
840
|
+
ready: status === "ready",
|
|
841
|
+
status,
|
|
842
|
+
provider,
|
|
843
|
+
transportFamily: profile.transportFamily,
|
|
844
|
+
requiredCapabilities,
|
|
845
|
+
missingCapabilities,
|
|
846
|
+
unsupportedCapabilities,
|
|
847
|
+
reasons,
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
|
|
657
851
|
export interface CreateLabsAgentRequest {
|
|
658
852
|
agencyId?: string;
|
|
659
853
|
agency_id?: string;
|
|
@@ -707,6 +901,15 @@ export interface CreateLabsAgentRequest {
|
|
|
707
901
|
language?: string;
|
|
708
902
|
preferredLanguage?: string;
|
|
709
903
|
preferred_language?: string;
|
|
904
|
+
/** Opt in to managed live language and matching-voice routing. Defaults to false. */
|
|
905
|
+
languageVoiceRouting?: boolean;
|
|
906
|
+
language_voice_routing?: boolean;
|
|
907
|
+
/** Optional ordered language list. The first language controls the greeting. */
|
|
908
|
+
routingLanguages?: string[];
|
|
909
|
+
routing_languages?: string[];
|
|
910
|
+
/** Optional per-language voice preferences. Supports two to four profiles. */
|
|
911
|
+
languageProfiles?: LabsLanguageVoiceProfile[];
|
|
912
|
+
language_profiles?: LabsLanguageVoiceProfile[];
|
|
710
913
|
voice?: LabsVoiceSelection;
|
|
711
914
|
/** Resolve a real current catalog voice from this plain-language preference. */
|
|
712
915
|
voicePreference?: LabsVoicePreference;
|
|
@@ -729,6 +932,9 @@ export interface CreateLabsAgentRequest {
|
|
|
729
932
|
voice_watcher?: boolean;
|
|
730
933
|
voiceWatcherModel?: string;
|
|
731
934
|
voice_watcher_model?: string;
|
|
935
|
+
/** Opt-in outbound IVR mode. Execution depends on adapter-reported readiness. */
|
|
936
|
+
outboundCallMode?: OutboundCallModeConfig;
|
|
937
|
+
outbound_call_mode?: OutboundCallModeConfig;
|
|
732
938
|
metadata?: Record<string, unknown>;
|
|
733
939
|
}
|
|
734
940
|
|
|
@@ -755,6 +961,25 @@ export interface DeleteLabsAgentResponse {
|
|
|
755
961
|
[extra: string]: unknown;
|
|
756
962
|
}
|
|
757
963
|
|
|
964
|
+
export type LabsAgentLifecycle = "draft" | "active" | "paused" | "archived" | string;
|
|
965
|
+
|
|
966
|
+
export interface LabsAgentReadiness {
|
|
967
|
+
ready: boolean;
|
|
968
|
+
status?: string;
|
|
969
|
+
checks?: Array<Record<string, unknown>>;
|
|
970
|
+
[extra: string]: unknown;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
export type UpdateLabsAgentRequest = Partial<CreateLabsAgentRequest>;
|
|
974
|
+
|
|
975
|
+
export interface LabsAgentLifecycleResponse {
|
|
976
|
+
success?: boolean;
|
|
977
|
+
agent?: LabsAgentResponse;
|
|
978
|
+
lifecycle?: LabsAgentLifecycle;
|
|
979
|
+
readiness?: LabsAgentReadiness;
|
|
980
|
+
[extra: string]: unknown;
|
|
981
|
+
}
|
|
982
|
+
|
|
758
983
|
export interface LabsCapabilitiesResponse {
|
|
759
984
|
product: string;
|
|
760
985
|
api_namespace: string;
|
|
@@ -1134,6 +1359,8 @@ export interface LabsAgentResponse {
|
|
|
1134
1359
|
display_name?: string;
|
|
1135
1360
|
runtime_mode?: string;
|
|
1136
1361
|
preset_key?: string;
|
|
1362
|
+
status?: string;
|
|
1363
|
+
lifecycle?: LabsAgentLifecycle;
|
|
1137
1364
|
profile?: Record<string, unknown>;
|
|
1138
1365
|
runtime?: Record<string, unknown>;
|
|
1139
1366
|
[extra: string]: unknown;
|
|
@@ -1149,6 +1376,16 @@ export interface CreateLabsAgentResponse {
|
|
|
1149
1376
|
[extra: string]: unknown;
|
|
1150
1377
|
};
|
|
1151
1378
|
call_plan?: LabsCallPlan;
|
|
1379
|
+
language_voice_routing?: {
|
|
1380
|
+
enabled: boolean;
|
|
1381
|
+
voice_routing_enabled: boolean;
|
|
1382
|
+
profiles: Array<Record<string, unknown>>;
|
|
1383
|
+
greeting_translation?: {
|
|
1384
|
+
language: string;
|
|
1385
|
+
language_hint: string;
|
|
1386
|
+
status: "not_needed" | "translated";
|
|
1387
|
+
};
|
|
1388
|
+
};
|
|
1152
1389
|
[extra: string]: unknown;
|
|
1153
1390
|
}
|
|
1154
1391
|
|
|
@@ -1159,6 +1396,9 @@ export interface ListLabsAgentsResponse {
|
|
|
1159
1396
|
|
|
1160
1397
|
export interface GetLabsAgentResponse {
|
|
1161
1398
|
agent: LabsAgentResponse;
|
|
1399
|
+
runtime?: Record<string, unknown>;
|
|
1400
|
+
widget?: Record<string, unknown>;
|
|
1401
|
+
readiness?: LabsAgentReadiness;
|
|
1162
1402
|
}
|
|
1163
1403
|
|
|
1164
1404
|
export interface STTResult {
|
|
@@ -2344,6 +2584,15 @@ export interface CampaignLiveView {
|
|
|
2344
2584
|
stats?: Record<string, unknown> | null;
|
|
2345
2585
|
}
|
|
2346
2586
|
|
|
2587
|
+
export interface CampaignCreateInput {
|
|
2588
|
+
name?: string;
|
|
2589
|
+
goal?: string;
|
|
2590
|
+
agentId?: string;
|
|
2591
|
+
accountId?: string;
|
|
2592
|
+
settings?: Record<string, unknown>;
|
|
2593
|
+
outboundCallMode?: OutboundCallModeConfig;
|
|
2594
|
+
}
|
|
2595
|
+
|
|
2347
2596
|
export interface CampaignUpdateInput {
|
|
2348
2597
|
name?: string;
|
|
2349
2598
|
goal?: string;
|
|
@@ -2352,6 +2601,7 @@ export interface CampaignUpdateInput {
|
|
|
2352
2601
|
emailBody?: string;
|
|
2353
2602
|
cadence?: { channel: "voice" | "email"; delay_hours: number }[];
|
|
2354
2603
|
settings?: Record<string, unknown>;
|
|
2604
|
+
outboundCallMode?: OutboundCallModeConfig;
|
|
2355
2605
|
}
|
|
2356
2606
|
|
|
2357
2607
|
export interface BrandScanResult {
|
|
@@ -2416,13 +2666,21 @@ class CampaignsNamespace {
|
|
|
2416
2666
|
return this.sm.requestAccountApi("GET", `/api/v1/campaigns${query}`);
|
|
2417
2667
|
}
|
|
2418
2668
|
|
|
2419
|
-
create(opts:
|
|
2420
|
-
|
|
2669
|
+
async create(opts: CampaignCreateInput = {}): Promise<{ campaign: CampaignSummary }> {
|
|
2670
|
+
const created = await this.sm.requestAccountApi<{ campaign: CampaignSummary }>("POST", "/api/v1/campaigns", compact({
|
|
2421
2671
|
name: opts.name ?? "New campaign",
|
|
2422
2672
|
goal: opts.goal ?? "book",
|
|
2423
2673
|
agent_id: opts.agentId,
|
|
2424
2674
|
account_id: opts.accountId,
|
|
2425
2675
|
}));
|
|
2676
|
+
const settings = campaignSettingsPayload(opts.settings, opts.outboundCallMode);
|
|
2677
|
+
if (!Object.keys(settings).length) return created;
|
|
2678
|
+
if (!created.campaign?.id) {
|
|
2679
|
+
throw new SupafoneLabsError(
|
|
2680
|
+
"Campaign was created but no campaign id was returned; outbound call mode was not saved.",
|
|
2681
|
+
);
|
|
2682
|
+
}
|
|
2683
|
+
return this.update(created.campaign.id, { settings });
|
|
2426
2684
|
}
|
|
2427
2685
|
|
|
2428
2686
|
get(campaignId: string): Promise<{ campaign: CampaignSummary }> {
|
|
@@ -2430,6 +2688,7 @@ class CampaignsNamespace {
|
|
|
2430
2688
|
}
|
|
2431
2689
|
|
|
2432
2690
|
update(campaignId: string, input: CampaignUpdateInput): Promise<{ campaign: CampaignSummary }> {
|
|
2691
|
+
const settings = campaignSettingsPayload(input.settings, input.outboundCallMode);
|
|
2433
2692
|
const payload = compact({
|
|
2434
2693
|
name: input.name,
|
|
2435
2694
|
goal: input.goal,
|
|
@@ -2437,10 +2696,12 @@ class CampaignsNamespace {
|
|
|
2437
2696
|
email_subject: input.emailSubject,
|
|
2438
2697
|
email_body: input.emailBody,
|
|
2439
2698
|
cadence: input.cadence,
|
|
2440
|
-
settings:
|
|
2699
|
+
settings: Object.keys(settings).length ? settings : undefined,
|
|
2441
2700
|
});
|
|
2442
2701
|
if (!Object.keys(payload as Record<string, unknown>).length) {
|
|
2443
|
-
throw new SupafoneLabsError(
|
|
2702
|
+
throw new SupafoneLabsError(
|
|
2703
|
+
"Nothing to update — pass name, goal, agentId, emailSubject, emailBody, cadence, settings, or outboundCallMode",
|
|
2704
|
+
);
|
|
2444
2705
|
}
|
|
2445
2706
|
return this.sm.requestAccountApi("PUT", `/api/v1/campaigns/${encodeURIComponent(campaignId)}`, payload);
|
|
2446
2707
|
}
|
|
@@ -2876,6 +3137,61 @@ class LabsAgentsNamespace {
|
|
|
2876
3137
|
);
|
|
2877
3138
|
}
|
|
2878
3139
|
|
|
3140
|
+
/** Update an existing durable agent without changing omitted fields. */
|
|
3141
|
+
update(
|
|
3142
|
+
agentKey: string,
|
|
3143
|
+
input: UpdateLabsAgentRequest,
|
|
3144
|
+
opts: GetLabsAgentOptions = {},
|
|
3145
|
+
): Promise<LabsAgentLifecycleResponse> {
|
|
3146
|
+
const q = new URLSearchParams();
|
|
3147
|
+
if (opts.agencyId) q.set("agency_id", opts.agencyId);
|
|
3148
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
3149
|
+
return this.sm.requestSupafoneApi<LabsAgentLifecycleResponse>(
|
|
3150
|
+
"PATCH",
|
|
3151
|
+
`/api/v1/labs/agents/${encodeURIComponent(agentKey)}${suffix}`,
|
|
3152
|
+
labsAgentUpdatePayload(input),
|
|
3153
|
+
);
|
|
3154
|
+
}
|
|
3155
|
+
|
|
3156
|
+
/** Ask the managed server whether this agent is ready to activate. */
|
|
3157
|
+
readiness(agentKey: string, opts: GetLabsAgentOptions = {}): Promise<LabsAgentReadiness> {
|
|
3158
|
+
const q = new URLSearchParams();
|
|
3159
|
+
if (opts.agencyId) q.set("agency_id", opts.agencyId);
|
|
3160
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
3161
|
+
return this.sm.requestSupafoneApi<LabsAgentReadiness>(
|
|
3162
|
+
"GET",
|
|
3163
|
+
`/api/v1/labs/agents/${encodeURIComponent(agentKey)}/readiness${suffix}`,
|
|
3164
|
+
);
|
|
3165
|
+
}
|
|
3166
|
+
|
|
3167
|
+
/** Activate a ready hosted agent. */
|
|
3168
|
+
activate(
|
|
3169
|
+
agentKey: string,
|
|
3170
|
+
opts: GetLabsAgentOptions = {},
|
|
3171
|
+
): Promise<LabsAgentLifecycleResponse> {
|
|
3172
|
+
const q = new URLSearchParams();
|
|
3173
|
+
if (opts.agencyId) q.set("agency_id", opts.agencyId);
|
|
3174
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
3175
|
+
return this.sm.requestSupafoneApi<LabsAgentLifecycleResponse>(
|
|
3176
|
+
"POST",
|
|
3177
|
+
`/api/v1/labs/agents/${encodeURIComponent(agentKey)}/activate${suffix}`,
|
|
3178
|
+
);
|
|
3179
|
+
}
|
|
3180
|
+
|
|
3181
|
+
/** Pause a hosted agent without deleting it. */
|
|
3182
|
+
pause(
|
|
3183
|
+
agentKey: string,
|
|
3184
|
+
opts: GetLabsAgentOptions = {},
|
|
3185
|
+
): Promise<LabsAgentLifecycleResponse> {
|
|
3186
|
+
const q = new URLSearchParams();
|
|
3187
|
+
if (opts.agencyId) q.set("agency_id", opts.agencyId);
|
|
3188
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
3189
|
+
return this.sm.requestSupafoneApi<LabsAgentLifecycleResponse>(
|
|
3190
|
+
"POST",
|
|
3191
|
+
`/api/v1/labs/agents/${encodeURIComponent(agentKey)}/pause${suffix}`,
|
|
3192
|
+
);
|
|
3193
|
+
}
|
|
3194
|
+
|
|
2879
3195
|
/** Delete an agent. Optionally ask the backend to release assigned numbers. */
|
|
2880
3196
|
delete(agentKey: string, opts: DeleteLabsAgentOptions = {}): Promise<DeleteLabsAgentResponse> {
|
|
2881
3197
|
const q = new URLSearchParams();
|
|
@@ -3466,6 +3782,128 @@ class OptimizerNamespace {
|
|
|
3466
3782
|
}
|
|
3467
3783
|
}
|
|
3468
3784
|
|
|
3785
|
+
function outboundCallModePayload(config: OutboundCallModeConfig): Record<string, unknown> {
|
|
3786
|
+
const enabled = outboundModeBoolean(config.enabled, false, "enabled");
|
|
3787
|
+
if (!enabled) return { version: 1, enabled: false };
|
|
3788
|
+
|
|
3789
|
+
const observabilityInput = config.observability ?? {};
|
|
3790
|
+
if (typeof observabilityInput !== "object" || Array.isArray(observabilityInput)) {
|
|
3791
|
+
throw new SupafoneLabsError("outboundCallMode.observability must be an object");
|
|
3792
|
+
}
|
|
3793
|
+
if (
|
|
3794
|
+
observabilityInput.metadata !== undefined
|
|
3795
|
+
&& (typeof observabilityInput.metadata !== "object"
|
|
3796
|
+
|| observabilityInput.metadata === null
|
|
3797
|
+
|| Array.isArray(observabilityInput.metadata))
|
|
3798
|
+
) {
|
|
3799
|
+
throw new SupafoneLabsError("outboundCallMode.observability.metadata must be an object");
|
|
3800
|
+
}
|
|
3801
|
+
const observability = compact({
|
|
3802
|
+
enabled: outboundModeBoolean(observabilityInput.enabled, true, "observability.enabled"),
|
|
3803
|
+
include_trigger: outboundModeBoolean(
|
|
3804
|
+
observabilityInput.includeTrigger,
|
|
3805
|
+
true,
|
|
3806
|
+
"observability.includeTrigger",
|
|
3807
|
+
),
|
|
3808
|
+
include_transitions: outboundModeBoolean(
|
|
3809
|
+
observabilityInput.includeTransitions,
|
|
3810
|
+
true,
|
|
3811
|
+
"observability.includeTransitions",
|
|
3812
|
+
),
|
|
3813
|
+
include_termination_reason: outboundModeBoolean(
|
|
3814
|
+
observabilityInput.includeTerminationReason,
|
|
3815
|
+
true,
|
|
3816
|
+
"observability.includeTerminationReason",
|
|
3817
|
+
),
|
|
3818
|
+
metadata: observabilityInput.metadata,
|
|
3819
|
+
});
|
|
3820
|
+
const payload: Record<string, unknown> = {
|
|
3821
|
+
version: 1,
|
|
3822
|
+
enabled: true,
|
|
3823
|
+
initial_mode: "mission",
|
|
3824
|
+
ivr_mode: "dynamic",
|
|
3825
|
+
transport_scope: "provider_agnostic",
|
|
3826
|
+
capability_policy: "fail_closed",
|
|
3827
|
+
auto_detect: outboundModeBoolean(config.autoDetect, true, "autoDetect"),
|
|
3828
|
+
dtmf_tool_enabled: outboundModeBoolean(config.dtmfToolEnabled, true, "dtmfToolEnabled"),
|
|
3829
|
+
max_duration_seconds: outboundModeInteger(
|
|
3830
|
+
config.maxDurationSeconds,
|
|
3831
|
+
"maxDurationSeconds",
|
|
3832
|
+
),
|
|
3833
|
+
max_keypresses: outboundModeInteger(config.maxKeypresses, "maxKeypresses"),
|
|
3834
|
+
repeated_menu_limit: outboundModeInteger(config.repeatedMenuLimit, "repeatedMenuLimit"),
|
|
3835
|
+
no_progress_timeout_seconds: outboundModeInteger(
|
|
3836
|
+
config.noProgressTimeoutSeconds,
|
|
3837
|
+
"noProgressTimeoutSeconds",
|
|
3838
|
+
),
|
|
3839
|
+
human_detection_enabled: outboundModeBoolean(
|
|
3840
|
+
config.humanDetectionEnabled,
|
|
3841
|
+
true,
|
|
3842
|
+
"humanDetectionEnabled",
|
|
3843
|
+
),
|
|
3844
|
+
resume_on_human: outboundModeBoolean(config.resumeOnHuman, true, "resumeOnHuman"),
|
|
3845
|
+
observability,
|
|
3846
|
+
};
|
|
3847
|
+
payload.required_capabilities = outboundCallModeRequiredCapabilities(payload);
|
|
3848
|
+
return payload;
|
|
3849
|
+
}
|
|
3850
|
+
|
|
3851
|
+
function outboundModeBoolean(value: boolean | undefined, fallback: boolean, key: string): boolean {
|
|
3852
|
+
if (value === undefined) return fallback;
|
|
3853
|
+
if (typeof value !== "boolean") {
|
|
3854
|
+
throw new SupafoneLabsError(`outboundCallMode.${key} must be a boolean`);
|
|
3855
|
+
}
|
|
3856
|
+
return value;
|
|
3857
|
+
}
|
|
3858
|
+
|
|
3859
|
+
function outboundModeInteger(
|
|
3860
|
+
value: number | undefined,
|
|
3861
|
+
key: keyof typeof OUTBOUND_CALL_MODE_BOUNDS,
|
|
3862
|
+
): number {
|
|
3863
|
+
const bounds = OUTBOUND_CALL_MODE_BOUNDS[key];
|
|
3864
|
+
const normalized = value ?? bounds.default;
|
|
3865
|
+
if (!Number.isInteger(normalized)) {
|
|
3866
|
+
throw new SupafoneLabsError(`outboundCallMode.${key} must be an integer`);
|
|
3867
|
+
}
|
|
3868
|
+
if (normalized < bounds.min || normalized > bounds.max) {
|
|
3869
|
+
throw new SupafoneLabsError(
|
|
3870
|
+
`outboundCallMode.${key} must be between ${bounds.min} and ${bounds.max}`,
|
|
3871
|
+
);
|
|
3872
|
+
}
|
|
3873
|
+
return normalized;
|
|
3874
|
+
}
|
|
3875
|
+
|
|
3876
|
+
function outboundCallModeRequiredCapabilities(payload: Record<string, unknown>): string[] {
|
|
3877
|
+
const required = ["state_persistence"];
|
|
3878
|
+
if (payload.dtmf_tool_enabled !== false) required.unshift("dtmf");
|
|
3879
|
+
if (payload.human_detection_enabled !== false || payload.resume_on_human !== false) {
|
|
3880
|
+
required.push("human_detection");
|
|
3881
|
+
}
|
|
3882
|
+
const observability = payload.observability as Record<string, unknown> | undefined;
|
|
3883
|
+
if (observability?.enabled === true) required.push("observability");
|
|
3884
|
+
return required;
|
|
3885
|
+
}
|
|
3886
|
+
|
|
3887
|
+
function labsAgentMetadataPayload(input: CreateLabsAgentRequest): Record<string, unknown> | undefined {
|
|
3888
|
+
const metadata = { ...(input.metadata ?? {}) };
|
|
3889
|
+
const mode = input.outboundCallMode ?? input.outbound_call_mode;
|
|
3890
|
+
if (mode !== undefined) metadata.outbound_call_mode = outboundCallModePayload(mode);
|
|
3891
|
+
return Object.keys(metadata).length ? metadata : undefined;
|
|
3892
|
+
}
|
|
3893
|
+
|
|
3894
|
+
function labsAgentUpdatePayload(input: UpdateLabsAgentRequest): Record<string, unknown> {
|
|
3895
|
+
return labsAgentPayload(input as CreateLabsAgentRequest);
|
|
3896
|
+
}
|
|
3897
|
+
|
|
3898
|
+
function campaignSettingsPayload(
|
|
3899
|
+
settings?: Record<string, unknown>,
|
|
3900
|
+
mode?: OutboundCallModeConfig,
|
|
3901
|
+
): Record<string, unknown> {
|
|
3902
|
+
const payload = { ...(settings ?? {}) };
|
|
3903
|
+
if (mode !== undefined) payload.outbound_call_mode = outboundCallModePayload(mode);
|
|
3904
|
+
return payload;
|
|
3905
|
+
}
|
|
3906
|
+
|
|
3469
3907
|
function labsAgentPayload(input: CreateLabsAgentRequest): Record<string, unknown> {
|
|
3470
3908
|
const fixedLanguage = input.preferred_language ?? input.preferredLanguage ?? input.language;
|
|
3471
3909
|
return compact({
|
|
@@ -3494,6 +3932,9 @@ function labsAgentPayload(input: CreateLabsAgentRequest): Record<string, unknown
|
|
|
3494
3932
|
greeting: input.greeting,
|
|
3495
3933
|
system_prompt: input.system_prompt ?? input.systemPrompt,
|
|
3496
3934
|
language: fixedLanguage,
|
|
3935
|
+
language_voice_routing: input.language_voice_routing ?? input.languageVoiceRouting,
|
|
3936
|
+
routing_languages: input.routing_languages ?? input.routingLanguages,
|
|
3937
|
+
language_profiles: languageProfilesPayload(input.language_profiles ?? input.languageProfiles),
|
|
3497
3938
|
voice: input.voice ? voicePayload(input.voice) : undefined,
|
|
3498
3939
|
voice_preference: voicePreferencePayload(
|
|
3499
3940
|
input.voice_preference ?? input.voicePreference,
|
|
@@ -3512,7 +3953,7 @@ function labsAgentPayload(input: CreateLabsAgentRequest): Record<string, unknown
|
|
|
3512
3953
|
ultravox: input.ultravox ? ultravoxPayload(input.ultravox) : undefined,
|
|
3513
3954
|
voice_watcher: input.voice_watcher ?? input.voiceWatcher,
|
|
3514
3955
|
voice_watcher_model: input.voice_watcher_model ?? input.voiceWatcherModel,
|
|
3515
|
-
metadata: input
|
|
3956
|
+
metadata: labsAgentMetadataPayload(input),
|
|
3516
3957
|
});
|
|
3517
3958
|
}
|
|
3518
3959
|
|
|
@@ -3761,6 +4202,18 @@ function voicePayload(input: LabsVoiceSelection): Record<string, unknown> {
|
|
|
3761
4202
|
});
|
|
3762
4203
|
}
|
|
3763
4204
|
|
|
4205
|
+
function languageProfilesPayload(
|
|
4206
|
+
input?: LabsLanguageVoiceProfile[],
|
|
4207
|
+
): Record<string, unknown>[] | undefined {
|
|
4208
|
+
if (!Array.isArray(input)) return undefined;
|
|
4209
|
+
const profiles = input.map((profile) => compact({
|
|
4210
|
+
language: profile.language,
|
|
4211
|
+
language_hint: profile.language_hint ?? profile.languageHint,
|
|
4212
|
+
voice: profile.voice ? voicePayload(profile.voice) : undefined,
|
|
4213
|
+
}));
|
|
4214
|
+
return profiles.length ? profiles : undefined;
|
|
4215
|
+
}
|
|
4216
|
+
|
|
3764
4217
|
function voicePreferencePayload(
|
|
3765
4218
|
input?: LabsVoicePreference,
|
|
3766
4219
|
defaultLanguage?: string,
|