@absolutejs/voice 0.0.22-beta.522 → 0.0.22-beta.523

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
@@ -81,6 +81,10 @@ export { DEFAULT_VOICE_REDACTION_PATTERNS, createVoiceTranscriptRedactor, redact
81
81
  export type { CreateVoiceTranscriptRedactorOptions, VoiceRedactionPattern, VoiceTranscriptRedactor, } from "./redaction";
82
82
  export { deriveVoiceRecordingRedactionRanges, redactVoiceRecording, } from "./recordingRedaction";
83
83
  export type { DeriveVoiceRecordingRedactionRangesInput, RedactVoiceRecordingInput, RedactVoiceRecordingResult, VoiceRecordingRedactionRange, } from "./recordingRedaction";
84
+ export { buildVoiceVariableAnalytics } from "./variableAnalytics";
85
+ export type { BuildVoiceVariableAnalyticsInput, VoiceAnalyticsCall, VoiceAnalyticsVariableValue, VoiceVariableAnalyticsReport, VoiceVariableBreakdown, VoiceVariableValueStats, } from "./variableAnalytics";
86
+ export { VOICE_ZERO_DATA_RETENTION_REDACTION_MARKER, createVoiceZeroDataRetentionPolicy, isVoiceZeroDataRetentionActive, scrubVoiceTraceForZeroDataRetention, scrubVoiceTurnForZeroDataRetention, shouldRetainVoiceRecording, shouldRetainVoiceTranscript, } from "./zeroDataRetention";
87
+ export type { CreateVoiceZeroDataRetentionPolicyOptions, VoiceZeroDataRetentionMode, VoiceZeroDataRetentionPolicy, VoiceZeroDataRetentionRetainFlags, } from "./zeroDataRetention";
84
88
  export { DEFAULT_VOICE_PRICE_BOOK, createVoiceCostAccountant, } from "./costAccounting";
85
89
  export type { CreateVoiceCostAccountantOptions, VoiceCostAccountant, VoiceCostBreakdown, VoiceCostLLMRecord, VoiceCostSTTRecord, VoiceCostTTSRecord, VoiceCostTelephonyRecord, VoicePriceBook, VoiceProviderRates, } from "./costAccounting";
86
90
  export { describeVoiceAssistantMode, resolveVoiceAssistantMode, } from "./assistantMode";
package/dist/index.js CHANGED
@@ -35794,6 +35794,134 @@ var redactVoiceRecording = (input) => {
35794
35794
  redactedCount: ranges.length
35795
35795
  };
35796
35796
  };
