@absolutejs/voice 0.0.22-beta.485 → 0.0.22-beta.487

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.
@@ -0,0 +1,37 @@
1
+ import type { Transcript, VoiceSessionRecord, VoiceTurnRecord } from "./types";
2
+ import type { VoiceAssistantMemoryNamespaceInput } from "./assistantMemory";
3
+ export type VoiceCallerIdentity = {
4
+ email?: string;
5
+ externalId?: string;
6
+ phone?: string;
7
+ };
8
+ export type VoiceCallerMemorySnapshot = {
9
+ facts: Record<string, string>;
10
+ identity: VoiceCallerIdentity;
11
+ lastSessionAt: number;
12
+ openActions: string[];
13
+ summary: string;
14
+ };
15
+ export declare const VOICE_CALLER_MEMORY_KEY = "caller-memory-snapshot";
16
+ export declare const buildVoiceCallerMemoryNamespace: (identity: VoiceCallerIdentity | undefined, prefix?: string) => string;
17
+ export type CreateVoiceCallerMemoryNamespaceOptions<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord> = {
18
+ identifyCaller: (input: VoiceAssistantMemoryNamespaceInput<TContext, TSession>) => Promise<VoiceCallerIdentity | undefined> | VoiceCallerIdentity | undefined;
19
+ prefix?: string;
20
+ };
21
+ export declare const createVoiceCallerMemoryNamespace: <TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord>(options: CreateVoiceCallerMemoryNamespaceOptions<TContext, TSession>) => (input: VoiceAssistantMemoryNamespaceInput<TContext, TSession>) => Promise<string>;
22
+ export type VoiceCallerMemoryCompletion = (input: {
23
+ prompt: string;
24
+ systemPrompt?: string;
25
+ }) => Promise<string>;
26
+ export type SummarizeVoiceCallerTranscriptOptions = {
27
+ completion: VoiceCallerMemoryCompletion;
28
+ previousSnapshot?: VoiceCallerMemorySnapshot;
29
+ systemPrompt?: string;
30
+ };
31
+ export type VoiceCallerMemorySummarizerInput = {
32
+ identity: VoiceCallerIdentity;
33
+ options: SummarizeVoiceCallerTranscriptOptions;
34
+ transcripts?: Transcript[];
35
+ turns: VoiceTurnRecord[];
36
+ };
37
+ export declare const summarizeVoiceCallerTranscript: (input: VoiceCallerMemorySummarizerInput) => Promise<VoiceCallerMemorySnapshot>;
package/dist/index.d.ts CHANGED
@@ -81,6 +81,10 @@ export { describeVoiceAssistantMode, resolveVoiceAssistantMode, } from "./assist
81
81
  export type { VoiceAssistantMode, VoiceAssistantModality, VoiceAssistantModeDescriptor, VoiceSemanticVADConfig, } from "./assistantMode";
82
82
  export { createPunctuationSemanticTurnDetector, createRegexSemanticTurnDetector, } from "./semanticTurn";
83
83
  export { VOICE_WEBHOOK_SIGNATURE_HEADER, VOICE_WEBHOOK_TIMESTAMP_HEADER, extractVoiceWebhookSignatureFromHeaders, signVoiceWebhookBody, verifyVoiceWebhookSignature, } from "./webhookVerification";
84
+ export { VOICE_CALLER_MEMORY_KEY, buildVoiceCallerMemoryNamespace, createVoiceCallerMemoryNamespace, summarizeVoiceCallerTranscript, } from "./callerMemory";
85
+ export type { CreateVoiceCallerMemoryNamespaceOptions, SummarizeVoiceCallerTranscriptOptions, VoiceCallerIdentity, VoiceCallerMemoryCompletion, VoiceCallerMemorySnapshot, VoiceCallerMemorySummarizerInput, } from "./callerMemory";
86
+ export { aggregateVoiceTurnLatencySpans, buildOTELSpanId, buildOTELTraceId, buildVoiceOTELPayload, createVoiceOTELHTTPExporter, } from "./otelExporter";
87
+ export type { VoiceOTELAttribute, VoiceOTELExporter, VoiceOTELExporterOptions, VoiceOTELPayload, VoiceOTELResourceSpans, VoiceOTELSpan, VoiceTurnLatencySpanSet, VoiceTurnLatencySpanStage, } from "./otelExporter";
84
88
  export type { VoiceWebhookVerificationInput, VoiceWebhookVerificationReason, VoiceWebhookVerificationResult, } from "./webhookVerification";
