@absolutejs/voice 0.0.22-beta.114 → 0.0.22-beta.116

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.
@@ -0,0 +1,90 @@
1
+ import { applyVoiceCampaignTelephonyOutcome, VoiceCampaignDialer, VoiceCampaignDialerInput, type VoiceCampaignRecord, type VoiceCampaignStore, type VoiceCampaignTickResult } from './campaign';
2
+ type VoiceCampaignDialerFetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
3
+ type VoiceCampaignDialerMetadataInput = VoiceCampaignDialerInput & {
4
+ provider: 'plivo' | 'telnyx' | 'twilio';
5
+ };
6
+ type VoiceCampaignDialerMetadata = Record<string, string | number | boolean | undefined> | ((input: VoiceCampaignDialerMetadataInput) => Record<string, string | number | boolean | undefined> | Promise<Record<string, string | number | boolean | undefined>>);
7
+ type VoiceCampaignURLInput = {
8
+ attempt: VoiceCampaignDialerInput['attempt'];
9
+ campaign: VoiceCampaignDialerInput['campaign'];
10
+ recipient: VoiceCampaignDialerInput['recipient'];
11
+ };
12
+ type VoiceCampaignURLResolver = string | ((input: VoiceCampaignURLInput) => string | Promise<string>);
13
+ export type VoiceTwilioCampaignDialerOptions = {
14
+ accountSid: string;
15
+ answerMethod?: 'GET' | 'POST';
16
+ answerUrl: VoiceCampaignURLResolver;
17
+ apiBaseUrl?: string;
18
+ authToken: string;
19
+ fetch?: VoiceCampaignDialerFetch;
20
+ from: string;
21
+ machineDetection?: string;
22
+ metadata?: VoiceCampaignDialerMetadata;
23
+ statusCallbackEvents?: string[];
24
+ statusCallbackMethod?: 'GET' | 'POST';
25
+ statusCallbackUrl?: VoiceCampaignURLResolver;
26
+ };
27
+ export type VoiceTelnyxCampaignDialerOptions = {
28
+ apiBaseUrl?: string;
29
+ apiKey: string;
30
+ clientState?: VoiceCampaignDialerMetadata;
31
+ connectionId: string;
32
+ fetch?: VoiceCampaignDialerFetch;
33
+ from: string;
34
+ webhookUrl?: VoiceCampaignURLResolver;
35
+ webhookUrlMethod?: 'GET' | 'POST';
36
+ };
37
+ export type VoicePlivoCampaignDialerOptions = {
38
+ answerMethod?: 'GET' | 'POST';
39
+ answerUrl: VoiceCampaignURLResolver;
40
+ apiBaseUrl?: string;
41
+ authId: string;
42
+ authToken: string;
43
+ callbackMethod?: 'GET' | 'POST';
44
+ callbackUrl?: VoiceCampaignURLResolver;
45
+ fetch?: VoiceCampaignDialerFetch;
46
+ from: string;
47
+ metadata?: VoiceCampaignDialerMetadata;
48
+ };
49
+ export type VoiceCampaignDialerProofProvider = 'plivo' | 'telnyx' | 'twilio';
50
+ export type VoiceCampaignDialerProofCarrierRequest = {
51
+ body: unknown;
52
+ method: string;
53
+ provider: VoiceCampaignDialerProofProvider;
54
+ url: string;
55
+ };
56
+ export type VoiceCampaignDialerProofProviderResult = {
57
+ campaignId: string;
58
+ carrierRequests: VoiceCampaignDialerProofCarrierRequest[];
59
+ final?: VoiceCampaignRecord;
60
+ outcomes: Awaited<ReturnType<typeof applyVoiceCampaignTelephonyOutcome>>[];
61
+ provider: VoiceCampaignDialerProofProvider;
62
+ tick: VoiceCampaignTickResult;
63
+ };
64
+ export type VoiceCampaignDialerProofReport = {
65
+ generatedAt: number;
66
+ mode: 'dry-run';
67
+ ok: boolean;
68
+ providers: VoiceCampaignDialerProofProviderResult[];
69
+ };
70
+ export type VoiceCampaignDialerProofStatus = {
71
+ generatedAt: number;
72
+ mode: 'dry-run';
73
+ ok: boolean;
74
+ providers: VoiceCampaignDialerProofProvider[];
75
+ runPath?: string;
76
+ safe: true;
77
+ };
78
+ export type VoiceCampaignDialerProofOptions = {
79
+ baseUrl?: string;
80
+ from?: string;
81
+ providers?: VoiceCampaignDialerProofProvider[];
82
+ runPath?: string;
83
+ store?: VoiceCampaignStore;
84
+ };
85
+ export declare const createVoiceTwilioCampaignDialer: (options: VoiceTwilioCampaignDialerOptions) => VoiceCampaignDialer;
86
+ export declare const createVoiceTelnyxCampaignDialer: (options: VoiceTelnyxCampaignDialerOptions) => VoiceCampaignDialer;
87
+ export declare const createVoicePlivoCampaignDialer: (options: VoicePlivoCampaignDialerOptions) => VoiceCampaignDialer;
88
+ export declare const getVoiceCampaignDialerProofStatus: (options?: Pick<VoiceCampaignDialerProofOptions, "providers" | "runPath">) => VoiceCampaignDialerProofStatus;
89
+ export declare const runVoiceCampaignDialerProof: (options?: VoiceCampaignDialerProofOptions) => Promise<VoiceCampaignDialerProofReport>;
90
+ export {};
package/dist/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { voice } from './plugin';
2
2
  export { createVoiceAppKit, createVoiceAppKitRoutes, summarizeVoiceAppKitStatus } from './appKit';
