supafone-labs 0.4.14 → 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 +20 -0
- package/dist/cjs/index.d.ts +130 -6
- package/dist/cjs/index.js +257 -6
- package/dist/index.d.ts +130 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +254 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +416 -5
package/README.md
CHANGED
|
@@ -132,6 +132,26 @@ The first configured language owns the opening, and a non-English primary
|
|
|
132
132
|
greeting is translated during provisioning. See
|
|
133
133
|
[Live Language and Voice Routing](../gitbook/live-language-voice-routing.md).
|
|
134
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
|
+
|
|
135
155
|
BYOK is advanced and split into three independent lanes:
|
|
136
156
|
|
|
137
157
|
| Lane | Examples |
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -622,6 +622,95 @@ export interface LabsUltravoxRuntime {
|
|
|
622
622
|
sip?: LabsCustomSipConfig;
|
|
623
623
|
[extra: string]: unknown;
|
|
624
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;
|
|
625
714
|
export interface CreateLabsAgentRequest {
|
|
626
715
|
agencyId?: string;
|
|
627
716
|
agency_id?: string;
|
|
@@ -706,6 +795,9 @@ export interface CreateLabsAgentRequest {
|
|
|
706
795
|
voice_watcher?: boolean;
|
|
707
796
|
voiceWatcherModel?: string;
|
|
708
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;
|
|
709
801
|
metadata?: Record<string, unknown>;
|
|
710
802
|
}
|
|
711
803
|
export interface ListLabsAgentsOptions {
|
|
@@ -728,6 +820,21 @@ export interface DeleteLabsAgentResponse {
|
|
|
728
820
|
released_numbers?: unknown[];
|
|
729
821
|
[extra: string]: unknown;
|
|
730
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
|
+
}
|
|
731
838
|
export interface LabsCapabilitiesResponse {
|
|
732
839
|
product: string;
|
|
733
840
|
api_namespace: string;
|
|
@@ -1068,6 +1175,8 @@ export interface LabsAgentResponse {
|
|
|
1068
1175
|
display_name?: string;
|
|
1069
1176
|
runtime_mode?: string;
|
|
1070
1177
|
preset_key?: string;
|
|
1178
|
+
status?: string;
|
|
1179
|
+
lifecycle?: LabsAgentLifecycle;
|
|
1071
1180
|
profile?: Record<string, unknown>;
|
|
1072
1181
|
runtime?: Record<string, unknown>;
|
|
1073
1182
|
[extra: string]: unknown;
|
|
@@ -1100,6 +1209,9 @@ export interface ListLabsAgentsResponse {
|
|
|
1100
1209
|
}
|
|
1101
1210
|
export interface GetLabsAgentResponse {
|
|
1102
1211
|
agent: LabsAgentResponse;
|
|
1212
|
+
runtime?: Record<string, unknown>;
|
|
1213
|
+
widget?: Record<string, unknown>;
|
|
1214
|
+
readiness?: LabsAgentReadiness;
|
|
1103
1215
|
}
|
|
1104
1216
|
export interface STTResult {
|
|
1105
1217
|
transcript: string;
|
|
@@ -1625,6 +1737,14 @@ export interface CampaignLiveView {
|
|
|
1625
1737
|
portal_url: string;
|
|
1626
1738
|
stats?: Record<string, unknown> | null;
|
|
1627
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
|
+
}
|
|
1628
1748
|
export interface CampaignUpdateInput {
|
|
1629
1749
|
name?: string;
|
|
1630
1750
|
goal?: string;
|
|
@@ -1636,6 +1756,7 @@ export interface CampaignUpdateInput {
|
|
|
1636
1756
|
delay_hours: number;
|
|
1637
1757
|
}[];
|
|
1638
1758
|
settings?: Record<string, unknown>;
|
|
1759
|
+
outboundCallMode?: OutboundCallModeConfig;
|
|
1639
1760
|
}
|
|
1640
1761
|
export interface BrandScanResult {
|
|
1641
1762
|
url: string;
|
|
@@ -1699,12 +1820,7 @@ declare class CampaignsNamespace {
|
|
|
1699
1820
|
}): Promise<{
|
|
1700
1821
|
campaigns: CampaignSummary[];
|
|
1701
1822
|
}>;
|
|
1702
|
-
create(opts?: {
|
|
1703
|
-
name?: string;
|
|
1704
|
-
goal?: string;
|
|
1705
|
-
agentId?: string;
|
|
1706
|
-
accountId?: string;
|
|
1707
|
-
}): Promise<{
|
|
1823
|
+
create(opts?: CampaignCreateInput): Promise<{
|
|
1708
1824
|
campaign: CampaignSummary;
|
|
1709
1825
|
}>;
|
|
1710
1826
|
get(campaignId: string): Promise<{
|
|
@@ -1912,6 +2028,14 @@ declare class LabsAgentsNamespace {
|
|
|
1912
2028
|
list(opts?: ListLabsAgentsOptions): Promise<ListLabsAgentsResponse>;
|
|
1913
2029
|
/** Fetch one durable agent by key. */
|
|
1914
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>;
|
|
1915
2039
|
/** Delete an agent. Optionally ask the backend to release assigned numbers. */
|
|
1916
2040
|
delete(agentKey: string, opts?: DeleteLabsAgentOptions): Promise<DeleteLabsAgentResponse>;
|
|
1917
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({
|
|
@@ -1710,7 +1961,7 @@ function labsAgentPayload(input) {
|
|
|
1710
1961
|
ultravox: input.ultravox ? ultravoxPayload(input.ultravox) : undefined,
|
|
1711
1962
|
voice_watcher: input.voice_watcher ?? input.voiceWatcher,
|
|
1712
1963
|
voice_watcher_model: input.voice_watcher_model ?? input.voiceWatcherModel,
|
|
1713
|
-
metadata: input
|
|
1964
|
+
metadata: labsAgentMetadataPayload(input),
|
|
1714
1965
|
});
|
|
1715
1966
|
}
|
|
1716
1967
|
function hostedListQuery(opts) {
|