@absolutejs/voice 0.0.22-beta.134 → 0.0.22-beta.136
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.js +44 -2
- package/dist/opsStatus.d.ts +3 -0
- package/dist/productionReadiness.d.ts +2 -0
- package/dist/sessionReplay.d.ts +10 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12447,6 +12447,26 @@ var escapeHtml20 = (value) => value.replaceAll("&", "&").replaceAll("<", "&l
|
|
|
12447
12447
|
var increment4 = (record, key) => {
|
|
12448
12448
|
record[key] = (record[key] ?? 0) + 1;
|
|
12449
12449
|
};
|
|
12450
|
+
var isProviderErrorEvent = (event) => event.type === "session.error" && (event.payload.providerStatus === "error" || typeof event.payload.error === "string");
|
|
12451
|
+
var isRecoveredProviderFallbackEvent = (event) => event.type === "session.error" && (event.payload.providerStatus === "fallback" || event.payload.status === "fallback") && event.payload.recovered === true;
|
|
12452
|
+
var turnRecoveryKey = (event) => event.turnId ?? `session:${event.sessionId}`;
|
|
12453
|
+
var summarizeVoiceProviderFallbackRecovery = (events) => {
|
|
12454
|
+
const sorted = filterVoiceTraceEvents(events);
|
|
12455
|
+
const recoveredEvents = sorted.filter(isRecoveredProviderFallbackEvent);
|
|
12456
|
+
const recoveredKeys = new Set(recoveredEvents.map((event) => turnRecoveryKey(event)));
|
|
12457
|
+
const recoveredSessions = new Set(recoveredEvents.map((event) => event.sessionId));
|
|
12458
|
+
const unresolvedErrorEvents = sorted.filter((event) => isProviderErrorEvent(event) && !recoveredKeys.has(turnRecoveryKey(event)));
|
|
12459
|
+
const unresolvedSessions = new Set(unresolvedErrorEvents.map((event) => event.sessionId));
|
|
12460
|
+
return {
|
|
12461
|
+
recovered: recoveredEvents.length,
|
|
12462
|
+
recoveredSessions: recoveredSessions.size,
|
|
12463
|
+
recoveredTurns: recoveredKeys.size,
|
|
12464
|
+
status: unresolvedErrorEvents.length > 0 ? "fail" : "pass",
|
|
12465
|
+
total: recoveredEvents.length + unresolvedErrorEvents.length,
|
|
12466
|
+
unresolvedErrors: unresolvedErrorEvents.length,
|
|
12467
|
+
unresolvedSessions: unresolvedSessions.size
|
|
12468
|
+
};
|
|
12469
|
+
};
|
|
12450
12470
|
var buildReplayTurns = (events) => {
|
|
12451
12471
|
const turns = new Map;
|
|
12452
12472
|
const getTurn = (turnId) => {
|
|
@@ -12547,12 +12567,13 @@ var summarizeVoiceSessions = async (options = {}) => {
|
|
|
12547
12567
|
const providers = new Set;
|
|
12548
12568
|
let latestOutcome;
|
|
12549
12569
|
let errorCount = 0;
|
|
12570
|
+
const recoveredTurns = new Set(sorted.filter(isRecoveredProviderFallbackEvent).map((event) => turnRecoveryKey(event)));
|
|
12550
12571
|
for (const event of sorted) {
|
|
12551
12572
|
const provider = getString9(event.payload.provider);
|
|
12552
12573
|
if (provider) {
|
|
12553
12574
|
providers.add(provider);
|
|
12554
12575
|
}
|
|
12555
|
-
if (event
|
|
12576
|
+
if (isProviderErrorEvent(event) && !recoveredTurns.has(turnRecoveryKey(event))) {
|
|
12556
12577
|
errorCount += 1;
|
|
12557
12578
|
increment4(providerErrors, provider ?? "unknown");
|
|
12558
12579
|
}
|
|
@@ -19641,6 +19662,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
19641
19662
|
const routingEvents = listVoiceRoutingEvents(events);
|
|
19642
19663
|
const routingSessions = summarizeVoiceRoutingSessions(routingEvents);
|
|
19643
19664
|
const liveLatency = summarizeLiveLatency(events, options);
|
|
19665
|
+
const providerRecovery = summarizeVoiceProviderFallbackRecovery(events);
|
|
19644
19666
|
const [
|
|
19645
19667
|
quality,
|
|
19646
19668
|
providers,
|
|
@@ -19714,6 +19736,20 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
19714
19736
|
}
|
|
19715
19737
|
] : []
|
|
19716
19738
|
},
|
|
19739
|
+
{
|
|
19740
|
+
detail: providerRecovery.unresolvedErrors > 0 ? `${providerRecovery.unresolvedErrors} provider error(s) have no recovered fallback evidence.` : providerRecovery.recovered > 0 ? `${providerRecovery.recovered} provider fallback recovery event(s) kept sessions healthy.` : "No provider fallback recovery was needed in the current trace window.",
|
|
19741
|
+
href: options.links?.resilience ?? "/resilience",
|
|
19742
|
+
label: "Provider fallback recovery",
|
|
19743
|
+
status: providerRecovery.status,
|
|
19744
|
+
value: providerRecovery.total === 0 ? "0 events" : `${providerRecovery.recovered}/${providerRecovery.total}`,
|
|
19745
|
+
actions: providerRecovery.status === "pass" ? [] : [
|
|
19746
|
+
{
|
|
19747
|
+
description: "Open provider resilience traces and inspect unresolved provider errors.",
|
|
19748
|
+
href: options.links?.resilience ?? "/resilience",
|
|
19749
|
+
label: "Open provider recovery"
|
|
19750
|
+
}
|
|
19751
|
+
]
|
|
19752
|
+
},
|
|
19717
19753
|
{
|
|
19718
19754
|
detail: failedSessions === 0 ? sessions.length > 0 ? "Recent sessions have no recorded provider/session failures." : "No sessions have been recorded yet; run a smoke or live session for proof." : `${failedSessions} recent session(s) have failures.`,
|
|
19719
19755
|
href: options.links?.sessions ?? "/sessions",
|
|
@@ -20003,6 +20039,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
20003
20039
|
degraded: degradedProviders,
|
|
20004
20040
|
total: providers.length
|
|
20005
20041
|
},
|
|
20042
|
+
providerRecovery,
|
|
20006
20043
|
phoneAgentSmokes: phoneAgentSmokeSummary,
|
|
20007
20044
|
providerRoutingContracts: providerRoutingContractSummary,
|
|
20008
20045
|
reconnectContracts: reconnectContractSummary,
|
|
@@ -20281,6 +20318,7 @@ var summarizeVoiceOpsStatus = async (options) => {
|
|
|
20281
20318
|
events
|
|
20282
20319
|
})
|
|
20283
20320
|
]);
|
|
20321
|
+
const providerRecovery = shouldInclude("providerRecovery") ? summarizeVoiceProviderFallbackRecovery(events) : undefined;
|
|
20284
20322
|
const surfaces = {};
|
|
20285
20323
|
const statuses = [];
|
|
20286
20324
|
if (quality) {
|
|
@@ -20301,6 +20339,10 @@ var summarizeVoiceOpsStatus = async (options) => {
|
|
|
20301
20339
|
};
|
|
20302
20340
|
statuses.push(status);
|
|
20303
20341
|
}
|
|
20342
|
+
if (providerRecovery) {
|
|
20343
|
+
surfaces.providerRecovery = providerRecovery;
|
|
20344
|
+
statuses.push(providerRecovery.status);
|
|
20345
|
+
}
|
|
20304
20346
|
if (sessions) {
|
|
20305
20347
|
const failed = sessions.filter((session) => session.status === "failed").length;
|
|
20306
20348
|
const status = failed > 0 ? "fail" : "pass";
|
|
@@ -20334,7 +20376,7 @@ var escapeHtml34 = (value) => value.replaceAll("&", "&").replaceAll("<", "&l
|
|
|
20334
20376
|
var renderVoiceOpsStatusHTML = (report, options = {}) => {
|
|
20335
20377
|
const title = options.title ?? "AbsoluteJS Voice Ops Status";
|
|
20336
20378
|
const surfaces = Object.entries(report.surfaces).map(([key, surface]) => {
|
|
20337
|
-
const value = "total" in surface ? `${Math.max(surface.total - ("failed" in surface ? surface.failed : ("degraded" in surface) ? surface.degraded : 0), 0)}/${surface.total}` : surface.status;
|
|
20379
|
+
const value = "recovered" in surface ? surface.total === 0 ? "0 events" : `${surface.recovered}/${surface.total}` : ("total" in surface) ? `${Math.max(surface.total - ("failed" in surface ? surface.failed : ("degraded" in surface) ? surface.degraded : 0), 0)}/${surface.total}` : surface.status;
|
|
20338
20380
|
return `<article class="surface ${escapeHtml34(surface.status)}"><span>${escapeHtml34(surface.status.toUpperCase())}</span><h2>${escapeHtml34(key)}</h2><strong>${escapeHtml34(value)}</strong></article>`;
|
|
20339
20381
|
}).join("");
|
|
20340
20382
|
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml34(title)}</title><style>body{background:#0d141b;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.2),rgba(245,158,11,.12));border:1px solid #283544;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.surfaces{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.surface{background:#151d26;border:1px solid #283544;border-radius:20px;padding:18px}.surface span{color:#aab5c0;font-size:.78rem;font-weight:900;letter-spacing:.08em}.surface strong{font-size:1.5rem}.pass{border-color:rgba(34,197,94,.55)}.fail{border-color:rgba(239,68,68,.75)}a{color:#5eead4}</style></head><body><main><section class="hero"><p class="eyebrow">Ops status</p><h1>${escapeHtml34(title)}</h1><p>Compact pass/fail status for framework widgets, demos, and small customer-facing health badges.</p><p class="status ${escapeHtml34(report.status)}">Overall: ${escapeHtml34(report.status.toUpperCase())}</p><p>${report.passed}/${report.total} checks passing</p></section><section class="surfaces">${surfaces || '<article class="surface pass"><span>PASS</span><h2>No checks configured</h2><strong>0/0</strong></article>'}</section></main></body></html>`;
|
package/dist/opsStatus.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type VoiceEvalLink, type VoiceEvalRoutesOptions } from './evalRoutes';
|
|
2
2
|
import { type VoiceQualityRoutesOptions } from './qualityRoutes';
|
|
3
|
+
import { type VoiceProviderFallbackRecoverySummary } from './sessionReplay';
|
|
3
4
|
import { type VoiceTraceEventStore } from './trace';
|
|
4
5
|
export type VoiceOpsStatus = 'pass' | 'fail';
|
|
5
6
|
export type VoiceOpsStatusLink = VoiceEvalLink & {
|
|
@@ -11,6 +12,7 @@ export type VoiceOpsStatusOptions<TProvider extends string = string> = {
|
|
|
11
12
|
include?: {
|
|
12
13
|
handoffs?: boolean;
|
|
13
14
|
providers?: boolean;
|
|
15
|
+
providerRecovery?: boolean;
|
|
14
16
|
quality?: boolean;
|
|
15
17
|
sessions?: boolean;
|
|
16
18
|
workflows?: boolean;
|
|
@@ -40,6 +42,7 @@ export type VoiceOpsStatusReport = {
|
|
|
40
42
|
status: VoiceOpsStatus;
|
|
41
43
|
total: number;
|
|
42
44
|
};
|
|
45
|
+
providerRecovery?: VoiceProviderFallbackRecoverySummary;
|
|
43
46
|
quality?: {
|
|
44
47
|
status: VoiceOpsStatus;
|
|
45
48
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Elysia } from 'elysia';
|
|
2
|
+
import { type VoiceProviderFallbackRecoverySummary } from './sessionReplay';
|
|
2
3
|
import { type VoiceTelephonyCarrierMatrixInput } from './telephony/matrix';
|
|
3
4
|
import type { VoiceTraceEventStore } from './trace';
|
|
4
5
|
import type { VoiceTraceSinkDeliveryStore } from './trace';
|
|
@@ -83,6 +84,7 @@ export type VoiceProductionReadinessReport = {
|
|
|
83
84
|
degraded: number;
|
|
84
85
|
total: number;
|
|
85
86
|
};
|
|
87
|
+
providerRecovery: VoiceProviderFallbackRecoverySummary;
|
|
86
88
|
phoneAgentSmokes?: {
|
|
87
89
|
failed: number;
|
|
88
90
|
passed: number;
|
package/dist/sessionReplay.d.ts
CHANGED
|
@@ -43,6 +43,15 @@ export type VoiceSessionListItem = {
|
|
|
43
43
|
transcriptCount: number;
|
|
44
44
|
turnCount: number;
|
|
45
45
|
};
|
|
46
|
+
export type VoiceProviderFallbackRecoverySummary = {
|
|
47
|
+
recovered: number;
|
|
48
|
+
recoveredSessions: number;
|
|
49
|
+
recoveredTurns: number;
|
|
50
|
+
status: 'fail' | 'pass';
|
|
51
|
+
total: number;
|
|
52
|
+
unresolvedErrors: number;
|
|
53
|
+
unresolvedSessions: number;
|
|
54
|
+
};
|
|
46
55
|
export type VoiceSessionListOptions = {
|
|
47
56
|
events?: StoredVoiceTraceEvent[];
|
|
48
57
|
limit?: number;
|
|
@@ -78,6 +87,7 @@ export type VoiceSessionReplayRoutesOptions = VoiceSessionReplayHTMLHandlerOptio
|
|
|
78
87
|
name?: string;
|
|
79
88
|
path?: string;
|
|
80
89
|
};
|
|
90
|
+
export declare const summarizeVoiceProviderFallbackRecovery: (events: StoredVoiceTraceEvent[]) => VoiceProviderFallbackRecoverySummary;
|
|
81
91
|
export declare const summarizeVoiceSessionReplay: (options: VoiceSessionReplayOptions) => Promise<VoiceSessionReplay>;
|
|
82
92
|
export declare const summarizeVoiceSessions: (options?: VoiceSessionListOptions) => Promise<VoiceSessionListItem[]>;
|
|
83
93
|
export declare const renderVoiceSessionsHTML: (sessions: VoiceSessionListItem[]) => string;
|