3
3
  export { applyVoiceCampaignTelephonyOutcome, buildVoiceCampaignObservabilityReport, createVoiceCampaignTelephonyOutcomeHandler, createVoiceCampaign, createVoiceCampaignRoutes, createVoiceCampaignWorker, createVoiceCampaignWorkerLoop, createVoiceMemoryCampaignStore, renderVoiceCampaignObservabilityHTML, renderVoiceCampaignsHTML, runVoiceCampaignProof, summarizeVoiceCampaigns } from './campaign';
4
+ export { createVoicePlivoCampaignDialer, createVoiceTelnyxCampaignDialer, createVoiceTwilioCampaignDialer, getVoiceCampaignDialerProofStatus, runVoiceCampaignDialerProof } from './campaignDialers';
4
5
  export { createVoiceAssistant, createVoiceExperiment, summarizeVoiceAssistantRuns } from './assistant';
5
6
  export { createVoiceAssistantHealthHTMLHandler, createVoiceAssistantHealthJSONHandler, createVoiceAssistantHealthRoutes, renderVoiceAssistantHealthHTML, summarizeVoiceAssistantHealth } from './assistantHealth';
6
7
  export { createVoiceBargeInRoutes, renderVoiceBargeInHTML, summarizeVoiceBargeIn } from './bargeInRoutes';
@@ -55,6 +56,7 @@ export { resolveTurnDetectionConfig, TURN_PROFILE_DEFAULTS } from './turnProfile
55
56
  export { createVoiceCallReviewFromLiveTelephonyReport, createVoiceCallReviewRecorder, renderVoiceCallReviewHTML, renderVoiceCallReviewMarkdown } from './testing/review';
56
57
  export type { VoiceAppKitLink, VoiceAppKitRoutes, VoiceAppKitRoutesOptions, VoiceAppKitStatus, VoiceAppKitStatusOptions, VoiceAppKitStatusReport, VoiceAppKitSurface } from './appKit';
57
58
  export type { VoiceCampaign, VoiceCampaignAttempt, VoiceCampaignAttemptResultInput, VoiceCampaignAttemptStatus, VoiceCampaignCreateInput, VoiceCampaignDialer, VoiceCampaignDialerInput, VoiceCampaignDialerResult, VoiceCampaignProofOptions, VoiceCampaignProofReport, VoiceCampaignRecipient, VoiceCampaignRecipientInput, VoiceCampaignRecipientStatus, VoiceCampaignRecord, VoiceCampaignRoutesOptions, VoiceCampaignRuntime, VoiceCampaignRuntimeOptions, VoiceCampaignStatus, VoiceCampaignStore, VoiceCampaignSummary, VoiceCampaignTickResult } from './campaign';
59
+ export type { VoiceCampaignDialerProofCarrierRequest, VoiceCampaignDialerProofOptions, VoiceCampaignDialerProofProvider, VoiceCampaignDialerProofProviderResult, VoiceCampaignDialerProofReport, VoiceCampaignDialerProofStatus, VoicePlivoCampaignDialerOptions, VoiceTelnyxCampaignDialerOptions, VoiceTwilioCampaignDialerOptions } from './campaignDialers';
58
60
  export type { VoiceBargeInReport, VoiceBargeInRoutesOptions } from './bargeInRoutes';
59
61
  export type { VoiceAssistant, VoiceAssistantArtifactPlan, VoiceAssistantExperiment, VoiceAssistantExperimentOptions, VoiceAssistantGuardrailInput, VoiceAssistantGuardrails, VoiceAssistantMemoryLifecycle, VoiceAssistantMemoryLifecycleInput, VoiceAssistantOptions, VoiceAssistantOutputGuardrailInput, VoiceAssistantPreset, VoiceAssistantRunsSummary, VoiceAssistantRunSummary, VoiceAssistantVariant } from './assistant';
60
62
  export type { VoiceAssistantHealthFailure, VoiceAssistantHealthHTMLHandlerOptions, VoiceAssistantHealthRoutesOptions, VoiceAssistantHealthSummary, VoiceAssistantHealthSummaryOptions } from './assistantHealth';
package/dist/index.js CHANGED
@@ -11256,6 +11256,358 @@ var createVoiceCampaignRoutes = (options) => {
11256
11256
  }
11257
11257
  return app;
11258
11258
  };