35797
+ // src/variableAnalytics.ts
35798
+ var stringifyValue = (value) => {
35799
+ if (value === null || value === undefined)
35800
+ return "(none)";
35801
+ return String(value);
35802
+ };
35803
+ var mean = (values) => values.length === 0 ? null : values.reduce((sum, value) => sum + value, 0) / values.length;
35804
+ var successRateFor = (calls, successOutcomes) => {
35805
+ const flagged = calls.filter((call) => call.success !== undefined || call.outcome !== undefined);
35806
+ if (flagged.length === 0)
35807
+ return null;
35808
+ const successes = flagged.filter((call) => call.success !== undefined ? call.success : call.outcome !== undefined && successOutcomes.has(call.outcome.toLowerCase())).length;
35809
+ return successes / flagged.length;
35810
+ };
35811
+ var buildVoiceVariableAnalytics = (input) => {
35812
+ const successOutcomes = new Set((input.successOutcomes ?? ["won", "resolved", "qualified", "completed", "booked"]).map((outcome) => outcome.toLowerCase()));
35813
+ const totalCalls = input.calls.length;
35814
+ const overall = {
35815
+ avgCostUsd: mean(input.calls.map((call) => call.costUsd).filter((cost) => typeof cost === "number")),
35816
+ avgDurationSeconds: mean(input.calls.map((call) => call.durationSeconds).filter((duration) => typeof duration === "number")),
35817
+ successRate: successRateFor(input.calls, successOutcomes)
35818
+ };
35819
+ const byVariable = {};
35820
+ for (const variable of input.variables) {
35821
+ const groups = new Map;
35822
+ let missingCount = 0;
35823
+ for (const call of input.calls) {
35824
+ const raw = call.variables[variable];
35825
+ if (raw === undefined || raw === null) {
35826
+ missingCount += 1;
35827
+ continue;
35828
+ }
35829
+ const key = stringifyValue(raw);
35830
+ const bucket = groups.get(key) ?? [];
35831
+ bucket.push(call);
35832
+ groups.set(key, bucket);
35833
+ }
35834
+ let values = [];
35835
+ for (const [value, calls] of groups) {
35836
+ const outcomes = {};
35837
+ for (const call of calls) {
35838
+ if (call.outcome) {
35839
+ outcomes[call.outcome] = (outcomes[call.outcome] ?? 0) + 1;
35840
+ }
35841
+ }
35842
+ values.push({
35843
+ avgCostUsd: mean(calls.map((call) => call.costUsd).filter((cost) => typeof cost === "number")),
35844
+ avgDurationSeconds: mean(calls.map((call) => call.durationSeconds).filter((d) => typeof d === "number")),
35845
+ count: calls.length,
35846
+ outcomes,
35847
+ share: totalCalls === 0 ? 0 : calls.length / totalCalls,
35848
+ successRate: successRateFor(calls, successOutcomes),
35849
+ value
35850
+ });
35851
+ }
35852
+ values.sort((left, right) => right.count - left.count);
35853
+ if (input.topValuesPerVariable !== undefined) {
35854
+ values = values.slice(0, input.topValuesPerVariable);
35855
+ }
35856
+ byVariable[variable] = {
35857
+ distinctValues: groups.size,
35858
+ missingCount,
35859
+ values,
35860
+ variable
35861
+ };
35862
+ }
35863
+ return { byVariable, overall, totalCalls };
35864
+ };
35865
+ // src/zeroDataRetention.ts
35866
+ var RETAIN_ALL = {
35867
+ assistantText: true,
35868
+ metadata: true,
35869
+ recordings: true,
35870
+ traceText: true,
35871
+ transcriptText: true
35872
+ };
35873
+ var RETAIN_NONE = {
35874
+ assistantText: false,
35875
+ metadata: false,
35876
+ recordings: false,
35877
+ traceText: false,
35878
+ transcriptText: false
35879
+ };
35880
+ var REDACTED = "[redacted:zdr]";
35881
+ var createVoiceZeroDataRetentionPolicy = (options = {}) => {
35882
+ const mode = options.mode ?? "off";
35883
+ const base = mode === "strict" ? RETAIN_NONE : mode === "off" ? RETAIN_ALL : RETAIN_ALL;
35884
+ return {
35885
+ mode,
35886
+ retain: { ...base, ...options.retain ?? {} }
35887
+ };
35888
+ };
35889
+ var isVoiceZeroDataRetentionActive = (policy) => policy.mode !== "off" && Object.values(policy.retain).some((retain) => retain === false);
35890
+ var shouldRetainVoiceRecording = (policy) => policy.retain.recordings;
35891
+ var shouldRetainVoiceTranscript = (policy) => policy.retain.transcriptText;
35892
+ var scrubVoiceTurnForZeroDataRetention = (turn, policy) => {
35893
+ if (!isVoiceZeroDataRetentionActive(policy))
35894
+ return turn;
35895
+ const scrubbed = {
35896
+ ...turn,
35897
+ text: policy.retain.transcriptText ? turn.text : REDACTED,
35898
+ transcripts: policy.retain.transcriptText ? turn.transcripts : turn.transcripts.map((transcript) => ({
35899
+ ...transcript,
35900
+ text: REDACTED
35901
+ }))
35902
+ };
35903
+ if (!policy.retain.assistantText && scrubbed.assistantText !== undefined) {
35904
+ scrubbed.assistantText = REDACTED;
35905
+ }
35906
+ if (!policy.retain.metadata) {
35907
+ delete scrubbed.attachments;
35908
+ }
35909
+ return scrubbed;
35910
+ };
35911
+ var scrubVoiceTraceForZeroDataRetention = (event, policy) => {
35912
+ if (!isVoiceZeroDataRetentionActive(policy) || policy.retain.traceText) {
35913
+ return event;
35914
+ }
35915
+ const payload = event.payload;
35916
+ if (!payload || typeof payload !== "object")
35917
+ return event;
35918
+ const scrubbedPayload = {};
35919
+ for (const [key, value] of Object.entries(payload)) {
35920
+ scrubbedPayload[key] = typeof value === "string" ? REDACTED : value;
35921
+ }
35922
+ return { ...event, payload: scrubbedPayload };
35923
+ };
35924
+ var VOICE_ZERO_DATA_RETENTION_REDACTION_MARKER = REDACTED;
35797
35925
  // src/costAccounting.ts
