@absolutejs/voice 0.0.22-beta.497 → 0.0.22-beta.498
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/assistant.d.ts +1 -2
- package/dist/callQuota.d.ts +54 -0
- package/dist/client/costDashboard.d.ts +27 -0
- package/dist/client/liveCallViewer.d.ts +42 -0
- package/dist/client/replayTimeline.d.ts +26 -0
- package/dist/defineVoiceAssistant.d.ts +68 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +604 -37
- package/dist/recordingRedaction.d.ts +18 -0
- package/dist/retention.d.ts +37 -0
- package/dist/routeAuth.d.ts +58 -0
- package/package.json +1 -1
package/dist/assistant.d.ts
CHANGED
|
@@ -59,7 +59,7 @@ export type VoiceAssistantExperimentOptions<TContext = unknown, TSession extends
|
|
|
59
59
|
}) => VoiceAssistantVariant<TContext, TSession, TResult> | string | void;
|
|
60
60
|
variants: Array<VoiceAssistantVariant<TContext, TSession, TResult>>;
|
|
61
61
|
};
|
|
62
|
-
type VoiceAssistantAgentSource<TContext, TSession extends VoiceSessionRecord, TResult> = {
|
|
62
|
+
export type VoiceAssistantAgentSource<TContext, TSession extends VoiceSessionRecord, TResult> = {
|
|
63
63
|
agent: VoiceAgent<TContext, TSession, TResult>;
|
|
64
64
|
agents?: never;
|
|
65
65
|
defaultAgentId?: never;
|
|
@@ -140,4 +140,3 @@ export declare const summarizeVoiceAssistantRuns: (input: StoredVoiceTraceEvent[
|
|
|
140
140
|
events?: StoredVoiceTraceEvent[];
|
|
141
141
|
store?: VoiceTraceEventStore;
|
|
142
142
|
}) => Promise<VoiceAssistantRunsSummary>;
|
|
143
|
-
export {};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export type VoiceCallQuotaTier = {
|
|
2
|
+
burstAllowance?: number;
|
|
3
|
+
customerId: string;
|
|
4
|
+
monthlyMinutes?: number;
|
|
5
|
+
reservedConcurrent: number;
|
|
6
|
+
};
|
|
7
|
+
export type VoiceCallReservation = {
|
|
8
|
+
callId: string;
|
|
9
|
+
customerId: string;
|
|
10
|
+
release: () => Promise<void> | void;
|
|
11
|
+
reservedAt: number;
|
|
12
|
+
};
|
|
13
|
+
export type VoiceCallQuotaRejection = {
|
|
14
|
+
customerId: string;
|
|
15
|
+
reason: "concurrency-exceeded" | "customer-not-found" | "monthly-minutes-exceeded";
|
|
16
|
+
retryAfterMs?: number;
|
|
17
|
+
};
|
|
18
|
+
export type VoiceCallQuotaResult = {
|
|
19
|
+
ok: false;
|
|
20
|
+
rejection: VoiceCallQuotaRejection;
|
|
21
|
+
} | {
|
|
22
|
+
ok: true;
|
|
23
|
+
reservation: VoiceCallReservation;
|
|
24
|
+
};
|
|
25
|
+
export type VoiceCallQuota = {
|
|
26
|
+
describe: (customerId: string) => Promise<undefined | {
|
|
27
|
+
activeCalls: number;
|
|
28
|
+
burstAllowance: number;
|
|
29
|
+
monthlyMinutesUsed: number;
|
|
30
|
+
reservedConcurrent: number;
|
|
31
|
+
tier: VoiceCallQuotaTier;
|
|
32
|
+
}> | (undefined | {
|
|
33
|
+
activeCalls: number;
|
|
34
|
+
burstAllowance: number;
|
|
35
|
+
monthlyMinutesUsed: number;
|
|
36
|
+
reservedConcurrent: number;
|
|
37
|
+
tier: VoiceCallQuotaTier;
|
|
38
|
+
});
|
|
39
|
+
recordMinutes: (input: {
|
|
40
|
+
callId?: string;
|
|
41
|
+
customerId: string;
|
|
42
|
+
minutes: number;
|
|
43
|
+
}) => Promise<void> | void;
|
|
44
|
+
reserve: (input: {
|
|
45
|
+
callId: string;
|
|
46
|
+
customerId: string;
|
|
47
|
+
}) => Promise<VoiceCallQuotaResult> | VoiceCallQuotaResult;
|
|
48
|
+
};
|
|
49
|
+
export type CreateInMemoryVoiceCallQuotaOptions = {
|
|
50
|
+
/** When provided, treat any customer not in the map as denied with 'customer-not-found'. */
|
|
51
|
+
strict?: boolean;
|
|
52
|
+
tiers: VoiceCallQuotaTier[];
|
|
53
|
+
};
|
|
54
|
+
export declare const createInMemoryVoiceCallQuota: (options: CreateInMemoryVoiceCallQuotaOptions) => VoiceCallQuota;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { StoredVoiceTraceEvent } from "../trace";
|
|
2
|
+
export type VoiceCostDashboardBucket = {
|
|
3
|
+
bucketKey: string;
|
|
4
|
+
callCount: number;
|
|
5
|
+
llmUsd: number;
|
|
6
|
+
sttUsd: number;
|
|
7
|
+
telephonyMinutes: number;
|
|
8
|
+
telephonyUsd: number;
|
|
9
|
+
totalUsd: number;
|
|
10
|
+
ttsUsd: number;
|
|
11
|
+
};
|
|
12
|
+
export type VoiceCostDashboardReport = {
|
|
13
|
+
buckets: VoiceCostDashboardBucket[];
|
|
14
|
+
generatedAt: number;
|
|
15
|
+
grandTotal: VoiceCostDashboardBucket;
|
|
16
|
+
windowEndMs: number;
|
|
17
|
+
windowStartMs: number;
|
|
18
|
+
};
|
|
19
|
+
export type VoiceCostDashboardOptions = {
|
|
20
|
+
bucketBy?: "day" | "hour" | "month";
|
|
21
|
+
events: ReadonlyArray<StoredVoiceTraceEvent>;
|
|
22
|
+
/** Inclusive bucket start filter, epoch ms. */
|
|
23
|
+
fromMs?: number;
|
|
24
|
+
/** Inclusive bucket end filter, epoch ms. */
|
|
25
|
+
toMs?: number;
|
|
26
|
+
};
|
|
27
|
+
export declare const buildVoiceCostDashboardReport: (options: VoiceCostDashboardOptions) => VoiceCostDashboardReport;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { VoiceAgentUIState } from "../agentState";
|
|
2
|
+
export type LiveCallEventKind = "agent_audio" | "agent_text" | "lifecycle" | "transcript" | "tool";
|
|
3
|
+
export type LiveCallTimelineEvent = {
|
|
4
|
+
at: number;
|
|
5
|
+
detail?: string;
|
|
6
|
+
kind: LiveCallEventKind;
|
|
7
|
+
title: string;
|
|
8
|
+
};
|
|
9
|
+
export type LiveCallViewState = {
|
|
10
|
+
agentState: VoiceAgentUIState;
|
|
11
|
+
callDurationMs: number;
|
|
12
|
+
events: LiveCallTimelineEvent[];
|
|
13
|
+
isConnected: boolean;
|
|
14
|
+
isLiveListening: boolean;
|
|
15
|
+
lastAssistantAt?: number;
|
|
16
|
+
lastTranscriptAt?: number;
|
|
17
|
+
partialTranscript: string;
|
|
18
|
+
sessionId: string;
|
|
19
|
+
};
|
|
20
|
+
export type CreateLiveCallViewerOptions = {
|
|
21
|
+
bufferLimit?: number;
|
|
22
|
+
sessionId: string;
|
|
23
|
+
startedAt?: number;
|
|
24
|
+
};
|
|
25
|
+
export type LiveCallViewer = {
|
|
26
|
+
applyControl: (control: {
|
|
27
|
+
reason?: string;
|
|
28
|
+
type: string;
|
|
29
|
+
}) => void;
|
|
30
|
+
applyEvent: (event: LiveCallTimelineEvent) => void;
|
|
31
|
+
applyMonitorEvent: (event: {
|
|
32
|
+
payload: Record<string, unknown>;
|
|
33
|
+
type: string;
|
|
34
|
+
}) => void;
|
|
35
|
+
getState: () => LiveCallViewState;
|
|
36
|
+
noteAgentAudio: (at?: number) => void;
|
|
37
|
+
notePartial: (text: string, at?: number) => void;
|
|
38
|
+
noteTranscript: (text: string, at?: number) => void;
|
|
39
|
+
reset: (sessionId: string, startedAt?: number) => void;
|
|
40
|
+
subscribe: (subscriber: () => void) => () => void;
|
|
41
|
+
};
|
|
42
|
+
export declare const createLiveCallViewer: (options: CreateLiveCallViewerOptions) => LiveCallViewer;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { VoiceCallReviewArtifact } from "../testing/review";
|
|
2
|
+
export type ReplayTimelineEvent = {
|
|
3
|
+
at: number;
|
|
4
|
+
category: "agent" | "lifecycle" | "tool" | "user";
|
|
5
|
+
detail?: string;
|
|
6
|
+
durationMs?: number;
|
|
7
|
+
label: string;
|
|
8
|
+
};
|
|
9
|
+
export type ReplayTimelineReport = {
|
|
10
|
+
duration: number;
|
|
11
|
+
events: ReplayTimelineEvent[];
|
|
12
|
+
metadata: {
|
|
13
|
+
artifactId: string;
|
|
14
|
+
title: string;
|
|
15
|
+
};
|
|
16
|
+
startedAt: number;
|
|
17
|
+
summary: {
|
|
18
|
+
agentTurns: number;
|
|
19
|
+
toolCalls: number;
|
|
20
|
+
userTurns: number;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
export type ReplayTimelineInput = {
|
|
24
|
+
artifact: VoiceCallReviewArtifact;
|
|
25
|
+
};
|
|
26
|
+
export declare const buildReplayTimelineReport: (input: ReplayTimelineInput) => ReplayTimelineReport;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type VoiceAssistant, type VoiceAssistantAgentSource, type VoiceAssistantGuardrails, type VoiceAssistantMemoryLifecycle } from "./assistant";
|
|
2
|
+
import type { AudioFormat, CreateVoiceSessionOptions, RealtimeAdapter, STTAdapter, TTSAdapter, VoiceLanguageStrategy, VoiceLexiconEntry, VoicePhraseHint, VoiceReconnectConfig, VoiceResolvedSTTFallbackConfig, VoiceResolvedAudioConditioningConfig, VoiceRouteConfig, VoiceSessionRecord, VoiceSessionRecordingConfig, VoiceSessionStore, VoiceSocket, VoiceSTTLifecycle, VoiceTTSProsody, VoiceTurnDetectionConfig } from "./types";
|
|
3
|
+
import type { VoiceAssistantMemoryOptions } from "./assistantMemory";
|
|
4
|
+
import type { VoiceAgentTool } from "./agent";
|
|
5
|
+
import type { VoiceAMDDetector } from "./amdDetector";
|
|
6
|
+
import type { VoiceCostAccountant } from "./costAccounting";
|
|
7
|
+
import type { VoiceTraceEventStore } from "./trace";
|
|
8
|
+
import type { VoiceTranscriptRedactor } from "./redaction";
|
|
9
|
+
import type { VoiceSemanticTurnDetector } from "./semanticTurn";
|
|
10
|
+
import type { VoiceAssistantMode } from "./assistantMode";
|
|
11
|
+
import type { VoiceRuntimeOpsConfig } from "./types";
|
|
12
|
+
export type VoiceAssistantVoiceConfig = {
|
|
13
|
+
prosody?: VoiceTTSProsody;
|
|
14
|
+
realtime?: RealtimeAdapter;
|
|
15
|
+
realtimeInputFormat?: AudioFormat;
|
|
16
|
+
stt?: STTAdapter;
|
|
17
|
+
sttFallback?: VoiceResolvedSTTFallbackConfig;
|
|
18
|
+
tts?: TTSAdapter;
|
|
19
|
+
};
|
|
20
|
+
export type VoiceAssistantObservabilityConfig = {
|
|
21
|
+
costAccountant?: VoiceCostAccountant;
|
|
22
|
+
costTelephony?: {
|
|
23
|
+
provider?: string;
|
|
24
|
+
};
|
|
25
|
+
recording?: VoiceSessionRecordingConfig;
|
|
26
|
+
trace?: VoiceTraceEventStore;
|
|
27
|
+
};
|
|
28
|
+
export type VoiceAssistantDefinition<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
|
|
29
|
+
agent: VoiceAssistantAgentSource<TContext, TSession, TResult>;
|
|
30
|
+
amd?: VoiceAMDDetector<TContext, TSession, TResult>;
|
|
31
|
+
assistantMode?: VoiceAssistantMode;
|
|
32
|
+
audioConditioning?: VoiceResolvedAudioConditioningConfig;
|
|
33
|
+
callSilenceTimeoutMs?: number;
|
|
34
|
+
guardrails?: VoiceAssistantGuardrails<TContext, TSession, TResult>;
|
|
35
|
+
id: string;
|
|
36
|
+
languageStrategy?: VoiceLanguageStrategy;
|
|
37
|
+
lexicon?: VoiceLexiconEntry[];
|
|
38
|
+
memory?: VoiceAssistantMemoryOptions<TContext, TSession>;
|
|
39
|
+
memoryLifecycle?: VoiceAssistantMemoryLifecycle<TContext, TSession, TResult>;
|
|
40
|
+
metadata?: Record<string, unknown>;
|
|
41
|
+
modalities?: ReadonlyArray<"audio" | "text">;
|
|
42
|
+
ops?: VoiceRuntimeOpsConfig<TContext, TSession, TResult>;
|
|
43
|
+
observability?: VoiceAssistantObservabilityConfig;
|
|
44
|
+
phraseHints?: VoicePhraseHint[];
|
|
45
|
+
redact?: VoiceTranscriptRedactor;
|
|
46
|
+
route?: Partial<VoiceRouteConfig<TContext, TSession, TResult>>;
|
|
47
|
+
semanticTurnDetector?: VoiceSemanticTurnDetector;
|
|
48
|
+
tools?: ReadonlyArray<VoiceAgentTool<TContext, TSession, Record<string, unknown>, unknown, TResult>>;
|
|
49
|
+
turnDetection?: VoiceTurnDetectionConfig;
|
|
50
|
+
voice: VoiceAssistantVoiceConfig;
|
|
51
|
+
};
|
|
52
|
+
export type VoiceAssistantSessionInput<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord> = {
|
|
53
|
+
context: TContext;
|
|
54
|
+
id: string;
|
|
55
|
+
reconnect?: Partial<VoiceReconnectConfig>;
|
|
56
|
+
scenarioId?: string;
|
|
57
|
+
sessionMetadata?: Record<string, unknown>;
|
|
58
|
+
socket: VoiceSocket;
|
|
59
|
+
sttLifecycle?: VoiceSTTLifecycle;
|
|
60
|
+
store: VoiceSessionStore<TSession>;
|
|
61
|
+
};
|
|
62
|
+
export type DefinedVoiceAssistant<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
|
|
63
|
+
assistant: VoiceAssistant<TContext, TSession, TResult>;
|
|
64
|
+
definition: VoiceAssistantDefinition<TContext, TSession, TResult>;
|
|
65
|
+
id: string;
|
|
66
|
+
toSessionOptions: (input: VoiceAssistantSessionInput<TContext, TSession>) => CreateVoiceSessionOptions<TContext, TSession, TResult>;
|
|
67
|
+
};
|
|
68
|
+
export declare const defineVoiceAssistant: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown>(definition: VoiceAssistantDefinition<TContext, TSession, TResult>) => DefinedVoiceAssistant<TContext, TSession, TResult>;
|
package/dist/index.d.ts
CHANGED
|
@@ -75,6 +75,8 @@ export { createVoiceAIJudgeCompletion, createVoiceLLMJudge, } from "./llmJudge";
|
|
|
75
75
|
export type { CreateVoiceAIJudgeCompletionOptions, CreateVoiceLLMJudgeOptions, VoiceLLMJudge, VoiceLLMJudgeCompletion, VoiceLLMJudgeCriterionVerdict, VoiceLLMJudgeInput, VoiceLLMJudgeRubric, VoiceLLMJudgeRubricCriterion, VoiceLLMJudgeVerdict, } from "./llmJudge";
|
|
76
76
|
export { DEFAULT_VOICE_REDACTION_PATTERNS, createVoiceTranscriptRedactor, redactVoiceTranscript, } from "./redaction";
|
|
77
77
|
export type { CreateVoiceTranscriptRedactorOptions, VoiceRedactionPattern, VoiceTranscriptRedactor, } from "./redaction";
|
|
78
|
+
export { deriveVoiceRecordingRedactionRanges } from "./recordingRedaction";
|
|
79
|
+
export type { DeriveVoiceRecordingRedactionRangesInput, VoiceRecordingRedactionRange, } from "./recordingRedaction";
|
|
78
80
|
export { DEFAULT_VOICE_PRICE_BOOK, createVoiceCostAccountant, } from "./costAccounting";
|
|
79
81
|
export type { CreateVoiceCostAccountantOptions, VoiceCostAccountant, VoiceCostBreakdown, VoiceCostLLMRecord, VoiceCostSTTRecord, VoiceCostTTSRecord, VoiceCostTelephonyRecord, VoicePriceBook, VoiceProviderRates, } from "./costAccounting";
|
|
80
82
|
export { describeVoiceAssistantMode, resolveVoiceAssistantMode, } from "./assistantMode";
|
|
@@ -102,6 +104,20 @@ export type { MonologueAMDDetectorOptions, VoiceAMDDetector, VoiceAMDDetectorInp
|
|
|
102
104
|
export { createVoiceRAGTool, extractVoiceRAGCitations } from "./ragTool";
|
|
103
105
|
export type { VoiceRAGCitationSummary, VoiceRAGCollectionLike, VoiceRAGQueryResult, VoiceRAGSearchInput, VoiceRAGToolArgs, VoiceRAGToolOptions, VoiceRAGToolResult, } from "./ragTool";
|
|
104
106
|
export { createVoiceApiRequestTool, createVoiceDTMFTool, createVoiceEndCallTool, createVoiceTransferCallTool, createVoiceVoicemailDetectionTool, } from "./agentTools";
|
|
107
|
+
export { defineVoiceAssistant } from "./defineVoiceAssistant";
|
|
108
|
+
export type { DefinedVoiceAssistant, VoiceAssistantDefinition, VoiceAssistantObservabilityConfig, VoiceAssistantSessionInput, VoiceAssistantVoiceConfig, } from "./defineVoiceAssistant";
|
|
109
|
+
export { createInMemoryVoiceCallQuota } from "./callQuota";
|
|
110
|
+
export type { CreateInMemoryVoiceCallQuotaOptions, VoiceCallQuota, VoiceCallQuotaRejection, VoiceCallQuotaResult, VoiceCallQuotaTier, VoiceCallReservation, } from "./callQuota";
|
|
111
|
+
export { createVoiceBearerAuthVerifier, createVoiceHMACAuthVerifier, createVoiceRouteAuth, } from "./routeAuth";
|
|
112
|
+
export type { VoiceRouteAuthDecision, VoiceRouteAuthInput, VoiceRouteAuthOptions, VoiceRouteAuthVerifier, } from "./routeAuth";
|
|
113
|
+
export { buildVoiceCostDashboardReport } from "./client/costDashboard";
|
|
114
|
+
export type { VoiceCostDashboardBucket, VoiceCostDashboardOptions, VoiceCostDashboardReport, } from "./client/costDashboard";
|
|
115
|
+
export { createLiveCallViewer } from "./client/liveCallViewer";
|
|
116
|
+
export type { CreateLiveCallViewerOptions, LiveCallEventKind, LiveCallTimelineEvent, LiveCallViewState, LiveCallViewer, } from "./client/liveCallViewer";
|
|
117
|
+
export { buildReplayTimelineReport } from "./client/replayTimeline";
|
|
118
|
+
export type { ReplayTimelineEvent, ReplayTimelineInput, ReplayTimelineReport, } from "./client/replayTimeline";
|
|
119
|
+
export { createVoiceRetentionScheduler, purgeVoiceRetentionStore, } from "./retention";
|
|
120
|
+
export type { CreateVoiceRetentionSchedulerOptions, VoicePurgeReport, VoiceRetentionPolicyOptions, VoiceRetentionScheduler, VoiceRetentionStore, } from "./retention";
|
|
105
121
|
export { fromVapiAssistantConfig } from "./vapiAdapter";
|
|
106
122
|
export type { VapiAssistantConfig, VapiAssistantConfigModel, VapiAssistantConfigTool, VapiAssistantConfigTranscriber, VapiAssistantConfigTransferDestination, VapiAssistantConfigVoice, VapiAssistantMessage, VoiceFromVapiAssistantOptions, VoiceFromVapiAssistantResult, VoiceFromVapiCustomToolFactory, VoiceFromVapiCustomToolInput, VoiceFromVapiDTMFFactory, VoiceFromVapiKnowledgeBase, VoiceFromVapiModelFactory, VoiceFromVapiModelFactoryInput, VoiceFromVapiRouteHints, VoiceFromVapiUnsupportedReason, } from "./vapiAdapter";
|
|
107
123
|
export type { VoiceApiRequestToolArgs, VoiceApiRequestToolFetch, VoiceApiRequestToolHttpMethod, VoiceApiRequestToolOptions, VoiceApiRequestToolResult, VoiceDTMFToolArgs, VoiceDTMFToolOptions, VoiceDTMFToolResult, VoiceEndCallToolArgs, VoiceEndCallToolOptions, VoiceEndCallToolResult, VoiceTransferCallToolArgs, VoiceTransferCallToolDestination, VoiceTransferCallToolOptions, VoiceTransferCallToolResult, VoiceVoicemailDetectionToolArgs, VoiceVoicemailDetectionToolOptions, VoiceVoicemailDetectionToolResult, } from "./agentTools";
|
package/dist/index.js
CHANGED
|
@@ -35232,6 +35232,44 @@ var createVoiceTranscriptRedactor = (options = {}) => {
|
|
|
35232
35232
|
};
|
|
35233
35233
|
};
|
|
35234
35234
|
var redactVoiceTranscript = (transcript, patterns = DEFAULT_VOICE_REDACTION_PATTERNS) => createVoiceTranscriptRedactor({ patterns })(transcript);
|
|
35235
|
+
// src/recordingRedaction.ts
|
|
35236
|
+
var matchesAnyPattern = (text, patterns) => {
|
|
35237
|
+
for (const pattern of patterns) {
|
|
35238
|
+
pattern.regex.lastIndex = 0;
|
|
35239
|
+
if (pattern.regex.test(text)) {
|
|
35240
|
+
pattern.regex.lastIndex = 0;
|
|
35241
|
+
return pattern;
|
|
35242
|
+
}
|
|
35243
|
+
}
|
|
35244
|
+
return;
|
|
35245
|
+
};
|
|
35246
|
+
var deriveVoiceRecordingRedactionRanges = (input) => {
|
|
35247
|
+
const patterns = input.patterns ?? DEFAULT_VOICE_REDACTION_PATTERNS;
|
|
35248
|
+
const padding = Math.max(0, input.paddingMs ?? 100);
|
|
35249
|
+
const baseEpoch = input.recordingStartedAtEpochMs;
|
|
35250
|
+
const out = [];
|
|
35251
|
+
for (const transcript of input.transcripts) {
|
|
35252
|
+
if (!transcript.isFinal)
|
|
35253
|
+
continue;
|
|
35254
|
+
if (typeof transcript.startedAtMs !== "number")
|
|
35255
|
+
continue;
|
|
35256
|
+
if (typeof transcript.endedAtMs !== "number")
|
|
35257
|
+
continue;
|
|
35258
|
+
const matched = matchesAnyPattern(transcript.text, patterns);
|
|
35259
|
+
if (!matched)
|
|
35260
|
+
continue;
|
|
35261
|
+
const absStart = transcript.startedAtMs;
|
|
35262
|
+
const absEnd = transcript.endedAtMs;
|
|
35263
|
+
const start = typeof baseEpoch === "number" && absStart >= baseEpoch ? absStart - baseEpoch : absStart;
|
|
35264
|
+
const end = typeof baseEpoch === "number" && absEnd >= baseEpoch ? absEnd - baseEpoch : absEnd;
|
|
35265
|
+
out.push({
|
|
35266
|
+
endMs: end + padding,
|
|
35267
|
+
label: matched.label,
|
|
35268
|
+
startMs: Math.max(0, start - padding)
|
|
35269
|
+
});
|
|
35270
|
+
}
|
|
35271
|
+
return out;
|
|
35272
|
+
};
|
|
35235
35273
|
// src/costAccounting.ts
|
|
35236
35274
|
var DEFAULT_VOICE_PRICE_BOOK = {
|
|
35237
35275
|
"anthropic:claude-opus-4-5": {
|
|
@@ -36481,6 +36519,524 @@ var createVoiceApiRequestTool = (options) => {
|
|
|
36481
36519
|
resultToMessage: options.formatResult ?? ((result) => result.ok ? `API request ${options.name} succeeded (${String(result.status)}).` : `API request ${options.name} failed with status ${String(result.status)}.`)
|
|
36482
36520
|
});
|
|
36483
36521
|
};
|
|
36522
|
+
// src/defineVoiceAssistant.ts
|
|
36523
|
+
var DEFAULT_SPEECH_THRESHOLD2 = 0.015;
|
|
36524
|
+
var DEFAULT_SILENCE_MS2 = 700;
|
|
36525
|
+
var DEFAULT_TRANSCRIPT_STABILITY_MS2 = 200;
|
|
36526
|
+
var resolveTurnDetection = (input) => ({
|
|
36527
|
+
profile: input?.profile ?? "balanced",
|
|
36528
|
+
qualityProfile: input?.qualityProfile ?? "general",
|
|
36529
|
+
silenceMs: input?.silenceMs ?? DEFAULT_SILENCE_MS2,
|
|
36530
|
+
speechThreshold: input?.speechThreshold ?? DEFAULT_SPEECH_THRESHOLD2,
|
|
36531
|
+
transcriptStabilityMs: input?.transcriptStabilityMs ?? DEFAULT_TRANSCRIPT_STABILITY_MS2
|
|
36532
|
+
});
|
|
36533
|
+
var resolveReconnect = (input) => ({
|
|
36534
|
+
maxAttempts: input?.maxAttempts ?? 3,
|
|
36535
|
+
strategy: input?.strategy ?? "resume-last-turn",
|
|
36536
|
+
timeout: input?.timeout ?? 30000
|
|
36537
|
+
});
|
|
36538
|
+
var buildAssistantOptions = (def) => ({
|
|
36539
|
+
...def.agent,
|
|
36540
|
+
guardrails: def.guardrails,
|
|
36541
|
+
id: def.id,
|
|
36542
|
+
memory: def.memory,
|
|
36543
|
+
memoryLifecycle: def.memoryLifecycle,
|
|
36544
|
+
ops: def.ops
|
|
36545
|
+
});
|
|
36546
|
+
var defineVoiceAssistant = (definition) => {
|
|
36547
|
+
const assistantOptions = buildAssistantOptions(definition);
|
|
36548
|
+
const assistant = createVoiceAssistant(assistantOptions);
|
|
36549
|
+
return {
|
|
36550
|
+
assistant,
|
|
36551
|
+
definition,
|
|
36552
|
+
id: definition.id,
|
|
36553
|
+
toSessionOptions: (input) => {
|
|
36554
|
+
const route = assistant.route(definition.route ?? {});
|
|
36555
|
+
const observability = definition.observability ?? {};
|
|
36556
|
+
return {
|
|
36557
|
+
amd: definition.amd,
|
|
36558
|
+
assistantMode: definition.assistantMode,
|
|
36559
|
+
audioConditioning: definition.audioConditioning,
|
|
36560
|
+
callSilenceTimeoutMs: definition.callSilenceTimeoutMs,
|
|
36561
|
+
context: input.context,
|
|
36562
|
+
costAccountant: observability.costAccountant,
|
|
36563
|
+
costTelephony: observability.costTelephony,
|
|
36564
|
+
id: input.id,
|
|
36565
|
+
languageStrategy: definition.languageStrategy,
|
|
36566
|
+
lexicon: definition.lexicon,
|
|
36567
|
+
modalities: definition.modalities,
|
|
36568
|
+
phraseHints: definition.phraseHints,
|
|
36569
|
+
prosody: definition.voice.prosody,
|
|
36570
|
+
realtime: definition.voice.realtime,
|
|
36571
|
+
realtimeInputFormat: definition.voice.realtimeInputFormat,
|
|
36572
|
+
reconnect: resolveReconnect(input.reconnect),
|
|
36573
|
+
recording: observability.recording,
|
|
36574
|
+
redact: definition.redact,
|
|
36575
|
+
route,
|
|
36576
|
+
scenarioId: input.scenarioId,
|
|
36577
|
+
semanticTurnDetector: definition.semanticTurnDetector,
|
|
36578
|
+
sessionMetadata: { ...definition.metadata, ...input.sessionMetadata },
|
|
36579
|
+
socket: input.socket,
|
|
36580
|
+
store: input.store,
|
|
36581
|
+
stt: definition.voice.stt,
|
|
36582
|
+
sttFallback: definition.voice.sttFallback,
|
|
36583
|
+
sttLifecycle: input.sttLifecycle ?? "continuous",
|
|
36584
|
+
trace: observability.trace,
|
|
36585
|
+
tts: definition.voice.tts,
|
|
36586
|
+
turnDetection: resolveTurnDetection(definition.turnDetection)
|
|
36587
|
+
};
|
|
36588
|
+
}
|
|
36589
|
+
};
|
|
36590
|
+
};
|
|
36591
|
+
// src/callQuota.ts
|
|
36592
|
+
var monthBucketKey = (epochMs) => {
|
|
36593
|
+
const date = new Date(epochMs);
|
|
36594
|
+
return `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, "0")}`;
|
|
36595
|
+
};
|
|
36596
|
+
var createInMemoryVoiceCallQuota = (options) => {
|
|
36597
|
+
const tiers = new Map(options.tiers.map((tier) => [tier.customerId, tier]));
|
|
36598
|
+
const activeCalls = new Map;
|
|
36599
|
+
const monthlyUsage = new Map;
|
|
36600
|
+
const usageFor = (customerId) => {
|
|
36601
|
+
const key = monthBucketKey(Date.now());
|
|
36602
|
+
let bucket = monthlyUsage.get(customerId);
|
|
36603
|
+
if (!bucket) {
|
|
36604
|
+
bucket = new Map;
|
|
36605
|
+
monthlyUsage.set(customerId, bucket);
|
|
36606
|
+
}
|
|
36607
|
+
return { bucket, key };
|
|
36608
|
+
};
|
|
36609
|
+
return {
|
|
36610
|
+
describe: (customerId) => {
|
|
36611
|
+
const tier = tiers.get(customerId);
|
|
36612
|
+
if (!tier)
|
|
36613
|
+
return;
|
|
36614
|
+
const active = activeCalls.get(customerId)?.size ?? 0;
|
|
36615
|
+
const { bucket, key } = usageFor(customerId);
|
|
36616
|
+
return {
|
|
36617
|
+
activeCalls: active,
|
|
36618
|
+
burstAllowance: tier.burstAllowance ?? 0,
|
|
36619
|
+
monthlyMinutesUsed: bucket.get(key) ?? 0,
|
|
36620
|
+
reservedConcurrent: tier.reservedConcurrent,
|
|
36621
|
+
tier
|
|
36622
|
+
};
|
|
36623
|
+
},
|
|
36624
|
+
recordMinutes: ({ customerId, minutes }) => {
|
|
36625
|
+
const { bucket, key } = usageFor(customerId);
|
|
36626
|
+
bucket.set(key, (bucket.get(key) ?? 0) + Math.max(0, minutes));
|
|
36627
|
+
},
|
|
36628
|
+
reserve: ({ callId, customerId }) => {
|
|
36629
|
+
const tier = tiers.get(customerId);
|
|
36630
|
+
if (!tier) {
|
|
36631
|
+
if (options.strict) {
|
|
36632
|
+
return {
|
|
36633
|
+
ok: false,
|
|
36634
|
+
rejection: { customerId, reason: "customer-not-found" }
|
|
36635
|
+
};
|
|
36636
|
+
}
|
|
36637
|
+
return {
|
|
36638
|
+
ok: true,
|
|
36639
|
+
reservation: {
|
|
36640
|
+
callId,
|
|
36641
|
+
customerId,
|
|
36642
|
+
release: () => {},
|
|
36643
|
+
reservedAt: Date.now()
|
|
36644
|
+
}
|
|
36645
|
+
};
|
|
36646
|
+
}
|
|
36647
|
+
const limit = tier.reservedConcurrent + (tier.burstAllowance ?? 0);
|
|
36648
|
+
const set = activeCalls.get(customerId) ?? new Set;
|
|
36649
|
+
if (set.size >= limit) {
|
|
36650
|
+
return {
|
|
36651
|
+
ok: false,
|
|
36652
|
+
rejection: {
|
|
36653
|
+
customerId,
|
|
36654
|
+
reason: "concurrency-exceeded",
|
|
36655
|
+
retryAfterMs: 30000
|
|
36656
|
+
}
|
|
36657
|
+
};
|
|
36658
|
+
}
|
|
36659
|
+
if (typeof tier.monthlyMinutes === "number") {
|
|
36660
|
+
const { bucket, key } = usageFor(customerId);
|
|
36661
|
+
if ((bucket.get(key) ?? 0) >= tier.monthlyMinutes) {
|
|
36662
|
+
return {
|
|
36663
|
+
ok: false,
|
|
36664
|
+
rejection: {
|
|
36665
|
+
customerId,
|
|
36666
|
+
reason: "monthly-minutes-exceeded"
|
|
36667
|
+
}
|
|
36668
|
+
};
|
|
36669
|
+
}
|
|
36670
|
+
}
|
|
36671
|
+
set.add(callId);
|
|
36672
|
+
activeCalls.set(customerId, set);
|
|
36673
|
+
return {
|
|
36674
|
+
ok: true,
|
|
36675
|
+
reservation: {
|
|
36676
|
+
callId,
|
|
36677
|
+
customerId,
|
|
36678
|
+
release: () => {
|
|
36679
|
+
set.delete(callId);
|
|
36680
|
+
if (set.size === 0)
|
|
36681
|
+
activeCalls.delete(customerId);
|
|
36682
|
+
},
|
|
36683
|
+
reservedAt: Date.now()
|
|
36684
|
+
}
|
|
36685
|
+
};
|
|
36686
|
+
}
|
|
36687
|
+
};
|
|
36688
|
+
};
|
|
36689
|
+
// src/routeAuth.ts
|
|
36690
|
+
import { Elysia as Elysia53 } from "elysia";
|
|
36691
|
+
var createVoiceBearerAuthVerifier = (input) => {
|
|
36692
|
+
const headerName = (input.headerName ?? "authorization").toLowerCase();
|
|
36693
|
+
const expected = `Bearer ${input.expectedToken}`;
|
|
36694
|
+
return ({ headers }) => {
|
|
36695
|
+
const value = headers.get(headerName);
|
|
36696
|
+
if (!value) {
|
|
36697
|
+
return { allow: false, reason: "missing-bearer", status: 401 };
|
|
36698
|
+
}
|
|
36699
|
+
if (value !== expected) {
|
|
36700
|
+
return { allow: false, reason: "bearer-mismatch", status: 401 };
|
|
36701
|
+
}
|
|
36702
|
+
return { allow: true };
|
|
36703
|
+
};
|
|
36704
|
+
};
|
|
36705
|
+
var createVoiceHMACAuthVerifier = (input) => {
|
|
36706
|
+
return async ({ body, headers }) => {
|
|
36707
|
+
const { signature, timestamp } = extractVoiceWebhookSignatureFromHeaders(headers);
|
|
36708
|
+
const result = await verifyVoiceWebhookSignature({
|
|
36709
|
+
body: body ?? "",
|
|
36710
|
+
secret: input.secret,
|
|
36711
|
+
signature,
|
|
36712
|
+
timestamp,
|
|
36713
|
+
toleranceMs: input.toleranceMs
|
|
36714
|
+
});
|
|
36715
|
+
if (!result.ok) {
|
|
36716
|
+
return { allow: false, reason: result.reason, status: 401 };
|
|
36717
|
+
}
|
|
36718
|
+
return { allow: true };
|
|
36719
|
+
};
|
|
36720
|
+
};
|
|
36721
|
+
var isBypassed = (bypassPaths, url) => {
|
|
36722
|
+
for (const path of bypassPaths) {
|
|
36723
|
+
if (url.includes(path))
|
|
36724
|
+
return true;
|
|
36725
|
+
}
|
|
36726
|
+
return false;
|
|
36727
|
+
};
|
|
36728
|
+
var createVoiceRouteAuth = (options) => {
|
|
36729
|
+
const bypassPaths = options.bypassPaths ?? [];
|
|
36730
|
+
return new Elysia53({ name: options.name ?? "voice-route-auth" }).onRequest(async ({ request, set }) => {
|
|
36731
|
+
const url = request.url;
|
|
36732
|
+
if (isBypassed(bypassPaths, url))
|
|
36733
|
+
return;
|
|
36734
|
+
const cloned = request.clone();
|
|
36735
|
+
const body = request.method === "GET" || request.method === "HEAD" ? "" : await cloned.text().catch(() => "");
|
|
36736
|
+
const decision = await Promise.resolve(options.verify({
|
|
36737
|
+
body,
|
|
36738
|
+
headers: request.headers,
|
|
36739
|
+
method: request.method,
|
|
36740
|
+
url
|
|
36741
|
+
}));
|
|
36742
|
+
if (!decision.allow) {
|
|
36743
|
+
set.status = decision.status ?? 401;
|
|
36744
|
+
return new Response(JSON.stringify({ error: decision.reason, ok: false }), {
|
|
36745
|
+
headers: { "content-type": "application/json" },
|
|
36746
|
+
status: decision.status ?? 401
|
|
36747
|
+
});
|
|
36748
|
+
}
|
|
36749
|
+
});
|
|
36750
|
+
};
|
|
36751
|
+
// src/client/costDashboard.ts
|
|
36752
|
+
var padTwo = (value) => String(value).padStart(2, "0");
|
|
36753
|
+
var formatBucketKey = (epochMs, bucketBy) => {
|
|
36754
|
+
const date = new Date(epochMs);
|
|
36755
|
+
const y = date.getUTCFullYear();
|
|
36756
|
+
const m = padTwo(date.getUTCMonth() + 1);
|
|
36757
|
+
const d = padTwo(date.getUTCDate());
|
|
36758
|
+
const h = padTwo(date.getUTCHours());
|
|
36759
|
+
if (bucketBy === "month")
|
|
36760
|
+
return `${y}-${m}`;
|
|
36761
|
+
if (bucketBy === "day")
|
|
36762
|
+
return `${y}-${m}-${d}`;
|
|
36763
|
+
return `${y}-${m}-${d}T${h}`;
|
|
36764
|
+
};
|
|
36765
|
+
var isCostBreakdown = (value) => Boolean(value) && typeof value === "object" && typeof value.totalUsd === "number";
|
|
36766
|
+
var emptyBucket = (bucketKey2) => ({
|
|
36767
|
+
bucketKey: bucketKey2,
|
|
36768
|
+
callCount: 0,
|
|
36769
|
+
llmUsd: 0,
|
|
36770
|
+
sttUsd: 0,
|
|
36771
|
+
telephonyMinutes: 0,
|
|
36772
|
+
telephonyUsd: 0,
|
|
36773
|
+
totalUsd: 0,
|
|
36774
|
+
ttsUsd: 0
|
|
36775
|
+
});
|
|
36776
|
+
var accumulate = (bucket, payload) => {
|
|
36777
|
+
bucket.callCount += 1;
|
|
36778
|
+
bucket.llmUsd += payload.llm.usd;
|
|
36779
|
+
bucket.sttUsd += payload.stt.usd;
|
|
36780
|
+
bucket.ttsUsd += payload.tts.usd;
|
|
36781
|
+
bucket.telephonyUsd += payload.telephony.usd;
|
|
36782
|
+
bucket.telephonyMinutes += payload.telephony.minutes;
|
|
36783
|
+
bucket.totalUsd += payload.totalUsd;
|
|
36784
|
+
};
|
|
36785
|
+
var roundCurrency = (bucket) => {
|
|
36786
|
+
bucket.llmUsd = Math.round(bucket.llmUsd * 1e6) / 1e6;
|
|
36787
|
+
bucket.sttUsd = Math.round(bucket.sttUsd * 1e6) / 1e6;
|
|
36788
|
+
bucket.ttsUsd = Math.round(bucket.ttsUsd * 1e6) / 1e6;
|
|
36789
|
+
bucket.telephonyUsd = Math.round(bucket.telephonyUsd * 1e6) / 1e6;
|
|
36790
|
+
bucket.totalUsd = Math.round(bucket.totalUsd * 1e6) / 1e6;
|
|
36791
|
+
};
|
|
36792
|
+
var buildVoiceCostDashboardReport = (options) => {
|
|
36793
|
+
const bucketBy = options.bucketBy ?? "day";
|
|
36794
|
+
const fromMs = options.fromMs ?? Number.NEGATIVE_INFINITY;
|
|
36795
|
+
const toMs = options.toMs ?? Number.POSITIVE_INFINITY;
|
|
36796
|
+
const buckets = new Map;
|
|
36797
|
+
const grandTotal = emptyBucket("total");
|
|
36798
|
+
let minMs = Number.POSITIVE_INFINITY;
|
|
36799
|
+
let maxMs = Number.NEGATIVE_INFINITY;
|
|
36800
|
+
for (const event of options.events) {
|
|
36801
|
+
if (event.type !== "cost.ready")
|
|
36802
|
+
continue;
|
|
36803
|
+
if (event.at < fromMs || event.at > toMs)
|
|
36804
|
+
continue;
|
|
36805
|
+
if (!isCostBreakdown(event.payload))
|
|
36806
|
+
continue;
|
|
36807
|
+
minMs = Math.min(minMs, event.at);
|
|
36808
|
+
maxMs = Math.max(maxMs, event.at);
|
|
36809
|
+
const bucketKey2 = formatBucketKey(event.at, bucketBy);
|
|
36810
|
+
let bucket = buckets.get(bucketKey2);
|
|
36811
|
+
if (!bucket) {
|
|
36812
|
+
bucket = emptyBucket(bucketKey2);
|
|
36813
|
+
buckets.set(bucketKey2, bucket);
|
|
36814
|
+
}
|
|
36815
|
+
accumulate(bucket, event.payload);
|
|
36816
|
+
accumulate(grandTotal, event.payload);
|
|
36817
|
+
}
|
|
36818
|
+
for (const bucket of buckets.values()) {
|
|
36819
|
+
roundCurrency(bucket);
|
|
36820
|
+
}
|
|
36821
|
+
roundCurrency(grandTotal);
|
|
36822
|
+
return {
|
|
36823
|
+
buckets: Array.from(buckets.values()).sort((a, b) => a.bucketKey.localeCompare(b.bucketKey)),
|
|
36824
|
+
generatedAt: Date.now(),
|
|
36825
|
+
grandTotal,
|
|
36826
|
+
windowEndMs: Number.isFinite(maxMs) ? maxMs : 0,
|
|
36827
|
+
windowStartMs: Number.isFinite(minMs) ? minMs : 0
|
|
36828
|
+
};
|
|
36829
|
+
};
|
|
36830
|
+
// src/client/liveCallViewer.ts
|
|
36831
|
+
var EVENT_BUFFER_LIMIT = 200;
|
|
36832
|
+
var createLiveCallViewer = (options) => {
|
|
36833
|
+
const bufferLimit = options.bufferLimit ?? EVENT_BUFFER_LIMIT;
|
|
36834
|
+
const subscribers = new Set;
|
|
36835
|
+
let state = {
|
|
36836
|
+
agentState: "idle",
|
|
36837
|
+
callDurationMs: 0,
|
|
36838
|
+
events: [],
|
|
36839
|
+
isConnected: true,
|
|
36840
|
+
isLiveListening: true,
|
|
36841
|
+
partialTranscript: "",
|
|
36842
|
+
sessionId: options.sessionId
|
|
36843
|
+
};
|
|
36844
|
+
const startedAt = options.startedAt ?? Date.now();
|
|
36845
|
+
const notify = () => {
|
|
36846
|
+
for (const subscriber of subscribers)
|
|
36847
|
+
subscriber();
|
|
36848
|
+
};
|
|
36849
|
+
const update = (next) => {
|
|
36850
|
+
state = { ...state, ...next };
|
|
36851
|
+
state.callDurationMs = Math.max(0, Date.now() - startedAt);
|
|
36852
|
+
state.agentState = deriveVoiceAgentUIState({
|
|
36853
|
+
hasActivePartial: state.partialTranscript.length > 0,
|
|
36854
|
+
isConnected: state.isConnected,
|
|
36855
|
+
isPlaying: false,
|
|
36856
|
+
isRecording: state.isLiveListening,
|
|
36857
|
+
lastAssistantAt: state.lastAssistantAt,
|
|
36858
|
+
lastTranscriptAt: state.lastTranscriptAt
|
|
36859
|
+
});
|
|
36860
|
+
notify();
|
|
36861
|
+
};
|
|
36862
|
+
const pushEvent = (event) => {
|
|
36863
|
+
const next = state.events.concat(event);
|
|
36864
|
+
if (next.length > bufferLimit) {
|
|
36865
|
+
next.splice(0, next.length - bufferLimit);
|
|
36866
|
+
}
|
|
36867
|
+
update({ events: next });
|
|
36868
|
+
};
|
|
36869
|
+
return {
|
|
36870
|
+
applyControl: (control) => {
|
|
36871
|
+
pushEvent({
|
|
36872
|
+
at: Date.now(),
|
|
36873
|
+
detail: control.reason,
|
|
36874
|
+
kind: "lifecycle",
|
|
36875
|
+
title: `control:${control.type}`
|
|
36876
|
+
});
|
|
36877
|
+
},
|
|
36878
|
+
applyEvent: pushEvent,
|
|
36879
|
+
applyMonitorEvent: ({ payload, type }) => {
|
|
36880
|
+
pushEvent({
|
|
36881
|
+
at: Date.now(),
|
|
36882
|
+
detail: JSON.stringify(payload).slice(0, 240),
|
|
36883
|
+
kind: type === "call.lifecycle" ? "lifecycle" : "lifecycle",
|
|
36884
|
+
title: type
|
|
36885
|
+
});
|
|
36886
|
+
},
|
|
36887
|
+
getState: () => state,
|
|
36888
|
+
noteAgentAudio: (at) => {
|
|
36889
|
+
const ts = at ?? Date.now();
|
|
36890
|
+
update({ lastAssistantAt: ts });
|
|
36891
|
+
pushEvent({
|
|
36892
|
+
at: ts,
|
|
36893
|
+
kind: "agent_audio",
|
|
36894
|
+
title: "Agent audio frame"
|
|
36895
|
+
});
|
|
36896
|
+
},
|
|
36897
|
+
notePartial: (text, at) => {
|
|
36898
|
+
update({ partialTranscript: text });
|
|
36899
|
+
if (text) {
|
|
36900
|
+
pushEvent({
|
|
36901
|
+
at: at ?? Date.now(),
|
|
36902
|
+
detail: text,
|
|
36903
|
+
kind: "transcript",
|
|
36904
|
+
title: "Partial"
|
|
36905
|
+
});
|
|
36906
|
+
}
|
|
36907
|
+
},
|
|
36908
|
+
noteTranscript: (text, at) => {
|
|
36909
|
+
const ts = at ?? Date.now();
|
|
36910
|
+
update({ lastTranscriptAt: ts, partialTranscript: "" });
|
|
36911
|
+
pushEvent({
|
|
36912
|
+
at: ts,
|
|
36913
|
+
detail: text,
|
|
36914
|
+
kind: "transcript",
|
|
36915
|
+
title: "Final transcript"
|
|
36916
|
+
});
|
|
36917
|
+
},
|
|
36918
|
+
reset: (sessionId, startedAtOverride) => {
|
|
36919
|
+
state = {
|
|
36920
|
+
agentState: "idle",
|
|
36921
|
+
callDurationMs: 0,
|
|
36922
|
+
events: [],
|
|
36923
|
+
isConnected: true,
|
|
36924
|
+
isLiveListening: true,
|
|
36925
|
+
partialTranscript: "",
|
|
36926
|
+
sessionId
|
|
36927
|
+
};
|
|
36928
|
+
if (typeof startedAtOverride === "number") {}
|
|
36929
|
+
notify();
|
|
36930
|
+
},
|
|
36931
|
+
subscribe: (subscriber) => {
|
|
36932
|
+
subscribers.add(subscriber);
|
|
36933
|
+
return () => subscribers.delete(subscriber);
|
|
36934
|
+
}
|
|
36935
|
+
};
|
|
36936
|
+
};
|
|
36937
|
+
// src/client/replayTimeline.ts
|
|
36938
|
+
var categorize = (entry) => {
|
|
36939
|
+
const event = entry.event.toLowerCase();
|
|
36940
|
+
if (event.startsWith("stt.") || event.includes("user"))
|
|
36941
|
+
return "user";
|
|
36942
|
+
if (event.startsWith("tts.") || event.includes("assistant") || event.includes("agent"))
|
|
36943
|
+
return "agent";
|
|
36944
|
+
if (event.startsWith("tool.") || event.includes("tool"))
|
|
36945
|
+
return "tool";
|
|
36946
|
+
return "lifecycle";
|
|
36947
|
+
};
|
|
36948
|
+
var buildReplayTimelineReport = (input) => {
|
|
36949
|
+
const events = [];
|
|
36950
|
+
let summaryAgentTurns = 0;
|
|
36951
|
+
let summaryUserTurns = 0;
|
|
36952
|
+
let summaryToolCalls = 0;
|
|
36953
|
+
for (const entry of input.artifact.timeline ?? []) {
|
|
36954
|
+
const category = categorize(entry);
|
|
36955
|
+
if (category === "user")
|
|
36956
|
+
summaryUserTurns += 1;
|
|
36957
|
+
if (category === "agent")
|
|
36958
|
+
summaryAgentTurns += 1;
|
|
36959
|
+
if (category === "tool")
|
|
36960
|
+
summaryToolCalls += 1;
|
|
36961
|
+
events.push({
|
|
36962
|
+
at: entry.atMs,
|
|
36963
|
+
category,
|
|
36964
|
+
detail: entry.text ?? entry.reason,
|
|
36965
|
+
durationMs: entry.chunkDurationMs,
|
|
36966
|
+
label: entry.event
|
|
36967
|
+
});
|
|
36968
|
+
}
|
|
36969
|
+
events.sort((a, b) => a.at - b.at);
|
|
36970
|
+
const first = events[0]?.at ?? 0;
|
|
36971
|
+
const last = events.at(-1)?.at ?? first;
|
|
36972
|
+
return {
|
|
36973
|
+
duration: last - first,
|
|
36974
|
+
events,
|
|
36975
|
+
metadata: {
|
|
36976
|
+
artifactId: input.artifact.id ?? "",
|
|
36977
|
+
title: input.artifact.title
|
|
36978
|
+
},
|
|
36979
|
+
startedAt: first,
|
|
36980
|
+
summary: {
|
|
36981
|
+
agentTurns: summaryAgentTurns,
|
|
36982
|
+
toolCalls: summaryToolCalls,
|
|
36983
|
+
userTurns: summaryUserTurns
|
|
36984
|
+
}
|
|
36985
|
+
};
|
|
36986
|
+
};
|
|
36987
|
+
// src/retention.ts
|
|
36988
|
+
var defaultResolveAt = (event) => {
|
|
36989
|
+
if (!event || typeof event !== "object")
|
|
36990
|
+
return;
|
|
36991
|
+
const value = event.at;
|
|
36992
|
+
return typeof value === "number" ? value : undefined;
|
|
36993
|
+
};
|
|
36994
|
+
var purgeVoiceRetentionStore = async (store, options, now = Date.now()) => {
|
|
36995
|
+
const resolveAt = options.resolveAt ?? defaultResolveAt;
|
|
36996
|
+
const cutoff = now - Math.max(0, options.maxAgeMs);
|
|
36997
|
+
const records = await Promise.resolve(store.list());
|
|
36998
|
+
const purgedIds = [];
|
|
36999
|
+
let attempted = 0;
|
|
37000
|
+
let removed = 0;
|
|
37001
|
+
let failed = 0;
|
|
37002
|
+
for (const record of records) {
|
|
37003
|
+
const at = resolveAt(record);
|
|
37004
|
+
if (typeof at !== "number" || at >= cutoff)
|
|
37005
|
+
continue;
|
|
37006
|
+
attempted += 1;
|
|
37007
|
+
try {
|
|
37008
|
+
await Promise.resolve(store.remove(record.id));
|
|
37009
|
+
purgedIds.push(record.id);
|
|
37010
|
+
removed += 1;
|
|
37011
|
+
} catch {
|
|
37012
|
+
failed += 1;
|
|
37013
|
+
}
|
|
37014
|
+
}
|
|
37015
|
+
return { attempted, failed, purgedIds, reason: "expired", removed };
|
|
37016
|
+
};
|
|
37017
|
+
var createVoiceRetentionScheduler = (options) => {
|
|
37018
|
+
const intervalMs = Math.max(60000, options.intervalMs ?? 6 * 60 * 60000);
|
|
37019
|
+
let timer;
|
|
37020
|
+
const run = async () => {
|
|
37021
|
+
const report = await purgeVoiceRetentionStore(options.store, options.policy);
|
|
37022
|
+
options.onReport?.(report);
|
|
37023
|
+
};
|
|
37024
|
+
return {
|
|
37025
|
+
start: () => {
|
|
37026
|
+
if (timer)
|
|
37027
|
+
return;
|
|
37028
|
+
timer = setInterval(() => {
|
|
37029
|
+
run();
|
|
37030
|
+
}, intervalMs);
|
|
37031
|
+
},
|
|
37032
|
+
stop: () => {
|
|
37033
|
+
if (!timer)
|
|
37034
|
+
return;
|
|
37035
|
+
clearInterval(timer);
|
|
37036
|
+
timer = undefined;
|
|
37037
|
+
}
|
|
37038
|
+
};
|
|
37039
|
+
};
|
|
36484
37040
|
// src/vapiAdapter.ts
|
|
36485
37041
|
var VAPI_BUILT_IN_VARIABLES = {
|
|
36486
37042
|
date: () => new Date().toISOString().slice(0, 10),
|
|
@@ -37064,7 +37620,7 @@ var assertVoiceAgentSquadContractEvidence = (reports, input = {}) => {
|
|
|
37064
37620
|
return report;
|
|
37065
37621
|
};
|
|
37066
37622
|
// src/turnLatency.ts
|
|
37067
|
-
import { Elysia as
|
|
37623
|
+
import { Elysia as Elysia54 } from "elysia";
|
|
37068
37624
|
var DEFAULT_WARN_AFTER_MS2 = 1800;
|
|
37069
37625
|
var DEFAULT_FAIL_AFTER_MS2 = 3200;
|
|
37070
37626
|
var escapeHtml50 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
@@ -37224,7 +37780,7 @@ var createVoiceTurnLatencyHTMLHandler = (options) => async () => {
|
|
|
37224
37780
|
var createVoiceTurnLatencyRoutes = (options) => {
|
|
37225
37781
|
const path = options.path ?? "/api/turn-latency";
|
|
37226
37782
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
37227
|
-
const routes = new
|
|
37783
|
+
const routes = new Elysia54({
|
|
37228
37784
|
name: options.name ?? "absolutejs-voice-turn-latency"
|
|
37229
37785
|
}).get(path, createVoiceTurnLatencyJSONHandler(options));
|
|
37230
37786
|
if (htmlPath) {
|
|
@@ -37233,7 +37789,7 @@ var createVoiceTurnLatencyRoutes = (options) => {
|
|
|
37233
37789
|
return routes;
|
|
37234
37790
|
};
|
|
37235
37791
|
// src/liveLatency.ts
|
|
37236
|
-
import { Elysia as
|
|
37792
|
+
import { Elysia as Elysia55 } from "elysia";
|
|
37237
37793
|
var escapeHtml51 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
37238
37794
|
var percentile6 = (values, percentileValue) => {
|
|
37239
37795
|
if (values.length === 0) {
|
|
@@ -37307,7 +37863,7 @@ await traceStore.append({
|
|
|
37307
37863
|
var createVoiceLiveLatencyRoutes = (options) => {
|
|
37308
37864
|
const path = options.path ?? "/api/live-latency";
|
|
37309
37865
|
const htmlPath = options.htmlPath === undefined ? "/live-latency" : options.htmlPath;
|
|
37310
|
-
const routes = new
|
|
37866
|
+
const routes = new Elysia55({
|
|
37311
37867
|
name: options.name ?? "absolutejs-voice-live-latency"
|
|
37312
37868
|
}).get(path, () => summarizeVoiceLiveLatency(options));
|
|
37313
37869
|
if (htmlPath) {
|
|
@@ -37324,7 +37880,7 @@ var createVoiceLiveLatencyRoutes = (options) => {
|
|
|
37324
37880
|
return routes;
|
|
37325
37881
|
};
|
|
37326
37882
|
// src/turnQuality.ts
|
|
37327
|
-
import { Elysia as
|
|
37883
|
+
import { Elysia as Elysia56 } from "elysia";
|
|
37328
37884
|
var DEFAULT_CONFIDENCE_WARN_THRESHOLD = 0.72;
|
|
37329
37885
|
var escapeHtml52 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
37330
37886
|
var getTurnLatencyMs = (turn) => {
|
|
@@ -37430,7 +37986,7 @@ var createVoiceTurnQualityHTMLHandler = (options) => async () => {
|
|
|
37430
37986
|
var createVoiceTurnQualityRoutes = (options) => {
|
|
37431
37987
|
const path = options.path ?? "/api/turn-quality";
|
|
37432
37988
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
37433
|
-
const routes = new
|
|
37989
|
+
const routes = new Elysia56({
|
|
37434
37990
|
name: options.name ?? "absolutejs-voice-turn-quality"
|
|
37435
37991
|
}).get(path, createVoiceTurnQualityJSONHandler(options));
|
|
37436
37992
|
if (htmlPath) {
|
|
@@ -37439,10 +37995,10 @@ var createVoiceTurnQualityRoutes = (options) => {
|
|
|
37439
37995
|
return routes;
|
|
37440
37996
|
};
|
|
37441
37997
|
// src/phoneAgent.ts
|
|
37442
|
-
import { Elysia as
|
|
37998
|
+
import { Elysia as Elysia58 } from "elysia";
|
|
37443
37999
|
|
|
37444
38000
|
// src/phoneAgentProductionSmoke.ts
|
|
37445
|
-
import { Elysia as
|
|
38001
|
+
import { Elysia as Elysia57 } from "elysia";
|
|
37446
38002
|
var defaultRequirements = [
|
|
37447
38003
|
"media-started",
|
|
37448
38004
|
"transcript",
|
|
@@ -37585,7 +38141,7 @@ var createVoicePhoneAgentProductionSmokeHTMLHandler = (options) => async ({
|
|
|
37585
38141
|
var createVoicePhoneAgentProductionSmokeRoutes = (options) => {
|
|
37586
38142
|
const path = options.path ?? "/api/voice/phone/smoke-contract";
|
|
37587
38143
|
const htmlPath = options.htmlPath === undefined ? "/voice/phone/smoke-contract" : options.htmlPath;
|
|
37588
|
-
const routes = new
|
|
38144
|
+
const routes = new Elysia57({
|
|
37589
38145
|
name: options.name ?? "absolutejs-voice-phone-smoke-contract"
|
|
37590
38146
|
}).get(path, createVoicePhoneAgentProductionSmokeJSONHandler(options));
|
|
37591
38147
|
if (htmlPath) {
|
|
@@ -37916,7 +38472,7 @@ var createVoicePhoneAgent = (options) => {
|
|
|
37916
38472
|
setupPath: resolveSetupPath(carrier),
|
|
37917
38473
|
smokePath: resolveSmokePath(carrier)
|
|
37918
38474
|
}));
|
|
37919
|
-
const app = new
|
|
38475
|
+
const app = new Elysia58({
|
|
37920
38476
|
name: options.name ?? "absolutejs-voice-phone-agent"
|
|
37921
38477
|
});
|
|
37922
38478
|
for (const carrier of options.carriers) {
|
|
@@ -39725,7 +40281,7 @@ var createOpenAIVoiceTTS = (options) => {
|
|
|
39725
40281
|
};
|
|
39726
40282
|
};
|
|
39727
40283
|
// src/providerCapabilities.ts
|
|
39728
|
-
import { Elysia as
|
|
40284
|
+
import { Elysia as Elysia59 } from "elysia";
|
|
39729
40285
|
var escapeHtml55 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
39730
40286
|
var fromProviderList = (kind, providers, options) => (providers ?? []).map((provider) => ({
|
|
39731
40287
|
configured: true,
|
|
@@ -39828,7 +40384,7 @@ var createVoiceProviderCapabilityHTMLHandler = (options) => async () => {
|
|
|
39828
40384
|
var createVoiceProviderCapabilityRoutes = (options) => {
|
|
39829
40385
|
const path = options.path ?? "/api/provider-capabilities";
|
|
39830
40386
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
39831
|
-
const routes = new
|
|
40387
|
+
const routes = new Elysia59({
|
|
39832
40388
|
name: options.name ?? "absolutejs-voice-provider-capabilities"
|
|
39833
40389
|
}).get(path, createVoiceProviderCapabilityJSONHandler(options));
|
|
39834
40390
|
if (htmlPath) {
|
|
@@ -39837,7 +40393,7 @@ var createVoiceProviderCapabilityRoutes = (options) => {
|
|
|
39837
40393
|
return routes;
|
|
39838
40394
|
};
|
|
39839
40395
|
// src/providerOrchestration.ts
|
|
39840
|
-
import { Elysia as
|
|
40396
|
+
import { Elysia as Elysia60 } from "elysia";
|
|
39841
40397
|
var defaultRequirement = {
|
|
39842
40398
|
minProviders: 1,
|
|
39843
40399
|
requireBudgetPolicy: false,
|
|
@@ -40013,7 +40569,7 @@ var createVoiceProviderOrchestrationRoutes = (options) => {
|
|
|
40013
40569
|
const path = options.path ?? "/api/voice/provider-orchestration";
|
|
40014
40570
|
const htmlPath = options.htmlPath === undefined ? "/voice/provider-orchestration" : options.htmlPath;
|
|
40015
40571
|
const markdownPath = options.markdownPath === undefined ? "/voice/provider-orchestration.md" : options.markdownPath;
|
|
40016
|
-
const routes = new
|
|
40572
|
+
const routes = new Elysia60({
|
|
40017
40573
|
name: options.name ?? "absolutejs-voice-provider-orchestration"
|
|
40018
40574
|
}).get(path, () => buildVoiceProviderOrchestrationReport(options));
|
|
40019
40575
|
if (htmlPath) {
|
|
@@ -40186,7 +40742,7 @@ var assertVoiceProviderRoutingContractEvidence = (reports, input = {}) => {
|
|
|
40186
40742
|
return report;
|
|
40187
40743
|
};
|
|
40188
40744
|
// src/voiceMonitoring.ts
|
|
40189
|
-
import { Elysia as
|
|
40745
|
+
import { Elysia as Elysia61 } from "elysia";
|
|
40190
40746
|
var escapeHtml57 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
40191
40747
|
var issueIdForRun = (run) => `voice-monitor:${run.id}:${run.impactedSessions?.[0] ?? "global"}`;
|
|
40192
40748
|
var rollupStatus5 = (runs) => runs.some((run) => run.status === "fail") ? "fail" : runs.some((run) => run.status === "warn") ? "warn" : "pass";
|
|
@@ -40471,7 +41027,7 @@ var createVoiceMonitorRoutes = (options) => {
|
|
|
40471
41027
|
monitors: options.monitors,
|
|
40472
41028
|
now: options.now
|
|
40473
41029
|
});
|
|
40474
|
-
const routes = new
|
|
41030
|
+
const routes = new Elysia61({
|
|
40475
41031
|
name: options.name ?? "absolutejs-voice-monitoring"
|
|
40476
41032
|
}).get(path, report).get(`${path}.md`, async () => {
|
|
40477
41033
|
return new Response(renderVoiceMonitorMarkdown(await report()), {
|
|
@@ -40518,7 +41074,7 @@ var createVoiceMonitorRoutes = (options) => {
|
|
|
40518
41074
|
};
|
|
40519
41075
|
var createVoiceMonitorRunnerRoutes = (options) => {
|
|
40520
41076
|
const path = options.path ?? "/api/voice/monitor-runner";
|
|
40521
|
-
return new
|
|
41077
|
+
return new Elysia61({
|
|
40522
41078
|
name: options.name ?? "absolutejs-voice-monitor-runner"
|
|
40523
41079
|
}).get(path, () => ({
|
|
40524
41080
|
isRunning: options.runner.isRunning()
|
|
@@ -40906,7 +41462,7 @@ var recommendVoiceReadinessProfile = (options) => {
|
|
|
40906
41462
|
};
|
|
40907
41463
|
};
|
|
40908
41464
|
// src/providerStackRecommendations.ts
|
|
40909
|
-
import { Elysia as
|
|
41465
|
+
import { Elysia as Elysia62 } from "elysia";
|
|
40910
41466
|
var escapeHtml58 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
40911
41467
|
var profileProviderPriorities = {
|
|
40912
41468
|
"meeting-recorder": {
|
|
@@ -41268,7 +41824,7 @@ var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
|
|
|
41268
41824
|
var createVoiceProviderContractMatrixRoutes = (options) => {
|
|
41269
41825
|
const path = options.path ?? "/api/provider-contracts";
|
|
41270
41826
|
const htmlPath = options.htmlPath ?? "/provider-contracts";
|
|
41271
|
-
const routes = new
|
|
41827
|
+
const routes = new Elysia62({
|
|
41272
41828
|
name: options.name ?? "absolutejs-voice-provider-contract-matrix"
|
|
41273
41829
|
});
|
|
41274
41830
|
const jsonHandler = createVoiceProviderContractMatrixJSONHandler(options.matrix);
|
|
@@ -41386,7 +41942,7 @@ var assertVoiceProviderStackEvidence = (report, input = {}) => {
|
|
|
41386
41942
|
return assertion;
|
|
41387
41943
|
};
|
|
41388
41944
|
// src/opsConsoleRoutes.ts
|
|
41389
|
-
import { Elysia as
|
|
41945
|
+
import { Elysia as Elysia63 } from "elysia";
|
|
41390
41946
|
var DEFAULT_LINKS = [
|
|
41391
41947
|
{
|
|
41392
41948
|
description: "Quality gates for CI, deploy checks, and production readiness.",
|
|
@@ -41503,7 +42059,7 @@ var renderVoiceOpsConsoleHTML = (report, options = {}) => {
|
|
|
41503
42059
|
};
|
|
41504
42060
|
var createVoiceOpsConsoleRoutes = (options) => {
|
|
41505
42061
|
const path = options.path ?? "/ops-console";
|
|
41506
|
-
const routes = new
|
|
42062
|
+
const routes = new Elysia63({
|
|
41507
42063
|
name: options.name ?? "absolutejs-voice-ops-console"
|
|
41508
42064
|
});
|
|
41509
42065
|
const getReport = () => buildVoiceOpsConsoleReport(options);
|
|
@@ -41520,7 +42076,7 @@ var createVoiceOpsConsoleRoutes = (options) => {
|
|
|
41520
42076
|
return routes;
|
|
41521
42077
|
};
|
|
41522
42078
|
// src/incidentBundle.ts
|
|
41523
|
-
import { Elysia as
|
|
42079
|
+
import { Elysia as Elysia64 } from "elysia";
|
|
41524
42080
|
var filterIncidentBundleArtifacts = (artifacts, filter = {}) => artifacts.filter((artifact) => {
|
|
41525
42081
|
if (filter.sessionId && artifact.sessionId !== filter.sessionId) {
|
|
41526
42082
|
return false;
|
|
@@ -41737,7 +42293,7 @@ var buildVoiceIncidentBundle = async (options) => {
|
|
|
41737
42293
|
var createVoiceIncidentBundleRoutes = (options) => {
|
|
41738
42294
|
const path = options.path ?? "/api/voice-incidents/:sessionId";
|
|
41739
42295
|
const markdownPath = options.markdownPath === undefined ? "/voice-incidents/:sessionId/markdown" : options.markdownPath;
|
|
41740
|
-
const routes = new
|
|
42296
|
+
const routes = new Elysia64({
|
|
41741
42297
|
name: options.name ?? "absolutejs-voice-incident-bundle"
|
|
41742
42298
|
});
|
|
41743
42299
|
const getSessionId = (params) => params.sessionId ?? "";
|
|
@@ -41938,7 +42494,7 @@ var summarizeVoiceOpsStatus = async (options) => {
|
|
|
41938
42494
|
};
|
|
41939
42495
|
};
|
|
41940
42496
|
// src/opsStatusRoutes.ts
|
|
41941
|
-
import { Elysia as
|
|
42497
|
+
import { Elysia as Elysia65 } from "elysia";
|
|
41942
42498
|
var escapeHtml60 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
41943
42499
|
var renderVoiceOpsStatusHTML = (report, options = {}) => {
|
|
41944
42500
|
const title = options.title ?? "AbsoluteJS Voice Ops Status";
|
|
@@ -41950,7 +42506,7 @@ var renderVoiceOpsStatusHTML = (report, options = {}) => {
|
|
|
41950
42506
|
};
|
|
41951
42507
|
var createVoiceOpsStatusRoutes = (options) => {
|
|
41952
42508
|
const path = options.path ?? "/api/voice/ops-status";
|
|
41953
|
-
const routes = new
|
|
42509
|
+
const routes = new Elysia65({
|
|
41954
42510
|
name: options.name ?? "absolutejs-voice-ops-status"
|
|
41955
42511
|
});
|
|
41956
42512
|
routes.get(path, async () => summarizeVoiceOpsStatus(options));
|
|
@@ -42383,7 +42939,7 @@ var createVoiceTTSProviderRouter = (options) => {
|
|
|
42383
42939
|
};
|
|
42384
42940
|
};
|
|
42385
42941
|
// src/traceDeliveryRoutes.ts
|
|
42386
|
-
import { Elysia as
|
|
42942
|
+
import { Elysia as Elysia66 } from "elysia";
|
|
42387
42943
|
var escapeHtml61 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
42388
42944
|
var getString20 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
42389
42945
|
var getNumber12 = (value) => {
|
|
@@ -42492,7 +43048,7 @@ var createVoiceTraceDeliveryRoutes = (options) => {
|
|
|
42492
43048
|
const path = options.path ?? "/api/voice-trace-deliveries";
|
|
42493
43049
|
const htmlPath = options.htmlPath === undefined ? "/traces/deliveries" : options.htmlPath;
|
|
42494
43050
|
const workerPath = options.workerPath === undefined ? `${path}/drain` : options.workerPath;
|
|
42495
|
-
const routes = new
|
|
43051
|
+
const routes = new Elysia66({
|
|
42496
43052
|
name: options.name ?? "absolutejs-voice-trace-deliveries"
|
|
42497
43053
|
}).get(path, createVoiceTraceDeliveryJSONHandler(options));
|
|
42498
43054
|
if (htmlPath !== false) {
|
|
@@ -42645,7 +43201,7 @@ var createVoiceMemoryStore = () => {
|
|
|
42645
43201
|
return { get, getOrCreate, list, remove, set };
|
|
42646
43202
|
};
|
|
42647
43203
|
// src/opsWebhook.ts
|
|
42648
|
-
import { Elysia as
|
|
43204
|
+
import { Elysia as Elysia67 } from "elysia";
|
|
42649
43205
|
var toHex7 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
42650
43206
|
var signVoiceOpsWebhookBody = async (input) => {
|
|
42651
43207
|
const encoder2 = new TextEncoder;
|
|
@@ -42775,7 +43331,7 @@ var verifyVoiceOpsWebhookSignature = async (input) => {
|
|
|
42775
43331
|
};
|
|
42776
43332
|
var createVoiceOpsWebhookReceiverRoutes = (options = {}) => {
|
|
42777
43333
|
const path = options.path ?? "/api/voice-ops/webhook";
|
|
42778
|
-
return new
|
|
43334
|
+
return new Elysia67().post(path, async ({ body, request, set }) => {
|
|
42779
43335
|
const bodyText = typeof body === "string" ? body : JSON.stringify(body);
|
|
42780
43336
|
if (options.signingSecret) {
|
|
42781
43337
|
const verification = await verifyVoiceOpsWebhookSignature({
|
|
@@ -43230,7 +43786,7 @@ var resolveVoiceOpsPreset = (name, overrides = {}) => {
|
|
|
43230
43786
|
};
|
|
43231
43787
|
};
|
|
43232
43788
|
// src/postCallAnalysis.ts
|
|
43233
|
-
import { Elysia as
|
|
43789
|
+
import { Elysia as Elysia68 } from "elysia";
|
|
43234
43790
|
var isStore = (value) => Boolean(value) && typeof value === "object" && value !== null && ("list" in value);
|
|
43235
43791
|
var asArray = async (value) => Array.isArray(value) ? value : isStore(value) ? await value.list() : [];
|
|
43236
43792
|
var getPathValue3 = (source, path) => {
|
|
@@ -43409,7 +43965,7 @@ var resolvePostCallAnalysisReport = async (options, input) => {
|
|
|
43409
43965
|
};
|
|
43410
43966
|
var createVoicePostCallAnalysisRoutes = (options = {}) => {
|
|
43411
43967
|
const path = options.path ?? "/api/voice/post-call-analysis";
|
|
43412
|
-
const routes = new
|
|
43968
|
+
const routes = new Elysia68({
|
|
43413
43969
|
name: options.name ?? "absolutejs-voice-post-call-analysis"
|
|
43414
43970
|
});
|
|
43415
43971
|
routes.get(path, async ({ query }) => {
|
|
@@ -43434,7 +43990,7 @@ var createVoicePostCallAnalysisRoutes = (options = {}) => {
|
|
|
43434
43990
|
return routes;
|
|
43435
43991
|
};
|
|
43436
43992
|
// src/guardrails.ts
|
|
43437
|
-
import { Elysia as
|
|
43993
|
+
import { Elysia as Elysia69 } from "elysia";
|
|
43438
43994
|
var stringifyContent = (value) => typeof value === "string" ? value : JSON.stringify(value) ?? "";
|
|
43439
43995
|
var appliesToStage = (rule, stage) => !rule.stages || rule.stages.length === 0 || rule.stages.includes(stage);
|
|
43440
43996
|
var matchesRule = async (rule, input) => {
|
|
@@ -43736,7 +44292,7 @@ var resolveGuardrailReport = async (options, input) => {
|
|
|
43736
44292
|
};
|
|
43737
44293
|
var createVoiceGuardrailRoutes = (options = {}) => {
|
|
43738
44294
|
const path = options.path ?? "/api/voice/guardrails";
|
|
43739
|
-
const routes = new
|
|
44295
|
+
const routes = new Elysia69({
|
|
43740
44296
|
name: options.name ?? "absolutejs-voice-guardrails"
|
|
43741
44297
|
});
|
|
43742
44298
|
routes.all(path, async ({ request }) => {
|
|
@@ -44515,7 +45071,7 @@ var shapeTelephonyAssistantText = (text, options = {}) => {
|
|
|
44515
45071
|
return ensureTerminalPunctuation(normalizeWhitespace(limitedChars));
|
|
44516
45072
|
};
|
|
44517
45073
|
// src/proofPack.ts
|
|
44518
|
-
import { Elysia as
|
|
45074
|
+
import { Elysia as Elysia70 } from "elysia";
|
|
44519
45075
|
import { mkdir as mkdir5 } from "fs/promises";
|
|
44520
45076
|
import { dirname as dirname3, join as join4 } from "path";
|
|
44521
45077
|
var toGeneratedAt = (value) => value === undefined ? new Date().toISOString() : typeof value === "number" ? new Date(value).toISOString() : value;
|
|
@@ -45101,7 +45657,7 @@ var createVoiceProofPackArtifacts = (input) => [
|
|
|
45101
45657
|
var createVoiceProofPackRoutes = (options) => {
|
|
45102
45658
|
const jsonPath = options.jsonPath ?? "/api/voice/proof-pack";
|
|
45103
45659
|
const markdownPath = options.markdownPath ?? "/voice/proof-pack.md";
|
|
45104
|
-
const app = new
|
|
45660
|
+
const app = new Elysia70({ name: options.name ?? "voice-proof-pack" });
|
|
45105
45661
|
if (jsonPath !== false) {
|
|
45106
45662
|
app.get(jsonPath, async () => new Response(JSON.stringify(await resolveProofPack(options.source), null, 2), {
|
|
45107
45663
|
headers: {
|
|
@@ -46202,7 +46758,7 @@ var buildVoiceMultilingualProofReadinessCheck = (report, options = {}) => {
|
|
|
46202
46758
|
};
|
|
46203
46759
|
};
|
|
46204
46760
|
// src/monitor.ts
|
|
46205
|
-
import { Elysia as
|
|
46761
|
+
import { Elysia as Elysia71 } from "elysia";
|
|
46206
46762
|
var buildAudioFanout = () => {
|
|
46207
46763
|
const handlers = new Set;
|
|
46208
46764
|
return {
|
|
@@ -46491,7 +47047,7 @@ var createVoiceLiveMonitorRoutes = (options) => {
|
|
|
46491
47047
|
transfer: options.controlHandlers?.transfer ?? buildDefaultControlHandler("transfer"),
|
|
46492
47048
|
voicemail: options.controlHandlers?.voicemail ?? buildDefaultControlHandler("voicemail")
|
|
46493
47049
|
};
|
|
46494
|
-
const app = new
|
|
47050
|
+
const app = new Elysia71({ name: "absolutejs-voice-monitor" });
|
|
46495
47051
|
const unsubscribers = new WeakMap;
|
|
46496
47052
|
if (listenPath !== false && listenPath.length > 0) {
|
|
46497
47053
|
app.ws(`/${listenPath.replace(/^\/+/, "")}`, {
|
|
@@ -46889,6 +47445,7 @@ export {
|
|
|
46889
47445
|
recommendVoiceProviderStack,
|
|
46890
47446
|
recommendVoiceProfileSwitch,
|
|
46891
47447
|
readVoiceProofTrendReportFile,
|
|
47448
|
+
purgeVoiceRetentionStore,
|
|
46892
47449
|
pruneVoiceTraceEvents,
|
|
46893
47450
|
pruneVoiceIncidentBundleArtifacts,
|
|
46894
47451
|
parseVoiceTelephonyWebhookEvent,
|
|
@@ -46969,6 +47526,7 @@ export {
|
|
|
46969
47526
|
describeVoiceIVRPlan,
|
|
46970
47527
|
describeVoiceAssistantMode,
|
|
46971
47528
|
describeVoiceAgentUIState,
|
|
47529
|
+
deriveVoiceRecordingRedactionRanges,
|
|
46972
47530
|
deriveVoiceAgentUIState,
|
|
46973
47531
|
deliverVoiceTraceEventsToSinks,
|
|
46974
47532
|
deliverVoiceObservabilityExport,
|
|
@@ -46978,6 +47536,7 @@ export {
|
|
|
46978
47536
|
deliverVoiceHandoffDelivery,
|
|
46979
47537
|
deliverVoiceHandoff,
|
|
46980
47538
|
deliverVoiceAuditEventsToSinks,
|
|
47539
|
+
defineVoiceAssistant,
|
|
46981
47540
|
decodeTwilioMulawBase64,
|
|
46982
47541
|
deadLetterVoiceOpsTask,
|
|
46983
47542
|
createVoiceZeroRetentionPolicy,
|
|
@@ -47077,7 +47636,9 @@ export {
|
|
|
47077
47636
|
createVoiceS3RecordingStore,
|
|
47078
47637
|
createVoiceS3DeliverySink,
|
|
47079
47638
|
createVoiceRoutingDecisionSummary,
|
|
47639
|
+
createVoiceRouteAuth,
|
|
47080
47640
|
createVoiceReviewSavedEvent,
|
|
47641
|
+
createVoiceRetentionScheduler,
|
|
47081
47642
|
createVoiceResilienceRoutes,
|
|
47082
47643
|
createVoiceRedisTelnyxWebhookEventStore,
|
|
47083
47644
|
createVoiceRedisTelephonyWebhookIdempotencyStore,
|
|
@@ -47226,6 +47787,7 @@ export {
|
|
|
47226
47787
|
createVoiceHandoffDeliveryWorkerLoop,
|
|
47227
47788
|
createVoiceHandoffDeliveryWorker,
|
|
47228
47789
|
createVoiceHandoffDeliveryRecord,
|
|
47790
|
+
createVoiceHMACAuthVerifier,
|
|
47229
47791
|
createVoiceGuardrailRuntime,
|
|
47230
47792
|
createVoiceGuardrailRoutes,
|
|
47231
47793
|
createVoiceGuardrailPolicy,
|
|
@@ -47280,6 +47842,7 @@ export {
|
|
|
47280
47842
|
createVoiceCRMActivitySink,
|
|
47281
47843
|
createVoiceBrowserMediaRoutes,
|
|
47282
47844
|
createVoiceBrowserCallProfileRoutes,
|
|
47845
|
+
createVoiceBearerAuthVerifier,
|
|
47283
47846
|
createVoiceBargeInRoutes,
|
|
47284
47847
|
createVoiceBackchannelDriver,
|
|
47285
47848
|
createVoiceAuditTrailRoutes,
|
|
@@ -47330,7 +47893,9 @@ export {
|
|
|
47330
47893
|
createMemoryVoiceTelnyxWebhookEventStore,
|
|
47331
47894
|
createMemoryVoiceTelephonyWebhookIdempotencyStore,
|
|
47332
47895
|
createMemoryVoicePlivoWebhookNonceStore,
|
|
47896
|
+
createLiveCallViewer,
|
|
47333
47897
|
createJSONVoiceAssistantModel,
|
|
47898
|
+
createInMemoryVoiceCallQuota,
|
|
47334
47899
|
createInMemoryDNCList,
|
|
47335
47900
|
createId,
|
|
47336
47901
|
createGeminiVoiceAssistantModel,
|
|
@@ -47424,6 +47989,7 @@ export {
|
|
|
47424
47989
|
buildVoiceDeliveryRuntimeReport,
|
|
47425
47990
|
buildVoiceDataRetentionPlan,
|
|
47426
47991
|
buildVoiceDataControlReport,
|
|
47992
|
+
buildVoiceCostDashboardReport,
|
|
47427
47993
|
buildVoiceCompetitiveCoverageReport,
|
|
47428
47994
|
buildVoiceCampaignObservabilityReport,
|
|
47429
47995
|
buildVoiceCallerMemoryNamespace,
|
|
@@ -47432,6 +47998,7 @@ export {
|
|
|
47432
47998
|
buildVoiceAuditTrailReport,
|
|
47433
47999
|
buildVoiceAuditExport,
|
|
47434
48000
|
buildVoiceAuditDeliveryReport,
|
|
48001
|
+
buildReplayTimelineReport,
|
|
47435
48002
|
buildOTELTraceId,
|
|
47436
48003
|
buildOTELSpanId,
|
|
47437
48004
|
buildEmptyVoiceProofTrendReport,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Transcript } from "./types";
|
|
2
|
+
import { type VoiceRedactionPattern } from "./redaction";
|
|
3
|
+
export type VoiceRecordingRedactionRange = {
|
|
4
|
+
endMs: number;
|
|
5
|
+
label?: string;
|
|
6
|
+
startMs: number;
|
|
7
|
+
};
|
|
8
|
+
export type DeriveVoiceRecordingRedactionRangesInput = {
|
|
9
|
+
/** When the recording starts in epoch ms; used to convert absolute timestamps if transcripts use them. */
|
|
10
|
+
recordingStartedAtEpochMs?: number;
|
|
11
|
+
/** Optional padding around redaction ranges, in ms (default 100). */
|
|
12
|
+
paddingMs?: number;
|
|
13
|
+
/** Patterns to test against transcripts. Defaults to DEFAULT_VOICE_REDACTION_PATTERNS. */
|
|
14
|
+
patterns?: ReadonlyArray<VoiceRedactionPattern>;
|
|
15
|
+
/** Transcripts to scan. */
|
|
16
|
+
transcripts: ReadonlyArray<Transcript>;
|
|
17
|
+
};
|
|
18
|
+
export declare const deriveVoiceRecordingRedactionRanges: (input: DeriveVoiceRecordingRedactionRangesInput) => VoiceRecordingRedactionRange[];
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type VoiceRetentionPolicyOptions = {
|
|
2
|
+
/** Maximum age in milliseconds; events older than this are eligible for purge. */
|
|
3
|
+
maxAgeMs: number;
|
|
4
|
+
/** Optional override that returns the timestamp for an event. Defaults to event.at. */
|
|
5
|
+
resolveAt?: (event: unknown) => number | undefined;
|
|
6
|
+
};
|
|
7
|
+
export type VoiceRetentionStore<TRecord extends {
|
|
8
|
+
id: string;
|
|
9
|
+
}> = {
|
|
10
|
+
list: (filter?: Record<string, unknown>) => Promise<TRecord[]> | TRecord[];
|
|
11
|
+
remove: (id: string) => Promise<void> | void;
|
|
12
|
+
};
|
|
13
|
+
export type VoicePurgeReport = {
|
|
14
|
+
attempted: number;
|
|
15
|
+
failed: number;
|
|
16
|
+
purgedIds: string[];
|
|
17
|
+
reason: "expired" | "manual";
|
|
18
|
+
removed: number;
|
|
19
|
+
};
|
|
20
|
+
export declare const purgeVoiceRetentionStore: <TRecord extends {
|
|
21
|
+
id: string;
|
|
22
|
+
}>(store: VoiceRetentionStore<TRecord>, options: VoiceRetentionPolicyOptions, now?: number) => Promise<VoicePurgeReport>;
|
|
23
|
+
export type VoiceRetentionScheduler = {
|
|
24
|
+
start: () => void;
|
|
25
|
+
stop: () => void;
|
|
26
|
+
};
|
|
27
|
+
export type CreateVoiceRetentionSchedulerOptions<TRecord extends {
|
|
28
|
+
id: string;
|
|
29
|
+
}> = {
|
|
30
|
+
intervalMs?: number;
|
|
31
|
+
onReport?: (report: VoicePurgeReport) => void;
|
|
32
|
+
policy: VoiceRetentionPolicyOptions;
|
|
33
|
+
store: VoiceRetentionStore<TRecord>;
|
|
34
|
+
};
|
|
35
|
+
export declare const createVoiceRetentionScheduler: <TRecord extends {
|
|
36
|
+
id: string;
|
|
37
|
+
}>(options: CreateVoiceRetentionSchedulerOptions<TRecord>) => VoiceRetentionScheduler;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Elysia } from "elysia";
|
|
2
|
+
export type VoiceRouteAuthDecision = {
|
|
3
|
+
allow: true;
|
|
4
|
+
} | {
|
|
5
|
+
allow: false;
|
|
6
|
+
reason: string;
|
|
7
|
+
status?: number;
|
|
8
|
+
};
|
|
9
|
+
export type VoiceRouteAuthInput = {
|
|
10
|
+
body?: string;
|
|
11
|
+
headers: Headers;
|
|
12
|
+
method: string;
|
|
13
|
+
url: string;
|
|
14
|
+
};
|
|
15
|
+
export type VoiceRouteAuthVerifier = (input: VoiceRouteAuthInput) => Promise<VoiceRouteAuthDecision> | VoiceRouteAuthDecision;
|
|
16
|
+
export type VoiceRouteAuthOptions = {
|
|
17
|
+
bypassPaths?: ReadonlyArray<string>;
|
|
18
|
+
name?: string;
|
|
19
|
+
verify: VoiceRouteAuthVerifier;
|
|
20
|
+
};
|
|
21
|
+
export declare const createVoiceBearerAuthVerifier: (input: {
|
|
22
|
+
expectedToken: string;
|
|
23
|
+
headerName?: string;
|
|
24
|
+
}) => VoiceRouteAuthVerifier;
|
|
25
|
+
export declare const createVoiceHMACAuthVerifier: (input: {
|
|
26
|
+
secret: string;
|
|
27
|
+
toleranceMs?: number;
|
|
28
|
+
}) => VoiceRouteAuthVerifier;
|
|
29
|
+
export declare const createVoiceRouteAuth: (options: VoiceRouteAuthOptions) => Elysia<"", {
|
|
30
|
+
decorator: {};
|
|
31
|
+
store: {};
|
|
32
|
+
derive: {};
|
|
33
|
+
resolve: {};
|
|
34
|
+
}, {
|
|
35
|
+
typebox: {};
|
|
36
|
+
error: {};
|
|
37
|
+
}, {
|
|
38
|
+
schema: {};
|
|
39
|
+
standaloneSchema: {};
|
|
40
|
+
macro: {};
|
|
41
|
+
macroFn: {};
|
|
42
|
+
parser: {};
|
|
43
|
+
response: {};
|
|
44
|
+
}, {}, {
|
|
45
|
+
derive: {};
|
|
46
|
+
resolve: {};
|
|
47
|
+
schema: {};
|
|
48
|
+
standaloneSchema: {};
|
|
49
|
+
response: {};
|
|
50
|
+
}, {
|
|
51
|
+
derive: {};
|
|
52
|
+
resolve: {};
|
|
53
|
+
schema: {};
|
|
54
|
+
standaloneSchema: {};
|
|
55
|
+
response: {
|
|
56
|
+
200: Response;
|
|
57
|
+
};
|
|
58
|
+
}>;
|