11259
+ // src/campaignDialers.ts
11260
+ import { Buffer as Buffer3 } from "buffer";
11261
+ var resolveFetch = (fetcher) => {
11262
+ const activeFetch = fetcher ?? globalThis.fetch;
11263
+ if (!activeFetch) {
11264
+ throw new Error("Campaign dialer requires fetch or globalThis.fetch.");
11265
+ }
11266
+ return activeFetch;
11267
+ };
11268
+ var resolveURL = async (resolver, input) => {
11269
+ if (!resolver) {
11270
+ return;
11271
+ }
11272
+ return typeof resolver === "function" ? await resolver(input) : resolver;
11273
+ };
11274
+ var baseCampaignMetadata = (input) => ({
11275
+ attemptId: input.attempt.id,
11276
+ campaignId: input.campaign.id,
11277
+ recipientId: input.recipient.id,
11278
+ voiceCampaignAttemptId: input.attempt.id,
11279
+ voiceCampaignId: input.campaign.id,
11280
+ voiceCampaignRecipientId: input.recipient.id
11281
+ });
11282
+ var resolveMetadata = async (metadata, input) => ({
11283
+ ...baseCampaignMetadata(input),
11284
+ ...typeof metadata === "function" ? await metadata(input) : metadata
11285
+ });
11286
+ var appendQuery = (url, params) => {
11287
+ if (!url) {
11288
+ return;
11289
+ }
11290
+ const parsed = new URL(url);
11291
+ for (const [key, value] of Object.entries(params)) {
11292
+ if (value !== undefined) {
11293
+ parsed.searchParams.set(key, String(value));
11294
+ }
11295
+ }
11296
+ return parsed.toString();
11297
+ };
11298
+ var readJson = async (response) => {
11299
+ const text = await response.text();
11300
+ if (!text.trim()) {
11301
+ return {};
11302
+ }
11303
+ try {
11304
+ return JSON.parse(text);
11305
+ } catch {
11306
+ return {
11307
+ text
11308
+ };
11309
+ }
11310
+ };
11311
+ var assertOk = async (response, provider) => {
11312
+ const body = await readJson(response);
11313
+ if (!response.ok) {
11314
+ throw new Error(`${provider} campaign dialer failed with ${response.status}: ${JSON.stringify(body)}`);
11315
+ }
11316
+ return body;
11317
+ };
11318
+ var firstString2 = (values) => {
11319
+ for (const value of values) {
11320
+ if (typeof value === "string" && value.trim()) {
11321
+ return value.trim();
11322
+ }
11323
+ }
11324
+ };
11325
+ var formBody = (values) => {
11326
+ const body = new URLSearchParams;
11327
+ for (const [key, value] of Object.entries(values)) {
11328
+ if (Array.isArray(value)) {
11329
+ for (const item of value) {
11330
+ if (item !== undefined) {
11331
+ body.append(key, String(item));
11332
+ }
11333
+ }
11334
+ continue;
11335
+ }
11336
+ if (value !== undefined) {
11337
+ body.set(key, String(value));
11338
+ }
11339
+ }
11340
+ return body;
11341
+ };
11342
+ var createVoiceTwilioCampaignDialer = (options) => async (input) => {
11343
+ const fetcher = resolveFetch(options.fetch);
11344
+ const metadata = await resolveMetadata(options.metadata, {
11345
+ ...input,
11346
+ provider: "twilio"
11347
+ });
11348
+ const answerUrl = appendQuery(await resolveURL(options.answerUrl, input), metadata);
11349
+ const statusCallbackUrl = appendQuery(await resolveURL(options.statusCallbackUrl, input), metadata);
11350
+ const response = await fetcher(`${options.apiBaseUrl ?? "https://api.twilio.com"}/2010-04-01/Accounts/${encodeURIComponent(options.accountSid)}/Calls.json`, {
11351
+ body: formBody({
11352
+ From: options.from,
11353
+ MachineDetection: options.machineDetection,
11354
+ Method: options.answerMethod,
11355
+ StatusCallback: statusCallbackUrl,
11356
+ StatusCallbackEvent: options.statusCallbackEvents,
11357
+ StatusCallbackMethod: options.statusCallbackMethod,
11358
+ To: input.recipient.phone,
11359
+ Url: answerUrl
11360
+ }),
11361
+ headers: {
11362
+ authorization: `Basic ${Buffer3.from(`${options.accountSid}:${options.authToken}`).toString("base64")}`,
11363
+ "content-type": "application/x-www-form-urlencoded"
11364
+ },
11365
+ method: "POST"
11366
+ });
11367
+ const body = await assertOk(response, "Twilio");
11368
+ return {
11369
+ externalCallId: firstString2([body.sid, body.callSid, body.call_sid]),
11370
+ metadata: {
11371
+ provider: "twilio",
11372
+ response: body
11373
+ },
11374
+ status: "running"
11375
+ };
11376
+ };
11377
+ var createVoiceTelnyxCampaignDialer = (options) => async (input) => {
11378
+ const fetcher = resolveFetch(options.fetch);
11379
+ const metadata = await resolveMetadata(options.clientState, {
11380
+ ...input,
11381
+ provider: "telnyx"
11382
+ });
11383
+ const webhookUrl = appendQuery(await resolveURL(options.webhookUrl, input), metadata);
11384
+ const response = await fetcher(`${options.apiBaseUrl ?? "https://api.telnyx.com"}/v2/calls`, {
11385
+ body: JSON.stringify({
11386
+ client_state: Buffer3.from(JSON.stringify(metadata)).toString("base64"),
11387
+ connection_id: options.connectionId,
11388
+ from: options.from,
11389
+ to: input.recipient.phone,
11390
+ webhook_url: webhookUrl,
11391
+ webhook_url_method: options.webhookUrlMethod
11392
+ }),
11393
+ headers: {
11394
+ authorization: `Bearer ${options.apiKey}`,
11395
+ "content-type": "application/json"
11396
+ },
11397
+ method: "POST"
11398
+ });
11399
+ const body = await assertOk(response, "Telnyx");
11400
+ const data = typeof body.data === "object" && body.data !== null ? body.data : body;
11401
+ return {
11402
+ externalCallId: firstString2([
11403
+ data.call_control_id,
11404
+ data.call_session_id,
11405
+ data.call_leg_id,
11406
+ body.call_control_id,
11407
+ body.call_session_id
11408
+ ]),
11409
+ metadata: {
11410
+ provider: "telnyx",
11411
+ response: body
11412
+ },
11413
+ status: "running"
11414
+ };
11415
+ };
11416
+ var createVoicePlivoCampaignDialer = (options) => async (input) => {
11417
+ const fetcher = resolveFetch(options.fetch);
11418
+ const metadata = await resolveMetadata(options.metadata, {
11419
+ ...input,
11420
+ provider: "plivo"
11421
+ });
11422
+ const answerUrl = appendQuery(await resolveURL(options.answerUrl, input), metadata);
11423
+ const callbackUrl = appendQuery(await resolveURL(options.callbackUrl, input), metadata);
11424
+ const response = await fetcher(`${options.apiBaseUrl ?? "https://api.plivo.com"}/v1/Account/${encodeURIComponent(options.authId)}/Call/`, {
11425
+ body: formBody({
11426
+ answer_method: options.answerMethod,
11427
+ answer_url: answerUrl,
11428
+ callback_method: options.callbackMethod,
11429
+ callback_url: callbackUrl,
11430
+ from: options.from,
11431
+ to: input.recipient.phone
11432
+ }),
11433
+ headers: {
11434
+ authorization: `Basic ${Buffer3.from(`${options.authId}:${options.authToken}`).toString("base64")}`,
11435
+ "content-type": "application/x-www-form-urlencoded"
11436
+ },
11437
+ method: "POST"
11438
+ });
11439
+ const body = await assertOk(response, "Plivo");
11440
+ return {
11441
+ externalCallId: firstString2([
11442
+ body.request_uuid,
11443
+ body.requestUuid,
11444
+ body.call_uuid,
11445
+ body.callUuid
11446
+ ]),
11447
+ metadata: {
11448
+ provider: "plivo",
11449
+ response: body
11450
+ },
11451
+ status: "running"
11452
+ };
11453
+ };
11454
+ var campaignDialerProofProviders = [
11455
+ "twilio",
11456
+ "telnyx",
11457
+ "plivo"
11458
+ ];
11459
+ var joinUrlPath = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
11460
+ var serializeProofRequestBody = (body) => {
11461
+ if (body instanceof URLSearchParams) {
11462
+ return Object.fromEntries(body.entries());
11463
+ }
11464
+ if (typeof body === "string") {
11465
+ try {
11466
+ return JSON.parse(body);
11467
+ } catch {
11468
+ return body;
11469
+ }
11470
+ }
11471
+ return body ? String(body) : undefined;
11472
+ };
11473
+ var createProofFetch = (provider, requests) => {
11474
+ let sequence = 0;
11475
+ return async (url, init) => {
11476
+ sequence += 1;
11477
+ requests.push({
11478
+ body: serializeProofRequestBody(init?.body),
11479
+ method: init?.method ?? "GET",
11480
+ provider,
11481
+ url: String(url)
11482
+ });
11483
+ const externalCallId = `dry-run-${provider}-${sequence}`;
11484
+ const payload = provider === "twilio" ? { sid: externalCallId } : provider === "telnyx" ? { data: { call_control_id: externalCallId } } : { request_uuid: externalCallId };
11485
+ return Response.json(payload);
11486
+ };
11487
+ };
11488
+ var createProofDialer = (input) => {
11489
+ const fetch2 = createProofFetch(input.provider, input.requests);
11490
+ if (input.provider === "twilio") {
11491
+ return createVoiceTwilioCampaignDialer({
11492
+ accountSid: "AC_dry_run",
11493
+ answerUrl: joinUrlPath(input.baseUrl, "/api/twilio/voice"),
11494
+ apiBaseUrl: "https://twilio.dry-run.absolutejs.local",
11495
+ authToken: "dry-run-token",
11496
+ fetch: fetch2,
11497
+ from: input.from,
11498
+ statusCallbackEvents: ["answered", "completed"],
11499
+ statusCallbackUrl: joinUrlPath(input.baseUrl, "/api/telephony-webhook")
11500
+ });
11501
+ }
11502
+ if (input.provider === "telnyx") {
11503
+ return createVoiceTelnyxCampaignDialer({
11504
+ apiBaseUrl: "https://telnyx.dry-run.absolutejs.local",
11505
+ apiKey: "dry-run-token",
11506
+ connectionId: "dry-run-connection",
11507
+ fetch: fetch2,
11508
+ from: input.from,
11509
+ webhookUrl: joinUrlPath(input.baseUrl, "/api/telnyx/webhook")
11510
+ });
11511
+ }
11512
+ return createVoicePlivoCampaignDialer({
11513
+ answerUrl: joinUrlPath(input.baseUrl, "/api/plivo/voice"),
11514
+ apiBaseUrl: "https://plivo.dry-run.absolutejs.local",
11515
+ authId: "dry-run-auth-id",
11516
+ authToken: "dry-run-token",
11517
+ callbackUrl: joinUrlPath(input.baseUrl, "/api/plivo/webhook"),
11518
+ fetch: fetch2,
11519
+ from: input.from
11520
+ });
11521
+ };
11522
+ var runCampaignDialerProofForProvider = async (input) => {
11523
+ const carrierRequests = [];
11524
+ const runtime = createVoiceCampaign({
11525
+ dialer: createProofDialer({
11526
+ baseUrl: input.baseUrl,
11527
+ from: input.from,
11528
+ provider: input.provider,
11529
+ requests: carrierRequests
11530
+ }),
11531
+ store: input.store
11532
+ });
11533
+ const proof = await runVoiceCampaignProof({
11534
+ campaign: {
11535
+ description: "Dry-run carrier dialer proof with campaign metadata and webhook outcome resolution.",
11536
+ id: `campaign-dialer-proof-${input.provider}-${crypto.randomUUID()}`,
11537
+ metadata: {
11538
+ mode: "dry-run",
11539
+ provider: input.provider
11540
+ },
11541
+ name: `AbsoluteJS Voice ${input.provider} Campaign Dialer Proof`
11542
+ },
11543
+ completeAttempts: false,
11544
+ recipients: [
11545
+ {
11546
+ id: `dialer-proof-${input.provider}-recipient`,
11547
+ metadata: {
11548
+ provider: input.provider,
11549
+ source: "campaign-dialer-proof"
11550
+ },
11551
+ name: `${input.provider} dry-run recipient`,
11552
+ phone: "+15550001001"
11553
+ }
11554
+ ],
11555
+ runtime
11556
+ });
11557
+ const outcomes = await Promise.all(proof.tick.started.map((attempt) => applyVoiceCampaignTelephonyOutcome({
11558
+ decision: {
11559
+ action: "complete",
11560
+ confidence: "high",
11561
+ disposition: "completed",
11562
+ source: "status"
11563
+ },
11564
+ event: {
11565
+ metadata: {
11566
+ attemptId: attempt.id,
11567
+ campaignId: attempt.campaignId,
11568
+ externalCallId: attempt.externalCallId,
11569
+ voiceCampaignAttemptId: attempt.id,
11570
+ voiceCampaignId: attempt.campaignId
11571
+ },
11572
+ provider: input.provider,
11573
+ status: "completed"
11574
+ }
11575
+ }, {
11576
+ runtime
11577
+ })));
11578
+ return {
11579
+ campaignId: proof.campaign.campaign.id,
11580
+ carrierRequests,
11581
+ final: await runtime.get(proof.campaign.campaign.id),
11582
+ outcomes,
11583
+ provider: input.provider,
11584
+ tick: proof.tick
11585
+ };
11586
+ };
11587
+ var getVoiceCampaignDialerProofStatus = (options = {}) => ({
11588
+ generatedAt: Date.now(),
11589
+ mode: "dry-run",
11590
+ ok: true,
11591
+ providers: options.providers ?? campaignDialerProofProviders,
11592
+ runPath: options.runPath,
11593
+ safe: true
11594
+ });
11595
+ var runVoiceCampaignDialerProof = async (options = {}) => {
11596
+ const providers = options.providers ?? campaignDialerProofProviders;
11597
+ const store = options.store ?? createVoiceMemoryCampaignStore();
11598
+ const results = await Promise.all(providers.map((provider) => runCampaignDialerProofForProvider({
11599
+ baseUrl: options.baseUrl ?? "http://localhost",
11600
+ from: options.from ?? "+15550009999",
11601
+ provider,
11602
+ store
11603
+ })));
11604
+ return {
11605
+ generatedAt: Date.now(),
11606
+ mode: "dry-run",
11607
+ ok: results.every((result) => result.carrierRequests.length > 0 && result.outcomes.every((outcome) => outcome.applied)),
11608
+ providers: results
11609
+ };
11610
+ };
11259
11611
  // src/simulationSuite.ts
