@absolutejs/voice 0.0.22-beta.277 → 0.0.22-beta.279
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -0
- package/dist/index.js +388 -40
- package/dist/productionReadiness.d.ts +24 -0
- package/dist/voiceMonitoring.d.ts +325 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -53,6 +53,7 @@ export { assertVoiceProviderRoutingContractEvidence, assertVoiceProviderRoutingC
|
|
|
53
53
|
export { assertVoiceProviderSloEvidence, buildVoiceProviderSloReport, createVoiceProviderSloRoutes, evaluateVoiceProviderSloEvidence, renderVoiceProviderSloHTML, renderVoiceProviderSloMarkdown } from './providerSlo';
|
|
54
54
|
export { createVoicePhoneAgentProductionSmokeHTMLHandler, createVoicePhoneAgentProductionSmokeJSONHandler, createVoicePhoneAgentProductionSmokeRoutes, renderVoicePhoneAgentProductionSmokeHTML, runVoicePhoneAgentProductionSmokeContract } from './phoneAgentProductionSmoke';
|
|
55
55
|
export { assertVoiceProductionReadinessEvidence, buildVoiceProductionReadinessGate, buildVoiceProductionReadinessReport, createVoiceProductionReadinessRoutes, evaluateVoiceProductionReadinessEvidence, renderVoiceProductionReadinessHTML, summarizeVoiceProductionReadinessGate } from './productionReadiness';
|
|
56
|
+
export { acknowledgeVoiceMonitorIssue, buildVoiceMonitorRunReport, createVoiceMemoryMonitorIssueStore, createVoiceMemoryMonitorNotifierDeliveryReceiptStore, createVoiceMonitorRoutes, createVoiceMonitorWebhookNotifier, deliverVoiceMonitorIssueNotifications, muteVoiceMonitorIssue, renderVoiceMonitorHTML, renderVoiceMonitorMarkdown, resolveVoiceMonitorIssue } from './voiceMonitoring';
|
|
56
57
|
export { createVoiceReadinessProfile, recommendVoiceReadinessProfile } from './readinessProfiles';
|
|
57
58
|
export { assertVoiceProviderContractMatrixEvidence, assertVoiceProviderStackEvidence, buildVoiceProviderContractMatrix, createVoiceProviderContractMatrixHTMLHandler, createVoiceProviderContractMatrixJSONHandler, createVoiceProviderContractMatrixPreset, createVoiceProviderContractMatrixRoutes, evaluateVoiceProviderContractMatrixEvidence, evaluateVoiceProviderStackEvidence, evaluateVoiceProviderStackGaps, renderVoiceProviderContractMatrixHTML, recommendVoiceProviderStack } from './providerStackRecommendations';
|
|
58
59
|
export { buildVoiceOpsConsoleReport, createVoiceOpsConsoleRoutes, renderVoiceOpsConsoleHTML } from './opsConsoleRoutes';
|
|
@@ -121,6 +122,7 @@ export type { VoicePhoneAgentProductionSmokeIssue, VoicePhoneAgentProductionSmok
|
|
|
121
122
|
export type { VoiceOpsConsoleLink, VoiceOpsConsoleReport, VoiceOpsConsoleRoutesOptions } from './opsConsoleRoutes';
|
|
122
123
|
export type { VoiceOpsStatus, VoiceOpsStatusLink, VoiceOpsStatusOptions, VoiceOpsStatusReport, VoiceOpsStatusRoutesOptions } from './opsStatus';
|
|
123
124
|
export type { VoiceProductionReadinessAction, VoiceProductionReadinessAuditOptions, VoiceProductionReadinessAuditRequirement, VoiceProductionReadinessAuditSummary, VoiceProductionReadinessAssertionInput, VoiceProductionReadinessAssertionReport, VoiceProductionReadinessCheck, VoiceProductionReadinessGateIssue, VoiceProductionReadinessGateOptions, VoiceProductionReadinessGateProfile, VoiceProductionReadinessGateProfileSurface, VoiceProductionReadinessGateReport, VoiceProductionReadinessOpsActionHistoryOptions, VoiceProductionReadinessOpsActionHistorySummary, VoiceProductionReadinessOperationsRecordLink, VoiceProductionReadinessOperationsRecordLinks, VoiceProductionReadinessProfileExplanation, VoiceProductionReadinessProfileSurface, VoiceProductionReadinessProofSource, VoiceProductionReadinessReport, VoiceProductionReadinessRoutesOptions, VoiceProductionReadinessTraceDeliverySummary, VoiceProductionReadinessAuditDeliveryOptions, VoiceProductionReadinessAuditDeliverySummary, VoiceProductionReadinessTraceDeliveryOptions, VoiceProductionReadinessStatus } from './productionReadiness';
|
|
125
|
+
export type { VoiceMonitorDefinition, VoiceMonitorEvaluation, VoiceMonitorEvaluationInput, VoiceMonitorIssue, VoiceMonitorIssueStatus, VoiceMonitorIssueStore, VoiceMonitorNotifier, VoiceMonitorNotifierDeliveryInput, VoiceMonitorNotifierDeliveryOptions, VoiceMonitorNotifierDeliveryReceipt, VoiceMonitorNotifierDeliveryReceiptStore, VoiceMonitorNotifierDeliveryReport, VoiceMonitorNotifierDeliveryResult, VoiceMonitorRoutesOptions, VoiceMonitorRun, VoiceMonitorRunOptions, VoiceMonitorRunReport, VoiceMonitorSeverity, VoiceMonitorStatus, VoiceMonitorWebhookNotifierOptions } from './voiceMonitoring';
|
|
124
126
|
export type { VoiceReadinessProfileName, VoiceReadinessProfileOptions, VoiceReadinessProfileRecommendation, VoiceReadinessProfileRecommendationScore, VoiceReadinessProfileRoutesOptions } from './readinessProfiles';
|
|
125
127
|
export type { VoiceProviderStackChoice, VoiceProviderStackCapabilities, VoiceProviderStackCapabilityGap, VoiceProviderStackCapabilityGapInput, VoiceProviderStackCapabilityGapReport, VoiceProviderContractCheck, VoiceProviderContractCheckStatus, VoiceProviderContractDefinition, VoiceProviderContractMatrixAssertionInput, VoiceProviderContractMatrixAssertionReport, VoiceProviderContractMatrixHandlerOptions, VoiceProviderContractMatrixHTMLHandlerOptions, VoiceProviderContractMatrixInput, VoiceProviderContractMatrixPresetOptions, VoiceProviderContractMatrixReport, VoiceProviderContractMatrixRoutesOptions, VoiceProviderContractMatrixRow, VoiceProviderStackAssertionInput, VoiceProviderStackAssertionReport, VoiceProviderStackInput, VoiceProviderStackKind, VoiceProviderStackRecommendation } from './providerStackRecommendations';
|
|
126
128
|
export type { VoiceOperationsRecord, VoiceOperationsRecordAgentHandoff, VoiceOperationsRecordAuditSummary, VoiceOperationsRecordGuardrailAssertionInput, VoiceOperationsRecordGuardrailAssertionReport, VoiceOperationsRecordGuardrailDecision, VoiceOperationsRecordGuardrailFinding, VoiceOperationsRecordGuardrailSummary, VoiceOperationsRecordIntegrationEventSummary, VoiceOperationsRecordOptions, VoiceOperationsRecordOutcome, VoiceOperationsRecordProviderDecision, VoiceOperationsRecordReviewSummary, VoiceOperationsRecordRoutesOptions, VoiceOperationsRecordStatus, VoiceOperationsRecordTaskSummary, VoiceOperationsRecordTranscriptTurn, VoiceOperationsRecordTool } from './operationsRecord';
|
package/dist/index.js
CHANGED
|
@@ -27328,6 +27328,18 @@ var resolvePhoneAgentSmokes = async (options, input) => {
|
|
|
27328
27328
|
}
|
|
27329
27329
|
return typeof options.phoneAgentSmokes === "function" ? await options.phoneAgentSmokes(input) : options.phoneAgentSmokes;
|
|
27330
27330
|
};
|
|
27331
|
+
var resolveMonitoring = async (options, input) => {
|
|
27332
|
+
if (options.monitoring === false || options.monitoring === undefined) {
|
|
27333
|
+
return;
|
|
27334
|
+
}
|
|
27335
|
+
return typeof options.monitoring === "function" ? await options.monitoring(input) : options.monitoring;
|
|
27336
|
+
};
|
|
27337
|
+
var resolveMonitoringNotifierDelivery = async (options, input) => {
|
|
27338
|
+
if (options.monitoringNotifierDelivery === false || options.monitoringNotifierDelivery === undefined) {
|
|
27339
|
+
return;
|
|
27340
|
+
}
|
|
27341
|
+
return typeof options.monitoringNotifierDelivery === "function" ? await options.monitoringNotifierDelivery(input) : options.monitoringNotifierDelivery;
|
|
27342
|
+
};
|
|
27331
27343
|
var isVoiceTelephonyWebhookSecurityReport = (value) => typeof value.generatedAt === "number" && Array.isArray(value.providers) && typeof value.status === "string";
|
|
27332
27344
|
var resolveTelephonyWebhookSecurity = async (options, input) => {
|
|
27333
27345
|
if (options.telephonyWebhookSecurity === false || options.telephonyWebhookSecurity === undefined) {
|
|
@@ -27698,6 +27710,8 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
27698
27710
|
providerStack,
|
|
27699
27711
|
providerContractMatrix,
|
|
27700
27712
|
phoneAgentSmokes,
|
|
27713
|
+
monitoring,
|
|
27714
|
+
monitoringNotifierDelivery,
|
|
27701
27715
|
telephonyWebhookSecurity,
|
|
27702
27716
|
reconnectContracts,
|
|
27703
27717
|
bargeInReports,
|
|
@@ -27737,6 +27751,8 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
27737
27751
|
resolveProviderStack(options, { query, request }),
|
|
27738
27752
|
resolveProviderContractMatrix(options, { query, request }),
|
|
27739
27753
|
resolvePhoneAgentSmokes(options, { query, request }),
|
|
27754
|
+
resolveMonitoring(options, { query, request }),
|
|
27755
|
+
resolveMonitoringNotifierDelivery(options, { query, request }),
|
|
27740
27756
|
resolveTelephonyWebhookSecurity(options, { query, request }),
|
|
27741
27757
|
resolveReconnectContracts(options, { query, request }),
|
|
27742
27758
|
resolveBargeInReports(options, { query, request }),
|
|
@@ -27945,6 +27961,19 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
27945
27961
|
status: phoneAgentSmokes.some((report) => !report.pass) ? "fail" : phoneAgentSmokes.length === 0 ? "warn" : "pass",
|
|
27946
27962
|
total: phoneAgentSmokes.length
|
|
27947
27963
|
} : undefined;
|
|
27964
|
+
const monitoringSummary = monitoring ? {
|
|
27965
|
+
criticalOpen: monitoring.summary.criticalOpen,
|
|
27966
|
+
open: monitoring.summary.open,
|
|
27967
|
+
status: monitoring.status,
|
|
27968
|
+
total: monitoring.summary.total
|
|
27969
|
+
} : undefined;
|
|
27970
|
+
const monitoringNotifierDeliverySummary = monitoringNotifierDelivery ? {
|
|
27971
|
+
failed: monitoringNotifierDelivery.summary.failed,
|
|
27972
|
+
notifiers: monitoringNotifierDelivery.summary.notifiers,
|
|
27973
|
+
sent: monitoringNotifierDelivery.summary.sent,
|
|
27974
|
+
status: monitoringNotifierDelivery.status,
|
|
27975
|
+
total: monitoringNotifierDelivery.summary.total
|
|
27976
|
+
} : undefined;
|
|
27948
27977
|
const telephonyWebhookSecuritySummary = telephonyWebhookSecurity ? {
|
|
27949
27978
|
enabled: telephonyWebhookSecurity.summary.enabled,
|
|
27950
27979
|
failed: telephonyWebhookSecurity.summary.failed,
|
|
@@ -28341,6 +28370,38 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
28341
28370
|
]
|
|
28342
28371
|
});
|
|
28343
28372
|
}
|
|
28373
|
+
if (monitoring && monitoringSummary) {
|
|
28374
|
+
checks.push({
|
|
28375
|
+
detail: monitoringSummary.status === "pass" ? `${monitoringSummary.total} monitor(s) are passing with no open issues.` : `${monitoringSummary.open} monitor issue(s) open, ${monitoringSummary.criticalOpen} critical.`,
|
|
28376
|
+
href: options.links?.monitoring ?? "/voice/monitors",
|
|
28377
|
+
label: "Monitoring issues",
|
|
28378
|
+
status: monitoringSummary.status,
|
|
28379
|
+
value: `${monitoring.summary.passed}/${monitoringSummary.total}`,
|
|
28380
|
+
actions: monitoringSummary.status === "pass" ? [] : [
|
|
28381
|
+
{
|
|
28382
|
+
description: "Open monitor issues and resolve or acknowledge customer-owned alerts before deploy.",
|
|
28383
|
+
href: options.links?.monitoring ?? "/voice/monitors",
|
|
28384
|
+
label: "Open monitor issues"
|
|
28385
|
+
}
|
|
28386
|
+
]
|
|
28387
|
+
});
|
|
28388
|
+
}
|
|
28389
|
+
if (monitoringNotifierDelivery && monitoringNotifierDeliverySummary) {
|
|
28390
|
+
checks.push({
|
|
28391
|
+
detail: monitoringNotifierDeliverySummary.status === "pass" ? `${monitoringNotifierDeliverySummary.sent} monitor notification(s) delivered.` : `${monitoringNotifierDeliverySummary.failed} monitor notification delivery failure(s).`,
|
|
28392
|
+
href: options.links?.monitoringNotifierDelivery ?? "/api/voice/monitor-issues/notifications",
|
|
28393
|
+
label: "Monitor notifier delivery",
|
|
28394
|
+
status: monitoringNotifierDeliverySummary.status,
|
|
28395
|
+
value: `${monitoringNotifierDeliverySummary.sent}/${monitoringNotifierDeliverySummary.total}`,
|
|
28396
|
+
actions: monitoringNotifierDeliverySummary.status === "pass" ? [] : [
|
|
28397
|
+
{
|
|
28398
|
+
description: "Open monitor notification receipts and confirm webhook, Slack, email, or audit destinations are receiving issue alerts.",
|
|
28399
|
+
href: options.links?.monitoringNotifierDelivery ?? "/api/voice/monitor-issues/notifications",
|
|
28400
|
+
label: "Open monitor notification receipts"
|
|
28401
|
+
}
|
|
28402
|
+
]
|
|
28403
|
+
});
|
|
28404
|
+
}
|
|
28344
28405
|
return {
|
|
28345
28406
|
checkedAt: Date.now(),
|
|
28346
28407
|
checks,
|
|
@@ -28358,6 +28419,8 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
28358
28419
|
operationsRecords: "/voice-operations",
|
|
28359
28420
|
observabilityExport: "/voice/observability-export",
|
|
28360
28421
|
observabilityExportDeliveries: "/api/voice/observability-export/deliveries",
|
|
28422
|
+
monitoring: "/voice/monitors",
|
|
28423
|
+
monitoringNotifierDelivery: "/api/voice/monitor-issues/notifications",
|
|
28361
28424
|
opsActions: "/voice/ops-actions",
|
|
28362
28425
|
opsRecovery: "/ops-recovery",
|
|
28363
28426
|
phoneAgentSmoke: "/sessions",
|
|
@@ -28389,6 +28452,8 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
28389
28452
|
total: handoffs.total
|
|
28390
28453
|
},
|
|
28391
28454
|
liveLatency,
|
|
28455
|
+
monitoring: monitoringSummary,
|
|
28456
|
+
monitoringNotifierDelivery: monitoringNotifierDeliverySummary,
|
|
28392
28457
|
opsActionHistory,
|
|
28393
28458
|
opsRecovery: opsRecovery ? {
|
|
28394
28459
|
issues: opsRecovery.issues.length,
|
|
@@ -28502,6 +28567,278 @@ var createVoiceProductionReadinessRoutes = (options) => {
|
|
|
28502
28567
|
}
|
|
28503
28568
|
return routes;
|
|
28504
28569
|
};
|
|
28570
|
+
// src/voiceMonitoring.ts
|
|
28571
|
+
import { Elysia as Elysia44 } from "elysia";
|
|
28572
|
+
var escapeHtml41 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
28573
|
+
var issueIdForRun = (run) => `voice-monitor:${run.id}:${run.impactedSessions?.[0] ?? "global"}`;
|
|
28574
|
+
var rollupStatus4 = (runs) => runs.some((run) => run.status === "fail") ? "fail" : runs.some((run) => run.status === "warn") ? "warn" : "pass";
|
|
28575
|
+
var createVoiceMemoryMonitorIssueStore = (initial = []) => {
|
|
28576
|
+
const issues = new Map(initial.map((issue) => [issue.id, { ...issue }]));
|
|
28577
|
+
return {
|
|
28578
|
+
list: () => Array.from(issues.values()).map((issue) => ({ ...issue })),
|
|
28579
|
+
upsert: (issue) => {
|
|
28580
|
+
const previous = issues.get(issue.id);
|
|
28581
|
+
const next = previous ? {
|
|
28582
|
+
...previous,
|
|
28583
|
+
...issue,
|
|
28584
|
+
createdAt: previous.createdAt,
|
|
28585
|
+
status: previous.status === "resolved" || previous.status === "muted" ? previous.status : issue.status
|
|
28586
|
+
} : issue;
|
|
28587
|
+
issues.set(issue.id, { ...next });
|
|
28588
|
+
return { ...next };
|
|
28589
|
+
},
|
|
28590
|
+
update: (id, patch) => {
|
|
28591
|
+
const previous = issues.get(id);
|
|
28592
|
+
if (!previous) {
|
|
28593
|
+
return;
|
|
28594
|
+
}
|
|
28595
|
+
const next = { ...previous, ...patch };
|
|
28596
|
+
issues.set(id, next);
|
|
28597
|
+
return { ...next };
|
|
28598
|
+
}
|
|
28599
|
+
};
|
|
28600
|
+
};
|
|
28601
|
+
var createVoiceMemoryMonitorNotifierDeliveryReceiptStore = (initial = []) => {
|
|
28602
|
+
const receipts = new Map(initial.map((receipt) => [receipt.id, { ...receipt }]));
|
|
28603
|
+
return {
|
|
28604
|
+
list: () => Array.from(receipts.values()).map((receipt) => ({ ...receipt })),
|
|
28605
|
+
set: (id, receipt) => {
|
|
28606
|
+
receipts.set(id, { ...receipt });
|
|
28607
|
+
return { ...receipt };
|
|
28608
|
+
}
|
|
28609
|
+
};
|
|
28610
|
+
};
|
|
28611
|
+
var buildVoiceMonitorRunReport = async (options) => {
|
|
28612
|
+
const checkedAt = options.now ?? Date.now();
|
|
28613
|
+
const runs = await Promise.all(options.monitors.map(async (monitor) => {
|
|
28614
|
+
const evaluation = await monitor.evaluate({
|
|
28615
|
+
evidence: options.evidence,
|
|
28616
|
+
now: checkedAt
|
|
28617
|
+
});
|
|
28618
|
+
return {
|
|
28619
|
+
...evaluation,
|
|
28620
|
+
checkedAt,
|
|
28621
|
+
description: monitor.description,
|
|
28622
|
+
id: monitor.id,
|
|
28623
|
+
label: monitor.label,
|
|
28624
|
+
severity: monitor.severity ?? "warn",
|
|
28625
|
+
windowMs: monitor.windowMs
|
|
28626
|
+
};
|
|
28627
|
+
}));
|
|
28628
|
+
for (const run of runs) {
|
|
28629
|
+
if (run.status === "pass") {
|
|
28630
|
+
continue;
|
|
28631
|
+
}
|
|
28632
|
+
await options.issueStore?.upsert({
|
|
28633
|
+
createdAt: checkedAt,
|
|
28634
|
+
detail: run.detail,
|
|
28635
|
+
id: issueIdForRun(run),
|
|
28636
|
+
impactedSessions: [...run.impactedSessions ?? []],
|
|
28637
|
+
label: run.label,
|
|
28638
|
+
lastSeenAt: checkedAt,
|
|
28639
|
+
monitorId: run.id,
|
|
28640
|
+
operationsRecordHrefs: [...run.operationsRecordHrefs ?? []],
|
|
28641
|
+
severity: run.status === "fail" ? run.severity : "warn",
|
|
28642
|
+
status: "open",
|
|
28643
|
+
threshold: run.threshold,
|
|
28644
|
+
value: run.value
|
|
28645
|
+
});
|
|
28646
|
+
}
|
|
28647
|
+
const issues = await options.issueStore?.list() ?? [];
|
|
28648
|
+
const openIssues = issues.filter((issue) => issue.status === "open");
|
|
28649
|
+
const criticalOpen = openIssues.filter((issue) => issue.severity === "critical").length;
|
|
28650
|
+
return {
|
|
28651
|
+
checkedAt,
|
|
28652
|
+
issues,
|
|
28653
|
+
runs,
|
|
28654
|
+
status: criticalOpen > 0 ? "fail" : openIssues.length > 0 || rollupStatus4(runs) === "warn" ? "warn" : rollupStatus4(runs),
|
|
28655
|
+
summary: {
|
|
28656
|
+
acknowledged: issues.filter((issue) => issue.status === "acknowledged").length,
|
|
28657
|
+
criticalOpen,
|
|
28658
|
+
failed: runs.filter((run) => run.status === "fail").length,
|
|
28659
|
+
muted: issues.filter((issue) => issue.status === "muted").length,
|
|
28660
|
+
open: openIssues.length,
|
|
28661
|
+
passed: runs.filter((run) => run.status === "pass").length,
|
|
28662
|
+
resolved: issues.filter((issue) => issue.status === "resolved").length,
|
|
28663
|
+
total: runs.length,
|
|
28664
|
+
warned: runs.filter((run) => run.status === "warn").length
|
|
28665
|
+
}
|
|
28666
|
+
};
|
|
28667
|
+
};
|
|
28668
|
+
var acknowledgeVoiceMonitorIssue = async (store, id, input = {}) => store.update(id, {
|
|
28669
|
+
acknowledgedAt: input.now ?? Date.now(),
|
|
28670
|
+
acknowledgedBy: input.actorId,
|
|
28671
|
+
status: "acknowledged"
|
|
28672
|
+
});
|
|
28673
|
+
var resolveVoiceMonitorIssue = async (store, id, input = {}) => store.update(id, {
|
|
28674
|
+
resolvedAt: input.now ?? Date.now(),
|
|
28675
|
+
resolvedBy: input.actorId,
|
|
28676
|
+
status: "resolved"
|
|
28677
|
+
});
|
|
28678
|
+
var muteVoiceMonitorIssue = async (store, id, input = {}) => store.update(id, {
|
|
28679
|
+
mutedAt: input.now ?? Date.now(),
|
|
28680
|
+
mutedBy: input.actorId,
|
|
28681
|
+
status: "muted"
|
|
28682
|
+
});
|
|
28683
|
+
var createVoiceMonitorWebhookNotifier = (options) => ({
|
|
28684
|
+
id: options.id,
|
|
28685
|
+
label: options.label ?? options.id,
|
|
28686
|
+
deliver: async ({ issue }) => {
|
|
28687
|
+
const response = await (options.fetch ?? fetch)(options.url, {
|
|
28688
|
+
body: JSON.stringify(options.mapIssue?.(issue) ?? {
|
|
28689
|
+
detail: issue.detail,
|
|
28690
|
+
impactedSessions: issue.impactedSessions,
|
|
28691
|
+
issueId: issue.id,
|
|
28692
|
+
label: issue.label,
|
|
28693
|
+
monitorId: issue.monitorId,
|
|
28694
|
+
operationsRecordHrefs: issue.operationsRecordHrefs,
|
|
28695
|
+
severity: issue.severity,
|
|
28696
|
+
status: issue.status,
|
|
28697
|
+
value: issue.value
|
|
28698
|
+
}),
|
|
28699
|
+
headers: {
|
|
28700
|
+
"content-type": "application/json",
|
|
28701
|
+
...options.headers
|
|
28702
|
+
},
|
|
28703
|
+
method: "POST"
|
|
28704
|
+
});
|
|
28705
|
+
return {
|
|
28706
|
+
detail: `HTTP ${response.status}`,
|
|
28707
|
+
status: response.ok ? "sent" : "failed"
|
|
28708
|
+
};
|
|
28709
|
+
}
|
|
28710
|
+
});
|
|
28711
|
+
var deliverVoiceMonitorIssueNotifications = async (options) => {
|
|
28712
|
+
const checkedAt = options.now ?? Date.now();
|
|
28713
|
+
const statuses = new Set(options.statuses ?? ["open"]);
|
|
28714
|
+
const issues = (await options.issueStore.list()).filter((issue) => statuses.has(issue.status));
|
|
28715
|
+
const receipts = [];
|
|
28716
|
+
for (const issue of issues) {
|
|
28717
|
+
for (const notifier of options.notifiers) {
|
|
28718
|
+
const result = await notifier.deliver({ issue, now: checkedAt });
|
|
28719
|
+
const receipt = {
|
|
28720
|
+
detail: result.detail,
|
|
28721
|
+
id: `voice-monitor-notifier:${notifier.id}:${issue.id}:${checkedAt}`,
|
|
28722
|
+
issueId: issue.id,
|
|
28723
|
+
notifierId: notifier.id,
|
|
28724
|
+
notifierLabel: notifier.label,
|
|
28725
|
+
sentAt: checkedAt,
|
|
28726
|
+
status: result.status
|
|
28727
|
+
};
|
|
28728
|
+
receipts.push(receipt);
|
|
28729
|
+
await options.receiptStore?.set(receipt.id, receipt);
|
|
28730
|
+
}
|
|
28731
|
+
}
|
|
28732
|
+
const allReceipts = options.receiptStore ? await options.receiptStore.list() : receipts;
|
|
28733
|
+
const failed = allReceipts.filter((receipt) => receipt.status === "failed").length;
|
|
28734
|
+
const sent = allReceipts.filter((receipt) => receipt.status === "sent").length;
|
|
28735
|
+
const skipped = allReceipts.filter((receipt) => receipt.status === "skipped").length;
|
|
28736
|
+
return {
|
|
28737
|
+
checkedAt,
|
|
28738
|
+
receipts: allReceipts,
|
|
28739
|
+
status: failed > 0 ? "fail" : allReceipts.length === 0 ? "warn" : "pass",
|
|
28740
|
+
summary: {
|
|
28741
|
+
failed,
|
|
28742
|
+
notifiers: options.notifiers.length,
|
|
28743
|
+
sent,
|
|
28744
|
+
skipped,
|
|
28745
|
+
total: allReceipts.length
|
|
28746
|
+
}
|
|
28747
|
+
};
|
|
28748
|
+
};
|
|
28749
|
+
var renderVoiceMonitorMarkdown = (report) => {
|
|
28750
|
+
const rows = report.runs.map((run) => `| ${run.id} | ${run.status} | ${run.severity} | ${run.value ?? ""} | ${run.threshold ?? ""} | ${run.detail ?? ""} |`).join(`
|
|
28751
|
+
`);
|
|
28752
|
+
return `# Voice Monitor Report
|
|
28753
|
+
|
|
28754
|
+
- Status: ${report.status}
|
|
28755
|
+
- Checks: ${report.summary.passed}/${report.summary.total} passing
|
|
28756
|
+
- Open issues: ${report.summary.open}
|
|
28757
|
+
- Critical open issues: ${report.summary.criticalOpen}
|
|
28758
|
+
|
|
28759
|
+
| Monitor | Status | Severity | Value | Threshold | Detail |
|
|
28760
|
+
| --- | --- | --- | --- | --- | --- |
|
|
28761
|
+
${rows || "| none | pass | info | | | No monitors configured. |"}
|
|
28762
|
+
`;
|
|
28763
|
+
};
|
|
28764
|
+
var renderVoiceMonitorHTML = (report, options = {}) => {
|
|
28765
|
+
const title = options.title ?? "Voice Monitors";
|
|
28766
|
+
const runs = report.runs.map((run) => `<tr><td>${escapeHtml41(run.label)}</td><td class="${escapeHtml41(run.status)}">${escapeHtml41(run.status)}</td><td>${escapeHtml41(run.severity)}</td><td>${escapeHtml41(String(run.value ?? ""))}</td><td>${escapeHtml41(String(run.threshold ?? ""))}</td><td>${escapeHtml41(run.detail ?? "")}</td></tr>`).join("");
|
|
28767
|
+
const issues = report.issues.map((issue) => `<li><strong>${escapeHtml41(issue.label)}</strong> <span class="${escapeHtml41(issue.status)}">${escapeHtml41(issue.status)}</span> ${escapeHtml41(issue.detail ?? "")}</li>`).join("");
|
|
28768
|
+
const snippet = escapeHtml41(`app.use(createVoiceMonitorRoutes({
|
|
28769
|
+
evidence,
|
|
28770
|
+
issueStore,
|
|
28771
|
+
monitors: [defineVoiceMonitor(...)]
|
|
28772
|
+
}));`);
|
|
28773
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml41(title)}</title><style>body{background:#10141b;color:#f8f2df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1100px;padding:32px}.hero,.card{background:#171f2b;border:1px solid #2e3a4b;border-radius:24px;margin-bottom:16px;padding:22px}.eyebrow{color:#93c5fd;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.7rem);line-height:.92;margin:.2rem 0 1rem}.pill{border:1px solid #64748b;border-radius:999px;display:inline-flex;font-weight:900;margin-right:8px;padding:8px 12px}.pass{color:#86efac}.warn,.acknowledged{color:#fde68a}.fail,.open{color:#fca5a5}.resolved,.muted{color:#cbd5e1}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid #2e3a4b;padding:12px;text-align:left;vertical-align:top}pre{background:#0c1118;border:1px solid #2e3a4b;border-radius:16px;color:#dbeafe;overflow:auto;padding:16px}</style></head><body><main><section class="hero"><p class="eyebrow">Code-owned monitoring</p><h1>${escapeHtml41(title)}</h1><p class="pill ${escapeHtml41(report.status)}">Status: ${escapeHtml41(report.status)}</p><p class="pill">Open issues: ${String(report.summary.open)}</p><p class="pill">Critical: ${String(report.summary.criticalOpen)}</p></section><section class="card"><h2>Monitor Runs</h2><table><thead><tr><th>Monitor</th><th>Status</th><th>Severity</th><th>Value</th><th>Threshold</th><th>Detail</th></tr></thead><tbody>${runs}</tbody></table></section><section class="card"><h2>Issues</h2>${issues ? `<ul>${issues}</ul>` : '<p class="pass">No monitor issues.</p>'}</section><section class="card"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceMonitorRoutes(...)</code></h2><pre><code>${snippet}</code></pre></section></main></body></html>`;
|
|
28774
|
+
};
|
|
28775
|
+
var actorFromRequest = async (request) => {
|
|
28776
|
+
if (!request.headers.get("content-type")?.includes("application/json")) {
|
|
28777
|
+
return;
|
|
28778
|
+
}
|
|
28779
|
+
const body = await request.json().catch(() => {
|
|
28780
|
+
return;
|
|
28781
|
+
});
|
|
28782
|
+
return typeof body?.actorId === "string" ? body.actorId : undefined;
|
|
28783
|
+
};
|
|
28784
|
+
var createVoiceMonitorRoutes = (options) => {
|
|
28785
|
+
const path = options.path ?? "/api/voice/monitors";
|
|
28786
|
+
const htmlPath = options.htmlPath === undefined ? "/voice/monitors" : options.htmlPath;
|
|
28787
|
+
const issuePath = options.issuePath ?? "/api/voice/monitor-issues";
|
|
28788
|
+
const notifierPath = options.notifierPath === undefined ? "/api/voice/monitor-notifications" : options.notifierPath;
|
|
28789
|
+
const issueStore = options.issueStore ?? createVoiceMemoryMonitorIssueStore();
|
|
28790
|
+
const receiptStore = options.receiptStore ?? createVoiceMemoryMonitorNotifierDeliveryReceiptStore();
|
|
28791
|
+
const report = () => buildVoiceMonitorRunReport({
|
|
28792
|
+
evidence: options.evidence,
|
|
28793
|
+
issueStore,
|
|
28794
|
+
monitors: options.monitors,
|
|
28795
|
+
now: options.now
|
|
28796
|
+
});
|
|
28797
|
+
const routes = new Elysia44({
|
|
28798
|
+
name: options.name ?? "absolutejs-voice-monitoring"
|
|
28799
|
+
}).get(path, report).get(`${path}.md`, async () => {
|
|
28800
|
+
return new Response(renderVoiceMonitorMarkdown(await report()), {
|
|
28801
|
+
headers: {
|
|
28802
|
+
"Content-Type": "text/markdown; charset=utf-8",
|
|
28803
|
+
...options.headers
|
|
28804
|
+
}
|
|
28805
|
+
});
|
|
28806
|
+
}).get(issuePath, () => issueStore.list()).get(`${issuePath}/notifications`, () => receiptStore.list()).post(`${issuePath}/:id/acknowledge`, async ({ params, request }) => {
|
|
28807
|
+
const issue = await acknowledgeVoiceMonitorIssue(issueStore, params.id, {
|
|
28808
|
+
actorId: await actorFromRequest(request)
|
|
28809
|
+
});
|
|
28810
|
+
return issue ?? new Response("Issue not found", { status: 404 });
|
|
28811
|
+
}).post(`${issuePath}/:id/resolve`, async ({ params, request }) => {
|
|
28812
|
+
const issue = await resolveVoiceMonitorIssue(issueStore, params.id, {
|
|
28813
|
+
actorId: await actorFromRequest(request)
|
|
28814
|
+
});
|
|
28815
|
+
return issue ?? new Response("Issue not found", { status: 404 });
|
|
28816
|
+
}).post(`${issuePath}/:id/mute`, async ({ params, request }) => {
|
|
28817
|
+
const issue = await muteVoiceMonitorIssue(issueStore, params.id, {
|
|
28818
|
+
actorId: await actorFromRequest(request)
|
|
28819
|
+
});
|
|
28820
|
+
return issue ?? new Response("Issue not found", { status: 404 });
|
|
28821
|
+
});
|
|
28822
|
+
if (notifierPath !== false) {
|
|
28823
|
+
routes.post(notifierPath, () => deliverVoiceMonitorIssueNotifications({
|
|
28824
|
+
issueStore,
|
|
28825
|
+
notifiers: options.notifiers ?? [],
|
|
28826
|
+
receiptStore
|
|
28827
|
+
}));
|
|
28828
|
+
}
|
|
28829
|
+
if (htmlPath !== false) {
|
|
28830
|
+
routes.get(htmlPath, async () => {
|
|
28831
|
+
const body = await (options.render ?? renderVoiceMonitorHTML)(await report(), { title: options.title });
|
|
28832
|
+
return new Response(body, {
|
|
28833
|
+
headers: {
|
|
28834
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
28835
|
+
...options.headers
|
|
28836
|
+
}
|
|
28837
|
+
});
|
|
28838
|
+
});
|
|
28839
|
+
}
|
|
28840
|
+
return routes;
|
|
28841
|
+
};
|
|
28505
28842
|
// src/readinessProfiles.ts
|
|
28506
28843
|
var profileSurfaceLabels = {
|
|
28507
28844
|
"meeting-recorder": {
|
|
@@ -28862,8 +29199,8 @@ var recommendVoiceReadinessProfile = (options) => {
|
|
|
28862
29199
|
};
|
|
28863
29200
|
};
|
|
28864
29201
|
// src/providerStackRecommendations.ts
|
|
28865
|
-
import { Elysia as
|
|
28866
|
-
var
|
|
29202
|
+
import { Elysia as Elysia45 } from "elysia";
|
|
29203
|
+
var escapeHtml42 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
28867
29204
|
var profileProviderPriorities = {
|
|
28868
29205
|
"meeting-recorder": {
|
|
28869
29206
|
llm: ["openai", "anthropic", "gemini"],
|
|
@@ -29182,17 +29519,17 @@ var resolveProviderContractMatrixInput = async (matrix) => typeof matrix === "fu
|
|
|
29182
29519
|
var renderVoiceProviderContractMatrixHTML = (report, options = {}) => {
|
|
29183
29520
|
const title = options.title ?? "Voice Provider Contract Matrix";
|
|
29184
29521
|
const rows = report.rows.map((row) => {
|
|
29185
|
-
const checks = row.checks.map((check) => `<li class="${
|
|
29186
|
-
return `<article class="row ${
|
|
29522
|
+
const checks = row.checks.map((check) => `<li class="${escapeHtml42(check.status)}"><strong>${escapeHtml42(check.label)}</strong><span>${escapeHtml42(check.detail ?? check.status)}</span>${check.remediation ? `<em>${check.remediation.href ? `<a href="${escapeHtml42(check.remediation.href)}">${escapeHtml42(check.remediation.label)}</a>` : escapeHtml42(check.remediation.label)}: ${escapeHtml42(check.remediation.detail)}</em>` : ""}</li>`).join("");
|
|
29523
|
+
return `<article class="row ${escapeHtml42(row.status)}">
|
|
29187
29524
|
<div>
|
|
29188
|
-
<p class="eyebrow">${
|
|
29189
|
-
<h2>${
|
|
29190
|
-
<p class="status ${
|
|
29525
|
+
<p class="eyebrow">${escapeHtml42(row.kind)}${row.selected ? " \xB7 selected" : ""}</p>
|
|
29526
|
+
<h2>${escapeHtml42(row.provider)}</h2>
|
|
29527
|
+
<p class="status ${escapeHtml42(row.status)}">${escapeHtml42(row.status.toUpperCase())}</p>
|
|
29191
29528
|
</div>
|
|
29192
29529
|
<ul>${checks}</ul>
|
|
29193
29530
|
</article>`;
|
|
29194
29531
|
}).join("");
|
|
29195
|
-
const snippet =
|
|
29532
|
+
const snippet = escapeHtml42(`const providerContracts = () =>
|
|
29196
29533
|
createVoiceProviderContractMatrixPreset('phone-agent', {
|
|
29197
29534
|
env: process.env,
|
|
29198
29535
|
providers: {
|
|
@@ -29213,7 +29550,7 @@ createVoiceProductionReadinessRoutes({
|
|
|
29213
29550
|
providerContractMatrix: () =>
|
|
29214
29551
|
buildVoiceProviderContractMatrix(providerContracts())
|
|
29215
29552
|
});`);
|
|
29216
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
29553
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml42(title)}</title><style>body{background:#0f1412;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.primitive,.row{background:#17201b;border:1px solid #2d3b32;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.16),rgba(125,211,252,.12))}.primitive{background:#111814;border-color:#41604a}.eyebrow{color:#86efac;font-size:.78rem;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill,.status{border:1px solid #3f4f45;border-radius:999px;display:inline-flex;padding:8px 12px}.primitive code{color:#bbf7d0}.primitive p{color:#c8d8ca;line-height:1.55;margin:.45rem 0 0}.primitive pre{background:#08110d;border:1px solid #294132;border-radius:18px;color:#d9f99d;margin:16px 0 0;overflow:auto;padding:16px}.status.pass,.row.pass,.pass{border-color:rgba(34,197,94,.65)}.status.warn,.row.warn,.warn{border-color:rgba(245,158,11,.7)}.status.fail,.row.fail,.fail{border-color:rgba(239,68,68,.75)}.row{display:grid;gap:20px;grid-template-columns:minmax(180px,.45fr) 1fr}.row ul{display:grid;gap:10px;list-style:none;margin:0;padding:0}.row li{background:#111814;border:1px solid #2d3b32;border-radius:16px;display:grid;gap:4px;padding:12px}.row li span{color:#b8c2ba}.row li em{color:#f9d77e;font-style:normal}.row li a{color:#86efac}@media(max-width:760px){main{padding:18px}.row{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Provider contracts</p><h1>${escapeHtml42(title)}</h1><p>Self-hosted provider proof for configured state, required env, latency budgets, fallback, streaming, and declared capabilities.</p><div class="summary"><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.warned)} warning</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} total</span></div></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceProviderContractMatrixPreset(...)</code> builds this matrix</h2><p>Give AbsoluteJS your configured LLM, STT, and TTS providers once. It turns them into deploy-checkable proof for env, fallback, streaming, latency budgets, selected providers, and profile-required capabilities without a hosted dashboard.</p><pre><code>${snippet}</code></pre></section>${rows || '<article class="row"><p>No provider contracts configured.</p></article>'}</main></body></html>`;
|
|
29217
29554
|
};
|
|
29218
29555
|
var createVoiceProviderContractMatrixJSONHandler = (matrix) => async () => buildVoiceProviderContractMatrix(await resolveProviderContractMatrixInput(matrix));
|
|
29219
29556
|
var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
|
|
@@ -29228,7 +29565,7 @@ var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
|
|
|
29228
29565
|
var createVoiceProviderContractMatrixRoutes = (options) => {
|
|
29229
29566
|
const path = options.path ?? "/api/provider-contracts";
|
|
29230
29567
|
const htmlPath = options.htmlPath ?? "/provider-contracts";
|
|
29231
|
-
const routes = new
|
|
29568
|
+
const routes = new Elysia45({
|
|
29232
29569
|
name: options.name ?? "absolutejs-voice-provider-contract-matrix"
|
|
29233
29570
|
});
|
|
29234
29571
|
const jsonHandler = createVoiceProviderContractMatrixJSONHandler(options.matrix);
|
|
@@ -29346,7 +29683,7 @@ var assertVoiceProviderStackEvidence = (report, input = {}) => {
|
|
|
29346
29683
|
return assertion;
|
|
29347
29684
|
};
|
|
29348
29685
|
// src/opsConsoleRoutes.ts
|
|
29349
|
-
import { Elysia as
|
|
29686
|
+
import { Elysia as Elysia46 } from "elysia";
|
|
29350
29687
|
var DEFAULT_LINKS = [
|
|
29351
29688
|
{
|
|
29352
29689
|
description: "Quality gates for CI, deploy checks, and production readiness.",
|
|
@@ -29381,7 +29718,7 @@ var DEFAULT_LINKS = [
|
|
|
29381
29718
|
label: "Handoffs"
|
|
29382
29719
|
}
|
|
29383
29720
|
];
|
|
29384
|
-
var
|
|
29721
|
+
var escapeHtml43 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
29385
29722
|
var countProviderStatuses = (providers) => {
|
|
29386
29723
|
const degradedStatuses = new Set(["degraded", "rate-limited", "suppressed"]);
|
|
29387
29724
|
const healthy = providers.filter((provider) => provider.status === "healthy").length;
|
|
@@ -29450,20 +29787,20 @@ var buildVoiceOpsConsoleReport = async (options) => {
|
|
|
29450
29787
|
trace
|
|
29451
29788
|
};
|
|
29452
29789
|
};
|
|
29453
|
-
var renderMetricCard = (input) => `<article class="metric"><span>${
|
|
29790
|
+
var renderMetricCard = (input) => `<article class="metric"><span>${escapeHtml43(input.label)}</span><strong>${escapeHtml43(String(input.value))}</strong>${input.status ? `<p class="${escapeHtml43(input.status)}">${escapeHtml43(input.status)}</p>` : ""}${input.href ? `<a href="${escapeHtml43(input.href)}">Open</a>` : ""}</article>`;
|
|
29454
29791
|
var renderVoiceOpsConsoleHTML = (report, options = {}) => {
|
|
29455
29792
|
const links = report.links.map((link) => `<article class="surface">
|
|
29456
|
-
<div><h2>${
|
|
29457
|
-
<p><a href="${
|
|
29793
|
+
<div><h2>${escapeHtml43(link.label)}</h2>${link.description ? `<p>${escapeHtml43(link.description)}</p>` : ""}</div>
|
|
29794
|
+
<p><a href="${escapeHtml43(link.href)}">Open ${escapeHtml43(link.label)}</a>${link.statusHref ? ` \xB7 <a href="${escapeHtml43(link.statusHref)}">Status</a>` : ""}</p>
|
|
29458
29795
|
</article>`).join("");
|
|
29459
|
-
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${
|
|
29460
|
-
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${
|
|
29796
|
+
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${escapeHtml43(session.sessionId)}</td><td>${escapeHtml43(session.status)}</td><td>${session.turnCount}</td><td>${session.errorCount}</td><td>${session.replayHref ? `<a href="${escapeHtml43(session.replayHref)}">Replay</a>` : ""}</td></tr>`).join("") : '<tr><td colspan="5">No sessions yet.</td></tr>';
|
|
29797
|
+
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${escapeHtml43(event.kind)}</td><td>${escapeHtml43(event.provider ?? "unknown")}</td><td>${escapeHtml43(event.status ?? "unknown")}</td><td>${event.elapsedMs ?? 0}ms</td><td>${escapeHtml43(event.sessionId)}</td></tr>`).join("") : '<tr><td colspan="5">No provider routing events yet.</td></tr>';
|
|
29461
29798
|
const title = options.title ?? "AbsoluteJS Voice Ops Console";
|
|
29462
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
29799
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml43(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;background:#101316;color:#f6f2e8;margin:0}main{max-width:1180px;margin:auto;padding:32px}a{color:#fbbf24}header{display:flex;justify-content:space-between;gap:24px;align-items:flex-start;margin-bottom:24px}.eyebrow{color:#fbbf24;font-weight:800;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.2rem,5vw,4.5rem);line-height:.95;margin:.2rem 0 1rem}.muted{color:#a8b0b8}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.metric,.surface{background:#181d22;border:1px solid #2a323a;border-radius:20px;padding:18px}.metric strong{display:block;font-size:2.2rem;margin:.25rem 0}.pass,.healthy{color:#86efac}.fail,.failed,.degraded{color:#fca5a5}.surfaces{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));margin:24px 0}table{width:100%;border-collapse:collapse;background:#181d22;border-radius:16px;overflow:hidden;margin:12px 0 28px}td,th{border-bottom:1px solid #2a323a;padding:12px;text-align:left}section{margin-top:30px}@media(max-width:700px){main{padding:20px}header{display:block}}</style></head><body><main><header><div><p class="eyebrow">Self-hosted voice operations</p><h1>${escapeHtml43(title)}</h1><p class="muted">One deployable control plane for quality gates, failover, traces, sessions, handoffs, and provider health.</p></div><p class="muted">Checked ${escapeHtml43(new Date(report.checkedAt).toLocaleString())}</p></header><div class="grid">${renderMetricCard({ label: "Quality", value: report.quality.status, status: report.quality.status, href: "/quality" })}${renderMetricCard({ label: "Events", value: report.eventCount, href: "/diagnostics" })}${renderMetricCard({ label: "Sessions", value: report.sessions.total, status: report.sessions.failed > 0 ? "failed" : "healthy", href: "/sessions" })}${renderMetricCard({ label: "Handoffs failed", value: report.handoffs.failed, status: report.handoffs.failed > 0 ? "failed" : "healthy", href: "/handoffs" })}${renderMetricCard({ label: "Providers degraded", value: report.providers.degraded, status: report.providers.degraded > 0 ? "degraded" : "healthy", href: "/resilience" })}</div><section><h2>Operational Surfaces</h2><div class="surfaces">${links}</div></section><section><h2>Recent Sessions</h2><table><thead><tr><th>Session</th><th>Status</th><th>Turns</th><th>Errors</th><th>Replay</th></tr></thead><tbody>${sessions}</tbody></table></section><section><h2>Recent Provider Routing</h2><table><thead><tr><th>Kind</th><th>Provider</th><th>Status</th><th>Elapsed</th><th>Session</th></tr></thead><tbody>${routing}</tbody></table></section></main></body></html>`;
|
|
29463
29800
|
};
|
|
29464
29801
|
var createVoiceOpsConsoleRoutes = (options) => {
|
|
29465
29802
|
const path = options.path ?? "/ops-console";
|
|
29466
|
-
const routes = new
|
|
29803
|
+
const routes = new Elysia46({
|
|
29467
29804
|
name: options.name ?? "absolutejs-voice-ops-console"
|
|
29468
29805
|
});
|
|
29469
29806
|
const getReport = () => buildVoiceOpsConsoleReport(options);
|
|
@@ -29480,7 +29817,7 @@ var createVoiceOpsConsoleRoutes = (options) => {
|
|
|
29480
29817
|
return routes;
|
|
29481
29818
|
};
|
|
29482
29819
|
// src/incidentBundle.ts
|
|
29483
|
-
import { Elysia as
|
|
29820
|
+
import { Elysia as Elysia47 } from "elysia";
|
|
29484
29821
|
var filterIncidentBundleArtifacts = (artifacts, filter = {}) => artifacts.filter((artifact) => {
|
|
29485
29822
|
if (filter.sessionId && artifact.sessionId !== filter.sessionId) {
|
|
29486
29823
|
return false;
|
|
@@ -29681,7 +30018,7 @@ var buildVoiceIncidentBundle = async (options) => {
|
|
|
29681
30018
|
var createVoiceIncidentBundleRoutes = (options) => {
|
|
29682
30019
|
const path = options.path ?? "/api/voice-incidents/:sessionId";
|
|
29683
30020
|
const markdownPath = options.markdownPath === undefined ? "/voice-incidents/:sessionId/markdown" : options.markdownPath;
|
|
29684
|
-
const routes = new
|
|
30021
|
+
const routes = new Elysia47({
|
|
29685
30022
|
name: options.name ?? "absolutejs-voice-incident-bundle"
|
|
29686
30023
|
});
|
|
29687
30024
|
const getSessionId = (params) => params.sessionId ?? "";
|
|
@@ -29882,19 +30219,19 @@ var summarizeVoiceOpsStatus = async (options) => {
|
|
|
29882
30219
|
};
|
|
29883
30220
|
};
|
|
29884
30221
|
// src/opsStatusRoutes.ts
|
|
29885
|
-
import { Elysia as
|
|
29886
|
-
var
|
|
30222
|
+
import { Elysia as Elysia48 } from "elysia";
|
|
30223
|
+
var escapeHtml44 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
29887
30224
|
var renderVoiceOpsStatusHTML = (report, options = {}) => {
|
|
29888
30225
|
const title = options.title ?? "AbsoluteJS Voice Ops Status";
|
|
29889
30226
|
const surfaces = Object.entries(report.surfaces).map(([key, surface]) => {
|
|
29890
30227
|
const value = "recovered" in surface ? surface.total === 0 ? "0 events" : `${surface.recovered}/${surface.total}` : ("auditTotal" in surface) ? `${surface.auditTotal + surface.traceTotal} deliveries` : ("total" in surface) ? `${Math.max(surface.total - ("failed" in surface ? surface.failed : ("degraded" in surface) ? surface.degraded : 0), 0)}/${surface.total}` : surface.status;
|
|
29891
|
-
return `<article class="surface ${
|
|
30228
|
+
return `<article class="surface ${escapeHtml44(surface.status)}"><span>${escapeHtml44(surface.status.toUpperCase())}</span><h2>${escapeHtml44(key)}</h2><strong>${escapeHtml44(value)}</strong></article>`;
|
|
29892
30229
|
}).join("");
|
|
29893
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
30230
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml44(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>${escapeHtml44(title)}</h1><p>Compact pass/fail status for framework widgets, demos, and small customer-facing health badges.</p><p class="status ${escapeHtml44(report.status)}">Overall: ${escapeHtml44(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>`;
|
|
29894
30231
|
};
|
|
29895
30232
|
var createVoiceOpsStatusRoutes = (options) => {
|
|
29896
30233
|
const path = options.path ?? "/api/voice/ops-status";
|
|
29897
|
-
const routes = new
|
|
30234
|
+
const routes = new Elysia48({
|
|
29898
30235
|
name: options.name ?? "absolutejs-voice-ops-status"
|
|
29899
30236
|
});
|
|
29900
30237
|
routes.get(path, async () => summarizeVoiceOpsStatus(options));
|
|
@@ -30327,8 +30664,8 @@ var createVoiceTTSProviderRouter = (options) => {
|
|
|
30327
30664
|
};
|
|
30328
30665
|
};
|
|
30329
30666
|
// src/traceDeliveryRoutes.ts
|
|
30330
|
-
import { Elysia as
|
|
30331
|
-
var
|
|
30667
|
+
import { Elysia as Elysia49 } from "elysia";
|
|
30668
|
+
var escapeHtml45 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
30332
30669
|
var getString19 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
30333
30670
|
var getNumber11 = (value) => {
|
|
30334
30671
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -30409,14 +30746,14 @@ var renderSinkResults2 = (delivery) => {
|
|
|
30409
30746
|
if (entries.length === 0) {
|
|
30410
30747
|
return "<p>No sink delivery attempts recorded yet.</p>";
|
|
30411
30748
|
}
|
|
30412
|
-
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${
|
|
30749
|
+
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${escapeHtml45(sinkId)}</strong>: ${escapeHtml45(result.status)}${result.deliveredTo ? ` to ${escapeHtml45(result.deliveredTo)}` : ""}${result.error ? ` (${escapeHtml45(result.error)})` : ""}</li>`).join("")}</ul>`;
|
|
30413
30750
|
};
|
|
30414
|
-
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${
|
|
30751
|
+
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${escapeHtml45(event.type)} <small>${escapeHtml45(event.id)}</small>${event.sessionId ? ` session=${escapeHtml45(event.sessionId)}` : ""}</li>`).join("")}</ul>`;
|
|
30415
30752
|
var renderVoiceTraceDeliveryHTML = (report, options = {}) => {
|
|
30416
30753
|
const title = options.title ?? "AbsoluteJS Voice Trace Deliveries";
|
|
30417
|
-
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${
|
|
30418
|
-
const rows = report.deliveries.map((delivery) => `<article class="delivery ${
|
|
30419
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
30754
|
+
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${escapeHtml45(options.workerPath ?? "/api/voice-trace-deliveries/drain")}"><button type="submit">Drain trace deliveries</button></form>`;
|
|
30755
|
+
const rows = report.deliveries.map((delivery) => `<article class="delivery ${escapeHtml45(delivery.deliveryStatus)}"><div class="head"><div><span>${escapeHtml45(delivery.deliveryStatus)}</span><h2>${escapeHtml45(delivery.id)}</h2><p>${escapeHtml45(new Date(delivery.createdAt).toLocaleString())}${delivery.deliveredAt ? ` \xB7 delivered ${escapeHtml45(new Date(delivery.deliveredAt).toLocaleString())}` : ""}</p></div><strong>${String(delivery.deliveryAttempts ?? 0)} attempt(s)</strong></div>${delivery.deliveryError ? `<p class="error">${escapeHtml45(delivery.deliveryError)}</p>` : ""}<h3>Sinks</h3>${renderSinkResults2(delivery)}<h3>Events</h3>${renderEventList2(delivery)}</article>`).join("");
|
|
30756
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml45(title)}</title><style>body{background:#0f1318;color:#f4efe1;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.16),rgba(14,165,233,.14));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#86efac;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,5vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.grid{display:grid;gap:12px;grid-template-columns:repeat(4,1fr);margin-bottom:16px}.grid article,.delivery{background:#151b22;border:1px solid #26313d;border-radius:22px;padding:18px}.grid span,.delivery span{color:#86efac;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.grid strong{display:block;font-size:2rem}.deliveries{display:grid;gap:14px}.delivery.failed{border-color:rgba(239,68,68,.75)}.delivery.pending{border-color:rgba(245,158,11,.7)}.delivery.delivered{border-color:rgba(34,197,94,.55)}.delivery.skipped{border-color:rgba(148,163,184,.6)}.head{align-items:start;display:flex;gap:14px;justify-content:space-between}.delivery h2{font-size:1.05rem;margin:.3rem 0;overflow-wrap:anywhere}.delivery h3{margin:1rem 0 .3rem}.delivery p,.delivery li{color:#c8d0d8}.error{color:#fecaca!important}button{background:#86efac;border:0;border-radius:999px;color:#07111f;cursor:pointer;font-weight:900;margin-top:12px;padding:10px 14px}@media(max-width:760px){main{padding:20px}.grid{grid-template-columns:1fr 1fr}.head{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Trace export health</p><h1>${escapeHtml45(title)}</h1><p>Checked ${escapeHtml45(new Date(report.checkedAt).toLocaleString())}. Showing ${String(report.deliveries.length)} delivery item(s).</p>${drainAction}</section>${renderMetricGrid3(report)}<section class="deliveries">${rows || "<p>No trace deliveries match this filter.</p>"}</section></main></body></html>`;
|
|
30420
30757
|
};
|
|
30421
30758
|
var createVoiceTraceDeliveryJSONHandler = (options) => async ({ query }) => buildVoiceTraceDeliveryReport(options, resolveVoiceTraceDeliveryFilter(query, options.filter));
|
|
30422
30759
|
var createVoiceTraceDeliveryHTMLHandler = (options) => async ({ query }) => {
|
|
@@ -30436,7 +30773,7 @@ var createVoiceTraceDeliveryRoutes = (options) => {
|
|
|
30436
30773
|
const path = options.path ?? "/api/voice-trace-deliveries";
|
|
30437
30774
|
const htmlPath = options.htmlPath === undefined ? "/traces/deliveries" : options.htmlPath;
|
|
30438
30775
|
const workerPath = options.workerPath === undefined ? `${path}/drain` : options.workerPath;
|
|
30439
|
-
const routes = new
|
|
30776
|
+
const routes = new Elysia49({
|
|
30440
30777
|
name: options.name ?? "absolutejs-voice-trace-deliveries"
|
|
30441
30778
|
}).get(path, createVoiceTraceDeliveryJSONHandler(options));
|
|
30442
30779
|
if (htmlPath !== false) {
|
|
@@ -30533,7 +30870,7 @@ var createVoiceMemoryStore = () => {
|
|
|
30533
30870
|
return { get, getOrCreate, list, remove, set };
|
|
30534
30871
|
};
|
|
30535
30872
|
// src/opsWebhook.ts
|
|
30536
|
-
import { Elysia as
|
|
30873
|
+
import { Elysia as Elysia50 } from "elysia";
|
|
30537
30874
|
var toHex6 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
30538
30875
|
var signVoiceOpsWebhookBody = async (input) => {
|
|
30539
30876
|
const encoder = new TextEncoder;
|
|
@@ -30663,7 +31000,7 @@ var verifyVoiceOpsWebhookSignature = async (input) => {
|
|
|
30663
31000
|
};
|
|
30664
31001
|
var createVoiceOpsWebhookReceiverRoutes = (options = {}) => {
|
|
30665
31002
|
const path = options.path ?? "/api/voice-ops/webhook";
|
|
30666
|
-
return new
|
|
31003
|
+
return new Elysia50().post(path, async ({ body, request, set }) => {
|
|
30667
31004
|
const bodyText = typeof body === "string" ? body : JSON.stringify(body);
|
|
30668
31005
|
if (options.signingSecret) {
|
|
30669
31006
|
const verification = await verifyVoiceOpsWebhookSignature({
|
|
@@ -31118,7 +31455,7 @@ var resolveVoiceOpsPreset = (name, overrides = {}) => {
|
|
|
31118
31455
|
};
|
|
31119
31456
|
};
|
|
31120
31457
|
// src/postCallAnalysis.ts
|
|
31121
|
-
import { Elysia as
|
|
31458
|
+
import { Elysia as Elysia51 } from "elysia";
|
|
31122
31459
|
var isStore = (value) => Boolean(value) && typeof value === "object" && value !== null && ("list" in value);
|
|
31123
31460
|
var asArray = async (value) => Array.isArray(value) ? value : isStore(value) ? await value.list() : [];
|
|
31124
31461
|
var getPathValue3 = (source, path) => {
|
|
@@ -31297,7 +31634,7 @@ var resolvePostCallAnalysisReport = async (options, input) => {
|
|
|
31297
31634
|
};
|
|
31298
31635
|
var createVoicePostCallAnalysisRoutes = (options = {}) => {
|
|
31299
31636
|
const path = options.path ?? "/api/voice/post-call-analysis";
|
|
31300
|
-
const routes = new
|
|
31637
|
+
const routes = new Elysia51({
|
|
31301
31638
|
name: options.name ?? "absolutejs-voice-post-call-analysis"
|
|
31302
31639
|
});
|
|
31303
31640
|
routes.get(path, async ({ query }) => {
|
|
@@ -31322,7 +31659,7 @@ var createVoicePostCallAnalysisRoutes = (options = {}) => {
|
|
|
31322
31659
|
return routes;
|
|
31323
31660
|
};
|
|
31324
31661
|
// src/guardrails.ts
|
|
31325
|
-
import { Elysia as
|
|
31662
|
+
import { Elysia as Elysia52 } from "elysia";
|
|
31326
31663
|
var stringifyContent = (value) => typeof value === "string" ? value : JSON.stringify(value) ?? "";
|
|
31327
31664
|
var appliesToStage = (rule, stage) => !rule.stages || rule.stages.length === 0 || rule.stages.includes(stage);
|
|
31328
31665
|
var matchesRule = async (rule, input) => {
|
|
@@ -31624,7 +31961,7 @@ var resolveGuardrailReport = async (options, input) => {
|
|
|
31624
31961
|
};
|
|
31625
31962
|
var createVoiceGuardrailRoutes = (options = {}) => {
|
|
31626
31963
|
const path = options.path ?? "/api/voice/guardrails";
|
|
31627
|
-
const routes = new
|
|
31964
|
+
const routes = new Elysia52({
|
|
31628
31965
|
name: options.name ?? "absolutejs-voice-guardrails"
|
|
31629
31966
|
});
|
|
31630
31967
|
routes.all(path, async ({ request }) => {
|
|
@@ -32120,6 +32457,7 @@ export {
|
|
|
32120
32457
|
resolveVoiceOpsTaskAssignment,
|
|
32121
32458
|
resolveVoiceOpsTaskAgeBucket,
|
|
32122
32459
|
resolveVoiceOpsPreset,
|
|
32460
|
+
resolveVoiceMonitorIssue,
|
|
32123
32461
|
resolveVoiceDiagnosticsTraceFilter,
|
|
32124
32462
|
resolveVoiceAuditTrailFilter,
|
|
32125
32463
|
resolveVoiceAuditDeliveryFilter,
|
|
@@ -32164,6 +32502,8 @@ export {
|
|
|
32164
32502
|
renderVoiceOperationsRecordGuardrailMarkdown,
|
|
32165
32503
|
renderVoiceObservabilityExportReplayHTML,
|
|
32166
32504
|
renderVoiceObservabilityExportMarkdown,
|
|
32505
|
+
renderVoiceMonitorMarkdown,
|
|
32506
|
+
renderVoiceMonitorHTML,
|
|
32167
32507
|
renderVoiceLiveLatencyHTML,
|
|
32168
32508
|
renderVoiceLatencySLOMarkdown,
|
|
32169
32509
|
renderVoiceHandoffHealthHTML,
|
|
@@ -32206,6 +32546,7 @@ export {
|
|
|
32206
32546
|
pruneVoiceIncidentBundleArtifacts,
|
|
32207
32547
|
parseVoiceTelephonyWebhookEvent,
|
|
32208
32548
|
normalizeVoiceProofTrendReport,
|
|
32549
|
+
muteVoiceMonitorIssue,
|
|
32209
32550
|
matchesVoiceOpsTaskAssignmentRule,
|
|
32210
32551
|
markVoiceOpsTaskSLABreached,
|
|
32211
32552
|
loadVoiceObservabilityExportReplaySource,
|
|
@@ -32254,6 +32595,7 @@ export {
|
|
|
32254
32595
|
encodeTwilioMulawBase64,
|
|
32255
32596
|
deliverVoiceTraceEventsToSinks,
|
|
32256
32597
|
deliverVoiceObservabilityExport,
|
|
32598
|
+
deliverVoiceMonitorIssueNotifications,
|
|
32257
32599
|
deliverVoiceIntegrationEventToSinks,
|
|
32258
32600
|
deliverVoiceIntegrationEvent,
|
|
32259
32601
|
deliverVoiceHandoffDelivery,
|
|
@@ -32408,10 +32750,14 @@ export {
|
|
|
32408
32750
|
createVoiceObservabilityExportSchema,
|
|
32409
32751
|
createVoiceObservabilityExportRoutes,
|
|
32410
32752
|
createVoiceObservabilityExportReplayRoutes,
|
|
32753
|
+
createVoiceMonitorWebhookNotifier,
|
|
32754
|
+
createVoiceMonitorRoutes,
|
|
32411
32755
|
createVoiceMemoryTraceSinkDeliveryStore,
|
|
32412
32756
|
createVoiceMemoryTraceEventStore,
|
|
32413
32757
|
createVoiceMemoryStore,
|
|
32414
32758
|
createVoiceMemoryObservabilityExportDeliveryReceiptStore,
|
|
32759
|
+
createVoiceMemoryMonitorNotifierDeliveryReceiptStore,
|
|
32760
|
+
createVoiceMemoryMonitorIssueStore,
|
|
32415
32761
|
createVoiceMemoryLiveOpsControlStore,
|
|
32416
32762
|
createVoiceMemoryIncidentBundleStore,
|
|
32417
32763
|
createVoiceMemoryHandoffDeliveryStore,
|
|
@@ -32559,6 +32905,7 @@ export {
|
|
|
32559
32905
|
buildVoiceObservabilityExportDeliveryHistory,
|
|
32560
32906
|
buildVoiceObservabilityExport,
|
|
32561
32907
|
buildVoiceObservabilityArtifactIndex,
|
|
32908
|
+
buildVoiceMonitorRunReport,
|
|
32562
32909
|
buildVoiceLiveOpsControlState,
|
|
32563
32910
|
buildVoiceLatencySLOGate,
|
|
32564
32911
|
buildVoiceIncidentBundle,
|
|
@@ -32611,6 +32958,7 @@ export {
|
|
|
32611
32958
|
applyVoiceCampaignTelephonyOutcome,
|
|
32612
32959
|
applyRiskTieredPhraseHintCorrections,
|
|
32613
32960
|
applyPhraseHintCorrections,
|
|
32961
|
+
acknowledgeVoiceMonitorIssue,
|
|
32614
32962
|
VOICE_LIVE_OPS_ACTIONS,
|
|
32615
32963
|
TURN_PROFILE_DEFAULTS,
|
|
32616
32964
|
DEFAULT_VOICE_PROOF_TRENDS_MAX_AGE_MS
|
|
@@ -2,6 +2,7 @@ import { Elysia } from 'elysia';
|
|
|
2
2
|
import { type VoiceProviderFallbackRecoverySummary } from './sessionReplay';
|
|
3
3
|
import { type VoiceTelephonyCarrierMatrixInput } from './telephony/matrix';
|
|
4
4
|
import { type VoiceTelephonyWebhookSecurityOptions, type VoiceTelephonyWebhookSecurityReport } from './telephony/security';
|
|
5
|
+
import type { VoiceMonitorNotifierDeliveryReport, VoiceMonitorRunReport } from './voiceMonitoring';
|
|
5
6
|
import type { VoiceTraceEventStore } from './trace';
|
|
6
7
|
import type { VoiceTraceSinkDeliveryStore } from './trace';
|
|
7
8
|
import type { VoiceAgentSquadContractReport } from './agentSquadContract';
|
|
@@ -118,6 +119,8 @@ export type VoiceProductionReadinessReport = {
|
|
|
118
119
|
operationsRecords?: string;
|
|
119
120
|
observabilityExport?: string;
|
|
120
121
|
observabilityExportDeliveries?: string;
|
|
122
|
+
monitoring?: string;
|
|
123
|
+
monitoringNotifierDelivery?: string;
|
|
121
124
|
opsActions?: string;
|
|
122
125
|
opsRecovery?: string;
|
|
123
126
|
phoneAgentSmoke?: string;
|
|
@@ -176,6 +179,19 @@ export type VoiceProductionReadinessReport = {
|
|
|
176
179
|
total: number;
|
|
177
180
|
warnings: number;
|
|
178
181
|
};
|
|
182
|
+
monitoring?: {
|
|
183
|
+
criticalOpen: number;
|
|
184
|
+
open: number;
|
|
185
|
+
status: VoiceProductionReadinessStatus;
|
|
186
|
+
total: number;
|
|
187
|
+
};
|
|
188
|
+
monitoringNotifierDelivery?: {
|
|
189
|
+
failed: number;
|
|
190
|
+
notifiers: number;
|
|
191
|
+
sent: number;
|
|
192
|
+
status: VoiceProductionReadinessStatus;
|
|
193
|
+
total: number;
|
|
194
|
+
};
|
|
179
195
|
opsActionHistory?: VoiceProductionReadinessOpsActionHistorySummary;
|
|
180
196
|
opsRecovery?: {
|
|
181
197
|
issues: number;
|
|
@@ -393,6 +409,14 @@ export type VoiceProductionReadinessRoutesOptions = {
|
|
|
393
409
|
links?: VoiceProductionReadinessReport['links'];
|
|
394
410
|
llmProviders?: readonly string[];
|
|
395
411
|
name?: string;
|
|
412
|
+
monitoring?: false | VoiceMonitorRunReport | ((input: {
|
|
413
|
+
query: Record<string, unknown>;
|
|
414
|
+
request: Request;
|
|
415
|
+
}) => Promise<VoiceMonitorRunReport> | VoiceMonitorRunReport);
|
|
416
|
+
monitoringNotifierDelivery?: false | VoiceMonitorNotifierDeliveryReport | ((input: {
|
|
417
|
+
query: Record<string, unknown>;
|
|
418
|
+
request: Request;
|
|
419
|
+
}) => Promise<VoiceMonitorNotifierDeliveryReport> | VoiceMonitorNotifierDeliveryReport);
|
|
396
420
|
opsActionHistory?: false | VoiceProductionReadinessOpsActionHistoryOptions;
|
|
397
421
|
opsRecovery?: false | VoiceOpsRecoveryReport | ((input: {
|
|
398
422
|
query: Record<string, unknown>;
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { Elysia } from 'elysia';
|
|
2
|
+
export type VoiceMonitorStatus = 'fail' | 'pass' | 'warn';
|
|
3
|
+
export type VoiceMonitorSeverity = 'critical' | 'info' | 'warn';
|
|
4
|
+
export type VoiceMonitorIssueStatus = 'acknowledged' | 'muted' | 'open' | 'resolved';
|
|
5
|
+
export type VoiceMonitorEvaluationInput<TEvidence = unknown> = {
|
|
6
|
+
evidence: TEvidence;
|
|
7
|
+
now: number;
|
|
8
|
+
};
|
|
9
|
+
export type VoiceMonitorEvaluation = {
|
|
10
|
+
detail?: string;
|
|
11
|
+
impactedSessions?: readonly string[];
|
|
12
|
+
operationsRecordHrefs?: readonly string[];
|
|
13
|
+
status: VoiceMonitorStatus;
|
|
14
|
+
threshold?: number | string;
|
|
15
|
+
value?: number | string;
|
|
16
|
+
};
|
|
17
|
+
export type VoiceMonitorDefinition<TEvidence = unknown> = {
|
|
18
|
+
description?: string;
|
|
19
|
+
id: string;
|
|
20
|
+
label: string;
|
|
21
|
+
severity?: VoiceMonitorSeverity;
|
|
22
|
+
windowMs?: number;
|
|
23
|
+
evaluate: (input: VoiceMonitorEvaluationInput<TEvidence>) => Promise<VoiceMonitorEvaluation> | VoiceMonitorEvaluation;
|
|
24
|
+
};
|
|
25
|
+
export type VoiceMonitorRun = VoiceMonitorEvaluation & {
|
|
26
|
+
checkedAt: number;
|
|
27
|
+
description?: string;
|
|
28
|
+
id: string;
|
|
29
|
+
label: string;
|
|
30
|
+
severity: VoiceMonitorSeverity;
|
|
31
|
+
windowMs?: number;
|
|
32
|
+
};
|
|
33
|
+
export type VoiceMonitorIssue = {
|
|
34
|
+
acknowledgedAt?: number;
|
|
35
|
+
acknowledgedBy?: string;
|
|
36
|
+
createdAt: number;
|
|
37
|
+
detail?: string;
|
|
38
|
+
id: string;
|
|
39
|
+
impactedSessions: string[];
|
|
40
|
+
label: string;
|
|
41
|
+
lastSeenAt: number;
|
|
42
|
+
monitorId: string;
|
|
43
|
+
mutedAt?: number;
|
|
44
|
+
mutedBy?: string;
|
|
45
|
+
operationsRecordHrefs: string[];
|
|
46
|
+
resolvedAt?: number;
|
|
47
|
+
resolvedBy?: string;
|
|
48
|
+
severity: VoiceMonitorSeverity;
|
|
49
|
+
status: VoiceMonitorIssueStatus;
|
|
50
|
+
threshold?: number | string;
|
|
51
|
+
value?: number | string;
|
|
52
|
+
};
|
|
53
|
+
export type VoiceMonitorIssueStore = {
|
|
54
|
+
list: () => Promise<VoiceMonitorIssue[]> | VoiceMonitorIssue[];
|
|
55
|
+
upsert: (issue: VoiceMonitorIssue) => Promise<VoiceMonitorIssue> | VoiceMonitorIssue;
|
|
56
|
+
update: (id: string, patch: Partial<VoiceMonitorIssue>) => Promise<VoiceMonitorIssue | undefined> | VoiceMonitorIssue | undefined;
|
|
57
|
+
};
|
|
58
|
+
export type VoiceMonitorNotifier = {
|
|
59
|
+
deliver: (input: VoiceMonitorNotifierDeliveryInput) => Promise<VoiceMonitorNotifierDeliveryResult> | VoiceMonitorNotifierDeliveryResult;
|
|
60
|
+
id: string;
|
|
61
|
+
label: string;
|
|
62
|
+
};
|
|
63
|
+
export type VoiceMonitorNotifierDeliveryInput = {
|
|
64
|
+
issue: VoiceMonitorIssue;
|
|
65
|
+
now: number;
|
|
66
|
+
};
|
|
67
|
+
export type VoiceMonitorNotifierDeliveryResult = {
|
|
68
|
+
detail?: string;
|
|
69
|
+
status: 'failed' | 'sent' | 'skipped';
|
|
70
|
+
};
|
|
71
|
+
export type VoiceMonitorNotifierDeliveryReceipt = {
|
|
72
|
+
detail?: string;
|
|
73
|
+
id: string;
|
|
74
|
+
issueId: string;
|
|
75
|
+
notifierId: string;
|
|
76
|
+
notifierLabel: string;
|
|
77
|
+
sentAt: number;
|
|
78
|
+
status: 'failed' | 'sent' | 'skipped';
|
|
79
|
+
};
|
|
80
|
+
export type VoiceMonitorNotifierDeliveryReceiptStore = {
|
|
81
|
+
list: () => Promise<VoiceMonitorNotifierDeliveryReceipt[]> | VoiceMonitorNotifierDeliveryReceipt[];
|
|
82
|
+
set: (id: string, receipt: VoiceMonitorNotifierDeliveryReceipt) => Promise<VoiceMonitorNotifierDeliveryReceipt> | VoiceMonitorNotifierDeliveryReceipt;
|
|
83
|
+
};
|
|
84
|
+
export type VoiceMonitorNotifierDeliveryReport = {
|
|
85
|
+
checkedAt: number;
|
|
86
|
+
receipts: VoiceMonitorNotifierDeliveryReceipt[];
|
|
87
|
+
status: VoiceMonitorStatus;
|
|
88
|
+
summary: {
|
|
89
|
+
failed: number;
|
|
90
|
+
notifiers: number;
|
|
91
|
+
sent: number;
|
|
92
|
+
skipped: number;
|
|
93
|
+
total: number;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
export type VoiceMonitorRunReport = {
|
|
97
|
+
checkedAt: number;
|
|
98
|
+
issues: VoiceMonitorIssue[];
|
|
99
|
+
runs: VoiceMonitorRun[];
|
|
100
|
+
status: VoiceMonitorStatus;
|
|
101
|
+
summary: {
|
|
102
|
+
acknowledged: number;
|
|
103
|
+
criticalOpen: number;
|
|
104
|
+
failed: number;
|
|
105
|
+
muted: number;
|
|
106
|
+
open: number;
|
|
107
|
+
passed: number;
|
|
108
|
+
resolved: number;
|
|
109
|
+
total: number;
|
|
110
|
+
warned: number;
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
export type VoiceMonitorRunOptions<TEvidence = unknown> = {
|
|
114
|
+
evidence: TEvidence;
|
|
115
|
+
issueStore?: VoiceMonitorIssueStore;
|
|
116
|
+
monitors: readonly VoiceMonitorDefinition<TEvidence>[];
|
|
117
|
+
now?: number;
|
|
118
|
+
};
|
|
119
|
+
export type VoiceMonitorNotifierDeliveryOptions = {
|
|
120
|
+
issueStore: VoiceMonitorIssueStore;
|
|
121
|
+
notifiers: readonly VoiceMonitorNotifier[];
|
|
122
|
+
now?: number;
|
|
123
|
+
receiptStore?: VoiceMonitorNotifierDeliveryReceiptStore;
|
|
124
|
+
statuses?: readonly VoiceMonitorIssueStatus[];
|
|
125
|
+
};
|
|
126
|
+
export type VoiceMonitorWebhookNotifierOptions = {
|
|
127
|
+
fetch?: typeof fetch;
|
|
128
|
+
headers?: HeadersInit;
|
|
129
|
+
id: string;
|
|
130
|
+
label?: string;
|
|
131
|
+
mapIssue?: (issue: VoiceMonitorIssue) => unknown;
|
|
132
|
+
url: string;
|
|
133
|
+
};
|
|
134
|
+
export type VoiceMonitorRoutesOptions<TEvidence = unknown> = VoiceMonitorRunOptions<TEvidence> & {
|
|
135
|
+
headers?: HeadersInit;
|
|
136
|
+
htmlPath?: false | string;
|
|
137
|
+
issuePath?: string;
|
|
138
|
+
name?: string;
|
|
139
|
+
notifierPath?: false | string;
|
|
140
|
+
notifiers?: readonly VoiceMonitorNotifier[];
|
|
141
|
+
path?: string;
|
|
142
|
+
receiptStore?: VoiceMonitorNotifierDeliveryReceiptStore;
|
|
143
|
+
render?: (report: VoiceMonitorRunReport) => Promise<string> | string;
|
|
144
|
+
title?: string;
|
|
145
|
+
};
|
|
146
|
+
export declare const createVoiceMemoryMonitorIssueStore: (initial?: readonly VoiceMonitorIssue[]) => VoiceMonitorIssueStore;
|
|
147
|
+
export declare const createVoiceMemoryMonitorNotifierDeliveryReceiptStore: (initial?: readonly VoiceMonitorNotifierDeliveryReceipt[]) => VoiceMonitorNotifierDeliveryReceiptStore;
|
|
148
|
+
export declare const buildVoiceMonitorRunReport: <TEvidence = unknown>(options: VoiceMonitorRunOptions<TEvidence>) => Promise<VoiceMonitorRunReport>;
|
|
149
|
+
export declare const acknowledgeVoiceMonitorIssue: (store: VoiceMonitorIssueStore, id: string, input?: {
|
|
150
|
+
actorId?: string;
|
|
151
|
+
now?: number;
|
|
152
|
+
}) => Promise<VoiceMonitorIssue | undefined>;
|
|
153
|
+
export declare const resolveVoiceMonitorIssue: (store: VoiceMonitorIssueStore, id: string, input?: {
|
|
154
|
+
actorId?: string;
|
|
155
|
+
now?: number;
|
|
156
|
+
}) => Promise<VoiceMonitorIssue | undefined>;
|
|
157
|
+
export declare const muteVoiceMonitorIssue: (store: VoiceMonitorIssueStore, id: string, input?: {
|
|
158
|
+
actorId?: string;
|
|
159
|
+
now?: number;
|
|
160
|
+
}) => Promise<VoiceMonitorIssue | undefined>;
|
|
161
|
+
export declare const createVoiceMonitorWebhookNotifier: (options: VoiceMonitorWebhookNotifierOptions) => VoiceMonitorNotifier;
|
|
162
|
+
export declare const deliverVoiceMonitorIssueNotifications: (options: VoiceMonitorNotifierDeliveryOptions) => Promise<VoiceMonitorNotifierDeliveryReport>;
|
|
163
|
+
export declare const renderVoiceMonitorMarkdown: (report: VoiceMonitorRunReport) => string;
|
|
164
|
+
export declare const renderVoiceMonitorHTML: (report: VoiceMonitorRunReport, options?: {
|
|
165
|
+
title?: string;
|
|
166
|
+
}) => string;
|
|
167
|
+
export declare const createVoiceMonitorRoutes: <TEvidence = unknown>(options: VoiceMonitorRoutesOptions<TEvidence>) => Elysia<"", {
|
|
168
|
+
decorator: {};
|
|
169
|
+
store: {};
|
|
170
|
+
derive: {};
|
|
171
|
+
resolve: {};
|
|
172
|
+
}, {
|
|
173
|
+
typebox: {};
|
|
174
|
+
error: {};
|
|
175
|
+
}, {
|
|
176
|
+
schema: {};
|
|
177
|
+
standaloneSchema: {};
|
|
178
|
+
macro: {};
|
|
179
|
+
macroFn: {};
|
|
180
|
+
parser: {};
|
|
181
|
+
response: {};
|
|
182
|
+
}, {
|
|
183
|
+
[x: string]: {
|
|
184
|
+
get: {
|
|
185
|
+
body: unknown;
|
|
186
|
+
params: {};
|
|
187
|
+
query: unknown;
|
|
188
|
+
headers: unknown;
|
|
189
|
+
response: {
|
|
190
|
+
200: VoiceMonitorRunReport;
|
|
191
|
+
};
|
|
192
|
+
};
|
|
193
|
+
};
|
|
194
|
+
} & {
|
|
195
|
+
[x: `${string}.md`]: {
|
|
196
|
+
get: {
|
|
197
|
+
body: unknown;
|
|
198
|
+
params: {};
|
|
199
|
+
query: unknown;
|
|
200
|
+
headers: unknown;
|
|
201
|
+
response: {
|
|
202
|
+
200: Response;
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
};
|
|
206
|
+
} & {
|
|
207
|
+
[x: string]: {
|
|
208
|
+
get: {
|
|
209
|
+
body: unknown;
|
|
210
|
+
params: {};
|
|
211
|
+
query: unknown;
|
|
212
|
+
headers: unknown;
|
|
213
|
+
response: {
|
|
214
|
+
200: VoiceMonitorIssue[];
|
|
215
|
+
};
|
|
216
|
+
};
|
|
217
|
+
};
|
|
218
|
+
} & {
|
|
219
|
+
[x: string]: {
|
|
220
|
+
notifications: {
|
|
221
|
+
get: {
|
|
222
|
+
body: unknown;
|
|
223
|
+
params: {};
|
|
224
|
+
query: unknown;
|
|
225
|
+
headers: unknown;
|
|
226
|
+
response: {
|
|
227
|
+
200: VoiceMonitorNotifierDeliveryReceipt[];
|
|
228
|
+
};
|
|
229
|
+
};
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
} & {
|
|
233
|
+
[x: string]: {
|
|
234
|
+
":id": {
|
|
235
|
+
acknowledge: {
|
|
236
|
+
post: {
|
|
237
|
+
body: unknown;
|
|
238
|
+
params: {
|
|
239
|
+
id: string;
|
|
240
|
+
} & {};
|
|
241
|
+
query: unknown;
|
|
242
|
+
headers: unknown;
|
|
243
|
+
response: {
|
|
244
|
+
200: Response | VoiceMonitorIssue;
|
|
245
|
+
422: {
|
|
246
|
+
type: "validation";
|
|
247
|
+
on: string;
|
|
248
|
+
summary?: string;
|
|
249
|
+
message?: string;
|
|
250
|
+
found?: unknown;
|
|
251
|
+
property?: string;
|
|
252
|
+
expected?: string;
|
|
253
|
+
};
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
};
|
|
257
|
+
};
|
|
258
|
+
};
|
|
259
|
+
} & {
|
|
260
|
+
[x: string]: {
|
|
261
|
+
":id": {
|
|
262
|
+
resolve: {
|
|
263
|
+
post: {
|
|
264
|
+
body: unknown;
|
|
265
|
+
params: {
|
|
266
|
+
id: string;
|
|
267
|
+
} & {};
|
|
268
|
+
query: unknown;
|
|
269
|
+
headers: unknown;
|
|
270
|
+
response: {
|
|
271
|
+
200: Response | VoiceMonitorIssue;
|
|
272
|
+
422: {
|
|
273
|
+
type: "validation";
|
|
274
|
+
on: string;
|
|
275
|
+
summary?: string;
|
|
276
|
+
message?: string;
|
|
277
|
+
found?: unknown;
|
|
278
|
+
property?: string;
|
|
279
|
+
expected?: string;
|
|
280
|
+
};
|
|
281
|
+
};
|
|
282
|
+
};
|
|
283
|
+
};
|
|
284
|
+
};
|
|
285
|
+
};
|
|
286
|
+
} & {
|
|
287
|
+
[x: string]: {
|
|
288
|
+
":id": {
|
|
289
|
+
mute: {
|
|
290
|
+
post: {
|
|
291
|
+
body: unknown;
|
|
292
|
+
params: {
|
|
293
|
+
id: string;
|
|
294
|
+
} & {};
|
|
295
|
+
query: unknown;
|
|
296
|
+
headers: unknown;
|
|
297
|
+
response: {
|
|
298
|
+
200: Response | VoiceMonitorIssue;
|
|
299
|
+
422: {
|
|
300
|
+
type: "validation";
|
|
301
|
+
on: string;
|
|
302
|
+
summary?: string;
|
|
303
|
+
message?: string;
|
|
304
|
+
found?: unknown;
|
|
305
|
+
property?: string;
|
|
306
|
+
expected?: string;
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
};
|
|
310
|
+
};
|
|
311
|
+
};
|
|
312
|
+
};
|
|
313
|
+
}, {
|
|
314
|
+
derive: {};
|
|
315
|
+
resolve: {};
|
|
316
|
+
schema: {};
|
|
317
|
+
standaloneSchema: {};
|
|
318
|
+
response: {};
|
|
319
|
+
}, {
|
|
320
|
+
derive: {};
|
|
321
|
+
resolve: {};
|
|
322
|
+
schema: {};
|
|
323
|
+
standaloneSchema: {};
|
|
324
|
+
response: {};
|
|
325
|
+
}>;
|