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/README.md
CHANGED
|
@@ -114,6 +114,44 @@ filtering, structured SecondMind directives, and the same configuration model
|
|
|
114
114
|
across TypeScript and Python. Fixed language selection does not enable
|
|
115
115
|
mid-call language or voice switching.
|
|
116
116
|
|
|
117
|
+
Agent Factory can opt into managed live language and matching-voice routing
|
|
118
|
+
with one field. It remains absent and disabled for existing agents:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
const agent = await supafone.labs.agents.createInbound({
|
|
122
|
+
agentKey: "bilingual-intake",
|
|
123
|
+
name: "Bilingual intake",
|
|
124
|
+
languageVoiceRouting: true,
|
|
125
|
+
routingLanguages: ["en-US", "es-MX"], // optional; defaults to English + Spanish
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The package sends only these preferences. Supafone's private hosted runtime
|
|
130
|
+
selects compatible live-catalog voices and performs the in-call transition.
|
|
131
|
+
The first configured language owns the opening, and a non-English primary
|
|
132
|
+
greeting is translated during provisioning. See
|
|
133
|
+
[Live Language and Voice Routing](../gitbook/live-language-voice-routing.md).
|
|
134
|
+
|
|
135
|
+
Outbound agents can opt into temporary, bounded phone-tree navigation without
|
|
136
|
+
changing their normal campaign objective:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
await supafone.labs.agents.createOutbound({
|
|
140
|
+
name: "Benefits verification",
|
|
141
|
+
outboundCallMode: {
|
|
142
|
+
enabled: true,
|
|
143
|
+
maxDurationSeconds: 180,
|
|
144
|
+
maxKeypresses: 12,
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The SDK stores this provider-neutral contract and exposes fail-closed adapter
|
|
150
|
+
readiness checks. Supafone-managed, Twilio, Telnyx, Plivo, SignalWire, and
|
|
151
|
+
SIP/BYOC use the same public shape; private detection and DTMF execution stay in
|
|
152
|
+
the managed runtime. See
|
|
153
|
+
[Outbound IVR Call Mode](../docs/outbound-ivr-call-mode.md).
|
|
154
|
+
|
|
117
155
|
BYOK is advanced and split into three independent lanes:
|
|
118
156
|
|
|
119
157
|
| Lane | Examples |
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -210,6 +210,13 @@ export interface LabsVoiceSelection {
|
|
|
210
210
|
voice_id?: string;
|
|
211
211
|
model?: string;
|
|
212
212
|
}
|
|
213
|
+
/** Public Agent Factory preference for one routed language. */
|
|
214
|
+
export interface LabsLanguageVoiceProfile {
|
|
215
|
+
language: string;
|
|
216
|
+
languageHint?: string;
|
|
217
|
+
language_hint?: string;
|
|
218
|
+
voice?: LabsVoiceSelection;
|
|
219
|
+
}
|
|
213
220
|
export type LabsVoiceGender = "female" | "male" | "neutral";
|
|
214
221
|
export interface LabsVoiceLanguage {
|
|
215
222
|
code: string;
|
|
@@ -615,6 +622,95 @@ export interface LabsUltravoxRuntime {
|
|
|
615
622
|
sip?: LabsCustomSipConfig;
|
|
616
623
|
[extra: string]: unknown;
|
|
617
624
|
}
|
|
625
|
+
export interface OutboundCallModeObservability {
|
|
626
|
+
enabled?: boolean;
|
|
627
|
+
includeTrigger?: boolean;
|
|
628
|
+
includeTransitions?: boolean;
|
|
629
|
+
includeTerminationReason?: boolean;
|
|
630
|
+
metadata?: Record<string, unknown>;
|
|
631
|
+
}
|
|
632
|
+
/** Public, provider-neutral controls for temporary outbound IVR navigation. */
|
|
633
|
+
export interface OutboundCallModeConfig {
|
|
634
|
+
enabled?: boolean;
|
|
635
|
+
autoDetect?: boolean;
|
|
636
|
+
dtmfToolEnabled?: boolean;
|
|
637
|
+
maxDurationSeconds?: number;
|
|
638
|
+
maxKeypresses?: number;
|
|
639
|
+
repeatedMenuLimit?: number;
|
|
640
|
+
noProgressTimeoutSeconds?: number;
|
|
641
|
+
humanDetectionEnabled?: boolean;
|
|
642
|
+
resumeOnHuman?: boolean;
|
|
643
|
+
observability?: OutboundCallModeObservability;
|
|
644
|
+
}
|
|
645
|
+
/** Capabilities positively reported by the active telephony/runtime adapter. */
|
|
646
|
+
export interface OutboundCallModeCapabilities {
|
|
647
|
+
provider?: string;
|
|
648
|
+
dtmf?: boolean;
|
|
649
|
+
statePersistence?: boolean;
|
|
650
|
+
humanDetection?: boolean;
|
|
651
|
+
observability?: boolean;
|
|
652
|
+
metadata?: Record<string, unknown>;
|
|
653
|
+
}
|
|
654
|
+
export interface OutboundCallModeReadiness {
|
|
655
|
+
ready: boolean;
|
|
656
|
+
status: "disabled" | "ready" | "unknown" | "unsupported";
|
|
657
|
+
provider: string;
|
|
658
|
+
transportFamily: string;
|
|
659
|
+
requiredCapabilities: string[];
|
|
660
|
+
missingCapabilities: string[];
|
|
661
|
+
unsupportedCapabilities: string[];
|
|
662
|
+
reasons: string[];
|
|
663
|
+
}
|
|
664
|
+
export declare const OUTBOUND_CALL_MODE_BOUNDS: {
|
|
665
|
+
readonly maxDurationSeconds: {
|
|
666
|
+
readonly default: 180;
|
|
667
|
+
readonly min: 30;
|
|
668
|
+
readonly max: 900;
|
|
669
|
+
};
|
|
670
|
+
readonly maxKeypresses: {
|
|
671
|
+
readonly default: 12;
|
|
672
|
+
readonly min: 1;
|
|
673
|
+
readonly max: 64;
|
|
674
|
+
};
|
|
675
|
+
readonly repeatedMenuLimit: {
|
|
676
|
+
readonly default: 3;
|
|
677
|
+
readonly min: 1;
|
|
678
|
+
readonly max: 10;
|
|
679
|
+
};
|
|
680
|
+
readonly noProgressTimeoutSeconds: {
|
|
681
|
+
readonly default: 30;
|
|
682
|
+
readonly min: 5;
|
|
683
|
+
readonly max: 120;
|
|
684
|
+
};
|
|
685
|
+
};
|
|
686
|
+
export declare const OUTBOUND_CALL_MODE_DEFAULTS: {
|
|
687
|
+
readonly enabled: false;
|
|
688
|
+
readonly autoDetect: true;
|
|
689
|
+
readonly dtmfToolEnabled: true;
|
|
690
|
+
readonly maxDurationSeconds: 180;
|
|
691
|
+
readonly maxKeypresses: 12;
|
|
692
|
+
readonly repeatedMenuLimit: 3;
|
|
693
|
+
readonly noProgressTimeoutSeconds: 30;
|
|
694
|
+
readonly humanDetectionEnabled: true;
|
|
695
|
+
readonly resumeOnHuman: true;
|
|
696
|
+
readonly observability: {
|
|
697
|
+
readonly enabled: true;
|
|
698
|
+
readonly includeTrigger: true;
|
|
699
|
+
readonly includeTransitions: true;
|
|
700
|
+
readonly includeTerminationReason: true;
|
|
701
|
+
};
|
|
702
|
+
};
|
|
703
|
+
export interface OutboundCallModeProviderProfile {
|
|
704
|
+
contractSupported: boolean;
|
|
705
|
+
execution: "adapter_runtime_dependent";
|
|
706
|
+
capabilityPolicy: "fail_closed";
|
|
707
|
+
transportFamily: string;
|
|
708
|
+
providers: readonly string[];
|
|
709
|
+
requiredCapabilities: readonly string[];
|
|
710
|
+
}
|
|
711
|
+
export declare const OUTBOUND_CALL_MODE_PROVIDER_MATRIX: Record<string, OutboundCallModeProviderProfile>;
|
|
712
|
+
export declare function outboundCallModeProviderProfile(provider: string): OutboundCallModeProviderProfile;
|
|
713
|
+
export declare function outboundCallModeReadiness(config?: OutboundCallModeConfig, capabilities?: OutboundCallModeCapabilities): OutboundCallModeReadiness;
|
|
618
714
|
export interface CreateLabsAgentRequest {
|
|
619
715
|
agencyId?: string;
|
|
620
716
|
agency_id?: string;
|
|
@@ -668,6 +764,15 @@ export interface CreateLabsAgentRequest {
|
|
|
668
764
|
language?: string;
|
|
669
765
|
preferredLanguage?: string;
|
|
670
766
|
preferred_language?: string;
|
|
767
|
+
/** Opt in to managed live language and matching-voice routing. Defaults to false. */
|
|
768
|
+
languageVoiceRouting?: boolean;
|
|
769
|
+
language_voice_routing?: boolean;
|
|
770
|
+
/** Optional ordered language list. The first language controls the greeting. */
|
|
771
|
+
routingLanguages?: string[];
|
|
772
|
+
routing_languages?: string[];
|
|
773
|
+
/** Optional per-language voice preferences. Supports two to four profiles. */
|
|
774
|
+
languageProfiles?: LabsLanguageVoiceProfile[];
|
|
775
|
+
language_profiles?: LabsLanguageVoiceProfile[];
|
|
671
776
|
voice?: LabsVoiceSelection;
|
|
672
777
|
/** Resolve a real current catalog voice from this plain-language preference. */
|
|
673
778
|
voicePreference?: LabsVoicePreference;
|
|
@@ -690,6 +795,9 @@ export interface CreateLabsAgentRequest {
|
|
|
690
795
|
voice_watcher?: boolean;
|
|
691
796
|
voiceWatcherModel?: string;
|
|
692
797
|
voice_watcher_model?: string;
|
|
798
|
+
/** Opt-in outbound IVR mode. Execution depends on adapter-reported readiness. */
|
|
799
|
+
outboundCallMode?: OutboundCallModeConfig;
|
|
800
|
+
outbound_call_mode?: OutboundCallModeConfig;
|
|
693
801
|
metadata?: Record<string, unknown>;
|
|
694
802
|
}
|
|
695
803
|
export interface ListLabsAgentsOptions {
|
|
@@ -712,6 +820,21 @@ export interface DeleteLabsAgentResponse {
|
|
|
712
820
|
released_numbers?: unknown[];
|
|
713
821
|
[extra: string]: unknown;
|
|
714
822
|
}
|
|
823
|
+
export type LabsAgentLifecycle = "draft" | "active" | "paused" | "archived" | string;
|
|
824
|
+
export interface LabsAgentReadiness {
|
|
825
|
+
ready: boolean;
|
|
826
|
+
status?: string;
|
|
827
|
+
checks?: Array<Record<string, unknown>>;
|
|
828
|
+
[extra: string]: unknown;
|
|
829
|
+
}
|
|
830
|
+
export type UpdateLabsAgentRequest = Partial<CreateLabsAgentRequest>;
|
|
831
|
+
export interface LabsAgentLifecycleResponse {
|
|
832
|
+
success?: boolean;
|
|
833
|
+
agent?: LabsAgentResponse;
|
|
834
|
+
lifecycle?: LabsAgentLifecycle;
|
|
835
|
+
readiness?: LabsAgentReadiness;
|
|
836
|
+
[extra: string]: unknown;
|
|
837
|
+
}
|
|
715
838
|
export interface LabsCapabilitiesResponse {
|
|
716
839
|
product: string;
|
|
717
840
|
api_namespace: string;
|
|
@@ -1052,6 +1175,8 @@ export interface LabsAgentResponse {
|
|
|
1052
1175
|
display_name?: string;
|
|
1053
1176
|
runtime_mode?: string;
|
|
1054
1177
|
preset_key?: string;
|
|
1178
|
+
status?: string;
|
|
1179
|
+
lifecycle?: LabsAgentLifecycle;
|
|
1055
1180
|
profile?: Record<string, unknown>;
|
|
1056
1181
|
runtime?: Record<string, unknown>;
|
|
1057
1182
|
[extra: string]: unknown;
|
|
@@ -1066,6 +1191,16 @@ export interface CreateLabsAgentResponse {
|
|
|
1066
1191
|
[extra: string]: unknown;
|
|
1067
1192
|
};
|
|
1068
1193
|
call_plan?: LabsCallPlan;
|
|
1194
|
+
language_voice_routing?: {
|
|
1195
|
+
enabled: boolean;
|
|
1196
|
+
voice_routing_enabled: boolean;
|
|
1197
|
+
profiles: Array<Record<string, unknown>>;
|
|
1198
|
+
greeting_translation?: {
|
|
1199
|
+
language: string;
|
|
1200
|
+
language_hint: string;
|
|
1201
|
+
status: "not_needed" | "translated";
|
|
1202
|
+
};
|
|
1203
|
+
};
|
|
1069
1204
|
[extra: string]: unknown;
|
|
1070
1205
|
}
|
|
1071
1206
|
export interface ListLabsAgentsResponse {
|
|
@@ -1074,6 +1209,9 @@ export interface ListLabsAgentsResponse {
|
|
|
1074
1209
|
}
|
|
1075
1210
|
export interface GetLabsAgentResponse {
|
|
1076
1211
|
agent: LabsAgentResponse;
|
|
1212
|
+
runtime?: Record<string, unknown>;
|
|
1213
|
+
widget?: Record<string, unknown>;
|
|
1214
|
+
readiness?: LabsAgentReadiness;
|
|
1077
1215
|
}
|
|
1078
1216
|
export interface STTResult {
|
|
1079
1217
|
transcript: string;
|
|
@@ -1599,6 +1737,14 @@ export interface CampaignLiveView {
|
|
|
1599
1737
|
portal_url: string;
|
|
1600
1738
|
stats?: Record<string, unknown> | null;
|
|
1601
1739
|
}
|
|
1740
|
+
export interface CampaignCreateInput {
|
|
1741
|
+
name?: string;
|
|
1742
|
+
goal?: string;
|
|
1743
|
+
agentId?: string;
|
|
1744
|
+
accountId?: string;
|
|
1745
|
+
settings?: Record<string, unknown>;
|
|
1746
|
+
outboundCallMode?: OutboundCallModeConfig;
|
|
1747
|
+
}
|
|
1602
1748
|
export interface CampaignUpdateInput {
|
|
1603
1749
|
name?: string;
|
|
1604
1750
|
goal?: string;
|
|
@@ -1610,6 +1756,7 @@ export interface CampaignUpdateInput {
|
|
|
1610
1756
|
delay_hours: number;
|
|
1611
1757
|
}[];
|
|
1612
1758
|
settings?: Record<string, unknown>;
|
|
1759
|
+
outboundCallMode?: OutboundCallModeConfig;
|
|
1613
1760
|
}
|
|
1614
1761
|
export interface BrandScanResult {
|
|
1615
1762
|
url: string;
|
|
@@ -1673,12 +1820,7 @@ declare class CampaignsNamespace {
|
|
|
1673
1820
|
}): Promise<{
|
|
1674
1821
|
campaigns: CampaignSummary[];
|
|
1675
1822
|
}>;
|
|
1676
|
-
create(opts?: {
|
|
1677
|
-
name?: string;
|
|
1678
|
-
goal?: string;
|
|
1679
|
-
agentId?: string;
|
|
1680
|
-
accountId?: string;
|
|
1681
|
-
}): Promise<{
|
|
1823
|
+
create(opts?: CampaignCreateInput): Promise<{
|
|
1682
1824
|
campaign: CampaignSummary;
|
|
1683
1825
|
}>;
|
|
1684
1826
|
get(campaignId: string): Promise<{
|
|
@@ -1886,6 +2028,14 @@ declare class LabsAgentsNamespace {
|
|
|
1886
2028
|
list(opts?: ListLabsAgentsOptions): Promise<ListLabsAgentsResponse>;
|
|
1887
2029
|
/** Fetch one durable agent by key. */
|
|
1888
2030
|
get(agentKey: string, opts?: GetLabsAgentOptions): Promise<GetLabsAgentResponse>;
|
|
2031
|
+
/** Update an existing durable agent without changing omitted fields. */
|
|
2032
|
+
update(agentKey: string, input: UpdateLabsAgentRequest, opts?: GetLabsAgentOptions): Promise<LabsAgentLifecycleResponse>;
|
|
2033
|
+
/** Ask the managed server whether this agent is ready to activate. */
|
|
2034
|
+
readiness(agentKey: string, opts?: GetLabsAgentOptions): Promise<LabsAgentReadiness>;
|
|
2035
|
+
/** Activate a ready hosted agent. */
|
|
2036
|
+
activate(agentKey: string, opts?: GetLabsAgentOptions): Promise<LabsAgentLifecycleResponse>;
|
|
2037
|
+
/** Pause a hosted agent without deleting it. */
|
|
2038
|
+
pause(agentKey: string, opts?: GetLabsAgentOptions): Promise<LabsAgentLifecycleResponse>;
|
|
1889
2039
|
/** Delete an agent. Optionally ask the backend to release assigned numbers. */
|
|
1890
2040
|
delete(agentKey: string, opts?: DeleteLabsAgentOptions): Promise<DeleteLabsAgentResponse>;
|
|
1891
2041
|
}
|
package/dist/cjs/index.js
CHANGED
|
@@ -21,8 +21,131 @@
|
|
|
21
21
|
* });
|
|
22
22
|
*/
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
-
exports.Supafone = exports.SupafoneLabs = exports.SupafoneLabsError = void 0;
|
|
24
|
+
exports.Supafone = exports.SupafoneLabs = exports.SupafoneLabsError = exports.OUTBOUND_CALL_MODE_PROVIDER_MATRIX = exports.OUTBOUND_CALL_MODE_DEFAULTS = exports.OUTBOUND_CALL_MODE_BOUNDS = void 0;
|
|
25
|
+
exports.outboundCallModeProviderProfile = outboundCallModeProviderProfile;
|
|
26
|
+
exports.outboundCallModeReadiness = outboundCallModeReadiness;
|
|
25
27
|
exports.generateCallStages = generateCallStages;
|
|
28
|
+
exports.OUTBOUND_CALL_MODE_BOUNDS = {
|
|
29
|
+
maxDurationSeconds: { default: 180, min: 30, max: 900 },
|
|
30
|
+
maxKeypresses: { default: 12, min: 1, max: 64 },
|
|
31
|
+
repeatedMenuLimit: { default: 3, min: 1, max: 10 },
|
|
32
|
+
noProgressTimeoutSeconds: { default: 30, min: 5, max: 120 },
|
|
33
|
+
};
|
|
34
|
+
exports.OUTBOUND_CALL_MODE_DEFAULTS = {
|
|
35
|
+
enabled: false,
|
|
36
|
+
autoDetect: true,
|
|
37
|
+
dtmfToolEnabled: true,
|
|
38
|
+
maxDurationSeconds: 180,
|
|
39
|
+
maxKeypresses: 12,
|
|
40
|
+
repeatedMenuLimit: 3,
|
|
41
|
+
noProgressTimeoutSeconds: 30,
|
|
42
|
+
humanDetectionEnabled: true,
|
|
43
|
+
resumeOnHuman: true,
|
|
44
|
+
observability: {
|
|
45
|
+
enabled: true,
|
|
46
|
+
includeTrigger: true,
|
|
47
|
+
includeTransitions: true,
|
|
48
|
+
includeTerminationReason: true,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
const OUTBOUND_PROVIDER_PROFILE_BASE = {
|
|
52
|
+
contractSupported: true,
|
|
53
|
+
execution: "adapter_runtime_dependent",
|
|
54
|
+
capabilityPolicy: "fail_closed",
|
|
55
|
+
requiredCapabilities: ["dtmf", "state_persistence", "human_detection"],
|
|
56
|
+
};
|
|
57
|
+
exports.OUTBOUND_CALL_MODE_PROVIDER_MATRIX = {
|
|
58
|
+
supafoneManaged: {
|
|
59
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
60
|
+
transportFamily: "supafone_managed",
|
|
61
|
+
providers: ["supafone", "supafone_managed", "managed"],
|
|
62
|
+
},
|
|
63
|
+
twilio: {
|
|
64
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
65
|
+
transportFamily: "twilio",
|
|
66
|
+
providers: ["twilio", "byo_twilio"],
|
|
67
|
+
},
|
|
68
|
+
telnyx: {
|
|
69
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
70
|
+
transportFamily: "telnyx",
|
|
71
|
+
providers: ["telnyx", "byo_telnyx"],
|
|
72
|
+
},
|
|
73
|
+
plivo: {
|
|
74
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
75
|
+
transportFamily: "plivo",
|
|
76
|
+
providers: ["plivo", "byo_plivo"],
|
|
77
|
+
},
|
|
78
|
+
signalWire: {
|
|
79
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
80
|
+
transportFamily: "signalwire",
|
|
81
|
+
providers: ["signalwire", "signal_wire", "byo_signalwire"],
|
|
82
|
+
},
|
|
83
|
+
sipByoc: {
|
|
84
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
85
|
+
transportFamily: "sip_byoc",
|
|
86
|
+
providers: ["sip", "custom_sip", "byo_sip", "byoc", "sip_byoc"],
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
function outboundCallModeProviderProfile(provider) {
|
|
90
|
+
const normalized = String(provider ?? "").trim().toLowerCase();
|
|
91
|
+
for (const profile of Object.values(exports.OUTBOUND_CALL_MODE_PROVIDER_MATRIX)) {
|
|
92
|
+
if (profile.providers.includes(normalized)) {
|
|
93
|
+
return { ...profile, providers: [...profile.providers] };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
contractSupported: false,
|
|
98
|
+
execution: "adapter_runtime_dependent",
|
|
99
|
+
capabilityPolicy: "fail_closed",
|
|
100
|
+
transportFamily: "unknown",
|
|
101
|
+
providers: normalized ? [normalized] : [],
|
|
102
|
+
requiredCapabilities: ["dtmf", "state_persistence", "human_detection"],
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
function outboundCallModeReadiness(config, capabilities) {
|
|
106
|
+
const provider = String(capabilities?.provider ?? "");
|
|
107
|
+
const profile = outboundCallModeProviderProfile(provider);
|
|
108
|
+
const normalized = outboundCallModePayload(config ?? {});
|
|
109
|
+
if (normalized.enabled !== true) {
|
|
110
|
+
return {
|
|
111
|
+
ready: true,
|
|
112
|
+
status: "disabled",
|
|
113
|
+
provider,
|
|
114
|
+
transportFamily: profile.transportFamily,
|
|
115
|
+
requiredCapabilities: [],
|
|
116
|
+
missingCapabilities: [],
|
|
117
|
+
unsupportedCapabilities: [],
|
|
118
|
+
reasons: [],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const requiredCapabilities = [...normalized.required_capabilities];
|
|
122
|
+
const reported = {
|
|
123
|
+
dtmf: capabilities?.dtmf,
|
|
124
|
+
state_persistence: capabilities?.statePersistence,
|
|
125
|
+
human_detection: capabilities?.humanDetection,
|
|
126
|
+
observability: capabilities?.observability,
|
|
127
|
+
};
|
|
128
|
+
const missingCapabilities = requiredCapabilities.filter((key) => reported[key] === undefined);
|
|
129
|
+
const unsupportedCapabilities = requiredCapabilities.filter((key) => reported[key] === false);
|
|
130
|
+
const status = unsupportedCapabilities.length
|
|
131
|
+
? "unsupported"
|
|
132
|
+
: missingCapabilities.length
|
|
133
|
+
? "unknown"
|
|
134
|
+
: "ready";
|
|
135
|
+
const reasons = unsupportedCapabilities.length
|
|
136
|
+
? unsupportedCapabilities.map((key) => `Adapter reports ${key} is unsupported.`)
|
|
137
|
+
: missingCapabilities.map((key) => `Adapter did not report ${key} support.`);
|
|
138
|
+
return {
|
|
139
|
+
ready: status === "ready",
|
|
140
|
+
status,
|
|
141
|
+
provider,
|
|
142
|
+
transportFamily: profile.transportFamily,
|
|
143
|
+
requiredCapabilities,
|
|
144
|
+
missingCapabilities,
|
|
145
|
+
unsupportedCapabilities,
|
|
146
|
+
reasons,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
26
149
|
class SupafoneLabsError extends Error {
|
|
27
150
|
status;
|
|
28
151
|
body;
|
|
@@ -770,18 +893,26 @@ class CampaignsNamespace {
|
|
|
770
893
|
const query = opts.accountId ? `?${new URLSearchParams({ account_id: opts.accountId })}` : "";
|
|
771
894
|
return this.sm.requestAccountApi("GET", `/api/v1/campaigns${query}`);
|
|
772
895
|
}
|
|
773
|
-
create(opts = {}) {
|
|
774
|
-
|
|
896
|
+
async create(opts = {}) {
|
|
897
|
+
const created = await this.sm.requestAccountApi("POST", "/api/v1/campaigns", compact({
|
|
775
898
|
name: opts.name ?? "New campaign",
|
|
776
899
|
goal: opts.goal ?? "book",
|
|
777
900
|
agent_id: opts.agentId,
|
|
778
901
|
account_id: opts.accountId,
|
|
779
902
|
}));
|
|
903
|
+
const settings = campaignSettingsPayload(opts.settings, opts.outboundCallMode);
|
|
904
|
+
if (!Object.keys(settings).length)
|
|
905
|
+
return created;
|
|
906
|
+
if (!created.campaign?.id) {
|
|
907
|
+
throw new SupafoneLabsError("Campaign was created but no campaign id was returned; outbound call mode was not saved.");
|
|
908
|
+
}
|
|
909
|
+
return this.update(created.campaign.id, { settings });
|
|
780
910
|
}
|
|
781
911
|
get(campaignId) {
|
|
782
912
|
return this.sm.requestAccountApi("GET", `/api/v1/campaigns/${encodeURIComponent(campaignId)}`);
|
|
783
913
|
}
|
|
784
914
|
update(campaignId, input) {
|
|
915
|
+
const settings = campaignSettingsPayload(input.settings, input.outboundCallMode);
|
|
785
916
|
const payload = compact({
|
|
786
917
|
name: input.name,
|
|
787
918
|
goal: input.goal,
|
|
@@ -789,10 +920,10 @@ class CampaignsNamespace {
|
|
|
789
920
|
email_subject: input.emailSubject,
|
|
790
921
|
email_body: input.emailBody,
|
|
791
922
|
cadence: input.cadence,
|
|
792
|
-
settings:
|
|
923
|
+
settings: Object.keys(settings).length ? settings : undefined,
|
|
793
924
|
});
|
|
794
925
|
if (!Object.keys(payload).length) {
|
|
795
|
-
throw new SupafoneLabsError("Nothing to update — pass name, goal, agentId, emailSubject, emailBody, cadence, or
|
|
926
|
+
throw new SupafoneLabsError("Nothing to update — pass name, goal, agentId, emailSubject, emailBody, cadence, settings, or outboundCallMode");
|
|
796
927
|
}
|
|
797
928
|
return this.sm.requestAccountApi("PUT", `/api/v1/campaigns/${encodeURIComponent(campaignId)}`, payload);
|
|
798
929
|
}
|
|
@@ -1119,6 +1250,38 @@ class LabsAgentsNamespace {
|
|
|
1119
1250
|
const suffix = q.toString() ? `?${q}` : "";
|
|
1120
1251
|
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}${suffix}`);
|
|
1121
1252
|
}
|
|
1253
|
+
/** Update an existing durable agent without changing omitted fields. */
|
|
1254
|
+
update(agentKey, input, opts = {}) {
|
|
1255
|
+
const q = new URLSearchParams();
|
|
1256
|
+
if (opts.agencyId)
|
|
1257
|
+
q.set("agency_id", opts.agencyId);
|
|
1258
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
1259
|
+
return this.sm.requestSupafoneApi("PATCH", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}${suffix}`, labsAgentUpdatePayload(input));
|
|
1260
|
+
}
|
|
1261
|
+
/** Ask the managed server whether this agent is ready to activate. */
|
|
1262
|
+
readiness(agentKey, opts = {}) {
|
|
1263
|
+
const q = new URLSearchParams();
|
|
1264
|
+
if (opts.agencyId)
|
|
1265
|
+
q.set("agency_id", opts.agencyId);
|
|
1266
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
1267
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}/readiness${suffix}`);
|
|
1268
|
+
}
|
|
1269
|
+
/** Activate a ready hosted agent. */
|
|
1270
|
+
activate(agentKey, opts = {}) {
|
|
1271
|
+
const q = new URLSearchParams();
|
|
1272
|
+
if (opts.agencyId)
|
|
1273
|
+
q.set("agency_id", opts.agencyId);
|
|
1274
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
1275
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}/activate${suffix}`);
|
|
1276
|
+
}
|
|
1277
|
+
/** Pause a hosted agent without deleting it. */
|
|
1278
|
+
pause(agentKey, opts = {}) {
|
|
1279
|
+
const q = new URLSearchParams();
|
|
1280
|
+
if (opts.agencyId)
|
|
1281
|
+
q.set("agency_id", opts.agencyId);
|
|
1282
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
1283
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}/pause${suffix}`);
|
|
1284
|
+
}
|
|
1122
1285
|
/** Delete an agent. Optionally ask the backend to release assigned numbers. */
|
|
1123
1286
|
delete(agentKey, opts = {}) {
|
|
1124
1287
|
const q = new URLSearchParams();
|
|
@@ -1664,6 +1827,94 @@ class OptimizerNamespace {
|
|
|
1664
1827
|
return this.sm.request("GET", `/v1/optimizer/reports?agent=${encodeURIComponent(agent)}&limit=${limit}`);
|
|
1665
1828
|
}
|
|
1666
1829
|
}
|
|
1830
|
+
function outboundCallModePayload(config) {
|
|
1831
|
+
const enabled = outboundModeBoolean(config.enabled, false, "enabled");
|
|
1832
|
+
if (!enabled)
|
|
1833
|
+
return { version: 1, enabled: false };
|
|
1834
|
+
const observabilityInput = config.observability ?? {};
|
|
1835
|
+
if (typeof observabilityInput !== "object" || Array.isArray(observabilityInput)) {
|
|
1836
|
+
throw new SupafoneLabsError("outboundCallMode.observability must be an object");
|
|
1837
|
+
}
|
|
1838
|
+
if (observabilityInput.metadata !== undefined
|
|
1839
|
+
&& (typeof observabilityInput.metadata !== "object"
|
|
1840
|
+
|| observabilityInput.metadata === null
|
|
1841
|
+
|| Array.isArray(observabilityInput.metadata))) {
|
|
1842
|
+
throw new SupafoneLabsError("outboundCallMode.observability.metadata must be an object");
|
|
1843
|
+
}
|
|
1844
|
+
const observability = compact({
|
|
1845
|
+
enabled: outboundModeBoolean(observabilityInput.enabled, true, "observability.enabled"),
|
|
1846
|
+
include_trigger: outboundModeBoolean(observabilityInput.includeTrigger, true, "observability.includeTrigger"),
|
|
1847
|
+
include_transitions: outboundModeBoolean(observabilityInput.includeTransitions, true, "observability.includeTransitions"),
|
|
1848
|
+
include_termination_reason: outboundModeBoolean(observabilityInput.includeTerminationReason, true, "observability.includeTerminationReason"),
|
|
1849
|
+
metadata: observabilityInput.metadata,
|
|
1850
|
+
});
|
|
1851
|
+
const payload = {
|
|
1852
|
+
version: 1,
|
|
1853
|
+
enabled: true,
|
|
1854
|
+
initial_mode: "mission",
|
|
1855
|
+
ivr_mode: "dynamic",
|
|
1856
|
+
transport_scope: "provider_agnostic",
|
|
1857
|
+
capability_policy: "fail_closed",
|
|
1858
|
+
auto_detect: outboundModeBoolean(config.autoDetect, true, "autoDetect"),
|
|
1859
|
+
dtmf_tool_enabled: outboundModeBoolean(config.dtmfToolEnabled, true, "dtmfToolEnabled"),
|
|
1860
|
+
max_duration_seconds: outboundModeInteger(config.maxDurationSeconds, "maxDurationSeconds"),
|
|
1861
|
+
max_keypresses: outboundModeInteger(config.maxKeypresses, "maxKeypresses"),
|
|
1862
|
+
repeated_menu_limit: outboundModeInteger(config.repeatedMenuLimit, "repeatedMenuLimit"),
|
|
1863
|
+
no_progress_timeout_seconds: outboundModeInteger(config.noProgressTimeoutSeconds, "noProgressTimeoutSeconds"),
|
|
1864
|
+
human_detection_enabled: outboundModeBoolean(config.humanDetectionEnabled, true, "humanDetectionEnabled"),
|
|
1865
|
+
resume_on_human: outboundModeBoolean(config.resumeOnHuman, true, "resumeOnHuman"),
|
|
1866
|
+
observability,
|
|
1867
|
+
};
|
|
1868
|
+
payload.required_capabilities = outboundCallModeRequiredCapabilities(payload);
|
|
1869
|
+
return payload;
|
|
1870
|
+
}
|
|
1871
|
+
function outboundModeBoolean(value, fallback, key) {
|
|
1872
|
+
if (value === undefined)
|
|
1873
|
+
return fallback;
|
|
1874
|
+
if (typeof value !== "boolean") {
|
|
1875
|
+
throw new SupafoneLabsError(`outboundCallMode.${key} must be a boolean`);
|
|
1876
|
+
}
|
|
1877
|
+
return value;
|
|
1878
|
+
}
|
|
1879
|
+
function outboundModeInteger(value, key) {
|
|
1880
|
+
const bounds = exports.OUTBOUND_CALL_MODE_BOUNDS[key];
|
|
1881
|
+
const normalized = value ?? bounds.default;
|
|
1882
|
+
if (!Number.isInteger(normalized)) {
|
|
1883
|
+
throw new SupafoneLabsError(`outboundCallMode.${key} must be an integer`);
|
|
1884
|
+
}
|
|
1885
|
+
if (normalized < bounds.min || normalized > bounds.max) {
|
|
1886
|
+
throw new SupafoneLabsError(`outboundCallMode.${key} must be between ${bounds.min} and ${bounds.max}`);
|
|
1887
|
+
}
|
|
1888
|
+
return normalized;
|
|
1889
|
+
}
|
|
1890
|
+
function outboundCallModeRequiredCapabilities(payload) {
|
|
1891
|
+
const required = ["state_persistence"];
|
|
1892
|
+
if (payload.dtmf_tool_enabled !== false)
|
|
1893
|
+
required.unshift("dtmf");
|
|
1894
|
+
if (payload.human_detection_enabled !== false || payload.resume_on_human !== false) {
|
|
1895
|
+
required.push("human_detection");
|
|
1896
|
+
}
|
|
1897
|
+
const observability = payload.observability;
|
|
1898
|
+
if (observability?.enabled === true)
|
|
1899
|
+
required.push("observability");
|
|
1900
|
+
return required;
|
|
1901
|
+
}
|
|
1902
|
+
function labsAgentMetadataPayload(input) {
|
|
1903
|
+
const metadata = { ...(input.metadata ?? {}) };
|
|
1904
|
+
const mode = input.outboundCallMode ?? input.outbound_call_mode;
|
|
1905
|
+
if (mode !== undefined)
|
|
1906
|
+
metadata.outbound_call_mode = outboundCallModePayload(mode);
|
|
1907
|
+
return Object.keys(metadata).length ? metadata : undefined;
|
|
1908
|
+
}
|
|
1909
|
+
function labsAgentUpdatePayload(input) {
|
|
1910
|
+
return labsAgentPayload(input);
|
|
1911
|
+
}
|
|
1912
|
+
function campaignSettingsPayload(settings, mode) {
|
|
1913
|
+
const payload = { ...(settings ?? {}) };
|
|
1914
|
+
if (mode !== undefined)
|
|
1915
|
+
payload.outbound_call_mode = outboundCallModePayload(mode);
|
|
1916
|
+
return payload;
|
|
1917
|
+
}
|
|
1667
1918
|
function labsAgentPayload(input) {
|
|
1668
1919
|
const fixedLanguage = input.preferred_language ?? input.preferredLanguage ?? input.language;
|
|
1669
1920
|
return compact({
|
|
@@ -1692,6 +1943,9 @@ function labsAgentPayload(input) {
|
|
|
1692
1943
|
greeting: input.greeting,
|
|
1693
1944
|
system_prompt: input.system_prompt ?? input.systemPrompt,
|
|
1694
1945
|
language: fixedLanguage,
|
|
1946
|
+
language_voice_routing: input.language_voice_routing ?? input.languageVoiceRouting,
|
|
1947
|
+
routing_languages: input.routing_languages ?? input.routingLanguages,
|
|
1948
|
+
language_profiles: languageProfilesPayload(input.language_profiles ?? input.languageProfiles),
|
|
1695
1949
|
voice: input.voice ? voicePayload(input.voice) : undefined,
|
|
1696
1950
|
voice_preference: voicePreferencePayload(input.voice_preference ?? input.voicePreference, fixedLanguage),
|
|
1697
1951
|
provider_keys: input.provider_keys ?? (input.providerKeys ? providerKeysPayload(input.providerKeys) : undefined),
|
|
@@ -1707,7 +1961,7 @@ function labsAgentPayload(input) {
|
|
|
1707
1961
|
ultravox: input.ultravox ? ultravoxPayload(input.ultravox) : undefined,
|
|
1708
1962
|
voice_watcher: input.voice_watcher ?? input.voiceWatcher,
|
|
1709
1963
|
voice_watcher_model: input.voice_watcher_model ?? input.voiceWatcherModel,
|
|
1710
|
-
metadata: input
|
|
1964
|
+
metadata: labsAgentMetadataPayload(input),
|
|
1711
1965
|
});
|
|
1712
1966
|
}
|
|
1713
1967
|
function hostedListQuery(opts) {
|
|
@@ -1935,6 +2189,16 @@ function voicePayload(input) {
|
|
|
1935
2189
|
model: input.model,
|
|
1936
2190
|
});
|
|
1937
2191
|
}
|
|
2192
|
+
function languageProfilesPayload(input) {
|
|
2193
|
+
if (!Array.isArray(input))
|
|
2194
|
+
return undefined;
|
|
2195
|
+
const profiles = input.map((profile) => compact({
|
|
2196
|
+
language: profile.language,
|
|
2197
|
+
language_hint: profile.language_hint ?? profile.languageHint,
|
|
2198
|
+
voice: profile.voice ? voicePayload(profile.voice) : undefined,
|
|
2199
|
+
}));
|
|
2200
|
+
return profiles.length ? profiles : undefined;
|
|
2201
|
+
}
|
|
1938
2202
|
function voicePreferencePayload(input, defaultLanguage) {
|
|
1939
2203
|
if (!input)
|
|
1940
2204
|
return undefined;
|