@absolutejs/voice 0.0.22-beta.264 → 0.0.22-beta.266
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 +99 -20
- package/dist/telephonyOutcome.d.ts +47 -0
- package/dist/testing/index.js +77 -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,75 @@ 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 duplicateDecisions = decisions.filter((decision) => decision.duplicate === true);
|
|
17405
|
+
const duplicateProviders = uniqueSorted3(duplicateDecisions.map((decision) => decision.provider ?? decision.event?.provider).filter(isTelephonyWebhookProvider));
|
|
17406
|
+
const duplicateIdempotencyKeys = new Set(duplicateDecisions.map((decision) => decision.idempotencyKey).filter((key) => typeof key === "string" && key.length > 0)).size;
|
|
17407
|
+
const routeResults = decisions.filter((decision) => isRecord(decision.routeResult)).length;
|
|
17408
|
+
const missingSessionIds = decisions.filter((decision) => !decision.sessionId).length;
|
|
17409
|
+
if (input.minDecisions !== undefined && decisions.length < input.minDecisions) {
|
|
17410
|
+
issues.push(`Expected at least ${String(input.minDecisions)} telephony webhook decision(s), found ${String(decisions.length)}.`);
|
|
17411
|
+
}
|
|
17412
|
+
if (input.minApplied !== undefined && applied < input.minApplied) {
|
|
17413
|
+
issues.push(`Expected at least ${String(input.minApplied)} applied telephony webhook decision(s), found ${String(applied)}.`);
|
|
17414
|
+
}
|
|
17415
|
+
if (input.minDuplicates !== undefined && duplicateDecisions.length < input.minDuplicates) {
|
|
17416
|
+
issues.push(`Expected at least ${String(input.minDuplicates)} duplicate telephony webhook decision(s), found ${String(duplicateDecisions.length)}.`);
|
|
17417
|
+
}
|
|
17418
|
+
if (input.minDuplicateIdempotencyKeys !== undefined && duplicateIdempotencyKeys < input.minDuplicateIdempotencyKeys) {
|
|
17419
|
+
issues.push(`Expected at least ${String(input.minDuplicateIdempotencyKeys)} duplicate telephony webhook idempotency key(s), found ${String(duplicateIdempotencyKeys)}.`);
|
|
17420
|
+
}
|
|
17421
|
+
if (input.maxMissingSessionIds !== undefined && missingSessionIds > input.maxMissingSessionIds) {
|
|
17422
|
+
issues.push(`Expected at most ${String(input.maxMissingSessionIds)} telephony webhook decision(s) without sessionId, found ${String(missingSessionIds)}.`);
|
|
17423
|
+
}
|
|
17424
|
+
if (input.requireRouteResults && routeResults < decisions.length) {
|
|
17425
|
+
issues.push(`Expected every telephony webhook decision to include a route result, found ${String(routeResults)} of ${String(decisions.length)}.`);
|
|
17426
|
+
}
|
|
17427
|
+
for (const provider of findMissing3(providers, input.requiredProviders)) {
|
|
17428
|
+
issues.push(`Missing telephony webhook provider: ${provider}.`);
|
|
17429
|
+
}
|
|
17430
|
+
for (const provider of findMissing3(duplicateProviders, input.requiredDuplicateProviders)) {
|
|
17431
|
+
issues.push(`Missing duplicate telephony webhook provider: ${provider}.`);
|
|
17432
|
+
}
|
|
17433
|
+
for (const action of findMissing3(actions, input.requiredActions)) {
|
|
17434
|
+
issues.push(`Missing telephony webhook action: ${action}.`);
|
|
17435
|
+
}
|
|
17436
|
+
for (const disposition of findMissing3(dispositions, input.requiredDispositions)) {
|
|
17437
|
+
issues.push(`Missing telephony webhook disposition: ${disposition}.`);
|
|
17438
|
+
}
|
|
17439
|
+
return {
|
|
17440
|
+
actions,
|
|
17441
|
+
applied,
|
|
17442
|
+
decisions: decisions.length,
|
|
17443
|
+
dispositions,
|
|
17444
|
+
duplicateIdempotencyKeys,
|
|
17445
|
+
duplicateProviders,
|
|
17446
|
+
duplicates: duplicateDecisions.length,
|
|
17447
|
+
issues,
|
|
17448
|
+
missingSessionIds,
|
|
17449
|
+
ok: issues.length === 0,
|
|
17450
|
+
providers,
|
|
17451
|
+
routeResults,
|
|
17452
|
+
sources
|
|
17453
|
+
};
|
|
17454
|
+
};
|
|
17455
|
+
var assertVoiceTelephonyWebhookNormalizationEvidence = (input = {}) => {
|
|
17456
|
+
const assertion = evaluateVoiceTelephonyWebhookNormalizationEvidence(input);
|
|
17457
|
+
if (!assertion.ok) {
|
|
17458
|
+
throw new Error(`Voice telephony webhook normalization evidence assertion failed: ${assertion.issues.join(" ")}`);
|
|
17459
|
+
}
|
|
17460
|
+
return assertion;
|
|
17461
|
+
};
|
|
17385
17462
|
var normalizeToken = (value) => typeof value === "string" ? value.trim().toLowerCase().replace(/\s+/g, "-").replace(/_+/g, "-") : undefined;
|
|
17386
17463
|
var firstString2 = (source, keys) => {
|
|
17387
17464
|
for (const key of keys) {
|
|
@@ -20020,8 +20097,8 @@ var loadCarrierMatrixInputs = async (input) => {
|
|
|
20020
20097
|
};
|
|
20021
20098
|
var carrierAnswerLabel = (provider) => provider === "telnyx" ? "TeXML URL" : provider === "plivo" ? "Answer URL" : "TwiML URL";
|
|
20022
20099
|
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
|
|
20100
|
+
var uniqueSorted4 = (values) => Array.from(new Set(values)).sort();
|
|
20101
|
+
var findMissing4 = (values, required) => {
|
|
20025
20102
|
if (!required?.length) {
|
|
20026
20103
|
return [];
|
|
20027
20104
|
}
|
|
@@ -20030,8 +20107,8 @@ var findMissing3 = (values, required) => {
|
|
|
20030
20107
|
};
|
|
20031
20108
|
var evaluateVoicePhoneAssistantEvidence = (report, input = {}) => {
|
|
20032
20109
|
const issues = [];
|
|
20033
|
-
const providers =
|
|
20034
|
-
const lifecycleStages =
|
|
20110
|
+
const providers = uniqueSorted4(report.carriers.map((carrier) => carrier.provider));
|
|
20111
|
+
const lifecycleStages = uniqueSorted4(report.lifecycleStages);
|
|
20035
20112
|
const carrierSummary = report.matrix?.summary;
|
|
20036
20113
|
const carrierFailures = carrierSummary?.failing ?? 0;
|
|
20037
20114
|
const carrierWarnings = carrierSummary?.warnings ?? 0;
|
|
@@ -20071,10 +20148,10 @@ var evaluateVoicePhoneAssistantEvidence = (report, input = {}) => {
|
|
|
20071
20148
|
if (input.maxCarrierWarnings !== undefined && carrierWarnings > input.maxCarrierWarnings) {
|
|
20072
20149
|
issues.push(`Expected at most ${String(input.maxCarrierWarnings)} phone carrier warning(s), found ${String(carrierWarnings)}.`);
|
|
20073
20150
|
}
|
|
20074
|
-
for (const provider of
|
|
20151
|
+
for (const provider of findMissing4(providers, input.requiredProviders)) {
|
|
20075
20152
|
issues.push(`Missing phone carrier provider: ${provider}.`);
|
|
20076
20153
|
}
|
|
20077
|
-
for (const stage of
|
|
20154
|
+
for (const stage of findMissing4(lifecycleStages, input.requiredLifecycleStages)) {
|
|
20078
20155
|
issues.push(`Missing phone lifecycle stage: ${stage}.`);
|
|
20079
20156
|
}
|
|
20080
20157
|
const dialer = input.dialerProof ? evaluateVoiceCampaignDialerProofEvidence(input.dialerProof, {
|
|
@@ -20117,9 +20194,9 @@ var evaluateVoicePhoneCallControlEvidence = (input = {}) => {
|
|
|
20117
20194
|
const issues = [];
|
|
20118
20195
|
const setup = input.setup;
|
|
20119
20196
|
const productionSmokes = input.productionSmokes ?? [];
|
|
20120
|
-
const lifecycleStages =
|
|
20121
|
-
const providers =
|
|
20122
|
-
const outcomes =
|
|
20197
|
+
const lifecycleStages = uniqueSorted4(setup?.lifecycleStages ?? []);
|
|
20198
|
+
const providers = uniqueSorted4(productionSmokes.map((report) => report.provider).filter((provider) => Boolean(provider)));
|
|
20199
|
+
const outcomes = uniqueSorted4(productionSmokes.flatMap((report) => report.observed.lifecycleOutcomes));
|
|
20123
20200
|
const failedSmokeReports = productionSmokes.filter((report) => !report.pass).length;
|
|
20124
20201
|
const passingSmokeReports = productionSmokes.length - failedSmokeReports;
|
|
20125
20202
|
if (productionSmokes.length === 0) {
|
|
@@ -20129,7 +20206,7 @@ var evaluateVoicePhoneCallControlEvidence = (input = {}) => {
|
|
|
20129
20206
|
if (!setup) {
|
|
20130
20207
|
issues.push("Expected phone setup report to be present.");
|
|
20131
20208
|
}
|
|
20132
|
-
for (const stage of
|
|
20209
|
+
for (const stage of findMissing4(lifecycleStages, input.requiredLifecycleStages)) {
|
|
20133
20210
|
issues.push(`Missing phone call-control lifecycle stage: ${stage}.`);
|
|
20134
20211
|
}
|
|
20135
20212
|
}
|
|
@@ -20139,10 +20216,10 @@ var evaluateVoicePhoneCallControlEvidence = (input = {}) => {
|
|
|
20139
20216
|
if (input.maxFailedSmokeReports !== undefined && failedSmokeReports > input.maxFailedSmokeReports) {
|
|
20140
20217
|
issues.push(`Expected at most ${String(input.maxFailedSmokeReports)} failing phone call-control smoke report(s), found ${String(failedSmokeReports)}.`);
|
|
20141
20218
|
}
|
|
20142
|
-
for (const provider of
|
|
20219
|
+
for (const provider of findMissing4(providers, input.requiredProviders)) {
|
|
20143
20220
|
issues.push(`Missing phone call-control provider: ${provider}.`);
|
|
20144
20221
|
}
|
|
20145
|
-
for (const outcome of
|
|
20222
|
+
for (const outcome of findMissing4(outcomes, input.requiredOutcomes)) {
|
|
20146
20223
|
issues.push(`Missing phone call-control outcome: ${outcome}.`);
|
|
20147
20224
|
}
|
|
20148
20225
|
return {
|
|
@@ -23095,7 +23172,7 @@ var statusRank = {
|
|
|
23095
23172
|
var escapeHtml36 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23096
23173
|
var roundMetric3 = (value) => Math.round(value * 1e4) / 1e4;
|
|
23097
23174
|
var rate3 = (count, total) => count / Math.max(1, total);
|
|
23098
|
-
var
|
|
23175
|
+
var uniqueSorted5 = (values) => [
|
|
23099
23176
|
...new Set(values.filter((value) => typeof value === "string"))
|
|
23100
23177
|
].sort();
|
|
23101
23178
|
var percentile3 = (values, rank) => {
|
|
@@ -23278,7 +23355,7 @@ var buildVoiceProviderSloReport = async (options = {}) => {
|
|
|
23278
23355
|
var evaluateVoiceProviderSloEvidence = (report, input = {}) => {
|
|
23279
23356
|
const issues = [];
|
|
23280
23357
|
const kindReports = Object.values(report.kinds);
|
|
23281
|
-
const providers =
|
|
23358
|
+
const providers = uniqueSorted5(kindReports.flatMap((kind) => kind.providers));
|
|
23282
23359
|
const kinds = providerKinds.filter((kind) => report.kinds[kind].events > 0);
|
|
23283
23360
|
const fallbacks = kindReports.reduce((total, kind) => total + kind.fallbacks, 0);
|
|
23284
23361
|
const timeouts = kindReports.reduce((total, kind) => total + kind.timeouts, 0);
|
|
@@ -23999,7 +24076,7 @@ var hasPayloadValue = (payload, key, values) => {
|
|
|
23999
24076
|
return typeof value === "string" && values.has(value);
|
|
24000
24077
|
};
|
|
24001
24078
|
var countIntegrationDeliveryStatus = (events, status) => events.filter((event) => event.deliveryStatus === status).length;
|
|
24002
|
-
var
|
|
24079
|
+
var uniqueSorted6 = (values) => [
|
|
24003
24080
|
...new Set(values.filter((value) => typeof value === "string"))
|
|
24004
24081
|
].sort();
|
|
24005
24082
|
var pushMissingValuesIssue = (input) => {
|
|
@@ -24178,11 +24255,11 @@ var buildVoiceOperationsRecord = async (options) => {
|
|
|
24178
24255
|
var evaluateVoiceOperationsRecordGuardrails = (record, input = {}) => {
|
|
24179
24256
|
const issues = [];
|
|
24180
24257
|
const decisions = record.guardrails.decisions;
|
|
24181
|
-
const proofs =
|
|
24182
|
-
const ruleIds =
|
|
24183
|
-
const stages =
|
|
24184
|
-
const statuses =
|
|
24185
|
-
const toolNames =
|
|
24258
|
+
const proofs = uniqueSorted6(decisions.map((decision) => decision.proof));
|
|
24259
|
+
const ruleIds = uniqueSorted6(decisions.flatMap((decision) => decision.findings.map((finding) => finding.ruleId)));
|
|
24260
|
+
const stages = uniqueSorted6(decisions.map((decision) => decision.stage));
|
|
24261
|
+
const statuses = uniqueSorted6(decisions.map((decision) => decision.status));
|
|
24262
|
+
const toolNames = uniqueSorted6(decisions.map((decision) => decision.toolName));
|
|
24186
24263
|
const minDecisions = input.minDecisions ?? 1;
|
|
24187
24264
|
if (record.guardrails.total < minDecisions) {
|
|
24188
24265
|
issues.push(`Expected at least ${String(minDecisions)} guardrail decisions, found ${String(record.guardrails.total)}.`);
|
|
@@ -31419,6 +31496,7 @@ export {
|
|
|
31419
31496
|
exportVoiceAuditTrail,
|
|
31420
31497
|
evaluateVoiceTrace,
|
|
31421
31498
|
evaluateVoiceToolContractEvidence,
|
|
31499
|
+
evaluateVoiceTelephonyWebhookNormalizationEvidence,
|
|
31422
31500
|
evaluateVoiceTelephonyContract,
|
|
31423
31501
|
evaluateVoiceSimulationSuiteEvidence,
|
|
31424
31502
|
evaluateVoiceQuality,
|
|
@@ -31755,6 +31833,7 @@ export {
|
|
|
31755
31833
|
buildEmptyVoiceProofTrendReport,
|
|
31756
31834
|
assignVoiceOpsTask,
|
|
31757
31835
|
assertVoiceToolContractEvidence,
|
|
31836
|
+
assertVoiceTelephonyWebhookNormalizationEvidence,
|
|
31758
31837
|
assertVoiceSimulationSuiteEvidence,
|
|
31759
31838
|
assertVoiceProviderStackEvidence,
|
|
31760
31839
|
assertVoiceProviderSloEvidence,
|
|
@@ -65,6 +65,51 @@ 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
|
+
duplicate?: boolean;
|
|
78
|
+
event?: VoiceTelephonyOutcomeProviderEvent;
|
|
79
|
+
idempotencyKey?: string;
|
|
80
|
+
provider?: VoiceTelephonyWebhookProvider | string;
|
|
81
|
+
routeResult?: unknown;
|
|
82
|
+
sessionId?: string;
|
|
83
|
+
source?: VoiceTelephonyOutcomeDecision['source'] | string;
|
|
84
|
+
};
|
|
85
|
+
export type VoiceTelephonyWebhookNormalizationEvidenceInput = {
|
|
86
|
+
decisions?: VoiceTelephonyWebhookNormalizationEvidenceDecision[];
|
|
87
|
+
maxMissingSessionIds?: number;
|
|
88
|
+
minApplied?: number;
|
|
89
|
+
minDecisions?: number;
|
|
90
|
+
minDuplicateIdempotencyKeys?: number;
|
|
91
|
+
minDuplicates?: number;
|
|
92
|
+
requiredActions?: VoiceTelephonyOutcomeAction[];
|
|
93
|
+
requiredDispositions?: VoiceCallDisposition[];
|
|
94
|
+
requiredDuplicateProviders?: VoiceTelephonyWebhookProvider[];
|
|
95
|
+
requiredProviders?: VoiceTelephonyWebhookProvider[];
|
|
96
|
+
requireRouteResults?: boolean;
|
|
97
|
+
};
|
|
98
|
+
export type VoiceTelephonyWebhookNormalizationEvidenceReport = {
|
|
99
|
+
actions: VoiceTelephonyOutcomeAction[];
|
|
100
|
+
applied: number;
|
|
101
|
+
decisions: number;
|
|
102
|
+
dispositions: VoiceCallDisposition[];
|
|
103
|
+
duplicateIdempotencyKeys: number;
|
|
104
|
+
duplicateProviders: VoiceTelephonyWebhookProvider[];
|
|
105
|
+
duplicates: number;
|
|
106
|
+
issues: string[];
|
|
107
|
+
missingSessionIds: number;
|
|
108
|
+
ok: boolean;
|
|
109
|
+
providers: VoiceTelephonyWebhookProvider[];
|
|
110
|
+
routeResults: number;
|
|
111
|
+
sources: string[];
|
|
112
|
+
};
|
|
68
113
|
export type VoiceTelephonyWebhookIdempotencyStore<TResult = unknown> = {
|
|
69
114
|
get: (key: string) => Promise<StoredVoiceTelephonyWebhookDecision<TResult> | undefined> | StoredVoiceTelephonyWebhookDecision<TResult> | undefined;
|
|
70
115
|
set: (key: string, decision: StoredVoiceTelephonyWebhookDecision<TResult>) => Promise<void> | void;
|
|
@@ -139,6 +184,8 @@ export declare class VoiceTelephonyWebhookVerificationError extends Error {
|
|
|
139
184
|
constructor(result: VoiceTelephonyWebhookVerificationResult);
|
|
140
185
|
}
|
|
141
186
|
export declare const createMemoryVoiceTelephonyWebhookIdempotencyStore: <TResult = unknown>() => VoiceTelephonyWebhookIdempotencyStore<TResult>;
|
|
187
|
+
export declare const evaluateVoiceTelephonyWebhookNormalizationEvidence: (input?: VoiceTelephonyWebhookNormalizationEvidenceInput) => VoiceTelephonyWebhookNormalizationEvidenceReport;
|
|
188
|
+
export declare const assertVoiceTelephonyWebhookNormalizationEvidence: (input?: VoiceTelephonyWebhookNormalizationEvidenceInput) => VoiceTelephonyWebhookNormalizationEvidenceReport;
|
|
142
189
|
export declare const createVoiceTelephonyOutcomePolicy: (policy?: VoiceTelephonyOutcomePolicy) => Required<Pick<VoiceTelephonyOutcomePolicy, "completedStatuses" | "escalationStatuses" | "failedAsNoAnswer" | "failedStatuses" | "includeProviderPayload" | "machineDetectionVoicemailValues" | "noAnswerOnZeroDuration" | "noAnswerSipCodes" | "noAnswerStatuses" | "transferStatuses" | "voicemailStatuses">> & VoiceTelephonyOutcomePolicy;
|
|
143
190
|
export declare const resolveVoiceTelephonyOutcome: (event: VoiceTelephonyOutcomeProviderEvent, policyInput?: VoiceTelephonyOutcomePolicy) => VoiceTelephonyOutcomeDecision;
|
|
144
191
|
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,75 @@ 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 duplicateDecisions = decisions.filter((decision) => decision.duplicate === true);
|
|
8260
|
+
const duplicateProviders = uniqueSorted(duplicateDecisions.map((decision) => decision.provider ?? decision.event?.provider).filter(isTelephonyWebhookProvider));
|
|
8261
|
+
const duplicateIdempotencyKeys = new Set(duplicateDecisions.map((decision) => decision.idempotencyKey).filter((key) => typeof key === "string" && key.length > 0)).size;
|
|
8262
|
+
const routeResults = decisions.filter((decision) => isRecord(decision.routeResult)).length;
|
|
8263
|
+
const missingSessionIds = decisions.filter((decision) => !decision.sessionId).length;
|
|
8264
|
+
if (input.minDecisions !== undefined && decisions.length < input.minDecisions) {
|
|
8265
|
+
issues.push(`Expected at least ${String(input.minDecisions)} telephony webhook decision(s), found ${String(decisions.length)}.`);
|
|
8266
|
+
}
|
|
8267
|
+
if (input.minApplied !== undefined && applied < input.minApplied) {
|
|
8268
|
+
issues.push(`Expected at least ${String(input.minApplied)} applied telephony webhook decision(s), found ${String(applied)}.`);
|
|
8269
|
+
}
|
|
8270
|
+
if (input.minDuplicates !== undefined && duplicateDecisions.length < input.minDuplicates) {
|
|
8271
|
+
issues.push(`Expected at least ${String(input.minDuplicates)} duplicate telephony webhook decision(s), found ${String(duplicateDecisions.length)}.`);
|
|
8272
|
+
}
|
|
8273
|
+
if (input.minDuplicateIdempotencyKeys !== undefined && duplicateIdempotencyKeys < input.minDuplicateIdempotencyKeys) {
|
|
8274
|
+
issues.push(`Expected at least ${String(input.minDuplicateIdempotencyKeys)} duplicate telephony webhook idempotency key(s), found ${String(duplicateIdempotencyKeys)}.`);
|
|
8275
|
+
}
|
|
8276
|
+
if (input.maxMissingSessionIds !== undefined && missingSessionIds > input.maxMissingSessionIds) {
|
|
8277
|
+
issues.push(`Expected at most ${String(input.maxMissingSessionIds)} telephony webhook decision(s) without sessionId, found ${String(missingSessionIds)}.`);
|
|
8278
|
+
}
|
|
8279
|
+
if (input.requireRouteResults && routeResults < decisions.length) {
|
|
8280
|
+
issues.push(`Expected every telephony webhook decision to include a route result, found ${String(routeResults)} of ${String(decisions.length)}.`);
|
|
8281
|
+
}
|
|
8282
|
+
for (const provider of findMissing(providers, input.requiredProviders)) {
|
|
8283
|
+
issues.push(`Missing telephony webhook provider: ${provider}.`);
|
|
8284
|
+
}
|
|
8285
|
+
for (const provider of findMissing(duplicateProviders, input.requiredDuplicateProviders)) {
|
|
8286
|
+
issues.push(`Missing duplicate telephony webhook provider: ${provider}.`);
|
|
8287
|
+
}
|
|
8288
|
+
for (const action of findMissing(actions, input.requiredActions)) {
|
|
8289
|
+
issues.push(`Missing telephony webhook action: ${action}.`);
|
|
8290
|
+
}
|
|
8291
|
+
for (const disposition of findMissing(dispositions, input.requiredDispositions)) {
|
|
8292
|
+
issues.push(`Missing telephony webhook disposition: ${disposition}.`);
|
|
8293
|
+
}
|
|
8294
|
+
return {
|
|
8295
|
+
actions,
|
|
8296
|
+
applied,
|
|
8297
|
+
decisions: decisions.length,
|
|
8298
|
+
dispositions,
|
|
8299
|
+
duplicateIdempotencyKeys,
|
|
8300
|
+
duplicateProviders,
|
|
8301
|
+
duplicates: duplicateDecisions.length,
|
|
8302
|
+
issues,
|
|
8303
|
+
missingSessionIds,
|
|
8304
|
+
ok: issues.length === 0,
|
|
8305
|
+
providers,
|
|
8306
|
+
routeResults,
|
|
8307
|
+
sources
|
|
8308
|
+
};
|
|
8309
|
+
};
|
|
8310
|
+
var assertVoiceTelephonyWebhookNormalizationEvidence = (input = {}) => {
|
|
8311
|
+
const assertion = evaluateVoiceTelephonyWebhookNormalizationEvidence(input);
|
|
8312
|
+
if (!assertion.ok) {
|
|
8313
|
+
throw new Error(`Voice telephony webhook normalization evidence assertion failed: ${assertion.issues.join(" ")}`);
|
|
8314
|
+
}
|
|
8315
|
+
return assertion;
|
|
8316
|
+
};
|
|
8240
8317
|
var normalizeToken = (value) => typeof value === "string" ? value.trim().toLowerCase().replace(/\s+/g, "-").replace(/_+/g, "-") : undefined;
|
|
8241
8318
|
var firstString = (source, keys) => {
|
|
8242
8319
|
for (const key of keys) {
|