@absolutejs/voice 0.0.22-beta.214 → 0.0.22-beta.216

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/README.md CHANGED
@@ -3621,6 +3621,43 @@ if (!report.pass) {
3621
3621
 
3622
3622
  Pass provider routing contract reports into production readiness through `providerRoutingContracts`. Readiness fails when a fallback contract fails, so model-routing regressions become deploy blockers instead of dashboard-only surprises.
3623
3623
 
3624
+ Use `createVoiceProviderSloRoutes(...)` when provider speed needs to be release evidence instead of a dashboard claim. The report reads the same provider routing trace events and checks LLM, STT, and TTS latency, p95 latency, timeout rate, fallback rate, and unresolved provider error rate.
3625
+
3626
+ ```ts
3627
+ import {
3628
+ createVoiceProviderSloRoutes,
3629
+ createVoiceProductionReadinessRoutes
3630
+ } from '@absolutejs/voice';
3631
+
3632
+ const providerSlo = {
3633
+ requiredKinds: ['llm', 'stt', 'tts'],
3634
+ thresholds: {
3635
+ llm: { maxAverageElapsedMs: 2500, maxP95ElapsedMs: 4500 },
3636
+ stt: { maxAverageElapsedMs: 800, maxP95ElapsedMs: 1500 },
3637
+ tts: { maxAverageElapsedMs: 1200, maxP95ElapsedMs: 2200 }
3638
+ }
3639
+ } as const;
3640
+
3641
+ app
3642
+ .use(
3643
+ createVoiceProviderSloRoutes({
3644
+ store: runtime.traces,
3645
+ ...providerSlo
3646
+ })
3647
+ )
3648
+ .use(
3649
+ createVoiceProductionReadinessRoutes({
3650
+ store: runtime.traces,
3651
+ providerSlo,
3652
+ links: {
3653
+ providerSlo: '/voice/provider-slos'
3654
+ }
3655
+ })
3656
+ );
3657
+ ```
3658
+
3659
+ The provider SLO routes expose JSON at `/api/voice/provider-slos`, HTML at `/voice/provider-slos`, and Markdown at `/voice/provider-slos.md`. Readiness adds a `Provider SLO gates` check when `providerSlo` is configured; failing latency, timeout, fallback, or unresolved-error budgets close the deploy gate.
3660
+
3624
3661
  Use `createVoiceProviderContractMatrixPreset(...)` when you want readiness proof for the whole provider stack without hand-writing every LLM, STT, and TTS contract row. The preset stays primitive: you still own provider lists, selected providers, latency budgets, env, capabilities, and route mounting.
3625
3662
 
3626
3663
  ```ts
package/dist/index.d.ts CHANGED
@@ -46,6 +46,7 @@ export { createOpenAIVoiceTTS } from './openaiTTS';
46
46
  export { createVoiceProviderHealthHTMLHandler, createVoiceProviderHealthJSONHandler, createVoiceProviderHealthRoutes, renderVoiceProviderHealthHTML, summarizeVoiceProviderHealth } from './providerHealth';
47
47
  export { createVoiceProviderCapabilityHTMLHandler, createVoiceProviderCapabilityJSONHandler, createVoiceProviderCapabilityRoutes, renderVoiceProviderCapabilityHTML, summarizeVoiceProviderCapabilities } from './providerCapabilities';
48
48
  export { assertVoiceProviderRoutingContract, runVoiceProviderRoutingContract } from './providerRoutingContract';
49
+ export { buildVoiceProviderSloReport, createVoiceProviderSloRoutes, renderVoiceProviderSloHTML, renderVoiceProviderSloMarkdown } from './providerSlo';
49
50
  export { createVoicePhoneAgentProductionSmokeHTMLHandler, createVoicePhoneAgentProductionSmokeJSONHandler, createVoicePhoneAgentProductionSmokeRoutes, renderVoicePhoneAgentProductionSmokeHTML, runVoicePhoneAgentProductionSmokeContract } from './phoneAgentProductionSmoke';
50
51
  export { buildVoiceProductionReadinessGate, buildVoiceProductionReadinessReport, createVoiceProductionReadinessRoutes, renderVoiceProductionReadinessHTML, summarizeVoiceProductionReadinessGate } from './productionReadiness';
51
52
  export { createVoiceReadinessProfile, recommendVoiceReadinessProfile } from './readinessProfiles';
@@ -101,6 +102,7 @@ export type { OpenAIRealtimeAdapterOptions, OpenAIRealtimeModel, OpenAIRealtimeN
101
102
  export type { VoiceProviderHealthStatus, VoiceProviderHealthSummary, VoiceProviderHealthSummaryOptions } from './providerHealth';
102
103
  export type { VoiceProviderCapabilityDefinition, VoiceProviderCapabilityHandlerOptions, VoiceProviderCapabilityHTMLHandlerOptions, VoiceProviderCapabilityKind, VoiceProviderCapabilityOptions, VoiceProviderCapabilityReport, VoiceProviderCapabilityRoutesOptions, VoiceProviderCapabilitySummary } from './providerCapabilities';
103
104
  export type { VoiceProviderRoutingContractDefinition, VoiceProviderRoutingContractIssue, VoiceProviderRoutingContractReport, VoiceProviderRoutingContractRunOptions, VoiceProviderRoutingExpectation, VoiceProviderRoutingStatus } from './providerRoutingContract';
105
+ export type { VoiceProviderSloIssue, VoiceProviderSloKindReport, VoiceProviderSloMetric, VoiceProviderSloReport, VoiceProviderSloReportOptions, VoiceProviderSloRoutesOptions, VoiceProviderSloSessionReport, VoiceProviderSloStatus, VoiceProviderSloThresholdConfig, VoiceProviderSloThresholds } from './providerSlo';
104
106
  export type { VoiceTurnLatencyHTMLHandlerOptions, VoiceTurnLatencyItem, VoiceTurnLatencyOptions, VoiceTurnLatencyReport, VoiceTurnLatencyRoutesOptions, VoiceTurnLatencyStage, VoiceTurnLatencyStatus } from './turnLatency';
105
107
  export type { VoiceLiveLatencyOptions, VoiceLiveLatencyReport, VoiceLiveLatencyRoutesOptions, VoiceLiveLatencySample, VoiceLiveLatencyStatus } from './liveLatency';
106
108
  export type { VoiceLatencySLOBudget, VoiceLatencySLOGateError, VoiceLatencySLOGateOptions, VoiceLatencySLOGateReport, VoiceLatencySLOMeasurement, VoiceLatencySLOStage, VoiceLatencySLOStageSummary, VoiceLatencySLOStatus } from './latencySlo';
package/dist/index.js CHANGED
@@ -21888,12 +21888,309 @@ var assertVoiceProviderRoutingContract = async (options) => {
21888
21888
  }
21889
21889
  return report;
21890
21890
  };
21891
+ // src/providerSlo.ts
21892
+ import { Elysia as Elysia35 } from "elysia";
21893
+ var defaultThresholds = {
21894
+ llm: {
21895
+ maxAverageElapsedMs: 2500,
21896
+ maxErrorRate: 0.02,
21897
+ maxFallbackRate: 0.25,
21898
+ maxP95ElapsedMs: 4500,
21899
+ maxTimeoutRate: 0.02,
21900
+ minSamples: 1
21901
+ },
21902
+ stt: {
21903
+ maxAverageElapsedMs: 800,
21904
+ maxErrorRate: 0.02,
21905
+ maxFallbackRate: 0.25,
21906
+ maxP95ElapsedMs: 1500,
21907
+ maxTimeoutRate: 0.02,
21908
+ minSamples: 1
21909
+ },
21910
+ tts: {
21911
+ maxAverageElapsedMs: 1200,
21912
+ maxErrorRate: 0.02,
21913
+ maxFallbackRate: 0.25,
21914
+ maxP95ElapsedMs: 2200,
21915
+ maxTimeoutRate: 0.02,
21916
+ minSamples: 1
21917
+ }
21918
+ };
21919
+ var providerKinds = ["llm", "stt", "tts"];
21920
+ var escapeHtml36 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
21921
+ var roundMetric3 = (value) => Math.round(value * 1e4) / 1e4;
21922
+ var rate3 = (count, total) => count / Math.max(1, total);
21923
+ var percentile3 = (values, rank) => {
21924
+ if (values.length === 0) {
21925
+ return 0;
21926
+ }
21927
+ const sorted = [...values].sort((left, right) => left - right);
21928
+ const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(rank / 100 * sorted.length) - 1));
21929
+ return sorted[index] ?? 0;
21930
+ };
21931
+ var mergeThresholds = (thresholds) => Object.fromEntries(providerKinds.map((kind) => [
21932
+ kind,
21933
+ {
21934
+ ...defaultThresholds[kind],
21935
+ ...thresholds?.[kind] ?? {}
21936
+ }
21937
+ ]));
21938
+ var createMetric2 = (input) => ({
21939
+ ...input,
21940
+ actual: roundMetric3(input.actual),
21941
+ pass: input.actual <= input.threshold
21942
+ });
21943
+ var isRoutingEvent2 = (event) => Boolean(event && typeof event === "object" && "kind" in event && "sessionId" in event && !("payload" in event));
21944
+ var normalizeEvents2 = (events) => events.every(isRoutingEvent2) ? [...events] : listVoiceRoutingEvents(events);
21945
+ var issueFromMetric = (kind, code, metric) => metric.pass ? undefined : {
21946
+ code,
21947
+ detail: `${metric.label} ${formatMetricValue2(metric)} exceeds ${formatMetricThreshold(metric)}.`,
21948
+ kind,
21949
+ label: metric.label,
21950
+ status: "fail",
21951
+ value: formatMetricValue2(metric)
21952
+ };
21953
+ var summarizeKind = (kind, events, thresholds, required) => {
21954
+ const kindEvents = events.filter((event) => event.kind === kind);
21955
+ const latencies = kindEvents.map((event) => event.elapsedMs).filter((value) => typeof value === "number");
21956
+ const errors = kindEvents.filter((event) => event.status === "error");
21957
+ const unresolvedErrors = errors.filter((event) => !kindEvents.some((candidate) => candidate.sessionId === event.sessionId && candidate.at > event.at && (candidate.status === "fallback" || candidate.status === "success")));
21958
+ const fallbacks = kindEvents.filter((event) => event.status === "fallback");
21959
+ const timeouts = kindEvents.filter((event) => event.timedOut);
21960
+ const averageElapsedMs = latencies.length > 0 ? latencies.reduce((sum, value) => sum + value, 0) / latencies.length : 0;
21961
+ const metrics = {
21962
+ averageElapsedMs: createMetric2({
21963
+ actual: averageElapsedMs,
21964
+ label: "Average latency",
21965
+ threshold: thresholds.maxAverageElapsedMs,
21966
+ unit: "ms"
21967
+ }),
21968
+ errorRate: createMetric2({
21969
+ actual: rate3(unresolvedErrors.length, kindEvents.length),
21970
+ label: "Unresolved provider error rate",
21971
+ threshold: thresholds.maxErrorRate,
21972
+ unit: "rate"
21973
+ }),
21974
+ fallbackRate: createMetric2({
21975
+ actual: rate3(fallbacks.length, kindEvents.length),
21976
+ label: "Fallback rate",
21977
+ threshold: thresholds.maxFallbackRate,
21978
+ unit: "rate"
21979
+ }),
21980
+ p95ElapsedMs: createMetric2({
21981
+ actual: percentile3(latencies, 95),
21982
+ label: "P95 latency",
21983
+ threshold: thresholds.maxP95ElapsedMs,
21984
+ unit: "ms"
21985
+ }),
21986
+ timeoutRate: createMetric2({
21987
+ actual: rate3(timeouts.length, kindEvents.length),
21988
+ label: "Timeout rate",
21989
+ threshold: thresholds.maxTimeoutRate,
21990
+ unit: "rate"
21991
+ })
21992
+ };
21993
+ const issues = [
21994
+ (required || kindEvents.length > 0) && latencies.length < thresholds.minSamples ? {
21995
+ code: "provider_slo.insufficient_latency_samples",
21996
+ detail: `${kind.toUpperCase()} needs ${thresholds.minSamples} latency sample(s), saw ${latencies.length}.`,
21997
+ kind,
21998
+ label: "Provider latency samples",
21999
+ status: required ? "fail" : "warn",
22000
+ value: latencies.length
22001
+ } : undefined,
22002
+ issueFromMetric(kind, "provider_slo.average_latency", metrics.averageElapsedMs),
22003
+ issueFromMetric(kind, "provider_slo.p95_latency", metrics.p95ElapsedMs),
22004
+ issueFromMetric(kind, "provider_slo.error_rate", metrics.errorRate),
22005
+ issueFromMetric(kind, "provider_slo.timeout_rate", metrics.timeoutRate),
22006
+ issueFromMetric(kind, "provider_slo.fallback_rate", metrics.fallbackRate)
22007
+ ].filter((issue) => issue !== undefined);
22008
+ const providers = new Set;
22009
+ for (const event of kindEvents) {
22010
+ if (event.provider) {
22011
+ providers.add(event.provider);
22012
+ }
22013
+ if (event.selectedProvider) {
22014
+ providers.add(event.selectedProvider);
22015
+ }
22016
+ if (event.fallbackProvider) {
22017
+ providers.add(event.fallbackProvider);
22018
+ }
22019
+ }
22020
+ return {
22021
+ events: kindEvents.length,
22022
+ eventsWithLatency: latencies.length,
22023
+ fallbacks: fallbacks.length,
22024
+ issues,
22025
+ kind,
22026
+ metrics,
22027
+ providers: [...providers].sort(),
22028
+ status: issues.some((issue) => issue.status === "fail") ? "fail" : issues.some((issue) => issue.status === "warn") ? "warn" : "pass",
22029
+ thresholds,
22030
+ timeouts: timeouts.length,
22031
+ unresolvedErrors: unresolvedErrors.length
22032
+ };
22033
+ };
22034
+ var summarizeSessions = (events) => {
22035
+ const sessions = new Map;
22036
+ for (const event of events) {
22037
+ const session = sessions.get(event.sessionId) ?? {
22038
+ errorCount: 0,
22039
+ fallbackCount: 0,
22040
+ kinds: [],
22041
+ lastEventAt: event.at,
22042
+ sessionId: event.sessionId,
22043
+ status: "pass",
22044
+ timeoutCount: 0
22045
+ };
22046
+ session.lastEventAt = Math.max(session.lastEventAt, event.at);
22047
+ if (!session.kinds.includes(event.kind)) {
22048
+ session.kinds.push(event.kind);
22049
+ }
22050
+ if (typeof event.elapsedMs === "number") {
22051
+ session.maxElapsedMs = session.maxElapsedMs === undefined ? event.elapsedMs : Math.max(session.maxElapsedMs, event.elapsedMs);
22052
+ }
22053
+ if (event.status === "error") {
22054
+ session.errorCount += 1;
22055
+ }
22056
+ if (event.status === "fallback") {
22057
+ session.fallbackCount += 1;
22058
+ }
22059
+ if (event.timedOut) {
22060
+ session.timeoutCount += 1;
22061
+ }
22062
+ session.status = session.errorCount > 0 || session.timeoutCount > 0 ? "fail" : session.fallbackCount > 0 ? "warn" : "pass";
22063
+ sessions.set(event.sessionId, session);
22064
+ }
22065
+ return [...sessions.values()].sort((left, right) => right.lastEventAt - left.lastEventAt);
22066
+ };
22067
+ var buildVoiceProviderSloReport = async (options = {}) => {
22068
+ const rawEvents = options.events ?? await options.store?.list() ?? [];
22069
+ const now = options.now ?? Date.now();
22070
+ const events = normalizeEvents2(rawEvents).filter((event) => typeof options.maxAgeMs !== "number" || now - event.at <= options.maxAgeMs);
22071
+ const thresholds = mergeThresholds(options.thresholds);
22072
+ const observedKinds = new Set(events.map((event) => event.kind));
22073
+ const requiredKinds = new Set(options.requiredKinds ?? [...observedKinds]);
22074
+ const kindReports = Object.fromEntries(providerKinds.map((kind) => [
22075
+ kind,
22076
+ summarizeKind(kind, events, thresholds[kind], requiredKinds.has(kind))
22077
+ ]));
22078
+ const issues = Object.values(kindReports).flatMap((report) => report.issues);
22079
+ const eventsWithLatency = events.filter((event) => typeof event.elapsedMs === "number").length;
22080
+ if (events.length === 0) {
22081
+ issues.push({
22082
+ code: "provider_slo.no_routing_events",
22083
+ detail: "No provider routing events are recorded yet. Run a live turn, smoke, or provider simulation before certifying latency.",
22084
+ label: "Provider routing evidence",
22085
+ status: "warn",
22086
+ value: 0
22087
+ });
22088
+ }
22089
+ return {
22090
+ checkedAt: Date.now(),
22091
+ events: events.length,
22092
+ eventsWithLatency,
22093
+ issues,
22094
+ kinds: kindReports,
22095
+ sessions: summarizeSessions(events),
22096
+ status: issues.some((issue) => issue.status === "fail") ? "fail" : issues.some((issue) => issue.status === "warn") ? "warn" : "pass",
22097
+ thresholds
22098
+ };
22099
+ };
22100
+ var formatMetricValue2 = (metric) => metric.unit === "rate" ? `${(metric.actual * 100).toFixed(2)}%` : metric.unit === "ms" ? `${Math.round(metric.actual)}ms` : String(metric.actual);
22101
+ var formatMetricThreshold = (metric) => metric.unit === "rate" ? `${(metric.threshold * 100).toFixed(2)}%` : metric.unit === "ms" ? `${Math.round(metric.threshold)}ms` : String(metric.threshold);
22102
+ var getMetric = (report, key) => report.metrics[key];
22103
+ var renderVoiceProviderSloMarkdown = (report) => {
22104
+ const rows = providerKinds.map((kind) => {
22105
+ const kindReport = report.kinds[kind];
22106
+ return `| ${kind.toUpperCase()} | ${kindReport.status} | ${kindReport.events} | ${kindReport.eventsWithLatency} | ${formatMetricValue2(getMetric(kindReport, "averageElapsedMs"))} | ${formatMetricValue2(getMetric(kindReport, "p95ElapsedMs"))} | ${formatMetricValue2(getMetric(kindReport, "errorRate"))} | ${formatMetricValue2(getMetric(kindReport, "timeoutRate"))} | ${formatMetricValue2(getMetric(kindReport, "fallbackRate"))} |`;
22107
+ }).join(`
22108
+ `);
22109
+ const issues = report.issues.map((issue) => `- ${issue.status}: ${issue.kind ? `${issue.kind.toUpperCase()} ` : ""}${issue.label}${issue.detail ? ` - ${issue.detail}` : ""}`).join(`
22110
+ `) || "No provider SLO issues.";
22111
+ return `# Voice Provider SLO Report
22112
+
22113
+ Generated: ${new Date(report.checkedAt).toISOString()}
22114
+
22115
+ Overall: **${report.status}**
22116
+
22117
+ Events: ${report.events}
22118
+
22119
+ Events with latency: ${report.eventsWithLatency}
22120
+
22121
+ | Kind | Status | Events | Latency Samples | Avg | P95 | Error Rate | Timeout Rate | Fallback Rate |
22122
+ | --- | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
22123
+ ${rows}
22124
+
22125
+ ## Issues
22126
+
22127
+ ${issues}
22128
+ `;
22129
+ };
22130
+ var renderVoiceProviderSloHTML = (report, options = {}) => {
22131
+ const title = options.title ?? "AbsoluteJS Voice Provider SLOs";
22132
+ const kindCards = providerKinds.map((kind) => {
22133
+ const kindReport = report.kinds[kind];
22134
+ const metrics = Object.values(kindReport.metrics).map((metric) => `<div><dt>${escapeHtml36(metric.label)}</dt><dd>${escapeHtml36(formatMetricValue2(metric))}</dd><small>budget ${escapeHtml36(formatMetricThreshold(metric))}</small></div>`).join("");
22135
+ const providers = kindReport.providers.length ? kindReport.providers.join(", ") : "none recorded";
22136
+ return `<article class="${escapeHtml36(kindReport.status)}"><h2>${kind.toUpperCase()} <span>${escapeHtml36(kindReport.status)}</span></h2><p>${kindReport.events} routing event(s), ${kindReport.eventsWithLatency} latency sample(s), providers: ${escapeHtml36(providers)}.</p><dl>${metrics}</dl></article>`;
22137
+ }).join("");
22138
+ const issues = report.issues.length > 0 ? `<ul>${report.issues.map((issue) => `<li class="${escapeHtml36(issue.status)}"><strong>${escapeHtml36(issue.kind ? `${issue.kind.toUpperCase()} ${issue.label}` : issue.label)}</strong><span>${escapeHtml36(issue.detail ?? "")}</span></li>`).join("")}</ul>` : "<p>No provider SLO issues.</p>";
22139
+ const snippet = `createVoiceProviderSloRoutes({
22140
+ store: runtimeStorage.traces,
22141
+ requiredKinds: ['llm', 'stt', 'tts'],
22142
+ thresholds: {
22143
+ llm: { maxAverageElapsedMs: 2500, maxP95ElapsedMs: 4500 },
22144
+ stt: { maxAverageElapsedMs: 800, maxP95ElapsedMs: 1500 },
22145
+ tts: { maxAverageElapsedMs: 1200, maxP95ElapsedMs: 2200 }
22146
+ }
22147
+ })`;
22148
+ 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:#101318;color:#f8f4e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,article,.primitive{background:#171b22;border:1px solid #2c3340;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(14,165,233,.2),rgba(245,158,11,.12))}.eyebrow{color:#7dd3fc;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,4.9rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.status,article h2 span{border:1px solid #475569;border-radius:999px;display:inline-flex;font-size:.85rem;padding:6px 10px}.pass{border-color:rgba(34,197,94,.65)}.warn{border-color:rgba(245,158,11,.7)}.fail{border-color:rgba(239,68,68,.75)}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}dl{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}dt{color:#cbd5e1;font-size:.78rem;text-transform:uppercase}dd{font-size:1.7rem;font-weight:900;margin:0}small{color:#a8b3c2}ul{display:grid;gap:10px;list-style:none;padding:0}li{background:#101318;border:1px solid #2c3340;border-radius:16px;padding:12px}li span{color:#cbd5e1;display:block;margin-top:4px}.primitive{background:#11161d}.primitive code{color:#bae6fd}.primitive pre{background:#070b10;border:1px solid #243041;border-radius:16px;color:#e0f2fe;overflow:auto;padding:16px}</style></head><body><main><section class="hero"><p class="eyebrow">Provider latency and fallback proof</p><h1>${escapeHtml36(title)}</h1><p class="status ${escapeHtml36(report.status)}">${escapeHtml36(report.status)}</p><p>${report.events} provider routing event(s), ${report.eventsWithLatency} latency sample(s).</p></section><section class="grid">${kindCards}</section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceProviderSloRoutes(...)</code> turns provider speed into release evidence</h2><p>Pair this report with production readiness so LLM/STT/TTS latency, timeout, fallback, and unresolved error regressions block deploys.</p><pre><code>${escapeHtml36(snippet)}</code></pre></section><section><h2>Issues</h2>${issues}</section></main></body></html>`;
22149
+ };
22150
+ var createVoiceProviderSloRoutes = (options) => {
22151
+ const path = options.path ?? "/api/voice/provider-slos";
22152
+ const htmlPath = options.htmlPath ?? "/voice/provider-slos";
22153
+ const markdownPath = options.markdownPath ?? "/voice/provider-slos.md";
22154
+ const headers = {
22155
+ "cache-control": "no-store",
22156
+ ...options.headers ?? {}
22157
+ };
22158
+ const buildReport = () => buildVoiceProviderSloReport(options);
22159
+ const app = new Elysia35({ name: options.name ?? "absolute-voice-provider-slos" });
22160
+ app.get(path, async () => Response.json(await buildReport(), { headers }));
22161
+ if (markdownPath !== false) {
22162
+ app.get(markdownPath, async () => {
22163
+ const report = await buildReport();
22164
+ return new Response(renderVoiceProviderSloMarkdown(report), {
22165
+ headers: {
22166
+ ...headers,
22167
+ "content-type": "text/markdown; charset=utf-8"
22168
+ }
22169
+ });
22170
+ });
22171
+ }
22172
+ if (htmlPath !== false) {
22173
+ app.get(htmlPath, async () => {
22174
+ const report = await buildReport();
22175
+ const html = options.render ? await options.render(report) : renderVoiceProviderSloHTML(report, {
22176
+ title: options.title
22177
+ });
22178
+ return new Response(html, {
22179
+ headers: {
22180
+ ...headers,
22181
+ "content-type": "text/html; charset=utf-8"
22182
+ }
22183
+ });
22184
+ });
22185
+ }
22186
+ return app;
22187
+ };
21891
22188
  // src/productionReadiness.ts
