@absolutejs/voice 0.0.22-beta.113 → 0.0.22-beta.115

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.
@@ -1,5 +1,6 @@
1
1
  import { Elysia } from 'elysia';
2
2
  import type { VoiceRedisTaskLeaseCoordinator } from './queue';
3
+ import type { VoiceTelephonyOutcomeDecision, VoiceTelephonyOutcomeProviderEvent, VoiceTelephonyWebhookDecision } from './telephonyOutcome';
3
4
  export type VoiceCampaignStatus = 'canceled' | 'completed' | 'draft' | 'paused' | 'running';
4
5
  export type VoiceCampaignRecipientStatus = 'canceled' | 'completed' | 'failed' | 'pending' | 'queued';
5
6
  export type VoiceCampaignAttemptStatus = 'canceled' | 'failed' | 'queued' | 'running' | 'succeeded';
@@ -251,12 +252,41 @@ export type VoiceCampaignObservabilityReport = {
251
252
  };
252
253
  summary: VoiceCampaignSummary;
253
254
  };
255
+ export type VoiceCampaignTelephonyOutcomeInput<TResult = unknown> = {
256
+ campaignId?: string;
257
+ decision: VoiceTelephonyOutcomeDecision;
258
+ event?: VoiceTelephonyOutcomeProviderEvent;
259
+ externalCallId?: string;
260
+ routeResult?: TResult;
261
+ sessionId?: string;
262
+ attemptId?: string;
263
+ };
264
+ export type VoiceCampaignTelephonyOutcomeStatus = 'failed' | 'ignore' | 'succeeded';
265
+ export type VoiceCampaignTelephonyOutcomeOptions<TResult = unknown> = {
266
+ resolveCampaignId?: (input: VoiceCampaignTelephonyOutcomeInput<TResult>) => Promise<string | undefined> | string | undefined;
267
+ resolveExternalCallId?: (input: VoiceCampaignTelephonyOutcomeInput<TResult>) => Promise<string | undefined> | string | undefined;
268
+ resolveAttemptId?: (input: VoiceCampaignTelephonyOutcomeInput<TResult>) => Promise<string | undefined> | string | undefined;
269
+ runtime?: VoiceCampaignRuntime;
270
+ statusForDecision?: (input: VoiceCampaignTelephonyOutcomeInput<TResult>) => Promise<VoiceCampaignTelephonyOutcomeStatus> | VoiceCampaignTelephonyOutcomeStatus;
271
+ store?: VoiceCampaignStore;
272
+ };
273
+ export type VoiceCampaignTelephonyOutcomeResult = {
274
+ applied: boolean;
275
+ campaignId?: string;
276
+ error?: string;
277
+ externalCallId?: string;
278
+ reason?: 'ignored' | 'missing-attempt' | 'missing-campaign' | 'missing-runtime' | 'terminal-attempt';
279
+ status?: 'failed' | 'succeeded';
280
+ attemptId?: string;
281
+ };
254
282
  export declare const createVoiceMemoryCampaignStore: () => VoiceCampaignStore;
255
283
  export declare const summarizeVoiceCampaigns: (records: VoiceCampaignRecord[]) => VoiceCampaignSummary;
256
284
  export declare const buildVoiceCampaignObservabilityReport: (records: VoiceCampaignRecord[], options?: VoiceCampaignObservabilityOptions) => Promise<VoiceCampaignObservabilityReport>;
257
285
  export declare const createVoiceCampaign: (options: VoiceCampaignRuntimeOptions) => VoiceCampaignRuntime;
258
286
  export declare const createVoiceCampaignWorker: (options: VoiceCampaignWorkerOptions) => VoiceCampaignWorker;
259
287
  export declare const createVoiceCampaignWorkerLoop: (options: VoiceCampaignWorkerLoopOptions) => VoiceCampaignWorkerLoop;
288
+ export declare const applyVoiceCampaignTelephonyOutcome: <TResult = unknown>(input: VoiceCampaignTelephonyOutcomeInput<TResult>, options?: VoiceCampaignTelephonyOutcomeOptions<TResult>) => Promise<VoiceCampaignTelephonyOutcomeResult>;
289
+ export declare const createVoiceCampaignTelephonyOutcomeHandler: <TResult = unknown>(options: VoiceCampaignTelephonyOutcomeOptions<TResult>) => (input: VoiceTelephonyWebhookDecision<TResult>) => Promise<VoiceCampaignTelephonyOutcomeResult>;
260
290
  export declare const runVoiceCampaignProof: (options?: VoiceCampaignProofOptions) => Promise<VoiceCampaignProofReport>;