85
89
  export type { CreatePunctuationSemanticTurnDetectorOptions, CreateRegexSemanticTurnDetectorOptions, VoiceSemanticTurnDetector, VoiceSemanticTurnInput, VoiceSemanticTurnVerdict, } from "./semanticTurn";
86
90
  export { createMonologueAMDDetector } from "./amdDetector";
package/dist/index.js CHANGED
@@ -35475,6 +35475,234 @@ var extractVoiceWebhookSignatureFromHeaders = (headers) => {
35475
35475
  timestamp: get(VOICE_WEBHOOK_TIMESTAMP_HEADER)
35476
35476
  };
35477
35477
  };
35478
+ // src/callerMemory.ts
35479
+ var VOICE_CALLER_MEMORY_KEY = "caller-memory-snapshot";
35480
+ var normalizeIdentifier = (value) => value.trim().replace(/[^a-zA-Z0-9+@._-]/g, "-").toLowerCase();
35481
+ var buildVoiceCallerMemoryNamespace = (identity, prefix = "caller") => {
35482
+ const identifier = identity?.externalId ?? identity?.phone ?? identity?.email ?? "anonymous";
35483
+ return `${prefix}:${normalizeIdentifier(identifier)}`;
35484
+ };
35485
+ var createVoiceCallerMemoryNamespace = (options) => async (input) => {
35486
+ const identity = await Promise.resolve(options.identifyCaller(input));
35487
+ return buildVoiceCallerMemoryNamespace(identity, options.prefix);
35488
+ };
35489
+ var DEFAULT_SYSTEM_PROMPT2 = "You write structured caller memory snapshots for a voice agent. " + "Given the latest call transcript (and an optional previous snapshot), " + "merge them into JSON with shape " + '{"summary":"\u2026","facts":{"key":"value"},"openActions":["\u2026"]}. ' + "Keep summary under 240 chars. Facts must be short value strings (name, plan, last_issue). " + "openActions are concrete follow-ups still pending. JSON only.";
35490
+ var buildTranscriptBlock = (turns) => turns.map((turn, index) => {
35491
+ const userText = turn.text.trim();
35492
+ const assistantText = typeof turn.assistantText === "string" ? turn.assistantText.trim() : "";
35493
+ const lines = [`Turn ${index + 1}:`];
35494
+ if (userText) {
35495
+ lines.push(` user: ${userText}`);
35496
+ }
35497
+ if (assistantText) {
35498
+ lines.push(` agent: ${assistantText}`);
35499
+ }
35500
+ return lines.join(`
35501
+ `);
35502
+ }).join(`
35503
+ `);
35504
+ var extractJson2 = (raw) => {
35505
+ const trimmed = raw.trim();
35506
+ if (!trimmed) {
35507
+ throw new Error("Caller-memory summarizer returned empty response");
35508
+ }
35509
+ const fenced = /```(?:json)?\s*([\s\S]*?)```/i.exec(trimmed);
35510
+ const candidate = fenced ? fenced[1].trim() : trimmed;
35511
+ try {
35512
+ return JSON.parse(candidate);
35513
+ } catch {
35514
+ const start = candidate.indexOf("{");
35515
+ const end = candidate.lastIndexOf("}");
35516
+ if (start >= 0 && end > start) {
35517
+ return JSON.parse(candidate.slice(start, end + 1));
35518
+ }
35519
+ throw new Error("Caller-memory response was not valid JSON");
35520
+ }
35521
+ };
35522
+ var coerceFacts = (input) => {
35523
+ if (!input || typeof input !== "object") {
35524
+ return {};
35525
+ }
35526
+ const out = {};
35527
+ for (const [key, value] of Object.entries(input)) {
35528
+ if (typeof value === "string") {
35529
+ out[key] = value;
35530
+ } else if (value !== null && value !== undefined) {
35531
+ out[key] = String(value);
35532
+ }
35533
+ }
35534
+ return out;
35535
+ };
35536
+ var coerceActions = (input) => {
35537
+ if (!Array.isArray(input)) {
35538
+ return [];
35539
+ }
35540
+ return input.map((value) => typeof value === "string" ? value.trim() : "").filter((value) => value.length > 0);
35541
+ };
35542
+ var summarizeVoiceCallerTranscript = async (input) => {
35543
+ const transcriptBlock = buildTranscriptBlock(input.turns);
35544
+ const previousBlock = input.options.previousSnapshot ? `Previous snapshot:
35545
+ ${JSON.stringify(input.options.previousSnapshot, null, 2)}
35546
+
35547
+ ` : "";
35548
+ const prompt = `${previousBlock}Latest call transcript:
35549
+ ${transcriptBlock}
35550
+
35551
+ Return JSON only.`;
35552
+ const raw = await input.options.completion({
35553
+ prompt,
35554
+ systemPrompt: input.options.systemPrompt ?? DEFAULT_SYSTEM_PROMPT2
35555
+ });
35556
+ const parsed = extractJson2(raw);
35557
+ if (!parsed || typeof parsed !== "object") {
35558
+ throw new Error("Caller-memory summarizer returned non-object JSON");
35559
+ }
35560
+ const record = parsed;
35561
+ return {
35562
+ facts: coerceFacts(record.facts),
35563
+ identity: input.identity,
35564
+ lastSessionAt: Date.now(),
35565
+ openActions: coerceActions(record.openActions ?? record.open_actions),
35566
+ summary: typeof record.summary === "string" ? record.summary : ""
35567
+ };
35568
+ };
35569
+ // src/otelExporter.ts
35570
+ var SCOPE_NAME = "@absolutejs/voice";
35571
+ var SPAN_KIND_INTERNAL = 1;
35572
+ var STATUS_OK = 1;
35573
+ var HEX_CHARS = "0123456789abcdef";
35574
+ var hashToHex = (input, length) => {
35575
+ let hash = 0xcbf29ce4_84222325n;
35576
+ const prime = 0x100000001_b3n;
35577
+ for (let index = 0;index < input.length; index += 1) {
35578
+ hash ^= BigInt(input.charCodeAt(index));
35579
+ hash = hash * prime & 0xffffffffffffffffn;
35580
+ }
35581
+ let hex = hash.toString(16).padStart(16, "0");
35582
+ while (hex.length < length) {
35583
+ let next = 0n;
35584
+ for (const ch of hex) {
35585
+ next = next * 31n + BigInt(HEX_CHARS.indexOf(ch)) & 0xffffffffffffffffn;
35586
+ }
35587
+ hex += next.toString(16).padStart(16, "0");
35588
+ }
35589
+ return hex.slice(0, length);
35590
+ };
35591
+ var buildOTELTraceId = (sessionId) => hashToHex(`voice-trace:${sessionId}`, 32);
35592
+ var buildOTELSpanId = (sessionId, suffix) => hashToHex(`voice-span:${sessionId}:${suffix}`, 16);
35593
+ var toUnixNano = (ms) => `${Math.trunc(ms * 1e6)}`;
35594
+ var stringAttr = (key, value) => ({
35595
+ key,
35596
+ value: { stringValue: value }
35597
+ });
35598
+ var aggregateVoiceTurnLatencySpans = (events) => {
35599
+ const byTurn = new Map;
35600
+ for (const event of events) {
35601
+ if (event.type !== "turn_latency.stage" || !event.turnId) {
35602
+ continue;
35603
+ }
35604
+ const stage = typeof event.payload?.stage === "string" ? event.payload.stage : undefined;
35605
+ if (!stage) {
35606
+ continue;
35607
+ }
35608
+ const key = `${event.sessionId}::${event.turnId}`;
35609
+ const existing = byTurn.get(key);
35610
+ if (!existing) {
35611
+ byTurn.set(key, {
35612
+ endedAt: event.at,
35613
+ scenarioId: event.scenarioId,
35614
+ sessionId: event.sessionId,
35615
+ stages: [{ at: event.at, stage }],
35616
+ startedAt: event.at,
35617
+ turnId: event.turnId
35618
+ });
35619
+ continue;
35620
+ }
35621
+ existing.stages.push({ at: event.at, stage });
35622
+ existing.startedAt = Math.min(existing.startedAt, event.at);
35623
+ existing.endedAt = Math.max(existing.endedAt, event.at);
35624
+ }
35625
+ return Array.from(byTurn.values()).sort((left, right) => left.startedAt - right.startedAt);
35626
+ };
35627
+ var buildVoiceOTELPayload = (spanSets, options = {}) => {
35628
+ const resourceAttributes = [
35629
+ stringAttr("service.name", options.serviceName ?? "absolutejs-voice")
35630
+ ];
35631
+ for (const [key, value] of Object.entries(options.resourceAttributes ?? {})) {
35632
+ resourceAttributes.push(stringAttr(key, value));
35633
+ }
35634
+ const spans = [];
35635
+ for (const set of spanSets) {
35636
+ const traceId = buildOTELTraceId(set.sessionId);
35637
+ const parentSpanId = buildOTELSpanId(set.sessionId, `turn:${set.turnId}`);
35638
+ spans.push({
35639
+ attributes: [
35640
+ stringAttr("voice.session_id", set.sessionId),
35641
+ stringAttr("voice.turn_id", set.turnId),
35642
+ ...set.scenarioId ? [stringAttr("voice.scenario_id", set.scenarioId)] : []
35643
+ ],
35644
+ endTimeUnixNano: toUnixNano(set.endedAt),
35645
+ kind: SPAN_KIND_INTERNAL,
35646
+ name: "voice.turn",
35647
+ spanId: parentSpanId,
35648
+ startTimeUnixNano: toUnixNano(set.startedAt),
35649
+ status: { code: STATUS_OK },
35650
+ traceId
35651
+ });
35652
+ for (let index = 0;index < set.stages.length; index += 1) {
35653
+ const stage = set.stages[index];
35654
+ const next = set.stages[index + 1];
35655
+ const endsAt = next ? next.at : set.endedAt;
35656
+ spans.push({
35657
+ attributes: [
35658
+ stringAttr("voice.session_id", set.sessionId),
35659
+ stringAttr("voice.turn_id", set.turnId),
35660
+ stringAttr("voice.stage", stage.stage)
35661
+ ],
35662
+ endTimeUnixNano: toUnixNano(endsAt),
35663
+ kind: SPAN_KIND_INTERNAL,
35664
+ name: `voice.turn.stage.${stage.stage}`,
35665
+ parentSpanId,
35666
+ spanId: buildOTELSpanId(set.sessionId, `${set.turnId}:${stage.stage}:${index}`),
35667
+ startTimeUnixNano: toUnixNano(stage.at),
35668
+ status: { code: STATUS_OK },
35669
+ traceId
35670
+ });
35671
+ }
35672
+ }
35673
+ return {
35674
+ resourceSpans: [
35675
+ {
35676
+ resource: { attributes: resourceAttributes },
35677
+ scopeSpans: [{ scope: { name: SCOPE_NAME }, spans }]
35678
+ }
35679
+ ]
35680
+ };
35681
+ };
35682
+ var createVoiceOTELHTTPExporter = (options) => {
35683
+ const fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
35684
+ return {
35685
+ export: async (events) => {
35686
+ const spanSets = aggregateVoiceTurnLatencySpans(events);
35687
+ if (spanSets.length === 0) {
35688
+ return { ok: true };
35689
+ }
35690
+ const payload = buildVoiceOTELPayload(spanSets, {
35691
+ resourceAttributes: options.resourceAttributes,
35692
+ serviceName: options.serviceName
35693
+ });
35694
+ const response = await fetchImpl(options.url, {
35695
+ body: JSON.stringify(payload),
35696
+ headers: {
35697
+ "content-type": "application/json",
35698
+ ...options.headers
35699
+ },
35700
+ method: "POST"
35701
+ });
35702
+ return { ok: response.ok, status: response.status };
35703
+ }
35704
+ };
35705
+ };
35478
35706
  // src/amdDetector.ts
