@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 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 uniqueSorted3 = (values) => Array.from(new Set(values)).sort();
20024
- var findMissing3 = (values, required) => {
20085
+ var uniqueSorted4 = (values) => Array.from(new Set(values)).sort();
20086
+ var findMissing4 = (values, required) => {
20025
20087
  if (!required?.length) {
20026
20088
  return [];
20027
20089
  }
@@ -20030,8 +20092,8 @@ var findMissing3 = (values, required) => {
20030
20092
  };
20031
20093
  var evaluateVoicePhoneAssistantEvidence = (report, input = {}) => {
20032
20094
  const issues = [];
20033
- const providers = uniqueSorted3(report.carriers.map((carrier) => carrier.provider));
20034
- const lifecycleStages = uniqueSorted3(report.lifecycleStages);
20095
+ const providers = uniqueSorted4(report.carriers.map((carrier) => carrier.provider));
20096
+ const lifecycleStages = uniqueSorted4(report.lifecycleStages);
20035
20097
  const carrierSummary = report.matrix?.summary;
20036
20098
  const carrierFailures = carrierSummary?.failing ?? 0;
20037
20099
  const carrierWarnings = carrierSummary?.warnings ?? 0;
@@ -20071,10 +20133,10 @@ var evaluateVoicePhoneAssistantEvidence = (report, input = {}) => {
20071
20133
  if (input.maxCarrierWarnings !== undefined && carrierWarnings > input.maxCarrierWarnings) {
20072
20134
  issues.push(`Expected at most ${String(input.maxCarrierWarnings)} phone carrier warning(s), found ${String(carrierWarnings)}.`);
20073
20135
  }
20074
- for (const provider of findMissing3(providers, input.requiredProviders)) {
20136
+ for (const provider of findMissing4(providers, input.requiredProviders)) {
20075
20137
  issues.push(`Missing phone carrier provider: ${provider}.`);
20076
20138
  }
20077
- for (const stage of findMissing3(lifecycleStages, input.requiredLifecycleStages)) {
20139
+ for (const stage of findMissing4(lifecycleStages, input.requiredLifecycleStages)) {
20078
20140
  issues.push(`Missing phone lifecycle stage: ${stage}.`);
20079
20141
  }
20080
20142
  const dialer = input.dialerProof ? evaluateVoiceCampaignDialerProofEvidence(input.dialerProof, {
@@ -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("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
23046
23158
  var roundMetric3 = (value) => Math.round(value * 1e4) / 1e4;
23047
23159
  var rate3 = (count, total) => count / Math.max(1, total);
23048
- var uniqueSorted4 = (values) => [
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 = uniqueSorted4(kindReports.flatMap((kind) => kind.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 uniqueSorted5 = (values) => [
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 = uniqueSorted5(decisions.map((decision) => decision.proof));
24132
- const ruleIds = uniqueSorted5(decisions.flatMap((decision) => decision.findings.map((finding) => finding.ruleId)));
24133
- const stages = uniqueSorted5(decisions.map((decision) => decision.stage));
24134
- const statuses = uniqueSorted5(decisions.map((decision) => decision.status));
24135
- const toolNames = uniqueSorted5(decisions.map((decision) => decision.toolName));
24243
+ const proofs = uniqueSorted6(decisions.map((decision) => decision.proof));
24244
+ const ruleIds = uniqueSorted6(decisions.flatMap((decision) => decision.findings.map((finding) => finding.ruleId)));
24245
+ const stages = uniqueSorted6(decisions.map((decision) => decision.stage));
24246
+ const statuses = uniqueSorted6(decisions.map((decision) => decision.status));
24247
+ const toolNames = uniqueSorted6(decisions.map((decision) => decision.toolName));
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,
@@ -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>;
@@ -8219,6 +8219,14 @@ var DEFAULT_MACHINE_VOICEMAIL_VALUES = [
8219
8219
  ];
8220
8220
  var DEFAULT_NO_ANSWER_SIP_CODES = [408, 480, 486, 487, 603];
8221
8221
  var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
8222
+ var uniqueSorted = (values) => Array.from(new Set(values)).sort();
8223
+ var findMissing = (values, required) => {
8224
+ if (!required?.length) {
8225
+ return [];
8226
+ }
8227
+ const valueSet = new Set(values);
8228
+ return required.filter((value) => !valueSet.has(value));
8229
+ };
8222
8230
 
8223
8231
  class VoiceTelephonyWebhookVerificationError extends Error {
8224
8232
  result;
@@ -8237,6 +8245,60 @@ var createMemoryVoiceTelephonyWebhookIdempotencyStore = () => {
8237
8245
  }
8238
8246
  };
8239
8247
  };
8248
+ var isTelephonyWebhookProvider = (value) => value === "generic" || value === "plivo" || value === "telnyx" || value === "twilio";
8249
+ var isTelephonyOutcomeAction = (value) => value === "complete" || value === "escalate" || value === "ignore" || value === "no-answer" || value === "transfer" || value === "voicemail";
8250
+ var isCallDisposition = (value) => value === "completed" || value === "escalated" || value === "failed" || value === "no-answer" || value === "transferred" || value === "voicemail";
8251
+ var evaluateVoiceTelephonyWebhookNormalizationEvidence = (input = {}) => {
8252
+ const issues = [];
8253
+ const decisions = input.decisions ?? [];
8254
+ const actions = uniqueSorted(decisions.map((decision) => decision.decision?.action ?? decision.action).filter(isTelephonyOutcomeAction));
8255
+ const dispositions = uniqueSorted(decisions.map((decision) => decision.decision?.disposition ?? decision.disposition).filter(isCallDisposition));
8256
+ const providers = uniqueSorted(decisions.map((decision) => decision.provider ?? decision.event?.provider).filter(isTelephonyWebhookProvider));
8257
+ const sources = uniqueSorted(decisions.map((decision) => decision.decision?.source ?? decision.source).filter((source) => typeof source === "string"));
8258
+ const applied = decisions.filter((decision) => decision.applied === true).length;
8259
+ const routeResults = decisions.filter((decision) => isRecord(decision.routeResult)).length;
8260
+ const missingSessionIds = decisions.filter((decision) => !decision.sessionId).length;
8261
+ if (input.minDecisions !== undefined && decisions.length < input.minDecisions) {
8262
+ issues.push(`Expected at least ${String(input.minDecisions)} telephony webhook decision(s), found ${String(decisions.length)}.`);
8263
+ }
8264
+ if (input.minApplied !== undefined && applied < input.minApplied) {
8265
+ issues.push(`Expected at least ${String(input.minApplied)} applied telephony webhook decision(s), found ${String(applied)}.`);
8266
+ }
8267
+ if (input.maxMissingSessionIds !== undefined && missingSessionIds > input.maxMissingSessionIds) {
8268
+ issues.push(`Expected at most ${String(input.maxMissingSessionIds)} telephony webhook decision(s) without sessionId, found ${String(missingSessionIds)}.`);
8269
+ }
8270
+ if (input.requireRouteResults && routeResults < decisions.length) {
8271
+ issues.push(`Expected every telephony webhook decision to include a route result, found ${String(routeResults)} of ${String(decisions.length)}.`);
8272
+ }
8273
+ for (const provider of findMissing(providers, input.requiredProviders)) {
8274
+ issues.push(`Missing telephony webhook provider: ${provider}.`);
8275
+ }
8276
+ for (const action of findMissing(actions, input.requiredActions)) {
8277
+ issues.push(`Missing telephony webhook action: ${action}.`);
8278
+ }
8279
+ for (const disposition of findMissing(dispositions, input.requiredDispositions)) {
8280
+ issues.push(`Missing telephony webhook disposition: ${disposition}.`);
8281
+ }
8282
+ return {
8283
+ actions,
8284
+ applied,
8285
+ decisions: decisions.length,
8286
+ dispositions,
8287
+ issues,
8288
+ missingSessionIds,
8289
+ ok: issues.length === 0,
8290
+ providers,
8291
+ routeResults,
8292
+ sources
8293
+ };
8294
+ };
8295
+ var assertVoiceTelephonyWebhookNormalizationEvidence = (input = {}) => {
8296
+ const assertion = evaluateVoiceTelephonyWebhookNormalizationEvidence(input);
8297
+ if (!assertion.ok) {
8298
+ throw new Error(`Voice telephony webhook normalization evidence assertion failed: ${assertion.issues.join(" ")}`);
8299
+ }
8300
+ return assertion;
8301
+ };
8240
8302
  var normalizeToken = (value) => typeof value === "string" ? value.trim().toLowerCase().replace(/\s+/g, "-").replace(/_+/g, "-") : undefined;
8241
8303
  var firstString = (source, keys) => {
8242
8304
  for (const key of keys) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.263",
3
+ "version": "0.0.22-beta.265",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",