@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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +30 -0
  3. package/dist/chunk-D2EVSUZX.js +27 -0
  4. package/dist/chunk-Q3TJAJ54.js +165 -0
  5. package/dist/chunk-QHDIEE7O.js +453 -0
  6. package/dist/chunk-S3S2RNO6.js +671 -0
  7. package/dist/chunk-ZGYHDC5G.js +423 -0
  8. package/dist/index.cjs +1316 -0
  9. package/dist/index.d.cts +432 -0
  10. package/dist/index.d.ts +432 -0
  11. package/dist/index.js +735 -0
  12. package/dist/native/index.cjs +1667 -0
  13. package/dist/native/index.d.cts +285 -0
  14. package/dist/native/index.d.ts +285 -0
  15. package/dist/native/index.js +949 -0
  16. package/dist/organicParticlesShaders-DIva0vBO.d.cts +693 -0
  17. package/dist/organicParticlesShaders-DIva0vBO.d.ts +693 -0
  18. package/dist/react/index.cjs +1670 -0
  19. package/dist/react/index.d.cts +423 -0
  20. package/dist/react/index.d.ts +423 -0
  21. package/dist/react/index.js +952 -0
  22. package/dist/route/agent.cjs +981 -0
  23. package/dist/route/agent.d.cts +20 -0
  24. package/dist/route/agent.d.ts +20 -0
  25. package/dist/route/agent.js +17 -0
  26. package/dist/route/audio-tags.cjs +975 -0
  27. package/dist/route/audio-tags.d.cts +16 -0
  28. package/dist/route/audio-tags.d.ts +16 -0
  29. package/dist/route/audio-tags.js +13 -0
  30. package/dist/route/index.cjs +987 -0
  31. package/dist/route/index.d.cts +115 -0
  32. package/dist/route/index.d.ts +115 -0
  33. package/dist/route/index.js +12 -0
  34. package/dist/route/signed-url.cjs +975 -0
  35. package/dist/route/signed-url.d.cts +12 -0
  36. package/dist/route/signed-url.d.ts +12 -0
  37. package/dist/route/signed-url.js +13 -0
  38. package/dist/route/tts-preview.cjs +975 -0
  39. package/dist/route/tts-preview.d.cts +17 -0
  40. package/dist/route/tts-preview.d.ts +17 -0
  41. package/dist/route/tts-preview.js +13 -0
  42. package/dist/server.cjs +999 -0
  43. package/dist/server.d.cts +88 -0
  44. package/dist/server.d.ts +88 -0
  45. package/dist/server.js +11 -0
  46. package/dist/types-BKJtOUbE.d.cts +352 -0
  47. package/dist/types-BKJtOUbE.d.ts +352 -0
  48. 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