21892
- import { Elysia as Elysia36 } from "elysia";
22189
+ import { Elysia as Elysia37 } from "elysia";
21893
22190
 
21894
22191
  // src/opsRecovery.ts
21895
- import { Elysia as Elysia35 } from "elysia";
21896
- var escapeHtml36 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
22192
+ import { Elysia as Elysia36 } from "elysia";
22193
+ var escapeHtml37 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
21897
22194
  var getString14 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
21898
22195
  var hrefForSession = (value, sessionId) => {
21899
22196
  if (typeof value === "function") {
@@ -22107,19 +22404,19 @@ ${failedSessions || "None."}
22107
22404
  ${report.latency ? renderVoiceLatencySLOMarkdown(report.latency, { title: "Latency SLO" }) : "Latency SLO disabled."}
22108
22405
  `;
22109
22406
  };
22110
- var renderDeliverySummary = (label, summary) => summary ? `<article><span>${escapeHtml36(label)}</span><strong>${String(summary.failed + summary.deadLettered)} failed</strong><small>${String(summary.pending)} pending \xB7 ${String(summary.retryEligible)} retry eligible \xB7 ${String(summary.total)} total</small></article>` : `<article><span>${escapeHtml36(label)}</span><strong>not configured</strong></article>`;
22407
+ var renderDeliverySummary = (label, summary) => summary ? `<article><span>${escapeHtml37(label)}</span><strong>${String(summary.failed + summary.deadLettered)} failed</strong><small>${String(summary.pending)} pending \xB7 ${String(summary.retryEligible)} retry eligible \xB7 ${String(summary.total)} total</small></article>` : `<article><span>${escapeHtml37(label)}</span><strong>not configured</strong></article>`;
22111
22408
  var renderVoiceOpsRecoveryHTML = (report, options = {}) => {
22112
22409
  const title = options.title ?? "Voice Ops Recovery";
22113
- const issues = report.issues.map((issue) => `<tr><td>${escapeHtml36(issue.severity)}</td><td><code>${escapeHtml36(issue.code)}</code></td><td>${issue.href ? `<a href="${escapeHtml36(issue.href)}">${escapeHtml36(issue.label)}</a>` : escapeHtml36(issue.label)}</td><td>${escapeHtml36(String(issue.value ?? ""))}</td><td>${escapeHtml36(issue.detail ?? "")}</td></tr>`).join("");
22114
- const providers = report.providers.providers.map((provider) => `<tr><td>${escapeHtml36(provider.provider)}</td><td>${escapeHtml36(provider.status)}</td><td>${String(provider.runCount)}</td><td>${String(provider.errorCount)}</td><td>${String(provider.fallbackCount)}</td><td>${escapeHtml36(provider.lastError ?? "")}</td></tr>`).join("");
22115
- const failedSessions = report.failedSessions.map((session) => `<li>${session.operationsRecordHref ? `<a href="${escapeHtml36(session.operationsRecordHref)}">${escapeHtml36(session.sessionId)}</a>` : escapeHtml36(session.sessionId)}${session.provider ? ` via ${escapeHtml36(session.provider)}` : ""}${session.error ? `: ${escapeHtml36(session.error)}` : ""}</li>`).join("");
22116
- 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{font-family:ui-sans-serif,system-ui,sans-serif;background:#f8fafc;color:#172033;margin:2rem;line-height:1.45}main{max-width:1180px;margin:auto}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:.75rem;margin:1rem 0}article{background:white;border:1px solid #dbe3ef;border-radius:14px;padding:1rem;box-shadow:0 10px 28px rgba(15,23,42,.05)}article span{display:block;color:#64748b;font-size:.85rem}article strong{display:block;font-size:1.5rem;margin:.2rem 0}article small{color:#64748b}table{border-collapse:collapse;width:100%;background:white;border:1px solid #dbe3ef;border-radius:14px;overflow:hidden}th,td{border-bottom:1px solid #e2e8f0;padding:.7rem;text-align:left;vertical-align:top}code{font-size:.85em}.status{display:inline-flex;border-radius:999px;padding:.35rem .7rem;background:${report.status === "fail" ? "#fee2e2" : report.status === "warn" ? "#fef3c7" : "#dcfce7"};color:${report.status === "fail" ? "#991b1b" : report.status === "warn" ? "#92400e" : "#166534"};font-weight:700}</style></head><body><main><h1>${escapeHtml36(title)}</h1><p><span class="status">${escapeHtml36(report.status)}</span> Checked ${escapeHtml36(new Date(report.checkedAt).toLocaleString())}</p><section class="grid"><article><span>Recovered fallbacks</span><strong>${String(report.providers.recoveredFallbacks)}</strong></article><article><span>Unresolved providers</span><strong>${String(report.providers.unresolvedFailures)}</strong></article><article><span>Operator interventions</span><strong>${String(report.interventions.total)}</strong></article><article><span>Latency status</span><strong>${escapeHtml36(report.latency?.status ?? "disabled")}</strong></article>${renderDeliverySummary("Audit delivery", report.auditDeliveries)}${renderDeliverySummary("Trace delivery", report.traceDeliveries)}${renderDeliverySummary("Handoff delivery", report.handoffDeliveries)}</section><h2>Issues</h2><table><thead><tr><th>Severity</th><th>Code</th><th>Label</th><th>Value</th><th>Detail</th></tr></thead><tbody>${issues || '<tr><td colspan="5">No recovery issues.</td></tr>'}</tbody></table><h2>Providers</h2><table><thead><tr><th>Provider</th><th>Status</th><th>Runs</th><th>Errors</th><th>Fallbacks</th><th>Last error</th></tr></thead><tbody>${providers || '<tr><td colspan="6">No provider activity.</td></tr>'}</tbody></table><h2>Failed Sessions</h2><ul>${failedSessions || "<li>None.</li>"}</ul></main></body></html>`;
22410
+ const issues = report.issues.map((issue) => `<tr><td>${escapeHtml37(issue.severity)}</td><td><code>${escapeHtml37(issue.code)}</code></td><td>${issue.href ? `<a href="${escapeHtml37(issue.href)}">${escapeHtml37(issue.label)}</a>` : escapeHtml37(issue.label)}</td><td>${escapeHtml37(String(issue.value ?? ""))}</td><td>${escapeHtml37(issue.detail ?? "")}</td></tr>`).join("");
22411
+ const providers = report.providers.providers.map((provider) => `<tr><td>${escapeHtml37(provider.provider)}</td><td>${escapeHtml37(provider.status)}</td><td>${String(provider.runCount)}</td><td>${String(provider.errorCount)}</td><td>${String(provider.fallbackCount)}</td><td>${escapeHtml37(provider.lastError ?? "")}</td></tr>`).join("");
22412
+ const failedSessions = report.failedSessions.map((session) => `<li>${session.operationsRecordHref ? `<a href="${escapeHtml37(session.operationsRecordHref)}">${escapeHtml37(session.sessionId)}</a>` : escapeHtml37(session.sessionId)}${session.provider ? ` via ${escapeHtml37(session.provider)}` : ""}${session.error ? `: ${escapeHtml37(session.error)}` : ""}</li>`).join("");
22413
+ 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:#f8fafc;color:#172033;margin:2rem;line-height:1.45}main{max-width:1180px;margin:auto}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:.75rem;margin:1rem 0}article{background:white;border:1px solid #dbe3ef;border-radius:14px;padding:1rem;box-shadow:0 10px 28px rgba(15,23,42,.05)}article span{display:block;color:#64748b;font-size:.85rem}article strong{display:block;font-size:1.5rem;margin:.2rem 0}article small{color:#64748b}table{border-collapse:collapse;width:100%;background:white;border:1px solid #dbe3ef;border-radius:14px;overflow:hidden}th,td{border-bottom:1px solid #e2e8f0;padding:.7rem;text-align:left;vertical-align:top}code{font-size:.85em}.status{display:inline-flex;border-radius:999px;padding:.35rem .7rem;background:${report.status === "fail" ? "#fee2e2" : report.status === "warn" ? "#fef3c7" : "#dcfce7"};color:${report.status === "fail" ? "#991b1b" : report.status === "warn" ? "#92400e" : "#166534"};font-weight:700}</style></head><body><main><h1>${escapeHtml37(title)}</h1><p><span class="status">${escapeHtml37(report.status)}</span> Checked ${escapeHtml37(new Date(report.checkedAt).toLocaleString())}</p><section class="grid"><article><span>Recovered fallbacks</span><strong>${String(report.providers.recoveredFallbacks)}</strong></article><article><span>Unresolved providers</span><strong>${String(report.providers.unresolvedFailures)}</strong></article><article><span>Operator interventions</span><strong>${String(report.interventions.total)}</strong></article><article><span>Latency status</span><strong>${escapeHtml37(report.latency?.status ?? "disabled")}</strong></article>${renderDeliverySummary("Audit delivery", report.auditDeliveries)}${renderDeliverySummary("Trace delivery", report.traceDeliveries)}${renderDeliverySummary("Handoff delivery", report.handoffDeliveries)}</section><h2>Issues</h2><table><thead><tr><th>Severity</th><th>Code</th><th>Label</th><th>Value</th><th>Detail</th></tr></thead><tbody>${issues || '<tr><td colspan="5">No recovery issues.</td></tr>'}</tbody></table><h2>Providers</h2><table><thead><tr><th>Provider</th><th>Status</th><th>Runs</th><th>Errors</th><th>Fallbacks</th><th>Last error</th></tr></thead><tbody>${providers || '<tr><td colspan="6">No provider activity.</td></tr>'}</tbody></table><h2>Failed Sessions</h2><ul>${failedSessions || "<li>None.</li>"}</ul></main></body></html>`;
22117
22414
  };
22118
22415
  var createVoiceOpsRecoveryRoutes = (options = {}) => {
22119
22416
  const path = options.path ?? "/api/voice/ops-recovery";
22120
22417
  const htmlPath = options.htmlPath === undefined ? "/ops-recovery" : options.htmlPath;
22121
22418
  const markdownPath = options.markdownPath === undefined ? `${path}.md` : options.markdownPath;
22122
- const routes = new Elysia35({
22419
+ const routes = new Elysia36({
22123
22420
  name: options.name ?? "absolutejs-voice-ops-recovery"
22124
22421
  }).get(path, async () => buildVoiceOpsRecoveryReport(options));
22125
22422
  if (htmlPath) {
@@ -22149,7 +22446,7 @@ var createVoiceOpsRecoveryRoutes = (options = {}) => {
22149
22446
  };
22150
22447
 
22151
22448
  // src/productionReadiness.ts
22152
- var escapeHtml37 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
22449
+ var escapeHtml38 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
22153
22450
  var rollupStatus3 = (checks) => checks.some((check) => check.status === "fail") ? "fail" : checks.some((check) => check.status === "warn") ? "warn" : "pass";
22154
22451
  var readinessGateCodes = {
22155
22452
  "Agent squad contracts": "voice.readiness.agent_squad_contracts",
@@ -22169,6 +22466,7 @@ var readinessGateCodes = {
22169
22466
  "Provider fallback recovery": "voice.readiness.provider_fallback_recovery",
22170
22467
  "Provider health": "voice.readiness.provider_health",
22171
22468
  "Provider routing contracts": "voice.readiness.provider_routing_contracts",
22469
+ "Provider SLO gates": "voice.readiness.provider_slo_gates",
22172
22470
  "Provider stack capabilities": "voice.readiness.provider_stack_capabilities",
22173
22471
  "Quality gates": "voice.readiness.quality_gates",
22174
22472
  "Reconnect recovery contracts": "voice.readiness.reconnect_contracts",
@@ -22241,6 +22539,18 @@ var resolveProviderRoutingContracts = async (options, input) => {
22241
22539
  }
22242
22540
  return typeof options.providerRoutingContracts === "function" ? await options.providerRoutingContracts(input) : options.providerRoutingContracts;
22243
22541
  };
22542
+ var isVoiceProviderSloReport = (value) => typeof value.status === "string" && typeof value.checkedAt === "number" && typeof value.events === "number";
22543
+ var resolveProviderSlo = async (options, input) => {
22544
+ if (options.providerSlo === false || options.providerSlo === undefined) {
22545
+ return;
22546
+ }
22547
+ const value = typeof options.providerSlo === "function" ? await options.providerSlo(input) : options.providerSlo;
22548
+ return isVoiceProviderSloReport(value) ? value : buildVoiceProviderSloReport({
22549
+ ...value,
22550
+ events: value.events,
22551
+ store: value.store ?? options.store
22552
+ });
22553
+ };
22244
22554
  var resolveProviderStack = async (options, input) => {
22245
22555
  if (options.providerStack === false || options.providerStack === undefined) {
22246
22556
  return;
@@ -22567,6 +22877,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
22567
22877
  carriers,
22568
22878
  agentSquadContracts,
22569
22879
  providerRoutingContracts,
22880
+ providerSlo,
22570
22881
  providerStack,
22571
22882
  providerContractMatrix,
22572
22883
  phoneAgentSmokes,
@@ -22601,6 +22912,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
22601
22912
  resolveCarriers(options, { query, request }),
22602
22913
  resolveAgentSquadContracts(options, { query, request }),
22603
22914
  resolveProviderRoutingContracts(options, { query, request }),
22915
+ resolveProviderSlo(options, { query, request }),
22604
22916
  resolveProviderStack(options, { query, request }),
22605
22917
  resolveProviderContractMatrix(options, { query, request }),
22606
22918
  resolvePhoneAgentSmokes(options, { query, request }),
@@ -22796,6 +23108,12 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
22796
23108
  status: providerRoutingContracts.some((report) => !report.pass) ? "fail" : providerRoutingContracts.length === 0 ? "warn" : "pass",
22797
23109
  total: providerRoutingContracts.length
22798
23110
  } : undefined;
23111
+ const providerSloSummary = providerSlo ? {
23112
+ events: providerSlo.events,
23113
+ eventsWithLatency: providerSlo.eventsWithLatency,
23114
+ issues: providerSlo.issues.length,
23115
+ status: providerSlo.status
23116
+ } : undefined;
22799
23117
  const phoneAgentSmokeSummary = phoneAgentSmokes ? {
22800
23118
  failed: phoneAgentSmokes.filter((report) => !report.pass).length,
22801
23119
  passed: phoneAgentSmokes.filter((report) => report.pass).length,
@@ -22854,6 +23172,31 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
22854
23172
  ]
22855
23173
  });
22856
23174
  }
23175
+ if (providerSloSummary && providerSlo) {
23176
+ const firstIssue = providerSlo.issues[0];
23177
+ checks.push({
23178
+ detail: providerSloSummary.status === "pass" ? `${providerSloSummary.eventsWithLatency} provider latency sample(s) are inside LLM/STT/TTS SLO budgets.` : firstIssue?.detail ?? `${providerSloSummary.issues} provider SLO issue(s) need review.`,
23179
+ href: firstIssue?.sessionId ? voiceOperationsRecordHref(options.links?.operationsRecords ?? "/voice-operations", firstIssue.sessionId) : options.links?.providerSlo ?? options.links?.resilience ?? "/voice/provider-slos",
23180
+ label: "Provider SLO gates",
23181
+ proofSource: proofSource("providerSlo", "providerSlos"),
23182
+ status: providerSloSummary.status,
23183
+ value: `${providerSloSummary.eventsWithLatency}/${providerSloSummary.events}`,
23184
+ actions: providerSloSummary.status === "pass" ? [] : [
23185
+ ...firstIssue?.sessionId ? [
23186
+ {
23187
+ description: "Open the exact call/session operations record for the first provider SLO issue.",
23188
+ href: voiceOperationsRecordHref(options.links?.operationsRecords ?? "/voice-operations", firstIssue.sessionId),
23189
+ label: "Open impacted operations record"
23190
+ }
23191
+ ] : [],
23192
+ {
23193
+ description: "Open provider SLO proof and inspect latency, timeout, fallback, and unresolved error budgets.",
23194
+ href: options.links?.providerSlo ?? options.links?.resilience ?? "/voice/provider-slos",
23195
+ label: "Open provider SLO report"
23196
+ }
23197
+ ]
23198
+ });
23199
+ }
22857
23200
  if (providerStack) {
22858
23201
  const missingLanes = providerStack.gaps.filter((gap) => gap.status !== "pass");
22859
23202
  checks.push({
@@ -23081,6 +23424,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
23081
23424
  phoneAgentSmoke: "/sessions",
23082
23425
  providerContracts: "/provider-contracts",
23083
23426
  providerRoutingContracts: "/resilience",
23427
+ providerSlo: "/voice/provider-slos",
23084
23428
  quality: "/quality",
23085
23429
  reconnectContracts: "/sessions",
23086
23430
  resilience: "/resilience",
@@ -23121,6 +23465,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
23121
23465
  providerRecovery,
23122
23466
  phoneAgentSmokes: phoneAgentSmokeSummary,
23123
23467
  providerRoutingContracts: providerRoutingContractSummary,
23468
+ providerSlo: providerSloSummary,
23124
23469
  reconnectContracts: reconnectContractSummary,
23125
23470
  quality: {
23126
23471
  status: quality.status
@@ -23140,22 +23485,22 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
23140
23485
  var buildVoiceProductionReadinessGate = async (options, input = {}) => summarizeVoiceProductionReadinessGate(await buildVoiceProductionReadinessReport(options, input), options.gate || undefined);
23141
23486
  var renderVoiceProductionReadinessHTML = (report, options = {}) => {
23142
23487
  const title = options.title ?? "AbsoluteJS Voice Production Readiness";
23143
- const profile = report.profile ? `<section class="profile"><p class="eyebrow">Readiness profile</p><h2>${escapeHtml37(report.profile.name)}</h2><p>${escapeHtml37(report.profile.description)}</p><p>${escapeHtml37(report.profile.purpose)}</p><div class="profile-surfaces">${report.profile.surfaces.map((surface) => `<article class="${surface.configured ? "pass" : "warn"}"><span>${surface.configured ? "CONFIGURED" : "EXPECTED"}</span><strong>${surface.href ? `<a href="${escapeHtml37(surface.href)}">${escapeHtml37(surface.label)}</a>` : escapeHtml37(surface.label)}</strong></article>`).join("")}</div></section>` : "";
23488
+ const profile = report.profile ? `<section class="profile"><p class="eyebrow">Readiness profile</p><h2>${escapeHtml38(report.profile.name)}</h2><p>${escapeHtml38(report.profile.description)}</p><p>${escapeHtml38(report.profile.purpose)}</p><div class="profile-surfaces">${report.profile.surfaces.map((surface) => `<article class="${surface.configured ? "pass" : "warn"}"><span>${surface.configured ? "CONFIGURED" : "EXPECTED"}</span><strong>${surface.href ? `<a href="${escapeHtml38(surface.href)}">${escapeHtml38(surface.label)}</a>` : escapeHtml38(surface.label)}</strong></article>`).join("")}</div></section>` : "";
23144
23489
  const checks = report.checks.map((check, index) => {
23145
- const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${escapeHtml37(action.href)}">${escapeHtml37(action.label)}</button>` : `<a href="${escapeHtml37(action.href)}">${escapeHtml37(action.label)}</a>`).join("");
23146
- return `<article class="check ${escapeHtml37(check.status)}">
23490
+ const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${escapeHtml38(action.href)}">${escapeHtml38(action.label)}</button>` : `<a href="${escapeHtml38(action.href)}">${escapeHtml38(action.label)}</a>`).join("");
23491
+ return `<article class="check ${escapeHtml38(check.status)}">
23147
23492
  <div>
23148
- <span>${escapeHtml37(check.status.toUpperCase())}</span>
23149
- <h2>${escapeHtml37(check.label)}</h2>
23150
- ${check.detail ? `<p>${escapeHtml37(check.detail)}</p>` : ""}
23151
- ${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${escapeHtml37(check.proofSource.href)}">${escapeHtml37(check.proofSource.sourceLabel)}</a>` : escapeHtml37(check.proofSource.sourceLabel)}${check.proofSource.detail ? ` \xB7 ${escapeHtml37(check.proofSource.detail)}` : ""}</p>` : ""}
23493
+ <span>${escapeHtml38(check.status.toUpperCase())}</span>
23494
+ <h2>${escapeHtml38(check.label)}</h2>
23495
+ ${check.detail ? `<p>${escapeHtml38(check.detail)}</p>` : ""}
23496
+ ${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${escapeHtml38(check.proofSource.href)}">${escapeHtml38(check.proofSource.sourceLabel)}</a>` : escapeHtml38(check.proofSource.sourceLabel)}${check.proofSource.detail ? ` \xB7 ${escapeHtml38(check.proofSource.detail)}` : ""}</p>` : ""}
23152
23497
  ${actions ? `<p class="actions">${actions}</p>` : ""}
23153
23498
  </div>
23154
- <strong>${escapeHtml37(String(check.value ?? check.status))}</strong>
23155
- ${check.href ? `<a href="${escapeHtml37(check.href)}">Open surface</a>` : ""}
23499
+ <strong>${escapeHtml38(String(check.value ?? check.status))}</strong>
23500
+ ${check.href ? `<a href="${escapeHtml38(check.href)}">Open surface</a>` : ""}
23156
23501
  </article>`;
23157
23502
  }).join("");
23158
- const snippet = escapeHtml37(`createVoiceProductionReadinessRoutes({
23503
+ const snippet = escapeHtml38(`createVoiceProductionReadinessRoutes({
23159
23504
  htmlPath: '/production-readiness',
23160
23505
  path: '/api/production-readiness',
23161
23506
  gatePath: '/api/production-readiness/gate',
@@ -23171,13 +23516,13 @@ var renderVoiceProductionReadinessHTML = (report, options = {}) => {
23171
23516
  providerRoutingContracts: loadProviderRoutingContracts,
23172
23517
  store: traceStore
23173
23518
  });`);
23174
- 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{background:#0c0f14;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1060px;padding:32px}.hero,.primitive,.profile{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.primitive,.profile{background:#111722}.primitive{border-color:#3a3f2d}.eyebrow{color:#fbbf24;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{display:inline-flex;border:1px solid #3f3f46;border-radius:999px;padding:8px 12px}.primitive code{color:#fde68a}.primitive p{color:#c8ccd3;line-height:1.55;margin:.45rem 0 0}.primitive pre{background:#0b0f16;border:1px solid #2c3440;border-radius:18px;color:#fef3c7;margin:16px 0 0;overflow:auto;padding:16px}.status.pass,.check.pass,.profile-surfaces .pass{border-color:rgba(34,197,94,.55)}.status.warn,.check.warn,.profile-surfaces .warn{border-color:rgba(245,158,11,.65)}.status.fail,.check.fail{border-color:rgba(239,68,68,.75)}.checks{display:grid;gap:14px}.check{align-items:center;background:#141922;border:1px solid #26313d;border-radius:22px;display:grid;gap:16px;grid-template-columns:1fr auto auto;padding:18px}.check span,.profile-surfaces span{color:#a8b0b8;font-size:.78rem;font-weight:900;letter-spacing:.08em}.check h2{margin:.2rem 0}.check p,.profile p{color:#b9c0c8;margin:.2rem 0 0}.check .proof-source{color:#f9d77e;font-weight:800}.check strong{font-size:1.5rem}.profile-surfaces{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));margin-top:16px}.profile-surfaces article{background:#141922;border:1px solid #26313d;border-radius:16px;padding:14px}.profile-surfaces strong{display:block;margin-top:6px}.actions{display:flex;flex-wrap:wrap;gap:10px}.check a,a{color:#fbbf24}button{background:#fbbf24;border:0;border-radius:999px;color:#111827;cursor:pointer;font-weight:800;padding:9px 12px}button:disabled{cursor:wait;opacity:.65}@media(max-width:760px){main{padding:20px}.check{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Self-hosted readiness</p><h1>${escapeHtml37(title)}</h1><p>One deployable pass/fail report for quality gates, provider failover, session health, handoffs, routing evidence, and optional carrier readiness.</p><p class="status ${escapeHtml37(report.status)}">Overall: ${escapeHtml37(report.status.toUpperCase())}</p><p>Checked ${escapeHtml37(new Date(report.checkedAt).toLocaleString())}</p></section>${profile}<section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceProductionReadinessRoutes(...)</code> builds this deploy gate</h2><p>Mount one package primitive to expose JSON readiness, HTML readiness, and a machine-readable gate route. Feed it the proof stores and contract reports your app already owns.</p><pre><code>${snippet}</code></pre></section><section class="checks">${checks}</section></main><script>document.querySelectorAll("[data-readiness-action]").forEach((button)=>{button.addEventListener("click",async()=>{const url=button.getAttribute("data-action-url");if(!url)return;button.disabled=true;const original=button.textContent;button.textContent="Running...";try{const response=await fetch(url,{method:"POST"});button.textContent=response.ok?"Done. Reloading...":"Failed";if(response.ok)setTimeout(()=>location.reload(),500)}catch{button.textContent="Failed"}finally{setTimeout(()=>{button.disabled=false;button.textContent=original},1500)}})});</script></body></html>`;
23519
+ 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:#0c0f14;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1060px;padding:32px}.hero,.primitive,.profile{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.primitive,.profile{background:#111722}.primitive{border-color:#3a3f2d}.eyebrow{color:#fbbf24;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{display:inline-flex;border:1px solid #3f3f46;border-radius:999px;padding:8px 12px}.primitive code{color:#fde68a}.primitive p{color:#c8ccd3;line-height:1.55;margin:.45rem 0 0}.primitive pre{background:#0b0f16;border:1px solid #2c3440;border-radius:18px;color:#fef3c7;margin:16px 0 0;overflow:auto;padding:16px}.status.pass,.check.pass,.profile-surfaces .pass{border-color:rgba(34,197,94,.55)}.status.warn,.check.warn,.profile-surfaces .warn{border-color:rgba(245,158,11,.65)}.status.fail,.check.fail{border-color:rgba(239,68,68,.75)}.checks{display:grid;gap:14px}.check{align-items:center;background:#141922;border:1px solid #26313d;border-radius:22px;display:grid;gap:16px;grid-template-columns:1fr auto auto;padding:18px}.check span,.profile-surfaces span{color:#a8b0b8;font-size:.78rem;font-weight:900;letter-spacing:.08em}.check h2{margin:.2rem 0}.check p,.profile p{color:#b9c0c8;margin:.2rem 0 0}.check .proof-source{color:#f9d77e;font-weight:800}.check strong{font-size:1.5rem}.profile-surfaces{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));margin-top:16px}.profile-surfaces article{background:#141922;border:1px solid #26313d;border-radius:16px;padding:14px}.profile-surfaces strong{display:block;margin-top:6px}.actions{display:flex;flex-wrap:wrap;gap:10px}.check a,a{color:#fbbf24}button{background:#fbbf24;border:0;border-radius:999px;color:#111827;cursor:pointer;font-weight:800;padding:9px 12px}button:disabled{cursor:wait;opacity:.65}@media(max-width:760px){main{padding:20px}.check{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Self-hosted readiness</p><h1>${escapeHtml38(title)}</h1><p>One deployable pass/fail report for quality gates, provider failover, session health, handoffs, routing evidence, and optional carrier readiness.</p><p class="status ${escapeHtml38(report.status)}">Overall: ${escapeHtml38(report.status.toUpperCase())}</p><p>Checked ${escapeHtml38(new Date(report.checkedAt).toLocaleString())}</p></section>${profile}<section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceProductionReadinessRoutes(...)</code> builds this deploy gate</h2><p>Mount one package primitive to expose JSON readiness, HTML readiness, and a machine-readable gate route. Feed it the proof stores and contract reports your app already owns.</p><pre><code>${snippet}</code></pre></section><section class="checks">${checks}</section></main><script>document.querySelectorAll("[data-readiness-action]").forEach((button)=>{button.addEventListener("click",async()=>{const url=button.getAttribute("data-action-url");if(!url)return;button.disabled=true;const original=button.textContent;button.textContent="Running...";try{const response=await fetch(url,{method:"POST"});button.textContent=response.ok?"Done. Reloading...":"Failed";if(response.ok)setTimeout(()=>location.reload(),500)}catch{button.textContent="Failed"}finally{setTimeout(()=>{button.disabled=false;button.textContent=original},1500)}})});</script></body></html>`;
23175
23520
  };
23176
23521
  var createVoiceProductionReadinessRoutes = (options) => {
23177
23522
  const path = options.path ?? "/api/production-readiness";
23178
23523
  const gatePath = options.gatePath === undefined ? "/api/production-readiness/gate" : options.gatePath;
23179
23524
  const htmlPath = options.htmlPath ?? "/production-readiness";
23180
- const routes = new Elysia36({
23525
+ const routes = new Elysia37({
23181
23526
  name: options.name ?? "absolutejs-voice-production-readiness"
23182
23527
  });
23183
23528
  routes.get(path, async ({ query, request }) => buildVoiceProductionReadinessReport(options, { query, request }));
@@ -23541,8 +23886,8 @@ var recommendVoiceReadinessProfile = (options) => {
23541
23886
  };
23542
23887
  };
23543
23888
  // src/providerStackRecommendations.ts
23544
- import { Elysia as Elysia37 } from "elysia";
23545
- var escapeHtml38 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
23889
+ import { Elysia as Elysia38 } from "elysia";
23890
+ var escapeHtml39 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
23546
23891
  var profileProviderPriorities = {
23547
23892
  "meeting-recorder": {
23548
23893
  llm: ["openai", "anthropic", "gemini"],
@@ -23785,17 +24130,17 @@ var resolveProviderContractMatrixInput = async (matrix) => typeof matrix === "fu
23785
24130
  var renderVoiceProviderContractMatrixHTML = (report, options = {}) => {
23786
24131
  const title = options.title ?? "Voice Provider Contract Matrix";
23787
24132
  const rows = report.rows.map((row) => {
23788
- const checks = row.checks.map((check) => `<li class="${escapeHtml38(check.status)}"><strong>${escapeHtml38(check.label)}</strong><span>${escapeHtml38(check.detail ?? check.status)}</span>${check.remediation ? `<em>${check.remediation.href ? `<a href="${escapeHtml38(check.remediation.href)}">${escapeHtml38(check.remediation.label)}</a>` : escapeHtml38(check.remediation.label)}: ${escapeHtml38(check.remediation.detail)}</em>` : ""}</li>`).join("");
23789
- return `<article class="row ${escapeHtml38(row.status)}">
24133
+ const checks = row.checks.map((check) => `<li class="${escapeHtml39(check.status)}"><strong>${escapeHtml39(check.label)}</strong><span>${escapeHtml39(check.detail ?? check.status)}</span>${check.remediation ? `<em>${check.remediation.href ? `<a href="${escapeHtml39(check.remediation.href)}">${escapeHtml39(check.remediation.label)}</a>` : escapeHtml39(check.remediation.label)}: ${escapeHtml39(check.remediation.detail)}</em>` : ""}</li>`).join("");
24134
+ return `<article class="row ${escapeHtml39(row.status)}">
23790
24135
  <div>
23791
- <p class="eyebrow">${escapeHtml38(row.kind)}${row.selected ? " \xB7 selected" : ""}</p>
23792
- <h2>${escapeHtml38(row.provider)}</h2>
23793
- <p class="status ${escapeHtml38(row.status)}">${escapeHtml38(row.status.toUpperCase())}</p>
24136
+ <p class="eyebrow">${escapeHtml39(row.kind)}${row.selected ? " \xB7 selected" : ""}</p>
24137
+ <h2>${escapeHtml39(row.provider)}</h2>
24138
+ <p class="status ${escapeHtml39(row.status)}">${escapeHtml39(row.status.toUpperCase())}</p>
23794
24139
  </div>
23795
24140
  <ul>${checks}</ul>
23796
24141
  </article>`;
23797
24142
  }).join("");
23798
- const snippet = escapeHtml38(`const providerContracts = () =>
24143
+ const snippet = escapeHtml39(`const providerContracts = () =>
23799
24144
  createVoiceProviderContractMatrixPreset('phone-agent', {
23800
24145
  env: process.env,
23801
24146
  providers: {
@@ -23816,7 +24161,7 @@ createVoiceProductionReadinessRoutes({
23816
24161
  providerContractMatrix: () =>
23817
24162
  buildVoiceProviderContractMatrix(providerContracts())
23818
24163
  });`);
23819
- 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:#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>${escapeHtml38(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>`;
24164
+ 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:#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>${escapeHtml39(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>`;
23820
24165
  };
23821
24166
  var createVoiceProviderContractMatrixJSONHandler = (matrix) => async () => buildVoiceProviderContractMatrix(await resolveProviderContractMatrixInput(matrix));
23822
24167
  var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
@@ -23831,7 +24176,7 @@ var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
23831
24176
  var createVoiceProviderContractMatrixRoutes = (options) => {
23832
24177
  const path = options.path ?? "/api/provider-contracts";
23833
24178
  const htmlPath = options.htmlPath ?? "/provider-contracts";
23834
- const routes = new Elysia37({
24179
+ const routes = new Elysia38({
23835
24180
  name: options.name ?? "absolutejs-voice-provider-contract-matrix"
23836
24181
  });
23837
24182
  const jsonHandler = createVoiceProviderContractMatrixJSONHandler(options.matrix);
@@ -23890,7 +24235,7 @@ var evaluateVoiceProviderStackGaps = (input) => {
23890
24235
  };
23891
24236
  };
23892
24237
  // src/opsConsoleRoutes.ts
23893
- import { Elysia as Elysia38 } from "elysia";
24238
+ import { Elysia as Elysia39 } from "elysia";
23894
24239
  var DEFAULT_LINKS = [
23895
24240
  {
23896
24241
  description: "Quality gates for CI, deploy checks, and production readiness.",
@@ -23925,7 +24270,7 @@ var DEFAULT_LINKS = [
23925
24270
  label: "Handoffs"
23926
24271
  }
23927
24272
  ];
23928
- var escapeHtml39 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
24273
+ var escapeHtml40 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
23929
24274
  var countProviderStatuses = (providers) => {
23930
24275
  const degradedStatuses = new Set(["degraded", "rate-limited", "suppressed"]);
23931
24276
  const healthy = providers.filter((provider) => provider.status === "healthy").length;
@@ -23994,20 +24339,20 @@ var buildVoiceOpsConsoleReport = async (options) => {
23994
24339
  trace
23995
24340
  };
23996
24341
  };
23997
- var renderMetricCard = (input) => `<article class="metric"><span>${escapeHtml39(input.label)}</span><strong>${escapeHtml39(String(input.value))}</strong>${input.status ? `<p class="${escapeHtml39(input.status)}">${escapeHtml39(input.status)}</p>` : ""}${input.href ? `<a href="${escapeHtml39(input.href)}">Open</a>` : ""}</article>`;
24342
+ var renderMetricCard = (input) => `<article class="metric"><span>${escapeHtml40(input.label)}</span><strong>${escapeHtml40(String(input.value))}</strong>${input.status ? `<p class="${escapeHtml40(input.status)}">${escapeHtml40(input.status)}</p>` : ""}${input.href ? `<a href="${escapeHtml40(input.href)}">Open</a>` : ""}</article>`;
23998
24343
  var renderVoiceOpsConsoleHTML = (report, options = {}) => {
23999
24344
  const links = report.links.map((link) => `<article class="surface">
24000
- <div><h2>${escapeHtml39(link.label)}</h2>${link.description ? `<p>${escapeHtml39(link.description)}</p>` : ""}</div>
24001
- <p><a href="${escapeHtml39(link.href)}">Open ${escapeHtml39(link.label)}</a>${link.statusHref ? ` \xB7 <a href="${escapeHtml39(link.statusHref)}">Status</a>` : ""}</p>
24345
+ <div><h2>${escapeHtml40(link.label)}</h2>${link.description ? `<p>${escapeHtml40(link.description)}</p>` : ""}</div>
24346
+ <p><a href="${escapeHtml40(link.href)}">Open ${escapeHtml40(link.label)}</a>${link.statusHref ? ` \xB7 <a href="${escapeHtml40(link.statusHref)}">Status</a>` : ""}</p>
24002
24347
  </article>`).join("");
24003
- const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${escapeHtml39(session.sessionId)}</td><td>${escapeHtml39(session.status)}</td><td>${session.turnCount}</td><td>${session.errorCount}</td><td>${session.replayHref ? `<a href="${escapeHtml39(session.replayHref)}">Replay</a>` : ""}</td></tr>`).join("") : '<tr><td colspan="5">No sessions yet.</td></tr>';
24004
- const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${escapeHtml39(event.kind)}</td><td>${escapeHtml39(event.provider ?? "unknown")}</td><td>${escapeHtml39(event.status ?? "unknown")}</td><td>${event.elapsedMs ?? 0}ms</td><td>${escapeHtml39(event.sessionId)}</td></tr>`).join("") : '<tr><td colspan="5">No provider routing events yet.</td></tr>';
24348
+ const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${escapeHtml40(session.sessionId)}</td><td>${escapeHtml40(session.status)}</td><td>${session.turnCount}</td><td>${session.errorCount}</td><td>${session.replayHref ? `<a href="${escapeHtml40(session.replayHref)}">Replay</a>` : ""}</td></tr>`).join("") : '<tr><td colspan="5">No sessions yet.</td></tr>';
24349
+ const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${escapeHtml40(event.kind)}</td><td>${escapeHtml40(event.provider ?? "unknown")}</td><td>${escapeHtml40(event.status ?? "unknown")}</td><td>${event.elapsedMs ?? 0}ms</td><td>${escapeHtml40(event.sessionId)}</td></tr>`).join("") : '<tr><td colspan="5">No provider routing events yet.</td></tr>';
24005
24350
  const title = options.title ?? "AbsoluteJS Voice Ops Console";
24006
- 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{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>${escapeHtml39(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 ${escapeHtml39(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>`;
24351
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml40(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>${escapeHtml40(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 ${escapeHtml40(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>`;
24007
24352
  };
24008
24353
  var createVoiceOpsConsoleRoutes = (options) => {
24009
24354
  const path = options.path ?? "/ops-console";
24010
- const routes = new Elysia38({
24355
+ const routes = new Elysia39({
24011
24356
  name: options.name ?? "absolutejs-voice-ops-console"
24012
24357
  });
24013
24358
  const getReport = () => buildVoiceOpsConsoleReport(options);
@@ -24024,11 +24369,11 @@ var createVoiceOpsConsoleRoutes = (options) => {
24024
24369
  return routes;
24025
24370
  };
24026
24371
  // src/operationsRecord.ts
24027
- import { Elysia as Elysia40 } from "elysia";
24372
+ import { Elysia as Elysia41 } from "elysia";
24028
24373
 
24029
24374
  // src/traceTimeline.ts
24030
- import { Elysia as Elysia39 } from "elysia";
24031
- var escapeHtml40 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
24375
+ import { Elysia as Elysia40 } from "elysia";
24376
+ var escapeHtml41 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
24032
24377
  var getString16 = (value) => typeof value === "string" && value.trim() ? value : undefined;
24033
24378
  var getNumber9 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
24034
24379
  var firstString3 = (payload, keys) => {
@@ -24211,17 +24556,17 @@ var summarizeVoiceTraceTimeline = (events, options = {}) => {
24211
24556
  };
24212
24557
  };
24213
24558
  var formatMs3 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
24214
- 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>`;
24559
+ 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>${escapeHtml41(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>`;
24215
24560
  var renderVoiceTraceTimelineSessionHTML = (session, options = {}) => {
24216
- 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("");
24217
- 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>";
24218
- const supportLinks = session.operationsRecordHref ? `<p><a href="${escapeHtml40(session.operationsRecordHref)}">Open operations record</a></p>` : "";
24219
- 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>${supportLinks}</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>`;
24561
+ const events = session.events.map((event) => `<tr class="${escapeHtml41(event.status ?? "")}"><td>+${String(event.offsetMs)}ms</td><td>${escapeHtml41(event.type)}</td><td>${escapeHtml41(event.label)}</td><td>${escapeHtml41(event.provider ?? "")}</td><td>${escapeHtml41(event.status ?? "")}</td><td>${formatMs3(event.elapsedMs)}</td></tr>`).join("");
24562
+ const issues = session.evaluation.issues.length ? session.evaluation.issues.map((issue) => `<li class="${escapeHtml41(issue.severity)}">${escapeHtml41(issue.code)}: ${escapeHtml41(issue.message)}</li>`).join("") : "<li>none</li>";
24563
+ const supportLinks = session.operationsRecordHref ? `<p><a href="${escapeHtml41(session.operationsRecordHref)}">Open operations record</a></p>` : "";
24564
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml41(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>${escapeHtml41(session.sessionId)}</h1><p class="status ${escapeHtml41(session.status)}">${escapeHtml41(session.status)}</p>${supportLinks}</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>`;
24220
24565
  };
24221
- 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>${session.operationsRecordHref ? `<a href="${escapeHtml40(session.operationsRecordHref)}">${escapeHtml40(session.sessionId)}</a>` : `<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("");
24566
+ var renderSessionRows = (report) => report.sessions.length === 0 ? '<tr><td colspan="7">No trace events recorded yet.</td></tr>' : report.sessions.map((session) => `<tr class="${escapeHtml41(session.status)}"><td>${session.operationsRecordHref ? `<a href="${escapeHtml41(session.operationsRecordHref)}">${escapeHtml41(session.sessionId)}</a>` : `<a href="/traces/${encodeURIComponent(session.sessionId)}">${escapeHtml41(session.sessionId)}</a>`}</td><td>${escapeHtml41(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) => escapeHtml41(provider.provider)).join(", ")}</td></tr>`).join("");
24222
24567
  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}}";
24223
24568
  var renderVoiceTraceTimelineHTML = (report, options = {}) => {
24224
- const snippet = escapeHtml40(`const traceStore = createVoiceTraceSinkStore({
24569
+ const snippet = escapeHtml41(`const traceStore = createVoiceTraceSinkStore({
24225
24570
  store: runtimeStorage.traces,
24226
24571
  sinks: [
24227
24572
  createVoiceTraceHTTPSink({
@@ -24247,13 +24592,13 @@ app.use(
24247
24592
  traceDeliveries: runtimeStorage.traceDeliveries
24248
24593
  })
24249
24594
  );`);
24250
- 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 Timelines")}</title><style>${timelineCSS}.primitive{background:#181f27;border:1px solid #334155;border-radius:20px;margin:20px 0;padding:18px}.primitive p{line-height:1.55}.primitive pre{background:#0b1118;border:1px solid #2b3642;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}.primitive code{color:#bfdbfe}</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><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceTraceTimelineRoutes(...)</code> makes traces the proof backbone</h2><p class="muted">Mount trace timelines from the same trace store used by readiness, simulations, provider recovery, delivery sinks, and phone-agent smoke proof.</p><pre><code>${snippet}</code></pre></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>`;
24595
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml41(options.title ?? "Voice Trace Timelines")}</title><style>${timelineCSS}.primitive{background:#181f27;border:1px solid #334155;border-radius:20px;margin:20px 0;padding:18px}.primitive p{line-height:1.55}.primitive pre{background:#0b1118;border:1px solid #2b3642;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}.primitive code{color:#bfdbfe}</style></head><body><main><header><p class="eyebrow">Self-hosted voice debugging</p><h1>${escapeHtml41(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><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceTraceTimelineRoutes(...)</code> makes traces the proof backbone</h2><p class="muted">Mount trace timelines from the same trace store used by readiness, simulations, provider recovery, delivery sinks, and phone-agent smoke proof.</p><pre><code>${snippet}</code></pre></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>`;
24251
24596
  };
24252
24597
  var createVoiceTraceTimelineRoutes = (options) => {
24253
24598
  const path = options.path ?? "/api/voice-traces";
24254
24599
  const htmlPath = options.htmlPath ?? "/traces";
24255
24600
  const title = options.title ?? "AbsoluteJS Voice Trace Timelines";
24256
- const routes = new Elysia39({
24601
+ const routes = new Elysia40({
24257
24602
  name: options.name ?? "absolutejs-voice-trace-timelines"
24258
24603
  });
24259
24604
  const buildReport = async () => summarizeVoiceTraceTimeline(await options.store.list(), {
@@ -24440,7 +24785,7 @@ var buildVoiceOperationsRecord = async (options) => {
24440
24785
  transcript: buildTranscript(replay)
24441
24786
  };
24442
24787
  };
24443
- var escapeHtml41 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
24788
+ var escapeHtml42 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
24444
24789
  var formatMs4 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
24445
24790
  var outcomeLabels = (outcome) => [
24446
24791
  outcome.complete ? "complete" : undefined,
@@ -24473,15 +24818,15 @@ var renderVoiceOperationsRecordIncidentMarkdown = (record) => {
24473
24818
  `);
24474
24819
  };
24475
24820
  var renderVoiceOperationsRecordHTML = (record, options = {}) => {
24476
- const providers = record.providers.length ? record.providers.map((provider) => `<article><strong>${escapeHtml41(provider.provider)}</strong><span>${String(provider.eventCount)} events</span><span>${formatMs4(provider.averageElapsedMs)} avg</span><span>${String(provider.errorCount)} errors</span></article>`).join("") : '<p class="muted">No provider events recorded.</p>';
24477
- const transcript = record.transcript.length ? record.transcript.map((turn) => `<li><strong>${escapeHtml41(turn.id)}</strong>${turn.committedText ? `<p><span class="label">Caller</span>${escapeHtml41(turn.committedText)}</p>` : ""}${turn.assistantReplies.map((reply) => `<p><span class="label">Assistant</span>${escapeHtml41(reply)}</p>`).join("")}${turn.errors.map((error) => `<p class="error"><span class="label">Error</span>${escapeHtml41(error)}</p>`).join("")}</li>`).join("") : "<li>No transcript turns recorded.</li>";
24478
- const providerDecisions = record.providerDecisions.length ? record.providerDecisions.map((decision) => `<li><strong>${escapeHtml41(decision.provider ?? decision.selectedProvider ?? decision.fallbackProvider ?? "provider")}</strong> <span>${escapeHtml41(decision.status ?? decision.type)}</span> ${formatMs4(decision.elapsedMs)}${decision.fallbackProvider ? `<p>Fallback: ${escapeHtml41(decision.fallbackProvider)}</p>` : ""}${decision.error ? `<p class="error">${escapeHtml41(decision.error)}</p>` : ""}${decision.reason ? `<p>${escapeHtml41(decision.reason)}</p>` : ""}</li>`).join("") : "<li>No provider decisions recorded.</li>";
24479
- const handoffs = record.handoffs.length ? record.handoffs.map((handoff) => `<li><strong>${escapeHtml41(handoff.fromAgentId ?? "unknown")}</strong> to <strong>${escapeHtml41(handoff.targetAgentId ?? "unknown")}</strong> <span>${escapeHtml41(handoff.status ?? "")}</span><p>${escapeHtml41(handoff.summary ?? handoff.reason ?? "")}</p></li>`).join("") : "<li>No agent handoffs recorded.</li>";
24480
- const tools = record.tools.length ? record.tools.map((tool) => `<li><strong>${escapeHtml41(tool.toolName ?? "tool")}</strong> <span>${escapeHtml41(tool.status ?? "")}</span> ${formatMs4(tool.elapsedMs)} ${tool.error ? `<p>${escapeHtml41(tool.error)}</p>` : ""}</li>`).join("") : "<li>No tool calls recorded.</li>";
24481
- const reviews = record.reviews?.reviews.length ? record.reviews.reviews.map((review) => `<li><strong>${escapeHtml41(review.title)}</strong> <span>${escapeHtml41(review.summary.outcome ?? "")}</span><p>${escapeHtml41(review.postCall?.summary ?? review.transcript.actual)}</p></li>`).join("") : "<li>No call reviews recorded.</li>";
24482
- const tasks = record.tasks?.tasks.length ? record.tasks.tasks.map((task) => `<li><strong>${escapeHtml41(task.title)}</strong> <span>${escapeHtml41(task.status)}</span><p>${escapeHtml41(task.recommendedAction)}</p></li>`).join("") : "<li>No ops tasks recorded.</li>";
24483
- const integrationEvents = record.integrationEvents?.events.length ? record.integrationEvents.events.map((event) => `<li><strong>${escapeHtml41(event.type)}</strong> <span>${escapeHtml41(event.deliveryStatus ?? "local")}</span><p>${escapeHtml41(event.deliveryError ?? event.deliveredTo ?? "")}</p></li>`).join("") : "<li>No integration events recorded.</li>";
24484
- const snippet = escapeHtml41(`app.use(
24821
+ const providers = record.providers.length ? record.providers.map((provider) => `<article><strong>${escapeHtml42(provider.provider)}</strong><span>${String(provider.eventCount)} events</span><span>${formatMs4(provider.averageElapsedMs)} avg</span><span>${String(provider.errorCount)} errors</span></article>`).join("") : '<p class="muted">No provider events recorded.</p>';
24822
+ const transcript = record.transcript.length ? record.transcript.map((turn) => `<li><strong>${escapeHtml42(turn.id)}</strong>${turn.committedText ? `<p><span class="label">Caller</span>${escapeHtml42(turn.committedText)}</p>` : ""}${turn.assistantReplies.map((reply) => `<p><span class="label">Assistant</span>${escapeHtml42(reply)}</p>`).join("")}${turn.errors.map((error) => `<p class="error"><span class="label">Error</span>${escapeHtml42(error)}</p>`).join("")}</li>`).join("") : "<li>No transcript turns recorded.</li>";
24823
+ const providerDecisions = record.providerDecisions.length ? record.providerDecisions.map((decision) => `<li><strong>${escapeHtml42(decision.provider ?? decision.selectedProvider ?? decision.fallbackProvider ?? "provider")}</strong> <span>${escapeHtml42(decision.status ?? decision.type)}</span> ${formatMs4(decision.elapsedMs)}${decision.fallbackProvider ? `<p>Fallback: ${escapeHtml42(decision.fallbackProvider)}</p>` : ""}${decision.error ? `<p class="error">${escapeHtml42(decision.error)}</p>` : ""}${decision.reason ? `<p>${escapeHtml42(decision.reason)}</p>` : ""}</li>`).join("") : "<li>No provider decisions recorded.</li>";
24824
+ const handoffs = record.handoffs.length ? record.handoffs.map((handoff) => `<li><strong>${escapeHtml42(handoff.fromAgentId ?? "unknown")}</strong> to <strong>${escapeHtml42(handoff.targetAgentId ?? "unknown")}</strong> <span>${escapeHtml42(handoff.status ?? "")}</span><p>${escapeHtml42(handoff.summary ?? handoff.reason ?? "")}</p></li>`).join("") : "<li>No agent handoffs recorded.</li>";
24825
+ const tools = record.tools.length ? record.tools.map((tool) => `<li><strong>${escapeHtml42(tool.toolName ?? "tool")}</strong> <span>${escapeHtml42(tool.status ?? "")}</span> ${formatMs4(tool.elapsedMs)} ${tool.error ? `<p>${escapeHtml42(tool.error)}</p>` : ""}</li>`).join("") : "<li>No tool calls recorded.</li>";
24826
+ const reviews = record.reviews?.reviews.length ? record.reviews.reviews.map((review) => `<li><strong>${escapeHtml42(review.title)}</strong> <span>${escapeHtml42(review.summary.outcome ?? "")}</span><p>${escapeHtml42(review.postCall?.summary ?? review.transcript.actual)}</p></li>`).join("") : "<li>No call reviews recorded.</li>";
24827
+ const tasks = record.tasks?.tasks.length ? record.tasks.tasks.map((task) => `<li><strong>${escapeHtml42(task.title)}</strong> <span>${escapeHtml42(task.status)}</span><p>${escapeHtml42(task.recommendedAction)}</p></li>`).join("") : "<li>No ops tasks recorded.</li>";
24828
+ const integrationEvents = record.integrationEvents?.events.length ? record.integrationEvents.events.map((event) => `<li><strong>${escapeHtml42(event.type)}</strong> <span>${escapeHtml42(event.deliveryStatus ?? "local")}</span><p>${escapeHtml42(event.deliveryError ?? event.deliveredTo ?? "")}</p></li>`).join("") : "<li>No integration events recorded.</li>";
24829
+ const snippet = escapeHtml42(`app.use(
24485
24830
  createVoiceOperationsRecordRoutes({
24486
24831
  audit: auditStore,
24487
24832
  integrationEvents: opsEvents,
@@ -24495,16 +24840,16 @@ var renderVoiceOperationsRecordHTML = (record, options = {}) => {
24495
24840
  tasks: opsTasks
24496
24841
  })
24497
24842
  );`);
24498
- const incidentMarkdown = escapeHtml41(renderVoiceOperationsRecordIncidentMarkdown(record));
24499
- const incidentLink = options.incidentHref ? `<a href="${escapeHtml41(options.incidentHref)}">Download incident.md</a>` : "";
24500
- return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml41(options.title ?? "Voice Operations Record")}</title><style>body{background:#101417;color:#f9f4e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.eyebrow{color:#fbbf24;font-size:.8rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.8rem);line-height:.9;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}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.card,.primitive{background:#182025;border:1px solid #2d3a43;border-radius:20px;padding:16px}.card span,.muted,.label{color:#a9b4bd}.label{display:block;font-size:.72rem;font-weight:900;letter-spacing:.12em;text-transform:uppercase}.card strong{display:block;font-size:2rem}section{margin-top:28px}article{display:grid;gap:8px}ul{display:grid;gap:10px;list-style:none;padding:0}li{background:#182025;border:1px solid #2d3a43;border-radius:16px;padding:14px}pre{background:#080d10;border:1px solid #2d3a43;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}.hero-actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:16px}.hero-actions a{background:#fbbf24;border-radius:999px;color:#111827;font-weight:900;padding:10px 14px;text-decoration:none}.two-column{display:grid;gap:18px;grid-template-columns:minmax(0,1.15fr) minmax(280px,.85fr)}@media(max-width:860px){main{padding:20px}.two-column{grid-template-columns:1fr}}</style></head><body><main><p class="eyebrow">Call log replacement</p><h1>${escapeHtml41(options.title ?? "Voice Operations Record")}</h1><p class="status ${escapeHtml41(record.status)}">${escapeHtml41(record.status)}</p><div class="hero-actions"><a href="#transcript">Transcript</a><a href="#provider-decisions">Provider decisions</a><a href="#incident-handoff">Incident handoff</a>${incidentLink}</div><section class="grid"><div class="card"><span>Events</span><strong>${String(record.summary.eventCount)}</strong></div><div class="card"><span>Turns</span><strong>${String(record.summary.turnCount)}</strong></div><div class="card"><span>Errors</span><strong>${String(record.summary.errorCount)}</strong></div><div class="card"><span>Duration</span><strong>${formatMs4(record.summary.callDurationMs)}</strong></div><div class="card"><span>Audit</span><strong>${String(record.audit?.total ?? 0)}</strong></div><div class="card"><span>Reviews</span><strong>${String(record.reviews?.total ?? 0)}</strong></div><div class="card"><span>Tasks</span><strong>${String(record.tasks?.total ?? 0)}</strong></div><div class="card"><span>Integrations</span><strong>${String(record.integrationEvents?.total ?? 0)}</strong></div></section><section class="two-column"><div><h2 id="transcript">Transcript</h2><ul>${transcript}</ul></div><div><h2 id="provider-decisions">Provider Decisions</h2><ul>${providerDecisions}</ul></div></section><section id="incident-handoff"><h2>Copyable Incident Handoff</h2><p class="muted">Paste this into Slack, Linear, Zendesk, or an incident review. ${incidentLink}</p><pre><code>${incidentMarkdown}</code></pre></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceOperationsRecordRoutes(...)</code> gives every call one debuggable object</h2><p class="muted">Use this as the support/debug payload across traces, provider routing, tools, handoffs, audit, latency, replay, reviews, tasks, and webhook delivery.</p><pre><code>${snippet}</code></pre></section><section><h2>Provider Summary</h2><div class="grid">${providers}</div></section><section><h2>Handoffs</h2><ul>${handoffs}</ul></section><section><h2>Tools</h2><ul>${tools}</ul></section><section><h2>Reviews</h2><ul>${reviews}</ul></section><section><h2>Tasks</h2><ul>${tasks}</ul></section><section><h2>Integration Events</h2><ul>${integrationEvents}</ul></section></main></body></html>`;
24843
+ const incidentMarkdown = escapeHtml42(renderVoiceOperationsRecordIncidentMarkdown(record));
24844
+ const incidentLink = options.incidentHref ? `<a href="${escapeHtml42(options.incidentHref)}">Download incident.md</a>` : "";
24845
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml42(options.title ?? "Voice Operations Record")}</title><style>body{background:#101417;color:#f9f4e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.eyebrow{color:#fbbf24;font-size:.8rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.8rem);line-height:.9;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}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.card,.primitive{background:#182025;border:1px solid #2d3a43;border-radius:20px;padding:16px}.card span,.muted,.label{color:#a9b4bd}.label{display:block;font-size:.72rem;font-weight:900;letter-spacing:.12em;text-transform:uppercase}.card strong{display:block;font-size:2rem}section{margin-top:28px}article{display:grid;gap:8px}ul{display:grid;gap:10px;list-style:none;padding:0}li{background:#182025;border:1px solid #2d3a43;border-radius:16px;padding:14px}pre{background:#080d10;border:1px solid #2d3a43;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}.hero-actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:16px}.hero-actions a{background:#fbbf24;border-radius:999px;color:#111827;font-weight:900;padding:10px 14px;text-decoration:none}.two-column{display:grid;gap:18px;grid-template-columns:minmax(0,1.15fr) minmax(280px,.85fr)}@media(max-width:860px){main{padding:20px}.two-column{grid-template-columns:1fr}}</style></head><body><main><p class="eyebrow">Call log replacement</p><h1>${escapeHtml42(options.title ?? "Voice Operations Record")}</h1><p class="status ${escapeHtml42(record.status)}">${escapeHtml42(record.status)}</p><div class="hero-actions"><a href="#transcript">Transcript</a><a href="#provider-decisions">Provider decisions</a><a href="#incident-handoff">Incident handoff</a>${incidentLink}</div><section class="grid"><div class="card"><span>Events</span><strong>${String(record.summary.eventCount)}</strong></div><div class="card"><span>Turns</span><strong>${String(record.summary.turnCount)}</strong></div><div class="card"><span>Errors</span><strong>${String(record.summary.errorCount)}</strong></div><div class="card"><span>Duration</span><strong>${formatMs4(record.summary.callDurationMs)}</strong></div><div class="card"><span>Audit</span><strong>${String(record.audit?.total ?? 0)}</strong></div><div class="card"><span>Reviews</span><strong>${String(record.reviews?.total ?? 0)}</strong></div><div class="card"><span>Tasks</span><strong>${String(record.tasks?.total ?? 0)}</strong></div><div class="card"><span>Integrations</span><strong>${String(record.integrationEvents?.total ?? 0)}</strong></div></section><section class="two-column"><div><h2 id="transcript">Transcript</h2><ul>${transcript}</ul></div><div><h2 id="provider-decisions">Provider Decisions</h2><ul>${providerDecisions}</ul></div></section><section id="incident-handoff"><h2>Copyable Incident Handoff</h2><p class="muted">Paste this into Slack, Linear, Zendesk, or an incident review. ${incidentLink}</p><pre><code>${incidentMarkdown}</code></pre></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceOperationsRecordRoutes(...)</code> gives every call one debuggable object</h2><p class="muted">Use this as the support/debug payload across traces, provider routing, tools, handoffs, audit, latency, replay, reviews, tasks, and webhook delivery.</p><pre><code>${snippet}</code></pre></section><section><h2>Provider Summary</h2><div class="grid">${providers}</div></section><section><h2>Handoffs</h2><ul>${handoffs}</ul></section><section><h2>Tools</h2><ul>${tools}</ul></section><section><h2>Reviews</h2><ul>${reviews}</ul></section><section><h2>Tasks</h2><ul>${tasks}</ul></section><section><h2>Integration Events</h2><ul>${integrationEvents}</ul></section></main></body></html>`;
24501
24846
  };
24502
24847
  var createVoiceOperationsRecordRoutes = (options) => {
24503
24848
  const path = options.path ?? "/api/voice-operations/:sessionId";
24504
24849
  const htmlPath = options.htmlPath === undefined ? "/voice-operations/:sessionId" : options.htmlPath;
24505
24850
  const incidentPath = options.incidentPath === undefined ? `${path}/incident.md` : options.incidentPath;
24506
24851
  const incidentHtmlPath = options.incidentHtmlPath === undefined && htmlPath ? `${htmlPath}/incident.md` : options.incidentHtmlPath;
24507
- const routes = new Elysia40({
24852
+ const routes = new Elysia41({
24508
24853
  name: options.name ?? "absolutejs-voice-operations-record"
24509
24854
  });
24510
24855
  const buildRecord = (sessionId) => buildVoiceOperationsRecord({
@@ -24556,7 +24901,7 @@ var createVoiceOperationsRecordRoutes = (options) => {
24556
24901
  return routes;
24557
24902
  };
24558
24903
  // src/incidentBundle.ts
24559
- import { Elysia as Elysia41 } from "elysia";
24904
+ import { Elysia as Elysia42 } from "elysia";
24560
24905
  var filterIncidentBundleArtifacts = (artifacts, filter = {}) => artifacts.filter((artifact) => {
24561
24906
  if (filter.sessionId && artifact.sessionId !== filter.sessionId) {
24562
24907
  return false;
@@ -24755,7 +25100,7 @@ var buildVoiceIncidentBundle = async (options) => {
24755
25100
  var createVoiceIncidentBundleRoutes = (options) => {
24756
25101
  const path = options.path ?? "/api/voice-incidents/:sessionId";
24757
25102
  const markdownPath = options.markdownPath === undefined ? "/voice-incidents/:sessionId/markdown" : options.markdownPath;
24758
- const routes = new Elysia41({
25103
+ const routes = new Elysia42({
24759
25104
  name: options.name ?? "absolutejs-voice-incident-bundle"
24760
25105
  });
24761
25106
  const getSessionId = (params) => params.sessionId ?? "";
@@ -24956,19 +25301,19 @@ var summarizeVoiceOpsStatus = async (options) => {
24956
25301
  };
24957
25302
  };
24958
25303
  // src/opsStatusRoutes.ts
24959
- import { Elysia as Elysia42 } from "elysia";
24960
- var escapeHtml42 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
25304
+ import { Elysia as Elysia43 } from "elysia";
25305
+ var escapeHtml43 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
24961
25306
  var renderVoiceOpsStatusHTML = (report, options = {}) => {
24962
25307
  const title = options.title ?? "AbsoluteJS Voice Ops Status";
24963
25308
  const surfaces = Object.entries(report.surfaces).map(([key, surface]) => {
24964
25309
  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;
24965
- return `<article class="surface ${escapeHtml42(surface.status)}"><span>${escapeHtml42(surface.status.toUpperCase())}</span><h2>${escapeHtml42(key)}</h2><strong>${escapeHtml42(value)}</strong></article>`;
25310
+ return `<article class="surface ${escapeHtml43(surface.status)}"><span>${escapeHtml43(surface.status.toUpperCase())}</span><h2>${escapeHtml43(key)}</h2><strong>${escapeHtml43(value)}</strong></article>`;
24966
25311
  }).join("");
24967
- 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:#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>${escapeHtml42(title)}</h1><p>Compact pass/fail status for framework widgets, demos, and small customer-facing health badges.</p><p class="status ${escapeHtml42(report.status)}">Overall: ${escapeHtml42(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>`;
25312
+ 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{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>${escapeHtml43(title)}</h1><p>Compact pass/fail status for framework widgets, demos, and small customer-facing health badges.</p><p class="status ${escapeHtml43(report.status)}">Overall: ${escapeHtml43(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>`;
24968
25313
  };
24969
25314
  var createVoiceOpsStatusRoutes = (options) => {
24970
25315
  const path = options.path ?? "/api/voice/ops-status";
24971
- const routes = new Elysia42({
25316
+ const routes = new Elysia43({
24972
25317
  name: options.name ?? "absolutejs-voice-ops-status"
24973
25318
  });
24974
25319
  routes.get(path, async () => summarizeVoiceOpsStatus(options));
@@ -25401,8 +25746,8 @@ var createVoiceTTSProviderRouter = (options) => {
25401
25746
  };
25402
25747
  };
25403
25748
  // src/traceDeliveryRoutes.ts
25404
- import { Elysia as Elysia43 } from "elysia";
25405
- var escapeHtml43 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
25749
+ import { Elysia as Elysia44 } from "elysia";
25750
+ var escapeHtml44 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
25406
25751
  var getString18 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
25407
25752
  var getNumber11 = (value) => {
25408
25753
  if (typeof value === "number" && Number.isFinite(value)) {
@@ -25483,14 +25828,14 @@ var renderSinkResults2 = (delivery) => {
25483
25828
  if (entries.length === 0) {
25484
25829
  return "<p>No sink delivery attempts recorded yet.</p>";
25485
25830
  }
25486
- return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${escapeHtml43(sinkId)}</strong>: ${escapeHtml43(result.status)}${result.deliveredTo ? ` to ${escapeHtml43(result.deliveredTo)}` : ""}${result.error ? ` (${escapeHtml43(result.error)})` : ""}</li>`).join("")}</ul>`;
25831
+ return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${escapeHtml44(sinkId)}</strong>: ${escapeHtml44(result.status)}${result.deliveredTo ? ` to ${escapeHtml44(result.deliveredTo)}` : ""}${result.error ? ` (${escapeHtml44(result.error)})` : ""}</li>`).join("")}</ul>`;
25487
25832
  };
25488
- var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${escapeHtml43(event.type)} <small>${escapeHtml43(event.id)}</small>${event.sessionId ? ` session=${escapeHtml43(event.sessionId)}` : ""}</li>`).join("")}</ul>`;
25833
+ var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${escapeHtml44(event.type)} <small>${escapeHtml44(event.id)}</small>${event.sessionId ? ` session=${escapeHtml44(event.sessionId)}` : ""}</li>`).join("")}</ul>`;
25489
25834
  var renderVoiceTraceDeliveryHTML = (report, options = {}) => {
25490
25835
  const title = options.title ?? "AbsoluteJS Voice Trace Deliveries";
25491
- const drainAction = options.workerPath === false ? "" : `<form method="post" action="${escapeHtml43(options.workerPath ?? "/api/voice-trace-deliveries/drain")}"><button type="submit">Drain trace deliveries</button></form>`;
25492
- const rows = report.deliveries.map((delivery) => `<article class="delivery ${escapeHtml43(delivery.deliveryStatus)}"><div class="head"><div><span>${escapeHtml43(delivery.deliveryStatus)}</span><h2>${escapeHtml43(delivery.id)}</h2><p>${escapeHtml43(new Date(delivery.createdAt).toLocaleString())}${delivery.deliveredAt ? ` \xB7 delivered ${escapeHtml43(new Date(delivery.deliveredAt).toLocaleString())}` : ""}</p></div><strong>${String(delivery.deliveryAttempts ?? 0)} attempt(s)</strong></div>${delivery.deliveryError ? `<p class="error">${escapeHtml43(delivery.deliveryError)}</p>` : ""}<h3>Sinks</h3>${renderSinkResults2(delivery)}<h3>Events</h3>${renderEventList2(delivery)}</article>`).join("");
25493
- 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{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>${escapeHtml43(title)}</h1><p>Checked ${escapeHtml43(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>`;
25836
+ const drainAction = options.workerPath === false ? "" : `<form method="post" action="${escapeHtml44(options.workerPath ?? "/api/voice-trace-deliveries/drain")}"><button type="submit">Drain trace deliveries</button></form>`;
25837
+ const rows = report.deliveries.map((delivery) => `<article class="delivery ${escapeHtml44(delivery.deliveryStatus)}"><div class="head"><div><span>${escapeHtml44(delivery.deliveryStatus)}</span><h2>${escapeHtml44(delivery.id)}</h2><p>${escapeHtml44(new Date(delivery.createdAt).toLocaleString())}${delivery.deliveredAt ? ` \xB7 delivered ${escapeHtml44(new Date(delivery.deliveredAt).toLocaleString())}` : ""}</p></div><strong>${String(delivery.deliveryAttempts ?? 0)} attempt(s)</strong></div>${delivery.deliveryError ? `<p class="error">${escapeHtml44(delivery.deliveryError)}</p>` : ""}<h3>Sinks</h3>${renderSinkResults2(delivery)}<h3>Events</h3>${renderEventList2(delivery)}</article>`).join("");
25838
+ 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:#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>${escapeHtml44(title)}</h1><p>Checked ${escapeHtml44(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>`;
25494
25839
  };
25495
25840
  var createVoiceTraceDeliveryJSONHandler = (options) => async ({ query }) => buildVoiceTraceDeliveryReport(options, resolveVoiceTraceDeliveryFilter(query, options.filter));
25496
25841
  var createVoiceTraceDeliveryHTMLHandler = (options) => async ({ query }) => {
@@ -25510,7 +25855,7 @@ var createVoiceTraceDeliveryRoutes = (options) => {
25510
25855
  const path = options.path ?? "/api/voice-trace-deliveries";
25511
25856
  const htmlPath = options.htmlPath === undefined ? "/traces/deliveries" : options.htmlPath;
25512
25857
  const workerPath = options.workerPath === undefined ? `${path}/drain` : options.workerPath;
25513
- const routes = new Elysia43({
25858
+ const routes = new Elysia44({
25514
25859
  name: options.name ?? "absolutejs-voice-trace-deliveries"
25515
25860
  }).get(path, createVoiceTraceDeliveryJSONHandler(options));
25516
25861
  if (htmlPath !== false) {
@@ -26134,7 +26479,7 @@ var createVoiceMemoryStore = () => {
26134
26479
  return { get, getOrCreate, list, remove, set };
26135
26480
  };
26136
26481
  // src/opsWebhook.ts
26137
- import { Elysia as Elysia44 } from "elysia";
26482
+ import { Elysia as Elysia45 } from "elysia";
26138
26483
  var toHex6 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
26139
26484
  var signVoiceOpsWebhookBody = async (input) => {
26140
26485
  const encoder = new TextEncoder;
@@ -26264,7 +26609,7 @@ var verifyVoiceOpsWebhookSignature = async (input) => {
26264
26609
  };
26265
26610
  var createVoiceOpsWebhookReceiverRoutes = (options = {}) => {
26266
26611
  const path = options.path ?? "/api/voice-ops/webhook";
26267
- return new Elysia44().post(path, async ({ body, request, set }) => {
26612
+ return new Elysia45().post(path, async ({ body, request, set }) => {
26268
26613
  const bodyText = typeof body === "string" ? body : JSON.stringify(body);
26269
26614
  if (options.signingSecret) {
26270
26615
  const verification = await verifyVoiceOpsWebhookSignature({
@@ -27200,6 +27545,8 @@ export {
27200
27545
  renderVoiceResilienceHTML,
27201
27546
  renderVoiceReconnectContractHTML,
27202
27547
  renderVoiceQualityHTML,
27548
+ renderVoiceProviderSloMarkdown,
27549
+ renderVoiceProviderSloHTML,
27203
27550
  renderVoiceProviderHealthHTML,
27204
27551
  renderVoiceProviderContractMatrixHTML,
27205
27552
  renderVoiceProviderCapabilityHTML,
@@ -27365,6 +27712,7 @@ export {
27365
27712
  createVoiceReconnectContractRoutes,
27366
27713
  createVoiceReadinessProfile,
27367
27714
  createVoiceQualityRoutes,
27715
+ createVoiceProviderSloRoutes,
27368
27716
  createVoiceProviderRouter,
27369
27717
  createVoiceProviderHealthRoutes,
27370
27718
  createVoiceProviderHealthJSONHandler,
@@ -27535,6 +27883,7 @@ export {
27535
27883
  claimVoiceOpsTask,
27536
27884
  buildVoiceTraceReplay,
27537
27885
  buildVoiceTraceDeliveryReport,
27886
+ buildVoiceProviderSloReport,
27538
27887
  buildVoiceProviderContractMatrix,
27539
27888
  buildVoiceProductionReadinessReport,
27540
27889
  buildVoiceProductionReadinessGate,
@@ -12,6 +12,7 @@ import type { VoiceReconnectContractReport } from './reconnectContract';
12
12
  import type { VoiceAuditEventStore, VoiceAuditEventType, VoiceAuditOutcome } from './audit';
13
13
  import { type VoiceAuditSinkDeliveryStore } from './auditSinks';
14
14
  import type { VoiceProviderContractMatrixReport, VoiceProviderStackCapabilityGapReport } from './providerStackRecommendations';
15
+ import { type VoiceProviderSloReport, type VoiceProviderSloReportOptions } from './providerSlo';
15
16
  import type { VoiceCampaignReadinessProofReport } from './campaign';
16
17
  import { type VoiceOpsRecoveryReport } from './opsRecovery';
17
18
  export type VoiceProductionReadinessStatus = 'fail' | 'pass' | 'warn';
@@ -95,6 +96,7 @@ export type VoiceProductionReadinessReport = {
95
96
  phoneAgentSmoke?: string;
96
97
  providerContracts?: string;
97
98
  providerRoutingContracts?: string;
99
+ providerSlo?: string;
98
100
  quality?: string;
99
101
  reconnectContracts?: string;
100
102
  resilience?: string;
@@ -172,6 +174,12 @@ export type VoiceProductionReadinessReport = {
172
174
  status: VoiceProductionReadinessStatus;
173
175
  total: number;
174
176
  };
177
+ providerSlo?: {
178
+ events: number;
179
+ eventsWithLatency: number;
180
+ issues: number;
181
+ status: VoiceProductionReadinessStatus;
182
+ };
175
183
  reconnectContracts?: {
176
184
  failed: number;
177
185
  passed: number;
@@ -339,6 +347,10 @@ export type VoiceProductionReadinessRoutesOptions = {
339
347
  query: Record<string, unknown>;
340
348
  request: Request;
341
349
  }) => Promise<readonly VoiceProviderRoutingContractReport[]> | readonly VoiceProviderRoutingContractReport[]);
350
+ providerSlo?: false | VoiceProviderSloReport | VoiceProviderSloReportOptions | ((input: {
351
+ query: Record<string, unknown>;
352
+ request: Request;
353
+ }) => Promise<VoiceProviderSloReport | VoiceProviderSloReportOptions> | VoiceProviderSloReport | VoiceProviderSloReportOptions);
342
354
  providerStack?: false | VoiceProviderStackCapabilityGapReport | ((input: {
343
355
  query: Record<string, unknown>;
344
356
  request: Request;
@@ -0,0 +1,112 @@
1
+ import { Elysia } from 'elysia';
2
+ import { type VoiceRoutingEvent, type VoiceRoutingEventKind } from './resilienceRoutes';
3
+ import type { StoredVoiceTraceEvent, VoiceTraceEventStore } from './trace';
4
+ export type VoiceProviderSloStatus = 'fail' | 'pass' | 'warn';
5
+ export type VoiceProviderSloThresholds = {
6
+ maxAverageElapsedMs?: number;
7
+ maxErrorRate?: number;
8
+ maxFallbackRate?: number;
9
+ maxP95ElapsedMs?: number;
10
+ maxTimeoutRate?: number;
11
+ minSamples?: number;
12
+ };
13
+ export type VoiceProviderSloThresholdConfig = Partial<Record<VoiceRoutingEventKind, VoiceProviderSloThresholds>>;
14
+ export type VoiceProviderSloMetric = {
15
+ actual: number;
16
+ label: string;
17
+ pass: boolean;
18
+ threshold: number;
19
+ unit: 'count' | 'ms' | 'rate';
20
+ };
21
+ export type VoiceProviderSloIssue = {
22
+ code: string;
23
+ detail?: string;
24
+ kind?: VoiceRoutingEventKind;
25
+ label: string;
26
+ sessionId?: string;
27
+ status: Exclude<VoiceProviderSloStatus, 'pass'>;
28
+ value?: number | string;
29
+ };
30
+ export type VoiceProviderSloKindReport = {
31
+ events: number;
32
+ eventsWithLatency: number;
33
+ fallbacks: number;
34
+ issues: VoiceProviderSloIssue[];
35
+ kind: VoiceRoutingEventKind;
36
+ metrics: Record<string, VoiceProviderSloMetric>;
37
+ providers: string[];
38
+ status: VoiceProviderSloStatus;
39
+ thresholds: Required<VoiceProviderSloThresholds>;
40
+ timeouts: number;
41
+ unresolvedErrors: number;
42
+ };
43
+ export type VoiceProviderSloSessionReport = {
44
+ errorCount: number;
45
+ fallbackCount: number;
46
+ kinds: VoiceRoutingEventKind[];
47
+ lastEventAt: number;
48
+ maxElapsedMs?: number;
49
+ sessionId: string;
50
+ status: VoiceProviderSloStatus;
51
+ timeoutCount: number;
52
+ };
53
+ export type VoiceProviderSloReport = {
54
+ checkedAt: number;
55
+ events: number;
56
+ eventsWithLatency: number;
57
+ issues: VoiceProviderSloIssue[];
58
+ kinds: Record<VoiceRoutingEventKind, VoiceProviderSloKindReport>;
59
+ sessions: VoiceProviderSloSessionReport[];
60
+ status: VoiceProviderSloStatus;
61
+ thresholds: Record<VoiceRoutingEventKind, Required<VoiceProviderSloThresholds>>;
62
+ };
63
+ export type VoiceProviderSloReportOptions = {
64
+ events?: StoredVoiceTraceEvent[] | VoiceRoutingEvent[];
65
+ maxAgeMs?: number;
66
+ now?: number;
67
+ requiredKinds?: readonly VoiceRoutingEventKind[];
68
+ store?: VoiceTraceEventStore;
69
+ thresholds?: VoiceProviderSloThresholdConfig;
70
+ };
71
+ export type VoiceProviderSloRoutesOptions = VoiceProviderSloReportOptions & {
72
+ headers?: HeadersInit;
73
+ htmlPath?: false | string;
74
+ markdownPath?: false | string;
75
+ name?: string;
76
+ path?: string;
77
+ render?: (report: VoiceProviderSloReport) => string | Promise<string>;
78
+ title?: string;
79
+ };
80
+ export declare const buildVoiceProviderSloReport: (options?: VoiceProviderSloReportOptions) => Promise<VoiceProviderSloReport>;
81
+ export declare const renderVoiceProviderSloMarkdown: (report: VoiceProviderSloReport) => string;
82
+ export declare const renderVoiceProviderSloHTML: (report: VoiceProviderSloReport, options?: {
83
+ title?: string;
84
+ }) => string;
85
+ export declare const createVoiceProviderSloRoutes: (options: VoiceProviderSloRoutesOptions) => Elysia<"", {
86
+ decorator: {};
87
+ store: {};
88
+ derive: {};
89
+ resolve: {};
90
+ }, {
91
+ typebox: {};
92
+ error: {};
93
+ }, {
94
+ schema: {};
95
+ standaloneSchema: {};
96
+ macro: {};
97
+ macroFn: {};
98
+ parser: {};
99
+ response: {};
100
+ }, {}, {
101
+ derive: {};
102
+ resolve: {};
103
+ schema: {};
104
+ standaloneSchema: {};
105
+ response: {};
106
+ }, {
107
+ derive: {};
108
+ resolve: {};
109
+ schema: {};
110
+ standaloneSchema: {};
111
+ response: {};
112
+ }>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.214",
3
+ "version": "0.0.22-beta.216",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",