@absolutejs/voice 0.0.22-beta.67 → 0.0.22-beta.69
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 -0
- package/dist/index.js +434 -2
- package/dist/outcomeContract.d.ts +112 -0
- package/dist/telephonyOutcome.d.ts +49 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -10,6 +10,8 @@ export { createVoiceAgent, createVoiceAgentSquad, createVoiceAgentTool } from '.
|
|
|
10
10
|
export { createVoiceToolIdempotencyKey, createVoiceToolRuntime } from './toolRuntime';
|
|
11
11
|
export { createVoiceToolContract, createVoiceToolContractHTMLHandler, createVoiceToolContractJSONHandler, createVoiceToolContractRoutes, createVoiceToolRuntimeContractDefaults, renderVoiceToolContractHTML, runVoiceToolContractSuite, runVoiceToolContract } from './toolContract';
|
|
12
12
|
export { createVoiceTurnQualityHTMLHandler, createVoiceTurnQualityJSONHandler, createVoiceTurnQualityRoutes, renderVoiceTurnQualityHTML, summarizeVoiceTurnQuality } from './turnQuality';
|
|
13
|
+
export { createVoiceOutcomeContractHTMLHandler, createVoiceOutcomeContractJSONHandler, createVoiceOutcomeContractRoutes, renderVoiceOutcomeContractHTML, runVoiceOutcomeContractSuite } from './outcomeContract';
|
|
14
|
+
export { applyVoiceTelephonyOutcome, createVoiceTelephonyOutcomePolicy, resolveVoiceTelephonyOutcome, voiceTelephonyOutcomeToRouteResult } from './telephonyOutcome';
|
|
13
15
|
export { createStoredVoiceCallReviewArtifact, createStoredVoiceExternalObjectMap, createStoredVoiceIntegrationEvent, createStoredVoiceOpsTask, createVoiceFileExternalObjectMapStore, createVoiceFileAssistantMemoryStore, createVoiceFileIntegrationEventStore, createVoiceFileReviewStore, createVoiceFileRuntimeStorage, createVoiceFileSessionStore, createVoiceFileTaskStore, createVoiceFileTraceSinkDeliveryStore, createVoiceFileTraceEventStore } from './fileStore';
|
|
14
16
|
export { createVoiceAssistantMemoryHandle, createVoiceAssistantMemoryRecord, createVoiceMemoryAssistantMemoryStore, resolveVoiceAssistantMemoryNamespace } from './assistantMemory';
|
|
15
17
|
export { createAnthropicVoiceAssistantModel, createGeminiVoiceAssistantModel, createJSONVoiceAssistantModel, createOpenAIVoiceAssistantModel, resolveVoiceProviderRoutingPolicyPreset, createVoiceProviderRouter } from './modelAdapters';
|
|
@@ -54,6 +56,8 @@ export type { AnthropicVoiceAssistantModelOptions, GeminiVoiceAssistantModelOpti
|
|
|
54
56
|
export type { VoiceProviderHealthStatus, VoiceProviderHealthSummary, VoiceProviderHealthSummaryOptions } from './providerHealth';
|
|
55
57
|
export type { VoiceProviderCapabilityDefinition, VoiceProviderCapabilityHandlerOptions, VoiceProviderCapabilityHTMLHandlerOptions, VoiceProviderCapabilityKind, VoiceProviderCapabilityOptions, VoiceProviderCapabilityReport, VoiceProviderCapabilityRoutesOptions, VoiceProviderCapabilitySummary } from './providerCapabilities';
|
|
56
58
|
export type { VoiceTurnQualityHTMLHandlerOptions, VoiceTurnQualityItem, VoiceTurnQualityOptions, VoiceTurnQualityReport, VoiceTurnQualityRoutesOptions, VoiceTurnQualityStatus } from './turnQuality';
|
|
59
|
+
export type { VoiceOutcomeContractDefinition, VoiceOutcomeContractHTMLHandlerOptions, VoiceOutcomeContractIssue, VoiceOutcomeContractOptions, VoiceOutcomeContractReport, VoiceOutcomeContractRoutesOptions, VoiceOutcomeContractStatus, VoiceOutcomeContractSuiteReport } from './outcomeContract';
|
|
60
|
+
export type { VoiceTelephonyOutcomeAction, VoiceTelephonyOutcomeDecision, VoiceTelephonyOutcomePolicy, VoiceTelephonyOutcomeProviderEvent, VoiceTelephonyOutcomeRouteResult, VoiceTelephonyOutcomeStatusDecision } from './telephonyOutcome';
|
|
57
61
|
export type { VoiceOpsConsoleLink, VoiceOpsConsoleReport, VoiceOpsConsoleRoutesOptions } from './opsConsoleRoutes';
|
|
58
62
|
export type { VoiceQualityLink, VoiceQualityMetric, VoiceQualityReport, VoiceQualityRoutesOptions, VoiceQualityStatus, VoiceQualityThresholds } from './qualityRoutes';
|
|
59
63
|
export type { VoiceResilienceIOSimulator, VoiceResilienceLink, VoiceResiliencePageData, VoiceResilienceRoutesOptions, VoiceResilienceSimulationProvider, VoiceRoutingDecisionSummary, VoiceRoutingDecisionSummaryOptions, VoiceRoutingEvent, VoiceRoutingEventKind } from './resilienceRoutes';
|
package/dist/index.js
CHANGED
|
@@ -10410,6 +10410,429 @@ var createVoiceTurnQualityRoutes = (options) => {
|
|
|
10410
10410
|
}
|
|
10411
10411
|
return routes;
|
|
10412
10412
|
};
|
|
10413
|
+
// src/outcomeContract.ts
|
|
10414
|
+
import { Elysia as Elysia15 } from "elysia";
|
|
10415
|
+
var escapeHtml16 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
10416
|
+
var getPayloadString = (event, key) => typeof event.payload[key] === "string" ? event.payload[key] : undefined;
|
|
10417
|
+
var toList = async (input) => Array.isArray(input) ? input : await input?.list() ?? [];
|
|
10418
|
+
var hydrateSessions = async (input) => {
|
|
10419
|
+
if (!input)
|
|
10420
|
+
return [];
|
|
10421
|
+
if (Array.isArray(input))
|
|
10422
|
+
return input;
|
|
10423
|
+
const summaries = await input.list();
|
|
10424
|
+
const sessions = await Promise.all(summaries.map((summary) => input.get(summary.id)));
|
|
10425
|
+
const hydrated = [];
|
|
10426
|
+
for (const session of sessions) {
|
|
10427
|
+
if (session) {
|
|
10428
|
+
hydrated.push(session);
|
|
10429
|
+
}
|
|
10430
|
+
}
|
|
10431
|
+
return hydrated;
|
|
10432
|
+
};
|
|
10433
|
+
var dispositionForSession = (session) => session.call?.disposition ?? (session.status === "completed" ? "completed" : undefined);
|
|
10434
|
+
var matchesDisposition = (disposition, expected) => expected === undefined || disposition === expected;
|
|
10435
|
+
var reportContract = (input) => {
|
|
10436
|
+
const { contract } = input;
|
|
10437
|
+
const sessions = input.sessions.filter((session) => (!contract.scenarioId || session.scenarioId === contract.scenarioId) && matchesDisposition(dispositionForSession(session), contract.expectedDisposition));
|
|
10438
|
+
const sessionIds = new Set(sessions.map((session) => session.id));
|
|
10439
|
+
const reviews = input.reviews.filter((review) => matchesDisposition(review.summary.outcome, contract.expectedDisposition));
|
|
10440
|
+
const tasks = input.tasks.filter((task) => matchesDisposition(task.outcome, contract.expectedDisposition));
|
|
10441
|
+
const handoffs = input.handoffs.filter((handoff) => (!contract.expectedDisposition || handoff.action === contract.expectedDisposition || contract.expectedDisposition === "transferred" && handoff.action === "transfer" || contract.expectedDisposition === "escalated" && handoff.action === "escalate") && (sessionIds.size === 0 || sessionIds.has(handoff.sessionId)));
|
|
10442
|
+
const events = input.events.filter((event) => {
|
|
10443
|
+
const eventSessionId = getPayloadString(event, "sessionId");
|
|
10444
|
+
const eventOutcome = getPayloadString(event, "outcome") ?? getPayloadString(event, "disposition");
|
|
10445
|
+
return (sessionIds.size === 0 || !eventSessionId || sessionIds.has(eventSessionId)) && (!contract.expectedDisposition || eventOutcome === contract.expectedDisposition);
|
|
10446
|
+
});
|
|
10447
|
+
const issues = [];
|
|
10448
|
+
const minSessions = contract.minSessions ?? 1;
|
|
10449
|
+
if (sessions.length < minSessions) {
|
|
10450
|
+
issues.push({
|
|
10451
|
+
code: "outcome.sessions_missing",
|
|
10452
|
+
message: `Expected at least ${minSessions} matching session(s), saw ${sessions.length}.`
|
|
10453
|
+
});
|
|
10454
|
+
}
|
|
10455
|
+
if (contract.requireReview !== false && reviews.length === 0) {
|
|
10456
|
+
issues.push({
|
|
10457
|
+
code: "outcome.review_missing",
|
|
10458
|
+
message: "Expected at least one matching review artifact."
|
|
10459
|
+
});
|
|
10460
|
+
}
|
|
10461
|
+
if (contract.requireTask && tasks.length < (contract.minTasks ?? 1)) {
|
|
10462
|
+
issues.push({
|
|
10463
|
+
code: "outcome.task_missing",
|
|
10464
|
+
message: `Expected at least ${contract.minTasks ?? 1} matching task(s), saw ${tasks.length}.`
|
|
10465
|
+
});
|
|
10466
|
+
}
|
|
10467
|
+
for (const action of contract.requireHandoffActions ?? []) {
|
|
10468
|
+
if (!handoffs.some((handoff) => handoff.action === action)) {
|
|
10469
|
+
issues.push({
|
|
10470
|
+
code: "outcome.handoff_missing",
|
|
10471
|
+
message: `Expected handoff action ${action}.`
|
|
10472
|
+
});
|
|
10473
|
+
}
|
|
10474
|
+
}
|
|
10475
|
+
for (const type of contract.requireIntegrationEvents ?? []) {
|
|
10476
|
+
if (!events.some((event) => event.type === type)) {
|
|
10477
|
+
issues.push({
|
|
10478
|
+
code: "outcome.integration_event_missing",
|
|
10479
|
+
message: `Expected integration event ${type}.`
|
|
10480
|
+
});
|
|
10481
|
+
}
|
|
10482
|
+
}
|
|
10483
|
+
return {
|
|
10484
|
+
contractId: contract.id,
|
|
10485
|
+
description: contract.description,
|
|
10486
|
+
issues,
|
|
10487
|
+
label: contract.label,
|
|
10488
|
+
matched: {
|
|
10489
|
+
handoffs: handoffs.length,
|
|
10490
|
+
integrationEvents: events.length,
|
|
10491
|
+
reviews: reviews.length,
|
|
10492
|
+
sessions: sessions.length,
|
|
10493
|
+
tasks: tasks.length
|
|
10494
|
+
},
|
|
10495
|
+
pass: issues.length === 0
|
|
10496
|
+
};
|
|
10497
|
+
};
|
|
10498
|
+
var runVoiceOutcomeContractSuite = async (options) => {
|
|
10499
|
+
const [sessions, reviews, tasks, events, handoffs] = await Promise.all([
|
|
10500
|
+
hydrateSessions(options.sessions),
|
|
10501
|
+
toList(options.reviews),
|
|
10502
|
+
toList(options.tasks),
|
|
10503
|
+
toList(options.events),
|
|
10504
|
+
toList(options.handoffs)
|
|
10505
|
+
]);
|
|
10506
|
+
const contracts = options.contracts.map((contract) => reportContract({ contract, events, handoffs, reviews, sessions, tasks }));
|
|
10507
|
+
const passed = contracts.filter((contract) => contract.pass).length;
|
|
10508
|
+
const failed = contracts.length - passed;
|
|
10509
|
+
return {
|
|
10510
|
+
checkedAt: Date.now(),
|
|
10511
|
+
contracts,
|
|
10512
|
+
failed,
|
|
10513
|
+
passed,
|
|
10514
|
+
status: failed > 0 ? "fail" : "pass",
|
|
10515
|
+
total: contracts.length
|
|
10516
|
+
};
|
|
10517
|
+
};
|
|
10518
|
+
var renderVoiceOutcomeContractHTML = (report, options = {}) => {
|
|
10519
|
+
const title = options.title ?? "Voice Outcome Contracts";
|
|
10520
|
+
const contracts = report.contracts.map((contract) => `<section class="contract ${contract.pass ? "pass" : "fail"}">
|
|
10521
|
+
<div class="contract-header">
|
|
10522
|
+
<div>
|
|
10523
|
+
<p class="eyebrow">${escapeHtml16(contract.contractId)}</p>
|
|
10524
|
+
<h2>${escapeHtml16(contract.label ?? contract.contractId)}</h2>
|
|
10525
|
+
${contract.description ? `<p>${escapeHtml16(contract.description)}</p>` : ""}
|
|
10526
|
+
</div>
|
|
10527
|
+
<strong>${contract.pass ? "pass" : "fail"}</strong>
|
|
10528
|
+
</div>
|
|
10529
|
+
<div class="grid">
|
|
10530
|
+
<span>sessions ${String(contract.matched.sessions)}</span>
|
|
10531
|
+
<span>reviews ${String(contract.matched.reviews)}</span>
|
|
10532
|
+
<span>tasks ${String(contract.matched.tasks)}</span>
|
|
10533
|
+
<span>handoffs ${String(contract.matched.handoffs)}</span>
|
|
10534
|
+
<span>events ${String(contract.matched.integrationEvents)}</span>
|
|
10535
|
+
</div>
|
|
10536
|
+
${contract.issues.length ? `<ul>${contract.issues.map((issue) => `<li>${escapeHtml16(issue.message)}</li>`).join("")}</ul>` : ""}
|
|
10537
|
+
</section>`).join("");
|
|
10538
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml16(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.contract{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.14),rgba(14,165,233,.12))}.eyebrow{color:#7dd3fc;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0}.summary,.grid{display:flex;flex-wrap:wrap;gap:10px}.pill,.grid span{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.contract-header{display:flex;gap:16px;justify-content:space-between}.pass{color:#86efac}.fail{color:#fca5a5}.contract.fail{border-color:rgba(248,113,113,.45)}li{margin:8px 0}@media(max-width:800px){main{padding:18px}.contract-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Business Outcome Verification</p><h1>${escapeHtml16(title)}</h1><div class="summary"><span class="pill ${report.status}">${report.status}</span><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} contracts</span></div></section>${contracts || '<section class="contract"><p>No outcome contracts configured.</p></section>'}</main></body></html>`;
|
|
10539
|
+
};
|
|
10540
|
+
var createVoiceOutcomeContractJSONHandler = (options) => async () => runVoiceOutcomeContractSuite(options);
|
|
10541
|
+
var createVoiceOutcomeContractHTMLHandler = (options) => async () => {
|
|
10542
|
+
const report = await runVoiceOutcomeContractSuite(options);
|
|
10543
|
+
const render = options.render ?? ((input) => renderVoiceOutcomeContractHTML(input, options));
|
|
10544
|
+
return new Response(await render(report), {
|
|
10545
|
+
headers: {
|
|
10546
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
10547
|
+
...options.headers
|
|
10548
|
+
}
|
|
10549
|
+
});
|
|
10550
|
+
};
|
|
10551
|
+
var createVoiceOutcomeContractRoutes = (options) => {
|
|
10552
|
+
const path = options.path ?? "/api/outcome-contracts";
|
|
10553
|
+
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
10554
|
+
const routes = new Elysia15({
|
|
10555
|
+
name: options.name ?? "absolutejs-voice-outcome-contracts"
|
|
10556
|
+
}).get(path, createVoiceOutcomeContractJSONHandler(options));
|
|
10557
|
+
if (htmlPath) {
|
|
10558
|
+
routes.get(htmlPath, createVoiceOutcomeContractHTMLHandler(options));
|
|
10559
|
+
}
|
|
10560
|
+
return routes;
|
|
10561
|
+
};
|
|
10562
|
+
// src/telephonyOutcome.ts
|
|
10563
|
+
var DEFAULT_COMPLETED_STATUSES = [
|
|
10564
|
+
"answered",
|
|
10565
|
+
"completed",
|
|
10566
|
+
"complete",
|
|
10567
|
+
"connected",
|
|
10568
|
+
"in-progress",
|
|
10569
|
+
"live"
|
|
10570
|
+
];
|
|
10571
|
+
var DEFAULT_NO_ANSWER_STATUSES = [
|
|
10572
|
+
"busy",
|
|
10573
|
+
"canceled",
|
|
10574
|
+
"cancelled",
|
|
10575
|
+
"failed",
|
|
10576
|
+
"no-answer",
|
|
10577
|
+
"no_answer",
|
|
10578
|
+
"not-answered",
|
|
10579
|
+
"ring-no-answer",
|
|
10580
|
+
"timeout",
|
|
10581
|
+
"unanswered"
|
|
10582
|
+
];
|
|
10583
|
+
var DEFAULT_VOICEMAIL_STATUSES = [
|
|
10584
|
+
"answering-machine",
|
|
10585
|
+
"machine",
|
|
10586
|
+
"voicemail",
|
|
10587
|
+
"voice-mail"
|
|
10588
|
+
];
|
|
10589
|
+
var DEFAULT_TRANSFER_STATUSES = ["bridged", "forwarded", "transferred"];
|
|
10590
|
+
var DEFAULT_ESCALATION_STATUSES = ["escalated", "human-required", "operator"];
|
|
10591
|
+
var DEFAULT_FAILED_STATUSES = ["busy", "failed", "no-answer"];
|
|
10592
|
+
var DEFAULT_MACHINE_VOICEMAIL_VALUES = [
|
|
10593
|
+
"answering-machine",
|
|
10594
|
+
"fax",
|
|
10595
|
+
"machine",
|
|
10596
|
+
"machine-end-beep",
|
|
10597
|
+
"machine-end-other",
|
|
10598
|
+
"machine-start",
|
|
10599
|
+
"voicemail"
|
|
10600
|
+
];
|
|
10601
|
+
var DEFAULT_NO_ANSWER_SIP_CODES = [408, 480, 486, 487, 603];
|
|
10602
|
+
var normalizeToken = (value) => typeof value === "string" ? value.trim().toLowerCase().replace(/\s+/g, "-").replace(/_+/g, "-") : undefined;
|
|
10603
|
+
var normalizeList = (values, fallback) => new Set((values ?? fallback).map(normalizeToken).filter(Boolean));
|
|
10604
|
+
var metadataValue = (metadata, keys) => {
|
|
10605
|
+
for (const key of keys) {
|
|
10606
|
+
const value = metadata?.[key];
|
|
10607
|
+
if (typeof value === "string" && value.trim()) {
|
|
10608
|
+
return value.trim();
|
|
10609
|
+
}
|
|
10610
|
+
}
|
|
10611
|
+
};
|
|
10612
|
+
var resolveTransferTarget = (event, policy) => {
|
|
10613
|
+
if (typeof event.target === "string" && event.target.trim()) {
|
|
10614
|
+
return event.target.trim();
|
|
10615
|
+
}
|
|
10616
|
+
const metadataTarget = metadataValue(event.metadata, [
|
|
10617
|
+
"transferTarget",
|
|
10618
|
+
"target",
|
|
10619
|
+
"queue",
|
|
10620
|
+
"department"
|
|
10621
|
+
]);
|
|
10622
|
+
if (metadataTarget) {
|
|
10623
|
+
return metadataTarget;
|
|
10624
|
+
}
|
|
10625
|
+
if (typeof policy.transferTarget === "function") {
|
|
10626
|
+
const target = policy.transferTarget(event);
|
|
10627
|
+
return typeof target === "string" && target.trim() ? target.trim() : undefined;
|
|
10628
|
+
}
|
|
10629
|
+
return typeof policy.transferTarget === "string" && policy.transferTarget.trim() ? policy.transferTarget.trim() : undefined;
|
|
10630
|
+
};
|
|
10631
|
+
var mergeMetadata = (event, policy) => ({
|
|
10632
|
+
...policy.includeProviderPayload ? {
|
|
10633
|
+
answeredBy: event.answeredBy,
|
|
10634
|
+
durationMs: event.durationMs,
|
|
10635
|
+
provider: event.provider,
|
|
10636
|
+
reason: event.reason,
|
|
10637
|
+
sipCode: event.sipCode,
|
|
10638
|
+
status: event.status
|
|
10639
|
+
} : undefined,
|
|
10640
|
+
...policy.metadata,
|
|
10641
|
+
...event.metadata
|
|
10642
|
+
});
|
|
10643
|
+
var withDecisionDefaults = (decision, input) => {
|
|
10644
|
+
if (typeof decision === "string") {
|
|
10645
|
+
return buildDecision(decision, input);
|
|
10646
|
+
}
|
|
10647
|
+
return {
|
|
10648
|
+
...buildDecision(decision.action, input),
|
|
10649
|
+
...decision,
|
|
10650
|
+
confidence: decision.confidence ?? "high",
|
|
10651
|
+
metadata: {
|
|
10652
|
+
...mergeMetadata(input.event, input.policy),
|
|
10653
|
+
...decision.metadata
|
|
10654
|
+
},
|
|
10655
|
+
source: decision.source ?? input.source,
|
|
10656
|
+
target: decision.target ?? (decision.action === "transfer" ? resolveTransferTarget(input.event, input.policy) : undefined)
|
|
10657
|
+
};
|
|
10658
|
+
};
|
|
10659
|
+
var dispositionForAction = (action) => {
|
|
10660
|
+
switch (action) {
|
|
10661
|
+
case "complete":
|
|
10662
|
+
return "completed";
|
|
10663
|
+
case "escalate":
|
|
10664
|
+
return "escalated";
|
|
10665
|
+
case "no-answer":
|
|
10666
|
+
return "no-answer";
|
|
10667
|
+
case "transfer":
|
|
10668
|
+
return "transferred";
|
|
10669
|
+
case "voicemail":
|
|
10670
|
+
return "voicemail";
|
|
10671
|
+
default:
|
|
10672
|
+
return;
|
|
10673
|
+
}
|
|
10674
|
+
};
|
|
10675
|
+
var buildDecision = (action, input) => ({
|
|
10676
|
+
action,
|
|
10677
|
+
confidence: action === "ignore" ? "low" : "high",
|
|
10678
|
+
disposition: dispositionForAction(action),
|
|
10679
|
+
metadata: mergeMetadata(input.event, input.policy),
|
|
10680
|
+
reason: input.event.reason,
|
|
10681
|
+
source: input.source,
|
|
10682
|
+
target: action === "transfer" ? resolveTransferTarget(input.event, input.policy) : undefined
|
|
10683
|
+
});
|
|
10684
|
+
var createVoiceTelephonyOutcomePolicy = (policy = {}) => ({
|
|
10685
|
+
completedStatuses: policy.completedStatuses ?? DEFAULT_COMPLETED_STATUSES,
|
|
10686
|
+
escalationStatuses: policy.escalationStatuses ?? DEFAULT_ESCALATION_STATUSES,
|
|
10687
|
+
failedAsNoAnswer: policy.failedAsNoAnswer ?? true,
|
|
10688
|
+
failedStatuses: policy.failedStatuses ?? DEFAULT_FAILED_STATUSES,
|
|
10689
|
+
includeProviderPayload: policy.includeProviderPayload ?? true,
|
|
10690
|
+
machineDetectionVoicemailValues: policy.machineDetectionVoicemailValues ?? DEFAULT_MACHINE_VOICEMAIL_VALUES,
|
|
10691
|
+
metadata: policy.metadata,
|
|
10692
|
+
minAnsweredDurationMs: policy.minAnsweredDurationMs,
|
|
10693
|
+
noAnswerOnZeroDuration: policy.noAnswerOnZeroDuration ?? true,
|
|
10694
|
+
noAnswerSipCodes: policy.noAnswerSipCodes ?? DEFAULT_NO_ANSWER_SIP_CODES,
|
|
10695
|
+
noAnswerStatuses: policy.noAnswerStatuses ?? DEFAULT_NO_ANSWER_STATUSES,
|
|
10696
|
+
statusMap: policy.statusMap,
|
|
10697
|
+
transferStatuses: policy.transferStatuses ?? DEFAULT_TRANSFER_STATUSES,
|
|
10698
|
+
transferTarget: policy.transferTarget,
|
|
10699
|
+
voicemailStatuses: policy.voicemailStatuses ?? DEFAULT_VOICEMAIL_STATUSES
|
|
10700
|
+
});
|
|
10701
|
+
var resolveVoiceTelephonyOutcome = (event, policyInput = {}) => {
|
|
10702
|
+
const policy = createVoiceTelephonyOutcomePolicy(policyInput);
|
|
10703
|
+
const status = normalizeToken(event.status);
|
|
10704
|
+
const provider = normalizeToken(event.provider);
|
|
10705
|
+
const answeredBy = normalizeToken(event.answeredBy);
|
|
10706
|
+
const target = resolveTransferTarget(event, policy);
|
|
10707
|
+
if (status) {
|
|
10708
|
+
const mapped = policy.statusMap?.[status] ?? (provider ? policy.statusMap?.[`${provider}:${status}`] : undefined);
|
|
10709
|
+
if (mapped) {
|
|
10710
|
+
return withDecisionDefaults(mapped, {
|
|
10711
|
+
event,
|
|
10712
|
+
policy,
|
|
10713
|
+
source: "policy"
|
|
10714
|
+
});
|
|
10715
|
+
}
|
|
10716
|
+
}
|
|
10717
|
+
if (answeredBy && normalizeList(policy.machineDetectionVoicemailValues, []).has(answeredBy)) {
|
|
10718
|
+
return buildDecision("voicemail", { event, policy, source: "answered-by" });
|
|
10719
|
+
}
|
|
10720
|
+
if (typeof event.sipCode === "number" && policy.noAnswerSipCodes.includes(event.sipCode)) {
|
|
10721
|
+
return buildDecision("no-answer", { event, policy, source: "sip" });
|
|
10722
|
+
}
|
|
10723
|
+
if (target && status && normalizeList(policy.transferStatuses, []).has(status)) {
|
|
10724
|
+
return buildDecision("transfer", { event, policy, source: "status" });
|
|
10725
|
+
}
|
|
10726
|
+
if (status && normalizeList(policy.voicemailStatuses, []).has(status)) {
|
|
10727
|
+
return buildDecision("voicemail", { event, policy, source: "status" });
|
|
10728
|
+
}
|
|
10729
|
+
if (status && normalizeList(policy.escalationStatuses, []).has(status)) {
|
|
10730
|
+
return buildDecision("escalate", { event, policy, source: "status" });
|
|
10731
|
+
}
|
|
10732
|
+
if (status && (policy.failedAsNoAnswer ? normalizeList(policy.noAnswerStatuses, []).has(status) || normalizeList(policy.failedStatuses, []).has(status) : normalizeList(policy.noAnswerStatuses, []).has(status))) {
|
|
10733
|
+
return buildDecision("no-answer", { event, policy, source: "status" });
|
|
10734
|
+
}
|
|
10735
|
+
if (policy.noAnswerOnZeroDuration && typeof event.durationMs === "number" && event.durationMs <= 0) {
|
|
10736
|
+
return buildDecision("no-answer", { event, policy, source: "duration" });
|
|
10737
|
+
}
|
|
10738
|
+
if (typeof policy.minAnsweredDurationMs === "number" && typeof event.durationMs === "number" && event.durationMs < policy.minAnsweredDurationMs) {
|
|
10739
|
+
return {
|
|
10740
|
+
...buildDecision("no-answer", { event, policy, source: "duration" }),
|
|
10741
|
+
confidence: "medium"
|
|
10742
|
+
};
|
|
10743
|
+
}
|
|
10744
|
+
if (status && normalizeList(policy.completedStatuses, []).has(status)) {
|
|
10745
|
+
return buildDecision("complete", { event, policy, source: "status" });
|
|
10746
|
+
}
|
|
10747
|
+
if (target) {
|
|
10748
|
+
return {
|
|
10749
|
+
...buildDecision("transfer", { event, policy, source: "explicit-target" }),
|
|
10750
|
+
confidence: "medium"
|
|
10751
|
+
};
|
|
10752
|
+
}
|
|
10753
|
+
return buildDecision("ignore", { event, policy, source: "status" });
|
|
10754
|
+
};
|
|
10755
|
+
var voiceTelephonyOutcomeToRouteResult = (decision, result) => {
|
|
10756
|
+
switch (decision.action) {
|
|
10757
|
+
case "complete":
|
|
10758
|
+
return { complete: true, result };
|
|
10759
|
+
case "escalate":
|
|
10760
|
+
return {
|
|
10761
|
+
escalate: {
|
|
10762
|
+
metadata: decision.metadata,
|
|
10763
|
+
reason: decision.reason ?? "telephony-escalation"
|
|
10764
|
+
},
|
|
10765
|
+
result
|
|
10766
|
+
};
|
|
10767
|
+
case "no-answer":
|
|
10768
|
+
return {
|
|
10769
|
+
noAnswer: {
|
|
10770
|
+
metadata: decision.metadata
|
|
10771
|
+
},
|
|
10772
|
+
result
|
|
10773
|
+
};
|
|
10774
|
+
case "transfer":
|
|
10775
|
+
if (!decision.target) {
|
|
10776
|
+
return { result };
|
|
10777
|
+
}
|
|
10778
|
+
return {
|
|
10779
|
+
result,
|
|
10780
|
+
transfer: {
|
|
10781
|
+
metadata: decision.metadata,
|
|
10782
|
+
reason: decision.reason,
|
|
10783
|
+
target: decision.target
|
|
10784
|
+
}
|
|
10785
|
+
};
|
|
10786
|
+
case "voicemail":
|
|
10787
|
+
return {
|
|
10788
|
+
result,
|
|
10789
|
+
voicemail: {
|
|
10790
|
+
metadata: decision.metadata
|
|
10791
|
+
}
|
|
10792
|
+
};
|
|
10793
|
+
default:
|
|
10794
|
+
return { result };
|
|
10795
|
+
}
|
|
10796
|
+
};
|
|
10797
|
+
var applyVoiceTelephonyOutcome = async (api, decision, result) => {
|
|
10798
|
+
switch (decision.action) {
|
|
10799
|
+
case "complete":
|
|
10800
|
+
await api.complete(result);
|
|
10801
|
+
break;
|
|
10802
|
+
case "escalate":
|
|
10803
|
+
await api.escalate({
|
|
10804
|
+
metadata: decision.metadata,
|
|
10805
|
+
reason: decision.reason ?? "telephony-escalation",
|
|
10806
|
+
result
|
|
10807
|
+
});
|
|
10808
|
+
break;
|
|
10809
|
+
case "no-answer":
|
|
10810
|
+
await api.markNoAnswer({
|
|
10811
|
+
metadata: decision.metadata,
|
|
10812
|
+
result
|
|
10813
|
+
});
|
|
10814
|
+
break;
|
|
10815
|
+
case "transfer":
|
|
10816
|
+
if (!decision.target) {
|
|
10817
|
+
return;
|
|
10818
|
+
}
|
|
10819
|
+
await api.transfer({
|
|
10820
|
+
metadata: decision.metadata,
|
|
10821
|
+
reason: decision.reason,
|
|
10822
|
+
result,
|
|
10823
|
+
target: decision.target
|
|
10824
|
+
});
|
|
10825
|
+
break;
|
|
10826
|
+
case "voicemail":
|
|
10827
|
+
await api.markVoicemail({
|
|
10828
|
+
metadata: decision.metadata,
|
|
10829
|
+
result
|
|
10830
|
+
});
|
|
10831
|
+
break;
|
|
10832
|
+
default:
|
|
10833
|
+
break;
|
|
10834
|
+
}
|
|
10835
|
+
};
|
|
10413
10836
|
// src/fileStore.ts
|
|
10414
10837
|
import { mkdir as mkdir2, readFile, readdir, rename, rm, writeFile } from "fs/promises";
|
|
10415
10838
|
import { join } from "path";
|
|
@@ -12503,7 +12926,7 @@ var createVoiceMemoryStore = () => {
|
|
|
12503
12926
|
return { get, getOrCreate, list, remove, set };
|
|
12504
12927
|
};
|
|
12505
12928
|
// src/opsWebhook.ts
|
|
12506
|
-
import { Elysia as
|
|
12929
|
+
import { Elysia as Elysia16 } from "elysia";
|
|
12507
12930
|
var toHex5 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
12508
12931
|
var signVoiceOpsWebhookBody = async (input) => {
|
|
12509
12932
|
const encoder = new TextEncoder;
|
|
@@ -12633,7 +13056,7 @@ var verifyVoiceOpsWebhookSignature = async (input) => {
|
|
|
12633
13056
|
};
|
|
12634
13057
|
var createVoiceOpsWebhookReceiverRoutes = (options = {}) => {
|
|
12635
13058
|
const path = options.path ?? "/api/voice-ops/webhook";
|
|
12636
|
-
return new
|
|
13059
|
+
return new Elysia16().post(path, async ({ body, request, set }) => {
|
|
12637
13060
|
const bodyText = typeof body === "string" ? body : JSON.stringify(body);
|
|
12638
13061
|
if (options.signingSecret) {
|
|
12639
13062
|
const verification = await verifyVoiceOpsWebhookSignature({
|
|
@@ -14775,6 +15198,7 @@ var shapeTelephonyAssistantText = (text, options = {}) => {
|
|
|
14775
15198
|
export {
|
|
14776
15199
|
withVoiceOpsTaskId,
|
|
14777
15200
|
withVoiceIntegrationEventId,
|
|
15201
|
+
voiceTelephonyOutcomeToRouteResult,
|
|
14778
15202
|
voice,
|
|
14779
15203
|
verifyVoiceOpsWebhookSignature,
|
|
14780
15204
|
validateVoiceWorkflowRouteResult,
|
|
@@ -14805,7 +15229,9 @@ export {
|
|
|
14805
15229
|
runVoiceSessionEvals,
|
|
14806
15230
|
runVoiceScenarioFixtureEvals,
|
|
14807
15231
|
runVoiceScenarioEvals,
|
|
15232
|
+
runVoiceOutcomeContractSuite,
|
|
14808
15233
|
resolveVoiceTraceRedactionOptions,
|
|
15234
|
+
resolveVoiceTelephonyOutcome,
|
|
14809
15235
|
resolveVoiceSTTRoutingStrategy,
|
|
14810
15236
|
resolveVoiceRuntimePreset,
|
|
14811
15237
|
resolveVoiceProviderRoutingPolicyPreset,
|
|
@@ -14831,6 +15257,7 @@ export {
|
|
|
14831
15257
|
renderVoiceQualityHTML,
|
|
14832
15258
|
renderVoiceProviderHealthHTML,
|
|
14833
15259
|
renderVoiceProviderCapabilityHTML,
|
|
15260
|
+
renderVoiceOutcomeContractHTML,
|
|
14834
15261
|
renderVoiceOpsConsoleHTML,
|
|
14835
15262
|
renderVoiceHandoffHealthHTML,
|
|
14836
15263
|
renderVoiceEvalHTML,
|
|
@@ -14893,6 +15320,7 @@ export {
|
|
|
14893
15320
|
createVoiceToolContractJSONHandler,
|
|
14894
15321
|
createVoiceToolContractHTMLHandler,
|
|
14895
15322
|
createVoiceToolContract,
|
|
15323
|
+
createVoiceTelephonyOutcomePolicy,
|
|
14896
15324
|
createVoiceTaskUpdatedEvent,
|
|
14897
15325
|
createVoiceTaskSLABreachedEvent,
|
|
14898
15326
|
createVoiceTaskCreatedEvent,
|
|
@@ -14937,6 +15365,9 @@ export {
|
|
|
14937
15365
|
createVoicePostgresReviewStore,
|
|
14938
15366
|
createVoicePostgresIntegrationEventStore,
|
|
14939
15367
|
createVoicePostgresExternalObjectMapStore,
|
|
15368
|
+
createVoiceOutcomeContractRoutes,
|
|
15369
|
+
createVoiceOutcomeContractJSONHandler,
|
|
15370
|
+
createVoiceOutcomeContractHTMLHandler,
|
|
14940
15371
|
createVoiceOpsWebhookSink,
|
|
14941
15372
|
createVoiceOpsWebhookReceiverRoutes,
|
|
14942
15373
|
createVoiceOpsWebhookEnvelope,
|
|
@@ -15024,6 +15455,7 @@ export {
|
|
|
15024
15455
|
buildVoiceOpsConsoleReport,
|
|
15025
15456
|
buildVoiceDiagnosticsMarkdown,
|
|
15026
15457
|
assignVoiceOpsTask,
|
|
15458
|
+
applyVoiceTelephonyOutcome,
|
|
15027
15459
|
applyVoiceOpsTaskPolicy,
|
|
15028
15460
|
applyVoiceOpsTaskAssignmentRule,
|
|
15029
15461
|
applyVoiceHandoffDeliveryResult,
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { Elysia } from 'elysia';
|
|
2
|
+
import type { StoredVoiceHandoffDelivery, VoiceCallDisposition, VoiceHandoffAction, VoiceHandoffDeliveryStore, VoiceSessionRecord, VoiceSessionStore } from './types';
|
|
3
|
+
import type { StoredVoiceIntegrationEvent, StoredVoiceOpsTask, VoiceIntegrationEventType } from './ops';
|
|
4
|
+
import type { StoredVoiceCallReviewArtifact } from './testing/review';
|
|
5
|
+
export type VoiceOutcomeContractStatus = 'pass' | 'fail';
|
|
6
|
+
export type VoiceOutcomeContractDefinition = {
|
|
7
|
+
description?: string;
|
|
8
|
+
expectedDisposition?: VoiceCallDisposition;
|
|
9
|
+
id: string;
|
|
10
|
+
label?: string;
|
|
11
|
+
minSessions?: number;
|
|
12
|
+
minTasks?: number;
|
|
13
|
+
requireHandoffActions?: VoiceHandoffAction[];
|
|
14
|
+
requireIntegrationEvents?: VoiceIntegrationEventType[];
|
|
15
|
+
requireReview?: boolean;
|
|
16
|
+
requireTask?: boolean;
|
|
17
|
+
scenarioId?: string;
|
|
18
|
+
};
|
|
19
|
+
export type VoiceOutcomeContractIssue = {
|
|
20
|
+
code: string;
|
|
21
|
+
message: string;
|
|
22
|
+
};
|
|
23
|
+
export type VoiceOutcomeContractReport = {
|
|
24
|
+
contractId: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
issues: VoiceOutcomeContractIssue[];
|
|
27
|
+
label?: string;
|
|
28
|
+
matched: {
|
|
29
|
+
handoffs: number;
|
|
30
|
+
integrationEvents: number;
|
|
31
|
+
reviews: number;
|
|
32
|
+
sessions: number;
|
|
33
|
+
tasks: number;
|
|
34
|
+
};
|
|
35
|
+
pass: boolean;
|
|
36
|
+
};
|
|
37
|
+
export type VoiceOutcomeContractSuiteReport = {
|
|
38
|
+
checkedAt: number;
|
|
39
|
+
contracts: VoiceOutcomeContractReport[];
|
|
40
|
+
failed: number;
|
|
41
|
+
passed: number;
|
|
42
|
+
status: VoiceOutcomeContractStatus;
|
|
43
|
+
total: number;
|
|
44
|
+
};
|
|
45
|
+
type ListStore<T> = {
|
|
46
|
+
list: () => Promise<T[]> | T[];
|
|
47
|
+
};
|
|
48
|
+
export type VoiceOutcomeContractOptions<TSession extends VoiceSessionRecord = VoiceSessionRecord> = {
|
|
49
|
+
contracts: VoiceOutcomeContractDefinition[];
|
|
50
|
+
events?: StoredVoiceIntegrationEvent[] | ListStore<StoredVoiceIntegrationEvent>;
|
|
51
|
+
handoffs?: StoredVoiceHandoffDelivery[] | VoiceHandoffDeliveryStore;
|
|
52
|
+
reviews?: StoredVoiceCallReviewArtifact[] | ListStore<StoredVoiceCallReviewArtifact>;
|
|
53
|
+
sessions?: TSession[] | VoiceSessionStore<TSession>;
|
|
54
|
+
tasks?: StoredVoiceOpsTask[] | ListStore<StoredVoiceOpsTask>;
|
|
55
|
+
};
|
|
56
|
+
export type VoiceOutcomeContractHTMLHandlerOptions<TSession extends VoiceSessionRecord = VoiceSessionRecord> = VoiceOutcomeContractOptions<TSession> & {
|
|
57
|
+
headers?: HeadersInit;
|
|
58
|
+
render?: (report: VoiceOutcomeContractSuiteReport) => string | Promise<string>;
|
|
59
|
+
title?: string;
|
|
60
|
+
};
|
|
61
|
+
export type VoiceOutcomeContractRoutesOptions<TSession extends VoiceSessionRecord = VoiceSessionRecord> = VoiceOutcomeContractHTMLHandlerOptions<TSession> & {
|
|
62
|
+
htmlPath?: false | string;
|
|
63
|
+
name?: string;
|
|
64
|
+
path?: string;
|
|
65
|
+
};
|
|
66
|
+
export declare const runVoiceOutcomeContractSuite: <TSession extends VoiceSessionRecord = VoiceSessionRecord>(options: VoiceOutcomeContractOptions<TSession>) => Promise<VoiceOutcomeContractSuiteReport>;
|
|
67
|
+
export declare const renderVoiceOutcomeContractHTML: (report: VoiceOutcomeContractSuiteReport, options?: {
|
|
68
|
+
title?: string;
|
|
69
|
+
}) => string;
|
|
70
|
+
export declare const createVoiceOutcomeContractJSONHandler: <TSession extends VoiceSessionRecord = VoiceSessionRecord>(options: VoiceOutcomeContractOptions<TSession>) => () => Promise<VoiceOutcomeContractSuiteReport>;
|
|
71
|
+
export declare const createVoiceOutcomeContractHTMLHandler: <TSession extends VoiceSessionRecord = VoiceSessionRecord>(options: VoiceOutcomeContractHTMLHandlerOptions<TSession>) => () => Promise<Response>;
|
|
72
|
+
export declare const createVoiceOutcomeContractRoutes: <TSession extends VoiceSessionRecord = VoiceSessionRecord>(options: VoiceOutcomeContractRoutesOptions<TSession>) => Elysia<"", {
|
|
73
|
+
decorator: {};
|
|
74
|
+
store: {};
|
|
75
|
+
derive: {};
|
|
76
|
+
resolve: {};
|
|
77
|
+
}, {
|
|
78
|
+
typebox: {};
|
|
79
|
+
error: {};
|
|
80
|
+
}, {
|
|
81
|
+
schema: {};
|
|
82
|
+
standaloneSchema: {};
|
|
83
|
+
macro: {};
|
|
84
|
+
macroFn: {};
|
|
85
|
+
parser: {};
|
|
86
|
+
response: {};
|
|
87
|
+
}, {
|
|
88
|
+
[x: string]: {
|
|
89
|
+
get: {
|
|
90
|
+
body: unknown;
|
|
91
|
+
params: {};
|
|
92
|
+
query: unknown;
|
|
93
|
+
headers: unknown;
|
|
94
|
+
response: {
|
|
95
|
+
200: VoiceOutcomeContractSuiteReport;
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
};
|
|
99
|
+
}, {
|
|
100
|
+
derive: {};
|
|
101
|
+
resolve: {};
|
|
102
|
+
schema: {};
|
|
103
|
+
standaloneSchema: {};
|
|
104
|
+
response: {};
|
|
105
|
+
}, {
|
|
106
|
+
derive: {};
|
|
107
|
+
resolve: {};
|
|
108
|
+
schema: {};
|
|
109
|
+
standaloneSchema: {};
|
|
110
|
+
response: {};
|
|
111
|
+
}>;
|
|
112
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { VoiceCallDisposition, VoiceRouteResult, VoiceSessionHandle, VoiceSessionRecord } from './types';
|
|
2
|
+
export type VoiceTelephonyOutcomeAction = 'complete' | 'escalate' | 'ignore' | 'no-answer' | 'transfer' | 'voicemail';
|
|
3
|
+
export type VoiceTelephonyOutcomeProviderEvent = {
|
|
4
|
+
answeredBy?: string;
|
|
5
|
+
durationMs?: number;
|
|
6
|
+
from?: string;
|
|
7
|
+
metadata?: Record<string, unknown>;
|
|
8
|
+
provider?: string;
|
|
9
|
+
reason?: string;
|
|
10
|
+
sipCode?: number;
|
|
11
|
+
status?: string;
|
|
12
|
+
target?: string;
|
|
13
|
+
to?: string;
|
|
14
|
+
};
|
|
15
|
+
export type VoiceTelephonyOutcomeDecision = {
|
|
16
|
+
action: VoiceTelephonyOutcomeAction;
|
|
17
|
+
confidence: 'high' | 'low' | 'medium';
|
|
18
|
+
disposition?: VoiceCallDisposition;
|
|
19
|
+
metadata?: Record<string, unknown>;
|
|
20
|
+
reason?: string;
|
|
21
|
+
source: 'answered-by' | 'duration' | 'explicit-target' | 'policy' | 'sip' | 'status';
|
|
22
|
+
target?: string;
|
|
23
|
+
};
|
|
24
|
+
export type VoiceTelephonyOutcomeStatusDecision = VoiceTelephonyOutcomeAction | Omit<VoiceTelephonyOutcomeDecision, 'confidence' | 'source'> & {
|
|
25
|
+
confidence?: VoiceTelephonyOutcomeDecision['confidence'];
|
|
26
|
+
source?: VoiceTelephonyOutcomeDecision['source'];
|
|
27
|
+
};
|
|
28
|
+
export type VoiceTelephonyOutcomePolicy = {
|
|
29
|
+
completedStatuses?: string[];
|
|
30
|
+
escalationStatuses?: string[];
|
|
31
|
+
failedAsNoAnswer?: boolean;
|
|
32
|
+
failedStatuses?: string[];
|
|
33
|
+
includeProviderPayload?: boolean;
|
|
34
|
+
machineDetectionVoicemailValues?: string[];
|
|
35
|
+
metadata?: Record<string, unknown>;
|
|
36
|
+
minAnsweredDurationMs?: number;
|
|
37
|
+
noAnswerOnZeroDuration?: boolean;
|
|
38
|
+
noAnswerSipCodes?: number[];
|
|
39
|
+
noAnswerStatuses?: string[];
|
|
40
|
+
statusMap?: Record<string, VoiceTelephonyOutcomeStatusDecision>;
|
|
41
|
+
transferStatuses?: string[];
|
|
42
|
+
transferTarget?: string | ((event: VoiceTelephonyOutcomeProviderEvent) => string | undefined);
|
|
43
|
+
voicemailStatuses?: string[];
|
|
44
|
+
};
|
|
45
|
+
export type VoiceTelephonyOutcomeRouteResult<TResult = unknown> = VoiceRouteResult<TResult>;
|
|
46
|
+
export declare const createVoiceTelephonyOutcomePolicy: (policy?: VoiceTelephonyOutcomePolicy) => Required<Pick<VoiceTelephonyOutcomePolicy, "completedStatuses" | "escalationStatuses" | "failedAsNoAnswer" | "failedStatuses" | "includeProviderPayload" | "machineDetectionVoicemailValues" | "noAnswerOnZeroDuration" | "noAnswerSipCodes" | "noAnswerStatuses" | "transferStatuses" | "voicemailStatuses">> & VoiceTelephonyOutcomePolicy;
|
|
47
|
+
export declare const resolveVoiceTelephonyOutcome: (event: VoiceTelephonyOutcomeProviderEvent, policyInput?: VoiceTelephonyOutcomePolicy) => VoiceTelephonyOutcomeDecision;
|
|
48
|
+
export declare const voiceTelephonyOutcomeToRouteResult: <TResult = unknown>(decision: VoiceTelephonyOutcomeDecision, result?: TResult) => VoiceTelephonyOutcomeRouteResult<TResult>;
|
|
49
|
+
export declare const applyVoiceTelephonyOutcome: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(api: VoiceSessionHandle<TContext, TSession, TResult>, decision: VoiceTelephonyOutcomeDecision, result?: TResult) => Promise<void>;
|