@sinch/functions-runtime 0.3.8 → 0.3.9

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/dist/index.js CHANGED
@@ -168,6 +168,21 @@ var IceSvamlBuilder = class extends BaseSvamlBuilder {
168
168
  *
169
169
  * @param number - E.164 formatted phone number (e.g., '+15551234567')
170
170
  * @param options - Connection options
171
+ *
172
+ * @example
173
+ * ```typescript
174
+ * // Simple connect
175
+ * new IceSvamlBuilder().connectPstn('+15551234567').build();
176
+ *
177
+ * // With options: caller ID, timeout, and AMD
178
+ * new IceSvamlBuilder()
179
+ * .connectPstn('+15551234567', {
180
+ * cli: '+15559876543',
181
+ * timeout: 30,
182
+ * amd: { enabled: true },
183
+ * })
184
+ * .build();
185
+ * ```
171
186
  */
172
187
  connectPstn(number, options) {
173
188
  this.action = {
@@ -224,6 +239,22 @@ var IceSvamlBuilder = class extends BaseSvamlBuilder {
224
239
  *
225
240
  * @param menus - Array of menu definitions or MenuStructure from createMenu().build()
226
241
  * @param options - Menu options
242
+ *
243
+ * @example
244
+ * ```typescript
245
+ * // Using MenuTemplates
246
+ * new IceSvamlBuilder()
247
+ * .runMenu(MenuTemplates.business('Acme Corp'))
248
+ * .build();
249
+ *
250
+ * // Using createMenu builder
251
+ * const menu = createMenu()
252
+ * .prompt('Press 1 for sales, 2 for support.')
253
+ * .option('1', 'return(sales)')
254
+ * .option('2', 'return(support)')
255
+ * .build();
256
+ * new IceSvamlBuilder().runMenu(menu).build();
257
+ * ```
227
258
  */
228
259
  runMenu(menus, options) {
229
260
  const menuArray = Array.isArray(menus) ? menus : menus.menus;
@@ -241,6 +272,14 @@ var IceSvamlBuilder = class extends BaseSvamlBuilder {
241
272
  *
242
273
  * @param holdPrompt - Prompt to play while on hold
243
274
  * @param options - Park options
275
+ *
276
+ * @example
277
+ * ```typescript
278
+ * // Park with hold music prompt
279
+ * new IceSvamlBuilder()
280
+ * .park('Please hold while we connect you.', { maxDuration: 120 })
281
+ * .build();
282
+ * ```
244
283
  */
245
284
  park(holdPrompt, options) {
246
285
  this.action = {
@@ -1412,145 +1451,895 @@ var ElevenLabsStateManager = class {
1412
1451
  };
1413
1452
  var ElevenLabsState = new ElevenLabsStateManager();
1414
1453
 
1415
- // ../runtime-shared/dist/utils/templateRender.js
1416
- import fs from "fs";
1417
- import path from "path";
1418
- var TemplateRender = class {
1454
+ // ../runtime-shared/dist/ai/elevenlabs/client.js
1455
+ var ELEVENLABS_API_BASE = "https://api.elevenlabs.io/v1";
1456
+ var FETCH_TIMEOUT_MS = 1e4;
1457
+ var ElevenLabsClient = class {
1458
+ apiKey;
1459
+ constructor(apiKey) {
1460
+ this.apiKey = apiKey || process.env.ELEVENLABS_API_KEY || "";
1461
+ if (!this.apiKey) {
1462
+ console.warn("[ElevenLabs] No API key provided. Set ELEVENLABS_API_KEY environment variable.");
1463
+ }
1464
+ }
1465
+ get authHeaders() {
1466
+ return { "xi-api-key": this.apiKey, "Content-Type": "application/json" };
1467
+ }
1419
1468
  /**
1420
- * Load and render an HTML template with variables
1421
- * @param templatePath - Path to the HTML template file
1422
- * @param variables - Object containing variables to replace in template
1423
- * @returns Rendered HTML string
1469
+ * Initiate an outbound call to a phone number using an ElevenLabs agent
1424
1470
  */
1425
- static render(templatePath, variables = {}) {
1426
- try {
1427
- const template = fs.readFileSync(templatePath, "utf8");
1428
- let rendered = template;
1429
- for (const [key, value] of Object.entries(variables)) {
1430
- const placeholder = `{{${key}}}`;
1431
- const regex = new RegExp(placeholder.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
1432
- rendered = rendered.replace(regex, String(value));
1433
- }
1434
- return rendered;
1435
- } catch (error) {
1436
- console.error("Error rendering template:", error);
1437
- throw new Error(`Failed to render template: ${templatePath}`);
1471
+ async makeCall(options) {
1472
+ const phoneNumberId = ElevenLabsState.getPhoneNumberId();
1473
+ const request = {
1474
+ agent_id: options.agentId,
1475
+ customer_phone_number: options.toNumber,
1476
+ agent_phone_number_id: phoneNumberId
1477
+ };
1478
+ if (options.dynamicVariables && Object.keys(options.dynamicVariables).length > 0) {
1479
+ request.custom_llm_extra_body = {
1480
+ dynamic_variables: options.dynamicVariables
1481
+ };
1482
+ }
1483
+ const response = await fetch(`${ELEVENLABS_API_BASE}/convai/conversation/create_call`, {
1484
+ method: "POST",
1485
+ headers: this.authHeaders,
1486
+ body: JSON.stringify(request),
1487
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
1488
+ });
1489
+ if (!response.ok) {
1490
+ const error = await response.text();
1491
+ throw new Error(`ElevenLabs API error: ${response.status} - ${error}`);
1492
+ }
1493
+ return response.json();
1494
+ }
1495
+ /**
1496
+ * Get conversation details including transcript and analysis
1497
+ */
1498
+ async getConversationDetails(conversationId) {
1499
+ const response = await fetch(`${ELEVENLABS_API_BASE}/convai/conversations/${encodeURIComponent(conversationId)}`, {
1500
+ headers: { "xi-api-key": this.apiKey },
1501
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
1502
+ });
1503
+ if (!response.ok) {
1504
+ const error = await response.text();
1505
+ throw new Error(`ElevenLabs API error: ${response.status} - ${error}`);
1506
+ }
1507
+ const data = await response.json();
1508
+ return this.mapConversationResponse(data);
1509
+ }
1510
+ /**
1511
+ * Auto-configure ElevenLabs with Sinch SIP trunk.
1512
+ *
1513
+ * Step 1: Get or import phone number with SIP trunk config
1514
+ * Step 2: Configure agent conversation-init webhook
1515
+ *
1516
+ * Called by tryAutoConfigureAsync (the orchestrator that reads env vars).
1517
+ */
1518
+ async autoConfigureAsync(options) {
1519
+ console.log(`[ElevenLabs] Starting auto-configuration for agent ${options.agentId}`);
1520
+ const phoneResult = await this.getOrImportPhoneNumber(options);
1521
+ if (options.webhookBaseUrl) {
1522
+ await this.configureAgentWebhooks(options.agentId, options.webhookBaseUrl);
1438
1523
  }
1524
+ console.log(`[ElevenLabs] Auto-configuration completed: PhoneNumberId=${phoneResult.phoneNumberId}`);
1525
+ return {
1526
+ success: true,
1527
+ phoneNumberId: phoneResult.phoneNumberId,
1528
+ phoneNumber: options.phoneNumber,
1529
+ sipAddress: options.sipAddress
1530
+ };
1439
1531
  }
1440
1532
  /**
1441
- * Get the absolute path to a template file relative to the current directory
1442
- * @param templateName - Name of the template file (e.g., 'index.html')
1443
- * @param baseDir - Base directory to search from (defaults to cwd)
1444
- * @returns Absolute path to the template file
1533
+ * Check if phone number already exists in ElevenLabs; create or update it.
1534
+ * Matches C# GetOrImportPhoneNumberAsync.
1445
1535
  */
1446
- static getTemplatePath(templateName, baseDir) {
1447
- const searchDir = baseDir || process.cwd();
1448
- const pagesPath = path.join(searchDir, "utils", "pages", templateName);
1449
- if (fs.existsSync(pagesPath)) {
1450
- return pagesPath;
1536
+ async getOrImportPhoneNumber(config) {
1537
+ const listResponse = await fetch(`${ELEVENLABS_API_BASE}/convai/phone-numbers`, {
1538
+ headers: this.authHeaders,
1539
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
1540
+ });
1541
+ if (!listResponse.ok) {
1542
+ throw new Error(`Failed to list phone numbers: ${listResponse.status}`);
1543
+ }
1544
+ const phoneNumbers = await listResponse.json();
1545
+ const sipConfig = {
1546
+ address: config.sipAddress,
1547
+ transport: "tcp",
1548
+ media_encryption: "disabled",
1549
+ credentials: { username: config.sipUsername, password: config.sipPassword }
1550
+ };
1551
+ const existing = phoneNumbers.find((p) => p.phone_number === config.phoneNumber);
1552
+ if (existing) {
1553
+ console.log(`[ElevenLabs] Phone number already exists (${existing.phone_number_id}), updating config`);
1554
+ await this.updatePhoneNumberConfig(existing.phone_number_id, config.agentId, sipConfig);
1555
+ return { phoneNumberId: existing.phone_number_id };
1556
+ }
1557
+ const label = `Sinch Functions - ${config.functionName || "Sinch Function"}`;
1558
+ console.log("[ElevenLabs] Creating SIP trunk phone number");
1559
+ const createResponse2 = await fetch(`${ELEVENLABS_API_BASE}/convai/phone-numbers`, {
1560
+ method: "POST",
1561
+ headers: this.authHeaders,
1562
+ body: JSON.stringify({
1563
+ phone_number: config.phoneNumber,
1564
+ label,
1565
+ provider: "sip_trunk",
1566
+ supports_inbound: true,
1567
+ supports_outbound: true,
1568
+ inbound_trunk_config: { media_encryption: "disabled" },
1569
+ outbound_trunk_config: sipConfig
1570
+ }),
1571
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
1572
+ });
1573
+ if (!createResponse2.ok) {
1574
+ const err = await createResponse2.text();
1575
+ throw new Error(`Failed to create phone number: ${createResponse2.status} - ${err}`);
1576
+ }
1577
+ const created = await createResponse2.json();
1578
+ console.log("[ElevenLabs] Phone number created successfully");
1579
+ await this.updatePhoneNumberConfig(created.phone_number_id, config.agentId, sipConfig);
1580
+ return { phoneNumberId: created.phone_number_id };
1581
+ }
1582
+ /**
1583
+ * Update phone number with agent association and SIP trunk config.
1584
+ * Matches C# UpdatePhoneNumberConfigAsync.
1585
+ */
1586
+ async updatePhoneNumberConfig(phoneNumberId, agentId, sipConfig) {
1587
+ const response = await fetch(`${ELEVENLABS_API_BASE}/convai/phone-numbers/${encodeURIComponent(phoneNumberId)}`, {
1588
+ method: "PATCH",
1589
+ headers: this.authHeaders,
1590
+ body: JSON.stringify({
1591
+ agent_id: agentId,
1592
+ outbound_trunk_config: sipConfig
1593
+ }),
1594
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
1595
+ });
1596
+ if (!response.ok) {
1597
+ await response.text();
1598
+ console.warn(`[ElevenLabs] Failed to update phone number config: ${response.status}`);
1599
+ } else {
1600
+ console.log("[ElevenLabs] Phone number configuration updated");
1451
1601
  }
1452
- const directPath = path.join(searchDir, "pages", templateName);
1453
- if (fs.existsSync(directPath)) {
1454
- return directPath;
1602
+ }
1603
+ /**
1604
+ * Configure agent webhook for conversation-init events.
1605
+ * Matches C# ConfigureAgentWebhooksAsync.
1606
+ */
1607
+ async configureAgentWebhooks(agentId, webhookBaseUrl) {
1608
+ const webhookUrl = `${webhookBaseUrl}/webhook/elevenlabs/conversation-init`;
1609
+ console.log(`[ElevenLabs] Configuring agent webhook`);
1610
+ const response = await fetch(`${ELEVENLABS_API_BASE}/convai/agents/${encodeURIComponent(agentId)}`, {
1611
+ method: "PATCH",
1612
+ headers: this.authHeaders,
1613
+ body: JSON.stringify({
1614
+ platform_settings: {
1615
+ workspace_overrides: {
1616
+ conversation_initiation_client_data_webhook: {
1617
+ url: webhookUrl,
1618
+ request_headers: {}
1619
+ }
1620
+ },
1621
+ overrides: {
1622
+ enable_conversation_initiation_client_data_from_webhook: true
1623
+ }
1624
+ }
1625
+ }),
1626
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
1627
+ });
1628
+ if (!response.ok) {
1629
+ await response.text();
1630
+ console.error(`[ElevenLabs] Failed to configure agent webhooks: ${response.status}`);
1631
+ } else {
1632
+ console.log("[ElevenLabs] Agent webhooks configured successfully");
1455
1633
  }
1456
- return path.join(searchDir, "utils", "pages", templateName);
1634
+ }
1635
+ /**
1636
+ * Map API response to our interface
1637
+ */
1638
+ mapConversationResponse(data) {
1639
+ return {
1640
+ conversationId: data.conversation_id,
1641
+ agentId: data.agent_id,
1642
+ status: data.status,
1643
+ metadata: data.metadata ? {
1644
+ callDurationSeconds: data.metadata.call_duration_secs,
1645
+ startTime: data.metadata.start_time_unix_secs ? new Date(data.metadata.start_time_unix_secs * 1e3) : void 0,
1646
+ endTime: data.metadata.end_time_unix_secs ? new Date(data.metadata.end_time_unix_secs * 1e3) : void 0
1647
+ } : void 0,
1648
+ transcript: data.transcript?.map((t) => ({
1649
+ role: t.role,
1650
+ message: t.message,
1651
+ timeInCallSeconds: t.time_in_call_secs
1652
+ })),
1653
+ analysis: data.analysis ? {
1654
+ summary: data.analysis.transcript_summary,
1655
+ customData: data.analysis.custom_data
1656
+ } : void 0
1657
+ };
1457
1658
  }
1458
1659
  };
1660
+ function createElevenLabsClient(apiKey) {
1661
+ return new ElevenLabsClient(apiKey);
1662
+ }
1459
1663
 
1460
- // ../runtime-shared/dist/utils/versionExtractor.js
1461
- import fs2 from "fs";
1462
- import path2 from "path";
1463
- var VersionExtractor = class {
1664
+ // ../runtime-shared/dist/ai/elevenlabs/controller.js
1665
+ var ElevenLabsController = class {
1464
1666
  /**
1465
- * Extract version from README.md file in the template directory
1466
- * Looks for "Template Version: X.X.X" pattern in the last few lines
1467
- * @param templateDir - Path to the template directory containing README.md
1468
- * @returns Version string or default fallback
1667
+ * Handle conversation initialization webhook
1668
+ * Called when an inbound call is received - allows customizing the conversation
1669
+ *
1670
+ * @param request - The conversation init request with caller info
1671
+ * @returns Response with dynamic variables and/or config overrides
1469
1672
  */
1470
- static getTemplateVersion(templateDir = process.cwd()) {
1471
- try {
1472
- const readmePath = path2.join(templateDir, "README.md");
1473
- if (!fs2.existsSync(readmePath)) {
1474
- console.warn("README.md not found, using default version");
1475
- return "v1.0.0";
1673
+ async handleConversationInit(request) {
1674
+ return {};
1675
+ }
1676
+ /**
1677
+ * Handle post-call webhook with transcript and analysis
1678
+ *
1679
+ * @param webhook - The post-call webhook payload
1680
+ */
1681
+ async handlePostCall(webhook) {
1682
+ console.log("[ElevenLabs] Post-call webhook received:", {
1683
+ conversationId: webhook.data.conversation_id,
1684
+ agentId: webhook.data.agent_id,
1685
+ status: webhook.data.status,
1686
+ duration: webhook.data.metadata.call_duration_secs
1687
+ });
1688
+ }
1689
+ /**
1690
+ * Handle post-call audio webhook with recording
1691
+ *
1692
+ * @param webhook - The audio webhook with base64-encoded MP3
1693
+ */
1694
+ async handlePostCallAudio(webhook) {
1695
+ console.log("[ElevenLabs] Post-call audio received:", {
1696
+ conversationId: webhook.data.conversation_id,
1697
+ agentId: webhook.data.agent_id
1698
+ });
1699
+ }
1700
+ /**
1701
+ * Handle call initiation failure webhook
1702
+ *
1703
+ * @param webhook - The failure webhook with error details
1704
+ */
1705
+ async handleCallInitiationFailure(webhook) {
1706
+ console.error("[ElevenLabs] Call initiation failed:", {
1707
+ agentId: webhook.data.agent_id,
1708
+ customerNumber: webhook.data.customer_phone_number,
1709
+ reason: webhook.data.failure_reason
1710
+ });
1711
+ }
1712
+ /**
1713
+ * Express route handler for conversation init endpoint
1714
+ * Mount at: POST /elevenlabs/conversation-init
1715
+ */
1716
+ getConversationInitHandler() {
1717
+ return async (req, res) => {
1718
+ try {
1719
+ const request = req.body;
1720
+ const response = await this.handleConversationInit(request);
1721
+ res.json(response);
1722
+ } catch (error) {
1723
+ console.error("[ElevenLabs] Error in conversation init:", error);
1724
+ res.status(500).json({ error: "Internal server error" });
1476
1725
  }
1477
- const readmeContent = fs2.readFileSync(readmePath, "utf8");
1478
- const lines = readmeContent.split("\n");
1479
- const versionPattern = /Template Version:\s*([\d.]+)/i;
1480
- for (let i = lines.length - 1; i >= Math.max(0, lines.length - 10); i--) {
1481
- const match = lines[i].match(versionPattern);
1482
- if (match) {
1483
- return `v${match[1]}`;
1726
+ };
1727
+ }
1728
+ /**
1729
+ * Express route handler for webhook endpoint
1730
+ * Mount at: POST /elevenlabs/webhook
1731
+ * Handles all webhook types based on the 'type' field
1732
+ */
1733
+ getWebhookHandler() {
1734
+ return async (req, res) => {
1735
+ try {
1736
+ const webhook = req.body;
1737
+ switch (webhook.type) {
1738
+ case "post_call_transcription":
1739
+ await this.handlePostCall(webhook);
1740
+ break;
1741
+ case "post_call_audio":
1742
+ await this.handlePostCallAudio(webhook);
1743
+ break;
1744
+ case "call_initiation_failure":
1745
+ await this.handleCallInitiationFailure(webhook);
1746
+ break;
1747
+ default:
1748
+ console.warn("[ElevenLabs] Unknown webhook type:", webhook.type);
1484
1749
  }
1750
+ res.status(200).send("OK");
1751
+ } catch (error) {
1752
+ console.error("[ElevenLabs] Error processing webhook:", error);
1753
+ res.status(500).json({ error: "Internal server error" });
1485
1754
  }
1486
- console.warn("Version pattern not found in README.md, using default");
1487
- return "v1.0.0";
1488
- } catch (error) {
1489
- console.error("Error reading template version:", error.message);
1490
- return "v1.0.0";
1755
+ };
1756
+ }
1757
+ };
1758
+
1759
+ // ../runtime-shared/dist/ai/elevenlabs/autoconfigurator.js
1760
+ var FETCH_TIMEOUT_MS2 = 1e4;
1761
+ function maskPhone(phone) {
1762
+ if (phone.length <= 6)
1763
+ return "***";
1764
+ return phone.slice(0, 4) + "***" + phone.slice(-4);
1765
+ }
1766
+ async function tryAutoConfigureAsync(webhookUrl, functionName, sinchPhoneNumber) {
1767
+ const autoConfig = process.env.ELEVENLABS_AUTO_CONFIGURE;
1768
+ if (!autoConfig || autoConfig.toLowerCase() !== "true") {
1769
+ return null;
1770
+ }
1771
+ console.log("[ElevenLabs] Auto-configuration is enabled");
1772
+ const agentId = process.env.ELEVENLABS_AGENT_ID;
1773
+ const apiKey = process.env.ELEVENLABS_API_KEY;
1774
+ if (!agentId) {
1775
+ console.warn("[ElevenLabs] Auto-configuration skipped: ELEVENLABS_AGENT_ID not set. Please configure your agent ID in environment variables.");
1776
+ return null;
1777
+ }
1778
+ if (!apiKey) {
1779
+ console.warn("[ElevenLabs] Auto-configuration skipped: ELEVENLABS_API_KEY not set. Please store your API key using: sinch secrets add ELEVENLABS_API_KEY sk_...");
1780
+ return null;
1781
+ }
1782
+ let phoneNumber = sinchPhoneNumber ?? process.env.SINCH_PHONE_NUMBER;
1783
+ if (!phoneNumber) {
1784
+ console.log("[ElevenLabs] SINCH_PHONE_NUMBER not configured, attempting auto-discovery from Voice API...");
1785
+ phoneNumber = await autoDiscoverPhoneNumber();
1786
+ }
1787
+ if (!phoneNumber) {
1788
+ console.warn("[ElevenLabs] Auto-configuration skipped: Sinch phone number not provided and auto-discovery failed. Please set SINCH_PHONE_NUMBER or ensure Voice API credentials are configured.");
1789
+ return null;
1790
+ }
1791
+ if (!webhookUrl) {
1792
+ console.warn("[ElevenLabs] Auto-configuration skipped: Webhook URL not provided.");
1793
+ return null;
1794
+ }
1795
+ try {
1796
+ new URL(webhookUrl);
1797
+ } catch {
1798
+ console.warn("[ElevenLabs] Auto-configuration skipped: Webhook URL is not a valid URL.");
1799
+ return null;
1800
+ }
1801
+ const voiceAppKey = process.env.VOICE_APPLICATION_KEY || "";
1802
+ const voiceAppSecret = process.env.VOICE_APPLICATION_SECRET || "";
1803
+ const safeName = functionName ? functionName.slice(0, 100) : "Sinch Function";
1804
+ try {
1805
+ console.log(`[ElevenLabs] Starting auto-configuration: Agent=${agentId}, Number=${maskPhone(phoneNumber)}`);
1806
+ const client = new ElevenLabsClient(apiKey);
1807
+ const result = await client.autoConfigureAsync({
1808
+ agentId,
1809
+ phoneNumber,
1810
+ sipAddress: "use1.vp.sip.sinch.com",
1811
+ sipUsername: voiceAppKey,
1812
+ sipPassword: voiceAppSecret,
1813
+ webhookBaseUrl: webhookUrl,
1814
+ functionName: safeName
1815
+ });
1816
+ if (result.success) {
1817
+ ElevenLabsState.setConfigured({
1818
+ phoneNumberId: result.phoneNumberId,
1819
+ phoneNumber: result.phoneNumber,
1820
+ agentId,
1821
+ sipAddress: result.sipAddress
1822
+ });
1823
+ console.log(`[ElevenLabs] Auto-configuration completed! Phone: ${maskPhone(result.phoneNumber ?? "")}, SIP: ${result.sipAddress}`);
1491
1824
  }
1825
+ return result;
1826
+ } catch (error) {
1827
+ const message = error instanceof Error ? error.message : "Unknown error";
1828
+ console.error(`[ElevenLabs] Auto-configuration failed: ${message}. The function will start but ElevenLabs integration may not work.`);
1829
+ return null;
1830
+ }
1831
+ }
1832
+ async function autoDiscoverPhoneNumber() {
1833
+ const appKey = process.env.VOICE_APPLICATION_KEY || "";
1834
+ const appSecret = process.env.VOICE_APPLICATION_SECRET || "";
1835
+ if (!appKey || !appSecret) {
1836
+ console.warn("[ElevenLabs] Voice client not available for phone number auto-discovery. Ensure VOICE_APPLICATION_KEY and VOICE_APPLICATION_SECRET are configured.");
1837
+ return void 0;
1492
1838
  }
1839
+ try {
1840
+ const auth = Buffer.from(`${appKey}:${appSecret}`).toString("base64");
1841
+ const configResponse = await fetch(`https://callingapi.sinch.com/v1/configuration/numbers/`, {
1842
+ headers: { Authorization: `Basic ${auth}` },
1843
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS2)
1844
+ });
1845
+ if (!configResponse.ok) {
1846
+ console.warn(`[ElevenLabs] Failed to query Voice API for phone numbers: ${configResponse.status}`);
1847
+ return void 0;
1848
+ }
1849
+ const data = await configResponse.json();
1850
+ const numbers = (data.numbers || []).filter((n) => n.applicationkey === appKey).map((n) => n.number).filter(Boolean);
1851
+ if (numbers.length > 0) {
1852
+ console.log(`[ElevenLabs] Auto-discovered ${numbers.length} phone number(s), using first`);
1853
+ return numbers[0];
1854
+ }
1855
+ console.warn("[ElevenLabs] No phone numbers found for the configured Voice application");
1856
+ return void 0;
1857
+ } catch (error) {
1858
+ const message = error instanceof Error ? error.message : "Unknown error";
1859
+ console.error(`[ElevenLabs] Failed to auto-discover phone number: ${message}`);
1860
+ return void 0;
1861
+ }
1862
+ }
1863
+ var ElevenLabsAutoConfigurator = class {
1864
+ static tryAutoConfigureAsync = tryAutoConfigureAsync;
1493
1865
  };
1494
1866
 
1495
- // ../runtime-shared/dist/utils/defaultEndpointHandler.js
1496
- var DefaultEndpointHandler = class {
1497
- /**
1498
- * Handle the default endpoint with content negotiation
1499
- * @param context - Function context with config and cache
1500
- * @param request - HTTP request object with method, path, query, headers, body, params
1501
- * @param options - Configuration options for the response
1502
- * @returns HTTP response object with statusCode, headers, and body
1503
- */
1504
- static async handle(context, request, options = {}) {
1505
- const { serviceName = "Sinch Function", companyName = "Your Company", templateVersion = "v1.0.0", availableEndpoints = {} } = options;
1506
- console.info("Default endpoint called");
1507
- console.info(`Request: ${request.method} ${request.path}`);
1508
- const acceptHeader = request.headers?.accept || request.headers?.Accept || "";
1509
- const wantsHtml = acceptHeader.includes("text/html") || !acceptHeader.includes("application/json") && !acceptHeader.includes("application/*") && !acceptHeader.includes("*/*");
1510
- if (wantsHtml) {
1511
- try {
1512
- const templatePath = TemplateRender.getTemplatePath("index.html");
1513
- const html = TemplateRender.render(templatePath, {
1514
- companyName: serviceName,
1515
- requestId: context.requestId,
1516
- method: request.method,
1517
- path: request.path,
1518
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1519
- templateVersion,
1520
- runtime: "Node.js"
1521
- });
1522
- return {
1523
- statusCode: 200,
1524
- headers: {
1525
- "Content-Type": "text/html; charset=utf-8"
1526
- },
1527
- body: html
1867
+ // ../runtime-shared/dist/conversation/message-builder.js
1868
+ var ConversationMessageBuilder = class {
1869
+ config;
1870
+ appId;
1871
+ channel;
1872
+ identity;
1873
+ conversationId;
1874
+ messageText;
1875
+ /**
1876
+ * Create a new builder with configuration
1877
+ */
1878
+ constructor(config = {}) {
1879
+ this.config = config;
1880
+ }
1881
+ /**
1882
+ * Pre-fill builder from an inbound message (for replies)
1883
+ */
1884
+ fromInbound(inbound) {
1885
+ this.appId = inbound.app_id;
1886
+ this.channel = inbound.message?.channel_identity?.channel;
1887
+ this.identity = inbound.message?.channel_identity?.identity;
1888
+ this.conversationId = inbound.message?.conversation_id;
1889
+ return this;
1890
+ }
1891
+ /**
1892
+ * Set the app ID (defaults to CONVERSATION_APP_ID from config)
1893
+ */
1894
+ setAppId(appId) {
1895
+ this.appId = appId;
1896
+ return this;
1897
+ }
1898
+ /**
1899
+ * Set the recipient channel and identity
1900
+ *
1901
+ * @param channel - Channel name (SMS, WHATSAPP, MESSENGER, etc.)
1902
+ * @param identity - Channel-specific identity (phone number, PSID, etc.)
1903
+ */
1904
+ to(channel, identity) {
1905
+ this.channel = channel;
1906
+ this.identity = identity;
1907
+ return this;
1908
+ }
1909
+ /**
1910
+ * Set the conversation ID (for non-DISPATCH mode apps)
1911
+ */
1912
+ setConversationId(conversationId) {
1913
+ this.conversationId = conversationId;
1914
+ return this;
1915
+ }
1916
+ /**
1917
+ * Set a text message
1918
+ */
1919
+ text(text) {
1920
+ this.messageText = text;
1921
+ return this;
1922
+ }
1923
+ /**
1924
+ * Build the SendMessageRequest.
1925
+ * Automatically sets SMS_SENDER from config if sending to SMS channel.
1926
+ *
1927
+ * @returns A SendMessageRequest ready to send via context.conversation.messages.send()
1928
+ */
1929
+ build() {
1930
+ const appId = this.appId || this.config.CONVERSATION_APP_ID;
1931
+ if (!appId) {
1932
+ throw new Error("AppId required - use setAppId() or set CONVERSATION_APP_ID in config");
1933
+ }
1934
+ if (!this.channel || !this.identity) {
1935
+ throw new Error("Recipient required - use to() or fromInbound()");
1936
+ }
1937
+ if (!this.messageText) {
1938
+ throw new Error("Message required - use text()");
1939
+ }
1940
+ const request = {
1941
+ app_id: appId,
1942
+ message: {
1943
+ text_message: {
1944
+ text: this.messageText
1945
+ }
1946
+ },
1947
+ recipient: {
1948
+ identified_by: {
1949
+ channel_identities: [
1950
+ {
1951
+ channel: this.channel,
1952
+ identity: this.identity
1953
+ }
1954
+ ]
1955
+ }
1956
+ }
1957
+ };
1958
+ if (this.channel.toUpperCase() === "SMS") {
1959
+ const smsSender = this.config.FROM_NUMBER;
1960
+ if (smsSender) {
1961
+ request.channel_properties = {
1962
+ SMS_SENDER: smsSender
1528
1963
  };
1529
- } catch (error) {
1530
- console.warn("HTML template not found, returning JSON");
1531
1964
  }
1532
1965
  }
1533
- return {
1534
- statusCode: 200,
1535
- headers: {
1536
- "Content-Type": "application/json"
1537
- },
1538
- body: {
1539
- message: `Welcome to ${serviceName}`,
1540
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1541
- requestId: context.requestId,
1542
- method: request.method,
1543
- path: request.path,
1544
- availableEndpoints,
1545
- usage: {
1546
- html: "Set Accept: text/html header or visit in browser",
1547
- json: "Set Accept: application/json header or use API client"
1966
+ return request;
1967
+ }
1968
+ };
1969
+ function createMessageBuilder(config = {}) {
1970
+ return new ConversationMessageBuilder(config);
1971
+ }
1972
+
1973
+ // ../runtime-shared/dist/conversation/message-helpers.js
1974
+ function textReply(inbound, text, smsSender) {
1975
+ const channelIdentity = inbound.message?.channel_identity;
1976
+ if (!channelIdentity) {
1977
+ throw new Error("Inbound message has no channel identity");
1978
+ }
1979
+ const channel = channelIdentity.channel;
1980
+ const identity = channelIdentity.identity;
1981
+ if (!channel || !identity) {
1982
+ throw new Error("Inbound message has no channel or identity");
1983
+ }
1984
+ return createChannelMessage(inbound.app_id, channel, identity, text, smsSender);
1985
+ }
1986
+ function createSms(appId, phoneNumber, text, smsSender) {
1987
+ return createChannelMessage(appId, "SMS", phoneNumber, text, smsSender);
1988
+ }
1989
+ function createWhatsApp(appId, phoneNumber, text) {
1990
+ return createChannelMessage(appId, "WHATSAPP", phoneNumber, text);
1991
+ }
1992
+ function createMessenger(appId, psid, text) {
1993
+ return createChannelMessage(appId, "MESSENGER", psid, text);
1994
+ }
1995
+ function createChannelMessage(appId, channel, identity, text, smsSender) {
1996
+ if (!appId)
1997
+ throw new Error("appId is required");
1998
+ if (!channel)
1999
+ throw new Error("channel is required");
2000
+ if (!identity)
2001
+ throw new Error("identity is required");
2002
+ if (!text)
2003
+ throw new Error("text is required");
2004
+ const request = {
2005
+ app_id: appId,
2006
+ message: {
2007
+ text_message: {
2008
+ text
2009
+ }
2010
+ },
2011
+ recipient: {
2012
+ identified_by: {
2013
+ channel_identities: [
2014
+ {
2015
+ channel,
2016
+ identity
2017
+ }
2018
+ ]
2019
+ }
2020
+ }
2021
+ };
2022
+ if (channel.toUpperCase() === "SMS" && smsSender) {
2023
+ request.channel_properties = {
2024
+ SMS_SENDER: smsSender
2025
+ };
2026
+ }
2027
+ return request;
2028
+ }
2029
+ var ConversationMessage = {
2030
+ textReply,
2031
+ createSms,
2032
+ createWhatsApp,
2033
+ createMessenger,
2034
+ createChannelMessage
2035
+ };
2036
+
2037
+ // ../runtime-shared/dist/conversation/controller.js
2038
+ var ConversationController = class {
2039
+ conversation;
2040
+ config;
2041
+ constructor(conversation, config = {}) {
2042
+ this.conversation = conversation;
2043
+ this.config = config;
2044
+ }
2045
+ /**
2046
+ * MESSAGE_INBOUND - Called when a user sends a message.
2047
+ * Override this to handle incoming messages from users.
2048
+ */
2049
+ async handleMessageInbound(event) {
2050
+ }
2051
+ /**
2052
+ * MESSAGE_DELIVERY - Called when message delivery status updates.
2053
+ * Override this to track delivery receipts.
2054
+ */
2055
+ async handleMessageDelivery(event) {
2056
+ }
2057
+ /**
2058
+ * EVENT_INBOUND - Called for events like composing indicators.
2059
+ * Override this to handle typing indicators, read receipts, etc.
2060
+ */
2061
+ async handleEventInbound(event) {
2062
+ }
2063
+ /**
2064
+ * CONVERSATION_START - Called when a conversation begins.
2065
+ * Override this to handle conversation initialization.
2066
+ */
2067
+ async handleConversationStart(event) {
2068
+ }
2069
+ /**
2070
+ * CONVERSATION_STOP - Called when a conversation ends.
2071
+ * Override this to handle conversation cleanup.
2072
+ */
2073
+ async handleConversationStop(event) {
2074
+ }
2075
+ /**
2076
+ * Gets the sender number from configuration (FROM_NUMBER).
2077
+ * Used for setting SMS_SENDER channel property when sending to SMS channel.
2078
+ */
2079
+ get fromNumber() {
2080
+ return this.config.FROM_NUMBER;
2081
+ }
2082
+ /**
2083
+ * Create a text reply to an inbound message.
2084
+ * Automatically sets SMS_SENDER from FROM_NUMBER config.
2085
+ */
2086
+ reply(inbound, text) {
2087
+ return textReply(inbound, text, this.fromNumber);
2088
+ }
2089
+ /**
2090
+ * Create a ConversationMessageBuilder for building messages with full control.
2091
+ */
2092
+ createMessage() {
2093
+ return new ConversationMessageBuilder(this.config);
2094
+ }
2095
+ /**
2096
+ * Create a ConversationMessageBuilder pre-filled from an inbound message.
2097
+ */
2098
+ createReply(inbound) {
2099
+ return new ConversationMessageBuilder(this.config).fromInbound(inbound);
2100
+ }
2101
+ /**
2102
+ * Express route handler for the webhook endpoint.
2103
+ * Mount at: POST /conversation
2104
+ *
2105
+ * Routes to appropriate handler based on event type.
2106
+ * Uses manual JSON parsing as workaround for SDK issues with extra fields.
2107
+ */
2108
+ getWebhookHandler() {
2109
+ return async (req, res) => {
2110
+ try {
2111
+ const body = req.body;
2112
+ const verbose = this.config.VERBOSE === "true";
2113
+ if (!body) {
2114
+ res.status(400).json({ error: "Empty request body" });
2115
+ return;
2116
+ }
2117
+ if (!this.conversation) {
2118
+ console.error("[Conversation] Client not configured");
2119
+ res.status(500).json({ error: "Conversation API not configured" });
2120
+ return;
2121
+ }
2122
+ if (body.message?.direction === "TO_APP" && body.message?.contact_message) {
2123
+ if (verbose) {
2124
+ console.log("[Conversation] MESSAGE_INBOUND event detected");
2125
+ }
2126
+ await this.handleMessageInbound(body);
2127
+ res.status(200).send("OK");
2128
+ return;
2129
+ }
2130
+ if (body.message_delivery_report) {
2131
+ if (verbose) {
2132
+ console.log("[Conversation] MESSAGE_DELIVERY event detected");
2133
+ }
2134
+ await this.handleMessageDelivery(body);
2135
+ res.status(200).send("OK");
2136
+ return;
1548
2137
  }
2138
+ if (body.event?.contact_event) {
2139
+ if (verbose) {
2140
+ console.log("[Conversation] EVENT_INBOUND event detected");
2141
+ }
2142
+ await this.handleEventInbound(body);
2143
+ res.status(200).send("OK");
2144
+ return;
2145
+ }
2146
+ if (body.conversation_event) {
2147
+ const eventType = body.conversation_event.type;
2148
+ if (eventType === "CONVERSATION_START") {
2149
+ if (verbose) {
2150
+ console.log("[Conversation] CONVERSATION_START event detected");
2151
+ }
2152
+ await this.handleConversationStart(body);
2153
+ } else if (eventType === "CONVERSATION_STOP") {
2154
+ if (verbose) {
2155
+ console.log("[Conversation] CONVERSATION_STOP event detected");
2156
+ }
2157
+ await this.handleConversationStop(body);
2158
+ }
2159
+ res.status(200).send("OK");
2160
+ return;
2161
+ }
2162
+ if (verbose) {
2163
+ console.log("[Conversation] Unknown event type, ignoring");
2164
+ }
2165
+ res.status(200).json({ status: "ignored", reason: "unknown_event_type" });
2166
+ } catch (error) {
2167
+ console.error("[Conversation] Error processing webhook:", error);
2168
+ res.status(500).json({ error: "Internal server error" });
1549
2169
  }
1550
2170
  };
1551
2171
  }
1552
2172
  };
1553
2173
 
2174
+ // ../runtime-shared/dist/conversation/extensions.js
2175
+ function getText(event) {
2176
+ return event.message?.contact_message?.text_message?.text;
2177
+ }
2178
+ function getMedia(event) {
2179
+ return event.message?.contact_message?.media_message;
2180
+ }
2181
+ function getPostbackData(event) {
2182
+ return event.message?.contact_message?.choice_response_message?.postback_data;
2183
+ }
2184
+ function getContactId(event) {
2185
+ return event.message?.contact_id;
2186
+ }
2187
+ function getConversationId(event) {
2188
+ return event.message?.conversation_id;
2189
+ }
2190
+ function getChannel(event) {
2191
+ return event.message?.channel_identity?.channel;
2192
+ }
2193
+ function getIdentity(event) {
2194
+ return event.message?.channel_identity?.identity;
2195
+ }
2196
+ function getTo(event) {
2197
+ return event.message?.sender_id;
2198
+ }
2199
+ function getLocation(event) {
2200
+ return event.message?.contact_message?.location_message;
2201
+ }
2202
+ function isTextMessage(event) {
2203
+ return event.message?.contact_message?.text_message !== void 0;
2204
+ }
2205
+ function isMediaMessage(event) {
2206
+ return event.message?.contact_message?.media_message !== void 0;
2207
+ }
2208
+ function isPostback(event) {
2209
+ return event.message?.contact_message?.choice_response_message !== void 0;
2210
+ }
2211
+ function createMessageHelper(event) {
2212
+ return {
2213
+ getText: () => getText(event),
2214
+ getMedia: () => getMedia(event),
2215
+ getPostbackData: () => getPostbackData(event),
2216
+ getContactId: () => getContactId(event),
2217
+ getConversationId: () => getConversationId(event),
2218
+ getChannel: () => getChannel(event),
2219
+ getIdentity: () => getIdentity(event),
2220
+ getTo: () => getTo(event),
2221
+ getLocation: () => getLocation(event),
2222
+ isTextMessage: () => isTextMessage(event),
2223
+ isMediaMessage: () => isMediaMessage(event),
2224
+ isPostback: () => isPostback(event)
2225
+ };
2226
+ }
2227
+
2228
+ // ../runtime-shared/dist/voice/helpers.js
2229
+ var COUNTRY_CODES = {
2230
+ "+1": { code: "US", format: (n) => `(${n.slice(0, 3)}) ${n.slice(3, 6)}-${n.slice(6)}` },
2231
+ "+44": { code: "GB", format: (n) => `0${n.slice(0, 4)} ${n.slice(4)}` },
2232
+ "+46": {
2233
+ code: "SE",
2234
+ format: (n) => `0${n.slice(0, 2)}-${n.slice(2, 5)} ${n.slice(5, 7)} ${n.slice(7)}`
2235
+ },
2236
+ "+49": { code: "DE", format: (n) => `0${n.slice(0, 3)} ${n.slice(3)}` },
2237
+ "+33": {
2238
+ code: "FR",
2239
+ format: (n) => `0${n.slice(0, 1)} ${n.slice(1, 3)} ${n.slice(3, 5)} ${n.slice(5, 7)} ${n.slice(7)}`
2240
+ },
2241
+ "+61": { code: "AU", format: (n) => `0${n.slice(0, 1)} ${n.slice(1, 5)} ${n.slice(5)}` }
2242
+ };
2243
+ function toLocalPhoneNumber(phoneNumber, defaultRegion = "US") {
2244
+ if (!phoneNumber || !phoneNumber.startsWith("+")) {
2245
+ return phoneNumber;
2246
+ }
2247
+ const sortedCodes = Object.keys(COUNTRY_CODES).sort((a, b) => b.length - a.length);
2248
+ for (const prefix of sortedCodes) {
2249
+ if (phoneNumber.startsWith(prefix)) {
2250
+ const national = phoneNumber.slice(prefix.length);
2251
+ try {
2252
+ return COUNTRY_CODES[prefix].format(national);
2253
+ } catch {
2254
+ return phoneNumber;
2255
+ }
2256
+ }
2257
+ }
2258
+ return phoneNumber.slice(1);
2259
+ }
2260
+ function formatPhoneNumber(phoneNumber) {
2261
+ const cleaned = phoneNumber.replace(/[^\d+]/g, "");
2262
+ if (cleaned.startsWith("+")) {
2263
+ return toLocalPhoneNumber(cleaned);
2264
+ }
2265
+ if (cleaned.length === 10) {
2266
+ return `(${cleaned.slice(0, 3)}) ${cleaned.slice(3, 6)}-${cleaned.slice(6)}`;
2267
+ }
2268
+ return phoneNumber;
2269
+ }
2270
+ var VoiceErrorHelper = {
2271
+ /**
2272
+ * Create a standard error response that plays a message and hangs up
2273
+ *
2274
+ * @param message - Error message to speak
2275
+ * @param locale - Language locale (default: "en-US")
2276
+ * @returns SVAML response object
2277
+ */
2278
+ createErrorResponse(message, locale = "en-US") {
2279
+ return new IceSvamlBuilder().say(message, locale).hangup().build();
2280
+ },
2281
+ /**
2282
+ * Create a service unavailable response
2283
+ *
2284
+ * @param locale - Language locale (default: "en-US")
2285
+ * @returns SVAML response object
2286
+ */
2287
+ serviceUnavailable(locale = "en-US") {
2288
+ return this.createErrorResponse("We're sorry, our service is temporarily unavailable. Please try again later.", locale);
2289
+ },
2290
+ /**
2291
+ * Create an invalid input response for PIE handlers
2292
+ *
2293
+ * @param locale - Language locale (default: "en-US")
2294
+ * @returns SVAML response object
2295
+ */
2296
+ invalidInput(locale = "en-US") {
2297
+ return new PieSvamlBuilder().say("Invalid selection. Please try again.", locale).continue().build();
2298
+ },
2299
+ /**
2300
+ * Create a timeout response for PIE handlers
2301
+ *
2302
+ * @param locale - Language locale (default: "en-US")
2303
+ * @returns SVAML response object
2304
+ */
2305
+ timeout(locale = "en-US") {
2306
+ return new PieSvamlBuilder().say("We did not receive a response. Please try again.", locale).continue().build();
2307
+ },
2308
+ /**
2309
+ * Create a goodbye response that thanks the caller and hangs up
2310
+ *
2311
+ * @param locale - Language locale (default: "en-US")
2312
+ * @returns SVAML response object
2313
+ */
2314
+ goodbye(locale = "en-US") {
2315
+ return new IceSvamlBuilder().say("Thank you for calling. Goodbye!", locale).hangup().build();
2316
+ }
2317
+ };
2318
+ function formatDuration(seconds) {
2319
+ if (seconds < 60) {
2320
+ return `${Math.round(seconds)}s`;
2321
+ }
2322
+ const minutes = Math.floor(seconds / 60);
2323
+ const remainingSeconds = Math.round(seconds % 60);
2324
+ if (remainingSeconds === 0) {
2325
+ return `${minutes}m`;
2326
+ }
2327
+ return `${minutes}m ${remainingSeconds}s`;
2328
+ }
2329
+ function extractCallerNumber(cli) {
2330
+ if (!cli)
2331
+ return void 0;
2332
+ return cli.replace(/[^\d+]/g, "");
2333
+ }
2334
+
2335
+ // ../runtime-shared/dist/utils/templateRender.js
2336
+ import fs from "fs";
2337
+ import path from "path";
2338
+
2339
+ // ../runtime-shared/dist/utils/versionExtractor.js
2340
+ import fs2 from "fs";
2341
+ import path2 from "path";
2342
+
1554
2343
  // ../runtime-shared/dist/utils/functionLoader.js
1555
2344
  import path3 from "path";
1556
2345
 
@@ -2213,7 +3002,14 @@ var SecretsLoader = class {
2213
3002
  var secretsLoader = new SecretsLoader();
2214
3003
  export {
2215
3004
  AceSvamlBuilder,
2216
- DefaultEndpointHandler,
3005
+ AgentProvider,
3006
+ ConversationController,
3007
+ ConversationMessage,
3008
+ ConversationMessageBuilder,
3009
+ ElevenLabsAutoConfigurator,
3010
+ ElevenLabsClient,
3011
+ ElevenLabsController,
3012
+ ElevenLabsState,
2217
3013
  IceSvamlBuilder,
2218
3014
  LocalCache,
2219
3015
  MenuBuilder,
@@ -2221,36 +3017,59 @@ export {
2221
3017
  NOTIFICATION_EVENTS,
2222
3018
  PieSvamlBuilder,
2223
3019
  SecretsLoader,
2224
- TemplateRender,
2225
3020
  TunnelClient,
2226
3021
  UniversalConfig,
2227
3022
  VOICE_CALLBACKS,
2228
- VersionExtractor,
3023
+ VoiceErrorHelper,
2229
3024
  buildBaseContext,
2230
3025
  toCamelCase as convertToCamelCase,
2231
3026
  createAceBuilder,
2232
3027
  createApp,
2233
3028
  createCacheClient,
3029
+ createChannelMessage,
2234
3030
  createConfig,
3031
+ createElevenLabsClient,
2235
3032
  createErrorResponse,
2236
3033
  createIceBuilder,
2237
3034
  createLenientJsonParser as createJsonParser,
2238
3035
  createJsonResponse,
2239
3036
  createLenientJsonParser,
2240
3037
  createMenu,
3038
+ createMessageBuilder,
3039
+ createMessageHelper,
3040
+ createMessenger,
2241
3041
  createPieBuilder,
2242
3042
  createResponse,
2243
3043
  createSimpleMenu,
3044
+ createSms,
2244
3045
  createUniversalConfig,
3046
+ createWhatsApp,
3047
+ extractCallerNumber,
2245
3048
  extractFunctionName,
2246
3049
  formatCustomResponse,
3050
+ formatDuration,
3051
+ formatPhoneNumber,
2247
3052
  formatSvamlResponse,
2248
3053
  generateRequestId,
3054
+ getChannel,
3055
+ getContactId,
3056
+ getConversationId,
3057
+ getFaviconSvg,
3058
+ getIdentity,
3059
+ getLandingPageHtml,
3060
+ getLocation,
3061
+ getMedia,
3062
+ getPostbackData,
2249
3063
  getSinchClients,
3064
+ getText,
3065
+ getTo,
2250
3066
  getTunnelClient,
2251
3067
  handleCustomEndpoint,
2252
3068
  handleVoiceCallback,
3069
+ isMediaMessage,
2253
3070
  isNotificationEvent,
3071
+ isPostback,
3072
+ isTextMessage,
2254
3073
  isVoiceCallback,
2255
3074
  parseJson,
2256
3075
  parseJson as parseJsonLenient,
@@ -2259,9 +3078,12 @@ export {
2259
3078
  setupJsonParsing as setupJsonMiddleware,
2260
3079
  setupJsonParsing,
2261
3080
  setupRequestHandler,
3081
+ textReply,
2262
3082
  toCamelCase,
3083
+ toLocalPhoneNumber,
2263
3084
  transformKeys,
2264
3085
  transformKeys as transformKeysToCamelCase,
3086
+ tryAutoConfigureAsync,
2265
3087
  validateVoiceRequest
2266
3088
  };
2267
3089
  //# sourceMappingURL=index.js.map