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/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;
|
|
@@ -264,6 +387,33 @@ class SupafoneLabs {
|
|
|
264
387
|
clearTimeout(timer);
|
|
265
388
|
}
|
|
266
389
|
}
|
|
390
|
+
/** @internal Multipart upload to the Supafone product API with the Labs key. */
|
|
391
|
+
async requestSupafoneUpload(path, file, filename) {
|
|
392
|
+
const ctrl = new AbortController();
|
|
393
|
+
const timer = setTimeout(() => ctrl.abort(), Math.max(this.timeoutMs, 120_000));
|
|
394
|
+
try {
|
|
395
|
+
const safeFilename = filename.replace(/["\\\r\n]/g, "").slice(0, 120) || "document.txt";
|
|
396
|
+
const blob = file instanceof Blob ? file : new Blob([file]);
|
|
397
|
+
const form = new FormData();
|
|
398
|
+
form.append("file", blob, safeFilename);
|
|
399
|
+
const res = await fetch(this.supafoneApiBaseUrl + path, {
|
|
400
|
+
method: "POST",
|
|
401
|
+
signal: ctrl.signal,
|
|
402
|
+
headers: { Authorization: `Bearer ${this.supafoneApiKey}` },
|
|
403
|
+
body: form,
|
|
404
|
+
});
|
|
405
|
+
const text = await res.text();
|
|
406
|
+
const parsed = text ? safeJson(text) : {};
|
|
407
|
+
if (!res.ok) {
|
|
408
|
+
const detail = parsed?.detail ?? text ?? `HTTP ${res.status}`;
|
|
409
|
+
throw new SupafoneLabsError(`POST ${path}: ${detail}`, res.status, parsed);
|
|
410
|
+
}
|
|
411
|
+
return parsed;
|
|
412
|
+
}
|
|
413
|
+
finally {
|
|
414
|
+
clearTimeout(timer);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
267
417
|
/** @internal Authenticated binary request to the Supafone app API. */
|
|
268
418
|
async requestSupafoneBinary(path) {
|
|
269
419
|
const ctrl = new AbortController();
|
|
@@ -770,18 +920,26 @@ class CampaignsNamespace {
|
|
|
770
920
|
const query = opts.accountId ? `?${new URLSearchParams({ account_id: opts.accountId })}` : "";
|
|
771
921
|
return this.sm.requestAccountApi("GET", `/api/v1/campaigns${query}`);
|
|
772
922
|
}
|
|
773
|
-
create(opts = {}) {
|
|
774
|
-
|
|
923
|
+
async create(opts = {}) {
|
|
924
|
+
const created = await this.sm.requestAccountApi("POST", "/api/v1/campaigns", compact({
|
|
775
925
|
name: opts.name ?? "New campaign",
|
|
776
926
|
goal: opts.goal ?? "book",
|
|
777
927
|
agent_id: opts.agentId,
|
|
778
928
|
account_id: opts.accountId,
|
|
779
929
|
}));
|
|
930
|
+
const settings = campaignSettingsPayload(opts.settings, opts.outboundCallMode);
|
|
931
|
+
if (!Object.keys(settings).length)
|
|
932
|
+
return created;
|
|
933
|
+
if (!created.campaign?.id) {
|
|
934
|
+
throw new SupafoneLabsError("Campaign was created but no campaign id was returned; outbound call mode was not saved.");
|
|
935
|
+
}
|
|
936
|
+
return this.update(created.campaign.id, { settings });
|
|
780
937
|
}
|
|
781
938
|
get(campaignId) {
|
|
782
939
|
return this.sm.requestAccountApi("GET", `/api/v1/campaigns/${encodeURIComponent(campaignId)}`);
|
|
783
940
|
}
|
|
784
941
|
update(campaignId, input) {
|
|
942
|
+
const settings = campaignSettingsPayload(input.settings, input.outboundCallMode);
|
|
785
943
|
const payload = compact({
|
|
786
944
|
name: input.name,
|
|
787
945
|
goal: input.goal,
|
|
@@ -789,10 +947,10 @@ class CampaignsNamespace {
|
|
|
789
947
|
email_subject: input.emailSubject,
|
|
790
948
|
email_body: input.emailBody,
|
|
791
949
|
cadence: input.cadence,
|
|
792
|
-
settings:
|
|
950
|
+
settings: Object.keys(settings).length ? settings : undefined,
|
|
793
951
|
});
|
|
794
952
|
if (!Object.keys(payload).length) {
|
|
795
|
-
throw new SupafoneLabsError("Nothing to update — pass name, goal, agentId, emailSubject, emailBody, cadence, or
|
|
953
|
+
throw new SupafoneLabsError("Nothing to update — pass name, goal, agentId, emailSubject, emailBody, cadence, settings, or outboundCallMode");
|
|
796
954
|
}
|
|
797
955
|
return this.sm.requestAccountApi("PUT", `/api/v1/campaigns/${encodeURIComponent(campaignId)}`, payload);
|
|
798
956
|
}
|
|
@@ -1119,6 +1277,66 @@ class LabsAgentsNamespace {
|
|
|
1119
1277
|
const suffix = q.toString() ? `?${q}` : "";
|
|
1120
1278
|
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}${suffix}`);
|
|
1121
1279
|
}
|
|
1280
|
+
/** Update an existing durable agent without changing omitted fields. */
|
|
1281
|
+
update(agentKey, input, opts = {}) {
|
|
1282
|
+
const q = new URLSearchParams();
|
|
1283
|
+
if (opts.agencyId)
|
|
1284
|
+
q.set("agency_id", opts.agencyId);
|
|
1285
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
1286
|
+
return this.sm.requestSupafoneApi("PATCH", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}${suffix}`, labsAgentUpdatePayload(input));
|
|
1287
|
+
}
|
|
1288
|
+
/** Scrape a website and rebuild this agent's managed knowledge corpus. */
|
|
1289
|
+
syncKnowledge(agentId, input = {}) {
|
|
1290
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/agents/${encodeURIComponent(agentId)}/sync-knowledge`, { url: input.url ?? input.website_url ?? input.websiteUrl ?? "" });
|
|
1291
|
+
}
|
|
1292
|
+
/** Remove website knowledge while preserving uploaded documents and notes. */
|
|
1293
|
+
detachWebsiteKnowledge(agentId) {
|
|
1294
|
+
return this.sm.requestSupafoneApi("DELETE", `/api/v1/agents/${encodeURIComponent(agentId)}/knowledge/website`);
|
|
1295
|
+
}
|
|
1296
|
+
/** Ask the managed server whether this agent is ready to activate. */
|
|
1297
|
+
readiness(agentKey, opts = {}) {
|
|
1298
|
+
const q = new URLSearchParams();
|
|
1299
|
+
if (opts.agencyId)
|
|
1300
|
+
q.set("agency_id", opts.agencyId);
|
|
1301
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
1302
|
+
return this.sm.requestSupafoneApi("GET", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}/readiness${suffix}`);
|
|
1303
|
+
}
|
|
1304
|
+
/** Activate a ready hosted agent. */
|
|
1305
|
+
activate(agentKey, opts = {}) {
|
|
1306
|
+
const q = new URLSearchParams();
|
|
1307
|
+
if (opts.agencyId)
|
|
1308
|
+
q.set("agency_id", opts.agencyId);
|
|
1309
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
1310
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}/activate${suffix}`);
|
|
1311
|
+
}
|
|
1312
|
+
/** Pause a hosted agent without deleting it. */
|
|
1313
|
+
pause(agentKey, opts = {}) {
|
|
1314
|
+
const q = new URLSearchParams();
|
|
1315
|
+
if (opts.agencyId)
|
|
1316
|
+
q.set("agency_id", opts.agencyId);
|
|
1317
|
+
const suffix = q.toString() ? `?${q}` : "";
|
|
1318
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/labs/agents/${encodeURIComponent(agentKey)}/pause${suffix}`);
|
|
1319
|
+
}
|
|
1320
|
+
/** Start the native provider-neutral browser voice session for this agent. */
|
|
1321
|
+
startWebRtcCall(agentId) {
|
|
1322
|
+
return this.sm.startWebRtcCall({ agentId });
|
|
1323
|
+
}
|
|
1324
|
+
/** Rebuild retrieval data from the agent's already saved knowledge sources. */
|
|
1325
|
+
reindexKnowledge(agentId) {
|
|
1326
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/agents/${encodeURIComponent(agentId)}/knowledge/reindex`);
|
|
1327
|
+
}
|
|
1328
|
+
/** Upload and index a txt, md, csv, or PDF knowledge document. */
|
|
1329
|
+
uploadKnowledgeDocument(agentId, file, filename) {
|
|
1330
|
+
return this.sm.requestSupafoneUpload(`/api/v1/agents/${encodeURIComponent(agentId)}/knowledge/upload`, file, filename);
|
|
1331
|
+
}
|
|
1332
|
+
/** Delete one document and rebuild the agent's managed corpus. */
|
|
1333
|
+
deleteKnowledgeDocument(agentId, documentId) {
|
|
1334
|
+
return this.sm.requestSupafoneApi("DELETE", `/api/v1/agents/${encodeURIComponent(agentId)}/knowledge/docs/${encodeURIComponent(documentId)}`);
|
|
1335
|
+
}
|
|
1336
|
+
/** Ask a grounded question against the same corpus used during calls. */
|
|
1337
|
+
chatKnowledge(agentId, message, history = []) {
|
|
1338
|
+
return this.sm.requestSupafoneApi("POST", `/api/v1/agents/${encodeURIComponent(agentId)}/knowledge-chat`, { message, history });
|
|
1339
|
+
}
|
|
1122
1340
|
/** Delete an agent. Optionally ask the backend to release assigned numbers. */
|
|
1123
1341
|
delete(agentKey, opts = {}) {
|
|
1124
1342
|
const q = new URLSearchParams();
|
|
@@ -1664,6 +1882,94 @@ class OptimizerNamespace {
|
|
|
1664
1882
|
return this.sm.request("GET", `/v1/optimizer/reports?agent=${encodeURIComponent(agent)}&limit=${limit}`);
|
|
1665
1883
|
}
|
|
1666
1884
|
}
|
|
1885
|
+
function outboundCallModePayload(config) {
|
|
1886
|
+
const enabled = outboundModeBoolean(config.enabled, false, "enabled");
|
|
1887
|
+
if (!enabled)
|
|
1888
|
+
return { version: 1, enabled: false };
|
|
1889
|
+
const observabilityInput = config.observability ?? {};
|
|
1890
|
+
if (typeof observabilityInput !== "object" || Array.isArray(observabilityInput)) {
|
|
1891
|
+
throw new SupafoneLabsError("outboundCallMode.observability must be an object");
|
|
1892
|
+
}
|
|
1893
|
+
if (observabilityInput.metadata !== undefined
|
|
1894
|
+
&& (typeof observabilityInput.metadata !== "object"
|
|
1895
|
+
|| observabilityInput.metadata === null
|
|
1896
|
+
|| Array.isArray(observabilityInput.metadata))) {
|
|
1897
|
+
throw new SupafoneLabsError("outboundCallMode.observability.metadata must be an object");
|
|
1898
|
+
}
|
|
1899
|
+
const observability = compact({
|
|
1900
|
+
enabled: outboundModeBoolean(observabilityInput.enabled, true, "observability.enabled"),
|
|
1901
|
+
include_trigger: outboundModeBoolean(observabilityInput.includeTrigger, true, "observability.includeTrigger"),
|
|
1902
|
+
include_transitions: outboundModeBoolean(observabilityInput.includeTransitions, true, "observability.includeTransitions"),
|
|
1903
|
+
include_termination_reason: outboundModeBoolean(observabilityInput.includeTerminationReason, true, "observability.includeTerminationReason"),
|
|
1904
|
+
metadata: observabilityInput.metadata,
|
|
1905
|
+
});
|
|
1906
|
+
const payload = {
|
|
1907
|
+
version: 1,
|
|
1908
|
+
enabled: true,
|
|
1909
|
+
initial_mode: "mission",
|
|
1910
|
+
ivr_mode: "dynamic",
|
|
1911
|
+
transport_scope: "provider_agnostic",
|
|
1912
|
+
capability_policy: "fail_closed",
|
|
1913
|
+
auto_detect: outboundModeBoolean(config.autoDetect, true, "autoDetect"),
|
|
1914
|
+
dtmf_tool_enabled: outboundModeBoolean(config.dtmfToolEnabled, true, "dtmfToolEnabled"),
|
|
1915
|
+
max_duration_seconds: outboundModeInteger(config.maxDurationSeconds, "maxDurationSeconds"),
|
|
1916
|
+
max_keypresses: outboundModeInteger(config.maxKeypresses, "maxKeypresses"),
|
|
1917
|
+
repeated_menu_limit: outboundModeInteger(config.repeatedMenuLimit, "repeatedMenuLimit"),
|
|
1918
|
+
no_progress_timeout_seconds: outboundModeInteger(config.noProgressTimeoutSeconds, "noProgressTimeoutSeconds"),
|
|
1919
|
+
human_detection_enabled: outboundModeBoolean(config.humanDetectionEnabled, true, "humanDetectionEnabled"),
|
|
1920
|
+
resume_on_human: outboundModeBoolean(config.resumeOnHuman, true, "resumeOnHuman"),
|
|
1921
|
+
observability,
|
|
1922
|
+
};
|
|
1923
|
+
payload.required_capabilities = outboundCallModeRequiredCapabilities(payload);
|
|
1924
|
+
return payload;
|
|
1925
|
+
}
|
|
1926
|
+
function outboundModeBoolean(value, fallback, key) {
|
|
1927
|
+
if (value === undefined)
|
|
1928
|
+
return fallback;
|
|
1929
|
+
if (typeof value !== "boolean") {
|
|
1930
|
+
throw new SupafoneLabsError(`outboundCallMode.${key} must be a boolean`);
|
|
1931
|
+
}
|
|
1932
|
+
return value;
|
|
1933
|
+
}
|
|
1934
|
+
function outboundModeInteger(value, key) {
|
|
1935
|
+
const bounds = exports.OUTBOUND_CALL_MODE_BOUNDS[key];
|
|
1936
|
+
const normalized = value ?? bounds.default;
|
|
1937
|
+
if (!Number.isInteger(normalized)) {
|
|
1938
|
+
throw new SupafoneLabsError(`outboundCallMode.${key} must be an integer`);
|
|
1939
|
+
}
|
|
1940
|
+
if (normalized < bounds.min || normalized > bounds.max) {
|
|
1941
|
+
throw new SupafoneLabsError(`outboundCallMode.${key} must be between ${bounds.min} and ${bounds.max}`);
|
|
1942
|
+
}
|
|
1943
|
+
return normalized;
|
|
1944
|
+
}
|
|
1945
|
+
function outboundCallModeRequiredCapabilities(payload) {
|
|
1946
|
+
const required = ["state_persistence"];
|
|
1947
|
+
if (payload.dtmf_tool_enabled !== false)
|
|
1948
|
+
required.unshift("dtmf");
|
|
1949
|
+
if (payload.human_detection_enabled !== false || payload.resume_on_human !== false) {
|
|
1950
|
+
required.push("human_detection");
|
|
1951
|
+
}
|
|
1952
|
+
const observability = payload.observability;
|
|
1953
|
+
if (observability?.enabled === true)
|
|
1954
|
+
required.push("observability");
|
|
1955
|
+
return required;
|
|
1956
|
+
}
|
|
1957
|
+
function labsAgentMetadataPayload(input) {
|
|
1958
|
+
const metadata = { ...(input.metadata ?? {}) };
|
|
1959
|
+
const mode = input.outboundCallMode ?? input.outbound_call_mode;
|
|
1960
|
+
if (mode !== undefined)
|
|
1961
|
+
metadata.outbound_call_mode = outboundCallModePayload(mode);
|
|
1962
|
+
return Object.keys(metadata).length ? metadata : undefined;
|
|
1963
|
+
}
|
|
1964
|
+
function labsAgentUpdatePayload(input) {
|
|
1965
|
+
return labsAgentPayload(input);
|
|
1966
|
+
}
|
|
1967
|
+
function campaignSettingsPayload(settings, mode) {
|
|
1968
|
+
const payload = { ...(settings ?? {}) };
|
|
1969
|
+
if (mode !== undefined)
|
|
1970
|
+
payload.outbound_call_mode = outboundCallModePayload(mode);
|
|
1971
|
+
return payload;
|
|
1972
|
+
}
|
|
1667
1973
|
function labsAgentPayload(input) {
|
|
1668
1974
|
const fixedLanguage = input.preferred_language ?? input.preferredLanguage ?? input.language;
|
|
1669
1975
|
return compact({
|
|
@@ -1710,7 +2016,7 @@ function labsAgentPayload(input) {
|
|
|
1710
2016
|
ultravox: input.ultravox ? ultravoxPayload(input.ultravox) : undefined,
|
|
1711
2017
|
voice_watcher: input.voice_watcher ?? input.voiceWatcher,
|
|
1712
2018
|
voice_watcher_model: input.voice_watcher_model ?? input.voiceWatcherModel,
|
|
1713
|
-
metadata: input
|
|
2019
|
+
metadata: labsAgentMetadataPayload(input),
|
|
1714
2020
|
});
|
|
1715
2021
|
}
|
|
1716
2022
|
function hostedListQuery(opts) {
|
|
@@ -1941,6 +2247,9 @@ function voicePayload(input) {
|
|
|
1941
2247
|
function languageProfilesPayload(input) {
|
|
1942
2248
|
if (!Array.isArray(input))
|
|
1943
2249
|
return undefined;
|
|
2250
|
+
if (input.length > 4) {
|
|
2251
|
+
throw new SupafoneLabsError("languageProfiles supports at most four profiles");
|
|
2252
|
+
}
|
|
1944
2253
|
const profiles = input.map((profile) => compact({
|
|
1945
2254
|
language: profile.language,
|
|
1946
2255
|
language_hint: profile.language_hint ?? profile.languageHint,
|
package/dist/index.d.ts
CHANGED
|
@@ -257,16 +257,37 @@ export interface LabsVoiceProviderTraits {
|
|
|
257
257
|
is_public?: boolean | null;
|
|
258
258
|
is_owner?: boolean | null;
|
|
259
259
|
}
|
|
260
|
+
/** Stable provider presentation metadata returned by the hosted catalog. */
|
|
261
|
+
export interface LabsVoiceProviderBrand {
|
|
262
|
+
/** Canonical provider key used for filtering and grouping. */
|
|
263
|
+
key: string;
|
|
264
|
+
/** Human-readable provider/company name. */
|
|
265
|
+
name: string;
|
|
266
|
+
/** Stable local asset slug when Supafone has an official provider mark. */
|
|
267
|
+
logo_slug?: string | null;
|
|
268
|
+
/** Absolute, browser-ready provider logo URL. */
|
|
269
|
+
logo_url?: string | null;
|
|
270
|
+
/** Provider's official product or company website. */
|
|
271
|
+
website_url?: string | null;
|
|
272
|
+
}
|
|
260
273
|
export interface LabsVoiceCatalogItem {
|
|
261
274
|
/** Stable runtime reference, including provider prefix when required. */
|
|
262
275
|
id: string;
|
|
263
276
|
voice_id: string;
|
|
264
277
|
/** Human-readable provider display name. */
|
|
265
278
|
provider: string;
|
|
279
|
+
/** Canonical brand display name for the synthesis provider. */
|
|
280
|
+
provider_label?: string;
|
|
281
|
+
/** Browser-ready official provider mark, or null for an unknown provider. */
|
|
282
|
+
provider_logo_url?: string | null;
|
|
283
|
+
/** Branding for the provider that renders the voice. */
|
|
284
|
+
provider_brand?: LabsVoiceProviderBrand;
|
|
266
285
|
/** Canonical SDK/runtime provider key. */
|
|
267
286
|
provider_key: string;
|
|
268
287
|
/** Runtime adapter used to place the voice on an Ultravox call. */
|
|
269
288
|
runtime_provider_key: string;
|
|
289
|
+
/** Branding for the realtime runtime, which can differ from synthesis. */
|
|
290
|
+
runtime_provider_brand?: LabsVoiceProviderBrand;
|
|
270
291
|
/** Actual TTS engine behind the voice (can differ for Ultravox built-ins). */
|
|
271
292
|
synthesis_provider_key: string;
|
|
272
293
|
/** Raw identifier used by the provider API. */
|
|
@@ -622,6 +643,95 @@ export interface LabsUltravoxRuntime {
|
|
|
622
643
|
sip?: LabsCustomSipConfig;
|
|
623
644
|
[extra: string]: unknown;
|
|
624
645
|
}
|
|
646
|
+
export interface OutboundCallModeObservability {
|
|
647
|
+
enabled?: boolean;
|
|
648
|
+
includeTrigger?: boolean;
|
|
649
|
+
includeTransitions?: boolean;
|
|
650
|
+
includeTerminationReason?: boolean;
|
|
651
|
+
metadata?: Record<string, unknown>;
|
|
652
|
+
}
|
|
653
|
+
/** Public, provider-neutral controls for temporary outbound IVR navigation. */
|
|
654
|
+
export interface OutboundCallModeConfig {
|
|
655
|
+
enabled?: boolean;
|
|
656
|
+
autoDetect?: boolean;
|
|
657
|
+
dtmfToolEnabled?: boolean;
|
|
658
|
+
maxDurationSeconds?: number;
|
|
659
|
+
maxKeypresses?: number;
|
|
660
|
+
repeatedMenuLimit?: number;
|
|
661
|
+
noProgressTimeoutSeconds?: number;
|
|
662
|
+
humanDetectionEnabled?: boolean;
|
|
663
|
+
resumeOnHuman?: boolean;
|
|
664
|
+
observability?: OutboundCallModeObservability;
|
|
665
|
+
}
|
|
666
|
+
/** Capabilities positively reported by the active telephony/runtime adapter. */
|
|
667
|
+
export interface OutboundCallModeCapabilities {
|
|
668
|
+
provider?: string;
|
|
669
|
+
dtmf?: boolean;
|
|
670
|
+
statePersistence?: boolean;
|
|
671
|
+
humanDetection?: boolean;
|
|
672
|
+
observability?: boolean;
|
|
673
|
+
metadata?: Record<string, unknown>;
|
|
674
|
+
}
|
|
675
|
+
export interface OutboundCallModeReadiness {
|
|
676
|
+
ready: boolean;
|
|
677
|
+
status: "disabled" | "ready" | "unknown" | "unsupported";
|
|
678
|
+
provider: string;
|
|
679
|
+
transportFamily: string;
|
|
680
|
+
requiredCapabilities: string[];
|
|
681
|
+
missingCapabilities: string[];
|
|
682
|
+
unsupportedCapabilities: string[];
|
|
683
|
+
reasons: string[];
|
|
684
|
+
}
|
|
685
|
+
export declare const OUTBOUND_CALL_MODE_BOUNDS: {
|
|
686
|
+
readonly maxDurationSeconds: {
|
|
687
|
+
readonly default: 180;
|
|
688
|
+
readonly min: 30;
|
|
689
|
+
readonly max: 900;
|
|
690
|
+
};
|
|
691
|
+
readonly maxKeypresses: {
|
|
692
|
+
readonly default: 12;
|
|
693
|
+
readonly min: 1;
|
|
694
|
+
readonly max: 64;
|
|
695
|
+
};
|
|
696
|
+
readonly repeatedMenuLimit: {
|
|
697
|
+
readonly default: 3;
|
|
698
|
+
readonly min: 1;
|
|
699
|
+
readonly max: 10;
|
|
700
|
+
};
|
|
701
|
+
readonly noProgressTimeoutSeconds: {
|
|
702
|
+
readonly default: 30;
|
|
703
|
+
readonly min: 5;
|
|
704
|
+
readonly max: 120;
|
|
705
|
+
};
|
|
706
|
+
};
|
|
707
|
+
export declare const OUTBOUND_CALL_MODE_DEFAULTS: {
|
|
708
|
+
readonly enabled: false;
|
|
709
|
+
readonly autoDetect: true;
|
|
710
|
+
readonly dtmfToolEnabled: true;
|
|
711
|
+
readonly maxDurationSeconds: 180;
|
|
712
|
+
readonly maxKeypresses: 12;
|
|
713
|
+
readonly repeatedMenuLimit: 3;
|
|
714
|
+
readonly noProgressTimeoutSeconds: 30;
|
|
715
|
+
readonly humanDetectionEnabled: true;
|
|
716
|
+
readonly resumeOnHuman: true;
|
|
717
|
+
readonly observability: {
|
|
718
|
+
readonly enabled: true;
|
|
719
|
+
readonly includeTrigger: true;
|
|
720
|
+
readonly includeTransitions: true;
|
|
721
|
+
readonly includeTerminationReason: true;
|
|
722
|
+
};
|
|
723
|
+
};
|
|
724
|
+
export interface OutboundCallModeProviderProfile {
|
|
725
|
+
contractSupported: boolean;
|
|
726
|
+
execution: "adapter_runtime_dependent";
|
|
727
|
+
capabilityPolicy: "fail_closed";
|
|
728
|
+
transportFamily: string;
|
|
729
|
+
providers: readonly string[];
|
|
730
|
+
requiredCapabilities: readonly string[];
|
|
731
|
+
}
|
|
732
|
+
export declare const OUTBOUND_CALL_MODE_PROVIDER_MATRIX: Record<string, OutboundCallModeProviderProfile>;
|
|
733
|
+
export declare function outboundCallModeProviderProfile(provider: string): OutboundCallModeProviderProfile;
|
|
734
|
+
export declare function outboundCallModeReadiness(config?: OutboundCallModeConfig, capabilities?: OutboundCallModeCapabilities): OutboundCallModeReadiness;
|
|
625
735
|
export interface CreateLabsAgentRequest {
|
|
626
736
|
agencyId?: string;
|
|
627
737
|
agency_id?: string;
|
|
@@ -706,6 +816,9 @@ export interface CreateLabsAgentRequest {
|
|
|
706
816
|
voice_watcher?: boolean;
|
|
707
817
|
voiceWatcherModel?: string;
|
|
708
818
|
voice_watcher_model?: string;
|
|
819
|
+
/** Opt-in outbound IVR mode. Execution depends on adapter-reported readiness. */
|
|
820
|
+
outboundCallMode?: OutboundCallModeConfig;
|
|
821
|
+
outbound_call_mode?: OutboundCallModeConfig;
|
|
709
822
|
metadata?: Record<string, unknown>;
|
|
710
823
|
}
|
|
711
824
|
export interface ListLabsAgentsOptions {
|
|
@@ -728,6 +841,41 @@ export interface DeleteLabsAgentResponse {
|
|
|
728
841
|
released_numbers?: unknown[];
|
|
729
842
|
[extra: string]: unknown;
|
|
730
843
|
}
|
|
844
|
+
export type LabsAgentLifecycle = "draft" | "active" | "paused" | "archived" | string;
|
|
845
|
+
export interface LabsAgentReadiness {
|
|
846
|
+
ready: boolean;
|
|
847
|
+
status?: string;
|
|
848
|
+
checks?: Array<Record<string, unknown>>;
|
|
849
|
+
[extra: string]: unknown;
|
|
850
|
+
}
|
|
851
|
+
export type UpdateLabsAgentRequest = Partial<CreateLabsAgentRequest>;
|
|
852
|
+
export interface LabsAgentLifecycleResponse {
|
|
853
|
+
success?: boolean;
|
|
854
|
+
agent?: LabsAgentResponse;
|
|
855
|
+
lifecycle?: LabsAgentLifecycle;
|
|
856
|
+
readiness?: LabsAgentReadiness;
|
|
857
|
+
[extra: string]: unknown;
|
|
858
|
+
}
|
|
859
|
+
export interface LabsKnowledgeSyncRequest {
|
|
860
|
+
websiteUrl?: string;
|
|
861
|
+
website_url?: string;
|
|
862
|
+
url?: string;
|
|
863
|
+
}
|
|
864
|
+
export interface LabsKnowledgeSyncResponse {
|
|
865
|
+
agent: LabsAgentResponse;
|
|
866
|
+
chunks_indexed?: number;
|
|
867
|
+
scraped_ok?: boolean;
|
|
868
|
+
last_error?: string | null;
|
|
869
|
+
website_detached?: boolean;
|
|
870
|
+
}
|
|
871
|
+
export interface LabsKnowledgeChatTurn {
|
|
872
|
+
role: "user" | "assistant";
|
|
873
|
+
content: string;
|
|
874
|
+
}
|
|
875
|
+
export interface LabsKnowledgeChatResponse {
|
|
876
|
+
reply: string;
|
|
877
|
+
sources: string[];
|
|
878
|
+
}
|
|
731
879
|
export interface LabsCapabilitiesResponse {
|
|
732
880
|
product: string;
|
|
733
881
|
api_namespace: string;
|
|
@@ -778,7 +926,10 @@ export interface LabsVoiceListResponse {
|
|
|
778
926
|
configured: boolean;
|
|
779
927
|
voice_count: number;
|
|
780
928
|
error?: string | null;
|
|
929
|
+
brand?: LabsVoiceProviderBrand;
|
|
781
930
|
}>;
|
|
931
|
+
/** Distinct synthesis-provider brands represented in the complete result. */
|
|
932
|
+
provider_brands?: LabsVoiceProviderBrand[];
|
|
782
933
|
provider_accounts?: Record<string, unknown>;
|
|
783
934
|
errors?: Record<string, string>;
|
|
784
935
|
catalog?: {
|
|
@@ -1068,6 +1219,8 @@ export interface LabsAgentResponse {
|
|
|
1068
1219
|
display_name?: string;
|
|
1069
1220
|
runtime_mode?: string;
|
|
1070
1221
|
preset_key?: string;
|
|
1222
|
+
status?: string;
|
|
1223
|
+
lifecycle?: LabsAgentLifecycle;
|
|
1071
1224
|
profile?: Record<string, unknown>;
|
|
1072
1225
|
runtime?: Record<string, unknown>;
|
|
1073
1226
|
[extra: string]: unknown;
|
|
@@ -1100,6 +1253,9 @@ export interface ListLabsAgentsResponse {
|
|
|
1100
1253
|
}
|
|
1101
1254
|
export interface GetLabsAgentResponse {
|
|
1102
1255
|
agent: LabsAgentResponse;
|
|
1256
|
+
runtime?: Record<string, unknown>;
|
|
1257
|
+
widget?: Record<string, unknown>;
|
|
1258
|
+
readiness?: LabsAgentReadiness;
|
|
1103
1259
|
}
|
|
1104
1260
|
export interface STTResult {
|
|
1105
1261
|
transcript: string;
|
|
@@ -1394,6 +1550,8 @@ export declare class SupafoneLabs {
|
|
|
1394
1550
|
request<T>(method: string, path: string, body?: unknown, useSession?: boolean): Promise<T>;
|
|
1395
1551
|
/** @internal Authenticated JSON request to the Supafone app API (`/api/v1/labs/*`). */
|
|
1396
1552
|
requestSupafoneApi<T>(method: string, path: string, body?: unknown): Promise<T>;
|
|
1553
|
+
/** @internal Multipart upload to the Supafone product API with the Labs key. */
|
|
1554
|
+
requestSupafoneUpload<T>(path: string, file: Uint8Array | ArrayBuffer | Blob, filename: string): Promise<T>;
|
|
1397
1555
|
/** @internal Authenticated binary request to the Supafone app API. */
|
|
1398
1556
|
requestSupafoneBinary(path: string): Promise<LabsVoicePreview>;
|
|
1399
1557
|
/**
|
|
@@ -1625,6 +1783,14 @@ export interface CampaignLiveView {
|
|
|
1625
1783
|
portal_url: string;
|
|
1626
1784
|
stats?: Record<string, unknown> | null;
|
|
1627
1785
|
}
|
|
1786
|
+
export interface CampaignCreateInput {
|
|
1787
|
+
name?: string;
|
|
1788
|
+
goal?: string;
|
|
1789
|
+
agentId?: string;
|
|
1790
|
+
accountId?: string;
|
|
1791
|
+
settings?: Record<string, unknown>;
|
|
1792
|
+
outboundCallMode?: OutboundCallModeConfig;
|
|
1793
|
+
}
|
|
1628
1794
|
export interface CampaignUpdateInput {
|
|
1629
1795
|
name?: string;
|
|
1630
1796
|
goal?: string;
|
|
@@ -1636,6 +1802,7 @@ export interface CampaignUpdateInput {
|
|
|
1636
1802
|
delay_hours: number;
|
|
1637
1803
|
}[];
|
|
1638
1804
|
settings?: Record<string, unknown>;
|
|
1805
|
+
outboundCallMode?: OutboundCallModeConfig;
|
|
1639
1806
|
}
|
|
1640
1807
|
export interface BrandScanResult {
|
|
1641
1808
|
url: string;
|
|
@@ -1699,12 +1866,7 @@ declare class CampaignsNamespace {
|
|
|
1699
1866
|
}): Promise<{
|
|
1700
1867
|
campaigns: CampaignSummary[];
|
|
1701
1868
|
}>;
|
|
1702
|
-
create(opts?: {
|
|
1703
|
-
name?: string;
|
|
1704
|
-
goal?: string;
|
|
1705
|
-
agentId?: string;
|
|
1706
|
-
accountId?: string;
|
|
1707
|
-
}): Promise<{
|
|
1869
|
+
create(opts?: CampaignCreateInput): Promise<{
|
|
1708
1870
|
campaign: CampaignSummary;
|
|
1709
1871
|
}>;
|
|
1710
1872
|
get(campaignId: string): Promise<{
|
|
@@ -1912,6 +2074,32 @@ declare class LabsAgentsNamespace {
|
|
|
1912
2074
|
list(opts?: ListLabsAgentsOptions): Promise<ListLabsAgentsResponse>;
|
|
1913
2075
|
/** Fetch one durable agent by key. */
|
|
1914
2076
|
get(agentKey: string, opts?: GetLabsAgentOptions): Promise<GetLabsAgentResponse>;
|
|
2077
|
+
/** Update an existing durable agent without changing omitted fields. */
|
|
2078
|
+
update(agentKey: string, input: UpdateLabsAgentRequest, opts?: GetLabsAgentOptions): Promise<LabsAgentLifecycleResponse>;
|
|
2079
|
+
/** Scrape a website and rebuild this agent's managed knowledge corpus. */
|
|
2080
|
+
syncKnowledge(agentId: string, input?: LabsKnowledgeSyncRequest): Promise<LabsKnowledgeSyncResponse>;
|
|
2081
|
+
/** Remove website knowledge while preserving uploaded documents and notes. */
|
|
2082
|
+
detachWebsiteKnowledge(agentId: string): Promise<LabsKnowledgeSyncResponse>;
|
|
2083
|
+
/** Ask the managed server whether this agent is ready to activate. */
|
|
2084
|
+
readiness(agentKey: string, opts?: GetLabsAgentOptions): Promise<LabsAgentReadiness>;
|
|
2085
|
+
/** Activate a ready hosted agent. */
|
|
2086
|
+
activate(agentKey: string, opts?: GetLabsAgentOptions): Promise<LabsAgentLifecycleResponse>;
|
|
2087
|
+
/** Pause a hosted agent without deleting it. */
|
|
2088
|
+
pause(agentKey: string, opts?: GetLabsAgentOptions): Promise<LabsAgentLifecycleResponse>;
|
|
2089
|
+
/** Start the native provider-neutral browser voice session for this agent. */
|
|
2090
|
+
startWebRtcCall(agentId: string): Promise<WebRtcCallResult>;
|
|
2091
|
+
/** Rebuild retrieval data from the agent's already saved knowledge sources. */
|
|
2092
|
+
reindexKnowledge(agentId: string): Promise<Record<string, unknown>>;
|
|
2093
|
+
/** Upload and index a txt, md, csv, or PDF knowledge document. */
|
|
2094
|
+
uploadKnowledgeDocument(agentId: string, file: Uint8Array | ArrayBuffer | Blob, filename: string): Promise<{
|
|
2095
|
+
agent: LabsAgentResponse;
|
|
2096
|
+
}>;
|
|
2097
|
+
/** Delete one document and rebuild the agent's managed corpus. */
|
|
2098
|
+
deleteKnowledgeDocument(agentId: string, documentId: string): Promise<{
|
|
2099
|
+
agent: LabsAgentResponse;
|
|
2100
|
+
}>;
|
|
2101
|
+
/** Ask a grounded question against the same corpus used during calls. */
|
|
2102
|
+
chatKnowledge(agentId: string, message: string, history?: LabsKnowledgeChatTurn[]): Promise<LabsKnowledgeChatResponse>;
|
|
1915
2103
|
/** Delete an agent. Optionally ask the backend to release assigned numbers. */
|
|
1916
2104
|
delete(agentKey: string, opts?: DeleteLabsAgentOptions): Promise<DeleteLabsAgentResponse>;
|
|
1917
2105
|
}
|