@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 +2 -2
- package/dist/index.js +84 -20
- package/dist/telephonyOutcome.d.ts +39 -0
- package/dist/testing/index.js +62 -0
- package/package.json +1 -1
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
|
|
20024
|
-
var
|
|
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 =
|
|
20034
|
-
const 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
|
|
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
|
|
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 =
|
|
20121
|
-
const providers =
|
|
20122
|
-
const outcomes =
|
|
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
|
|
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
|
|
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
|
|
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("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23096
23158
|
var roundMetric3 = (value) => Math.round(value * 1e4) / 1e4;
|
|
23097
23159
|
var rate3 = (count, total) => count / Math.max(1, total);
|
|
23098
|
-
var
|
|
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 =
|
|
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
|
|
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 =
|
|
24182
|
-
const ruleIds =
|
|
24183
|
-
const stages =
|
|
24184
|
-
const statuses =
|
|
24185
|
-
const toolNames =
|
|
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>;
|
package/dist/testing/index.js
CHANGED
|
@@ -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) {
|