11260
11612
  import { Elysia as Elysia20 } from "elysia";
11261
11613
 
@@ -12690,7 +13042,7 @@ var createMemoryVoiceTelephonyWebhookIdempotencyStore = () => {
12690
13042
  };
12691
13043
  };
12692
13044
  var normalizeToken = (value) => typeof value === "string" ? value.trim().toLowerCase().replace(/\s+/g, "-").replace(/_+/g, "-") : undefined;
12693
- var firstString2 = (source, keys) => {
13045
+ var firstString3 = (source, keys) => {
12694
13046
  for (const key of keys) {
12695
13047
  const value = source[key];
12696
13048
  if (typeof value === "string" && value.trim()) {
@@ -13062,8 +13414,8 @@ var verifyVoiceTelephonyWebhook = async (input) => {
13062
13414
  var durationMsFromSeconds = (value) => typeof value === "number" ? value * 1000 : undefined;
13063
13415
  var parseVoiceTelephonyWebhookEvent = (input) => {
13064
13416
  const payload = flattenPayload(input.body);
13065
- const provider = firstString2(payload, ["provider", "Provider"]) ?? input.provider;
13066
- const status = firstString2(payload, [
13417
+ const provider = firstString3(payload, ["provider", "Provider"]) ?? input.provider;
13418
+ const status = firstString3(payload, [
13067
13419
  "CallStatus",
13068
13420
  "call_status",
13069
13421
  "callStatus",
@@ -13088,9 +13440,9 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
13088
13440
  "sip_code",
13089
13441
  "hangupCauseCode"
13090
13442
  ]);
13091
- const from = firstString2(payload, ["From", "from", "caller_id", "callerId"]);
13092
- const to = firstString2(payload, ["To", "to", "called_number", "calledNumber"]);
13093
- const target = firstString2(payload, [
13443
+ const from = firstString3(payload, ["From", "from", "caller_id", "callerId"]);
13444
+ const to = firstString3(payload, ["To", "to", "called_number", "calledNumber"]);
13445
+ const target = firstString3(payload, [
13094
13446
  "transferTarget",
13095
13447
  "TransferTarget",
13096
13448
  "target",
@@ -13098,7 +13450,7 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
13098
13450
  "department"
13099
13451
  ]);
13100
13452
  return {
13101
- answeredBy: firstString2(payload, [
13453
+ answeredBy: firstString3(payload, [
13102
13454
  "AnsweredBy",
13103
13455
  "answered_by",
13104
13456
  "answeredBy",
@@ -13107,9 +13459,12 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
13107
13459
  ]),
13108
13460
  durationMs,
13109
13461
  from,
13110
- metadata: payload,
13462
+ metadata: {
13463
+ ...input.query,
13464
+ ...payload
13465
+ },
13111
13466
  provider,
13112
- reason: firstString2(payload, [
13467
+ reason: firstString3(payload, [
13113
13468
  "Reason",
13114
13469
  "reason",
13115
13470
  "HangupCause",
@@ -13125,7 +13480,7 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
13125
13480
  var defaultSessionId = (input) => {
13126
13481
  const payload = flattenPayload(input.body);
13127
13482
  const metadataSessionId = input.event.metadata?.sessionId;
13128
- return firstString2(input.query, ["sessionId", "session_id"]) ?? firstString2(payload, [
13483
+ return firstString3(input.query, ["sessionId", "session_id"]) ?? firstString3(payload, [
13129
13484
  "sessionId",
13130
13485
  "session_id",
13131
13486
  "SessionId",
@@ -13140,7 +13495,7 @@ var defaultSessionId = (input) => {
13140
13495
  };
13141
13496
  var defaultIdempotencyKey = (input) => {
13142
13497
  const payload = flattenPayload(input.body);
13143
- const eventId = firstString2(payload, [
13498
+ const eventId = firstString3(payload, [
13144
13499
  "id",
13145
13500
  "event_id",
13146
13501
  "eventId",
@@ -13301,11 +13656,11 @@ var createVoiceTelephonyWebhookRoutes = (options = {}) => {
13301
13656
  import { Elysia as Elysia28 } from "elysia";
13302
13657
 
13303
13658
  // src/telephony/plivo.ts
13304
- import { Buffer as Buffer4 } from "buffer";
13659
+ import { Buffer as Buffer5 } from "buffer";
13305
13660
  import { Elysia as Elysia26 } from "elysia";
13306
13661
 
13307
13662
  // src/telephony/twilio.ts
13308
- import { Buffer as Buffer3 } from "buffer";
13663
+ import { Buffer as Buffer4 } from "buffer";
13309
13664
  import { Elysia as Elysia25 } from "elysia";
13310
13665
  var TWILIO_MULAW_SAMPLE_RATE = 8000;
13311
13666
  var VOICE_PCM_SAMPLE_RATE = 16000;
@@ -13335,7 +13690,7 @@ var resolveTwilioStreamParameters = async (parameters, input) => {
13335
13690
  }
13336
13691
  return parameters;
13337
13692
  };
13338
- var joinUrlPath = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
13693
+ var joinUrlPath2 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
13339
13694
  var escapeHtml25 = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&#39;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
13340
13695
  var getWebhookVerificationUrl = (webhook, input) => {
13341
13696
  if (!webhook?.verificationUrl) {
@@ -13349,8 +13704,8 @@ var getWebhookVerificationUrl = (webhook, input) => {
13349
13704
  var buildTwilioVoiceSetupStatus = async (options, input) => {
13350
13705
  const origin = resolveRequestOrigin(input.request);
13351
13706
  const stream = await resolveTwilioStreamUrl(options, input);
13352
- const twiml = joinUrlPath(origin, input.twimlPath);
13353
- const webhook = joinUrlPath(origin, input.webhookPath);
13707
+ const twiml = joinUrlPath2(origin, input.twimlPath);
13708
+ const webhook = joinUrlPath2(origin, input.webhookPath);
13354
13709
  const verificationUrl = getWebhookVerificationUrl(options.webhook, input);
13355
13710
  const missing = Object.entries(options.setup?.requiredEnv ?? {}).filter((entry) => !entry[1]).map(([name]) => name);
13356
13711
  const signingConfigured = Boolean(options.webhook?.signingSecret || options.webhook?.verify);
@@ -13596,7 +13951,7 @@ var bytesToInt16Array = (bytes) => {
13596
13951
  return output;
13597
13952
  };
13598
13953
  var decodeTwilioMulawBase64 = (payload) => {
13599
- const bytes = Uint8Array.from(Buffer3.from(payload, "base64"));
13954
+ const bytes = Uint8Array.from(Buffer4.from(payload, "base64"));
13600
13955
  const samples = new Int16Array(bytes.length);
13601
13956
  for (let index = 0;index < bytes.length; index += 1) {
13602
13957
  samples[index] = decodeMulawSample(bytes[index] ?? 0);
@@ -13608,7 +13963,7 @@ var encodeTwilioMulawBase64 = (samples) => {
13608
13963
  for (let index = 0;index < samples.length; index += 1) {
13609
13964
  bytes[index] = encodeMulawSample(samples[index] ?? 0);
13610
13965
  }
13611
- return Buffer3.from(bytes).toString("base64");
13966
+ return Buffer4.from(bytes).toString("base64");
13612
13967
  };
13613
13968
  var transcodeTwilioInboundPayloadToPCM16 = (payload) => {
13614
13969
  const narrowband = decodeTwilioMulawBase64(payload);
@@ -13617,7 +13972,7 @@ var transcodeTwilioInboundPayloadToPCM16 = (payload) => {
13617
13972
  };
13618
13973
  var transcodePCMToTwilioOutboundPayload = (chunk, format) => {
13619
13974
  if (format.container === "raw" && format.encoding === "mulaw" && format.channels === 1 && format.sampleRateHz === TWILIO_MULAW_SAMPLE_RATE) {
13620
- return Buffer3.from(chunk).toString("base64");
13975
+ return Buffer4.from(chunk).toString("base64");
13621
13976
  }
13622
13977
  if (format.encoding !== "pcm_s16le") {
13623
13978
  throw new Error(`Unsupported outbound telephony audio format: ${format.container}/${format.encoding}`);
@@ -13658,7 +14013,7 @@ var createTwilioSocketAdapter = (socket, getState) => ({
13658
14013
  return;
13659
14014
  }
13660
14015
  if (message.type === "audio") {
13661
- const payload = transcodePCMToTwilioOutboundPayload(Uint8Array.from(Buffer3.from(message.chunkBase64, "base64")), message.format);
14016
+ const payload = transcodePCMToTwilioOutboundPayload(Uint8Array.from(Buffer4.from(message.chunkBase64, "base64")), message.format);
13662
14017
  state.hasOutboundAudioSinceLastInbound = true;
13663
14018
  state.reviewRecorder?.recordTwilioOutbound({
13664
14019
  bytes: payload.length,
@@ -14017,7 +14372,7 @@ var createTwilioVoiceRoutes = (options) => {
14017
14372
  // src/telephony/plivo.ts
14018
14373
  var escapeXml3 = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&apos;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
14019
14374
  var escapeHtml26 = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&#39;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
14020
- var joinUrlPath2 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
14375
+ var joinUrlPath3 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
14021
14376
  var resolveRequestOrigin2 = (request) => {
14022
14377
  const url = new URL(request.url);
14023
14378
  const forwardedHost = request.headers.get("x-forwarded-host");
@@ -14184,7 +14539,7 @@ var createPlivoMediaStreamBridge = (socket, options) => {
14184
14539
  }
14185
14540
  };
14186
14541
  };
14187
- var toBase642 = (bytes) => Buffer4.from(new Uint8Array(bytes)).toString("base64");
14542
+ var toBase642 = (bytes) => Buffer5.from(new Uint8Array(bytes)).toString("base64");
14188
14543
  var timingSafeEqual2 = (left, right) => {
14189
14544
  const encoder = new TextEncoder;
14190
14545
  const leftBytes = encoder.encode(left);
@@ -14238,8 +14593,8 @@ var verifyVoicePlivoWebhookSignature = async (input) => {
14238
14593
  var buildPlivoVoiceSetupStatus = async (options, input) => {
14239
14594
  const origin = resolveRequestOrigin2(input.request);
14240
14595
  const stream = await resolvePlivoStreamUrl(options, input);
14241
- const answer = joinUrlPath2(origin, input.answerPath);
14242
- const webhook = joinUrlPath2(origin, input.webhookPath);
14596
+ const answer = joinUrlPath3(origin, input.answerPath);
14597
+ const webhook = joinUrlPath3(origin, input.webhookPath);
14243
14598
  const missing = Object.entries(options.setup?.requiredEnv ?? {}).filter((entry) => !entry[1]).map(([name]) => name);
14244
14599
  const signingConfigured = Boolean(options.webhook?.authToken || options.webhook?.verify);
14245
14600
  const warnings = [
@@ -14486,11 +14841,11 @@ var createPlivoVoiceRoutes = (options = {}) => {
14486
14841
  };
14487
14842
 
14488
14843
  // src/telephony/telnyx.ts
14489
- import { Buffer as Buffer5 } from "buffer";
14844
+ import { Buffer as Buffer6 } from "buffer";
14490
14845
  import { Elysia as Elysia27 } from "elysia";
14491
14846
  var escapeXml4 = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&apos;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
14492
14847
  var escapeHtml27 = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&#39;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
14493
- var joinUrlPath3 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
14848
+ var joinUrlPath4 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
14494
14849
  var resolveRequestOrigin3 = (request) => {
14495
14850
  const url = new URL(request.url);
14496
14851
  const forwardedHost = request.headers.get("x-forwarded-host");
@@ -14636,7 +14991,7 @@ var createTelnyxMediaStreamBridge = (socket, options) => {
14636
14991
  }
14637
14992
  };
14638
14993
  };
14639
- var decodeBase64 = (value) => Uint8Array.from(Buffer5.from(value, "base64"));
14994
+ var decodeBase64 = (value) => Uint8Array.from(Buffer6.from(value, "base64"));
14640
14995
  var verifyVoiceTelnyxWebhookSignature = async (input) => {
14641
14996
  if (!input.publicKey) {
14642
14997
  return { ok: false, reason: "missing-secret" };
@@ -14662,8 +15017,8 @@ var verifyVoiceTelnyxWebhookSignature = async (input) => {
14662
15017
  var buildTelnyxVoiceSetupStatus = async (options, input) => {
14663
15018
  const origin = resolveRequestOrigin3(input.request);
14664
15019
  const stream = await resolveTelnyxStreamUrl(options, input);
14665
- const texml = joinUrlPath3(origin, input.texmlPath);
14666
- const webhook = joinUrlPath3(origin, input.webhookPath);
15020
+ const texml = joinUrlPath4(origin, input.texmlPath);
15021
+ const webhook = joinUrlPath4(origin, input.webhookPath);
14667
15022
  const missing = Object.entries(options.setup?.requiredEnv ?? {}).filter((entry) => !entry[1]).map(([name]) => name);
14668
15023
  const signingConfigured = Boolean(options.webhook?.publicKey || options.webhook?.verify);
14669
15024
  const warnings = [
@@ -19262,6 +19617,7 @@ export {
19262
19617
  runVoiceScenarioEvals,
19263
19618
  runVoiceOutcomeContractSuite,
19264
19619
  runVoiceCampaignProof,
19620
+ runVoiceCampaignDialerProof,
19265
19621
  resolveVoiceTraceRedactionOptions,
19266
19622
  resolveVoiceTelephonyOutcome,
19267
19623
  resolveVoiceSTTRoutingStrategy,
@@ -19321,6 +19677,7 @@ export {
19321
19677
  isVoiceOpsTaskOverdue,
19322
19678
  heartbeatVoiceOpsTask,
19323
19679
  hasVoiceOpsTaskSLABreach,
19680
+ getVoiceCampaignDialerProofStatus,
19324
19681
  filterVoiceTraceEvents,
19325
19682
  failVoiceOpsTask,
19326
19683
  exportVoiceTrace,
@@ -19346,6 +19703,7 @@ export {
19346
19703
  createVoiceWebhookDeliveryWorkerLoop,
19347
19704
  createVoiceWebhookDeliveryWorker,
19348
19705
  createVoiceTwilioRedirectHandoffAdapter,
19706
+ createVoiceTwilioCampaignDialer,
19349
19707
  createVoiceTurnQualityRoutes,
19350
19708
  createVoiceTurnQualityJSONHandler,
19351
19709
  createVoiceTurnQualityHTMLHandler,
@@ -19368,6 +19726,7 @@ export {
19368
19726
  createVoiceToolContractJSONHandler,
19369
19727
  createVoiceToolContractHTMLHandler,
19370
19728
  createVoiceToolContract,
19729
+ createVoiceTelnyxCampaignDialer,
19371
19730
  createVoiceTelephonyWebhookRoutes,
19372
19731
  createVoiceTelephonyWebhookHandler,
19373
19732
  createVoiceTelephonyOutcomePolicy,
@@ -19424,6 +19783,7 @@ export {
19424
19783
  createVoicePostgresIntegrationEventStore,
19425
19784
  createVoicePostgresExternalObjectMapStore,
19426
19785
  createVoicePostgresCampaignStore,
19786
+ createVoicePlivoCampaignDialer,
19427
19787
  createVoicePhoneAgent,
19428
19788
  createVoiceOutcomeContractRoutes,
19429
19789
  createVoiceOutcomeContractJSONHandler,
@@ -8434,7 +8434,10 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
8434
8434
  ]),
8435
8435
  durationMs,
8436
8436
  from,
8437
- metadata: payload,
8437
+ metadata: {
8438
+ ...input.query,
8439
+ ...payload
8440
+ },
8438
8441
  provider,
8439
8442
  reason: firstString(payload, [
8440
8443
  "Reason",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.114",
3
+ "version": "0.0.22-beta.116",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",