@absolutejs/voice 0.0.22-beta.68 → 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 CHANGED
@@ -11,6 +11,7 @@ export { createVoiceToolIdempotencyKey, createVoiceToolRuntime } from './toolRun
11
11
  export { createVoiceToolContract, createVoiceToolContractHTMLHandler, createVoiceToolContractJSONHandler, createVoiceToolContractRoutes, createVoiceToolRuntimeContractDefaults, renderVoiceToolContractHTML, runVoiceToolContractSuite, runVoiceToolContract } from './toolContract';
12
12
  export { createVoiceTurnQualityHTMLHandler, createVoiceTurnQualityJSONHandler, createVoiceTurnQualityRoutes, renderVoiceTurnQualityHTML, summarizeVoiceTurnQuality } from './turnQuality';
13
13
  export { createVoiceOutcomeContractHTMLHandler, createVoiceOutcomeContractJSONHandler, createVoiceOutcomeContractRoutes, renderVoiceOutcomeContractHTML, runVoiceOutcomeContractSuite } from './outcomeContract';
14
+ export { applyVoiceTelephonyOutcome, createVoiceTelephonyOutcomePolicy, resolveVoiceTelephonyOutcome, voiceTelephonyOutcomeToRouteResult } from './telephonyOutcome';
14
15
  export { createStoredVoiceCallReviewArtifact, createStoredVoiceExternalObjectMap, createStoredVoiceIntegrationEvent, createStoredVoiceOpsTask, createVoiceFileExternalObjectMapStore, createVoiceFileAssistantMemoryStore, createVoiceFileIntegrationEventStore, createVoiceFileReviewStore, createVoiceFileRuntimeStorage, createVoiceFileSessionStore, createVoiceFileTaskStore, createVoiceFileTraceSinkDeliveryStore, createVoiceFileTraceEventStore } from './fileStore';
15
16
  export { createVoiceAssistantMemoryHandle, createVoiceAssistantMemoryRecord, createVoiceMemoryAssistantMemoryStore, resolveVoiceAssistantMemoryNamespace } from './assistantMemory';
16
17
  export { createAnthropicVoiceAssistantModel, createGeminiVoiceAssistantModel, createJSONVoiceAssistantModel, createOpenAIVoiceAssistantModel, resolveVoiceProviderRoutingPolicyPreset, createVoiceProviderRouter } from './modelAdapters';
@@ -56,6 +57,7 @@ export type { VoiceProviderHealthStatus, VoiceProviderHealthSummary, VoiceProvid
56
57
  export type { VoiceProviderCapabilityDefinition, VoiceProviderCapabilityHandlerOptions, VoiceProviderCapabilityHTMLHandlerOptions, VoiceProviderCapabilityKind, VoiceProviderCapabilityOptions, VoiceProviderCapabilityReport, VoiceProviderCapabilityRoutesOptions, VoiceProviderCapabilitySummary } from './providerCapabilities';
57
58
  export type { VoiceTurnQualityHTMLHandlerOptions, VoiceTurnQualityItem, VoiceTurnQualityOptions, VoiceTurnQualityReport, VoiceTurnQualityRoutesOptions, VoiceTurnQualityStatus } from './turnQuality';
58
59
  export type { VoiceOutcomeContractDefinition, VoiceOutcomeContractHTMLHandlerOptions, VoiceOutcomeContractIssue, VoiceOutcomeContractOptions, VoiceOutcomeContractReport, VoiceOutcomeContractRoutesOptions, VoiceOutcomeContractStatus, VoiceOutcomeContractSuiteReport } from './outcomeContract';
60
+ export type { VoiceTelephonyOutcomeAction, VoiceTelephonyOutcomeDecision, VoiceTelephonyOutcomePolicy, VoiceTelephonyOutcomeProviderEvent, VoiceTelephonyOutcomeRouteResult, VoiceTelephonyOutcomeStatusDecision } from './telephonyOutcome';
59
61
  export type { VoiceOpsConsoleLink, VoiceOpsConsoleReport, VoiceOpsConsoleRoutesOptions } from './opsConsoleRoutes';
60
62
  export type { VoiceQualityLink, VoiceQualityMetric, VoiceQualityReport, VoiceQualityRoutesOptions, VoiceQualityStatus, VoiceQualityThresholds } from './qualityRoutes';
61
63
  export type { VoiceResilienceIOSimulator, VoiceResilienceLink, VoiceResiliencePageData, VoiceResilienceRoutesOptions, VoiceResilienceSimulationProvider, VoiceRoutingDecisionSummary, VoiceRoutingDecisionSummaryOptions, VoiceRoutingEvent, VoiceRoutingEventKind } from './resilienceRoutes';
package/dist/index.js CHANGED
@@ -10559,6 +10559,280 @@ var createVoiceOutcomeContractRoutes = (options) => {
10559
10559
  }
10560
10560
  return routes;
10561
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
+ };
10562
10836
  // src/fileStore.ts
10563
10837
  import { mkdir as mkdir2, readFile, readdir, rename, rm, writeFile } from "fs/promises";
10564
10838
  import { join } from "path";
@@ -14924,6 +15198,7 @@ var shapeTelephonyAssistantText = (text, options = {}) => {
14924
15198
  export {
14925
15199
  withVoiceOpsTaskId,
14926
15200
  withVoiceIntegrationEventId,
15201
+ voiceTelephonyOutcomeToRouteResult,
14927
15202
  voice,
14928
15203
  verifyVoiceOpsWebhookSignature,
14929
15204
  validateVoiceWorkflowRouteResult,
@@ -14956,6 +15231,7 @@ export {
14956
15231
  runVoiceScenarioEvals,
14957
15232
  runVoiceOutcomeContractSuite,
14958
15233
  resolveVoiceTraceRedactionOptions,
15234
+ resolveVoiceTelephonyOutcome,
14959
15235
  resolveVoiceSTTRoutingStrategy,
14960
15236
  resolveVoiceRuntimePreset,
14961
15237
  resolveVoiceProviderRoutingPolicyPreset,
@@ -15044,6 +15320,7 @@ export {
15044
15320
  createVoiceToolContractJSONHandler,
15045
15321
  createVoiceToolContractHTMLHandler,
15046
15322
  createVoiceToolContract,
15323
+ createVoiceTelephonyOutcomePolicy,
15047
15324
  createVoiceTaskUpdatedEvent,
15048
15325
  createVoiceTaskSLABreachedEvent,
15049
15326
  createVoiceTaskCreatedEvent,
@@ -15178,6 +15455,7 @@ export {
15178
15455
  buildVoiceOpsConsoleReport,
15179
15456
  buildVoiceDiagnosticsMarkdown,
15180
15457
  assignVoiceOpsTask,
15458
+ applyVoiceTelephonyOutcome,
15181
15459
  applyVoiceOpsTaskPolicy,
15182
15460
  applyVoiceOpsTaskAssignmentRule,
15183
15461
  applyVoiceHandoffDeliveryResult,
@@ -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>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.68",
3
+ "version": "0.0.22-beta.69",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",