@shogo-ai/voice 1.4.1
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/LICENSE +21 -0
- package/README.md +30 -0
- package/dist/chunk-D2EVSUZX.js +27 -0
- package/dist/chunk-Q3TJAJ54.js +165 -0
- package/dist/chunk-QHDIEE7O.js +453 -0
- package/dist/chunk-S3S2RNO6.js +671 -0
- package/dist/chunk-ZGYHDC5G.js +423 -0
- package/dist/index.cjs +1316 -0
- package/dist/index.d.cts +432 -0
- package/dist/index.d.ts +432 -0
- package/dist/index.js +735 -0
- package/dist/native/index.cjs +1667 -0
- package/dist/native/index.d.cts +285 -0
- package/dist/native/index.d.ts +285 -0
- package/dist/native/index.js +949 -0
- package/dist/organicParticlesShaders-DIva0vBO.d.cts +693 -0
- package/dist/organicParticlesShaders-DIva0vBO.d.ts +693 -0
- package/dist/react/index.cjs +1670 -0
- package/dist/react/index.d.cts +423 -0
- package/dist/react/index.d.ts +423 -0
- package/dist/react/index.js +952 -0
- package/dist/route/agent.cjs +981 -0
- package/dist/route/agent.d.cts +20 -0
- package/dist/route/agent.d.ts +20 -0
- package/dist/route/agent.js +17 -0
- package/dist/route/audio-tags.cjs +975 -0
- package/dist/route/audio-tags.d.cts +16 -0
- package/dist/route/audio-tags.d.ts +16 -0
- package/dist/route/audio-tags.js +13 -0
- package/dist/route/index.cjs +987 -0
- package/dist/route/index.d.cts +115 -0
- package/dist/route/index.d.ts +115 -0
- package/dist/route/index.js +12 -0
- package/dist/route/signed-url.cjs +975 -0
- package/dist/route/signed-url.d.cts +12 -0
- package/dist/route/signed-url.d.ts +12 -0
- package/dist/route/signed-url.js +13 -0
- package/dist/route/tts-preview.cjs +975 -0
- package/dist/route/tts-preview.d.cts +17 -0
- package/dist/route/tts-preview.d.ts +17 -0
- package/dist/route/tts-preview.js +13 -0
- package/dist/server.cjs +999 -0
- package/dist/server.d.cts +88 -0
- package/dist/server.d.ts +88 -0
- package/dist/server.js +11 -0
- package/dist/types-BKJtOUbE.d.cts +352 -0
- package/dist/types-BKJtOUbE.d.ts +352 -0
- package/package.json +148 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CONVAI_SUPPORTED_TTS_MODELS,
|
|
3
|
+
CONVAI_TTS_MODEL_FALLBACK,
|
|
4
|
+
DEFAULT_ELEVENLABS_BASE_URL,
|
|
5
|
+
DEFAULT_MEMORY_BLOCK,
|
|
6
|
+
ElevenLabsApiError,
|
|
7
|
+
ElevenLabsClient,
|
|
8
|
+
MEMORY_CLIENT_TOOLS,
|
|
9
|
+
composeAgentPrompt,
|
|
10
|
+
extractBasePrompt,
|
|
11
|
+
resolveConvaiTtsModel,
|
|
12
|
+
stripMemoryBlock
|
|
13
|
+
} from "./chunk-ZGYHDC5G.js";
|
|
14
|
+
import {
|
|
15
|
+
AUDIO_TAGS,
|
|
16
|
+
AUDIO_TAG_GROUPS,
|
|
17
|
+
DEFAULT_ALLOWED_TAGS,
|
|
18
|
+
DEFAULT_VOICE_SETTINGS,
|
|
19
|
+
EXPRESSIVITY_BLOCK_CLOSE,
|
|
20
|
+
EXPRESSIVITY_BLOCK_OPEN,
|
|
21
|
+
EXPRESSIVITY_OPTIONS,
|
|
22
|
+
buildPreviewLine,
|
|
23
|
+
composeExpressivityBlock,
|
|
24
|
+
normalizeAudioTags,
|
|
25
|
+
normalizeExpressivity,
|
|
26
|
+
normalizeVoiceSettings,
|
|
27
|
+
readAudioTags,
|
|
28
|
+
readExpressivity,
|
|
29
|
+
readVoiceSettings,
|
|
30
|
+
stripAudioTags,
|
|
31
|
+
stripExpressivityBlock
|
|
32
|
+
} from "./chunk-Q3TJAJ54.js";
|
|
33
|
+
|
|
34
|
+
// src/twilio.ts
|
|
35
|
+
var DEFAULT_TWILIO_BASE_URL = "https://api.twilio.com";
|
|
36
|
+
var TwilioApiError = class extends Error {
|
|
37
|
+
constructor(message, status, body) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.status = status;
|
|
40
|
+
this.body = body;
|
|
41
|
+
this.name = "TwilioApiError";
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
function toBase64(s) {
|
|
45
|
+
if (typeof btoa === "function") return btoa(s);
|
|
46
|
+
const nodeBuffer = globalThis.Buffer;
|
|
47
|
+
if (nodeBuffer) return nodeBuffer.from(s, "utf-8").toString("base64");
|
|
48
|
+
throw new Error("twilio.ts: no base64 encoder available in this runtime");
|
|
49
|
+
}
|
|
50
|
+
var TwilioClient = class {
|
|
51
|
+
constructor(config) {
|
|
52
|
+
this.config = config;
|
|
53
|
+
if (!config.accountSid) throw new Error("TwilioClient: accountSid is required");
|
|
54
|
+
if (!config.authToken) throw new Error("TwilioClient: authToken is required");
|
|
55
|
+
this.baseUrl = (config.baseUrl ?? DEFAULT_TWILIO_BASE_URL).replace(/\/+$/, "");
|
|
56
|
+
this.fetchImpl = config.fetch ?? globalThis.fetch;
|
|
57
|
+
if (typeof this.fetchImpl !== "function") {
|
|
58
|
+
throw new Error("TwilioClient: global fetch is unavailable; pass config.fetch");
|
|
59
|
+
}
|
|
60
|
+
this.basicAuth = `Basic ${toBase64(`${config.accountSid}:${config.authToken}`)}`;
|
|
61
|
+
}
|
|
62
|
+
accountUrl(path) {
|
|
63
|
+
return `${this.baseUrl}/2010-04-01/Accounts/${encodeURIComponent(
|
|
64
|
+
this.config.accountSid
|
|
65
|
+
)}${path}`;
|
|
66
|
+
}
|
|
67
|
+
headers(extra = {}) {
|
|
68
|
+
return { authorization: this.basicAuth, ...extra };
|
|
69
|
+
}
|
|
70
|
+
async searchAvailable(params = {}) {
|
|
71
|
+
const country = (params.country ?? "US").toUpperCase();
|
|
72
|
+
const search = new URLSearchParams();
|
|
73
|
+
if (params.areaCode) search.set("AreaCode", params.areaCode);
|
|
74
|
+
if (params.contains) search.set("Contains", params.contains);
|
|
75
|
+
search.set("PageSize", String(params.limit ?? 10));
|
|
76
|
+
const url = `${this.accountUrl(
|
|
77
|
+
`/AvailablePhoneNumbers/${country}/Local.json`
|
|
78
|
+
)}?${search.toString()}`;
|
|
79
|
+
const res = await this.fetchImpl(url, { headers: this.headers() });
|
|
80
|
+
if (!res.ok) {
|
|
81
|
+
throw new TwilioApiError(
|
|
82
|
+
`searchAvailable failed: ${res.status}`,
|
|
83
|
+
res.status,
|
|
84
|
+
await res.text()
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
const data = await res.json();
|
|
88
|
+
return (data.available_phone_numbers ?? []).map((n) => ({
|
|
89
|
+
phoneNumber: n.phone_number,
|
|
90
|
+
friendlyName: n.friendly_name ?? n.phone_number,
|
|
91
|
+
region: n.region,
|
|
92
|
+
locality: n.locality,
|
|
93
|
+
isoCountry: n.iso_country
|
|
94
|
+
}));
|
|
95
|
+
}
|
|
96
|
+
async purchaseNumber(params) {
|
|
97
|
+
const body = new URLSearchParams();
|
|
98
|
+
body.set("PhoneNumber", params.phoneNumber);
|
|
99
|
+
if (params.friendlyName) body.set("FriendlyName", params.friendlyName);
|
|
100
|
+
if (params.voiceUrl) body.set("VoiceUrl", params.voiceUrl);
|
|
101
|
+
const res = await this.fetchImpl(this.accountUrl("/IncomingPhoneNumbers.json"), {
|
|
102
|
+
method: "POST",
|
|
103
|
+
headers: this.headers({ "content-type": "application/x-www-form-urlencoded" }),
|
|
104
|
+
body: body.toString()
|
|
105
|
+
});
|
|
106
|
+
if (!res.ok) {
|
|
107
|
+
throw new TwilioApiError(
|
|
108
|
+
`purchaseNumber failed: ${res.status}`,
|
|
109
|
+
res.status,
|
|
110
|
+
await res.text()
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
const data = await res.json();
|
|
114
|
+
return {
|
|
115
|
+
sid: data.sid,
|
|
116
|
+
phoneNumber: data.phone_number,
|
|
117
|
+
friendlyName: data.friendly_name ?? data.phone_number,
|
|
118
|
+
voiceUrl: data.voice_url
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
async releaseNumber(sid) {
|
|
122
|
+
const res = await this.fetchImpl(
|
|
123
|
+
this.accountUrl(`/IncomingPhoneNumbers/${encodeURIComponent(sid)}.json`),
|
|
124
|
+
{ method: "DELETE", headers: this.headers() }
|
|
125
|
+
);
|
|
126
|
+
if (!res.ok && res.status !== 404) {
|
|
127
|
+
throw new TwilioApiError(
|
|
128
|
+
`releaseNumber failed: ${res.status}`,
|
|
129
|
+
res.status,
|
|
130
|
+
await res.text()
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// src/telephony.ts
|
|
137
|
+
var TelephonyConfigError = class extends Error {
|
|
138
|
+
constructor(message) {
|
|
139
|
+
super(message);
|
|
140
|
+
this.name = "TelephonyConfigError";
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var TelephonyApiError = class extends Error {
|
|
144
|
+
constructor(message, status, body) {
|
|
145
|
+
super(message);
|
|
146
|
+
this.status = status;
|
|
147
|
+
this.body = body;
|
|
148
|
+
this.name = "TelephonyApiError";
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
var HostedTelephonyClient = class {
|
|
152
|
+
constructor(opts) {
|
|
153
|
+
this.opts = opts;
|
|
154
|
+
this.mode = "hosted";
|
|
155
|
+
if (!opts.shogoApiKey) {
|
|
156
|
+
throw new TelephonyConfigError("Hosted telephony requires shogoApiKey");
|
|
157
|
+
}
|
|
158
|
+
if (!opts.projectId) {
|
|
159
|
+
throw new TelephonyConfigError("Hosted telephony requires projectId");
|
|
160
|
+
}
|
|
161
|
+
if (!opts.apiUrl) {
|
|
162
|
+
throw new TelephonyConfigError("Hosted telephony requires apiUrl");
|
|
163
|
+
}
|
|
164
|
+
this.fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
165
|
+
if (typeof this.fetchImpl !== "function") {
|
|
166
|
+
throw new TelephonyConfigError("global fetch is unavailable; pass opts.fetch");
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
url(path) {
|
|
170
|
+
return `${this.opts.apiUrl.replace(/\/+$/, "")}${path}`;
|
|
171
|
+
}
|
|
172
|
+
async request(path, init = {}) {
|
|
173
|
+
const headers = {
|
|
174
|
+
authorization: `Bearer ${this.opts.shogoApiKey}`,
|
|
175
|
+
...init.headers
|
|
176
|
+
};
|
|
177
|
+
let body = init.body;
|
|
178
|
+
if (init.json !== void 0) {
|
|
179
|
+
headers["content-type"] = "application/json";
|
|
180
|
+
body = JSON.stringify(init.json);
|
|
181
|
+
}
|
|
182
|
+
const res = await this.fetchImpl(this.url(path), {
|
|
183
|
+
...init,
|
|
184
|
+
headers,
|
|
185
|
+
body,
|
|
186
|
+
credentials: "omit"
|
|
187
|
+
});
|
|
188
|
+
const text = await res.text();
|
|
189
|
+
let parsed = void 0;
|
|
190
|
+
if (text) {
|
|
191
|
+
try {
|
|
192
|
+
parsed = JSON.parse(text);
|
|
193
|
+
} catch {
|
|
194
|
+
parsed = text;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (!res.ok) {
|
|
198
|
+
const message = parsed?.error ?? parsed?.message ?? `Shogo API error: ${res.status}`;
|
|
199
|
+
throw new TelephonyApiError(message, res.status, parsed);
|
|
200
|
+
}
|
|
201
|
+
return parsed;
|
|
202
|
+
}
|
|
203
|
+
async provisionNumber(opts = {}) {
|
|
204
|
+
const data = await this.request(
|
|
205
|
+
`/api/voice/twilio/provision-number/${encodeURIComponent(
|
|
206
|
+
this.opts.projectId
|
|
207
|
+
)}`,
|
|
208
|
+
{
|
|
209
|
+
method: "POST",
|
|
210
|
+
json: {
|
|
211
|
+
areaCode: opts.areaCode,
|
|
212
|
+
country: opts.country,
|
|
213
|
+
friendlyName: opts.friendlyName
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
);
|
|
217
|
+
return {
|
|
218
|
+
phoneNumber: data.phoneNumber,
|
|
219
|
+
twilioPhoneSid: data.twilioPhoneSid,
|
|
220
|
+
elevenlabsPhoneId: data.elevenlabsPhoneId,
|
|
221
|
+
setupBilledUsd: data.setupBilledUsd,
|
|
222
|
+
monthlyBilledUsd: data.monthlyBilledUsd,
|
|
223
|
+
usageDebited: data.usageDebited
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
async outboundCall(opts) {
|
|
227
|
+
const data = await this.request(
|
|
228
|
+
`/api/voice/twilio/outbound/${encodeURIComponent(this.opts.projectId)}`,
|
|
229
|
+
{
|
|
230
|
+
method: "POST",
|
|
231
|
+
json: { to: opts.to, dynamicVariables: opts.dynamicVariables }
|
|
232
|
+
}
|
|
233
|
+
);
|
|
234
|
+
return {
|
|
235
|
+
callSid: data.callSid,
|
|
236
|
+
conversationId: data.conversationId,
|
|
237
|
+
estimatedBilledUsd: data.estimatedBilledUsd,
|
|
238
|
+
billedUsdPerMinute: data.billedUsdPerMinute
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
async releaseNumber() {
|
|
242
|
+
await this.request(
|
|
243
|
+
`/api/voice/twilio/number/${encodeURIComponent(this.opts.projectId)}`,
|
|
244
|
+
{ method: "DELETE" }
|
|
245
|
+
);
|
|
246
|
+
return { released: true };
|
|
247
|
+
}
|
|
248
|
+
async getUsage(range = {}) {
|
|
249
|
+
const q = new URLSearchParams();
|
|
250
|
+
if (range.from) q.set("from", range.from);
|
|
251
|
+
if (range.to) q.set("to", range.to);
|
|
252
|
+
const path = `/api/voice/usage/${encodeURIComponent(this.opts.projectId)}${q.toString() ? `?${q.toString()}` : ""}`;
|
|
253
|
+
return this.request(path, { method: "GET" });
|
|
254
|
+
}
|
|
255
|
+
async listCalls(opts = {}) {
|
|
256
|
+
const q = new URLSearchParams();
|
|
257
|
+
if (opts.limit) q.set("limit", String(opts.limit));
|
|
258
|
+
if (opts.includeTranscript) q.set("includeTranscript", "1");
|
|
259
|
+
const path = `/api/voice/calls/${encodeURIComponent(this.opts.projectId)}${q.toString() ? `?${q.toString()}` : ""}`;
|
|
260
|
+
const res = await this.request(
|
|
261
|
+
path,
|
|
262
|
+
{ method: "GET" }
|
|
263
|
+
);
|
|
264
|
+
return res.calls;
|
|
265
|
+
}
|
|
266
|
+
async getCall(callId) {
|
|
267
|
+
const path = `/api/voice/calls/${encodeURIComponent(
|
|
268
|
+
this.opts.projectId
|
|
269
|
+
)}/${encodeURIComponent(callId)}`;
|
|
270
|
+
return this.request(path, { method: "GET" });
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
var HostedRuntimeTokenClient = class {
|
|
274
|
+
constructor(opts) {
|
|
275
|
+
this.opts = opts;
|
|
276
|
+
this.mode = "hosted";
|
|
277
|
+
if (!opts.runtimeToken) {
|
|
278
|
+
throw new TelephonyConfigError(
|
|
279
|
+
"Runtime-token telephony requires runtimeToken (process.env.RUNTIME_AUTH_SECRET)"
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
if (!opts.projectId) {
|
|
283
|
+
throw new TelephonyConfigError("Runtime-token telephony requires projectId");
|
|
284
|
+
}
|
|
285
|
+
if (!opts.apiUrl) {
|
|
286
|
+
throw new TelephonyConfigError("Runtime-token telephony requires apiUrl");
|
|
287
|
+
}
|
|
288
|
+
this.fetchImpl = opts.fetch ?? globalThis.fetch;
|
|
289
|
+
if (typeof this.fetchImpl !== "function") {
|
|
290
|
+
throw new TelephonyConfigError("global fetch is unavailable; pass opts.fetch");
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
url(path) {
|
|
294
|
+
const base = this.opts.apiUrl.replace(/\/+$/, "");
|
|
295
|
+
const sep = path.includes("?") ? "&" : "?";
|
|
296
|
+
return `${base}${path}${sep}projectId=${encodeURIComponent(this.opts.projectId)}`;
|
|
297
|
+
}
|
|
298
|
+
async request(path, init = {}) {
|
|
299
|
+
const headers = {
|
|
300
|
+
"x-runtime-token": this.opts.runtimeToken,
|
|
301
|
+
...init.headers
|
|
302
|
+
};
|
|
303
|
+
let body = init.body;
|
|
304
|
+
if (init.json !== void 0) {
|
|
305
|
+
headers["content-type"] = "application/json";
|
|
306
|
+
body = JSON.stringify(init.json);
|
|
307
|
+
}
|
|
308
|
+
const res = await this.fetchImpl(this.url(path), {
|
|
309
|
+
...init,
|
|
310
|
+
headers,
|
|
311
|
+
body,
|
|
312
|
+
credentials: "omit"
|
|
313
|
+
});
|
|
314
|
+
const text = await res.text();
|
|
315
|
+
let parsed = void 0;
|
|
316
|
+
if (text) {
|
|
317
|
+
try {
|
|
318
|
+
parsed = JSON.parse(text);
|
|
319
|
+
} catch {
|
|
320
|
+
parsed = text;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (!res.ok) {
|
|
324
|
+
const message = parsed?.error ?? parsed?.message ?? `Shogo API error: ${res.status}`;
|
|
325
|
+
throw new TelephonyApiError(message, res.status, parsed);
|
|
326
|
+
}
|
|
327
|
+
return parsed;
|
|
328
|
+
}
|
|
329
|
+
async provisionNumber(opts = {}) {
|
|
330
|
+
const data = await this.request(
|
|
331
|
+
`/api/voice/twilio/provision-number/${encodeURIComponent(
|
|
332
|
+
this.opts.projectId
|
|
333
|
+
)}`,
|
|
334
|
+
{
|
|
335
|
+
method: "POST",
|
|
336
|
+
json: {
|
|
337
|
+
areaCode: opts.areaCode,
|
|
338
|
+
country: opts.country,
|
|
339
|
+
friendlyName: opts.friendlyName
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
);
|
|
343
|
+
return {
|
|
344
|
+
phoneNumber: data.phoneNumber,
|
|
345
|
+
twilioPhoneSid: data.twilioPhoneSid,
|
|
346
|
+
elevenlabsPhoneId: data.elevenlabsPhoneId,
|
|
347
|
+
setupBilledUsd: data.setupBilledUsd,
|
|
348
|
+
monthlyBilledUsd: data.monthlyBilledUsd,
|
|
349
|
+
usageDebited: data.usageDebited
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
async outboundCall(opts) {
|
|
353
|
+
const data = await this.request(
|
|
354
|
+
`/api/voice/twilio/outbound/${encodeURIComponent(this.opts.projectId)}`,
|
|
355
|
+
{
|
|
356
|
+
method: "POST",
|
|
357
|
+
json: { to: opts.to, dynamicVariables: opts.dynamicVariables }
|
|
358
|
+
}
|
|
359
|
+
);
|
|
360
|
+
return {
|
|
361
|
+
callSid: data.callSid,
|
|
362
|
+
conversationId: data.conversationId,
|
|
363
|
+
estimatedBilledUsd: data.estimatedBilledUsd,
|
|
364
|
+
billedUsdPerMinute: data.billedUsdPerMinute
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
async releaseNumber() {
|
|
368
|
+
await this.request(
|
|
369
|
+
`/api/voice/twilio/number/${encodeURIComponent(this.opts.projectId)}`,
|
|
370
|
+
{ method: "DELETE" }
|
|
371
|
+
);
|
|
372
|
+
return { released: true };
|
|
373
|
+
}
|
|
374
|
+
async getUsage(range = {}) {
|
|
375
|
+
const q = new URLSearchParams();
|
|
376
|
+
if (range.from) q.set("from", range.from);
|
|
377
|
+
if (range.to) q.set("to", range.to);
|
|
378
|
+
const path = `/api/voice/usage/${encodeURIComponent(this.opts.projectId)}${q.toString() ? `?${q.toString()}` : ""}`;
|
|
379
|
+
return this.request(path, { method: "GET" });
|
|
380
|
+
}
|
|
381
|
+
async listCalls(opts = {}) {
|
|
382
|
+
const q = new URLSearchParams();
|
|
383
|
+
if (opts.limit) q.set("limit", String(opts.limit));
|
|
384
|
+
if (opts.includeTranscript) q.set("includeTranscript", "1");
|
|
385
|
+
const path = `/api/voice/calls/${encodeURIComponent(this.opts.projectId)}${q.toString() ? `?${q.toString()}` : ""}`;
|
|
386
|
+
const res = await this.request(
|
|
387
|
+
path,
|
|
388
|
+
{ method: "GET" }
|
|
389
|
+
);
|
|
390
|
+
return res.calls;
|
|
391
|
+
}
|
|
392
|
+
async getCall(callId) {
|
|
393
|
+
const path = `/api/voice/calls/${encodeURIComponent(
|
|
394
|
+
this.opts.projectId
|
|
395
|
+
)}/${encodeURIComponent(callId)}`;
|
|
396
|
+
return this.request(path, { method: "GET" });
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
var DirectTelephonyClient = class {
|
|
400
|
+
constructor(opts) {
|
|
401
|
+
this.opts = opts;
|
|
402
|
+
this.mode = "direct";
|
|
403
|
+
if (!opts.elevenlabs?.apiKey) {
|
|
404
|
+
throw new TelephonyConfigError("Direct telephony requires elevenlabs.apiKey");
|
|
405
|
+
}
|
|
406
|
+
if (!opts.elevenlabs?.agentId) {
|
|
407
|
+
throw new TelephonyConfigError("Direct telephony requires elevenlabs.agentId");
|
|
408
|
+
}
|
|
409
|
+
if (!opts.twilio?.accountSid || !opts.twilio?.authToken) {
|
|
410
|
+
throw new TelephonyConfigError(
|
|
411
|
+
"Direct telephony requires twilio.accountSid + twilio.authToken"
|
|
412
|
+
);
|
|
413
|
+
}
|
|
414
|
+
this.el = new ElevenLabsClient({
|
|
415
|
+
apiKey: opts.elevenlabs.apiKey,
|
|
416
|
+
baseUrl: opts.elevenlabs.baseUrl,
|
|
417
|
+
fetch: opts.fetch
|
|
418
|
+
});
|
|
419
|
+
this.tw = new TwilioClient({
|
|
420
|
+
accountSid: opts.twilio.accountSid,
|
|
421
|
+
authToken: opts.twilio.authToken,
|
|
422
|
+
baseUrl: opts.twilio.baseUrl,
|
|
423
|
+
fetch: opts.fetch
|
|
424
|
+
});
|
|
425
|
+
this.state = {
|
|
426
|
+
phoneNumber: opts.twilio.fromNumber,
|
|
427
|
+
twilioSid: opts.twilio.phoneSid,
|
|
428
|
+
elevenlabsPhoneId: opts.elevenlabs.phoneNumberId
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
async provisionNumber(opts = {}) {
|
|
432
|
+
const candidates = await this.tw.searchAvailable({
|
|
433
|
+
country: opts.country,
|
|
434
|
+
areaCode: opts.areaCode,
|
|
435
|
+
limit: 1
|
|
436
|
+
});
|
|
437
|
+
if (candidates.length === 0) {
|
|
438
|
+
throw new TelephonyApiError(
|
|
439
|
+
"No Twilio numbers available matching criteria",
|
|
440
|
+
404,
|
|
441
|
+
{ areaCode: opts.areaCode, country: opts.country }
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
const purchased = await this.tw.purchaseNumber({
|
|
445
|
+
phoneNumber: candidates[0].phoneNumber,
|
|
446
|
+
friendlyName: opts.friendlyName
|
|
447
|
+
});
|
|
448
|
+
try {
|
|
449
|
+
const { phoneNumberId } = await this.el.createPhoneNumberTwilio({
|
|
450
|
+
phoneNumber: purchased.phoneNumber,
|
|
451
|
+
label: opts.friendlyName ?? purchased.phoneNumber,
|
|
452
|
+
agentId: this.opts.elevenlabs.agentId,
|
|
453
|
+
twilioAccountSid: this.opts.twilio.accountSid,
|
|
454
|
+
twilioAuthToken: this.opts.twilio.authToken
|
|
455
|
+
});
|
|
456
|
+
this.state = {
|
|
457
|
+
phoneNumber: purchased.phoneNumber,
|
|
458
|
+
twilioSid: purchased.sid,
|
|
459
|
+
elevenlabsPhoneId: phoneNumberId
|
|
460
|
+
};
|
|
461
|
+
return {
|
|
462
|
+
phoneNumber: purchased.phoneNumber,
|
|
463
|
+
twilioPhoneSid: purchased.sid,
|
|
464
|
+
elevenlabsPhoneId: phoneNumberId
|
|
465
|
+
};
|
|
466
|
+
} catch (err) {
|
|
467
|
+
try {
|
|
468
|
+
await this.tw.releaseNumber(purchased.sid);
|
|
469
|
+
} catch {
|
|
470
|
+
}
|
|
471
|
+
throw err;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
async outboundCall(opts) {
|
|
475
|
+
if (!this.state.elevenlabsPhoneId) {
|
|
476
|
+
throw new TelephonyConfigError(
|
|
477
|
+
"outboundCall requires an ElevenLabs phone id; call provisionNumber() or pass elevenlabs.phoneNumberId"
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
const res = await this.el.outboundCall({
|
|
481
|
+
phoneNumberId: this.state.elevenlabsPhoneId,
|
|
482
|
+
agentId: this.opts.elevenlabs.agentId,
|
|
483
|
+
toNumber: opts.to,
|
|
484
|
+
dynamicVariables: opts.dynamicVariables
|
|
485
|
+
});
|
|
486
|
+
return { callSid: res.callSid, conversationId: res.conversationId };
|
|
487
|
+
}
|
|
488
|
+
async releaseNumber() {
|
|
489
|
+
let released = false;
|
|
490
|
+
if (this.state.elevenlabsPhoneId) {
|
|
491
|
+
await this.el.deletePhoneNumber(this.state.elevenlabsPhoneId);
|
|
492
|
+
released = true;
|
|
493
|
+
}
|
|
494
|
+
if (this.state.twilioSid) {
|
|
495
|
+
await this.tw.releaseNumber(this.state.twilioSid);
|
|
496
|
+
released = true;
|
|
497
|
+
}
|
|
498
|
+
this.state = {};
|
|
499
|
+
return { released };
|
|
500
|
+
}
|
|
501
|
+
async getUsage() {
|
|
502
|
+
throw new TelephonyConfigError(
|
|
503
|
+
"getUsage() is unavailable in Mode A (self-hosted). Query Twilio + ElevenLabs directly."
|
|
504
|
+
);
|
|
505
|
+
}
|
|
506
|
+
async listCalls() {
|
|
507
|
+
throw new TelephonyConfigError(
|
|
508
|
+
"listCalls() is unavailable in Mode A (self-hosted). Query ElevenLabs conversations directly."
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
async getCall() {
|
|
512
|
+
throw new TelephonyConfigError(
|
|
513
|
+
"getCall() is unavailable in Mode A (self-hosted). Query ElevenLabs conversations directly."
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
function createTelephonyClient(opts) {
|
|
518
|
+
if (opts.mode === "hosted") return new HostedTelephonyClient(opts);
|
|
519
|
+
if (opts.mode === "runtime-token") return new HostedRuntimeTokenClient(opts);
|
|
520
|
+
return new DirectTelephonyClient(opts);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// src/mock-telephony.ts
|
|
524
|
+
var DEFAULT_PHONE = "+14155550143";
|
|
525
|
+
var DEFAULT_TWILIO_SID = "PNmock0000000000000000000000000001";
|
|
526
|
+
var DEFAULT_EL_PHONE_ID = "phn_mock_0000000001";
|
|
527
|
+
var DEFAULT_CALL_FIXTURES = [
|
|
528
|
+
{
|
|
529
|
+
id: "mock_call_001",
|
|
530
|
+
conversationId: "mock_conv_001",
|
|
531
|
+
callSid: "CAmock00000000000000000000000000001",
|
|
532
|
+
direction: "outbound",
|
|
533
|
+
durationSeconds: 184,
|
|
534
|
+
billedMinutes: 4,
|
|
535
|
+
startedAt: "2026-05-06T22:01:00.000Z",
|
|
536
|
+
endedAt: "2026-05-06T22:04:04.000Z",
|
|
537
|
+
createdAt: "2026-05-06T22:01:00.000Z",
|
|
538
|
+
billed: true,
|
|
539
|
+
hasTranscript: true,
|
|
540
|
+
transcriptSummary: "Prospect agreed to a 30-min demo on Thursday at 2pm PT. Booked via Calendly link."
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
id: "mock_call_002",
|
|
544
|
+
conversationId: "mock_conv_002",
|
|
545
|
+
callSid: "CAmock00000000000000000000000000002",
|
|
546
|
+
direction: "outbound",
|
|
547
|
+
durationSeconds: 41,
|
|
548
|
+
billedMinutes: 1,
|
|
549
|
+
startedAt: "2026-05-06T22:05:00.000Z",
|
|
550
|
+
endedAt: "2026-05-06T22:05:41.000Z",
|
|
551
|
+
createdAt: "2026-05-06T22:05:00.000Z",
|
|
552
|
+
billed: true,
|
|
553
|
+
hasTranscript: true,
|
|
554
|
+
transcriptSummary: "Voicemail. Left a 25-second AI-disclosed message with callback number."
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
id: "mock_call_003",
|
|
558
|
+
conversationId: "mock_conv_003",
|
|
559
|
+
callSid: "CAmock00000000000000000000000000003",
|
|
560
|
+
direction: "outbound",
|
|
561
|
+
durationSeconds: 22,
|
|
562
|
+
billedMinutes: 1,
|
|
563
|
+
startedAt: "2026-05-06T22:06:30.000Z",
|
|
564
|
+
endedAt: "2026-05-06T22:06:52.000Z",
|
|
565
|
+
createdAt: "2026-05-06T22:06:30.000Z",
|
|
566
|
+
billed: true,
|
|
567
|
+
hasTranscript: true,
|
|
568
|
+
transcriptSummary: 'Prospect said "remove me" 8 seconds in. Honored, persisted to do-not-call list.'
|
|
569
|
+
}
|
|
570
|
+
];
|
|
571
|
+
var DEFAULT_TRANSCRIPTS = {
|
|
572
|
+
mock_conv_001: [
|
|
573
|
+
{ role: "agent", message: "Hi, this is an AI assistant calling on behalf of Russell at Shogo. Do you have 30 seconds?", time_in_call_secs: 1 },
|
|
574
|
+
{ role: "user", message: "Sure, what is this about?", time_in_call_secs: 6 },
|
|
575
|
+
{ role: "agent", message: "Shogo is a universal AI agent that runs every job-to-be-done with real interfaces, not chat. We help teams like yours replace point tools with one platform.", time_in_call_secs: 10 },
|
|
576
|
+
{ role: "user", message: "Interesting. We've been looking at workflow automation. How is this different from Zapier?", time_in_call_secs: 24 },
|
|
577
|
+
{ role: "agent", message: "Zapier connects steps. Shogo runs the whole workflow with an agent that can use tools, build interfaces, and learn from your data.", time_in_call_secs: 30 },
|
|
578
|
+
{ role: "user", message: "OK I'd like to see it. Send me a demo invite.", time_in_call_secs: 52 },
|
|
579
|
+
{ role: "agent", message: "Great. I'm sending the Calendly link now. Does Thursday at 2pm Pacific work?", time_in_call_secs: 58 },
|
|
580
|
+
{ role: "user", message: "Yes, perfect.", time_in_call_secs: 66 },
|
|
581
|
+
{ role: "agent", message: "Booked. You'll get a confirmation email in 30 seconds. Thanks for your time.", time_in_call_secs: 70 }
|
|
582
|
+
],
|
|
583
|
+
mock_conv_002: [
|
|
584
|
+
{ role: "agent", message: "Hi, this is an AI assistant calling on behalf of Russell at Shogo. Leaving a voicemail \u2014 we'd love to show you our universal AI agent platform. Call us back at +1 415 555 0143 or visit shogo.ai. Have a great day.", time_in_call_secs: 1 }
|
|
585
|
+
],
|
|
586
|
+
mock_conv_003: [
|
|
587
|
+
{ role: "agent", message: "Hi, this is an AI assistant calling on behalf of Russell at Shogo \u2014", time_in_call_secs: 1 },
|
|
588
|
+
{ role: "user", message: "Remove me from your list.", time_in_call_secs: 8 },
|
|
589
|
+
{ role: "agent", message: "Understood. You're removed and we won't call again. Thank you.", time_in_call_secs: 11 }
|
|
590
|
+
]
|
|
591
|
+
};
|
|
592
|
+
var warned = false;
|
|
593
|
+
var MockTelephonyClient = class {
|
|
594
|
+
constructor(opts = {}) {
|
|
595
|
+
this.mode = "mock";
|
|
596
|
+
this.callCounter = 0;
|
|
597
|
+
this.projectId = opts.projectId ?? "demo-project";
|
|
598
|
+
this.phoneNumber = opts.phoneNumber ?? DEFAULT_PHONE;
|
|
599
|
+
this.fixtures = opts.callFixtures ?? DEFAULT_CALL_FIXTURES;
|
|
600
|
+
if (!warned) {
|
|
601
|
+
console.warn(
|
|
602
|
+
"[shogo] voice mock mode active \u2014 telephony returns canned data, no real provider calls are made."
|
|
603
|
+
);
|
|
604
|
+
warned = true;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
async provisionNumber(_opts = {}) {
|
|
608
|
+
return {
|
|
609
|
+
phoneNumber: this.phoneNumber,
|
|
610
|
+
twilioPhoneSid: DEFAULT_TWILIO_SID,
|
|
611
|
+
elevenlabsPhoneId: DEFAULT_EL_PHONE_ID,
|
|
612
|
+
setupBilledUsd: 0,
|
|
613
|
+
monthlyBilledUsd: 0,
|
|
614
|
+
usageDebited: { setup: false, monthly: false }
|
|
615
|
+
};
|
|
616
|
+
}
|
|
617
|
+
async outboundCall(_opts) {
|
|
618
|
+
this.callCounter++;
|
|
619
|
+
const seq = String(this.callCounter).padStart(32, "0").slice(-32);
|
|
620
|
+
return {
|
|
621
|
+
callSid: `CAmock${seq}`,
|
|
622
|
+
conversationId: `mock_conv_${String(this.callCounter).padStart(3, "0")}`,
|
|
623
|
+
estimatedBilledUsd: 0,
|
|
624
|
+
billedUsdPerMinute: 0
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
async releaseNumber() {
|
|
628
|
+
return { released: true };
|
|
629
|
+
}
|
|
630
|
+
async getUsage(_range) {
|
|
631
|
+
const outboundCalls = this.fixtures.filter((c) => c.direction === "outbound").length;
|
|
632
|
+
const inboundCalls = this.fixtures.length - outboundCalls;
|
|
633
|
+
const totalMinutes = this.fixtures.reduce((sum, c) => sum + c.billedMinutes, 0);
|
|
634
|
+
return {
|
|
635
|
+
projectId: this.projectId,
|
|
636
|
+
range: { from: null, to: null },
|
|
637
|
+
totals: {
|
|
638
|
+
minutesInbound: 0,
|
|
639
|
+
minutesOutbound: totalMinutes,
|
|
640
|
+
billedUsdInbound: 0,
|
|
641
|
+
billedUsdOutbound: 0,
|
|
642
|
+
billedUsdNumbers: 0,
|
|
643
|
+
billedUsd: 0,
|
|
644
|
+
calls: this.fixtures.length,
|
|
645
|
+
inboundCalls,
|
|
646
|
+
outboundCalls
|
|
647
|
+
},
|
|
648
|
+
events: []
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
async listCalls(opts = {}) {
|
|
652
|
+
const limit = opts.limit ?? this.fixtures.length;
|
|
653
|
+
const slice = this.fixtures.slice(0, limit);
|
|
654
|
+
if (!opts.includeTranscript) return slice.map((c) => ({ ...c }));
|
|
655
|
+
return slice.map((c) => ({
|
|
656
|
+
...c,
|
|
657
|
+
transcript: DEFAULT_TRANSCRIPTS[c.conversationId ?? ""] ?? null
|
|
658
|
+
}));
|
|
659
|
+
}
|
|
660
|
+
async getCall(callId) {
|
|
661
|
+
const found = this.fixtures.find(
|
|
662
|
+
(c) => c.id === callId || c.conversationId === callId || c.callSid === callId
|
|
663
|
+
);
|
|
664
|
+
if (!found) {
|
|
665
|
+
return {
|
|
666
|
+
id: callId,
|
|
667
|
+
conversationId: callId,
|
|
668
|
+
callSid: callId,
|
|
669
|
+
direction: "outbound",
|
|
670
|
+
durationSeconds: 0,
|
|
671
|
+
billedMinutes: 0,
|
|
672
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
673
|
+
endedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
674
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
675
|
+
billed: false,
|
|
676
|
+
transcriptSummary: null,
|
|
677
|
+
transcript: null
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
const { hasTranscript: _hasTranscript, ...rest } = found;
|
|
681
|
+
return {
|
|
682
|
+
...rest,
|
|
683
|
+
transcript: DEFAULT_TRANSCRIPTS[found.conversationId ?? ""] ?? null
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
function isVoiceMockEnv() {
|
|
688
|
+
if (typeof process === "undefined") return false;
|
|
689
|
+
const value = process.env?.SHOGO_VOICE_MODE;
|
|
690
|
+
if (!value) return false;
|
|
691
|
+
const v = value.toLowerCase();
|
|
692
|
+
return v === "mock" || v === "demo" || v === "fake";
|
|
693
|
+
}
|
|
694
|
+
export {
|
|
695
|
+
AUDIO_TAGS,
|
|
696
|
+
AUDIO_TAG_GROUPS,
|
|
697
|
+
CONVAI_SUPPORTED_TTS_MODELS,
|
|
698
|
+
CONVAI_TTS_MODEL_FALLBACK,
|
|
699
|
+
DEFAULT_ALLOWED_TAGS,
|
|
700
|
+
DEFAULT_ELEVENLABS_BASE_URL,
|
|
701
|
+
DEFAULT_MEMORY_BLOCK,
|
|
702
|
+
DEFAULT_TWILIO_BASE_URL,
|
|
703
|
+
DEFAULT_VOICE_SETTINGS,
|
|
704
|
+
DirectTelephonyClient,
|
|
705
|
+
EXPRESSIVITY_BLOCK_CLOSE,
|
|
706
|
+
EXPRESSIVITY_BLOCK_OPEN,
|
|
707
|
+
EXPRESSIVITY_OPTIONS,
|
|
708
|
+
ElevenLabsApiError,
|
|
709
|
+
ElevenLabsClient,
|
|
710
|
+
HostedRuntimeTokenClient,
|
|
711
|
+
HostedTelephonyClient,
|
|
712
|
+
MEMORY_CLIENT_TOOLS,
|
|
713
|
+
MockTelephonyClient,
|
|
714
|
+
TelephonyApiError,
|
|
715
|
+
TelephonyConfigError,
|
|
716
|
+
TwilioApiError,
|
|
717
|
+
TwilioClient,
|
|
718
|
+
buildPreviewLine,
|
|
719
|
+
composeAgentPrompt,
|
|
720
|
+
composeExpressivityBlock,
|
|
721
|
+
createTelephonyClient,
|
|
722
|
+
extractBasePrompt,
|
|
723
|
+
isVoiceMockEnv,
|
|
724
|
+
normalizeAudioTags,
|
|
725
|
+
normalizeExpressivity,
|
|
726
|
+
normalizeVoiceSettings,
|
|
727
|
+
readAudioTags,
|
|
728
|
+
readExpressivity,
|
|
729
|
+
readVoiceSettings,
|
|
730
|
+
resolveConvaiTtsModel,
|
|
731
|
+
stripAudioTags,
|
|
732
|
+
stripExpressivityBlock,
|
|
733
|
+
stripMemoryBlock
|
|
734
|
+
};
|
|
735
|
+
//# sourceMappingURL=index.js.map
|