@absolutejs/voice 0.0.22-beta.263 → 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 +4 -4
- package/dist/index.js +130 -14
- package/dist/phoneAgent.d.ts +23 -0
- 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,8 +40,8 @@ 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';
|
|
44
|
-
export { assertVoicePhoneAssistantEvidence, createVoicePhoneAgent, evaluateVoicePhoneAssistantEvidence } from './phoneAgent';
|
|
43
|
+
export { applyVoiceTelephonyOutcome, assertVoiceTelephonyWebhookNormalizationEvidence, createMemoryVoiceTelephonyWebhookIdempotencyStore, createVoiceTelephonyOutcomePolicy, createVoiceTelephonyWebhookHandler, createVoiceTelephonyWebhookRoutes, evaluateVoiceTelephonyWebhookNormalizationEvidence, parseVoiceTelephonyWebhookEvent, resolveVoiceTelephonyOutcome, signVoiceTwilioWebhook, verifyVoiceTwilioWebhookSignature, voiceTelephonyOutcomeToRouteResult } from './telephonyOutcome';
|
|
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';
|
|
47
47
|
export { createAnthropicVoiceAssistantModel, createGeminiVoiceAssistantModel, createJSONVoiceAssistantModel, createOpenAIVoiceAssistantModel, resolveVoiceProviderRoutingPolicyPreset, createVoiceProviderRouter } from './modelAdapters';
|
|
@@ -115,8 +115,8 @@ 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';
|
|
119
|
-
export type { VoicePhoneAgentCarrier, VoicePhoneAgentCarrierSummary, VoicePhoneAssistantEvidenceInput, VoicePhoneAssistantEvidenceReport, VoicePhoneAgentLifecycleStage, VoicePhoneAgentPlivoCarrier, VoicePhoneAgentRoutes, VoicePhoneAgentRoutesOptions, VoicePhoneAgentSetupReport, VoicePhoneAgentTelnyxCarrier, VoicePhoneAgentTwilioCarrier } from './phoneAgent';
|
|
118
|
+
export type { VoiceTelephonyOutcomeAction, VoiceTelephonyOutcomeDecision, VoiceTelephonyOutcomePolicy, VoiceTelephonyOutcomeProviderEvent, VoiceTelephonyOutcomeRouteResult, VoiceTelephonyOutcomeStatusDecision, VoiceTelephonyWebhookDecision, VoiceTelephonyWebhookHandlerOptions, VoiceTelephonyWebhookIdempotencyStore, VoiceTelephonyWebhookNormalizationEvidenceDecision, VoiceTelephonyWebhookNormalizationEvidenceInput, VoiceTelephonyWebhookNormalizationEvidenceReport, VoiceTelephonyWebhookParseInput, VoiceTelephonyWebhookProvider, VoiceTelephonyWebhookRoutesOptions, VoiceTelephonyWebhookVerificationResult, StoredVoiceTelephonyWebhookDecision } from './telephonyOutcome';
|
|
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';
|
|
122
122
|
export type { VoiceOpsStatus, VoiceOpsStatusLink, VoiceOpsStatusOptions, VoiceOpsStatusReport, VoiceOpsStatusRoutesOptions } from './opsStatus';
|
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, {
|
|
@@ -20113,6 +20175,56 @@ var assertVoicePhoneAssistantEvidence = (report, input = {}) => {
|
|
|
20113
20175
|
}
|
|
20114
20176
|
return assertion;
|
|
20115
20177
|
};
|
|
20178
|
+
var evaluateVoicePhoneCallControlEvidence = (input = {}) => {
|
|
20179
|
+
const issues = [];
|
|
20180
|
+
const setup = input.setup;
|
|
20181
|
+
const productionSmokes = input.productionSmokes ?? [];
|
|
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));
|
|
20185
|
+
const failedSmokeReports = productionSmokes.filter((report) => !report.pass).length;
|
|
20186
|
+
const passingSmokeReports = productionSmokes.length - failedSmokeReports;
|
|
20187
|
+
if (productionSmokes.length === 0) {
|
|
20188
|
+
issues.push("Expected phone call-control smoke report(s) to be present.");
|
|
20189
|
+
}
|
|
20190
|
+
if (input.requireSetupLifecycleStages ?? true) {
|
|
20191
|
+
if (!setup) {
|
|
20192
|
+
issues.push("Expected phone setup report to be present.");
|
|
20193
|
+
}
|
|
20194
|
+
for (const stage of findMissing4(lifecycleStages, input.requiredLifecycleStages)) {
|
|
20195
|
+
issues.push(`Missing phone call-control lifecycle stage: ${stage}.`);
|
|
20196
|
+
}
|
|
20197
|
+
}
|
|
20198
|
+
if (input.minPassingSmokeReports !== undefined && passingSmokeReports < input.minPassingSmokeReports) {
|
|
20199
|
+
issues.push(`Expected at least ${String(input.minPassingSmokeReports)} passing phone call-control smoke report(s), found ${String(passingSmokeReports)}.`);
|
|
20200
|
+
}
|
|
20201
|
+
if (input.maxFailedSmokeReports !== undefined && failedSmokeReports > input.maxFailedSmokeReports) {
|
|
20202
|
+
issues.push(`Expected at most ${String(input.maxFailedSmokeReports)} failing phone call-control smoke report(s), found ${String(failedSmokeReports)}.`);
|
|
20203
|
+
}
|
|
20204
|
+
for (const provider of findMissing4(providers, input.requiredProviders)) {
|
|
20205
|
+
issues.push(`Missing phone call-control provider: ${provider}.`);
|
|
20206
|
+
}
|
|
20207
|
+
for (const outcome of findMissing4(outcomes, input.requiredOutcomes)) {
|
|
20208
|
+
issues.push(`Missing phone call-control outcome: ${outcome}.`);
|
|
20209
|
+
}
|
|
20210
|
+
return {
|
|
20211
|
+
failedSmokeReports,
|
|
20212
|
+
issues,
|
|
20213
|
+
lifecycleStages,
|
|
20214
|
+
ok: issues.length === 0,
|
|
20215
|
+
outcomes,
|
|
20216
|
+
passingSmokeReports,
|
|
20217
|
+
providers,
|
|
20218
|
+
smokeReports: productionSmokes.length
|
|
20219
|
+
};
|
|
20220
|
+
};
|
|
20221
|
+
var assertVoicePhoneCallControlEvidence = (input = {}) => {
|
|
20222
|
+
const assertion = evaluateVoicePhoneCallControlEvidence(input);
|
|
20223
|
+
if (!assertion.ok) {
|
|
20224
|
+
throw new Error(`Voice phone call-control evidence assertion failed: ${assertion.issues.join(" ")}`);
|
|
20225
|
+
}
|
|
20226
|
+
return assertion;
|
|
20227
|
+
};
|
|
20116
20228
|
var buildVoicePhoneAgentSetupInstructions = (input) => input.carriers.map((carrier) => {
|
|
20117
20229
|
const entry = findCarrierMatrixEntry(input.matrix, carrier);
|
|
20118
20230
|
const urls = entry?.setup.urls;
|
|
@@ -23045,7 +23157,7 @@ var statusRank = {
|
|
|
23045
23157
|
var escapeHtml36 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23046
23158
|
var roundMetric3 = (value) => Math.round(value * 1e4) / 1e4;
|
|
23047
23159
|
var rate3 = (count, total) => count / Math.max(1, total);
|
|
23048
|
-
var
|
|
23160
|
+
var uniqueSorted5 = (values) => [
|
|
23049
23161
|
...new Set(values.filter((value) => typeof value === "string"))
|
|
23050
23162
|
].sort();
|
|
23051
23163
|
var percentile3 = (values, rank) => {
|
|
@@ -23228,7 +23340,7 @@ var buildVoiceProviderSloReport = async (options = {}) => {
|
|
|
23228
23340
|
var evaluateVoiceProviderSloEvidence = (report, input = {}) => {
|
|
23229
23341
|
const issues = [];
|
|
23230
23342
|
const kindReports = Object.values(report.kinds);
|
|
23231
|
-
const providers =
|
|
23343
|
+
const providers = uniqueSorted5(kindReports.flatMap((kind) => kind.providers));
|
|
23232
23344
|
const kinds = providerKinds.filter((kind) => report.kinds[kind].events > 0);
|
|
23233
23345
|
const fallbacks = kindReports.reduce((total, kind) => total + kind.fallbacks, 0);
|
|
23234
23346
|
const timeouts = kindReports.reduce((total, kind) => total + kind.timeouts, 0);
|
|
@@ -23949,7 +24061,7 @@ var hasPayloadValue = (payload, key, values) => {
|
|
|
23949
24061
|
return typeof value === "string" && values.has(value);
|
|
23950
24062
|
};
|
|
23951
24063
|
var countIntegrationDeliveryStatus = (events, status) => events.filter((event) => event.deliveryStatus === status).length;
|
|
23952
|
-
var
|
|
24064
|
+
var uniqueSorted6 = (values) => [
|
|
23953
24065
|
...new Set(values.filter((value) => typeof value === "string"))
|
|
23954
24066
|
].sort();
|
|
23955
24067
|
var pushMissingValuesIssue = (input) => {
|
|
@@ -24128,11 +24240,11 @@ var buildVoiceOperationsRecord = async (options) => {
|
|
|
24128
24240
|
var evaluateVoiceOperationsRecordGuardrails = (record, input = {}) => {
|
|
24129
24241
|
const issues = [];
|
|
24130
24242
|
const decisions = record.guardrails.decisions;
|
|
24131
|
-
const proofs =
|
|
24132
|
-
const ruleIds =
|
|
24133
|
-
const stages =
|
|
24134
|
-
const statuses =
|
|
24135
|
-
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));
|
|
24136
24248
|
const minDecisions = input.minDecisions ?? 1;
|
|
24137
24249
|
if (record.guardrails.total < minDecisions) {
|
|
24138
24250
|
issues.push(`Expected at least ${String(minDecisions)} guardrail decisions, found ${String(record.guardrails.total)}.`);
|
|
@@ -31369,6 +31481,7 @@ export {
|
|
|
31369
31481
|
exportVoiceAuditTrail,
|
|
31370
31482
|
evaluateVoiceTrace,
|
|
31371
31483
|
evaluateVoiceToolContractEvidence,
|
|
31484
|
+
evaluateVoiceTelephonyWebhookNormalizationEvidence,
|
|
31372
31485
|
evaluateVoiceTelephonyContract,
|
|
31373
31486
|
evaluateVoiceSimulationSuiteEvidence,
|
|
31374
31487
|
evaluateVoiceQuality,
|
|
@@ -31380,6 +31493,7 @@ export {
|
|
|
31380
31493
|
evaluateVoiceProofTrendEvidence,
|
|
31381
31494
|
evaluateVoiceProductionReadinessEvidence,
|
|
31382
31495
|
evaluateVoicePlatformCoverage,
|
|
31496
|
+
evaluateVoicePhoneCallControlEvidence,
|
|
31383
31497
|
evaluateVoicePhoneAssistantEvidence,
|
|
31384
31498
|
evaluateVoiceOutcomeContractEvidence,
|
|
31385
31499
|
evaluateVoiceOperationsRecordGuardrails,
|
|
@@ -31704,6 +31818,7 @@ export {
|
|
|
31704
31818
|
buildEmptyVoiceProofTrendReport,
|
|
31705
31819
|
assignVoiceOpsTask,
|
|
31706
31820
|
assertVoiceToolContractEvidence,
|
|
31821
|
+
assertVoiceTelephonyWebhookNormalizationEvidence,
|
|
31707
31822
|
assertVoiceSimulationSuiteEvidence,
|
|
31708
31823
|
assertVoiceProviderStackEvidence,
|
|
31709
31824
|
assertVoiceProviderSloEvidence,
|
|
@@ -31713,6 +31828,7 @@ export {
|
|
|
31713
31828
|
assertVoiceProofTrendEvidence,
|
|
31714
31829
|
assertVoiceProductionReadinessEvidence,
|
|
31715
31830
|
assertVoicePlatformCoverage,
|
|
31831
|
+
assertVoicePhoneCallControlEvidence,
|
|
31716
31832
|
assertVoicePhoneAssistantEvidence,
|
|
31717
31833
|
assertVoiceOutcomeContractEvidence,
|
|
31718
31834
|
assertVoiceOperationsRecordGuardrails,
|
package/dist/phoneAgent.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { type VoiceTelephonyCarrierMatrix, type VoiceTelephonyCarrierMatrixRoute
|
|
|
6
6
|
import { type VoicePhoneAgentProductionSmokeRoutesOptions } from './phoneAgentProductionSmoke';
|
|
7
7
|
import { type VoiceCampaignDialerProofAssertionReport, type VoiceCampaignDialerProofProvider, type VoiceCampaignDialerProofReport } from './campaignDialers';
|
|
8
8
|
import type { VoiceTelephonyProvider } from './telephony/contract';
|
|
9
|
+
import type { VoicePhoneAgentProductionSmokeReport } from './phoneAgentProductionSmoke';
|
|
9
10
|
import type { VoiceSessionRecord } from './types';
|
|
10
11
|
export type VoicePhoneAgentLifecycleStage = 'ringing' | 'answered' | 'media-started' | 'transcript' | 'assistant-response' | 'transfer' | 'voicemail' | 'no-answer' | 'completed' | 'failed';
|
|
11
12
|
type VoicePhoneAgentCarrierBase = {
|
|
@@ -110,7 +111,29 @@ export type VoicePhoneAssistantEvidenceReport = {
|
|
|
110
111
|
setupPath?: string;
|
|
111
112
|
smokePassing: number;
|
|
112
113
|
};
|
|
114
|
+
export type VoicePhoneCallControlEvidenceInput = {
|
|
115
|
+
maxFailedSmokeReports?: number;
|
|
116
|
+
minPassingSmokeReports?: number;
|
|
117
|
+
productionSmokes?: VoicePhoneAgentProductionSmokeReport[];
|
|
118
|
+
requiredLifecycleStages?: VoicePhoneAgentLifecycleStage[];
|
|
119
|
+
requiredOutcomes?: string[];
|
|
120
|
+
requiredProviders?: VoiceTelephonyProvider[];
|
|
121
|
+
requireSetupLifecycleStages?: boolean;
|
|
122
|
+
setup?: VoicePhoneAgentSetupReport;
|
|
123
|
+
};
|
|
124
|
+
export type VoicePhoneCallControlEvidenceReport = {
|
|
125
|
+
failedSmokeReports: number;
|
|
126
|
+
issues: string[];
|
|
127
|
+
lifecycleStages: VoicePhoneAgentLifecycleStage[];
|
|
128
|
+
ok: boolean;
|
|
129
|
+
outcomes: string[];
|
|
130
|
+
passingSmokeReports: number;
|
|
131
|
+
providers: VoiceTelephonyProvider[];
|
|
132
|
+
smokeReports: number;
|
|
133
|
+
};
|
|
113
134
|
export declare const evaluateVoicePhoneAssistantEvidence: (report: VoicePhoneAgentSetupReport, input?: VoicePhoneAssistantEvidenceInput) => VoicePhoneAssistantEvidenceReport;
|
|
114
135
|
export declare const assertVoicePhoneAssistantEvidence: (report: VoicePhoneAgentSetupReport, input?: VoicePhoneAssistantEvidenceInput) => VoicePhoneAssistantEvidenceReport;
|
|
136
|
+
export declare const evaluateVoicePhoneCallControlEvidence: (input?: VoicePhoneCallControlEvidenceInput) => VoicePhoneCallControlEvidenceReport;
|
|
137
|
+
export declare const assertVoicePhoneCallControlEvidence: (input?: VoicePhoneCallControlEvidenceInput) => VoicePhoneCallControlEvidenceReport;
|
|
115
138
|
export declare const createVoicePhoneAgent: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(options: VoicePhoneAgentRoutesOptions<TContext, TSession, TResult>) => VoicePhoneAgentRoutes;
|
|
116
139
|
export {};
|
|
@@ -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) {
|