@absolutejs/voice 0.0.22-beta.490 → 0.0.22-beta.491

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,37 @@
1
+ import type { VoiceCampaign, VoiceCampaignAttemptStatus, VoiceCampaignRecipient, VoiceCampaignRecord, VoiceCampaignTimeWindow } from "./campaign";
2
+ export type VoiceCampaignDisposition = "answered" | "busy" | "failed" | "no-answer" | "voicemail";
3
+ export type VoiceCampaignDispositionRetryRule = {
4
+ backoffMs?: number;
5
+ maxAttempts?: number;
6
+ retry?: boolean;
7
+ };
8
+ export type VoiceCampaignDispositionRetryPolicy = Partial<Record<VoiceCampaignDisposition, VoiceCampaignDispositionRetryRule>>;
9
+ export type VoiceDNCList = {
10
+ contains: (phone: string) => boolean | Promise<boolean>;
11
+ };
12
+ export declare const normalizePhoneNumber: (phone: string) => string;
13
+ export declare const createInMemoryDNCList: (phones: ReadonlyArray<string>) => VoiceDNCList;
14
+ export declare const isPhoneOnDNC: (phone: string, list: VoiceDNCList) => Promise<boolean>;
15
+ export type VoiceCampaignWindowCheckInput = {
16
+ now?: Date;
17
+ window: VoiceCampaignTimeWindow;
18
+ };
19
+ export declare const isWithinCampaignWindow: (input: VoiceCampaignWindowCheckInput) => boolean;
20
+ export declare const shouldRetryCampaignAttempt: (input: {
21
+ attempts: number;
22
+ campaign: Pick<VoiceCampaign, "maxAttempts">;
23
+ disposition?: VoiceCampaignDisposition;
24
+ policy?: VoiceCampaignDispositionRetryPolicy;
25
+ }) => {
26
+ backoffMs?: number;
27
+ retry: boolean;
28
+ };
29
+ export type VoiceCampaignDispositionSummary = {
30
+ attempts: number;
31
+ byDisposition: Partial<Record<VoiceCampaignDisposition, number>>;
32
+ byStatus: Record<VoiceCampaignAttemptStatus, number>;
33
+ campaignId: string;
34
+ recipientsByStatus: Record<VoiceCampaignRecipient["status"], number>;
35
+ totalRecipients: number;
36
+ };
37
+ export declare const summarizeVoiceCampaignDispositions: (record: VoiceCampaignRecord) => VoiceCampaignDispositionSummary;
package/dist/index.d.ts CHANGED
@@ -83,6 +83,8 @@ export { createPunctuationSemanticTurnDetector, createRegexSemanticTurnDetector,
83
83
  export { VOICE_WEBHOOK_SIGNATURE_HEADER, VOICE_WEBHOOK_TIMESTAMP_HEADER, extractVoiceWebhookSignatureFromHeaders, signVoiceWebhookBody, verifyVoiceWebhookSignature, } from "./webhookVerification";
84
84
  export { describeVoiceAgentUIState, deriveVoiceAgentUIState, voiceAgentUIStateOrder, } from "./agentState";
85
85
  export type { VoiceAgentUIInput, VoiceAgentUIState, } from "./agentState";
86
+ export { createInMemoryDNCList, isPhoneOnDNC, isWithinCampaignWindow, normalizePhoneNumber, shouldRetryCampaignAttempt, summarizeVoiceCampaignDispositions, } from "./campaignControls";
87
+ export type { VoiceCampaignDisposition, VoiceCampaignDispositionRetryPolicy, VoiceCampaignDispositionRetryRule, VoiceCampaignDispositionSummary, VoiceCampaignWindowCheckInput, VoiceDNCList, } from "./campaignControls";
86
88
  export { createVoiceBackchannelDriver } from "./backchannel";
87
89
  export type { VoiceBackchannelCue, VoiceBackchannelDriver, VoiceBackchannelDriverOptions, } from "./backchannel";
88
90
  export { createVoiceIVRSession, describeVoiceIVRPlan, evaluateVoiceIVRPlan, } from "./ivrPlan";
package/dist/index.js CHANGED
@@ -35515,6 +35515,90 @@ var voiceAgentUIStateOrder = [
35515
35515
  "thinking",
35516
35516
  "speaking"
35517
35517
  ];
35518
+ // src/campaignControls.ts
35519
+ var normalizePhoneNumber = (phone) => phone.replace(/[\s()-]/g, "").trim();
35520
+ var createInMemoryDNCList = (phones) => {
35521
+ const set = new Set(phones.map(normalizePhoneNumber));
35522
+ return {
35523
+ contains: (phone) => set.has(normalizePhoneNumber(phone))
35524
+ };
35525
+ };
35526
+ var isPhoneOnDNC = async (phone, list) => Promise.resolve(list.contains(phone));
35527
+ var isWithinCampaignWindow = (input) => {
35528
+ const now = input.now ?? new Date;
35529
+ const offsetMinutes = input.window.timeZoneOffsetMinutes ?? 0;
35530
+ const shifted = new Date(now.getTime() + offsetMinutes * 60000);
35531
+ const day = shifted.getUTCDay();
35532
+ if (input.window.daysOfWeek && !input.window.daysOfWeek.includes(day)) {
35533
+ return false;
35534
+ }
35535
+ const hour = shifted.getUTCHours();
35536
+ if (input.window.startHour <= input.window.endHour) {
35537
+ return hour >= input.window.startHour && hour < input.window.endHour;
35538
+ }
35539
+ return hour >= input.window.startHour || hour < input.window.endHour;
35540
+ };
35541
+ var shouldRetryCampaignAttempt = (input) => {
35542
+ if (input.attempts >= input.campaign.maxAttempts) {
35543
+ return { retry: false };
35544
+ }
35545
+ const rule = input.disposition ? input.policy?.[input.disposition] : undefined;
35546
+ if (rule && rule.retry === false) {
35547
+ return { retry: false };
35548
+ }
35549
+ if (rule?.maxAttempts !== undefined && input.attempts >= rule.maxAttempts) {
35550
+ return { retry: false };
35551
+ }
35552
+ return { backoffMs: rule?.backoffMs, retry: true };
35553
+ };
35554
+ var dispositionFromAttempt = (attempt) => {
35555
+ const metadata = attempt.metadata;
35556
+ if (metadata && typeof metadata.disposition === "string") {
35557
+ return metadata.disposition;
35558
+ }
35559
+ if (attempt.status === "failed") {
35560
+ return "failed";
35561
+ }
35562
+ if (attempt.status === "succeeded") {
35563
+ return "answered";
35564
+ }
35565
+ return;
35566
+ };
35567
+ var summarizeVoiceCampaignDispositions = (record) => {
35568
+ const byStatus = {
35569
+ canceled: 0,
35570
+ failed: 0,
35571
+ queued: 0,
35572
+ running: 0,
35573
+ succeeded: 0
35574
+ };
35575
+ const byDisposition = {};
35576
+ for (const attempt of record.attempts) {
35577
+ byStatus[attempt.status] = (byStatus[attempt.status] ?? 0) + 1;
35578
+ const disposition = dispositionFromAttempt(attempt);
35579
+ if (disposition) {
35580
+ byDisposition[disposition] = (byDisposition[disposition] ?? 0) + 1;
35581
+ }
35582
+ }
35583
+ const recipientsByStatus = {
35584
+ canceled: 0,
35585
+ completed: 0,
35586
+ failed: 0,
35587
+ pending: 0,
35588
+ queued: 0
35589
+ };
35590
+ for (const recipient of record.recipients) {
35591
+ recipientsByStatus[recipient.status] = (recipientsByStatus[recipient.status] ?? 0) + 1;
35592
+ }
35593
+ return {
35594
+ attempts: record.attempts.length,
35595
+ byDisposition,
35596
+ byStatus,
35597
+ campaignId: record.campaign.id,
35598
+ recipientsByStatus,
35599
+ totalRecipients: record.recipients.length
35600
+ };
35601
+ };
35518
35602
  // src/backchannel.ts
35519
35603
  var DEFAULT_CUES = [
35520
35604
  { text: "mm-hmm" },
@@ -46502,6 +46586,7 @@ export {
46502
46586
  summarizeVoiceHandoffHealth,
46503
46587
  summarizeVoiceHandoffDeliveries,
46504
46588
  summarizeVoiceCampaigns,
46589
+ summarizeVoiceCampaignDispositions,
46505
46590
  summarizeVoiceCallerTranscript,
46506
46591
  summarizeVoiceBrowserMedia,
46507
46592
  summarizeVoiceBargeIn,
@@ -46513,6 +46598,7 @@ export {
46513
46598
  signVoiceWebhookBody,
46514
46599
  signVoiceTwilioWebhook,
46515
46600
  signVoicePlivoWebhook,
46601
+ shouldRetryCampaignAttempt,
46516
46602
  shapeTelephonyAssistantText,
46517
46603
  selectVoiceTraceEventsForPrune,
46518
46604
  saveVoiceIncidentBundleArtifact,
@@ -46684,6 +46770,7 @@ export {
46684
46770
  parseVoiceTelephonyWebhookEvent,
46685
46771
  parseVoiceSessionSnapshot,
46686
46772
  normalizeVoiceProofTrendReport,
46773
+ normalizePhoneNumber,
46687
46774
  muteVoiceMonitorIssue,
46688
46775
  matchesVoiceOpsTaskAssignmentRule,
46689
46776
  markVoiceOpsTaskSLABreached,
@@ -46694,7 +46781,9 @@ export {
46694
46781
  listVoiceRoutingEvents,
46695
46782
  listVoiceProviderDecisionTraces,
46696
46783
  listVoiceOpsTasks,
46784
+ isWithinCampaignWindow,
46697
46785
  isVoiceOpsTaskOverdue,
46786
+ isPhoneOnDNC,
46698
46787
  importVoiceCampaignRecipients,
46699
46788
  heartbeatVoiceOpsTask,
46700
46789
  hasVoiceOpsTaskSLABreach,
@@ -47116,6 +47205,7 @@ export {
47116
47205
  createMemoryVoiceTelephonyWebhookIdempotencyStore,
47117
47206
  createMemoryVoicePlivoWebhookNonceStore,
47118
47207
  createJSONVoiceAssistantModel,
47208
+ createInMemoryDNCList,
47119
47209
  createId,
47120
47210
  createGeminiVoiceAssistantModel,
47121
47211
  createDomainPhraseHints,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.490",
3
+ "version": "0.0.22-beta.491",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",