35479
35707
  var createMonologueAMDDetector = (options = {}) => {
35480
35708
  const minMonologueMs = options.minMonologueMs ?? 8000;
@@ -46085,6 +46313,7 @@ export {
46085
46313
  summarizeVoiceHandoffHealth,
46086
46314
  summarizeVoiceHandoffDeliveries,
46087
46315
  summarizeVoiceCampaigns,
46316
+ summarizeVoiceCallerTranscript,
46088
46317
  summarizeVoiceBrowserMedia,
46089
46318
  summarizeVoiceBargeIn,
46090
46319
  summarizeVoiceAuditTrail,
@@ -46540,6 +46769,7 @@ export {
46540
46769
  createVoiceObservabilityExportSchema,
46541
46770
  createVoiceObservabilityExportRoutes,
46542
46771
  createVoiceObservabilityExportReplayRoutes,
46772
+ createVoiceOTELHTTPExporter,
46543
46773
  createVoiceMonitorWebhookNotifier,
46544
46774
  createVoiceMonitorSession,
46545
46775
  createVoiceMonitorRuntimeBinding,
@@ -46632,6 +46862,7 @@ export {
46632
46862
  createVoiceCampaignTelephonyOutcomeHandler,
46633
46863
  createVoiceCampaignRoutes,
46634
46864
  createVoiceCampaign,
46865
+ createVoiceCallerMemoryNamespace,
46635
46866
  createVoiceCallReviewRecorder,
46636
46867
  createVoiceCallReviewFromSession,
46637
46868
  createVoiceCallReviewFromLiveTelephonyReport,
@@ -46758,6 +46989,7 @@ export {
46758
46989
  buildVoiceObservabilityExportDeliveryHistory,
46759
46990
  buildVoiceObservabilityExport,
46760
46991
  buildVoiceObservabilityArtifactIndex,
46992
+ buildVoiceOTELPayload,
46761
46993
  buildVoiceMultilingualProofReadinessCheck,
46762
46994
  buildVoiceMonitorRunReport,
46763
46995
  buildVoiceMonitorPlan,
@@ -46783,11 +47015,14 @@ export {
46783
47015
  buildVoiceDataControlReport,
46784
47016
  buildVoiceCompetitiveCoverageReport,
46785
47017
  buildVoiceCampaignObservabilityReport,
47018
+ buildVoiceCallerMemoryNamespace,
46786
47019
  buildVoiceCallDebuggerReport,
46787
47020
  buildVoiceBrowserCallProfileReport,
46788
47021
  buildVoiceAuditTrailReport,
46789
47022
  buildVoiceAuditExport,
46790
47023
  buildVoiceAuditDeliveryReport,
47024
+ buildOTELTraceId,
47025
+ buildOTELSpanId,
46791
47026
  buildEmptyVoiceProofTrendReport,
46792
47027
  assignVoiceOpsTask,
46793
47028
  assertVoiceToolContractEvidence,
@@ -46838,10 +47073,12 @@ export {
46838
47073
  appendVoiceRealCallProfileRecoveryEvidence,
46839
47074
  appendVoiceProviderRouterTraceEvent,
46840
47075
  appendVoiceIOProviderRouterTraceEvent,
47076
+ aggregateVoiceTurnLatencySpans,
46841
47077
  acknowledgeVoiceMonitorIssue,
46842
47078
  VOICE_WEBHOOK_TIMESTAMP_HEADER,
46843
47079
  VOICE_WEBHOOK_SIGNATURE_HEADER,
46844
47080
  VOICE_LIVE_OPS_ACTIONS,
47081
+ VOICE_CALLER_MEMORY_KEY,
46845
47082
  TURN_PROFILE_DEFAULTS,
46846
47083
  DEFAULT_VOICE_REDACTION_PATTERNS,
46847
47084
  DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS,
@@ -0,0 +1,83 @@
1
+ import type { StoredVoiceTraceEvent } from "./trace";
2
+ export type VoiceOTELAttribute = {
3
+ key: string;
4
+ value: {
5
+ boolValue: boolean;
6
+ };
7
+ } | {
8
+ key: string;
9
+ value: {
10
+ doubleValue: number;
11
+ };
12
+ } | {
13
+ key: string;
14
+ value: {
15
+ intValue: string;
16
+ };
17
+ } | {
18
+ key: string;
19
+ value: {
20
+ stringValue: string;
21
+ };
22
+ };
23
+ export type VoiceOTELSpan = {
24
+ attributes: VoiceOTELAttribute[];
25
+ endTimeUnixNano: string;
26
+ kind: number;
27
+ name: string;
28
+ parentSpanId?: string;
29
+ spanId: string;
30
+ startTimeUnixNano: string;
31
+ status: {
32
+ code: number;
33
+ };
34
+ traceId: string;
35
+ };
36
+ export type VoiceOTELResourceSpans = {
37
+ resource: {
38
+ attributes: VoiceOTELAttribute[];
39
+ };
40
+ scopeSpans: ReadonlyArray<{
41
+ scope: {
42
+ name: string;
43
+ version?: string;
44
+ };
45
+ spans: VoiceOTELSpan[];
46
+ }>;
47
+ };
48
+ export type VoiceOTELPayload = {
49
+ resourceSpans: VoiceOTELResourceSpans[];
50
+ };
51
+ export type VoiceTurnLatencySpanStage = {
52
+ at: number;
53
+ stage: string;
54
+ };
55
+ export type VoiceTurnLatencySpanSet = {
56
+ endedAt: number;
57
+ scenarioId?: string;
58
+ sessionId: string;
59
+ stages: VoiceTurnLatencySpanStage[];
60
+ startedAt: number;
61
+ turnId: string;
62
+ };
63
+ export declare const buildOTELTraceId: (sessionId: string) => string;
64
+ export declare const buildOTELSpanId: (sessionId: string, suffix: string) => string;
65
+ export declare const aggregateVoiceTurnLatencySpans: (events: StoredVoiceTraceEvent[]) => VoiceTurnLatencySpanSet[];
66
+ export type VoiceOTELExporterOptions = {
67
+ fetch?: typeof fetch;
68
+ headers?: Record<string, string>;
69
+ resourceAttributes?: Record<string, string>;
70
+ serviceName?: string;
71
+ url: string;
72
+ };
73
+ export declare const buildVoiceOTELPayload: (spanSets: VoiceTurnLatencySpanSet[], options?: {
74
+ resourceAttributes?: Record<string, string>;
75
+ serviceName?: string;
76
+ }) => VoiceOTELPayload;
77
+ export type VoiceOTELExporter = {
78
+ export: (events: StoredVoiceTraceEvent[]) => Promise<{
79
+ ok: boolean;
80
+ status?: number;
81
+ }>;
82
+ };
83
+ export declare const createVoiceOTELHTTPExporter: (options: VoiceOTELExporterOptions) => VoiceOTELExporter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.485",
3
+ "version": "0.0.22-beta.487",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",