35798
35926
  var DEFAULT_VOICE_PRICE_BOOK = {
35799
35927
  "anthropic:claude-opus-4-5": {
@@ -51913,8 +52041,12 @@ export {
51913
52041
  signVoiceTwilioWebhook,
51914
52042
  signVoicePlivoWebhook,
51915
52043
  shouldRetryCampaignAttempt,
52044
+ shouldRetainVoiceTranscript,
52045
+ shouldRetainVoiceRecording,
51916
52046
  shapeTelephonyAssistantText,
51917
52047
  selectVoiceTraceEventsForPrune,
52048
+ scrubVoiceTurnForZeroDataRetention,
52049
+ scrubVoiceTraceForZeroDataRetention,
51918
52050
  scoreVoiceNoShowRisk,
51919
52051
  saveVoiceIncidentBundleArtifact,
51920
52052
  runVoiceToolContractSuite,
@@ -52116,6 +52248,7 @@ export {
52116
52248
  listVoiceProviderDecisionTraces,
52117
52249
  listVoiceOpsTasks,
52118
52250
  isWithinCampaignWindow,
52251
+ isVoiceZeroDataRetentionActive,
52119
52252
  isVoiceOpsTaskOverdue,
52120
52253
  isPhoneOnDNC,
52121
52254
  interleaveStereoPcm,
@@ -52202,6 +52335,7 @@ export {
52202
52335
  decodeTwilioMulawBase64,
52203
52336
  deadLetterVoiceOpsTask,
52204
52337
  createVoiceZeroRetentionPolicy,
52338
+ createVoiceZeroDataRetentionPolicy,
52205
52339
  createVoiceZendeskTicketUpdateSink,
52206
52340
  createVoiceZendeskTicketSyncSinks,
52207
52341
  createVoiceZendeskTicketSink,
@@ -52611,6 +52745,7 @@ export {
52611
52745
  collectVoiceDTMFInput,
52612
52746
  collectVoiceCampaignTemplateVariables,
52613
52747
  claimVoiceOpsTask,
52748
+ buildVoiceVariableAnalytics,
52614
52749
  buildVoiceTraceReplay,
52615
52750
  buildVoiceTraceDeliveryReport,
52616
52751
  buildVoiceTelephonyWebhookSecurityReport,
@@ -52760,6 +52895,7 @@ export {
52760
52895
  appendVoiceIOProviderRouterTraceEvent,
52761
52896
  aggregateVoiceTurnLatencySpans,
52762
52897
  acknowledgeVoiceMonitorIssue,
52898
+ VOICE_ZERO_DATA_RETENTION_REDACTION_MARKER,
52763
52899
  VOICE_WEBHOOK_TIMESTAMP_HEADER,
52764
52900
  VOICE_WEBHOOK_SIGNATURE_HEADER,
52765
52901
  VOICE_TCPA_DEFAULT_WINDOW,
@@ -0,0 +1,47 @@
1
+ export type VoiceAnalyticsVariableValue = string | number | boolean | null;
2
+ export type VoiceAnalyticsCall = {
3
+ sessionId: string;
4
+ variables: Record<string, VoiceAnalyticsVariableValue>;
5
+ durationSeconds?: number;
6
+ costUsd?: number;
7
+ outcome?: string;
8
+ success?: boolean;
9
+ at?: number;
10
+ };
11
+ export type VoiceVariableValueStats = {
12
+ value: string;
13
+ count: number;
14
+ share: number;
15
+ successRate: number | null;
16
+ avgDurationSeconds: number | null;
17
+ avgCostUsd: number | null;
18
+ outcomes: Record<string, number>;
19
+ };
20
+ export type VoiceVariableBreakdown = {
21
+ variable: string;
22
+ distinctValues: number;
23
+ missingCount: number;
24
+ values: VoiceVariableValueStats[];
25
+ };
26
+ export type VoiceVariableAnalyticsReport = {
27
+ totalCalls: number;
28
+ overall: {
29
+ successRate: number | null;
30
+ avgDurationSeconds: number | null;
31
+ avgCostUsd: number | null;
32
+ };
33
+ byVariable: Record<string, VoiceVariableBreakdown>;
34
+ };
35
+ export type BuildVoiceVariableAnalyticsInput = {
36
+ calls: ReadonlyArray<VoiceAnalyticsCall>;
37
+ /** Variable keys to break analytics down by. */
38
+ variables: ReadonlyArray<string>;
39
+ /**
40
+ * Treat these outcome strings as successes when a call has no explicit
41
+ * `success` flag. Case-insensitive.
42
+ */
43
+ successOutcomes?: ReadonlyArray<string>;
44
+ /** Cap the values returned per variable (highest count first). */
45
+ topValuesPerVariable?: number;
46
+ };
47
+ export declare const buildVoiceVariableAnalytics: (input: BuildVoiceVariableAnalyticsInput) => VoiceVariableAnalyticsReport;
@@ -0,0 +1,41 @@
1
+ import type { VoiceTraceEvent } from "./trace";
2
+ import type { VoiceTurnRecord } from "./types";
3
+ export type VoiceZeroDataRetentionMode = "off" | "strict" | "custom";
4
+ export type VoiceZeroDataRetentionRetainFlags = {
5
+ /** Persist committed/partial transcript text. */
6
+ transcriptText: boolean;
7
+ /** Persist assistant response text. */
8
+ assistantText: boolean;
9
+ /** Persist recorded audio artifacts. */
10
+ recordings: boolean;
11
+ /** Persist free-text payloads on trace events. */
12
+ traceText: boolean;
13
+ /** Persist arbitrary session/turn metadata. */
14
+ metadata: boolean;
15
+ };
16
+ export type VoiceZeroDataRetentionPolicy = {
17
+ mode: VoiceZeroDataRetentionMode;
18
+ retain: VoiceZeroDataRetentionRetainFlags;
19
+ };
20
+ export type CreateVoiceZeroDataRetentionPolicyOptions = {
21
+ mode?: VoiceZeroDataRetentionMode;
22
+ /** Per-flag overrides; only meaningful with mode "custom" (or to relax "strict"). */
23
+ retain?: Partial<VoiceZeroDataRetentionRetainFlags>;
24
+ };
25
+ export declare const createVoiceZeroDataRetentionPolicy: (options?: CreateVoiceZeroDataRetentionPolicyOptions) => VoiceZeroDataRetentionPolicy;
26
+ export declare const isVoiceZeroDataRetentionActive: (policy: VoiceZeroDataRetentionPolicy) => boolean;
27
+ export declare const shouldRetainVoiceRecording: (policy: VoiceZeroDataRetentionPolicy) => boolean;
28
+ export declare const shouldRetainVoiceTranscript: (policy: VoiceZeroDataRetentionPolicy) => boolean;
29
+ /**
30
+ * Returns a scrubbed copy of a turn record honoring the ZDR policy. Structural
31
+ * fields (ids, timestamps, transcript count, citation ids) are preserved;
32
+ * content fields are replaced with a redaction marker or dropped.
33
+ */
34
+ export declare const scrubVoiceTurnForZeroDataRetention: <TResult = unknown>(turn: VoiceTurnRecord<TResult>, policy: VoiceZeroDataRetentionPolicy) => VoiceTurnRecord<TResult>;
35
+ /**
36
+ * Returns a scrubbed copy of a trace event honoring the ZDR policy. Drops or
37
+ * masks free-text payload fields while keeping the event type, ids, and
38
+ * timestamps so observability/lifecycle accounting still works.
39
+ */
40
+ export declare const scrubVoiceTraceForZeroDataRetention: (event: VoiceTraceEvent, policy: VoiceZeroDataRetentionPolicy) => VoiceTraceEvent;
41
+ export declare const VOICE_ZERO_DATA_RETENTION_REDACTION_MARKER = "[redacted:zdr]";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.522",
3
+ "version": "0.0.22-beta.523",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",