supafone-labs 0.4.14 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +24 -0
- package/dist/cjs/index.d.ts +194 -6
- package/dist/cjs/index.js +315 -6
- package/dist/index.d.ts +194 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +312 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +569 -5
package/dist/index.js
CHANGED
|
@@ -19,6 +19,127 @@
|
|
|
19
19
|
* number: { search: { areaCode: "415" } },
|
|
20
20
|
* });
|
|
21
21
|
*/
|
|
22
|
+
export const OUTBOUND_CALL_MODE_BOUNDS = {
|
|
23
|
+
maxDurationSeconds: { default: 180, min: 30, max: 900 },
|
|
24
|
+
maxKeypresses: { default: 12, min: 1, max: 64 },
|
|
25
|
+
repeatedMenuLimit: { default: 3, min: 1, max: 10 },
|
|
26
|
+
noProgressTimeoutSeconds: { default: 30, min: 5, max: 120 },
|
|
27
|
+
};
|
|
28
|
+
export const OUTBOUND_CALL_MODE_DEFAULTS = {
|
|
29
|
+
enabled: false,
|
|
30
|
+
autoDetect: true,
|
|
31
|
+
dtmfToolEnabled: true,
|
|
32
|
+
maxDurationSeconds: 180,
|
|
33
|
+
maxKeypresses: 12,
|
|
34
|
+
repeatedMenuLimit: 3,
|
|
35
|
+
noProgressTimeoutSeconds: 30,
|
|
36
|
+
humanDetectionEnabled: true,
|
|
37
|
+
resumeOnHuman: true,
|
|
38
|
+
observability: {
|
|
39
|
+
enabled: true,
|
|
40
|
+
includeTrigger: true,
|
|
41
|
+
includeTransitions: true,
|
|
42
|
+
includeTerminationReason: true,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
const OUTBOUND_PROVIDER_PROFILE_BASE = {
|
|
46
|
+
contractSupported: true,
|
|
47
|
+
execution: "adapter_runtime_dependent",
|
|
48
|
+
capabilityPolicy: "fail_closed",
|
|
49
|
+
requiredCapabilities: ["dtmf", "state_persistence", "human_detection"],
|
|
50
|
+
};
|
|
51
|
+
export const OUTBOUND_CALL_MODE_PROVIDER_MATRIX = {
|
|
52
|
+
supafoneManaged: {
|
|
53
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
54
|
+
transportFamily: "supafone_managed",
|
|
55
|
+
providers: ["supafone", "supafone_managed", "managed"],
|
|
56
|
+
},
|
|
57
|
+
twilio: {
|
|
58
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
59
|
+
transportFamily: "twilio",
|
|
60
|
+
providers: ["twilio", "byo_twilio"],
|
|
61
|
+
},
|
|
62
|
+
telnyx: {
|
|
63
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
64
|
+
transportFamily: "telnyx",
|
|
65
|
+
providers: ["telnyx", "byo_telnyx"],
|
|
66
|
+
},
|
|
67
|
+
plivo: {
|
|
68
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
69
|
+
transportFamily: "plivo",
|
|
70
|
+
providers: ["plivo", "byo_plivo"],
|
|
71
|
+
},
|
|
72
|
+
signalWire: {
|
|
73
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
74
|
+
transportFamily: "signalwire",
|
|
75
|
+
providers: ["signalwire", "signal_wire", "byo_signalwire"],
|
|
76
|
+
},
|
|
77
|
+
sipByoc: {
|
|
78
|
+
...OUTBOUND_PROVIDER_PROFILE_BASE,
|
|
79
|
+
transportFamily: "sip_byoc",
|
|
80
|
+
providers: ["sip", "custom_sip", "byo_sip", "byoc", "sip_byoc"],
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
export function outboundCallModeProviderProfile(provider) {
|
|
84
|
+
const normalized = String(provider ?? "").trim().toLowerCase();
|
|
85
|
+
for (const profile of Object.values(OUTBOUND_CALL_MODE_PROVIDER_MATRIX)) {
|
|
86
|
+
if (profile.providers.includes(normalized)) {
|
|
87
|
+
return { ...profile, providers: [...profile.providers] };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return {
|
|
91
|
+
contractSupported: false,
|
|
92
|
+
execution: "adapter_runtime_dependent",
|
|
93
|
+
capabilityPolicy: "fail_closed",
|
|
94
|
+
transportFamily: "unknown",
|
|
95
|
+
providers: normalized ? [normalized] : [],
|
|
96
|
+
requiredCapabilities: ["dtmf", "state_persistence", "human_detection"],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
export function outboundCallModeReadiness(config, capabilities) {
|
|
100
|
+
const provider = String(capabilities?.provider ?? "");
|
|
101
|
+
const profile = outboundCallModeProviderProfile(provider);
|
|
102
|
+
const normalized = outboundCallModePayload(config ?? {});
|
|
103
|
+
if (normalized.enabled !== true) {
|
|
104
|
+
return {
|
|
105
|
+
ready: true,
|
|
106
|
+
status: "disabled",
|
|
107
|
+
provider,
|
|
108
|
+
transportFamily: profile.transportFamily,
|
|
109
|
+
requiredCapabilities: [],
|
|
110
|
+
missingCapabilities: [],
|
|
111
|
+
unsupportedCapabilities: [],
|
|
112
|
+
reasons: [],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const requiredCapabilities = [...normalized.required_capabilities];
|
|
116
|
+
const reported = {
|
|
117
|
+
dtmf: capabilities?.dtmf,
|
|
118
|
+
state_persistence: capabilities?.statePersistence,
|
|
119
|
+
human_detection: capabilities?.humanDetection,
|
|
120
|
+
observability: capabilities?.observability,
|
|
121
|
+
};
|
|
122
|
+
const missingCapabilities = requiredCapabilities.filter((key) => reported[key] === undefined);
|
|
123
|
+
const unsupportedCapabilities = requiredCapabilities.filter((key) => reported[key] === false);
|
|
124
|
+
const status = unsupportedCapabilities.length
|
|
125
|
+
? "unsupported"
|
|
126
|
+
: missingCapabilities.length
|
|
127
|
+
? "unknown"
|
|
128
|
+
: "ready";
|
|
129
|
+
const reasons = unsupportedCapabilities.length
|
|
130
|
+
? unsupportedCapabilities.map((key) => `Adapter reports ${key} is unsupported.`)
|
|
131
|
+
: missingCapabilities.map((key) => `Adapter did not report ${key} support.`);
|
|
132
|
+
return {
|
|
133
|
+
ready: status === "ready",
|
|
134
|
+
status,
|
|
135
|
+
provider,
|
|
136
|
+
transportFamily: profile.transportFamily,
|
|
137
|
+
requiredCapabilities,
|
|
138
|
+
missingCapabilities,
|
|
139
|
+
unsupportedCapabilities,
|
|
140
|
+
reasons,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
22
143
|
export class SupafoneLabsError extends Error {
|
|
23
144
|
status;
|
|
24
145
|
body;
|
|
@@ -259,6 +380,33 @@ export class SupafoneLabs {
|
|
|
259
380
|
clearTimeout(timer);
|
|
260
381
|
}
|
|
261
382
|
}
|
|
383
|
+
/** @internal Multipart upload to the Supafone product API with the Labs key. */
|
|
384
|
+
async requestSupafoneUpload(path, file, filename) {
|
|
385
|
+
const ctrl = new AbortController();
|
|
386
|
+
const timer = setTimeout(() => ctrl.abort(), Math.max(this.timeoutMs, 120_000));
|
|
387
|
+
try {
|
|
388
|
+
const safeFilename = filename.replace(/["\\\r\n]/g, "").slice(0, 120) || "document.txt";
|
|
389
|
+
const blob = file instanceof Blob ? file : new Blob([file]);
|
|
390
|
+
const form = new FormData();
|
|
391
|
+
form.append("file", blob, safeFilename);
|
|
392
|
+
const res = await fetch(this.supafoneApiBaseUrl + path, {
|
|
393
|
+
method: "POST",
|
|
394
|
+
signal: ctrl.signal,
|
|
395
|
+
headers: { Authorization: `Bearer ${this.supafoneApiKey}` },
|
|
396
|
+
body: form,
|
|
397
|
+
});
|
|
398
|
+
const text = await res.text();
|
|
399
|
+
const parsed = text ? safeJson(text) : {};
|
|
400
|
+
if (!res.ok) {
|
|
401
|
+
const detail = parsed?.detail ?? text ?? `HTTP ${res.status}`;
|
|
402
|
+
throw new SupafoneLabsError(`POST ${path}: ${detail}`, res.status, parsed);
|
|
403
|
+
}
|
|
404
|
+
return parsed;
|
|
405
|
+
}
|
|
406
|
+
finally {
|
|
407
|
+
clearTimeout(timer);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
262
410
|
/** @internal Authenticated binary request to the Supafone app API. */
|
|
263
411
|
async requestSupafoneBinary(path) {
|
|
264
412
|
const ctrl = new AbortController();
|
|
@@ -763,18 +911,26 @@ class CampaignsNamespace {
|
|
|
763
911
|
const query = opts.accountId ? `?${new URLSearchParams({ account_id: opts.accountId })}` : "";
|
|
764
912
|
return this.sm.requestAccountApi("GET", `/api/v1/campaigns${query}`);
|
|
765
913
|
}
|
|
766
|
-
create(opts = {}) {
|
|
767
|
-
|
|
914
|
+
async create(opts = {}) {
|
|
915
|
+
const created = await this.sm.requestAccountApi("POST", "/api/v1/campaigns", compact({
|
|
768
916
|
name: opts.name ?? "New campaign",
|
|
769
917
|
goal: opts.goal ?? "book",
|
|
770
918
|
agent_id: opts.agentId,
|
|
771
919
|
account_id: opts.accountId,
|
|
772
920
|
}));
|
|
921
|
+
const settings = campaignSettingsPayload(opts.settings, opts.outboundCallMode);
|
|
922
|
+
if (!Object.keys(settings).length)
|
|
923
|
+
return created;
|
|
924
|
+
if (!created.campaign?.id) {
|
|
925
|
+
throw new SupafoneLabsError("Campaign was created but no campaign id was returned; outbound call mode was not saved.");
|
|
926
|
+
}
|
|
927
|
+
return this.update(created.campaign.id, { settings });
|
|
773
928
|
}
|
|
774
929
|
get(campaignId) {
|
|
775
930
|
return this.sm.requestAccountApi("GET", `/api/v1/campaigns/${encodeURIComponent(campaignId)}`);
|
|
776
931
|
}
|
|
777
932
|
update(campaignId, input) {
|
|
933
|
+
const settings = campaignSettingsPayload(input.settings, input.outboundCallMode);
|
|
778
934
|
const payload = compact({
|
|
779
935
|
name: input.name,
|
|
780
936
|
goal: input.goal,
|
|
@@ -782,10 +938,10 @@ class CampaignsNamespace {
|
|
|
782
938
|
email_subject: input.emailSubject,
|
|
783
939
|
email_body: input.emailBody,
|
|
784
940
|
cadence: input.cadence,
|
|
785
|
-
settings:
|
|
941
|
+
settings: Object.keys(settings).length ? settings : undefined,
|
|
786
942
|
});
|
|
787
943
|
if (!Object.keys(payload).length) {
|
|
788
|
-
throw new SupafoneLabsError("Nothing to update — pass name, goal, agentId, emailSubject, emailBody, cadence, or
|
|
944
|
+
throw new SupafoneLabsError("Nothing to update — pass name, goal, agentId, emailSubject, emailBody, cadence, settings, or outboundCallMode");
|
|
789
945
|
}
|
|
790
946
|
return this.sm.requestAccountApi("PUT", `/api/v1/campaigns/${encodeURIComponent(campaignId)}`, payload);
|
|
791
947
|
}
|
|
@@ -1112,6 +1268,66 @@ class LabsAgentsNamespace {
|
|
|
1112
1268
|
const suffix = q.toString() ? `?${q}` : "";
|
|
1113
1269
|
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}${suffix}`);
|
|
1114
1270
|
}
|
|
1271
|
+
/** Update an existing durable agent without changing omitted fields. */
|
|
1272
|
+
update(agentKey, input, opts = {}) {
|
|
1273
|
+
const q = new URLSearchParams();
|
|
1274
|
+
if (opts.agencyId)
|
|
1275
|
+
q.set("agency_id", opts.agencyId);
|
|
1276
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
1277
|
+
return this.sm.requestSupafoneApi("PATCH", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}${suffix}`, labsAgentUpdatePayload(input));
|
|
1278
|
+
}
|
|
1279
|
+
/** Scrape a website and rebuild this agent's managed knowledge corpus. */
|
|
1280
|
+
syncKnowledge(agentId, input = {}) {
|
|
1281
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/agents/${encodeURIComponent(agentId)}/sync-knowledge`, { url: input.url ?? input.website_url ?? input.websiteUrl ?? "" });
|
|
1282
|
+
}
|
|
1283
|
+
/** Remove website knowledge while preserving uploaded documents and notes. */
|
|
1284
|
+
detachWebsiteKnowledge(agentId) {
|
|
1285
|
+
return this.sm.requestSupafoneApi("DELETE", `/api/v1/agents/${encodeURIComponent(agentId)}/knowledge/website`);
|
|
1286
|
+
}
|
|
1287
|
+
/** Ask the managed server whether this agent is ready to activate. */
|
|
1288
|
+
readiness(agentKey, opts = {}) {
|
|
1289
|
+
const q = new URLSearchParams();
|
|
1290
|
+
if (opts.agencyId)
|
|
1291
|
+
q.set("agency_id", opts.agencyId);
|
|
1292
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
1293
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}/readiness${suffix}`);
|
|
1294
|
+
}
|
|
1295
|
+
/** Activate a ready hosted agent. */
|
|
1296
|
+
activate(agentKey, opts = {}) {
|
|
1297
|
+
const q = new URLSearchParams();
|
|
1298
|
+
if (opts.agencyId)
|
|
1299
|
+
q.set("agency_id", opts.agencyId);
|
|
1300
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
1301
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}/activate${suffix}`);
|
|
1302
|
+
}
|
|
1303
|
+
/** Pause a hosted agent without deleting it. */
|
|
1304
|
+
pause(agentKey, opts = {}) {
|
|
1305
|
+
const q = new URLSearchParams();
|
|
1306
|
+
if (opts.agencyId)
|
|
1307
|
+
q.set("agency_id", opts.agencyId);
|
|
1308
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
1309
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}/pause${suffix}`);
|
|
1310
|
+
}
|
|
1311
|
+
/** Start the native provider-neutral browser voice session for this agent. */
|
|
1312
|
+
startWebRtcCall(agentId) {
|
|
1313
|
+
return this.sm.startWebRtcCall({ agentId });
|
|
1314
|
+
}
|
|
1315
|
+
/** Rebuild retrieval data from the agent's already saved knowledge sources. */
|
|
1316
|
+
reindexKnowledge(agentId) {
|
|
1317
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/agents/${encodeURIComponent(agentId)}/knowledge/reindex`);
|
|
1318
|
+
}
|
|
1319
|
+
/** Upload and index a txt, md, csv, or PDF knowledge document. */
|
|
1320
|
+
uploadKnowledgeDocument(agentId, file, filename) {
|
|
1321
|
+
return this.sm.requestSupafoneUpload(`/api/v1/agents/${encodeURIComponent(agentId)}/knowledge/upload`, file, filename);
|
|
1322
|
+
}
|
|
1323
|
+
/** Delete one document and rebuild the agent's managed corpus. */
|
|
1324
|
+
deleteKnowledgeDocument(agentId, documentId) {
|
|
1325
|
+
return this.sm.requestSupafoneApi("DELETE", `/api/v1/agents/${encodeURIComponent(agentId)}/knowledge/docs/${encodeURIComponent(documentId)}`);
|
|
1326
|
+
}
|
|
1327
|
+
/** Ask a grounded question against the same corpus used during calls. */
|
|
1328
|
+
chatKnowledge(agentId, message, history = []) {
|
|
1329
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/agents/${encodeURIComponent(agentId)}/knowledge-chat`, { message, history });
|
|
1330
|
+
}
|
|
1115
1331
|
/** Delete an agent. Optionally ask the backend to release assigned numbers. */
|
|
1116
1332
|
delete(agentKey, opts = {}) {
|
|
1117
1333
|
const q = new URLSearchParams();
|
|
@@ -1657,6 +1873,94 @@ class OptimizerNamespace {
|
|
|
1657
1873
|
return this.sm.request("GET", `/v1/optimizer/reports?agent=${encodeURIComponent(agent)}&limit=${limit}`);
|
|
1658
1874
|
}
|
|
1659
1875
|
}
|
|
1876
|
+
function outboundCallModePayload(config) {
|
|
1877
|
+
const enabled = outboundModeBoolean(config.enabled, false, "enabled");
|
|
1878
|
+
if (!enabled)
|
|
1879
|
+
return { version: 1, enabled: false };
|
|
1880
|
+
const observabilityInput = config.observability ?? {};
|
|
1881
|
+
if (typeof observabilityInput !== "object" || Array.isArray(observabilityInput)) {
|
|
1882
|
+
throw new SupafoneLabsError("outboundCallMode.observability must be an object");
|
|
1883
|
+
}
|
|
1884
|
+
if (observabilityInput.metadata !== undefined
|
|
1885
|
+
&& (typeof observabilityInput.metadata !== "object"
|
|
1886
|
+
|| observabilityInput.metadata === null
|
|
1887
|
+
|| Array.isArray(observabilityInput.metadata))) {
|
|
1888
|
+
throw new SupafoneLabsError("outboundCallMode.observability.metadata must be an object");
|
|
1889
|
+
}
|
|
1890
|
+
const observability = compact({
|
|
1891
|
+
enabled: outboundModeBoolean(observabilityInput.enabled, true, "observability.enabled"),
|
|
1892
|
+
include_trigger: outboundModeBoolean(observabilityInput.includeTrigger, true, "observability.includeTrigger"),
|
|
1893
|
+
include_transitions: outboundModeBoolean(observabilityInput.includeTransitions, true, "observability.includeTransitions"),
|
|
1894
|
+
include_termination_reason: outboundModeBoolean(observabilityInput.includeTerminationReason, true, "observability.includeTerminationReason"),
|
|
1895
|
+
metadata: observabilityInput.metadata,
|
|
1896
|
+
});
|
|
1897
|
+
const payload = {
|
|
1898
|
+
version: 1,
|
|
1899
|
+
enabled: true,
|
|
1900
|
+
initial_mode: "mission",
|
|
1901
|
+
ivr_mode: "dynamic",
|
|
1902
|
+
transport_scope: "provider_agnostic",
|
|
1903
|
+
capability_policy: "fail_closed",
|
|
1904
|
+
auto_detect: outboundModeBoolean(config.autoDetect, true, "autoDetect"),
|
|
1905
|
+
dtmf_tool_enabled: outboundModeBoolean(config.dtmfToolEnabled, true, "dtmfToolEnabled"),
|
|
1906
|
+
max_duration_seconds: outboundModeInteger(config.maxDurationSeconds, "maxDurationSeconds"),
|
|
1907
|
+
max_keypresses: outboundModeInteger(config.maxKeypresses, "maxKeypresses"),
|
|
1908
|
+
repeated_menu_limit: outboundModeInteger(config.repeatedMenuLimit, "repeatedMenuLimit"),
|
|
1909
|
+
no_progress_timeout_seconds: outboundModeInteger(config.noProgressTimeoutSeconds, "noProgressTimeoutSeconds"),
|
|
1910
|
+
human_detection_enabled: outboundModeBoolean(config.humanDetectionEnabled, true, "humanDetectionEnabled"),
|
|
1911
|
+
resume_on_human: outboundModeBoolean(config.resumeOnHuman, true, "resumeOnHuman"),
|
|
1912
|
+
observability,
|
|
1913
|
+
};
|
|
1914
|
+
payload.required_capabilities = outboundCallModeRequiredCapabilities(payload);
|
|
1915
|
+
return payload;
|
|
1916
|
+
}
|
|
1917
|
+
function outboundModeBoolean(value, fallback, key) {
|
|
1918
|
+
if (value === undefined)
|
|
1919
|
+
return fallback;
|
|
1920
|
+
if (typeof value !== "boolean") {
|
|
1921
|
+
throw new SupafoneLabsError(`outboundCallMode.${key} must be a boolean`);
|
|
1922
|
+
}
|
|
1923
|
+
return value;
|
|
1924
|
+
}
|
|
1925
|
+
function outboundModeInteger(value, key) {
|
|
1926
|
+
const bounds = OUTBOUND_CALL_MODE_BOUNDS[key];
|
|
1927
|
+
const normalized = value ?? bounds.default;
|
|
1928
|
+
if (!Number.isInteger(normalized)) {
|
|
1929
|
+
throw new SupafoneLabsError(`outboundCallMode.${key} must be an integer`);
|
|
1930
|
+
}
|
|
1931
|
+
if (normalized < bounds.min || normalized > bounds.max) {
|
|
1932
|
+
throw new SupafoneLabsError(`outboundCallMode.${key} must be between ${bounds.min} and ${bounds.max}`);
|
|
1933
|
+
}
|
|
1934
|
+
return normalized;
|
|
1935
|
+
}
|
|
1936
|
+
function outboundCallModeRequiredCapabilities(payload) {
|
|
1937
|
+
const required = ["state_persistence"];
|
|
1938
|
+
if (payload.dtmf_tool_enabled !== false)
|
|
1939
|
+
required.unshift("dtmf");
|
|
1940
|
+
if (payload.human_detection_enabled !== false || payload.resume_on_human !== false) {
|
|
1941
|
+
required.push("human_detection");
|
|
1942
|
+
}
|
|
1943
|
+
const observability = payload.observability;
|
|
1944
|
+
if (observability?.enabled === true)
|
|
1945
|
+
required.push("observability");
|
|
1946
|
+
return required;
|
|
1947
|
+
}
|
|
1948
|
+
function labsAgentMetadataPayload(input) {
|
|
1949
|
+
const metadata = { ...(input.metadata ?? {}) };
|
|
1950
|
+
const mode = input.outboundCallMode ?? input.outbound_call_mode;
|
|
1951
|
+
if (mode !== undefined)
|
|
1952
|
+
metadata.outbound_call_mode = outboundCallModePayload(mode);
|
|
1953
|
+
return Object.keys(metadata).length ? metadata : undefined;
|
|
1954
|
+
}
|
|
1955
|
+
function labsAgentUpdatePayload(input) {
|
|
1956
|
+
return labsAgentPayload(input);
|
|
1957
|
+
}
|
|
1958
|
+
function campaignSettingsPayload(settings, mode) {
|
|
1959
|
+
const payload = { ...(settings ?? {}) };
|
|
1960
|
+
if (mode !== undefined)
|
|
1961
|
+
payload.outbound_call_mode = outboundCallModePayload(mode);
|
|
1962
|
+
return payload;
|
|
1963
|
+
}
|
|
1660
1964
|
function labsAgentPayload(input) {
|
|
1661
1965
|
const fixedLanguage = input.preferred_language ?? input.preferredLanguage ?? input.language;
|
|
1662
1966
|
return compact({
|
|
@@ -1703,7 +2007,7 @@ function labsAgentPayload(input) {
|
|
|
1703
2007
|
ultravox: input.ultravox ? ultravoxPayload(input.ultravox) : undefined,
|
|
1704
2008
|
voice_watcher: input.voice_watcher ?? input.voiceWatcher,
|
|
1705
2009
|
voice_watcher_model: input.voice_watcher_model ?? input.voiceWatcherModel,
|
|
1706
|
-
metadata: input
|
|
2010
|
+
metadata: labsAgentMetadataPayload(input),
|
|
1707
2011
|
});
|
|
1708
2012
|
}
|
|
1709
2013
|
function hostedListQuery(opts) {
|
|
@@ -1934,6 +2238,9 @@ function voicePayload(input) {
|
|
|
1934
2238
|
function languageProfilesPayload(input) {
|
|
1935
2239
|
if (!Array.isArray(input))
|
|
1936
2240
|
return undefined;
|
|
2241
|
+
if (input.length > 4) {
|
|
2242
|
+
throw new SupafoneLabsError("languageProfiles supports at most four profiles");
|
|
2243
|
+
}
|
|
1937
2244
|
const profiles = input.map((profile) => compact({
|
|
1938
2245
|
language: profile.language,
|
|
1939
2246
|
language_hint: profile.language_hint ?? profile.languageHint,
|