@absolutejs/voice 0.0.22-beta.264 → 0.0.22-beta.265

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.d.ts CHANGED
@@ -40,7 +40,7 @@ export { createVoiceLiveLatencyRoutes, renderVoiceLiveLatencyHTML, summarizeVoic
40
40
  export { assertVoiceLatencySLOGate, buildVoiceLatencySLOGate, renderVoiceLatencySLOMarkdown } from './latencySlo';
41
41
  export { createVoiceTurnQualityHTMLHandler, createVoiceTurnQualityJSONHandler, createVoiceTurnQualityRoutes, renderVoiceTurnQualityHTML, summarizeVoiceTurnQuality } from './turnQuality';
42
42
  export { assertVoiceOutcomeContractEvidence, createVoiceOutcomeContractHTMLHandler, createVoiceOutcomeContractJSONHandler, createVoiceOutcomeContractRoutes, evaluateVoiceOutcomeContractEvidence, renderVoiceOutcomeContractHTML, runVoiceOutcomeContractSuite } from './outcomeContract';
43
- export { applyVoiceTelephonyOutcome, createMemoryVoiceTelephonyWebhookIdempotencyStore, createVoiceTelephonyOutcomePolicy, createVoiceTelephonyWebhookHandler, createVoiceTelephonyWebhookRoutes, parseVoiceTelephonyWebhookEvent, resolveVoiceTelephonyOutcome, signVoiceTwilioWebhook, verifyVoiceTwilioWebhookSignature, voiceTelephonyOutcomeToRouteResult } from './telephonyOutcome';
43
+ export { applyVoiceTelephonyOutcome, assertVoiceTelephonyWebhookNormalizationEvidence, createMemoryVoiceTelephonyWebhookIdempotencyStore, createVoiceTelephonyOutcomePolicy, createVoiceTelephonyWebhookHandler, createVoiceTelephonyWebhookRoutes, evaluateVoiceTelephonyWebhookNormalizationEvidence, parseVoiceTelephonyWebhookEvent, resolveVoiceTelephonyOutcome, signVoiceTwilioWebhook, verifyVoiceTwilioWebhookSignature, voiceTelephonyOutcomeToRouteResult } from './telephonyOutcome';
44
44
  export { assertVoicePhoneCallControlEvidence, assertVoicePhoneAssistantEvidence, createVoicePhoneAgent, evaluateVoicePhoneCallControlEvidence, evaluateVoicePhoneAssistantEvidence } from './phoneAgent';
45
45
  export { createStoredVoiceCallReviewArtifact, createStoredVoiceExternalObjectMap, createStoredVoiceIntegrationEvent, createStoredVoiceOpsTask, createVoiceFileIncidentBundleStore, createVoiceFileExternalObjectMapStore, createVoiceFileAssistantMemoryStore, createVoiceFileAuditEventStore, createVoiceFileAuditSinkDeliveryStore, createVoiceFileCampaignStore, createVoiceFileIntegrationEventStore, createVoiceFileReviewStore, createVoiceFileRuntimeStorage, createVoiceFileSessionStore, createVoiceFileTaskStore, createVoiceFileTraceSinkDeliveryStore, createVoiceFileTraceEventStore } from './fileStore';
46
46
  export { createVoiceAssistantMemoryHandle, createVoiceAssistantMemoryRecord, createVoiceMemoryAssistantMemoryStore, resolveVoiceAssistantMemoryNamespace } from './assistantMemory';
@@ -115,7 +115,7 @@ export type { VoiceLiveLatencyOptions, VoiceLiveLatencyReport, VoiceLiveLatencyR
115
115
  export type { VoiceLatencySLOBudget, VoiceLatencySLOGateError, VoiceLatencySLOGateOptions, VoiceLatencySLOGateReport, VoiceLatencySLOMeasurement, VoiceLatencySLOStage, VoiceLatencySLOStageSummary, VoiceLatencySLOStatus } from './latencySlo';
116
116
  export type { VoiceTurnQualityHTMLHandlerOptions, VoiceTurnQualityItem, VoiceTurnQualityOptions, VoiceTurnQualityReport, VoiceTurnQualityRoutesOptions, VoiceTurnQualityStatus } from './turnQuality';
117
117
  export type { VoiceOutcomeContractAssertionInput, VoiceOutcomeContractAssertionReport, VoiceOutcomeContractDefinition, VoiceOutcomeContractHTMLHandlerOptions, VoiceOutcomeContractIssue, VoiceOutcomeContractOptions, VoiceOutcomeContractReport, VoiceOutcomeContractRoutesOptions, VoiceOutcomeContractStatus, VoiceOutcomeContractSuiteReport } from './outcomeContract';
118
- export type { VoiceTelephonyOutcomeAction, VoiceTelephonyOutcomeDecision, VoiceTelephonyOutcomePolicy, VoiceTelephonyOutcomeProviderEvent, VoiceTelephonyOutcomeRouteResult, VoiceTelephonyOutcomeStatusDecision, VoiceTelephonyWebhookDecision, VoiceTelephonyWebhookHandlerOptions, VoiceTelephonyWebhookIdempotencyStore, VoiceTelephonyWebhookParseInput, VoiceTelephonyWebhookProvider, VoiceTelephonyWebhookRoutesOptions, VoiceTelephonyWebhookVerificationResult, StoredVoiceTelephonyWebhookDecision } from './telephonyOutcome';
118
+ export type { VoiceTelephonyOutcomeAction, VoiceTelephonyOutcomeDecision, VoiceTelephonyOutcomePolicy, VoiceTelephonyOutcomeProviderEvent, VoiceTelephonyOutcomeRouteResult, VoiceTelephonyOutcomeStatusDecision, VoiceTelephonyWebhookDecision, VoiceTelephonyWebhookHandlerOptions, VoiceTelephonyWebhookIdempotencyStore, VoiceTelephonyWebhookNormalizationEvidenceDecision, VoiceTelephonyWebhookNormalizationEvidenceInput, VoiceTelephonyWebhookNormalizationEvidenceReport, VoiceTelephonyWebhookParseInput, VoiceTelephonyWebhookProvider, VoiceTelephonyWebhookRoutesOptions, VoiceTelephonyWebhookVerificationResult, StoredVoiceTelephonyWebhookDecision } from './telephonyOutcome';
119
119
  export type { VoicePhoneAgentCarrier, VoicePhoneAgentCarrierSummary, VoicePhoneAssistantEvidenceInput, VoicePhoneAssistantEvidenceReport, VoicePhoneCallControlEvidenceInput, VoicePhoneCallControlEvidenceReport, VoicePhoneAgentLifecycleStage, VoicePhoneAgentPlivoCarrier, VoicePhoneAgentRoutes, VoicePhoneAgentRoutesOptions, VoicePhoneAgentSetupReport, VoicePhoneAgentTelnyxCarrier, VoicePhoneAgentTwilioCarrier } from './phoneAgent';
120
120
  export type { VoicePhoneAgentProductionSmokeIssue, VoicePhoneAgentProductionSmokeHandlerOptions, VoicePhoneAgentProductionSmokeHTMLHandlerOptions, VoicePhoneAgentProductionSmokeOptions, VoicePhoneAgentProductionSmokeReport, VoicePhoneAgentProductionSmokeRoutesOptions, VoicePhoneAgentProductionSmokeRequirement } from './phoneAgentProductionSmoke';
121
121
  export type { VoiceOpsConsoleLink, VoiceOpsConsoleReport, VoiceOpsConsoleRoutesOptions } from './opsConsoleRoutes';
package/dist/index.js CHANGED
@@ -17364,6 +17364,14 @@ var DEFAULT_MACHINE_VOICEMAIL_VALUES = [
17364
17364
  ];
17365
17365
  var DEFAULT_NO_ANSWER_SIP_CODES = [408, 480, 486, 487, 603];
17366
17366
  var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
17367
+ var uniqueSorted3 = (values) => Array.from(new Set(values)).sort();
17368
+ var findMissing3 = (values, required) => {
17369
+ if (!required?.length) {
17370
+ return [];
17371
+ }
17372
+ const valueSet = new Set(values);
17373
+ return required.filter((value) => !valueSet.has(value));
17374
+ };
17367
17375
 
17368
17376
  class VoiceTelephonyWebhookVerificationError extends Error {
17369
17377
  result;
@@ -17382,6 +17390,60 @@ var createMemoryVoiceTelephonyWebhookIdempotencyStore = () => {
17382
17390
  }
17383
17391
  };
17384
17392
  };
17393
+ var isTelephonyWebhookProvider = (value) => value === "generic" || value === "plivo" || value === "telnyx" || value === "twilio";
17394
+ var isTelephonyOutcomeAction = (value) => value === "complete" || value === "escalate" || value === "ignore" || value === "no-answer" || value === "transfer" || value === "voicemail";
17395
+ var isCallDisposition = (value) => value === "completed" || value === "escalated" || value === "failed" || value === "no-answer" || value === "transferred" || value === "voicemail";
17396
+ var evaluateVoiceTelephonyWebhookNormalizationEvidence = (input = {}) => {
17397
+ const issues = [];
17398
+ const decisions = input.decisions ?? [];
17399
+ const actions = uniqueSorted3(decisions.map((decision) => decision.decision?.action ?? decision.action).filter(isTelephonyOutcomeAction));
17400
+ const dispositions = uniqueSorted3(decisions.map((decision) => decision.decision?.disposition ?? decision.disposition).filter(isCallDisposition));
17401
+ const providers = uniqueSorted3(decisions.map((decision) => decision.provider ?? decision.event?.provider).filter(isTelephonyWebhookProvider));
17402
+ const sources = uniqueSorted3(decisions.map((decision) => decision.decision?.source ?? decision.source).filter((source) => typeof source === "string"));
17403
+ const applied = decisions.filter((decision) => decision.applied === true).length;
17404
+ const routeResults = decisions.filter((decision) => isRecord(decision.routeResult)).length;
17405
+ const missingSessionIds = decisions.filter((decision) => !decision.sessionId).length;
17406
+ if (input.minDecisions !== undefined && decisions.length < input.minDecisions) {
17407
+ issues.push(`Expected at least ${String(input.minDecisions)} telephony webhook decision(s), found ${String(decisions.length)}.`);
17408
+ }
17409
+ if (input.minApplied !== undefined && applied < input.minApplied) {
17410
+ issues.push(`Expected at least ${String(input.minApplied)} applied telephony webhook decision(s), found ${String(applied)}.`);
17411
+ }
17412
+ if (input.maxMissingSessionIds !== undefined && missingSessionIds > input.maxMissingSessionIds) {
17413
+ issues.push(`Expected at most ${String(input.maxMissingSessionIds)} telephony webhook decision(s) without sessionId, found ${String(missingSessionIds)}.`);
17414
+ }
17415
+ if (input.requireRouteResults && routeResults < decisions.length) {
17416
+ issues.push(`Expected every telephony webhook decision to include a route result, found ${String(routeResults)} of ${String(decisions.length)}.`);
17417
+ }
17418
+ for (const provider of findMissing3(providers, input.requiredProviders)) {
17419
+ issues.push(`Missing telephony webhook provider: ${provider}.`);
17420
+ }
17421
+ for (const action of findMissing3(actions, input.requiredActions)) {
17422
+ issues.push(`Missing telephony webhook action: ${action}.`);
17423
+ }
17424
+ for (const disposition of findMissing3(dispositions, input.requiredDispositions)) {
17425
+ issues.push(`Missing telephony webhook disposition: ${disposition}.`);
17426
+ }
17427
+ return {
17428
+ actions,
17429
+ applied,
17430
+ decisions: decisions.length,
17431
+ dispositions,
17432
+ issues,
17433
+ missingSessionIds,
17434
+ ok: issues.length === 0,
17435
+ providers,
17436
+ routeResults,
17437
+ sources
17438
+ };
17439
+ };
17440
+ var assertVoiceTelephonyWebhookNormalizationEvidence = (input = {}) => {
17441
+ const assertion = evaluateVoiceTelephonyWebhookNormalizationEvidence(input);
17442
+ if (!assertion.ok) {
17443
+ throw new Error(`Voice telephony webhook normalization evidence assertion failed: ${assertion.issues.join(" ")}`);
17444
+ }
17445
+ return assertion;
17446
+ };
17385
17447
  var normalizeToken = (value) => typeof value === "string" ? value.trim().toLowerCase().replace(/\s+/g, "-").replace(/_+/g, "-") : undefined;
17386
17448
  var firstString2 = (source, keys) => {
17387
17449
  for (const key of keys) {
@@ -20020,8 +20082,8 @@ var loadCarrierMatrixInputs = async (input) => {
20020
20082
  };
20021
20083
  var carrierAnswerLabel = (provider) => provider === "telnyx" ? "TeXML URL" : provider === "plivo" ? "Answer URL" : "TwiML URL";
20022
20084
  var findCarrierMatrixEntry = (matrix, carrier) => matrix?.entries.find((candidate) => candidate.provider === carrier.provider && (candidate.name === carrier.name || candidate.name === (carrier.name ?? carrier.provider)));
20023
- var uniqueSorted3 = (values) => Array.from(new Set(values)).sort();
20024
- var findMissing3 = (values, required) => {
20085
+ var uniqueSorted4 = (values) => Array.from(new Set(values)).sort();
20086
+ var findMissing4 = (values, required) => {
20025
20087
  if (!required?.length) {
20026
20088
  return [];
20027
20089
  }
@@ -20030,8 +20092,8 @@ var findMissing3 = (values, required) => {
20030
20092
  };
20031
20093
  var evaluateVoicePhoneAssistantEvidence = (report, input = {}) => {
20032
20094
  const issues = [];
20033
- const providers = uniqueSorted3(report.carriers.map((carrier) => carrier.provider));
20034
- const lifecycleStages = uniqueSorted3(report.lifecycleStages);
20095
+ const providers = uniqueSorted4(report.carriers.map((carrier) => carrier.provider));
20096
+ const lifecycleStages = uniqueSorted4(report.lifecycleStages);
20035
20097
  const carrierSummary = report.matrix?.summary;
20036
20098
  const carrierFailures = carrierSummary?.failing ?? 0;
20037
20099
  const carrierWarnings = carrierSummary?.warnings ?? 0;
@@ -20071,10 +20133,10 @@ var evaluateVoicePhoneAssistantEvidence = (report, input = {}) => {
20071
20133
  if (input.maxCarrierWarnings !== undefined && carrierWarnings > input.maxCarrierWarnings) {
20072
20134
  issues.push(`Expected at most ${String(input.maxCarrierWarnings)} phone carrier warning(s), found ${String(carrierWarnings)}.`);
20073
20135
  }
20074
- for (const provider of findMissing3(providers, input.requiredProviders)) {
20136
+ for (const provider of findMissing4(providers, input.requiredProviders)) {
20075
20137
  issues.push(`Missing phone carrier provider: ${provider}.`);
20076
20138
  }
20077
- for (const stage of findMissing3(lifecycleStages, input.requiredLifecycleStages)) {
20139
+ for (const stage of findMissing4(lifecycleStages, input.requiredLifecycleStages)) {
20078
20140
  issues.push(`Missing phone lifecycle stage: ${stage}.`);
20079
20141
  }
20080
20142
  const dialer = input.dialerProof ? evaluateVoiceCampaignDialerProofEvidence(input.dialerProof, {
@@ -20117,9 +20179,9 @@ var evaluateVoicePhoneCallControlEvidence = (input = {}) => {
20117
20179
  const issues = [];
20118
20180
  const setup = input.setup;
20119
20181
  const productionSmokes = input.productionSmokes ?? [];
20120
- const lifecycleStages = uniqueSorted3(setup?.lifecycleStages ?? []);
20121
- const providers = uniqueSorted3(productionSmokes.map((report) => report.provider).filter((provider) => Boolean(provider)));
20122
- const outcomes = uniqueSorted3(productionSmokes.flatMap((report) => report.observed.lifecycleOutcomes));
20182
+ const lifecycleStages = uniqueSorted4(setup?.lifecycleStages ?? []);
20183
+ const providers = uniqueSorted4(productionSmokes.map((report) => report.provider).filter((provider) => Boolean(provider)));
20184
+ const outcomes = uniqueSorted4(productionSmokes.flatMap((report) => report.observed.lifecycleOutcomes));
20123
20185
  const failedSmokeReports = productionSmokes.filter((report) => !report.pass).length;
20124
20186
  const passingSmokeReports = productionSmokes.length - failedSmokeReports;
20125
20187
  if (productionSmokes.length === 0) {
@@ -20129,7 +20191,7 @@ var evaluateVoicePhoneCallControlEvidence = (input = {}) => {
20129
20191
  if (!setup) {
20130
20192
  issues.push("Expected phone setup report to be present.");
20131
20193
  }
20132
- for (const stage of findMissing3(lifecycleStages, input.requiredLifecycleStages)) {
20194
+ for (const stage of findMissing4(lifecycleStages, input.requiredLifecycleStages)) {
20133
20195
  issues.push(`Missing phone call-control lifecycle stage: ${stage}.`);
20134
20196
  }
20135
20197
  }
@@ -20139,10 +20201,10 @@ var evaluateVoicePhoneCallControlEvidence = (input = {}) => {
20139
20201
  if (input.maxFailedSmokeReports !== undefined && failedSmokeReports > input.maxFailedSmokeReports) {
20140
20202
  issues.push(`Expected at most ${String(input.maxFailedSmokeReports)} failing phone call-control smoke report(s), found ${String(failedSmokeReports)}.`);
20141
20203
  }
20142
- for (const provider of findMissing3(providers, input.requiredProviders)) {
20204
+ for (const provider of findMissing4(providers, input.requiredProviders)) {
20143
20205
  issues.push(`Missing phone call-control provider: ${provider}.`);
20144
20206
  }
20145
- for (const outcome of findMissing3(outcomes, input.requiredOutcomes)) {
20207
+ for (const outcome of findMissing4(outcomes, input.requiredOutcomes)) {
20146
20208
  issues.push(`Missing phone call-control outcome: ${outcome}.`);
20147
20209
  }
20148
20210
  return {
@@ -23095,7 +23157,7 @@ var statusRank = {
23095
23157
  var escapeHtml36 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
23096
23158
  var roundMetric3 = (value) => Math.round(value * 1e4) / 1e4;
23097
23159
  var rate3 = (count, total) => count / Math.max(1, total);
23098
- var uniqueSorted4 = (values) => [
23160
+ var uniqueSorted5 = (values) => [
23099
23161
  ...new Set(values.filter((value) => typeof value === "string"))
23100
23162
  ].sort();
23101
23163
  var percentile3 = (values, rank) => {
@@ -23278,7 +23340,7 @@ var buildVoiceProviderSloReport = async (options = {}) => {
23278
23340
  var evaluateVoiceProviderSloEvidence = (report, input = {}) => {
23279
23341
  const issues = [];
23280
23342
  const kindReports = Object.values(report.kinds);
23281
- const providers = uniqueSorted4(kindReports.flatMap((kind) => kind.providers));
23343
+ const providers = uniqueSorted5(kindReports.flatMap((kind) => kind.providers));
23282
23344
  const kinds = providerKinds.filter((kind) => report.kinds[kind].events > 0);
23283
23345
  const fallbacks = kindReports.reduce((total, kind) => total + kind.fallbacks, 0);
23284
23346
  const timeouts = kindReports.reduce((total, kind) => total + kind.timeouts, 0);
@@ -23999,7 +24061,7 @@ var hasPayloadValue = (payload, key, values) => {
23999
24061
  return typeof value === "string" && values.has(value);
24000
24062
  };
24001
24063
  var countIntegrationDeliveryStatus = (events, status) => events.filter((event) => event.deliveryStatus === status).length;
24002
- var uniqueSorted5 = (values) => [
24064
+ var uniqueSorted6 = (values) => [
24003
24065
  ...new Set(values.filter((value) => typeof value === "string"))
24004
24066
  ].sort();
24005
24067
  var pushMissingValuesIssue = (input) => {
@@ -24178,11 +24240,11 @@ var buildVoiceOperationsRecord = async (options) => {
24178
24240
  var evaluateVoiceOperationsRecordGuardrails = (record, input = {}) => {
24179
24241
  const issues = [];
24180
24242
  const decisions = record.guardrails.decisions;
24181
- const proofs = uniqueSorted5(decisions.map((decision) => decision.proof));
24182
- const ruleIds = uniqueSorted5(decisions.flatMap((decision) => decision.findings.map((finding) => finding.ruleId)));
24183
- const stages = uniqueSorted5(decisions.map((decision) => decision.stage));
24184
- const statuses = uniqueSorted5(decisions.map((decision) => decision.status));
24185
- const toolNames = uniqueSorted5(decisions.map((decision) => decision.toolName));
24243
+ const proofs = uniqueSorted6(decisions.map((decision) => decision.proof));
24244
+ const ruleIds = uniqueSorted6(decisions.flatMap((decision) => decision.findings.map((finding) => finding.ruleId)));
24245
+ const stages = uniqueSorted6(decisions.map((decision) => decision.stage));
24246
+ const statuses = uniqueSorted6(decisions.map((decision) => decision.status));
24247
+ const toolNames = uniqueSorted6(decisions.map((decision) => decision.toolName));
24186
24248
  const minDecisions = input.minDecisions ?? 1;
24187
24249
  if (record.guardrails.total < minDecisions) {
24188
24250
  issues.push(`Expected at least ${String(minDecisions)} guardrail decisions, found ${String(record.guardrails.total)}.`);
@@ -31419,6 +31481,7 @@ export {
31419
31481
  exportVoiceAuditTrail,
31420
31482
  evaluateVoiceTrace,
31421
31483
  evaluateVoiceToolContractEvidence,
31484
+ evaluateVoiceTelephonyWebhookNormalizationEvidence,
31422
31485
  evaluateVoiceTelephonyContract,
31423
31486
  evaluateVoiceSimulationSuiteEvidence,
31424
31487
  evaluateVoiceQuality,
@@ -31755,6 +31818,7 @@ export {
31755
31818
  buildEmptyVoiceProofTrendReport,
31756
31819
  assignVoiceOpsTask,
31757
31820
  assertVoiceToolContractEvidence,
31821
+ assertVoiceTelephonyWebhookNormalizationEvidence,
31758
31822
  assertVoiceSimulationSuiteEvidence,
31759
31823
  assertVoiceProviderStackEvidence,
31760
31824
  assertVoiceProviderSloEvidence,
@@ -65,6 +65,43 @@ export type StoredVoiceTelephonyWebhookDecision<TResult = unknown> = VoiceTeleph
65
65
  createdAt: number;
66
66
  updatedAt: number;
67
67
  };
68
+ export type VoiceTelephonyWebhookNormalizationEvidenceDecision = {
69
+ action?: VoiceTelephonyOutcomeAction | string;
70
+ applied?: boolean;
71
+ decision?: {
72
+ action?: VoiceTelephonyOutcomeAction | string;
73
+ disposition?: VoiceCallDisposition | string;
74
+ source?: VoiceTelephonyOutcomeDecision['source'] | string;
75
+ };
76
+ disposition?: VoiceCallDisposition | string;
77
+ event?: VoiceTelephonyOutcomeProviderEvent;
78
+ provider?: VoiceTelephonyWebhookProvider | string;
79
+ routeResult?: unknown;
80
+ sessionId?: string;
81
+ source?: VoiceTelephonyOutcomeDecision['source'] | string;
82
+ };
83
+ export type VoiceTelephonyWebhookNormalizationEvidenceInput = {
84
+ decisions?: VoiceTelephonyWebhookNormalizationEvidenceDecision[];
85
+ maxMissingSessionIds?: number;
86
+ minApplied?: number;
87
+ minDecisions?: number;
88
+ requiredActions?: VoiceTelephonyOutcomeAction[];
89
+ requiredDispositions?: VoiceCallDisposition[];
90
+ requiredProviders?: VoiceTelephonyWebhookProvider[];
91
+ requireRouteResults?: boolean;
92
+ };
93
+ export type VoiceTelephonyWebhookNormalizationEvidenceReport = {
94
+ actions: VoiceTelephonyOutcomeAction[];
95
+ applied: number;
96
+ decisions: number;
97
+ dispositions: VoiceCallDisposition[];
98
+ issues: string[];
99
+ missingSessionIds: number;
100
+ ok: boolean;
101
+ providers: VoiceTelephonyWebhookProvider[];
102
+ routeResults: number;
103
+ sources: string[];
104
+ };
68
105
  export type VoiceTelephonyWebhookIdempotencyStore<TResult = unknown> = {
69
106
  get: (key: string) => Promise<StoredVoiceTelephonyWebhookDecision<TResult> | undefined> | StoredVoiceTelephonyWebhookDecision<TResult> | undefined;
70
107
  set: (key: string, decision: StoredVoiceTelephonyWebhookDecision<TResult>) => Promise<void> | void;
@@ -139,6 +176,8 @@ export declare class VoiceTelephonyWebhookVerificationError extends Error {
139
176
  constructor(result: VoiceTelephonyWebhookVerificationResult);
140
177
  }
141
178
  export declare const createMemoryVoiceTelephonyWebhookIdempotencyStore: <TResult = unknown>() => VoiceTelephonyWebhookIdempotencyStore<TResult>;
179
+ export declare const evaluateVoiceTelephonyWebhookNormalizationEvidence: (input?: VoiceTelephonyWebhookNormalizationEvidenceInput) => VoiceTelephonyWebhookNormalizationEvidenceReport;
180
+ export declare const assertVoiceTelephonyWebhookNormalizationEvidence: (input?: VoiceTelephonyWebhookNormalizationEvidenceInput) => VoiceTelephonyWebhookNormalizationEvidenceReport;
142
181
  export declare const createVoiceTelephonyOutcomePolicy: (policy?: VoiceTelephonyOutcomePolicy) => Required<Pick<VoiceTelephonyOutcomePolicy, "completedStatuses" | "escalationStatuses" | "failedAsNoAnswer" | "failedStatuses" | "includeProviderPayload" | "machineDetectionVoicemailValues" | "noAnswerOnZeroDuration" | "noAnswerSipCodes" | "noAnswerStatuses" | "transferStatuses" | "voicemailStatuses">> & VoiceTelephonyOutcomePolicy;
143
182
  export declare const resolveVoiceTelephonyOutcome: (event: VoiceTelephonyOutcomeProviderEvent, policyInput?: VoiceTelephonyOutcomePolicy) => VoiceTelephonyOutcomeDecision;
144
183
  export declare const voiceTelephonyOutcomeToRouteResult: <TResult = unknown>(decision: VoiceTelephonyOutcomeDecision, result?: TResult) => VoiceTelephonyOutcomeRouteResult<TResult>;
@@ -8219,6 +8219,14 @@ var DEFAULT_MACHINE_VOICEMAIL_VALUES = [
8219
8219
  ];
8220
8220
  var DEFAULT_NO_ANSWER_SIP_CODES = [408, 480, 486, 487, 603];
8221
8221
  var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
8222
+ var uniqueSorted = (values) => Array.from(new Set(values)).sort();
8223
+ var findMissing = (values, required) => {
8224
+ if (!required?.length) {
8225
+ return [];
8226
+ }
8227
+ const valueSet = new Set(values);
8228
+ return required.filter((value) => !valueSet.has(value));
8229
+ };
8222
8230
 
8223
8231
  class VoiceTelephonyWebhookVerificationError extends Error {
8224
8232
  result;
@@ -8237,6 +8245,60 @@ var createMemoryVoiceTelephonyWebhookIdempotencyStore = () => {
8237
8245
  }
8238
8246
  };
8239
8247
  };
8248
+ var isTelephonyWebhookProvider = (value) => value === "generic" || value === "plivo" || value === "telnyx" || value === "twilio";
8249
+ var isTelephonyOutcomeAction = (value) => value === "complete" || value === "escalate" || value === "ignore" || value === "no-answer" || value === "transfer" || value === "voicemail";
8250
+ var isCallDisposition = (value) => value === "completed" || value === "escalated" || value === "failed" || value === "no-answer" || value === "transferred" || value === "voicemail";
8251
+ var evaluateVoiceTelephonyWebhookNormalizationEvidence = (input = {}) => {
8252
+ const issues = [];
8253
+ const decisions = input.decisions ?? [];
8254
+ const actions = uniqueSorted(decisions.map((decision) => decision.decision?.action ?? decision.action).filter(isTelephonyOutcomeAction));
8255
+ const dispositions = uniqueSorted(decisions.map((decision) => decision.decision?.disposition ?? decision.disposition).filter(isCallDisposition));
8256
+ const providers = uniqueSorted(decisions.map((decision) => decision.provider ?? decision.event?.provider).filter(isTelephonyWebhookProvider));
8257
+ const sources = uniqueSorted(decisions.map((decision) => decision.decision?.source ?? decision.source).filter((source) => typeof source === "string"));
8258
+ const applied = decisions.filter((decision) => decision.applied === true).length;
8259
+ const routeResults = decisions.filter((decision) => isRecord(decision.routeResult)).length;
8260
+ const missingSessionIds = decisions.filter((decision) => !decision.sessionId).length;
8261
+ if (input.minDecisions !== undefined && decisions.length < input.minDecisions) {
8262
+ issues.push(`Expected at least ${String(input.minDecisions)} telephony webhook decision(s), found ${String(decisions.length)}.`);
8263
+ }
8264
+ if (input.minApplied !== undefined && applied < input.minApplied) {
8265
+ issues.push(`Expected at least ${String(input.minApplied)} applied telephony webhook decision(s), found ${String(applied)}.`);
8266
+ }
8267
+ if (input.maxMissingSessionIds !== undefined && missingSessionIds > input.maxMissingSessionIds) {
8268
+ issues.push(`Expected at most ${String(input.maxMissingSessionIds)} telephony webhook decision(s) without sessionId, found ${String(missingSessionIds)}.`);
8269
+ }
8270
+ if (input.requireRouteResults && routeResults < decisions.length) {
8271
+ issues.push(`Expected every telephony webhook decision to include a route result, found ${String(routeResults)} of ${String(decisions.length)}.`);
8272
+ }
8273
+ for (const provider of findMissing(providers, input.requiredProviders)) {
8274
+ issues.push(`Missing telephony webhook provider: ${provider}.`);
8275
+ }
8276
+ for (const action of findMissing(actions, input.requiredActions)) {
8277
+ issues.push(`Missing telephony webhook action: ${action}.`);
8278
+ }
8279
+ for (const disposition of findMissing(dispositions, input.requiredDispositions)) {
8280
+ issues.push(`Missing telephony webhook disposition: ${disposition}.`);
8281
+ }
8282
+ return {
8283
+ actions,
8284
+ applied,
8285
+ decisions: decisions.length,
8286
+ dispositions,
8287
+ issues,
8288
+ missingSessionIds,
8289
+ ok: issues.length === 0,
8290
+ providers,
8291
+ routeResults,
8292
+ sources
8293
+ };
8294
+ };
8295
+ var assertVoiceTelephonyWebhookNormalizationEvidence = (input = {}) => {
8296
+ const assertion = evaluateVoiceTelephonyWebhookNormalizationEvidence(input);
8297
+ if (!assertion.ok) {
8298
+ throw new Error(`Voice telephony webhook normalization evidence assertion failed: ${assertion.issues.join(" ")}`);
8299
+ }
8300
+ return assertion;
8301
+ };
8240
8302
  var normalizeToken = (value) => typeof value === "string" ? value.trim().toLowerCase().replace(/\s+/g, "-").replace(/_+/g, "-") : undefined;
8241
8303
  var firstString = (source, keys) => {
8242
8304
  for (const key of keys) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.264",
3
+ "version": "0.0.22-beta.265",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",