@absolutejs/voice 0.0.22-beta.165 → 0.0.22-beta.167
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 -2
- package/dist/index.js +190 -33
- package/dist/productionReadiness.d.ts +6 -1
- package/dist/providerStackRecommendations.d.ts +86 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -46,7 +46,7 @@ export { assertVoiceProviderRoutingContract, runVoiceProviderRoutingContract } f
|
|
|
46
46
|
export { createVoicePhoneAgentProductionSmokeHTMLHandler, createVoicePhoneAgentProductionSmokeJSONHandler, createVoicePhoneAgentProductionSmokeRoutes, renderVoicePhoneAgentProductionSmokeHTML, runVoicePhoneAgentProductionSmokeContract } from './phoneAgentProductionSmoke';
|
|
47
47
|
export { buildVoiceProductionReadinessGate, buildVoiceProductionReadinessReport, createVoiceProductionReadinessRoutes, renderVoiceProductionReadinessHTML, summarizeVoiceProductionReadinessGate } from './productionReadiness';
|
|
48
48
|
export { createVoiceReadinessProfile, recommendVoiceReadinessProfile } from './readinessProfiles';
|
|
49
|
-
export { evaluateVoiceProviderStackGaps, recommendVoiceProviderStack } from './providerStackRecommendations';
|
|
49
|
+
export { buildVoiceProviderContractMatrix, createVoiceProviderContractMatrixHTMLHandler, createVoiceProviderContractMatrixJSONHandler, createVoiceProviderContractMatrixRoutes, evaluateVoiceProviderStackGaps, renderVoiceProviderContractMatrixHTML, recommendVoiceProviderStack } from './providerStackRecommendations';
|
|
50
50
|
export { buildVoiceOpsConsoleReport, createVoiceOpsConsoleRoutes, renderVoiceOpsConsoleHTML } from './opsConsoleRoutes';
|
|
51
51
|
export { summarizeVoiceOpsStatus } from './opsStatus';
|
|
52
52
|
export { createVoiceOpsStatusRoutes, renderVoiceOpsStatusHTML } from './opsStatusRoutes';
|
|
@@ -106,7 +106,7 @@ export type { VoiceOpsConsoleLink, VoiceOpsConsoleReport, VoiceOpsConsoleRoutesO
|
|
|
106
106
|
export type { VoiceOpsStatus, VoiceOpsStatusLink, VoiceOpsStatusOptions, VoiceOpsStatusReport, VoiceOpsStatusRoutesOptions } from './opsStatus';
|
|
107
107
|
export type { VoiceProductionReadinessAction, VoiceProductionReadinessAuditOptions, VoiceProductionReadinessAuditRequirement, VoiceProductionReadinessAuditSummary, VoiceProductionReadinessCheck, VoiceProductionReadinessGateIssue, VoiceProductionReadinessGateOptions, VoiceProductionReadinessGateProfile, VoiceProductionReadinessGateProfileSurface, VoiceProductionReadinessGateReport, VoiceProductionReadinessOpsActionHistoryOptions, VoiceProductionReadinessOpsActionHistorySummary, VoiceProductionReadinessProfileExplanation, VoiceProductionReadinessProfileSurface, VoiceProductionReadinessProofSource, VoiceProductionReadinessReport, VoiceProductionReadinessRoutesOptions, VoiceProductionReadinessTraceDeliverySummary, VoiceProductionReadinessAuditDeliveryOptions, VoiceProductionReadinessAuditDeliverySummary, VoiceProductionReadinessTraceDeliveryOptions, VoiceProductionReadinessStatus } from './productionReadiness';
|
|
108
108
|
export type { VoiceReadinessProfileName, VoiceReadinessProfileOptions, VoiceReadinessProfileRecommendation, VoiceReadinessProfileRecommendationScore, VoiceReadinessProfileRoutesOptions } from './readinessProfiles';
|
|
109
|
-
export type { VoiceProviderStackChoice, VoiceProviderStackCapabilities, VoiceProviderStackCapabilityGap, VoiceProviderStackCapabilityGapInput, VoiceProviderStackCapabilityGapReport, VoiceProviderStackInput, VoiceProviderStackKind, VoiceProviderStackRecommendation } from './providerStackRecommendations';
|
|
109
|
+
export type { VoiceProviderStackChoice, VoiceProviderStackCapabilities, VoiceProviderStackCapabilityGap, VoiceProviderStackCapabilityGapInput, VoiceProviderStackCapabilityGapReport, VoiceProviderContractCheck, VoiceProviderContractCheckStatus, VoiceProviderContractDefinition, VoiceProviderContractMatrixHandlerOptions, VoiceProviderContractMatrixHTMLHandlerOptions, VoiceProviderContractMatrixInput, VoiceProviderContractMatrixReport, VoiceProviderContractMatrixRoutesOptions, VoiceProviderContractMatrixRow, VoiceProviderStackInput, VoiceProviderStackKind, VoiceProviderStackRecommendation } from './providerStackRecommendations';
|
|
110
110
|
export type { VoiceQualityLink, VoiceQualityMetric, VoiceQualityReport, VoiceQualityRoutesOptions, VoiceQualityStatus, VoiceQualityThresholds } from './qualityRoutes';
|
|
111
111
|
export type { VoiceResilienceIOSimulator, VoiceResilienceLink, VoiceResiliencePageData, VoiceResilienceRoutesOptions, VoiceResilienceSimulationProvider, VoiceRoutingKindSummary, VoiceRoutingDecisionSummary, VoiceRoutingDecisionSummaryOptions, VoiceRoutingEvent, VoiceRoutingEventKind, VoiceRoutingSessionSummary, VoiceRoutingSessionSummaryOptions } from './resilienceRoutes';
|
|
112
112
|
export type { VoiceIOProviderRouterEvent, VoiceIOProviderRouterOptions, VoiceIOProviderRouterPolicy, VoiceIOProviderRouterPolicyConfig, VoiceSTTProviderRouterOptions, VoiceTTSProviderRouterOptions } from './providerAdapters';
|
package/dist/index.js
CHANGED
|
@@ -20139,6 +20139,7 @@ var readinessGateCodes = {
|
|
|
20139
20139
|
"Live latency proof": "voice.readiness.live_latency",
|
|
20140
20140
|
"Operator action history": "voice.readiness.operator_action_history",
|
|
20141
20141
|
"Phone agent production smoke": "voice.readiness.phone_agent_smoke",
|
|
20142
|
+
"Provider contract matrix": "voice.readiness.provider_contract_matrix",
|
|
20142
20143
|
"Provider fallback recovery": "voice.readiness.provider_fallback_recovery",
|
|
20143
20144
|
"Provider health": "voice.readiness.provider_health",
|
|
20144
20145
|
"Provider routing contracts": "voice.readiness.provider_routing_contracts",
|
|
@@ -20220,6 +20221,12 @@ var resolveProviderStack = async (options, input) => {
|
|
|
20220
20221
|
}
|
|
20221
20222
|
return typeof options.providerStack === "function" ? await options.providerStack(input) : options.providerStack;
|
|
20222
20223
|
};
|
|
20224
|
+
var resolveProviderContractMatrix = async (options, input) => {
|
|
20225
|
+
if (options.providerContractMatrix === false || options.providerContractMatrix === undefined) {
|
|
20226
|
+
return;
|
|
20227
|
+
}
|
|
20228
|
+
return typeof options.providerContractMatrix === "function" ? await options.providerContractMatrix(input) : options.providerContractMatrix;
|
|
20229
|
+
};
|
|
20223
20230
|
var resolvePhoneAgentSmokes = async (options, input) => {
|
|
20224
20231
|
if (options.phoneAgentSmokes === false || options.phoneAgentSmokes === undefined) {
|
|
20225
20232
|
return;
|
|
@@ -20480,6 +20487,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
20480
20487
|
agentSquadContracts,
|
|
20481
20488
|
providerRoutingContracts,
|
|
20482
20489
|
providerStack,
|
|
20490
|
+
providerContractMatrix,
|
|
20483
20491
|
phoneAgentSmokes,
|
|
20484
20492
|
reconnectContracts,
|
|
20485
20493
|
bargeInReports,
|
|
@@ -20511,6 +20519,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
20511
20519
|
resolveAgentSquadContracts(options, { query, request }),
|
|
20512
20520
|
resolveProviderRoutingContracts(options, { query, request }),
|
|
20513
20521
|
resolveProviderStack(options, { query, request }),
|
|
20522
|
+
resolveProviderContractMatrix(options, { query, request }),
|
|
20514
20523
|
resolvePhoneAgentSmokes(options, { query, request }),
|
|
20515
20524
|
resolveReconnectContracts(options, { query, request }),
|
|
20516
20525
|
resolveBargeInReports(options, { query, request }),
|
|
@@ -20721,6 +20730,26 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
20721
20730
|
]
|
|
20722
20731
|
});
|
|
20723
20732
|
}
|
|
20733
|
+
if (providerContractMatrix) {
|
|
20734
|
+
const blocked = providerContractMatrix.rows.filter((row) => row.status !== "pass");
|
|
20735
|
+
checks.push({
|
|
20736
|
+
detail: providerContractMatrix.status === "pass" ? `${providerContractMatrix.passed} provider contract row(s) are production-ready.` : blocked.length > 0 ? blocked.map((row) => {
|
|
20737
|
+
const issues = row.checks.filter((check) => check.status !== "pass").map((check) => check.label).join(", ");
|
|
20738
|
+
return `${row.kind.toUpperCase()} ${row.provider}: ${issues}`;
|
|
20739
|
+
}).join("; ") + "." : "Provider contract matrix needs review.",
|
|
20740
|
+
href: options.links?.providerRoutingContracts ?? options.links?.resilience ?? "/resilience",
|
|
20741
|
+
label: "Provider contract matrix",
|
|
20742
|
+
status: providerContractMatrix.status,
|
|
20743
|
+
value: `${providerContractMatrix.passed}/${providerContractMatrix.total}`,
|
|
20744
|
+
actions: providerContractMatrix.status === "pass" ? [] : [
|
|
20745
|
+
{
|
|
20746
|
+
description: "Open provider capabilities and inspect missing env, fallback, streaming, latency, or capability contracts.",
|
|
20747
|
+
href: options.links?.providerRoutingContracts ?? options.links?.resilience ?? "/resilience",
|
|
20748
|
+
label: "Open provider matrix"
|
|
20749
|
+
}
|
|
20750
|
+
]
|
|
20751
|
+
});
|
|
20752
|
+
}
|
|
20724
20753
|
if (phoneAgentSmokeSummary) {
|
|
20725
20754
|
checks.push({
|
|
20726
20755
|
detail: phoneAgentSmokeSummary.status === "pass" ? `${phoneAgentSmokeSummary.passed} phone-agent smoke contract(s) are passing.` : phoneAgentSmokeSummary.total === 0 ? "No phone-agent production smoke contracts are configured." : `${phoneAgentSmokeSummary.failed} phone-agent production smoke contract(s) failed.`,
|
|
@@ -20916,6 +20945,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
20916
20945
|
total: providers.length
|
|
20917
20946
|
},
|
|
20918
20947
|
providerStack,
|
|
20948
|
+
providerContractMatrix,
|
|
20919
20949
|
providerRecovery,
|
|
20920
20950
|
phoneAgentSmokes: phoneAgentSmokeSummary,
|
|
20921
20951
|
providerRoutingContracts: providerRoutingContractSummary,
|
|
@@ -21312,6 +21342,8 @@ var recommendVoiceReadinessProfile = (options) => {
|
|
|
21312
21342
|
};
|
|
21313
21343
|
};
|
|
21314
21344
|
// src/providerStackRecommendations.ts
|
|
21345
|
+
import { Elysia as Elysia34 } from "elysia";
|
|
21346
|
+
var escapeHtml36 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21315
21347
|
var profileProviderPriorities = {
|
|
21316
21348
|
"meeting-recorder": {
|
|
21317
21349
|
llm: ["openai", "anthropic", "gemini"],
|
|
@@ -21394,6 +21426,126 @@ var recommendVoiceProviderStack = (input) => {
|
|
|
21394
21426
|
stacks
|
|
21395
21427
|
};
|
|
21396
21428
|
};
|
|
21429
|
+
var rollupContractStatus = (checks) => checks.some((check) => check.status === "fail") ? "fail" : checks.some((check) => check.status === "warn") ? "warn" : "pass";
|
|
21430
|
+
var buildVoiceProviderContractMatrix = (input) => {
|
|
21431
|
+
const rows = input.contracts.map((contract) => {
|
|
21432
|
+
const configured = contract.configured !== false;
|
|
21433
|
+
const missingEnv = (contract.requiredEnv ?? []).filter((name) => !contract.env?.[name]);
|
|
21434
|
+
const missingCapabilities = (contract.requiredCapabilities ?? []).filter((capability) => !includesCapability(contract.capabilities ?? [], capability));
|
|
21435
|
+
const checks = [
|
|
21436
|
+
{
|
|
21437
|
+
detail: configured ? "Provider is configured for this deployment." : "Provider is declared but not configured.",
|
|
21438
|
+
key: "configured",
|
|
21439
|
+
label: "Configured",
|
|
21440
|
+
status: configured ? "pass" : "fail"
|
|
21441
|
+
},
|
|
21442
|
+
{
|
|
21443
|
+
detail: missingEnv.length === 0 ? "Required environment is present." : `Missing env: ${missingEnv.join(", ")}.`,
|
|
21444
|
+
key: "env",
|
|
21445
|
+
label: "Required env",
|
|
21446
|
+
status: missingEnv.length === 0 ? "pass" : "fail"
|
|
21447
|
+
},
|
|
21448
|
+
{
|
|
21449
|
+
detail: contract.latencyBudgetMs !== undefined ? `Latency budget is ${contract.latencyBudgetMs}ms.` : "No latency budget declared.",
|
|
21450
|
+
key: "latencyBudget",
|
|
21451
|
+
label: "Latency budget",
|
|
21452
|
+
status: contract.latencyBudgetMs !== undefined ? "pass" : "warn"
|
|
21453
|
+
},
|
|
21454
|
+
{
|
|
21455
|
+
detail: (contract.fallbackProviders ?? []).length > 0 ? `Fallback providers: ${contract.fallbackProviders?.join(", ")}.` : "No fallback provider declared.",
|
|
21456
|
+
key: "fallback",
|
|
21457
|
+
label: "Fallback",
|
|
21458
|
+
status: (contract.fallbackProviders ?? []).length > 0 ? "pass" : "warn"
|
|
21459
|
+
},
|
|
21460
|
+
{
|
|
21461
|
+
detail: contract.streaming ? "Streaming is supported." : "Streaming support is not declared.",
|
|
21462
|
+
key: "streaming",
|
|
21463
|
+
label: "Streaming",
|
|
21464
|
+
status: contract.streaming ? "pass" : "warn"
|
|
21465
|
+
},
|
|
21466
|
+
{
|
|
21467
|
+
detail: missingCapabilities.length === 0 ? "Required capabilities are declared." : `Missing capabilities: ${missingCapabilities.join(", ")}.`,
|
|
21468
|
+
key: "capabilities",
|
|
21469
|
+
label: "Capabilities",
|
|
21470
|
+
status: missingCapabilities.length === 0 ? "pass" : "warn"
|
|
21471
|
+
}
|
|
21472
|
+
];
|
|
21473
|
+
const status = rollupContractStatus(checks);
|
|
21474
|
+
return {
|
|
21475
|
+
checks,
|
|
21476
|
+
configured,
|
|
21477
|
+
kind: contract.kind,
|
|
21478
|
+
provider: contract.provider,
|
|
21479
|
+
selected: contract.selected === true,
|
|
21480
|
+
status
|
|
21481
|
+
};
|
|
21482
|
+
});
|
|
21483
|
+
const failed = rows.filter((row) => row.status === "fail").length;
|
|
21484
|
+
const warned = rows.filter((row) => row.status === "warn").length;
|
|
21485
|
+
return {
|
|
21486
|
+
failed,
|
|
21487
|
+
passed: rows.filter((row) => row.status === "pass").length,
|
|
21488
|
+
rows,
|
|
21489
|
+
status: failed > 0 ? "fail" : warned > 0 ? "warn" : "pass",
|
|
21490
|
+
total: rows.length,
|
|
21491
|
+
warned
|
|
21492
|
+
};
|
|
21493
|
+
};
|
|
21494
|
+
var resolveProviderContractMatrixInput = async (matrix) => typeof matrix === "function" ? await matrix() : matrix;
|
|
21495
|
+
var renderVoiceProviderContractMatrixHTML = (report, options = {}) => {
|
|
21496
|
+
const title = options.title ?? "Voice Provider Contract Matrix";
|
|
21497
|
+
const rows = report.rows.map((row) => {
|
|
21498
|
+
const checks = row.checks.map((check) => `<li class="${escapeHtml36(check.status)}"><strong>${escapeHtml36(check.label)}</strong><span>${escapeHtml36(check.detail ?? check.status)}</span></li>`).join("");
|
|
21499
|
+
return `<article class="row ${escapeHtml36(row.status)}">
|
|
21500
|
+
<div>
|
|
21501
|
+
<p class="eyebrow">${escapeHtml36(row.kind)}${row.selected ? " \xB7 selected" : ""}</p>
|
|
21502
|
+
<h2>${escapeHtml36(row.provider)}</h2>
|
|
21503
|
+
<p class="status ${escapeHtml36(row.status)}">${escapeHtml36(row.status.toUpperCase())}</p>
|
|
21504
|
+
</div>
|
|
21505
|
+
<ul>${checks}</ul>
|
|
21506
|
+
</article>`;
|
|
21507
|
+
}).join("");
|
|
21508
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml36(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,.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))}.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}.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}@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>${escapeHtml36(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>${rows || '<article class="row"><p>No provider contracts configured.</p></article>'}</main></body></html>`;
|
|
21509
|
+
};
|
|
21510
|
+
var createVoiceProviderContractMatrixJSONHandler = (matrix) => async () => buildVoiceProviderContractMatrix(await resolveProviderContractMatrixInput(matrix));
|
|
21511
|
+
var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
|
|
21512
|
+
const report = buildVoiceProviderContractMatrix(await resolveProviderContractMatrixInput(options.matrix));
|
|
21513
|
+
const body = await (options.render ?? renderVoiceProviderContractMatrixHTML)(report, { title: options.title });
|
|
21514
|
+
return new Response(body, {
|
|
21515
|
+
headers: {
|
|
21516
|
+
"Content-Type": "text/html; charset=utf-8"
|
|
21517
|
+
}
|
|
21518
|
+
});
|
|
21519
|
+
};
|
|
21520
|
+
var createVoiceProviderContractMatrixRoutes = (options) => {
|
|
21521
|
+
const path = options.path ?? "/api/provider-contracts";
|
|
21522
|
+
const htmlPath = options.htmlPath ?? "/provider-contracts";
|
|
21523
|
+
const routes = new Elysia34({
|
|
21524
|
+
name: options.name ?? "absolutejs-voice-provider-contract-matrix"
|
|
21525
|
+
});
|
|
21526
|
+
const jsonHandler = createVoiceProviderContractMatrixJSONHandler(options.matrix);
|
|
21527
|
+
routes.get(path, async () => {
|
|
21528
|
+
const report = await jsonHandler();
|
|
21529
|
+
return new Response(JSON.stringify(report), {
|
|
21530
|
+
headers: {
|
|
21531
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
21532
|
+
...options.headers
|
|
21533
|
+
}
|
|
21534
|
+
});
|
|
21535
|
+
});
|
|
21536
|
+
if (htmlPath !== false) {
|
|
21537
|
+
routes.get(htmlPath, async () => {
|
|
21538
|
+
const response = await createVoiceProviderContractMatrixHTMLHandler(options)();
|
|
21539
|
+
return new Response(response.body, {
|
|
21540
|
+
headers: {
|
|
21541
|
+
...Object.fromEntries(response.headers.entries()),
|
|
21542
|
+
...options.headers
|
|
21543
|
+
}
|
|
21544
|
+
});
|
|
21545
|
+
});
|
|
21546
|
+
}
|
|
21547
|
+
return routes;
|
|
21548
|
+
};
|
|
21397
21549
|
var normalizeCapability = (value) => value.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
21398
21550
|
var includesCapability = (capabilities, required) => {
|
|
21399
21551
|
const normalizedRequired = normalizeCapability(required);
|
|
@@ -21427,7 +21579,7 @@ var evaluateVoiceProviderStackGaps = (input) => {
|
|
|
21427
21579
|
};
|
|
21428
21580
|
};
|
|
21429
21581
|
// src/opsConsoleRoutes.ts
|
|
21430
|
-
import { Elysia as
|
|
21582
|
+
import { Elysia as Elysia35 } from "elysia";
|
|
21431
21583
|
var DEFAULT_LINKS = [
|
|
21432
21584
|
{
|
|
21433
21585
|
description: "Quality gates for CI, deploy checks, and production readiness.",
|
|
@@ -21462,7 +21614,7 @@ var DEFAULT_LINKS = [
|
|
|
21462
21614
|
label: "Handoffs"
|
|
21463
21615
|
}
|
|
21464
21616
|
];
|
|
21465
|
-
var
|
|
21617
|
+
var escapeHtml37 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21466
21618
|
var countProviderStatuses = (providers) => {
|
|
21467
21619
|
const degradedStatuses = new Set(["degraded", "rate-limited", "suppressed"]);
|
|
21468
21620
|
const healthy = providers.filter((provider) => provider.status === "healthy").length;
|
|
@@ -21531,20 +21683,20 @@ var buildVoiceOpsConsoleReport = async (options) => {
|
|
|
21531
21683
|
trace
|
|
21532
21684
|
};
|
|
21533
21685
|
};
|
|
21534
|
-
var renderMetricCard = (input) => `<article class="metric"><span>${
|
|
21686
|
+
var renderMetricCard = (input) => `<article class="metric"><span>${escapeHtml37(input.label)}</span><strong>${escapeHtml37(String(input.value))}</strong>${input.status ? `<p class="${escapeHtml37(input.status)}">${escapeHtml37(input.status)}</p>` : ""}${input.href ? `<a href="${escapeHtml37(input.href)}">Open</a>` : ""}</article>`;
|
|
21535
21687
|
var renderVoiceOpsConsoleHTML = (report, options = {}) => {
|
|
21536
21688
|
const links = report.links.map((link) => `<article class="surface">
|
|
21537
|
-
<div><h2>${
|
|
21538
|
-
<p><a href="${
|
|
21689
|
+
<div><h2>${escapeHtml37(link.label)}</h2>${link.description ? `<p>${escapeHtml37(link.description)}</p>` : ""}</div>
|
|
21690
|
+
<p><a href="${escapeHtml37(link.href)}">Open ${escapeHtml37(link.label)}</a>${link.statusHref ? ` \xB7 <a href="${escapeHtml37(link.statusHref)}">Status</a>` : ""}</p>
|
|
21539
21691
|
</article>`).join("");
|
|
21540
|
-
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${
|
|
21541
|
-
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${
|
|
21692
|
+
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${escapeHtml37(session.sessionId)}</td><td>${escapeHtml37(session.status)}</td><td>${session.turnCount}</td><td>${session.errorCount}</td><td>${session.replayHref ? `<a href="${escapeHtml37(session.replayHref)}">Replay</a>` : ""}</td></tr>`).join("") : '<tr><td colspan="5">No sessions yet.</td></tr>';
|
|
21693
|
+
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${escapeHtml37(event.kind)}</td><td>${escapeHtml37(event.provider ?? "unknown")}</td><td>${escapeHtml37(event.status ?? "unknown")}</td><td>${event.elapsedMs ?? 0}ms</td><td>${escapeHtml37(event.sessionId)}</td></tr>`).join("") : '<tr><td colspan="5">No provider routing events yet.</td></tr>';
|
|
21542
21694
|
const title = options.title ?? "AbsoluteJS Voice Ops Console";
|
|
21543
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
21695
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml37(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>${escapeHtml37(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 ${escapeHtml37(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>`;
|
|
21544
21696
|
};
|
|
21545
21697
|
var createVoiceOpsConsoleRoutes = (options) => {
|
|
21546
21698
|
const path = options.path ?? "/ops-console";
|
|
21547
|
-
const routes = new
|
|
21699
|
+
const routes = new Elysia35({
|
|
21548
21700
|
name: options.name ?? "absolutejs-voice-ops-console"
|
|
21549
21701
|
});
|
|
21550
21702
|
const getReport = () => buildVoiceOpsConsoleReport(options);
|
|
@@ -21732,19 +21884,19 @@ var summarizeVoiceOpsStatus = async (options) => {
|
|
|
21732
21884
|
};
|
|
21733
21885
|
};
|
|
21734
21886
|
// src/opsStatusRoutes.ts
|
|
21735
|
-
import { Elysia as
|
|
21736
|
-
var
|
|
21887
|
+
import { Elysia as Elysia36 } from "elysia";
|
|
21888
|
+
var escapeHtml38 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21737
21889
|
var renderVoiceOpsStatusHTML = (report, options = {}) => {
|
|
21738
21890
|
const title = options.title ?? "AbsoluteJS Voice Ops Status";
|
|
21739
21891
|
const surfaces = Object.entries(report.surfaces).map(([key, surface]) => {
|
|
21740
21892
|
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;
|
|
21741
|
-
return `<article class="surface ${
|
|
21893
|
+
return `<article class="surface ${escapeHtml38(surface.status)}"><span>${escapeHtml38(surface.status.toUpperCase())}</span><h2>${escapeHtml38(key)}</h2><strong>${escapeHtml38(value)}</strong></article>`;
|
|
21742
21894
|
}).join("");
|
|
21743
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
21895
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml38(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>${escapeHtml38(title)}</h1><p>Compact pass/fail status for framework widgets, demos, and small customer-facing health badges.</p><p class="status ${escapeHtml38(report.status)}">Overall: ${escapeHtml38(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>`;
|
|
21744
21896
|
};
|
|
21745
21897
|
var createVoiceOpsStatusRoutes = (options) => {
|
|
21746
21898
|
const path = options.path ?? "/api/voice/ops-status";
|
|
21747
|
-
const routes = new
|
|
21899
|
+
const routes = new Elysia36({
|
|
21748
21900
|
name: options.name ?? "absolutejs-voice-ops-status"
|
|
21749
21901
|
});
|
|
21750
21902
|
routes.get(path, async () => summarizeVoiceOpsStatus(options));
|
|
@@ -22177,8 +22329,8 @@ var createVoiceTTSProviderRouter = (options) => {
|
|
|
22177
22329
|
};
|
|
22178
22330
|
};
|
|
22179
22331
|
// src/traceDeliveryRoutes.ts
|
|
22180
|
-
import { Elysia as
|
|
22181
|
-
var
|
|
22332
|
+
import { Elysia as Elysia37 } from "elysia";
|
|
22333
|
+
var escapeHtml39 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
22182
22334
|
var getString12 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
22183
22335
|
var getNumber7 = (value) => {
|
|
22184
22336
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -22259,14 +22411,14 @@ var renderSinkResults2 = (delivery) => {
|
|
|
22259
22411
|
if (entries.length === 0) {
|
|
22260
22412
|
return "<p>No sink delivery attempts recorded yet.</p>";
|
|
22261
22413
|
}
|
|
22262
|
-
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${
|
|
22414
|
+
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${escapeHtml39(sinkId)}</strong>: ${escapeHtml39(result.status)}${result.deliveredTo ? ` to ${escapeHtml39(result.deliveredTo)}` : ""}${result.error ? ` (${escapeHtml39(result.error)})` : ""}</li>`).join("")}</ul>`;
|
|
22263
22415
|
};
|
|
22264
|
-
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${
|
|
22416
|
+
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${escapeHtml39(event.type)} <small>${escapeHtml39(event.id)}</small>${event.sessionId ? ` session=${escapeHtml39(event.sessionId)}` : ""}</li>`).join("")}</ul>`;
|
|
22265
22417
|
var renderVoiceTraceDeliveryHTML = (report, options = {}) => {
|
|
22266
22418
|
const title = options.title ?? "AbsoluteJS Voice Trace Deliveries";
|
|
22267
|
-
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${
|
|
22268
|
-
const rows = report.deliveries.map((delivery) => `<article class="delivery ${
|
|
22269
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
22419
|
+
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${escapeHtml39(options.workerPath ?? "/api/voice-trace-deliveries/drain")}"><button type="submit">Drain trace deliveries</button></form>`;
|
|
22420
|
+
const rows = report.deliveries.map((delivery) => `<article class="delivery ${escapeHtml39(delivery.deliveryStatus)}"><div class="head"><div><span>${escapeHtml39(delivery.deliveryStatus)}</span><h2>${escapeHtml39(delivery.id)}</h2><p>${escapeHtml39(new Date(delivery.createdAt).toLocaleString())}${delivery.deliveredAt ? ` \xB7 delivered ${escapeHtml39(new Date(delivery.deliveredAt).toLocaleString())}` : ""}</p></div><strong>${String(delivery.deliveryAttempts ?? 0)} attempt(s)</strong></div>${delivery.deliveryError ? `<p class="error">${escapeHtml39(delivery.deliveryError)}</p>` : ""}<h3>Sinks</h3>${renderSinkResults2(delivery)}<h3>Events</h3>${renderEventList2(delivery)}</article>`).join("");
|
|
22421
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml39(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>${escapeHtml39(title)}</h1><p>Checked ${escapeHtml39(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>`;
|
|
22270
22422
|
};
|
|
22271
22423
|
var createVoiceTraceDeliveryJSONHandler = (options) => async ({ query }) => buildVoiceTraceDeliveryReport(options, resolveVoiceTraceDeliveryFilter(query, options.filter));
|
|
22272
22424
|
var createVoiceTraceDeliveryHTMLHandler = (options) => async ({ query }) => {
|
|
@@ -22286,7 +22438,7 @@ var createVoiceTraceDeliveryRoutes = (options) => {
|
|
|
22286
22438
|
const path = options.path ?? "/api/voice-trace-deliveries";
|
|
22287
22439
|
const htmlPath = options.htmlPath === undefined ? "/traces/deliveries" : options.htmlPath;
|
|
22288
22440
|
const workerPath = options.workerPath === undefined ? `${path}/drain` : options.workerPath;
|
|
22289
|
-
const routes = new
|
|
22441
|
+
const routes = new Elysia37({
|
|
22290
22442
|
name: options.name ?? "absolutejs-voice-trace-deliveries"
|
|
22291
22443
|
}).get(path, createVoiceTraceDeliveryJSONHandler(options));
|
|
22292
22444
|
if (htmlPath !== false) {
|
|
@@ -22304,8 +22456,8 @@ var createVoiceTraceDeliveryRoutes = (options) => {
|
|
|
22304
22456
|
return routes;
|
|
22305
22457
|
};
|
|
22306
22458
|
// src/traceTimeline.ts
|
|
22307
|
-
import { Elysia as
|
|
22308
|
-
var
|
|
22459
|
+
import { Elysia as Elysia38 } from "elysia";
|
|
22460
|
+
var escapeHtml40 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
22309
22461
|
var getString13 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
22310
22462
|
var getNumber8 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
22311
22463
|
var firstString3 = (payload, keys) => {
|
|
@@ -22473,20 +22625,20 @@ var summarizeVoiceTraceTimeline = (events, options = {}) => {
|
|
|
22473
22625
|
};
|
|
22474
22626
|
};
|
|
22475
22627
|
var formatMs3 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
22476
|
-
var renderProviderCards2 = (session) => session.providers.length === 0 ? '<p class="muted">No provider events recorded for this session.</p>' : `<div class="providers">${session.providers.map((provider) => `<article><strong>${
|
|
22628
|
+
var renderProviderCards2 = (session) => session.providers.length === 0 ? '<p class="muted">No provider events recorded for this session.</p>' : `<div class="providers">${session.providers.map((provider) => `<article><strong>${escapeHtml40(provider.provider)}</strong><dl><div><dt>Events</dt><dd>${String(provider.eventCount)}</dd></div><div><dt>Avg</dt><dd>${formatMs3(provider.averageElapsedMs)}</dd></div><div><dt>Max</dt><dd>${formatMs3(provider.maxElapsedMs)}</dd></div><div><dt>Errors</dt><dd>${String(provider.errorCount)}</dd></div><div><dt>Fallbacks</dt><dd>${String(provider.fallbackCount)}</dd></div><div><dt>Timeouts</dt><dd>${String(provider.timeoutCount)}</dd></div></dl></article>`).join("")}</div>`;
|
|
22477
22629
|
var renderVoiceTraceTimelineSessionHTML = (session, options = {}) => {
|
|
22478
|
-
const events = session.events.map((event) => `<tr class="${
|
|
22479
|
-
const issues = session.evaluation.issues.length ? session.evaluation.issues.map((issue) => `<li class="${
|
|
22480
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
22630
|
+
const events = session.events.map((event) => `<tr class="${escapeHtml40(event.status ?? "")}"><td>+${String(event.offsetMs)}ms</td><td>${escapeHtml40(event.type)}</td><td>${escapeHtml40(event.label)}</td><td>${escapeHtml40(event.provider ?? "")}</td><td>${escapeHtml40(event.status ?? "")}</td><td>${formatMs3(event.elapsedMs)}</td></tr>`).join("");
|
|
22631
|
+
const issues = session.evaluation.issues.length ? session.evaluation.issues.map((issue) => `<li class="${escapeHtml40(issue.severity)}">${escapeHtml40(issue.code)}: ${escapeHtml40(issue.message)}</li>`).join("") : "<li>none</li>";
|
|
22632
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml40(options.title ?? "Voice Trace Timeline")}</title><style>${timelineCSS}</style></head><body><main><a href="/traces">Back to traces</a><header><p class="eyebrow">Call timeline</p><h1>${escapeHtml40(session.sessionId)}</h1><p class="status ${escapeHtml40(session.status)}">${escapeHtml40(session.status)}</p></header><section class="metrics"><article><span>Events</span><strong>${String(session.summary.eventCount)}</strong></article><article><span>Turns</span><strong>${String(session.summary.turnCount)}</strong></article><article><span>Errors</span><strong>${String(session.summary.errorCount)}</strong></article><article><span>Duration</span><strong>${formatMs3(session.summary.callDurationMs)}</strong></article></section><section><h2>Providers</h2>${renderProviderCards2(session)}</section><section><h2>Issues</h2><ul>${issues}</ul></section><section><h2>Timeline</h2><table><thead><tr><th>Offset</th><th>Type</th><th>Event</th><th>Provider</th><th>Status</th><th>Latency</th></tr></thead><tbody>${events}</tbody></table></section></main></body></html>`;
|
|
22481
22633
|
};
|
|
22482
|
-
var renderSessionRows = (report) => report.sessions.length === 0 ? '<tr><td colspan="7">No trace events recorded yet.</td></tr>' : report.sessions.map((session) => `<tr class="${
|
|
22634
|
+
var renderSessionRows = (report) => report.sessions.length === 0 ? '<tr><td colspan="7">No trace events recorded yet.</td></tr>' : report.sessions.map((session) => `<tr class="${escapeHtml40(session.status)}"><td><a href="/traces/${encodeURIComponent(session.sessionId)}">${escapeHtml40(session.sessionId)}</a></td><td>${escapeHtml40(session.status)}</td><td>${String(session.summary.eventCount)}</td><td>${String(session.summary.turnCount)}</td><td>${String(session.summary.errorCount)}</td><td>${formatMs3(session.summary.callDurationMs)}</td><td>${session.providers.map((provider) => escapeHtml40(provider.provider)).join(", ")}</td></tr>`).join("");
|
|
22483
22635
|
var timelineCSS = "body{background:#0f1318;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}a{color:#fbbf24}.eyebrow{color:#fbbf24;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.5rem);line-height:.92;margin:.2rem 0 1rem}.status{border:1px solid #475569;border-radius:999px;display:inline-flex;padding:8px 12px}.healthy{color:#86efac}.warning{color:#fbbf24}.failed,.error{color:#fca5a5}.metrics,.providers{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));margin:20px 0}.metrics article,.providers article{background:#181f27;border:1px solid #2b3642;border-radius:20px;padding:16px}.metrics span,dt,.muted{color:#a8b0b8}.metrics strong{display:block;font-size:2rem}dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin:12px 0 0}dd{font-weight:800;margin:4px 0 0}table{background:#181f27;border-collapse:collapse;border-radius:18px;overflow:hidden;width:100%}td,th{border-bottom:1px solid #2b3642;padding:12px;text-align:left}section{margin-top:28px}@media(max-width:760px){main{padding:20px}table{font-size:.9rem}}";
|
|
22484
|
-
var renderVoiceTraceTimelineHTML = (report, options = {}) => `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
22636
|
+
var renderVoiceTraceTimelineHTML = (report, options = {}) => `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml40(options.title ?? "Voice Trace Timelines")}</title><style>${timelineCSS}</style></head><body><main><header><p class="eyebrow">Self-hosted voice debugging</p><h1>${escapeHtml40(options.title ?? "Voice Trace Timelines")}</h1><p class="muted">Per-call event timelines with provider latency, fallback, timeout, handoff, and error context.</p></header><section class="metrics"><article><span>Sessions</span><strong>${String(report.total)}</strong></article><article><span>Failed</span><strong>${String(report.failed)}</strong></article><article><span>Warnings</span><strong>${String(report.warnings)}</strong></article></section><table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Turns</th><th>Errors</th><th>Duration</th><th>Providers</th></tr></thead><tbody>${renderSessionRows(report)}</tbody></table></main></body></html>`;
|
|
22485
22637
|
var createVoiceTraceTimelineRoutes = (options) => {
|
|
22486
22638
|
const path = options.path ?? "/api/voice-traces";
|
|
22487
22639
|
const htmlPath = options.htmlPath ?? "/traces";
|
|
22488
22640
|
const title = options.title ?? "AbsoluteJS Voice Trace Timelines";
|
|
22489
|
-
const routes = new
|
|
22641
|
+
const routes = new Elysia38({
|
|
22490
22642
|
name: options.name ?? "absolutejs-voice-trace-timelines"
|
|
22491
22643
|
});
|
|
22492
22644
|
const buildReport = async () => summarizeVoiceTraceTimeline(await options.store.list(), {
|
|
@@ -23139,7 +23291,7 @@ var createVoiceMemoryStore = () => {
|
|
|
23139
23291
|
return { get, getOrCreate, list, remove, set };
|
|
23140
23292
|
};
|
|
23141
23293
|
// src/opsWebhook.ts
|
|
23142
|
-
import { Elysia as
|
|
23294
|
+
import { Elysia as Elysia39 } from "elysia";
|
|
23143
23295
|
var toHex6 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
23144
23296
|
var signVoiceOpsWebhookBody = async (input) => {
|
|
23145
23297
|
const encoder = new TextEncoder;
|
|
@@ -23269,7 +23421,7 @@ var verifyVoiceOpsWebhookSignature = async (input) => {
|
|
|
23269
23421
|
};
|
|
23270
23422
|
var createVoiceOpsWebhookReceiverRoutes = (options = {}) => {
|
|
23271
23423
|
const path = options.path ?? "/api/voice-ops/webhook";
|
|
23272
|
-
return new
|
|
23424
|
+
return new Elysia39().post(path, async ({ body, request, set }) => {
|
|
23273
23425
|
const bodyText = typeof body === "string" ? body : JSON.stringify(body);
|
|
23274
23426
|
if (options.signingSecret) {
|
|
23275
23427
|
const verification = await verifyVoiceOpsWebhookSignature({
|
|
@@ -24203,6 +24355,7 @@ export {
|
|
|
24203
24355
|
renderVoiceReconnectContractHTML,
|
|
24204
24356
|
renderVoiceQualityHTML,
|
|
24205
24357
|
renderVoiceProviderHealthHTML,
|
|
24358
|
+
renderVoiceProviderContractMatrixHTML,
|
|
24206
24359
|
renderVoiceProviderCapabilityHTML,
|
|
24207
24360
|
renderVoiceProductionReadinessHTML,
|
|
24208
24361
|
renderVoicePhoneAgentProductionSmokeHTML,
|
|
@@ -24359,6 +24512,9 @@ export {
|
|
|
24359
24512
|
createVoiceProviderHealthRoutes,
|
|
24360
24513
|
createVoiceProviderHealthJSONHandler,
|
|
24361
24514
|
createVoiceProviderHealthHTMLHandler,
|
|
24515
|
+
createVoiceProviderContractMatrixRoutes,
|
|
24516
|
+
createVoiceProviderContractMatrixJSONHandler,
|
|
24517
|
+
createVoiceProviderContractMatrixHTMLHandler,
|
|
24362
24518
|
createVoiceProviderCapabilityRoutes,
|
|
24363
24519
|
createVoiceProviderCapabilityJSONHandler,
|
|
24364
24520
|
createVoiceProviderCapabilityHTMLHandler,
|
|
@@ -24511,6 +24667,7 @@ export {
|
|
|
24511
24667
|
claimVoiceOpsTask,
|
|
24512
24668
|
buildVoiceTraceReplay,
|
|
24513
24669
|
buildVoiceTraceDeliveryReport,
|
|
24670
|
+
buildVoiceProviderContractMatrix,
|
|
24514
24671
|
buildVoiceProductionReadinessReport,
|
|
24515
24672
|
buildVoiceProductionReadinessGate,
|
|
24516
24673
|
buildVoiceOpsTaskFromSLABreach,
|
|
@@ -11,7 +11,7 @@ import type { VoicePhoneAgentProductionSmokeReport } from './phoneAgentProductio
|
|
|
11
11
|
import type { VoiceReconnectContractReport } from './reconnectContract';
|
|
12
12
|
import type { VoiceAuditEventStore, VoiceAuditEventType, VoiceAuditOutcome } from './audit';
|
|
13
13
|
import { type VoiceAuditSinkDeliveryStore } from './auditSinks';
|
|
14
|
-
import type { VoiceProviderStackCapabilityGapReport } from './providerStackRecommendations';
|
|
14
|
+
import type { VoiceProviderContractMatrixReport, VoiceProviderStackCapabilityGapReport } from './providerStackRecommendations';
|
|
15
15
|
export type VoiceProductionReadinessStatus = 'fail' | 'pass' | 'warn';
|
|
16
16
|
export type VoiceProductionReadinessAction = {
|
|
17
17
|
description?: string;
|
|
@@ -139,6 +139,7 @@ export type VoiceProductionReadinessReport = {
|
|
|
139
139
|
total: number;
|
|
140
140
|
};
|
|
141
141
|
providerStack?: VoiceProviderStackCapabilityGapReport;
|
|
142
|
+
providerContractMatrix?: VoiceProviderContractMatrixReport;
|
|
142
143
|
providerRecovery: VoiceProviderFallbackRecoverySummary;
|
|
143
144
|
phoneAgentSmokes?: {
|
|
144
145
|
failed: number;
|
|
@@ -303,6 +304,10 @@ export type VoiceProductionReadinessRoutesOptions = {
|
|
|
303
304
|
query: Record<string, unknown>;
|
|
304
305
|
request: Request;
|
|
305
306
|
}) => Promise<VoiceProviderStackCapabilityGapReport> | VoiceProviderStackCapabilityGapReport);
|
|
307
|
+
providerContractMatrix?: false | VoiceProviderContractMatrixReport | ((input: {
|
|
308
|
+
query: Record<string, unknown>;
|
|
309
|
+
request: Request;
|
|
310
|
+
}) => Promise<VoiceProviderContractMatrixReport> | VoiceProviderContractMatrixReport);
|
|
306
311
|
reconnectContracts?: false | readonly VoiceReconnectContractReport[] | ((input: {
|
|
307
312
|
query: Record<string, unknown>;
|
|
308
313
|
request: Request;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Elysia } from 'elysia';
|
|
1
2
|
import type { VoiceReadinessProfileName } from './readinessProfiles';
|
|
2
3
|
export type VoiceProviderStackKind = 'llm' | 'stt' | 'tts';
|
|
3
4
|
export type VoiceProviderStackInput<TProvider extends string = string> = {
|
|
@@ -35,5 +36,90 @@ export type VoiceProviderStackCapabilityGapInput<TProvider extends string = stri
|
|
|
35
36
|
recommendation?: VoiceProviderStackRecommendation<TProvider>;
|
|
36
37
|
required?: Partial<Record<VoiceProviderStackKind, readonly string[]>>;
|
|
37
38
|
};
|
|
39
|
+
export type VoiceProviderContractCheckStatus = 'fail' | 'pass' | 'warn';
|
|
40
|
+
export type VoiceProviderContractCheck = {
|
|
41
|
+
detail?: string;
|
|
42
|
+
key: string;
|
|
43
|
+
label: string;
|
|
44
|
+
status: VoiceProviderContractCheckStatus;
|
|
45
|
+
};
|
|
46
|
+
export type VoiceProviderContractDefinition<TProvider extends string = string> = {
|
|
47
|
+
capabilities?: readonly string[];
|
|
48
|
+
configured?: boolean;
|
|
49
|
+
env?: Record<string, string | undefined>;
|
|
50
|
+
fallbackProviders?: readonly TProvider[];
|
|
51
|
+
kind: VoiceProviderStackKind;
|
|
52
|
+
latencyBudgetMs?: number;
|
|
53
|
+
provider: TProvider;
|
|
54
|
+
requiredCapabilities?: readonly string[];
|
|
55
|
+
requiredEnv?: readonly string[];
|
|
56
|
+
selected?: boolean;
|
|
57
|
+
streaming?: boolean;
|
|
58
|
+
};
|
|
59
|
+
export type VoiceProviderContractMatrixInput<TProvider extends string = string> = {
|
|
60
|
+
contracts: readonly VoiceProviderContractDefinition<TProvider>[];
|
|
61
|
+
};
|
|
62
|
+
export type VoiceProviderContractMatrixHandlerOptions<TProvider extends string = string> = VoiceProviderContractMatrixInput<TProvider> | (() => Promise<VoiceProviderContractMatrixInput<TProvider>> | VoiceProviderContractMatrixInput<TProvider>);
|
|
63
|
+
export type VoiceProviderContractMatrixHTMLHandlerOptions<TProvider extends string = string> = {
|
|
64
|
+
matrix: VoiceProviderContractMatrixHandlerOptions<TProvider>;
|
|
65
|
+
render?: (report: VoiceProviderContractMatrixReport<TProvider>) => string | Promise<string>;
|
|
66
|
+
title?: string;
|
|
67
|
+
};
|
|
68
|
+
export type VoiceProviderContractMatrixRoutesOptions<TProvider extends string = string> = VoiceProviderContractMatrixHTMLHandlerOptions<TProvider> & {
|
|
69
|
+
headers?: HeadersInit;
|
|
70
|
+
htmlPath?: false | string;
|
|
71
|
+
name?: string;
|
|
72
|
+
path?: string;
|
|
73
|
+
};
|
|
74
|
+
export type VoiceProviderContractMatrixRow<TProvider extends string = string> = {
|
|
75
|
+
checks: VoiceProviderContractCheck[];
|
|
76
|
+
configured: boolean;
|
|
77
|
+
kind: VoiceProviderStackKind;
|
|
78
|
+
provider: TProvider;
|
|
79
|
+
selected: boolean;
|
|
80
|
+
status: VoiceProviderContractCheckStatus;
|
|
81
|
+
};
|
|
82
|
+
export type VoiceProviderContractMatrixReport<TProvider extends string = string> = {
|
|
83
|
+
failed: number;
|
|
84
|
+
passed: number;
|
|
85
|
+
rows: VoiceProviderContractMatrixRow<TProvider>[];
|
|
86
|
+
status: VoiceProviderContractCheckStatus;
|
|
87
|
+
total: number;
|
|
88
|
+
warned: number;
|
|
89
|
+
};
|
|
38
90
|
export declare const recommendVoiceProviderStack: <TProvider extends string = string>(input: VoiceProviderStackInput<TProvider>) => VoiceProviderStackRecommendation<TProvider>;
|
|
91
|
+
export declare const buildVoiceProviderContractMatrix: <TProvider extends string = string>(input: VoiceProviderContractMatrixInput<TProvider>) => VoiceProviderContractMatrixReport<TProvider>;
|
|
92
|
+
export declare const renderVoiceProviderContractMatrixHTML: <TProvider extends string = string>(report: VoiceProviderContractMatrixReport<TProvider>, options?: {
|
|
93
|
+
title?: string;
|
|
94
|
+
}) => string;
|
|
95
|
+
export declare const createVoiceProviderContractMatrixJSONHandler: <TProvider extends string = string>(matrix: VoiceProviderContractMatrixHandlerOptions<TProvider>) => () => Promise<VoiceProviderContractMatrixReport<TProvider>>;
|
|
96
|
+
export declare const createVoiceProviderContractMatrixHTMLHandler: <TProvider extends string = string>(options: VoiceProviderContractMatrixHTMLHandlerOptions<TProvider>) => () => Promise<Response>;
|
|
97
|
+
export declare const createVoiceProviderContractMatrixRoutes: <TProvider extends string = string>(options: VoiceProviderContractMatrixRoutesOptions<TProvider>) => Elysia<"", {
|
|
98
|
+
decorator: {};
|
|
99
|
+
store: {};
|
|
100
|
+
derive: {};
|
|
101
|
+
resolve: {};
|
|
102
|
+
}, {
|
|
103
|
+
typebox: {};
|
|
104
|
+
error: {};
|
|
105
|
+
}, {
|
|
106
|
+
schema: {};
|
|
107
|
+
standaloneSchema: {};
|
|
108
|
+
macro: {};
|
|
109
|
+
macroFn: {};
|
|
110
|
+
parser: {};
|
|
111
|
+
response: {};
|
|
112
|
+
}, {}, {
|
|
113
|
+
derive: {};
|
|
114
|
+
resolve: {};
|
|
115
|
+
schema: {};
|
|
116
|
+
standaloneSchema: {};
|
|
117
|
+
response: {};
|
|
118
|
+
}, {
|
|
119
|
+
derive: {};
|
|
120
|
+
resolve: {};
|
|
121
|
+
schema: {};
|
|
122
|
+
standaloneSchema: {};
|
|
123
|
+
response: {};
|
|
124
|
+
}>;
|
|
39
125
|
export declare const evaluateVoiceProviderStackGaps: <TProvider extends string = string>(input: VoiceProviderStackCapabilityGapInput<TProvider>) => VoiceProviderStackCapabilityGapReport<TProvider>;
|