261
291
  export declare const renderVoiceCampaignsHTML: (records: VoiceCampaignRecord[], options?: {
262
292
  title?: string;
@@ -0,0 +1,52 @@
1
+ import type { VoiceCampaignDialer, VoiceCampaignDialerInput } 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 declare const createVoiceTwilioCampaignDialer: (options: VoiceTwilioCampaignDialerOptions) => VoiceCampaignDialer;
50
+ export declare const createVoiceTelnyxCampaignDialer: (options: VoiceTelnyxCampaignDialerOptions) => VoiceCampaignDialer;
51
+ export declare const createVoicePlivoCampaignDialer: (options: VoicePlivoCampaignDialerOptions) => VoiceCampaignDialer;
52
+ 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
- export { buildVoiceCampaignObservabilityReport, createVoiceCampaign, createVoiceCampaignRoutes, createVoiceCampaignWorker, createVoiceCampaignWorkerLoop, createVoiceMemoryCampaignStore, renderVoiceCampaignObservabilityHTML, renderVoiceCampaignsHTML, runVoiceCampaignProof, summarizeVoiceCampaigns } from './campaign';
3
+ export { applyVoiceCampaignTelephonyOutcome, buildVoiceCampaignObservabilityReport, createVoiceCampaignTelephonyOutcomeHandler, createVoiceCampaign, createVoiceCampaignRoutes, createVoiceCampaignWorker, createVoiceCampaignWorkerLoop, createVoiceMemoryCampaignStore, renderVoiceCampaignObservabilityHTML, renderVoiceCampaignsHTML, runVoiceCampaignProof, summarizeVoiceCampaigns } from './campaign';
4
+ export { createVoicePlivoCampaignDialer, createVoiceTelnyxCampaignDialer, createVoiceTwilioCampaignDialer } 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';
package/dist/index.js CHANGED
@@ -10984,6 +10984,155 @@ var createVoiceCampaignWorkerLoop = (options) => {
10984
10984
  tick
10985
10985
  };
10986
10986
  };
10987
+ var firstOutcomeString = (values) => {
10988
+ for (const value of values) {
10989
+ if (typeof value === "string" && value.trim()) {
10990
+ return value.trim();
10991
+ }
10992
+ if (typeof value === "number" && Number.isFinite(value)) {
10993
+ return String(value);
10994
+ }
10995
+ }
10996
+ };
10997
+ var resolveDefaultCampaignOutcomeIds = (input) => {
10998
+ const metadata = input.event?.metadata ?? {};
10999
+ const decisionMetadata = input.decision.metadata ?? {};
11000
+ const routeResult = typeof input.routeResult === "object" && input.routeResult !== null ? input.routeResult : {};
11001
+ return {
11002
+ campaignId: input.campaignId ?? firstOutcomeString([
11003
+ metadata.campaignId,
11004
+ metadata.voiceCampaignId,
11005
+ decisionMetadata.campaignId,
11006
+ decisionMetadata.voiceCampaignId,
11007
+ routeResult.campaignId,
11008
+ routeResult.voiceCampaignId
11009
+ ]),
11010
+ externalCallId: input.externalCallId ?? firstOutcomeString([
11011
+ metadata.externalCallId,
11012
+ metadata.callId,
11013
+ metadata.callSid,
11014
+ metadata.callUuid,
11015
+ decisionMetadata.externalCallId,
11016
+ decisionMetadata.callId,
11017
+ decisionMetadata.callSid,
11018
+ decisionMetadata.callUuid,
11019
+ routeResult.externalCallId,
11020
+ routeResult.callId,
11021
+ routeResult.callSid,
11022
+ routeResult.callUuid,
11023
+ input.sessionId
11024
+ ]),
11025
+ attemptId: input.attemptId ?? firstOutcomeString([
11026
+ metadata.attemptId,
11027
+ metadata.voiceCampaignAttemptId,
11028
+ decisionMetadata.attemptId,
11029
+ decisionMetadata.voiceCampaignAttemptId,
11030
+ routeResult.attemptId,
11031
+ routeResult.voiceCampaignAttemptId
11032
+ ])
11033
+ };
11034
+ };
11035
+ var defaultCampaignOutcomeStatus = (input) => {
11036
+ switch (input.decision.action) {
11037
+ case "complete":
11038
+ case "transfer":
11039
+ return "succeeded";
11040
+ case "escalate":
11041
+ case "no-answer":
11042
+ case "voicemail":
11043
+ return "failed";
11044
+ default:
11045
+ return "ignore";
11046
+ }
11047
+ };
11048
+ var findCampaignAttempt = async (input) => {
11049
+ const records = input.campaignId ? [await input.runtime.get(input.campaignId)].filter(Boolean) : await input.runtime.list();
11050
+ for (const record of records) {
11051
+ const attempt = record.attempts.find((item) => input.attemptId && item.id === input.attemptId || input.externalCallId && item.externalCallId === input.externalCallId);
11052
+ if (attempt) {
11053
+ return {
11054
+ attempt,
11055
+ record
11056
+ };
11057
+ }
11058
+ }
11059
+ };
11060
+ var applyVoiceCampaignTelephonyOutcome = async (input, options = {}) => {
11061
+ const runtime = options.runtime ?? (options.store ? createVoiceCampaign({
11062
+ store: options.store
11063
+ }) : undefined);
11064
+ if (!runtime) {
11065
+ return {
11066
+ applied: false,
11067
+ reason: "missing-runtime"
11068
+ };
11069
+ }
11070
+ const defaults = resolveDefaultCampaignOutcomeIds(input);
11071
+ const campaignId = await options.resolveCampaignId?.(input) ?? defaults.campaignId;
11072
+ const attemptId = await options.resolveAttemptId?.(input) ?? defaults.attemptId;
11073
+ const externalCallId = await options.resolveExternalCallId?.(input) ?? defaults.externalCallId;
11074
+ const status = await options.statusForDecision?.(input) ?? defaultCampaignOutcomeStatus(input);
11075
+ if (status === "ignore") {
11076
+ return {
11077
+ applied: false,
11078
+ campaignId,
11079
+ externalCallId,
11080
+ reason: "ignored",
11081
+ attemptId
11082
+ };
11083
+ }
11084
+ const match = await findCampaignAttempt({
11085
+ attemptId,
11086
+ campaignId,
11087
+ externalCallId,
11088
+ runtime
11089
+ });
11090
+ if (!match) {
11091
+ return {
11092
+ applied: false,
11093
+ campaignId,
11094
+ externalCallId,
11095
+ reason: campaignId ? "missing-attempt" : "missing-campaign",
11096
+ attemptId
11097
+ };
11098
+ }
11099
+ if (match.attempt.status === "failed" || match.attempt.status === "succeeded") {
11100
+ return {
11101
+ applied: false,
11102
+ campaignId: match.record.campaign.id,
11103
+ externalCallId: match.attempt.externalCallId ?? externalCallId,
11104
+ reason: "terminal-attempt",
11105
+ status: match.attempt.status,
11106
+ attemptId: match.attempt.id
11107
+ };
11108
+ }
11109
+ await runtime.completeAttempt(match.record.campaign.id, match.attempt.id, {
11110
+ error: status === "failed" ? input.decision.reason ?? input.decision.disposition ?? input.event?.reason ?? input.event?.status ?? input.decision.action : undefined,
11111
+ externalCallId: externalCallId ?? match.attempt.externalCallId,
11112
+ metadata: {
11113
+ telephonyAction: input.decision.action,
11114
+ telephonyConfidence: input.decision.confidence,
11115
+ telephonyDisposition: input.decision.disposition,
11116
+ telephonyProvider: input.event?.provider,
11117
+ telephonySource: input.decision.source,
11118
+ telephonyStatus: input.event?.status
11119
+ },
11120
+ status
11121
+ });
11122
+ return {
11123
+ applied: true,
11124
+ campaignId: match.record.campaign.id,
11125
+ externalCallId: externalCallId ?? match.attempt.externalCallId,
11126
+ status,
11127
+ attemptId: match.attempt.id
11128
+ };
11129
+ };
11130
+ var createVoiceCampaignTelephonyOutcomeHandler = (options) => (input) => applyVoiceCampaignTelephonyOutcome({
11131
+ decision: input.decision,
11132
+ event: input.event,
11133
+ routeResult: input.routeResult,
11134
+ sessionId: input.sessionId
11135
+ }, options);
10987
11136
  var defaultProofRecipients = () => [
10988
11137
  {
10989
11138
  id: "campaign-proof-recipient-1",
@@ -11107,6 +11256,201 @@ var createVoiceCampaignRoutes = (options) => {
11107
11256
  }
11108
11257
  return app;
11109
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
+ };
11110
11454
  // src/simulationSuite.ts
11111
11455
  import { Elysia as Elysia20 } from "elysia";
11112
11456
 
@@ -12541,7 +12885,7 @@ var createMemoryVoiceTelephonyWebhookIdempotencyStore = () => {
12541
12885
  };
12542
12886
  };
12543
12887
  var normalizeToken = (value) => typeof value === "string" ? value.trim().toLowerCase().replace(/\s+/g, "-").replace(/_+/g, "-") : undefined;
12544
- var firstString2 = (source, keys) => {
12888
+ var firstString3 = (source, keys) => {
12545
12889
  for (const key of keys) {
12546
12890
  const value = source[key];
12547
12891
  if (typeof value === "string" && value.trim()) {
@@ -12913,8 +13257,8 @@ var verifyVoiceTelephonyWebhook = async (input) => {
12913
13257
  var durationMsFromSeconds = (value) => typeof value === "number" ? value * 1000 : undefined;
12914
13258
  var parseVoiceTelephonyWebhookEvent = (input) => {
12915
13259
  const payload = flattenPayload(input.body);
12916
- const provider = firstString2(payload, ["provider", "Provider"]) ?? input.provider;
12917
- const status = firstString2(payload, [
13260
+ const provider = firstString3(payload, ["provider", "Provider"]) ?? input.provider;
13261
+ const status = firstString3(payload, [
12918
13262
  "CallStatus",
12919
13263
  "call_status",
12920
13264
  "callStatus",
@@ -12939,9 +13283,9 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
12939
13283
  "sip_code",
12940
13284
  "hangupCauseCode"
12941
13285
  ]);
12942
- const from = firstString2(payload, ["From", "from", "caller_id", "callerId"]);
12943
- const to = firstString2(payload, ["To", "to", "called_number", "calledNumber"]);
12944
- const target = firstString2(payload, [
13286
+ const from = firstString3(payload, ["From", "from", "caller_id", "callerId"]);
13287
+ const to = firstString3(payload, ["To", "to", "called_number", "calledNumber"]);
13288
+ const target = firstString3(payload, [
12945
13289
  "transferTarget",
12946
13290
  "TransferTarget",
12947
13291
  "target",
@@ -12949,7 +13293,7 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
12949
13293
  "department"
12950
13294
  ]);
12951
13295
  return {
12952
- answeredBy: firstString2(payload, [
13296
+ answeredBy: firstString3(payload, [
12953
13297
  "AnsweredBy",
12954
13298
  "answered_by",
12955
13299
  "answeredBy",
@@ -12958,9 +13302,12 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
12958
13302
  ]),
12959
13303
  durationMs,
12960
13304
  from,
12961
- metadata: payload,
13305
+ metadata: {
13306
+ ...input.query,
13307
+ ...payload
13308
+ },
12962
13309
  provider,
12963
- reason: firstString2(payload, [
13310
+ reason: firstString3(payload, [
12964
13311
  "Reason",
12965
13312
  "reason",
12966
13313
  "HangupCause",
@@ -12976,7 +13323,7 @@ var parseVoiceTelephonyWebhookEvent = (input) => {
12976
13323
  var defaultSessionId = (input) => {
12977
13324
  const payload = flattenPayload(input.body);
12978
13325
  const metadataSessionId = input.event.metadata?.sessionId;
12979
- return firstString2(input.query, ["sessionId", "session_id"]) ?? firstString2(payload, [
13326
+ return firstString3(input.query, ["sessionId", "session_id"]) ?? firstString3(payload, [
12980
13327
  "sessionId",
12981
13328
  "session_id",
12982
13329
  "SessionId",
@@ -12991,7 +13338,7 @@ var defaultSessionId = (input) => {
12991
13338
  };
12992
13339
  var defaultIdempotencyKey = (input) => {
12993
13340
  const payload = flattenPayload(input.body);
12994
- const eventId = firstString2(payload, [
13341
+ const eventId = firstString3(payload, [
12995
13342
  "id",
12996
13343
  "event_id",
12997
13344
  "eventId",
@@ -13152,11 +13499,11 @@ var createVoiceTelephonyWebhookRoutes = (options = {}) => {
13152
13499
  import { Elysia as Elysia28 } from "elysia";
13153
13500
 
13154
13501
  // src/telephony/plivo.ts
13155
- import { Buffer as Buffer4 } from "buffer";
13502
+ import { Buffer as Buffer5 } from "buffer";
13156
13503
  import { Elysia as Elysia26 } from "elysia";
13157
13504
 
13158
13505
  // src/telephony/twilio.ts
13159
- import { Buffer as Buffer3 } from "buffer";
13506
+ import { Buffer as Buffer4 } from "buffer";
13160
13507
  import { Elysia as Elysia25 } from "elysia";
13161
13508
  var TWILIO_MULAW_SAMPLE_RATE = 8000;
13162
13509
  var VOICE_PCM_SAMPLE_RATE = 16000;
@@ -13447,7 +13794,7 @@ var bytesToInt16Array = (bytes) => {
13447
13794
  return output;
13448
13795
  };
13449
13796
  var decodeTwilioMulawBase64 = (payload) => {
13450
- const bytes = Uint8Array.from(Buffer3.from(payload, "base64"));
13797
+ const bytes = Uint8Array.from(Buffer4.from(payload, "base64"));
13451
13798
  const samples = new Int16Array(bytes.length);
13452
13799
  for (let index = 0;index < bytes.length; index += 1) {
13453
13800
  samples[index] = decodeMulawSample(bytes[index] ?? 0);
@@ -13459,7 +13806,7 @@ var encodeTwilioMulawBase64 = (samples) => {
13459
13806
  for (let index = 0;index < samples.length; index += 1) {
13460
13807
  bytes[index] = encodeMulawSample(samples[index] ?? 0);
13461
13808
  }
13462
- return Buffer3.from(bytes).toString("base64");
13809
+ return Buffer4.from(bytes).toString("base64");
13463
13810
  };
13464
13811
  var transcodeTwilioInboundPayloadToPCM16 = (payload) => {
13465
13812
  const narrowband = decodeTwilioMulawBase64(payload);
@@ -13468,7 +13815,7 @@ var transcodeTwilioInboundPayloadToPCM16 = (payload) => {
13468
13815
  };
13469
13816
  var transcodePCMToTwilioOutboundPayload = (chunk, format) => {
13470
13817
  if (format.container === "raw" && format.encoding === "mulaw" && format.channels === 1 && format.sampleRateHz === TWILIO_MULAW_SAMPLE_RATE) {
13471
- return Buffer3.from(chunk).toString("base64");
13818
+ return Buffer4.from(chunk).toString("base64");
13472
13819
  }
13473
13820
  if (format.encoding !== "pcm_s16le") {
13474
13821
  throw new Error(`Unsupported outbound telephony audio format: ${format.container}/${format.encoding}`);
@@ -13509,7 +13856,7 @@ var createTwilioSocketAdapter = (socket, getState) => ({
13509
13856
  return;
13510
13857
  }
13511
13858
  if (message.type === "audio") {
13512
- const payload = transcodePCMToTwilioOutboundPayload(Uint8Array.from(Buffer3.from(message.chunkBase64, "base64")), message.format);
13859
+ const payload = transcodePCMToTwilioOutboundPayload(Uint8Array.from(Buffer4.from(message.chunkBase64, "base64")), message.format);
13513
13860
  state.hasOutboundAudioSinceLastInbound = true;
13514
13861
  state.reviewRecorder?.recordTwilioOutbound({
13515
13862
  bytes: payload.length,
@@ -14035,7 +14382,7 @@ var createPlivoMediaStreamBridge = (socket, options) => {
14035
14382
  }
14036
14383
  };
14037
14384
  };
14038
- var toBase642 = (bytes) => Buffer4.from(new Uint8Array(bytes)).toString("base64");
14385
+ var toBase642 = (bytes) => Buffer5.from(new Uint8Array(bytes)).toString("base64");
14039
14386
  var timingSafeEqual2 = (left, right) => {
14040
14387
  const encoder = new TextEncoder;
14041
14388
  const leftBytes = encoder.encode(left);
@@ -14337,7 +14684,7 @@ var createPlivoVoiceRoutes = (options = {}) => {
14337
14684
  };
14338
14685
 
14339
14686
  // src/telephony/telnyx.ts
14340
- import { Buffer as Buffer5 } from "buffer";
14687
+ import { Buffer as Buffer6 } from "buffer";
14341
14688
  import { Elysia as Elysia27 } from "elysia";
14342
14689
  var escapeXml4 = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&apos;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
14343
14690
  var escapeHtml27 = (value) => value.replaceAll("&", "&amp;").replaceAll('"', "&quot;").replaceAll("'", "&#39;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
@@ -14487,7 +14834,7 @@ var createTelnyxMediaStreamBridge = (socket, options) => {
14487
14834
  }
14488
14835
  };
14489
14836
  };
14490
- var decodeBase64 = (value) => Uint8Array.from(Buffer5.from(value, "base64"));
14837
+ var decodeBase64 = (value) => Uint8Array.from(Buffer6.from(value, "base64"));
14491
14838
  var verifyVoiceTelnyxWebhookSignature = async (input) => {
14492
14839
  if (!input.publicKey) {
14493
14840
  return { ok: false, reason: "missing-secret" };
@@ -19197,6 +19544,7 @@ export {
19197
19544
  createVoiceWebhookDeliveryWorkerLoop,
19198
19545
  createVoiceWebhookDeliveryWorker,
19199
19546
  createVoiceTwilioRedirectHandoffAdapter,
19547
+ createVoiceTwilioCampaignDialer,
19200
19548
  createVoiceTurnQualityRoutes,
19201
19549
  createVoiceTurnQualityJSONHandler,
19202
19550
  createVoiceTurnQualityHTMLHandler,
@@ -19219,6 +19567,7 @@ export {
19219
19567
  createVoiceToolContractJSONHandler,
19220
19568
  createVoiceToolContractHTMLHandler,
19221
19569
  createVoiceToolContract,
19570
+ createVoiceTelnyxCampaignDialer,
19222
19571
  createVoiceTelephonyWebhookRoutes,
19223
19572
  createVoiceTelephonyWebhookHandler,
19224
19573
  createVoiceTelephonyOutcomePolicy,
@@ -19275,6 +19624,7 @@ export {
19275
19624
  createVoicePostgresIntegrationEventStore,
19276
19625
  createVoicePostgresExternalObjectMapStore,
19277
19626
  createVoicePostgresCampaignStore,
19627
+ createVoicePlivoCampaignDialer,
19278
19628
  createVoicePhoneAgent,
19279
19629
  createVoiceOutcomeContractRoutes,
19280
19630
  createVoiceOutcomeContractJSONHandler,
@@ -19330,6 +19680,7 @@ export {
19330
19680
  createVoiceDiagnosticsRoutes,
19331
19681
  createVoiceCampaignWorkerLoop,
19332
19682
  createVoiceCampaignWorker,
19683
+ createVoiceCampaignTelephonyOutcomeHandler,
19333
19684
  createVoiceCampaignRoutes,
19334
19685
  createVoiceCampaign,
19335
19686
  createVoiceCallReviewRecorder,
@@ -19389,6 +19740,7 @@ export {
19389
19740
  applyVoiceOpsTaskPolicy,
19390
19741
  applyVoiceOpsTaskAssignmentRule,
19391
19742
  applyVoiceHandoffDeliveryResult,
19743
+ applyVoiceCampaignTelephonyOutcome,
19392
19744
  applyRiskTieredPhraseHintCorrections,
19393
19745
  applyPhraseHintCorrections,
19394
19746
  TURN_PROFILE_DEFAULTS
@@ -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.113",
3
+ "version": "0.0.22-beta.115",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",