@absolutejs/voice 0.0.22-beta.147 → 0.0.22-beta.148
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/deliveryRuntime.d.ts +61 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +301 -246
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11565,6 +11565,14 @@ var createVoiceDeliverySinkRoutes = (options) => {
|
|
|
11565
11565
|
return routes;
|
|
11566
11566
|
};
|
|
11567
11567
|
// src/deliveryRuntime.ts
|
|
11568
|
+
import { Elysia as Elysia12 } from "elysia";
|
|
11569
|
+
var escapeHtml15 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
11570
|
+
var renderSummaryCard = (label, summary) => {
|
|
11571
|
+
if (!summary) {
|
|
11572
|
+
return `<article><span>${escapeHtml15(label)}</span><strong>Disabled</strong><p class="muted">No worker configured.</p></article>`;
|
|
11573
|
+
}
|
|
11574
|
+
return `<article><span>${escapeHtml15(label)}</span><strong>${String(summary.delivered)}/${String(summary.total)}</strong><p class="muted">${String(summary.pending)} pending · ${String(summary.failed)} failed · ${String(summary.deadLettered)} dead-lettered</p></article>`;
|
|
11575
|
+
};
|
|
11568
11576
|
var createVoiceDeliveryRuntime = (config) => {
|
|
11569
11577
|
const audit = config.audit ? createVoiceAuditSinkDeliveryWorker(config.audit) : undefined;
|
|
11570
11578
|
const trace = config.trace ? createVoiceTraceSinkDeliveryWorker(config.trace) : undefined;
|
|
@@ -11626,6 +11634,50 @@ var createVoiceDeliveryRuntime = (config) => {
|
|
|
11626
11634
|
trace
|
|
11627
11635
|
};
|
|
11628
11636
|
};
|
|
11637
|
+
var buildVoiceDeliveryRuntimeReport = async (runtime) => ({
|
|
11638
|
+
checkedAt: Date.now(),
|
|
11639
|
+
isRunning: runtime.isRunning(),
|
|
11640
|
+
summary: await runtime.summarize()
|
|
11641
|
+
});
|
|
11642
|
+
var renderVoiceDeliveryRuntimeHTML = (report, options = {}) => {
|
|
11643
|
+
const title = options.title ?? "AbsoluteJS Voice Delivery Runtime";
|
|
11644
|
+
const tickForm = options.tickPath === false ? "" : `<form method="post" action="${escapeHtml15(options.tickPath ?? "/api/voice-delivery-runtime/tick")}"><button type="submit">Tick delivery workers</button></form>`;
|
|
11645
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml15(title)}</title><style>body{background:#0f1411;color:#f7f2df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1080px;padding:32px}a{color:#86efac;text-decoration:none}.hero{background:linear-gradient(135deg,rgba(34,197,94,.18),rgba(14,165,233,.13));border:1px solid #263a30;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}.status{border:1px solid #64748b;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.status.running{border-color:rgba(34,197,94,.7);color:#bbf7d0}.muted{color:#b9c3b4}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));margin:18px 0}article,.card{background:#151d18;border:1px solid #263a30;border-radius:22px;padding:18px}article span{color:#b9c3b4;display:block;font-weight:800}article strong{display:block;font-size:2.3rem;margin-top:8px}button{background:#86efac;border:0;border-radius:999px;color:#07120b;cursor:pointer;font-weight:900;margin-top:12px;padding:10px 14px}pre{background:#09100c;border:1px solid #263a30;border-radius:18px;color:#dcfce7;overflow:auto;padding:16px}</style></head><body><main><p><a href="/delivery-sinks">Delivery sinks</a></p><section class="hero"><p class="eyebrow">Worker control plane</p><h1>${escapeHtml15(title)}</h1><p class="muted">Inspect queue summaries and manually tick audit and trace delivery workers from one runtime primitive.</p><p class="status ${report.isRunning ? "running" : ""}">${report.isRunning ? "Running" : "Stopped"}</p><p class="muted">Checked ${escapeHtml15(new Date(report.checkedAt).toLocaleString())}</p>${tickForm}</section><section class="grid">${renderSummaryCard("Audit", report.summary.audit)}${renderSummaryCard("Trace", report.summary.trace)}</section><section class="card"><h2>Runtime shape</h2><pre>const runtime = createVoiceDeliveryRuntime({ audit, trace })
|
|
11646
|
+
|
|
11647
|
+
await runtime.tick()
|
|
11648
|
+
await runtime.summarize()</pre></section></main></body></html>`;
|
|
11649
|
+
};
|
|
11650
|
+
var createVoiceDeliveryRuntimeRoutes = (options) => {
|
|
11651
|
+
const path = options.path ?? "/api/voice-delivery-runtime";
|
|
11652
|
+
const htmlPath = options.htmlPath === undefined ? "/delivery-runtime" : options.htmlPath;
|
|
11653
|
+
const tickPath = options.tickPath === undefined ? "/api/voice-delivery-runtime/tick" : options.tickPath;
|
|
11654
|
+
const routes = new Elysia12({
|
|
11655
|
+
name: options.name ?? "absolutejs-voice-delivery-runtime"
|
|
11656
|
+
}).get(path, () => buildVoiceDeliveryRuntimeReport(options.runtime));
|
|
11657
|
+
if (tickPath !== false) {
|
|
11658
|
+
routes.post(tickPath, async () => ({
|
|
11659
|
+
drainedAt: Date.now(),
|
|
11660
|
+
result: await options.runtime.tick(),
|
|
11661
|
+
summary: await options.runtime.summarize()
|
|
11662
|
+
}));
|
|
11663
|
+
}
|
|
11664
|
+
if (htmlPath !== false) {
|
|
11665
|
+
routes.get(htmlPath, async () => {
|
|
11666
|
+
const report = await buildVoiceDeliveryRuntimeReport(options.runtime);
|
|
11667
|
+
const body = await (options.render ?? renderVoiceDeliveryRuntimeHTML)(report, {
|
|
11668
|
+
tickPath,
|
|
11669
|
+
title: options.title
|
|
11670
|
+
});
|
|
11671
|
+
return new Response(body, {
|
|
11672
|
+
headers: {
|
|
11673
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
11674
|
+
...options.headers
|
|
11675
|
+
}
|
|
11676
|
+
});
|
|
11677
|
+
});
|
|
11678
|
+
}
|
|
11679
|
+
return routes;
|
|
11680
|
+
};
|
|
11629
11681
|
// src/dataControl.ts
|
|
11630
11682
|
var allRetentionScopes = [
|
|
11631
11683
|
"auditDeliveries",
|
|
@@ -11829,16 +11881,16 @@ var applyVoiceDataRetentionPolicy = async (options) => {
|
|
|
11829
11881
|
};
|
|
11830
11882
|
var buildVoiceDataRetentionPlan = (options) => applyVoiceDataRetentionPolicy({ ...options, dryRun: true });
|
|
11831
11883
|
// src/evalRoutes.ts
|
|
11832
|
-
import { Elysia as
|
|
11884
|
+
import { Elysia as Elysia15 } from "elysia";
|
|
11833
11885
|
import { mkdir } from "fs/promises";
|
|
11834
11886
|
import { dirname } from "path";
|
|
11835
11887
|
|
|
11836
11888
|
// src/qualityRoutes.ts
|
|
11837
|
-
import { Elysia as
|
|
11889
|
+
import { Elysia as Elysia14 } from "elysia";
|
|
11838
11890
|
|
|
11839
11891
|
// src/handoffHealth.ts
|
|
11840
|
-
import { Elysia as
|
|
11841
|
-
var
|
|
11892
|
+
import { Elysia as Elysia13 } from "elysia";
|
|
11893
|
+
var escapeHtml16 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
11842
11894
|
var getString6 = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
|
|
11843
11895
|
var isStatus = (value) => value === "delivered" || value === "failed" || value === "skipped";
|
|
11844
11896
|
var increment3 = (record, key) => {
|
|
@@ -11956,10 +12008,10 @@ var renderActionSummary = (summary) => {
|
|
|
11956
12008
|
return [
|
|
11957
12009
|
'<section class="voice-handoff-health-columns">',
|
|
11958
12010
|
"<article><h3>Actions</h3>",
|
|
11959
|
-
actions.length === 0 ? "<p>No handoff actions yet.</p>" : `<ul>${actions.map(([action, count]) => `<li>${
|
|
12011
|
+
actions.length === 0 ? "<p>No handoff actions yet.</p>" : `<ul>${actions.map(([action, count]) => `<li>${escapeHtml16(action)}: ${String(count)}</li>`).join("")}</ul>`,
|
|
11960
12012
|
"</article>",
|
|
11961
12013
|
"<article><h3>Adapters</h3>",
|
|
11962
|
-
adapters.length === 0 ? "<p>No adapter deliveries yet.</p>" : `<ul>${adapters.map(([adapterId, counts]) => `<li>${
|
|
12014
|
+
adapters.length === 0 ? "<p>No adapter deliveries yet.</p>" : `<ul>${adapters.map(([adapterId, counts]) => `<li>${escapeHtml16(adapterId)}: ${String(counts.delivered)} delivered / ${String(counts.failed)} failed / ${String(counts.skipped)} skipped</li>`).join("")}</ul>`,
|
|
11963
12015
|
"</article>",
|
|
11964
12016
|
"</section>"
|
|
11965
12017
|
].join("");
|
|
@@ -11973,22 +12025,22 @@ var renderVoiceHandoffHealthHTML = (summary) => [
|
|
|
11973
12025
|
summary.events.length === 0 ? '<p class="voice-handoff-health-empty">No handoffs found.</p>' : [
|
|
11974
12026
|
'<div class="voice-handoff-health-events">',
|
|
11975
12027
|
...summary.events.map((event) => [
|
|
11976
|
-
`<article class="${
|
|
12028
|
+
`<article class="${escapeHtml16(event.status)}">`,
|
|
11977
12029
|
'<div class="voice-handoff-health-event-header">',
|
|
11978
|
-
`<strong>${
|
|
11979
|
-
`<span>${
|
|
12030
|
+
`<strong>${escapeHtml16(event.action ?? "handoff")}</strong>`,
|
|
12031
|
+
`<span>${escapeHtml16(event.status)}</span>`,
|
|
11980
12032
|
"</div>",
|
|
11981
|
-
`<p><small>${
|
|
11982
|
-
event.target ? `<p>Target: ${
|
|
11983
|
-
event.reason ? `<p>Reason: ${
|
|
12033
|
+
`<p><small>${escapeHtml16(event.sessionId)}</small></p>`,
|
|
12034
|
+
event.target ? `<p>Target: ${escapeHtml16(event.target)}</p>` : "",
|
|
12035
|
+
event.reason ? `<p>Reason: ${escapeHtml16(event.reason)}</p>` : "",
|
|
11984
12036
|
event.deliveries.length ? `<ul>${event.deliveries.map((delivery) => [
|
|
11985
12037
|
"<li>",
|
|
11986
|
-
`${
|
|
11987
|
-
delivery.deliveredTo ? ` to ${
|
|
11988
|
-
delivery.error ? ` (${
|
|
12038
|
+
`${escapeHtml16(delivery.adapterId)}: ${escapeHtml16(delivery.status)}`,
|
|
12039
|
+
delivery.deliveredTo ? ` to ${escapeHtml16(delivery.deliveredTo)}` : "",
|
|
12040
|
+
delivery.error ? ` (${escapeHtml16(delivery.error)})` : "",
|
|
11989
12041
|
"</li>"
|
|
11990
12042
|
].join("")).join("")}</ul>` : "",
|
|
11991
|
-
event.replayHref ? `<p><a href="${
|
|
12043
|
+
event.replayHref ? `<p><a href="${escapeHtml16(event.replayHref)}">Open replay</a></p>` : "",
|
|
11992
12044
|
"</article>"
|
|
11993
12045
|
].join("")),
|
|
11994
12046
|
"</div>"
|
|
@@ -12020,7 +12072,7 @@ var createVoiceHandoffHealthHTMLHandler = (options = {}) => async ({ query }) =>
|
|
|
12020
12072
|
var createVoiceHandoffHealthRoutes = (options = {}) => {
|
|
12021
12073
|
const path = options.path ?? "/api/voice-handoffs";
|
|
12022
12074
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
12023
|
-
const routes = new
|
|
12075
|
+
const routes = new Elysia13({
|
|
12024
12076
|
name: options.name ?? "absolutejs-voice-handoff-health"
|
|
12025
12077
|
}).get(path, createVoiceHandoffHealthJSONHandler(options));
|
|
12026
12078
|
if (htmlPath) {
|
|
@@ -12141,17 +12193,17 @@ var evaluateVoiceQuality = async (input) => {
|
|
|
12141
12193
|
thresholds
|
|
12142
12194
|
};
|
|
12143
12195
|
};
|
|
12144
|
-
var
|
|
12196
|
+
var escapeHtml17 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
12145
12197
|
var formatMetricValue = (metric) => metric.unit === "rate" ? `${(metric.actual * 100).toFixed(2)}%` : metric.unit === "ms" ? `${Math.round(metric.actual)}ms` : String(metric.actual);
|
|
12146
12198
|
var formatThreshold = (metric) => metric.unit === "rate" ? `${(metric.threshold * 100).toFixed(2)}%` : metric.unit === "ms" ? `${Math.round(metric.threshold)}ms` : String(metric.threshold);
|
|
12147
12199
|
var renderVoiceQualityHTML = (report, options = {}) => {
|
|
12148
|
-
const rows = Object.entries(report.metrics).map(([key, metric]) => `<tr class="${metric.pass ? "pass" : "fail"}"><td>${
|
|
12149
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
12200
|
+
const rows = Object.entries(report.metrics).map(([key, metric]) => `<tr class="${metric.pass ? "pass" : "fail"}"><td>${escapeHtml17(metric.label)}</td><td>${escapeHtml17(formatMetricValue(metric))}</td><td>${escapeHtml17(formatThreshold(metric))}</td><td>${metric.pass ? "pass" : "fail"}</td><td><code>${escapeHtml17(key)}</code></td></tr>`).join("");
|
|
12201
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml17(link.href)}">${escapeHtml17(link.label)}</a>`).join("")}</nav>` : "";
|
|
12150
12202
|
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>AbsoluteJS Voice Quality</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1100px;margin:auto}nav{display:flex;flex-wrap:wrap;gap:.5rem;margin:0 0 1.25rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.status{border-radius:999px;display:inline-flex;padding:.35rem .75rem;font-weight:800}.status.pass{background:#dcfce7;color:#166534}.status.fail{background:#fee2e2;color:#991b1b}table{border-collapse:collapse;width:100%;background:white;margin-top:1rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}.pass td{border-left:4px solid #16a34a}.fail td{border-left:4px solid #dc2626}code{background:#f3f4f6;padding:.15rem .3rem;border-radius:.3rem}</style></head><body><main>${links}<h1>Voice quality gates</h1><p class="status ${report.status}">${report.status}</p><p>${report.eventCount} event(s) checked.</p><table><thead><tr><th>Metric</th><th>Actual</th><th>Threshold</th><th>Status</th><th>Key</th></tr></thead><tbody>${rows}</tbody></table></main></body></html>`;
|
|
12151
12203
|
};
|
|
12152
12204
|
var createVoiceQualityRoutes = (options) => {
|
|
12153
12205
|
const path = options.path ?? "/quality";
|
|
12154
|
-
const routes = new
|
|
12206
|
+
const routes = new Elysia14({
|
|
12155
12207
|
name: options.name ?? "absolutejs-voice-quality"
|
|
12156
12208
|
});
|
|
12157
12209
|
const getReport = () => evaluateVoiceQuality({
|
|
@@ -12180,7 +12232,7 @@ var createVoiceQualityRoutes = (options) => {
|
|
|
12180
12232
|
};
|
|
12181
12233
|
|
|
12182
12234
|
// src/evalRoutes.ts
|
|
12183
|
-
var
|
|
12235
|
+
var escapeHtml18 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
12184
12236
|
var rate2 = (count, total) => count / Math.max(1, total);
|
|
12185
12237
|
var normalizeSearchText = (value) => value.trim().toLowerCase();
|
|
12186
12238
|
var getString8 = (value) => typeof value === "string" ? value : undefined;
|
|
@@ -12489,44 +12541,44 @@ var formatTime = (value) => value === undefined ? "unknown" : new Date(value).to
|
|
|
12489
12541
|
var formatPercent = (value) => `${(value * 100).toFixed(2)}%`;
|
|
12490
12542
|
var renderVoiceEvalHTML = (report, options = {}) => {
|
|
12491
12543
|
const title = options.title ?? "AbsoluteJS Voice Evals";
|
|
12492
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
12493
|
-
const trend = report.trend.length ? report.trend.map((bucket) => `<tr><td>${
|
|
12544
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml18(link.href)}">${escapeHtml18(link.label)}</a>`).join("")}</nav>` : "";
|
|
12545
|
+
const trend = report.trend.length ? report.trend.map((bucket) => `<tr><td>${escapeHtml18(bucket.key)}</td><td>${bucket.total}</td><td>${bucket.passed}</td><td>${bucket.failed}</td></tr>`).join("") : '<tr><td colspan="4">No eval buckets yet.</td></tr>';
|
|
12494
12546
|
const sessions = report.sessions.length ? report.sessions.map((session) => {
|
|
12495
12547
|
const failedMetrics = Object.entries(session.quality.metrics).filter(([, metric]) => !metric.pass).map(([, metric]) => metric.label).join(", ");
|
|
12496
|
-
return `<tr class="${session.status}"><td>${
|
|
12548
|
+
return `<tr class="${session.status}"><td>${escapeHtml18(session.sessionId)}</td><td>${escapeHtml18(session.status)}</td><td>${session.eventCount}</td><td>${session.summary.turnCount}</td><td>${session.summary.errorCount}</td><td>${escapeHtml18(formatTime(session.endedAt))}</td><td>${escapeHtml18(failedMetrics || "none")}</td></tr>`;
|
|
12497
12549
|
}).join("") : '<tr><td colspan="7">No sessions found.</td></tr>';
|
|
12498
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
12550
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml18(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1180px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.pass{color:#166534}.fail{color:#991b1b}.status.pass{background:#dcfce7}.status.fail{background:#fee2e2}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:1rem 0}.card{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.card strong{display:block;font-size:2rem}table{border-collapse:collapse;background:white;width:100%;margin:1rem 0 2rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}tr.fail td{border-left:4px solid #dc2626}tr.pass td{border-left:4px solid #16a34a}</style></head><body><main>${links}<h1>${escapeHtml18(title)}</h1><p class="status ${report.status}">${report.status}</p><div class="grid"><article class="card"><span>Total</span><strong>${report.total}</strong></article><article class="card"><span>Passed</span><strong>${report.passed}</strong></article><article class="card"><span>Failed</span><strong>${report.failed}</strong></article></div><h2>Trend</h2><table><thead><tr><th>Day</th><th>Total</th><th>Passed</th><th>Failed</th></tr></thead><tbody>${trend}</tbody></table><h2>Session Eval Results</h2><table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Turns</th><th>Errors</th><th>Last event</th><th>Failed metrics</th></tr></thead><tbody>${sessions}</tbody></table></main></body></html>`;
|
|
12499
12551
|
};
|
|
12500
12552
|
var renderVoiceEvalBaselineHTML = (comparison, options = {}) => {
|
|
12501
12553
|
const title = options.title ?? "AbsoluteJS Voice Eval Baseline";
|
|
12502
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
12503
|
-
const reasons = comparison.reasons.length ? comparison.reasons.map((reason) => `<li>${
|
|
12504
|
-
const newFailures = comparison.newFailedSessionIds.length ? comparison.newFailedSessionIds.map((id) => `<li>${
|
|
12505
|
-
const recovered = comparison.recoveredSessionIds.length ? comparison.recoveredSessionIds.map((id) => `<li>${
|
|
12506
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
12554
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml18(link.href)}">${escapeHtml18(link.label)}</a>`).join("")}</nav>` : "";
|
|
12555
|
+
const reasons = comparison.reasons.length ? comparison.reasons.map((reason) => `<li>${escapeHtml18(reason)}</li>`).join("") : "<li>No baseline regressions detected.</li>";
|
|
12556
|
+
const newFailures = comparison.newFailedSessionIds.length ? comparison.newFailedSessionIds.map((id) => `<li>${escapeHtml18(id)}</li>`).join("") : "<li>none</li>";
|
|
12557
|
+
const recovered = comparison.recoveredSessionIds.length ? comparison.recoveredSessionIds.map((id) => `<li>${escapeHtml18(id)}</li>`).join("") : "<li>none</li>";
|
|
12558
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml18(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1000px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.pass{background:#dcfce7;color:#166534}.fail{background:#fee2e2;color:#991b1b}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:1rem 0}.card{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.card strong{display:block;font-size:2rem}section{background:white;border:1px solid #e7e5e4;border-radius:1rem;margin:1rem 0;padding:1rem}</style></head><body><main>${links}<h1>${escapeHtml18(title)}</h1><p class="status ${comparison.status}">${comparison.status}</p><div class="grid"><article class="card"><span>Baseline pass rate</span><strong>${escapeHtml18(formatPercent(comparison.baseline.passRate))}</strong></article><article class="card"><span>Current pass rate</span><strong>${escapeHtml18(formatPercent(comparison.current.passRate))}</strong></article><article class="card"><span>Failed delta</span><strong>${comparison.deltas.failed}</strong></article><article class="card"><span>Pass rate delta</span><strong>${escapeHtml18(formatPercent(comparison.deltas.passRate))}</strong></article></div><section><h2>Regression Reasons</h2><ul>${reasons}</ul></section><section><h2>New Failed Sessions</h2><ul>${newFailures}</ul></section><section><h2>Recovered Sessions</h2><ul>${recovered}</ul></section></main></body></html>`;
|
|
12507
12559
|
};
|
|
12508
12560
|
var renderVoiceScenarioEvalHTML = (report, options = {}) => {
|
|
12509
12561
|
const title = options.title ?? "AbsoluteJS Voice Scenario Evals";
|
|
12510
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
12562
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml18(link.href)}">${escapeHtml18(link.label)}</a>`).join("")}</nav>` : "";
|
|
12511
12563
|
const scenarios = report.scenarios.length ? report.scenarios.map((scenario) => {
|
|
12512
|
-
const scenarioIssues = scenario.issues.length ? `<ul>${scenario.issues.map((issue) => `<li>${
|
|
12513
|
-
const sessions = scenario.sessions.length ? scenario.sessions.map((session) => `<tr class="${session.status}"><td>${
|
|
12514
|
-
return `<section class="scenario ${scenario.status}"><h2>${
|
|
12564
|
+
const scenarioIssues = scenario.issues.length ? `<ul>${scenario.issues.map((issue) => `<li>${escapeHtml18(issue)}</li>`).join("")}</ul>` : "";
|
|
12565
|
+
const sessions = scenario.sessions.length ? scenario.sessions.map((session) => `<tr class="${session.status}"><td>${escapeHtml18(session.sessionId)}</td><td>${escapeHtml18(session.status)}</td><td>${session.eventCount}</td><td>${escapeHtml18(session.issues.join(", ") || "none")}</td></tr>`).join("") : '<tr><td colspan="4">No matching sessions.</td></tr>';
|
|
12566
|
+
return `<section class="scenario ${scenario.status}"><h2>${escapeHtml18(scenario.label)}</h2>${scenario.description ? `<p>${escapeHtml18(scenario.description)}</p>` : ""}<p class="status ${scenario.status}">${scenario.status}</p><p>${scenario.passed} passed, ${scenario.failed} failed, ${scenario.matchedSessions} matched.</p>${scenarioIssues}<table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Issues</th></tr></thead><tbody>${sessions}</tbody></table></section>`;
|
|
12515
12567
|
}).join("") : "<section><p>No scenarios configured.</p></section>";
|
|
12516
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
12568
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml18(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1180px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.status.pass{background:#dcfce7;color:#166534}.status.fail{background:#fee2e2;color:#991b1b}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:1rem 0}.card,section{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.card strong{display:block;font-size:2rem}section{margin:1rem 0}table{border-collapse:collapse;width:100%;margin-top:1rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}tr.fail td{border-left:4px solid #dc2626}tr.pass td{border-left:4px solid #16a34a}</style></head><body><main>${links}<h1>${escapeHtml18(title)}</h1><p class="status ${report.status}">${report.status}</p><div class="grid"><article class="card"><span>Total</span><strong>${report.total}</strong></article><article class="card"><span>Passed</span><strong>${report.passed}</strong></article><article class="card"><span>Failed</span><strong>${report.failed}</strong></article></div>${scenarios}</main></body></html>`;
|
|
12517
12569
|
};
|
|
12518
12570
|
var renderVoiceScenarioFixtureEvalHTML = (report, options = {}) => {
|
|
12519
12571
|
const title = options.title ?? "AbsoluteJS Voice Fixture Evals";
|
|
12520
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
12572
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml18(link.href)}">${escapeHtml18(link.label)}</a>`).join("")}</nav>` : "";
|
|
12521
12573
|
const fixtures = report.fixtures.length ? report.fixtures.map((fixture) => {
|
|
12522
|
-
const scenarios = fixture.report.scenarios.map((scenario) => `<tr class="${scenario.status}"><td>${
|
|
12523
|
-
return `<section class="${fixture.status}"><h2>${
|
|
12574
|
+
const scenarios = fixture.report.scenarios.map((scenario) => `<tr class="${scenario.status}"><td>${escapeHtml18(scenario.label)}</td><td>${escapeHtml18(scenario.status)}</td><td>${scenario.matchedSessions}</td><td>${escapeHtml18([...scenario.issues, ...scenario.sessions.flatMap((session) => session.issues)].join(", ") || "none")}</td></tr>`).join("");
|
|
12575
|
+
return `<section class="${fixture.status}"><h2>${escapeHtml18(fixture.label)}</h2>${fixture.description ? `<p>${escapeHtml18(fixture.description)}</p>` : ""}<p class="status ${fixture.status}">${fixture.status}</p><table><thead><tr><th>Scenario</th><th>Status</th><th>Sessions</th><th>Issues</th></tr></thead><tbody>${scenarios}</tbody></table></section>`;
|
|
12524
12576
|
}).join("") : "<section><p>No scenario fixtures configured.</p></section>";
|
|
12525
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
12577
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml18(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1180px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.status.pass{background:#dcfce7;color:#166534}.status.fail{background:#fee2e2;color:#991b1b}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:1rem 0}.card,section{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.card strong{display:block;font-size:2rem}section{margin:1rem 0}table{border-collapse:collapse;width:100%;margin-top:1rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}tr.fail td{border-left:4px solid #dc2626}tr.pass td{border-left:4px solid #16a34a}</style></head><body><main>${links}<h1>${escapeHtml18(title)}</h1><p class="status ${report.status}">${report.status}</p><div class="grid"><article class="card"><span>Total</span><strong>${report.total}</strong></article><article class="card"><span>Passed</span><strong>${report.passed}</strong></article><article class="card"><span>Failed</span><strong>${report.failed}</strong></article></div>${fixtures}</main></body></html>`;
|
|
12526
12578
|
};
|
|
12527
12579
|
var createVoiceEvalRoutes = (options) => {
|
|
12528
12580
|
const path = options.path ?? "/evals";
|
|
12529
|
-
const routes = new
|
|
12581
|
+
const routes = new Elysia15({
|
|
12530
12582
|
name: options.name ?? "absolutejs-voice-evals"
|
|
12531
12583
|
});
|
|
12532
12584
|
const getReport = () => runVoiceSessionEvals({
|
|
@@ -12660,11 +12712,11 @@ var createVoiceEvalRoutes = (options) => {
|
|
|
12660
12712
|
return routes;
|
|
12661
12713
|
};
|
|
12662
12714
|
// src/simulationSuite.ts
|
|
12663
|
-
import { Elysia as
|
|
12715
|
+
import { Elysia as Elysia18 } from "elysia";
|
|
12664
12716
|
|
|
12665
12717
|
// src/outcomeContract.ts
|
|
12666
|
-
import { Elysia as
|
|
12667
|
-
var
|
|
12718
|
+
import { Elysia as Elysia16 } from "elysia";
|
|
12719
|
+
var escapeHtml19 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
12668
12720
|
var getPayloadString = (event, key) => typeof event.payload[key] === "string" ? event.payload[key] : undefined;
|
|
12669
12721
|
var toList = async (input) => Array.isArray(input) ? input : await input?.list() ?? [];
|
|
12670
12722
|
var hydrateSessions = async (input) => {
|
|
@@ -12772,9 +12824,9 @@ var renderVoiceOutcomeContractHTML = (report, options = {}) => {
|
|
|
12772
12824
|
const contracts = report.contracts.map((contract) => `<section class="contract ${contract.pass ? "pass" : "fail"}">
|
|
12773
12825
|
<div class="contract-header">
|
|
12774
12826
|
<div>
|
|
12775
|
-
<p class="eyebrow">${
|
|
12776
|
-
<h2>${
|
|
12777
|
-
${contract.description ? `<p>${
|
|
12827
|
+
<p class="eyebrow">${escapeHtml19(contract.contractId)}</p>
|
|
12828
|
+
<h2>${escapeHtml19(contract.label ?? contract.contractId)}</h2>
|
|
12829
|
+
${contract.description ? `<p>${escapeHtml19(contract.description)}</p>` : ""}
|
|
12778
12830
|
</div>
|
|
12779
12831
|
<strong>${contract.pass ? "pass" : "fail"}</strong>
|
|
12780
12832
|
</div>
|
|
@@ -12785,9 +12837,9 @@ var renderVoiceOutcomeContractHTML = (report, options = {}) => {
|
|
|
12785
12837
|
<span>handoffs ${String(contract.matched.handoffs)}</span>
|
|
12786
12838
|
<span>events ${String(contract.matched.integrationEvents)}</span>
|
|
12787
12839
|
</div>
|
|
12788
|
-
${contract.issues.length ? `<ul>${contract.issues.map((issue) => `<li>${
|
|
12840
|
+
${contract.issues.length ? `<ul>${contract.issues.map((issue) => `<li>${escapeHtml19(issue.message)}</li>`).join("")}</ul>` : ""}
|
|
12789
12841
|
</section>`).join("");
|
|
12790
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
12842
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml19(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.contract{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.14),rgba(14,165,233,.12))}.eyebrow{color:#7dd3fc;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0}.summary,.grid{display:flex;flex-wrap:wrap;gap:10px}.pill,.grid span{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.contract-header{display:flex;gap:16px;justify-content:space-between}.pass{color:#86efac}.fail{color:#fca5a5}.contract.fail{border-color:rgba(248,113,113,.45)}li{margin:8px 0}@media(max-width:800px){main{padding:18px}.contract-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Business Outcome Verification</p><h1>${escapeHtml19(title)}</h1><div class="summary"><span class="pill ${report.status}">${report.status}</span><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} contracts</span></div></section>${contracts || '<section class="contract"><p>No outcome contracts configured.</p></section>'}</main></body></html>`;
|
|
12791
12843
|
};
|
|
12792
12844
|
var createVoiceOutcomeContractJSONHandler = (options) => async () => runVoiceOutcomeContractSuite(options);
|
|
12793
12845
|
var createVoiceOutcomeContractHTMLHandler = (options) => async () => {
|
|
@@ -12803,7 +12855,7 @@ var createVoiceOutcomeContractHTMLHandler = (options) => async () => {
|
|
|
12803
12855
|
var createVoiceOutcomeContractRoutes = (options) => {
|
|
12804
12856
|
const path = options.path ?? "/api/outcome-contracts";
|
|
12805
12857
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
12806
|
-
const routes = new
|
|
12858
|
+
const routes = new Elysia16({
|
|
12807
12859
|
name: options.name ?? "absolutejs-voice-outcome-contracts"
|
|
12808
12860
|
}).get(path, createVoiceOutcomeContractJSONHandler(options));
|
|
12809
12861
|
if (htmlPath) {
|
|
@@ -12813,7 +12865,7 @@ var createVoiceOutcomeContractRoutes = (options) => {
|
|
|
12813
12865
|
};
|
|
12814
12866
|
|
|
12815
12867
|
// src/toolContract.ts
|
|
12816
|
-
import { Elysia as
|
|
12868
|
+
import { Elysia as Elysia17 } from "elysia";
|
|
12817
12869
|
|
|
12818
12870
|
// src/toolRuntime.ts
|
|
12819
12871
|
var toErrorMessage4 = (error) => error instanceof Error ? error.message : String(error);
|
|
@@ -13022,7 +13074,7 @@ var createDefaultTurn = (caseId) => ({
|
|
|
13022
13074
|
});
|
|
13023
13075
|
var defaultApi = {};
|
|
13024
13076
|
var sameJSON = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
13025
|
-
var
|
|
13077
|
+
var escapeHtml20 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13026
13078
|
var evaluateExpectation = (input) => {
|
|
13027
13079
|
const issues = [];
|
|
13028
13080
|
const expect = input.expect;
|
|
@@ -13188,19 +13240,19 @@ var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
|
13188
13240
|
const title = options.title ?? "Voice Tool Contracts";
|
|
13189
13241
|
const contracts = report.contracts.map((contract) => {
|
|
13190
13242
|
const cases = contract.cases.map((testCase) => `<tr>
|
|
13191
|
-
<td>${
|
|
13243
|
+
<td>${escapeHtml20(testCase.label ?? testCase.caseId)}</td>
|
|
13192
13244
|
<td class="${testCase.pass ? "pass" : "fail"}">${testCase.pass ? "pass" : "fail"}</td>
|
|
13193
|
-
<td>${
|
|
13245
|
+
<td>${escapeHtml20(testCase.status)}</td>
|
|
13194
13246
|
<td>${String(testCase.attempts)}</td>
|
|
13195
13247
|
<td>${String(testCase.elapsedMs)}ms</td>
|
|
13196
13248
|
<td>${testCase.timedOut ? "yes" : "no"}</td>
|
|
13197
|
-
<td>${
|
|
13249
|
+
<td>${escapeHtml20(testCase.issues.map((issue) => issue.message).join(" ") || testCase.error || "")}</td>
|
|
13198
13250
|
</tr>`).join("");
|
|
13199
13251
|
return `<section class="contract ${contract.pass ? "pass" : "fail"}">
|
|
13200
13252
|
<div class="contract-header">
|
|
13201
13253
|
<div>
|
|
13202
|
-
<p class="eyebrow">${
|
|
13203
|
-
<h2>${
|
|
13254
|
+
<p class="eyebrow">${escapeHtml20(contract.toolName)}</p>
|
|
13255
|
+
<h2>${escapeHtml20(contract.label ?? contract.contractId)}</h2>
|
|
13204
13256
|
</div>
|
|
13205
13257
|
<strong class="${contract.pass ? "pass" : "fail"}">${contract.pass ? "Passing" : "Failing"}</strong>
|
|
13206
13258
|
</div>
|
|
@@ -13210,7 +13262,7 @@ var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
|
13210
13262
|
</table>
|
|
13211
13263
|
</section>`;
|
|
13212
13264
|
}).join("");
|
|
13213
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
13265
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml20(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.contract{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.14),rgba(245,158,11,.12))}.eyebrow{color:#fbbf24;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.contract-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}h2{margin:.2rem 0 1rem}.pass{color:#86efac}.fail{color:#fca5a5}.contract.fail{border-color:rgba(248,113,113,.45)}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid #2a323a;padding:12px;text-align:left;vertical-align:top}th{color:#a8b0b8;font-size:.82rem}@media(max-width:800px){main{padding:18px}table{display:block;overflow:auto}.contract-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Tool Reliability</p><h1>${escapeHtml20(title)}</h1><div class="summary"><span class="pill ${report.status === "pass" ? "pass" : "fail"}">${escapeHtml20(report.status)}</span><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} contracts</span></div></section>${contracts || '<section class="contract"><p>No tool contracts configured.</p></section>'}</main></body></html>`;
|
|
13214
13266
|
};
|
|
13215
13267
|
var createVoiceToolContractJSONHandler = (options) => () => runVoiceToolContractSuite(options);
|
|
13216
13268
|
var createVoiceToolContractHTMLHandler = (options) => async () => {
|
|
@@ -13227,7 +13279,7 @@ var createVoiceToolContractHTMLHandler = (options) => async () => {
|
|
|
13227
13279
|
var createVoiceToolContractRoutes = (options) => {
|
|
13228
13280
|
const path = options.path ?? "/api/tool-contracts";
|
|
13229
13281
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
13230
|
-
const routes = new
|
|
13282
|
+
const routes = new Elysia17({
|
|
13231
13283
|
name: options.name ?? "absolutejs-voice-tool-contracts"
|
|
13232
13284
|
}).get(path, createVoiceToolContractJSONHandler(options));
|
|
13233
13285
|
if (htmlPath) {
|
|
@@ -13237,7 +13289,7 @@ var createVoiceToolContractRoutes = (options) => {
|
|
|
13237
13289
|
};
|
|
13238
13290
|
|
|
13239
13291
|
// src/simulationSuite.ts
|
|
13240
|
-
var
|
|
13292
|
+
var escapeHtml21 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13241
13293
|
var summarizeSection = (report) => ({
|
|
13242
13294
|
failed: report.failed,
|
|
13243
13295
|
passed: report.passed,
|
|
@@ -13373,20 +13425,20 @@ var renderSection = (label, summary) => {
|
|
|
13373
13425
|
if (!summary) {
|
|
13374
13426
|
return "";
|
|
13375
13427
|
}
|
|
13376
|
-
return `<article class="${
|
|
13428
|
+
return `<article class="${escapeHtml21(summary.status)}"><span>${escapeHtml21(label)}</span><strong>${escapeHtml21(summary.status)}</strong><p>${summary.passed}/${summary.total} passed, ${summary.failed} failed.</p></article>`;
|
|
13377
13429
|
};
|
|
13378
13430
|
var renderAction = (action) => {
|
|
13379
|
-
const content = `<strong>${
|
|
13380
|
-
return action.href ? `<a class="action" href="${
|
|
13431
|
+
const content = `<strong>${escapeHtml21(action.label)}</strong><p>${escapeHtml21(action.description)}</p><span>${escapeHtml21(action.section)} / ${escapeHtml21(action.severity)}</span>`;
|
|
13432
|
+
return action.href ? `<a class="action" href="${escapeHtml21(action.href)}">${content}</a>` : `<article class="action">${content}</article>`;
|
|
13381
13433
|
};
|
|
13382
13434
|
var renderVoiceSimulationSuiteHTML = (report, options = {}) => {
|
|
13383
13435
|
const title = options.title ?? "Voice Simulation Suite";
|
|
13384
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
13436
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml21(title)}</title><style>body{background:#10151c;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1080px;padding:32px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.18),rgba(59,130,246,.12));border:1px solid #283544;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#93c5fd;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.badge{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;padding:8px 12px}.pass{color:#86efac}.fail{color:#fca5a5}.grid,.actions{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));margin:18px 0}.grid article,.action{background:#151d27;border:1px solid #283544;border-radius:18px;color:inherit;padding:16px;text-decoration:none}.grid span,.action span{color:#aab5c0}.grid strong{display:block;font-size:2rem;margin:.25rem 0;text-transform:uppercase}.action strong{display:block;color:#f8f3e7;margin-bottom:.35rem}.action p{color:#d8dee6;margin:.3rem 0 .6rem}pre{background:#151d27;border:1px solid #283544;border-radius:18px;overflow:auto;padding:16px}</style></head><body><main><section class="hero"><p class="eyebrow">Pre-production proof</p><h1>${escapeHtml21(title)}</h1><p>One report for session quality, scenario evals, fixture simulations, tool contracts, and outcome contracts.</p><p class="badge ${escapeHtml21(report.status)}">Status: ${escapeHtml21(report.status)}</p><section class="grid">${renderSection("Sessions", report.summary.sessions)}${renderSection("Scenarios", report.summary.scenarios)}${renderSection("Fixtures", report.summary.fixtures)}${renderSection("Tools", report.summary.tools)}${renderSection("Outcomes", report.summary.outcomes)}</section></section><h2>Actions</h2><section class="actions">${report.actions.length > 0 ? report.actions.map(renderAction).join("") : '<article class="action"><strong>No action required</strong><p>All enabled simulation sections are passing.</p></article>'}</section><pre>${escapeHtml21(JSON.stringify({ summary: report.summary, actions: report.actions }, null, 2))}</pre></main></body></html>`;
|
|
13385
13437
|
};
|
|
13386
13438
|
var createVoiceSimulationSuiteRoutes = (options) => {
|
|
13387
13439
|
const path = options.path ?? "/api/voice/simulations";
|
|
13388
13440
|
const htmlPath = options.htmlPath === undefined ? "/voice/simulations" : options.htmlPath;
|
|
13389
|
-
const app = new
|
|
13441
|
+
const app = new Elysia18({
|
|
13390
13442
|
name: options.name ?? "absolutejs-voice-simulation-suite"
|
|
13391
13443
|
}).get(path, () => runVoiceSimulationSuite(options));
|
|
13392
13444
|
if (htmlPath) {
|
|
@@ -13698,9 +13750,9 @@ var createVoiceWorkflowContractHandler = (input) => {
|
|
|
13698
13750
|
};
|
|
13699
13751
|
};
|
|
13700
13752
|
// src/sessionReplay.ts
|
|
13701
|
-
import { Elysia as
|
|
13753
|
+
import { Elysia as Elysia19 } from "elysia";
|
|
13702
13754
|
var getString9 = (value) => typeof value === "string" ? value : undefined;
|
|
13703
|
-
var
|
|
13755
|
+
var escapeHtml22 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13704
13756
|
var increment4 = (record, key) => {
|
|
13705
13757
|
record[key] = (record[key] ?? 0) + 1;
|
|
13706
13758
|
};
|
|
@@ -13880,10 +13932,10 @@ var summarizeVoiceSessions = async (options = {}) => {
|
|
|
13880
13932
|
var renderVoiceSessionsHTML = (sessions) => sessions.length === 0 ? '<p class="voice-sessions-empty">No voice sessions found.</p>' : [
|
|
13881
13933
|
'<div class="voice-sessions-list">',
|
|
13882
13934
|
...sessions.map((session) => [
|
|
13883
|
-
`<article class="voice-session-card ${
|
|
13935
|
+
`<article class="voice-session-card ${escapeHtml22(session.status)}">`,
|
|
13884
13936
|
'<div class="voice-session-card-header">',
|
|
13885
|
-
`<strong>${
|
|
13886
|
-
`<span>${
|
|
13937
|
+
`<strong>${escapeHtml22(session.sessionId)}</strong>`,
|
|
13938
|
+
`<span>${escapeHtml22(session.status)}</span>`,
|
|
13887
13939
|
"</div>",
|
|
13888
13940
|
"<dl>",
|
|
13889
13941
|
`<div><dt>Events</dt><dd>${String(session.eventCount)}</dd></div>`,
|
|
@@ -13891,9 +13943,9 @@ var renderVoiceSessionsHTML = (sessions) => sessions.length === 0 ? '<p class="v
|
|
|
13891
13943
|
`<div><dt>Transcripts</dt><dd>${String(session.transcriptCount)}</dd></div>`,
|
|
13892
13944
|
`<div><dt>Errors</dt><dd>${String(session.errorCount)}</dd></div>`,
|
|
13893
13945
|
"</dl>",
|
|
13894
|
-
session.latestOutcome ? `<p>Outcome: ${
|
|
13895
|
-
session.providers.length ? `<p>Providers: ${session.providers.map(
|
|
13896
|
-
session.replayHref ? `<p><a href="${
|
|
13946
|
+
session.latestOutcome ? `<p>Outcome: ${escapeHtml22(session.latestOutcome)}</p>` : "",
|
|
13947
|
+
session.providers.length ? `<p>Providers: ${session.providers.map(escapeHtml22).join(", ")}</p>` : "",
|
|
13948
|
+
session.replayHref ? `<p><a href="${escapeHtml22(session.replayHref)}">Open replay</a></p>` : "",
|
|
13897
13949
|
"</article>"
|
|
13898
13950
|
].join("")),
|
|
13899
13951
|
"</div>"
|
|
@@ -13924,7 +13976,7 @@ var createVoiceSessionsHTMLHandler = (options = {}) => async ({ query }) => {
|
|
|
13924
13976
|
var createVoiceSessionListRoutes = (options = {}) => {
|
|
13925
13977
|
const path = options.path ?? "/api/voice-sessions";
|
|
13926
13978
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
13927
|
-
const routes = new
|
|
13979
|
+
const routes = new Elysia19({
|
|
13928
13980
|
name: options.name ?? "absolutejs-voice-session-list"
|
|
13929
13981
|
}).get(path, createVoiceSessionsJSONHandler(options));
|
|
13930
13982
|
if (htmlPath) {
|
|
@@ -13952,7 +14004,7 @@ var createVoiceSessionReplayHTMLHandler = (options) => async ({ params }) => {
|
|
|
13952
14004
|
var createVoiceSessionReplayRoutes = (options) => {
|
|
13953
14005
|
const path = options.path ?? "/api/voice-sessions/:sessionId/replay";
|
|
13954
14006
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
13955
|
-
const routes = new
|
|
14007
|
+
const routes = new Elysia19({
|
|
13956
14008
|
name: options.name ?? "absolutejs-voice-session-replay"
|
|
13957
14009
|
}).get(path, createVoiceSessionReplayJSONHandler(options));
|
|
13958
14010
|
if (htmlPath) {
|
|
@@ -14119,10 +14171,10 @@ var assertVoiceAgentSquadContract = async (options) => {
|
|
|
14119
14171
|
return report;
|
|
14120
14172
|
};
|
|
14121
14173
|
// src/turnLatency.ts
|
|
14122
|
-
import { Elysia as
|
|
14174
|
+
import { Elysia as Elysia20 } from "elysia";
|
|
14123
14175
|
var DEFAULT_WARN_AFTER_MS = 1800;
|
|
14124
14176
|
var DEFAULT_FAIL_AFTER_MS = 3200;
|
|
14125
|
-
var
|
|
14177
|
+
var escapeHtml23 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
14126
14178
|
var firstNumber = (values) => values.filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
14127
14179
|
var getString10 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
14128
14180
|
var createTraceStageIndex = (events) => {
|
|
@@ -14236,11 +14288,11 @@ var summarizeVoiceTurnLatency = async (options) => {
|
|
|
14236
14288
|
var formatMs = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
|
|
14237
14289
|
var renderVoiceTurnLatencyHTML = (report, options = {}) => {
|
|
14238
14290
|
const title = options.title ?? "Voice Turn Latency";
|
|
14239
|
-
const turns = report.turns.map((turn) => `<article class="turn ${
|
|
14240
|
-
<header><div><p class="eyebrow">${
|
|
14241
|
-
<dl>${turn.stages.map((stage) => `<div><dt>${
|
|
14291
|
+
const turns = report.turns.map((turn) => `<article class="turn ${escapeHtml23(turn.status)}">
|
|
14292
|
+
<header><div><p class="eyebrow">${escapeHtml23(turn.sessionId)} \xB7 ${escapeHtml23(turn.turnId)}</p><h2>${escapeHtml23(turn.text || "Empty turn")}</h2></div><strong>${escapeHtml23(turn.status)}</strong></header>
|
|
14293
|
+
<dl>${turn.stages.map((stage) => `<div><dt>${escapeHtml23(stage.label)}</dt><dd>${escapeHtml23(formatMs(stage.valueMs))}</dd></div>`).join("")}</dl>
|
|
14242
14294
|
</article>`).join("");
|
|
14243
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
14295
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml23(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.turn{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(94,234,212,.16),rgba(251,191,36,.1))}.eyebrow{color:#5eead4;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.turn header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.pass{color:#86efac}.warn,.empty{color:#fde68a}.fail{color:#fca5a5}.turn.fail{border-color:rgba(248,113,113,.45)}dl{display:grid;gap:8px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}dt{color:#a8b0b8;font-size:.8rem}dd{font-weight:900;margin:0}@media(max-width:800px){main{padding:18px}.turn header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">End-to-end responsiveness</p><h1>${escapeHtml23(title)}</h1><div class="summary"><span class="pill ${escapeHtml23(report.status)}">${escapeHtml23(report.status)}</span><span class="pill">${String(report.total)} turns</span><span class="pill">avg ${escapeHtml23(formatMs(report.averageTotalMs))}</span><span class="pill">${String(report.warnings)} warnings</span><span class="pill">${String(report.failed)} failed</span></div></section>${turns || '<section class="turn"><p>No committed turns found.</p></section>'}</main></body></html>`;
|
|
14244
14296
|
};
|
|
14245
14297
|
var createVoiceTurnLatencyJSONHandler = (options) => async () => summarizeVoiceTurnLatency(options);
|
|
14246
14298
|
var createVoiceTurnLatencyHTMLHandler = (options) => async () => {
|
|
@@ -14257,7 +14309,7 @@ var createVoiceTurnLatencyHTMLHandler = (options) => async () => {
|
|
|
14257
14309
|
var createVoiceTurnLatencyRoutes = (options) => {
|
|
14258
14310
|
const path = options.path ?? "/api/turn-latency";
|
|
14259
14311
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
14260
|
-
const routes = new
|
|
14312
|
+
const routes = new Elysia20({
|
|
14261
14313
|
name: options.name ?? "absolutejs-voice-turn-latency"
|
|
14262
14314
|
}).get(path, createVoiceTurnLatencyJSONHandler(options));
|
|
14263
14315
|
if (htmlPath) {
|
|
@@ -14266,8 +14318,8 @@ var createVoiceTurnLatencyRoutes = (options) => {
|
|
|
14266
14318
|
return routes;
|
|
14267
14319
|
};
|
|
14268
14320
|
// src/liveLatency.ts
|
|
14269
|
-
import { Elysia as
|
|
14270
|
-
var
|
|
14321
|
+
import { Elysia as Elysia21 } from "elysia";
|
|
14322
|
+
var escapeHtml24 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
14271
14323
|
var percentile = (values, percentileValue) => {
|
|
14272
14324
|
if (values.length === 0) {
|
|
14273
14325
|
return;
|
|
@@ -14315,13 +14367,13 @@ var summarizeVoiceLiveLatency = async (options) => {
|
|
|
14315
14367
|
var formatMs2 = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
|
|
14316
14368
|
var renderVoiceLiveLatencyHTML = (report, options = {}) => {
|
|
14317
14369
|
const title = options.title ?? "Voice Live Latency";
|
|
14318
|
-
const rows = report.recent.map((sample) => `<tr><td>${
|
|
14319
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
14370
|
+
const rows = report.recent.map((sample) => `<tr><td>${escapeHtml24(sample.sessionId)}</td><td>${escapeHtml24(formatMs2(sample.latencyMs))}</td><td>${escapeHtml24(sample.status ?? "unknown")}</td><td>${escapeHtml24(new Date(sample.at).toLocaleString())}</td></tr>`).join("");
|
|
14371
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml24(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{background:linear-gradient(135deg,rgba(94,234,212,.16),rgba(245,158,11,.1));border:1px solid #26313d;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;padding:8px 12px}.pass{color:#86efac}.warn,.empty{color:#fbbf24}.fail{color:#fca5a5}.metrics{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:18px 0}.metrics article,table{background:#141922;border:1px solid #26313d;border-radius:18px}.metrics article{padding:16px}.metrics span{color:#a8b0b8}.metrics strong{display:block;font-size:2rem;margin-top:.25rem}table{border-collapse:collapse;overflow:hidden;width:100%}td,th{border-bottom:1px solid #26313d;padding:12px;text-align:left}@media(max-width:760px){main{padding:20px}}</style></head><body><main><section class="hero"><p class="eyebrow">Browser proof</p><h1>${escapeHtml24(title)}</h1><p>Recent real browser speech-to-assistant response measurements from persisted <code>client.live_latency</code> traces.</p><p class="status ${escapeHtml24(report.status)}">Status: ${escapeHtml24(report.status)}</p><section class="metrics"><article><span>p50</span><strong>${escapeHtml24(formatMs2(report.p50LatencyMs))}</strong></article><article><span>p95</span><strong>${escapeHtml24(formatMs2(report.p95LatencyMs))}</strong></article><article><span>Average</span><strong>${escapeHtml24(formatMs2(report.averageLatencyMs))}</strong></article><article><span>Samples</span><strong>${String(report.total)}</strong></article></section></section><table><thead><tr><th>Session</th><th>Latency</th><th>Status</th><th>Measured</th></tr></thead><tbody>${rows || '<tr><td colspan="4">No live latency samples yet.</td></tr>'}</tbody></table></main></body></html>`;
|
|
14320
14372
|
};
|
|
14321
14373
|
var createVoiceLiveLatencyRoutes = (options) => {
|
|
14322
14374
|
const path = options.path ?? "/api/live-latency";
|
|
14323
14375
|
const htmlPath = options.htmlPath === undefined ? "/live-latency" : options.htmlPath;
|
|
14324
|
-
const routes = new
|
|
14376
|
+
const routes = new Elysia21({
|
|
14325
14377
|
name: options.name ?? "absolutejs-voice-live-latency"
|
|
14326
14378
|
}).get(path, () => summarizeVoiceLiveLatency(options));
|
|
14327
14379
|
if (htmlPath) {
|
|
@@ -14338,9 +14390,9 @@ var createVoiceLiveLatencyRoutes = (options) => {
|
|
|
14338
14390
|
return routes;
|
|
14339
14391
|
};
|
|
14340
14392
|
// src/turnQuality.ts
|
|
14341
|
-
import { Elysia as
|
|
14393
|
+
import { Elysia as Elysia22 } from "elysia";
|
|
14342
14394
|
var DEFAULT_CONFIDENCE_WARN_THRESHOLD = 0.72;
|
|
14343
|
-
var
|
|
14395
|
+
var escapeHtml25 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
14344
14396
|
var getTurnLatencyMs = (turn) => {
|
|
14345
14397
|
const firstTranscriptAt = turn.transcripts.map((transcript) => transcript.endedAtMs ?? transcript.startedAtMs).filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
14346
14398
|
if (firstTranscriptAt === undefined) {
|
|
@@ -14411,24 +14463,24 @@ var summarizeVoiceTurnQuality = async (options) => {
|
|
|
14411
14463
|
};
|
|
14412
14464
|
var renderVoiceTurnQualityHTML = (report, options = {}) => {
|
|
14413
14465
|
const title = options.title ?? "Voice Turn Quality";
|
|
14414
|
-
const turns = report.turns.map((turn) => `<article class="turn ${
|
|
14466
|
+
const turns = report.turns.map((turn) => `<article class="turn ${escapeHtml25(turn.status)}">
|
|
14415
14467
|
<div class="turn-header">
|
|
14416
14468
|
<div>
|
|
14417
|
-
<p class="eyebrow">${
|
|
14418
|
-
<h2>${
|
|
14469
|
+
<p class="eyebrow">${escapeHtml25(turn.sessionId)} \xB7 ${escapeHtml25(turn.turnId)}</p>
|
|
14470
|
+
<h2>${escapeHtml25(turn.text || "Empty turn")}</h2>
|
|
14419
14471
|
</div>
|
|
14420
|
-
<strong>${
|
|
14472
|
+
<strong>${escapeHtml25(turn.status)}</strong>
|
|
14421
14473
|
</div>
|
|
14422
14474
|
<dl>
|
|
14423
|
-
<div><dt>Source</dt><dd>${
|
|
14475
|
+
<div><dt>Source</dt><dd>${escapeHtml25(turn.source ?? "unknown")}</dd></div>
|
|
14424
14476
|
<div><dt>Confidence</dt><dd>${turn.averageConfidence === undefined ? "n/a" : `${Math.round(turn.averageConfidence * 100)}%`}</dd></div>
|
|
14425
|
-
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${
|
|
14426
|
-
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${
|
|
14477
|
+
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${escapeHtml25(turn.fallbackSelectionReason ?? "selected")})` : "no"}</dd></div>
|
|
14478
|
+
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${escapeHtml25(turn.correctionProvider)}` : ""}` : "none"}</dd></div>
|
|
14427
14479
|
<div><dt>Transcripts</dt><dd>${String(turn.selectedTranscriptCount)} selected \xB7 ${String(turn.finalTranscriptCount)} final \xB7 ${String(turn.partialTranscriptCount)} partial</dd></div>
|
|
14428
14480
|
<div><dt>Cost</dt><dd>${turn.costUnits === undefined ? "n/a" : String(turn.costUnits)}</dd></div>
|
|
14429
14481
|
</dl>
|
|
14430
14482
|
</article>`).join("");
|
|
14431
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
14483
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml25(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.turn{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(251,191,36,.16),rgba(34,197,94,.1))}.eyebrow{color:#fbbf24;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.turn-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.pass{color:#86efac}.warn,.unknown{color:#fde68a}.fail{color:#fca5a5}.turn.fail{border-color:rgba(248,113,113,.45)}dl{display:grid;gap:8px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}dt{color:#a8b0b8;font-size:.8rem}dd{margin:0}@media(max-width:800px){main{padding:18px}.turn-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Realtime STT Debugging</p><h1>${escapeHtml25(title)}</h1><div class="summary"><span class="pill ${escapeHtml25(report.status)}">${escapeHtml25(report.status)}</span><span class="pill">${String(report.total)} turns</span><span class="pill">${String(report.warnings)} warnings</span><span class="pill">${String(report.failed)} failed</span><span class="pill">${String(report.sessions)} sessions</span></div></section>${turns || '<section class="turn"><p>No committed turns found.</p></section>'}</main></body></html>`;
|
|
14432
14484
|
};
|
|
14433
14485
|
var createVoiceTurnQualityJSONHandler = (options) => async () => summarizeVoiceTurnQuality(options);
|
|
14434
14486
|
var createVoiceTurnQualityHTMLHandler = (options) => async () => {
|
|
@@ -14445,7 +14497,7 @@ var createVoiceTurnQualityHTMLHandler = (options) => async () => {
|
|
|
14445
14497
|
var createVoiceTurnQualityRoutes = (options) => {
|
|
14446
14498
|
const path = options.path ?? "/api/turn-quality";
|
|
14447
14499
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
14448
|
-
const routes = new
|
|
14500
|
+
const routes = new Elysia22({
|
|
14449
14501
|
name: options.name ?? "absolutejs-voice-turn-quality"
|
|
14450
14502
|
}).get(path, createVoiceTurnQualityJSONHandler(options));
|
|
14451
14503
|
if (htmlPath) {
|
|
@@ -14454,7 +14506,7 @@ var createVoiceTurnQualityRoutes = (options) => {
|
|
|
14454
14506
|
return routes;
|
|
14455
14507
|
};
|
|
14456
14508
|
// src/telephonyOutcome.ts
|
|
14457
|
-
import { Elysia as
|
|
14509
|
+
import { Elysia as Elysia23 } from "elysia";
|
|
14458
14510
|
var DEFAULT_COMPLETED_STATUSES = [
|
|
14459
14511
|
"answered",
|
|
14460
14512
|
"completed",
|
|
@@ -15104,7 +15156,7 @@ var createVoiceTelephonyWebhookHandler = (options = {}) => async (input) => {
|
|
|
15104
15156
|
var createVoiceTelephonyWebhookRoutes = (options = {}) => {
|
|
15105
15157
|
const path = options.path ?? "/api/voice/telephony/webhook";
|
|
15106
15158
|
const handler = createVoiceTelephonyWebhookHandler(options);
|
|
15107
|
-
return new
|
|
15159
|
+
return new Elysia23({
|
|
15108
15160
|
name: options.name ?? "absolutejs-voice-telephony-webhooks"
|
|
15109
15161
|
}).post(path, async ({ query, request }) => {
|
|
15110
15162
|
try {
|
|
@@ -15125,11 +15177,11 @@ var createVoiceTelephonyWebhookRoutes = (options = {}) => {
|
|
|
15125
15177
|
});
|
|
15126
15178
|
};
|
|
15127
15179
|
// src/phoneAgent.ts
|
|
15128
|
-
import { Elysia as
|
|
15180
|
+
import { Elysia as Elysia29 } from "elysia";
|
|
15129
15181
|
|
|
15130
15182
|
// src/telephony/plivo.ts
|
|
15131
15183
|
import { Buffer as Buffer5 } from "buffer";
|
|
15132
|
-
import { Elysia as
|
|
15184
|
+
import { Elysia as Elysia25 } from "elysia";
|
|
15133
15185
|
|
|
15134
15186
|
// src/telephony/contract.ts
|
|
15135
15187
|
var DEFAULT_REQUIREMENTS = [
|
|
@@ -15213,7 +15265,7 @@ var evaluateVoiceTelephonyContract = (input) => {
|
|
|
15213
15265
|
|
|
15214
15266
|
// src/telephony/twilio.ts
|
|
15215
15267
|
import { Buffer as Buffer4 } from "buffer";
|
|
15216
|
-
import { Elysia as
|
|
15268
|
+
import { Elysia as Elysia24 } from "elysia";
|
|
15217
15269
|
var TWILIO_MULAW_SAMPLE_RATE = 8000;
|
|
15218
15270
|
var VOICE_PCM_SAMPLE_RATE = 16000;
|
|
15219
15271
|
var escapeXml2 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
@@ -15243,7 +15295,7 @@ var resolveTwilioStreamParameters = async (parameters, input) => {
|
|
|
15243
15295
|
return parameters;
|
|
15244
15296
|
};
|
|
15245
15297
|
var joinUrlPath2 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
15246
|
-
var
|
|
15298
|
+
var escapeHtml26 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
15247
15299
|
var getWebhookVerificationUrl = (webhook, input) => {
|
|
15248
15300
|
if (!webhook?.verificationUrl) {
|
|
15249
15301
|
return;
|
|
@@ -15286,23 +15338,23 @@ var buildTwilioVoiceSetupStatus = async (options, input) => {
|
|
|
15286
15338
|
};
|
|
15287
15339
|
var renderTwilioVoiceSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
15288
15340
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Twilio setup</p>
|
|
15289
|
-
<h1>${
|
|
15341
|
+
<h1>${escapeHtml26(title)}</h1>
|
|
15290
15342
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
15291
15343
|
<section>
|
|
15292
15344
|
<h2>URLs</h2>
|
|
15293
15345
|
<ul>
|
|
15294
|
-
<li><strong>TwiML:</strong> <code>${
|
|
15295
|
-
<li><strong>Media stream:</strong> <code>${
|
|
15296
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
15346
|
+
<li><strong>TwiML:</strong> <code>${escapeHtml26(status.urls.twiml)}</code></li>
|
|
15347
|
+
<li><strong>Media stream:</strong> <code>${escapeHtml26(status.urls.stream)}</code></li>
|
|
15348
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml26(status.urls.webhook)}</code></li>
|
|
15297
15349
|
</ul>
|
|
15298
15350
|
</section>
|
|
15299
15351
|
<section>
|
|
15300
15352
|
<h2>Signing</h2>
|
|
15301
15353
|
<p>Mode: <code>${status.signing.mode}</code></p>
|
|
15302
|
-
${status.signing.verificationUrl ? `<p>Verification URL: <code>${
|
|
15354
|
+
${status.signing.verificationUrl ? `<p>Verification URL: <code>${escapeHtml26(status.signing.verificationUrl)}</code></p>` : ""}
|
|
15303
15355
|
</section>
|
|
15304
|
-
${status.missing.length ? `<section><h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
15305
|
-
${status.warnings.length ? `<section><h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
15356
|
+
${status.missing.length ? `<section><h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml26(name)}</code></li>`).join("")}</ul></section>` : ""}
|
|
15357
|
+
${status.warnings.length ? `<section><h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml26(warning)}</li>`).join("")}</ul></section>` : ""}
|
|
15306
15358
|
</main>`;
|
|
15307
15359
|
var extractTwilioStreamUrl = (twiml) => twiml.match(/<Stream\b[^>]*\surl="([^"]+)"/i)?.[1]?.replaceAll("&", "&");
|
|
15308
15360
|
var createSmokeCheck = (name, status, message, details) => ({
|
|
@@ -15313,20 +15365,20 @@ var createSmokeCheck = (name, status, message, details) => ({
|
|
|
15313
15365
|
});
|
|
15314
15366
|
var renderTwilioVoiceSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
15315
15367
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Twilio smoke test</p>
|
|
15316
|
-
<h1>${
|
|
15368
|
+
<h1>${escapeHtml26(title)}</h1>
|
|
15317
15369
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
15318
15370
|
<section>
|
|
15319
15371
|
<h2>Checks</h2>
|
|
15320
15372
|
<ul>
|
|
15321
|
-
${report.checks.map((check) => `<li><strong>${
|
|
15373
|
+
${report.checks.map((check) => `<li><strong>${escapeHtml26(check.name)}</strong>: ${escapeHtml26(check.status)}${check.message ? ` - ${escapeHtml26(check.message)}` : ""}</li>`).join("")}
|
|
15322
15374
|
</ul>
|
|
15323
15375
|
</section>
|
|
15324
15376
|
<section>
|
|
15325
15377
|
<h2>Observed URLs</h2>
|
|
15326
15378
|
<ul>
|
|
15327
|
-
<li><strong>TwiML:</strong> <code>${
|
|
15328
|
-
<li><strong>Stream:</strong> <code>${
|
|
15329
|
-
<li><strong>Webhook:</strong> <code>${
|
|
15379
|
+
<li><strong>TwiML:</strong> <code>${escapeHtml26(report.setup.urls.twiml)}</code></li>
|
|
15380
|
+
<li><strong>Stream:</strong> <code>${escapeHtml26(report.twiml?.streamUrl ?? report.setup.urls.stream)}</code></li>
|
|
15381
|
+
<li><strong>Webhook:</strong> <code>${escapeHtml26(report.setup.urls.webhook)}</code></li>
|
|
15330
15382
|
</ul>
|
|
15331
15383
|
</section>
|
|
15332
15384
|
</main>`;
|
|
@@ -15786,7 +15838,7 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
15786
15838
|
const smokePath = options.smoke?.path === false ? false : options.smoke?.path ?? "/api/voice/twilio/smoke";
|
|
15787
15839
|
const bridges = new WeakMap;
|
|
15788
15840
|
const webhookPolicy = options.webhook?.policy ?? options.outcomePolicy ?? createVoiceTelephonyOutcomePolicy();
|
|
15789
|
-
const app = new
|
|
15841
|
+
const app = new Elysia24({
|
|
15790
15842
|
name: options.name ?? "absolutejs-voice-twilio"
|
|
15791
15843
|
}).get(twimlPath, async ({ query, request }) => {
|
|
15792
15844
|
const streamUrl = await resolveTwilioStreamUrl(options, {
|
|
@@ -15923,7 +15975,7 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
15923
15975
|
|
|
15924
15976
|
// src/telephony/plivo.ts
|
|
15925
15977
|
var escapeXml3 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
15926
|
-
var
|
|
15978
|
+
var escapeHtml27 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
15927
15979
|
var joinUrlPath3 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
15928
15980
|
var resolveRequestOrigin2 = (request) => {
|
|
15929
15981
|
const url = new URL(request.url);
|
|
@@ -16174,21 +16226,21 @@ var buildPlivoVoiceSetupStatus = async (options, input) => {
|
|
|
16174
16226
|
};
|
|
16175
16227
|
var renderPlivoSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
16176
16228
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Plivo setup</p>
|
|
16177
|
-
<h1>${
|
|
16229
|
+
<h1>${escapeHtml27(title)}</h1>
|
|
16178
16230
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
16179
16231
|
<ul>
|
|
16180
|
-
<li><strong>Answer XML:</strong> <code>${
|
|
16181
|
-
<li><strong>Audio stream:</strong> <code>${
|
|
16182
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
16232
|
+
<li><strong>Answer XML:</strong> <code>${escapeHtml27(status.urls.answer)}</code></li>
|
|
16233
|
+
<li><strong>Audio stream:</strong> <code>${escapeHtml27(status.urls.stream)}</code></li>
|
|
16234
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml27(status.urls.webhook)}</code></li>
|
|
16183
16235
|
</ul>
|
|
16184
|
-
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
16185
|
-
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
16236
|
+
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml27(name)}</code></li>`).join("")}</ul>` : ""}
|
|
16237
|
+
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml27(warning)}</li>`).join("")}</ul>` : ""}
|
|
16186
16238
|
</main>`;
|
|
16187
16239
|
var renderPlivoSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
16188
16240
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Plivo smoke test</p>
|
|
16189
|
-
<h1>${
|
|
16241
|
+
<h1>${escapeHtml27(title)}</h1>
|
|
16190
16242
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
16191
|
-
<ul>${report.checks.map((check) => `<li><strong>${
|
|
16243
|
+
<ul>${report.checks.map((check) => `<li><strong>${escapeHtml27(check.name)}</strong>: ${escapeHtml27(check.status)}${check.message ? ` - ${escapeHtml27(check.message)}` : ""}</li>`).join("")}</ul>
|
|
16192
16244
|
</main>`;
|
|
16193
16245
|
var runPlivoSmokeTest = async (input) => {
|
|
16194
16246
|
const setup = await buildPlivoVoiceSetupStatus(input.options, input);
|
|
@@ -16283,7 +16335,7 @@ var createPlivoVoiceRoutes = (options = {}) => {
|
|
|
16283
16335
|
request: input.request
|
|
16284
16336
|
}) : verificationUrl ?? input.request.url
|
|
16285
16337
|
}) : undefined);
|
|
16286
|
-
const app = new
|
|
16338
|
+
const app = new Elysia25({
|
|
16287
16339
|
name: options.name ?? "absolutejs-voice-plivo"
|
|
16288
16340
|
}).get(answerPath, async ({ query, request }) => {
|
|
16289
16341
|
const streamUrl = await resolvePlivoStreamUrl(options, {
|
|
@@ -16394,9 +16446,9 @@ var createPlivoVoiceRoutes = (options = {}) => {
|
|
|
16394
16446
|
|
|
16395
16447
|
// src/telephony/telnyx.ts
|
|
16396
16448
|
import { Buffer as Buffer6 } from "buffer";
|
|
16397
|
-
import { Elysia as
|
|
16449
|
+
import { Elysia as Elysia26 } from "elysia";
|
|
16398
16450
|
var escapeXml4 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
16399
|
-
var
|
|
16451
|
+
var escapeHtml28 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
16400
16452
|
var joinUrlPath4 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
16401
16453
|
var resolveRequestOrigin3 = (request) => {
|
|
16402
16454
|
const url = new URL(request.url);
|
|
@@ -16597,21 +16649,21 @@ var buildTelnyxVoiceSetupStatus = async (options, input) => {
|
|
|
16597
16649
|
};
|
|
16598
16650
|
var renderTelnyxSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
16599
16651
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Telnyx setup</p>
|
|
16600
|
-
<h1>${
|
|
16652
|
+
<h1>${escapeHtml28(title)}</h1>
|
|
16601
16653
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
16602
16654
|
<ul>
|
|
16603
|
-
<li><strong>TeXML:</strong> <code>${
|
|
16604
|
-
<li><strong>Media stream:</strong> <code>${
|
|
16605
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
16655
|
+
<li><strong>TeXML:</strong> <code>${escapeHtml28(status.urls.texml)}</code></li>
|
|
16656
|
+
<li><strong>Media stream:</strong> <code>${escapeHtml28(status.urls.stream)}</code></li>
|
|
16657
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml28(status.urls.webhook)}</code></li>
|
|
16606
16658
|
</ul>
|
|
16607
|
-
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
16608
|
-
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
16659
|
+
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml28(name)}</code></li>`).join("")}</ul>` : ""}
|
|
16660
|
+
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml28(warning)}</li>`).join("")}</ul>` : ""}
|
|
16609
16661
|
</main>`;
|
|
16610
16662
|
var renderTelnyxSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
16611
16663
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Telnyx smoke test</p>
|
|
16612
|
-
<h1>${
|
|
16664
|
+
<h1>${escapeHtml28(title)}</h1>
|
|
16613
16665
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
16614
|
-
<ul>${report.checks.map((check) => `<li><strong>${
|
|
16666
|
+
<ul>${report.checks.map((check) => `<li><strong>${escapeHtml28(check.name)}</strong>: ${escapeHtml28(check.status)}${check.message ? ` - ${escapeHtml28(check.message)}` : ""}</li>`).join("")}</ul>
|
|
16615
16667
|
</main>`;
|
|
16616
16668
|
var runTelnyxSmokeTest = async (input) => {
|
|
16617
16669
|
const setup = await buildTelnyxVoiceSetupStatus(input.options, input);
|
|
@@ -16705,7 +16757,7 @@ var createTelnyxVoiceRoutes = (options = {}) => {
|
|
|
16705
16757
|
publicKey: options.webhook?.publicKey,
|
|
16706
16758
|
toleranceSeconds: options.webhook?.toleranceSeconds
|
|
16707
16759
|
}) : undefined);
|
|
16708
|
-
const app = new
|
|
16760
|
+
const app = new Elysia26({
|
|
16709
16761
|
name: options.name ?? "absolutejs-voice-telnyx"
|
|
16710
16762
|
}).get(texmlPath, async ({ query, request }) => {
|
|
16711
16763
|
const streamUrl = await resolveTelnyxStreamUrl(options, {
|
|
@@ -16815,8 +16867,8 @@ var createTelnyxVoiceRoutes = (options = {}) => {
|
|
|
16815
16867
|
};
|
|
16816
16868
|
|
|
16817
16869
|
// src/telephony/matrix.ts
|
|
16818
|
-
import { Elysia as
|
|
16819
|
-
var
|
|
16870
|
+
import { Elysia as Elysia27 } from "elysia";
|
|
16871
|
+
var escapeHtml29 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
16820
16872
|
var labelForProvider = (provider) => provider.split("-").map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`).join(" ");
|
|
16821
16873
|
var resolveEntryStatus = (contract, setup, smoke) => {
|
|
16822
16874
|
if (!contract.pass || !setup.ready || smoke?.pass === false) {
|
|
@@ -16877,13 +16929,13 @@ var badgeStyles = {
|
|
|
16877
16929
|
};
|
|
16878
16930
|
var renderVoiceTelephonyCarrierMatrixHTML = (matrix, options = {}) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 1040px; margin: 40px auto; padding: 0 20px; color: #172033;">
|
|
16879
16931
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Carrier matrix</p>
|
|
16880
|
-
<h1 style="font-size: 34px; margin: 0 0 8px;">${
|
|
16932
|
+
<h1 style="font-size: 34px; margin: 0 0 8px;">${escapeHtml29(options.title ?? "AbsoluteJS Voice Carrier Matrix")}</h1>
|
|
16881
16933
|
<p style="color:#52606d; margin: 0 0 24px;">${matrix.summary.ready}/${matrix.summary.providers} ready, ${matrix.summary.contractsPassing}/${matrix.summary.providers} contract passing, ${matrix.summary.smokePassing}/${matrix.summary.providers} smoke passing.</p>
|
|
16882
16934
|
<section style="display:grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px;">
|
|
16883
16935
|
${matrix.entries.map((entry) => `<article style="border:1px solid #d9e2ec; border-radius:18px; padding:18px; background:#fff; box-shadow:0 18px 48px rgba(15,23,42,.08);">
|
|
16884
16936
|
<div style="display:flex; justify-content:space-between; gap:12px; align-items:center;">
|
|
16885
|
-
<h2 style="margin:0; font-size:20px;">${
|
|
16886
|
-
<span style="border:1px solid; border-radius:999px; padding:4px 10px; font-size:12px; font-weight:700; ${badgeStyles[entry.status]}">${
|
|
16937
|
+
<h2 style="margin:0; font-size:20px;">${escapeHtml29(entry.name)}</h2>
|
|
16938
|
+
<span style="border:1px solid; border-radius:999px; padding:4px 10px; font-size:12px; font-weight:700; ${badgeStyles[entry.status]}">${escapeHtml29(entry.status.toUpperCase())}</span>
|
|
16887
16939
|
</div>
|
|
16888
16940
|
<dl style="display:grid; grid-template-columns: 1fr 1fr; gap:8px 12px; margin:16px 0;">
|
|
16889
16941
|
<dt style="color:#64748b;">Setup</dt><dd style="margin:0; font-weight:700;">${entry.ready ? "Ready" : "Needs attention"}</dd>
|
|
@@ -16891,15 +16943,15 @@ ${matrix.entries.map((entry) => `<article style="border:1px solid #d9e2ec; borde
|
|
|
16891
16943
|
<dt style="color:#64748b;">Smoke</dt><dd style="margin:0; font-weight:700;">${entry.smoke ? entry.smoke.pass ? "Pass" : "Fail" : "Missing"}</dd>
|
|
16892
16944
|
<dt style="color:#64748b;">Contract</dt><dd style="margin:0; font-weight:700;">${entry.contract.pass ? "Pass" : "Fail"}</dd>
|
|
16893
16945
|
</dl>
|
|
16894
|
-
<p style="margin:0 0 8px; color:#475569;"><strong>Stream:</strong> <code>${
|
|
16895
|
-
<p style="margin:0 0 12px; color:#475569;"><strong>Webhook:</strong> <code>${
|
|
16896
|
-
${entry.issues.length ? `<ul style="margin:12px 0 0; padding-left:18px;">${entry.issues.map((issue) => `<li>${
|
|
16946
|
+
<p style="margin:0 0 8px; color:#475569;"><strong>Stream:</strong> <code>${escapeHtml29(entry.setup.urls.stream || "missing")}</code></p>
|
|
16947
|
+
<p style="margin:0 0 12px; color:#475569;"><strong>Webhook:</strong> <code>${escapeHtml29(entry.setup.urls.webhook || "missing")}</code></p>
|
|
16948
|
+
${entry.issues.length ? `<ul style="margin:12px 0 0; padding-left:18px;">${entry.issues.map((issue) => `<li>${escapeHtml29(issue.severity)}: ${escapeHtml29(issue.message)}</li>`).join("")}</ul>` : '<p style="margin:12px 0 0; color:#166534;">No contract issues.</p>'}
|
|
16897
16949
|
</article>`).join("")}
|
|
16898
16950
|
</section>
|
|
16899
16951
|
</main>`;
|
|
16900
16952
|
var createVoiceTelephonyCarrierMatrixRoutes = (options) => {
|
|
16901
16953
|
const path = options.path ?? "/api/voice/telephony/carriers";
|
|
16902
|
-
return new
|
|
16954
|
+
return new Elysia27({
|
|
16903
16955
|
name: options.name ?? "absolutejs-voice-telephony-carrier-matrix"
|
|
16904
16956
|
}).get(path, async ({ query, request }) => {
|
|
16905
16957
|
const providers = await options.load({ query, request });
|
|
@@ -16921,7 +16973,7 @@ var createVoiceTelephonyCarrierMatrixRoutes = (options) => {
|
|
|
16921
16973
|
};
|
|
16922
16974
|
|
|
16923
16975
|
// src/phoneAgentProductionSmoke.ts
|
|
16924
|
-
import { Elysia as
|
|
16976
|
+
import { Elysia as Elysia28 } from "elysia";
|
|
16925
16977
|
var defaultRequirements = [
|
|
16926
16978
|
"media-started",
|
|
16927
16979
|
"transcript",
|
|
@@ -16929,7 +16981,7 @@ var defaultRequirements = [
|
|
|
16929
16981
|
"lifecycle-outcome",
|
|
16930
16982
|
"no-session-error"
|
|
16931
16983
|
];
|
|
16932
|
-
var
|
|
16984
|
+
var escapeHtml30 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
16933
16985
|
var payloadType = (event) => typeof event.payload.type === "string" ? event.payload.type : undefined;
|
|
16934
16986
|
var hasTextPayload = (event) => ["text", "assistantText", "transcript"].some((key) => {
|
|
16935
16987
|
const value = event.payload[key];
|
|
@@ -17038,10 +17090,10 @@ var resolveHandlerOptions = async (options, input) => ({
|
|
|
17038
17090
|
});
|
|
17039
17091
|
var renderVoicePhoneAgentProductionSmokeHTML = (report, options = {}) => {
|
|
17040
17092
|
const title = options.title ?? "AbsoluteJS Voice Phone Smoke Contract";
|
|
17041
|
-
const issues = report.issues.map((issue) => `<li><strong>${
|
|
17042
|
-
const outcomes = report.observed.lifecycleOutcomes.map((outcome) => `<span class="pill">${
|
|
17043
|
-
const requirements = report.required.map((requirement) => `<span class="pill">${
|
|
17044
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
17093
|
+
const issues = report.issues.map((issue) => `<li><strong>${escapeHtml30(issue.requirement)}</strong>: ${escapeHtml30(issue.message)}</li>`).join("");
|
|
17094
|
+
const outcomes = report.observed.lifecycleOutcomes.map((outcome) => `<span class="pill">${escapeHtml30(outcome)}</span>`).join("");
|
|
17095
|
+
const requirements = report.required.map((requirement) => `<span class="pill">${escapeHtml30(requirement)}</span>`).join("");
|
|
17096
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml30(title)}</title><style>body{background:#0e141b;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1050px;padding:32px}.hero,.panel{background:#151d26;border:1px solid #283544;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12))}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.status{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.pass{color:#86efac}.fail{color:#fca5a5}.grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.metric{background:#0f151d;border:1px solid #283544;border-radius:16px;padding:14px}.metric strong{display:block;font-size:1.8rem}.pill{background:#0f151d;border:1px solid #3f3f46;border-radius:999px;display:inline-flex;margin:4px;padding:7px 10px}.issues{color:#fca5a5}code{color:#fde68a}@media(max-width:720px){main{padding:18px}}</style></head><body><main><section class="hero"><p class="eyebrow">Phone agent production smoke</p><h1>${escapeHtml30(title)}</h1><p class="status ${report.pass ? "pass" : "fail"}">${report.pass ? "PASS" : "FAIL"}</p><p>Contract <code>${escapeHtml30(report.contractId)}</code>${report.provider ? ` for <code>${escapeHtml30(report.provider)}</code>` : ""}${report.sessionId ? ` on session <code>${escapeHtml30(report.sessionId)}</code>` : ""}.</p></section><section class="panel"><h2>Observed Trace Evidence</h2><div class="grid"><div class="metric"><span>Media starts</span><strong>${String(report.observed.mediaStarts)}</strong></div><div class="metric"><span>Transcripts</span><strong>${String(report.observed.transcripts)}</strong></div><div class="metric"><span>Assistant responses</span><strong>${String(report.observed.assistantResponses)}</strong></div><div class="metric"><span>Session errors</span><strong>${String(report.observed.sessionErrors)}</strong></div></div><p>${outcomes || '<span class="pill">No lifecycle outcome</span>'}</p></section><section class="panel"><h2>Requirements</h2><p>${requirements}</p>${issues ? `<ul class="issues">${issues}</ul>` : '<p class="pass">All required phone-agent smoke evidence is present.</p>'}</section></main></body></html>`;
|
|
17045
17097
|
};
|
|
17046
17098
|
var createVoicePhoneAgentProductionSmokeJSONHandler = (options) => async ({
|
|
17047
17099
|
query,
|
|
@@ -17064,7 +17116,7 @@ var createVoicePhoneAgentProductionSmokeHTMLHandler = (options) => async ({
|
|
|
17064
17116
|
var createVoicePhoneAgentProductionSmokeRoutes = (options) => {
|
|
17065
17117
|
const path = options.path ?? "/api/voice/phone/smoke-contract";
|
|
17066
17118
|
const htmlPath = options.htmlPath === undefined ? "/voice/phone/smoke-contract" : options.htmlPath;
|
|
17067
|
-
const routes = new
|
|
17119
|
+
const routes = new Elysia28({
|
|
17068
17120
|
name: options.name ?? "absolutejs-voice-phone-smoke-contract"
|
|
17069
17121
|
}).get(path, createVoicePhoneAgentProductionSmokeJSONHandler(options));
|
|
17070
17122
|
if (htmlPath) {
|
|
@@ -17107,7 +17159,7 @@ var PHONE_AGENT_LIFECYCLE_STAGES = [
|
|
|
17107
17159
|
"completed",
|
|
17108
17160
|
"failed"
|
|
17109
17161
|
];
|
|
17110
|
-
var
|
|
17162
|
+
var escapeHtml31 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
17111
17163
|
var loadRouteJson = async (input) => {
|
|
17112
17164
|
const response = await input.app.handle(new Request(new URL(input.path, input.origin).toString(), {
|
|
17113
17165
|
headers: {
|
|
@@ -17167,18 +17219,18 @@ var renderVoicePhoneAgentSetupHTML = (report) => {
|
|
|
17167
17219
|
const entry = report.matrix?.entries.find((candidate) => candidate.provider === carrier.provider && (candidate.name === carrier.name || candidate.name === (carrier.name ?? carrier.provider)));
|
|
17168
17220
|
const urls = entry?.setup.urls;
|
|
17169
17221
|
const primaryUrl = carrier.provider === "plivo" ? urls?.twiml : urls?.twiml;
|
|
17170
|
-
return `<tr><td>${
|
|
17222
|
+
return `<tr><td>${escapeHtml31(carrier.name ?? carrier.provider)}</td><td>${escapeHtml31(carrier.provider)}</td><td><code>${escapeHtml31(carrier.setupPath || "disabled")}</code></td><td><code>${escapeHtml31(carrier.smokePath || "disabled")}</code></td><td>${entry ? `<span class="${escapeHtml31(entry.status)}">${escapeHtml31(entry.status.toUpperCase())}</span>` : "unknown"}</td><td>${primaryUrl ? `<code>${escapeHtml31(primaryUrl)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.webhook ? `<code>${escapeHtml31(urls.webhook)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.stream ? `<code>${escapeHtml31(urls.stream)}</code>` : '<span class="muted">missing</span>'}</td></tr>`;
|
|
17171
17223
|
}).join("");
|
|
17172
|
-
const stageList = report.lifecycleStages.map((stage) => `<li><code>${
|
|
17224
|
+
const stageList = report.lifecycleStages.map((stage) => `<li><code>${escapeHtml31(stage)}</code></li>`).join("");
|
|
17173
17225
|
const checklist = report.carriers.map((carrier) => {
|
|
17174
17226
|
const entry = report.matrix?.entries.find((candidate) => candidate.provider === carrier.provider && (candidate.name === carrier.name || candidate.name === (carrier.name ?? carrier.provider)));
|
|
17175
17227
|
const urls = entry?.setup.urls;
|
|
17176
17228
|
const answerLabel = carrier.provider === "telnyx" ? "TeXML URL" : carrier.provider === "plivo" ? "Answer URL" : "TwiML URL";
|
|
17177
17229
|
const answerUrl = urls?.twiml;
|
|
17178
|
-
const issueList = entry?.issues.map((issue) => `<li>${
|
|
17179
|
-
return `<article><h3>${
|
|
17230
|
+
const issueList = entry?.issues.map((issue) => `<li>${escapeHtml31(issue.severity)}: ${escapeHtml31(issue.message)}</li>`).join("") ?? "";
|
|
17231
|
+
return `<article><h3>${escapeHtml31(carrier.name ?? carrier.provider)}</h3><ol><li>Set ${escapeHtml31(answerLabel)} to <code>${escapeHtml31(answerUrl ?? "missing")}</code>.</li><li>Set status webhook to <code>${escapeHtml31(urls?.webhook ?? "missing")}</code>.</li><li>Allow media stream URL <code>${escapeHtml31(urls?.stream ?? "missing")}</code>.</li><li>Open setup: ${carrier.setupPath ? `<a href="${escapeHtml31(carrier.setupPath)}?format=html">${escapeHtml31(carrier.setupPath)}</a>` : '<span class="muted">disabled</span>'}.</li><li>Run smoke: ${carrier.smokePath ? `<a href="${escapeHtml31(carrier.smokePath)}?format=html">${escapeHtml31(carrier.smokePath)}</a>` : '<span class="muted">disabled</span>'}.</li>${report.productionSmokePath ? `<li>Certify production smoke traces: <a href="${escapeHtml31(report.productionSmokePath.replace("/api/", "/"))}?sessionId=">${escapeHtml31(report.productionSmokePath)}</a>.</li>` : ""}</ol>${issueList ? `<ul class="issues">${issueList}</ul>` : '<p class="pass">No carrier contract issues.</p>'}</article>`;
|
|
17180
17232
|
}).join("");
|
|
17181
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
17233
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml31(report.title)}</title><style>body{background:#10151c;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;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.3rem,6vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.badge{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;padding:8px 12px}.pass{color:#86efac}.fail{color:#fca5a5}.warn{color:#fde68a}.muted{color:#aab5c0}table{background:#151d27;border:1px solid #283544;border-collapse:collapse;border-radius:18px;display:block;overflow:auto;width:100%}td,th{border-bottom:1px solid #283544;padding:12px;text-align:left;vertical-align:top}code{color:#fde68a;overflow-wrap:anywhere}.checklist{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));margin:18px 0}.checklist article{background:#151d27;border:1px solid #283544;border-radius:18px;padding:18px}.checklist ol{padding-left:20px}.issues{color:#fca5a5}.stages{display:grid;gap:8px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));padding-left:18px}a{color:#5eead4}</style></head><body><main><section class="hero"><p class="eyebrow">Phone agent setup</p><h1>${escapeHtml31(report.title)}</h1><p>One self-hosted entrypoint for carrier routes, setup reports, smoke checks, and normalized call lifecycle stages.</p><p class="badge ${report.ready ? "pass" : "fail"}">Ready: ${String(report.ready)}</p>${report.matrixPath ? `<p><a href="${escapeHtml31(report.matrixPath)}?format=html">Open carrier matrix</a></p>` : ""}</section><h2>Carrier Setup Checklist</h2><section class="checklist">${checklist}</section><h2>Carrier URLs</h2><table><thead><tr><th>Name</th><th>Provider</th><th>Setup</th><th>Smoke</th><th>Status</th><th>Answer/TwiML/TeXML</th><th>Webhook</th><th>Stream</th></tr></thead><tbody>${carrierRows}</tbody></table><h2>Lifecycle Schema</h2><ul class="stages">${stageList}</ul></main></body></html>`;
|
|
17182
17234
|
};
|
|
17183
17235
|
var createVoicePhoneAgent = (options) => {
|
|
17184
17236
|
const carrierSummaries = options.carriers.map((carrier) => ({
|
|
@@ -17187,7 +17239,7 @@ var createVoicePhoneAgent = (options) => {
|
|
|
17187
17239
|
setupPath: resolveSetupPath(carrier),
|
|
17188
17240
|
smokePath: resolveSmokePath(carrier)
|
|
17189
17241
|
}));
|
|
17190
|
-
const app = new
|
|
17242
|
+
const app = new Elysia29({
|
|
17191
17243
|
name: options.name ?? "absolutejs-voice-phone-agent"
|
|
17192
17244
|
});
|
|
17193
17245
|
for (const carrier of options.carriers) {
|
|
@@ -19199,8 +19251,8 @@ var createOpenAIVoiceTTS = (options) => {
|
|
|
19199
19251
|
};
|
|
19200
19252
|
};
|
|
19201
19253
|
// src/providerCapabilities.ts
|
|
19202
|
-
import { Elysia as
|
|
19203
|
-
var
|
|
19254
|
+
import { Elysia as Elysia30 } from "elysia";
|
|
19255
|
+
var escapeHtml32 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
19204
19256
|
var fromProviderList = (kind, providers, options) => (providers ?? []).map((provider) => ({
|
|
19205
19257
|
configured: true,
|
|
19206
19258
|
features: options.features?.[provider],
|
|
@@ -19263,27 +19315,27 @@ var summarizeVoiceProviderCapabilities = async (options) => {
|
|
|
19263
19315
|
var renderVoiceProviderCapabilityHTML = (report, options = {}) => {
|
|
19264
19316
|
const title = options.title ?? "Voice Provider Capabilities";
|
|
19265
19317
|
const cards = report.capabilities.map((capability) => {
|
|
19266
|
-
const features = (capability.features ?? []).map((feature) => `<span class="pill">${
|
|
19267
|
-
return `<article class="card ${
|
|
19318
|
+
const features = (capability.features ?? []).map((feature) => `<span class="pill">${escapeHtml32(feature)}</span>`).join("");
|
|
19319
|
+
return `<article class="card ${escapeHtml32(capability.status)}">
|
|
19268
19320
|
<div class="card-header">
|
|
19269
19321
|
<div>
|
|
19270
|
-
<p class="eyebrow">${
|
|
19271
|
-
<h2>${
|
|
19322
|
+
<p class="eyebrow">${escapeHtml32(capability.kind)}</p>
|
|
19323
|
+
<h2>${escapeHtml32(capability.label ?? capability.provider)}</h2>
|
|
19272
19324
|
</div>
|
|
19273
|
-
<strong>${
|
|
19325
|
+
<strong>${escapeHtml32(capability.status)}</strong>
|
|
19274
19326
|
</div>
|
|
19275
|
-
${capability.description ? `<p>${
|
|
19327
|
+
${capability.description ? `<p>${escapeHtml32(capability.description)}</p>` : ""}
|
|
19276
19328
|
<dl>
|
|
19277
19329
|
<div><dt>Configured</dt><dd>${capability.configured ? "yes" : "no"}</dd></div>
|
|
19278
19330
|
<div><dt>Selected</dt><dd>${capability.selected ? "yes" : "no"}</dd></div>
|
|
19279
|
-
<div><dt>Model</dt><dd>${
|
|
19331
|
+
<div><dt>Model</dt><dd>${escapeHtml32(capability.model ?? "default")}</dd></div>
|
|
19280
19332
|
<div><dt>Runs</dt><dd>${String(capability.health?.runCount ?? 0)}</dd></div>
|
|
19281
19333
|
<div><dt>Errors</dt><dd>${String(capability.health?.errorCount ?? 0)}</dd></div>
|
|
19282
19334
|
</dl>
|
|
19283
19335
|
${features ? `<div class="features">${features}</div>` : ""}
|
|
19284
19336
|
</article>`;
|
|
19285
19337
|
}).join("");
|
|
19286
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
19338
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml32(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.card{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(14,165,233,.16),rgba(34,197,94,.12))}.eyebrow{color:#7dd3fc;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0 1rem}.summary,.features{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.grid{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}.card-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.selected,.healthy{color:#86efac}.unconfigured,.degraded,.rate-limited,.suppressed{color:#fca5a5}.idle,.recoverable{color:#fde68a}dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr))}dt{color:#a8b0b8;font-size:.8rem}dd{margin:0}@media(max-width:800px){main{padding:18px}.card-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Provider Discovery</p><h1>${escapeHtml32(title)}</h1><div class="summary"><span class="pill">${String(report.configured)} configured</span><span class="pill">${String(report.selected)} selected</span><span class="pill">${String(report.unconfigured)} missing</span><span class="pill">${String(report.total)} total</span></div></section><section class="grid">${cards || '<article class="card"><p>No provider capabilities configured.</p></article>'}</section></main></body></html>`;
|
|
19287
19339
|
};
|
|
19288
19340
|
var createVoiceProviderCapabilityJSONHandler = (options) => async () => summarizeVoiceProviderCapabilities(options);
|
|
19289
19341
|
var createVoiceProviderCapabilityHTMLHandler = (options) => async () => {
|
|
@@ -19300,7 +19352,7 @@ var createVoiceProviderCapabilityHTMLHandler = (options) => async () => {
|
|
|
19300
19352
|
var createVoiceProviderCapabilityRoutes = (options) => {
|
|
19301
19353
|
const path = options.path ?? "/api/provider-capabilities";
|
|
19302
19354
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
19303
|
-
const routes = new
|
|
19355
|
+
const routes = new Elysia30({
|
|
19304
19356
|
name: options.name ?? "absolutejs-voice-provider-capabilities"
|
|
19305
19357
|
}).get(path, createVoiceProviderCapabilityJSONHandler(options));
|
|
19306
19358
|
if (htmlPath) {
|
|
@@ -19309,8 +19361,8 @@ var createVoiceProviderCapabilityRoutes = (options) => {
|
|
|
19309
19361
|
return routes;
|
|
19310
19362
|
};
|
|
19311
19363
|
// src/resilienceRoutes.ts
|
|
19312
|
-
import { Elysia as
|
|
19313
|
-
var
|
|
19364
|
+
import { Elysia as Elysia31 } from "elysia";
|
|
19365
|
+
var escapeHtml33 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
19314
19366
|
var getString11 = (value) => typeof value === "string" ? value : undefined;
|
|
19315
19367
|
var getNumber6 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
19316
19368
|
var getBoolean2 = (value) => value === true;
|
|
@@ -19457,13 +19509,13 @@ var summarizeRoutingEvents = (events) => {
|
|
|
19457
19509
|
};
|
|
19458
19510
|
var renderProviderCards = (title, providers) => {
|
|
19459
19511
|
if (providers.length === 0) {
|
|
19460
|
-
return `<p class="muted">No ${
|
|
19512
|
+
return `<p class="muted">No ${escapeHtml33(title)} provider health yet.</p>`;
|
|
19461
19513
|
}
|
|
19462
19514
|
return `<div class="provider-grid">${providers.map((provider) => `
|
|
19463
|
-
<article class="card provider ${
|
|
19515
|
+
<article class="card provider ${escapeHtml33(provider.status)}">
|
|
19464
19516
|
<div class="card-header">
|
|
19465
|
-
<strong>${
|
|
19466
|
-
<span>${
|
|
19517
|
+
<strong>${escapeHtml33(provider.provider)}</strong>
|
|
19518
|
+
<span>${escapeHtml33(provider.status)}${provider.recommended ? " \xB7 recommended" : ""}</span>
|
|
19467
19519
|
</div>
|
|
19468
19520
|
<dl>
|
|
19469
19521
|
<div><dt>Runs</dt><dd>${provider.runCount}</dd></div>
|
|
@@ -19472,7 +19524,7 @@ var renderProviderCards = (title, providers) => {
|
|
|
19472
19524
|
<div><dt>Timeouts</dt><dd>${provider.timeoutCount}</dd></div>
|
|
19473
19525
|
<div><dt>Fallbacks</dt><dd>${provider.fallbackCount}</dd></div>
|
|
19474
19526
|
</dl>
|
|
19475
|
-
${provider.lastError ? `<p class="muted">${
|
|
19527
|
+
${provider.lastError ? `<p class="muted">${escapeHtml33(provider.lastError)}</p>` : ""}
|
|
19476
19528
|
</article>
|
|
19477
19529
|
`).join("")}</div>`;
|
|
19478
19530
|
};
|
|
@@ -19481,24 +19533,24 @@ var renderTimeline2 = (events) => {
|
|
|
19481
19533
|
return '<p class="muted">No provider routing events yet. Run the app or simulate provider failover.</p>';
|
|
19482
19534
|
}
|
|
19483
19535
|
return `<div class="timeline">${events.slice(0, 40).map((event) => `
|
|
19484
|
-
<article class="card event ${
|
|
19536
|
+
<article class="card event ${escapeHtml33(event.status ?? "unknown")}">
|
|
19485
19537
|
<div class="card-header">
|
|
19486
|
-
<strong>${
|
|
19538
|
+
<strong>${escapeHtml33(event.kind.toUpperCase())} ${escapeHtml33(event.operation ?? "generate")}</strong>
|
|
19487
19539
|
<span>${new Date(event.at).toLocaleString()}</span>
|
|
19488
19540
|
</div>
|
|
19489
19541
|
<p>
|
|
19490
|
-
<span class="pill">${
|
|
19491
|
-
<span class="pill">provider: ${
|
|
19492
|
-
${event.fallbackProvider ? `<span class="pill">fallback: ${
|
|
19542
|
+
<span class="pill">${escapeHtml33(event.status ?? "unknown")}</span>
|
|
19543
|
+
<span class="pill">provider: ${escapeHtml33(event.provider ?? "unknown")}</span>
|
|
19544
|
+
${event.fallbackProvider ? `<span class="pill">fallback: ${escapeHtml33(event.fallbackProvider)}</span>` : ""}
|
|
19493
19545
|
${event.timedOut ? '<span class="pill danger">timed out</span>' : ""}
|
|
19494
19546
|
</p>
|
|
19495
19547
|
<dl>
|
|
19496
19548
|
<div><dt>Attempt</dt><dd>${event.attempt ?? 0}</dd></div>
|
|
19497
19549
|
<div><dt>Elapsed</dt><dd>${event.elapsedMs ?? 0}ms</dd></div>
|
|
19498
19550
|
<div><dt>Budget</dt><dd>${event.latencyBudgetMs ?? 0}ms</dd></div>
|
|
19499
|
-
<div><dt>Session</dt><dd>${
|
|
19551
|
+
<div><dt>Session</dt><dd>${escapeHtml33(event.sessionId)}</dd></div>
|
|
19500
19552
|
</dl>
|
|
19501
|
-
${event.error ? `<p class="muted">${
|
|
19553
|
+
${event.error ? `<p class="muted">${escapeHtml33(event.error)}</p>` : ""}
|
|
19502
19554
|
</article>
|
|
19503
19555
|
`).join("")}</div>`;
|
|
19504
19556
|
};
|
|
@@ -19508,9 +19560,9 @@ var renderSessionKind = (kind, summary) => {
|
|
|
19508
19560
|
const status = latest?.status ?? "idle";
|
|
19509
19561
|
const fallback = latest?.fallbackProvider && latest.fallbackProvider !== provider ? ` -> ${latest.fallbackProvider}` : "";
|
|
19510
19562
|
return `<div>
|
|
19511
|
-
<dt>${
|
|
19512
|
-
<dd>${
|
|
19513
|
-
<small>${
|
|
19563
|
+
<dt>${escapeHtml33(kind.toUpperCase())}</dt>
|
|
19564
|
+
<dd>${escapeHtml33(provider)}${escapeHtml33(fallback)}</dd>
|
|
19565
|
+
<small>${escapeHtml33(status)} \xB7 ${summary.runCount} event${summary.runCount === 1 ? "" : "s"} \xB7 ${summary.errorCount} error${summary.errorCount === 1 ? "" : "s"} \xB7 ${summary.fallbackCount} fallback${summary.fallbackCount === 1 ? "" : "s"}</small>
|
|
19514
19566
|
</div>`;
|
|
19515
19567
|
};
|
|
19516
19568
|
var renderSessionSummaries = (sessions) => {
|
|
@@ -19518,10 +19570,10 @@ var renderSessionSummaries = (sessions) => {
|
|
|
19518
19570
|
return '<p class="muted">No call-level routing summaries yet. Run a voice session or provider simulation.</p>';
|
|
19519
19571
|
}
|
|
19520
19572
|
return `<div class="session-grid">${sessions.slice(0, 12).map((session) => `
|
|
19521
|
-
<article class="card session ${
|
|
19573
|
+
<article class="card session ${escapeHtml33(session.status)}">
|
|
19522
19574
|
<div class="card-header">
|
|
19523
|
-
<strong>${
|
|
19524
|
-
<span>${
|
|
19575
|
+
<strong>${escapeHtml33(session.sessionId)}</strong>
|
|
19576
|
+
<span>${escapeHtml33(session.status)}</span>
|
|
19525
19577
|
</div>
|
|
19526
19578
|
<p>
|
|
19527
19579
|
<span class="pill">${session.eventCount} routing events</span>
|
|
@@ -19548,26 +19600,26 @@ var renderSimulationControls = (kind, simulation) => {
|
|
|
19548
19600
|
const pathPrefix = simulation.pathPrefix ?? `/api/${kind}-simulate`;
|
|
19549
19601
|
const failureProviders = simulation.failureProviders ?? configuredProviders.map(({ provider }) => provider);
|
|
19550
19602
|
const canFail = (provider) => configuredProviders.some((entry) => entry.provider === provider) && (!simulation.fallbackRequiredProvider || configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider));
|
|
19551
|
-
return `<div class="simulate-panel" data-sim-kind="${kind}" data-sim-prefix="${
|
|
19552
|
-
<p class="muted">${
|
|
19603
|
+
return `<div class="simulate-panel" data-sim-kind="${kind}" data-sim-prefix="${escapeHtml33(pathPrefix)}">
|
|
19604
|
+
<p class="muted">${escapeHtml33(simulation.failureMessage ?? `Simulate ${kind.toUpperCase()} provider failure without changing provider credentials.`)}</p>
|
|
19553
19605
|
<div class="simulate-actions">
|
|
19554
|
-
${failureProviders.map((provider) => `<button type="button" data-provider-fail="${
|
|
19555
|
-
${configuredProviders.map((provider) => `<button type="button" data-provider-recover="${
|
|
19606
|
+
${failureProviders.map((provider) => `<button type="button" data-provider-fail="${escapeHtml33(provider)}"${canFail(provider) ? "" : " disabled"}>Simulate ${escapeHtml33(provider)} ${kind.toUpperCase()} failure</button>`).join("")}
|
|
19607
|
+
${configuredProviders.map((provider) => `<button type="button" data-provider-recover="${escapeHtml33(provider.provider)}">Mark ${escapeHtml33(provider.provider)} recovered</button>`).join("")}
|
|
19556
19608
|
</div>
|
|
19557
|
-
${simulation.fallbackRequiredProvider && !configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider) ? `<p class="muted">${
|
|
19609
|
+
${simulation.fallbackRequiredProvider && !configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider) ? `<p class="muted">${escapeHtml33(simulation.fallbackRequiredMessage ?? `Configure ${simulation.fallbackRequiredProvider} to enable fallback simulation.`)}</p>` : ""}
|
|
19558
19610
|
<pre class="simulate-output" hidden></pre>
|
|
19559
19611
|
</div>`;
|
|
19560
19612
|
};
|
|
19561
19613
|
var renderVoiceResilienceHTML = (input) => {
|
|
19562
19614
|
const summary = summarizeRoutingEvents(input.routingEvents);
|
|
19563
|
-
const kindCounts = [...summary.byKind.entries()].map(([kind, count]) => `<span class="pill">${
|
|
19564
|
-
const links = input.links?.length ? input.links.map((link) => `<a href="${
|
|
19615
|
+
const kindCounts = [...summary.byKind.entries()].map(([kind, count]) => `<span class="pill">${escapeHtml33(kind)}: ${String(count)}</span>`).join("");
|
|
19616
|
+
const links = input.links?.length ? input.links.map((link) => `<a href="${escapeHtml33(link.href)}">${escapeHtml33(link.label)}</a>`).join(" \xB7 ") : "";
|
|
19565
19617
|
return `<!doctype html>
|
|
19566
19618
|
<html lang="en">
|
|
19567
19619
|
<head>
|
|
19568
19620
|
<meta charset="utf-8" />
|
|
19569
19621
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
19570
|
-
<title>${
|
|
19622
|
+
<title>${escapeHtml33(input.title ?? "AbsoluteJS Voice Resilience")}</title>
|
|
19571
19623
|
<style>
|
|
19572
19624
|
:root { color-scheme: dark; }
|
|
19573
19625
|
body { background: radial-gradient(circle at top left, #172554, #09090b 36%, #050505); color: #f4f4f5; font-family: ui-sans-serif, system-ui, sans-serif; margin: 0; padding: 24px; }
|
|
@@ -19710,7 +19762,7 @@ var registerSimulationRoutes = (routes, simulation, defaultPathPrefix) => {
|
|
|
19710
19762
|
};
|
|
19711
19763
|
var createVoiceResilienceRoutes = (options) => {
|
|
19712
19764
|
const path = options.path ?? "/resilience";
|
|
19713
|
-
const routes = new
|
|
19765
|
+
const routes = new Elysia31({
|
|
19714
19766
|
name: options.name ?? "absolutejs-voice-resilience"
|
|
19715
19767
|
}).get(path, async () => {
|
|
19716
19768
|
const events = await options.store.list();
|
|
@@ -19788,8 +19840,8 @@ var assertVoiceProviderRoutingContract = async (options) => {
|
|
|
19788
19840
|
return report;
|
|
19789
19841
|
};
|
|
19790
19842
|
// src/productionReadiness.ts
|
|
19791
|
-
import { Elysia as
|
|
19792
|
-
var
|
|
19843
|
+
import { Elysia as Elysia32 } from "elysia";
|
|
19844
|
+
var escapeHtml34 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
19793
19845
|
var rollupStatus2 = (checks) => checks.some((check) => check.status === "fail") ? "fail" : checks.some((check) => check.status === "warn") ? "warn" : "pass";
|
|
19794
19846
|
var carrierStatus = (matrix) => matrix.summary.failing > 0 ? "fail" : matrix.summary.warnings > 0 || matrix.summary.ready < matrix.summary.providers ? "warn" : "pass";
|
|
19795
19847
|
var resolveCarriers = async (options, input) => {
|
|
@@ -20392,25 +20444,25 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
20392
20444
|
var renderVoiceProductionReadinessHTML = (report, options = {}) => {
|
|
20393
20445
|
const title = options.title ?? "AbsoluteJS Voice Production Readiness";
|
|
20394
20446
|
const checks = report.checks.map((check, index) => {
|
|
20395
|
-
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${
|
|
20396
|
-
return `<article class="check ${
|
|
20447
|
+
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${escapeHtml34(action.href)}">${escapeHtml34(action.label)}</button>` : `<a href="${escapeHtml34(action.href)}">${escapeHtml34(action.label)}</a>`).join("");
|
|
20448
|
+
return `<article class="check ${escapeHtml34(check.status)}">
|
|
20397
20449
|
<div>
|
|
20398
|
-
<span>${
|
|
20399
|
-
<h2>${
|
|
20400
|
-
${check.detail ? `<p>${
|
|
20401
|
-
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${
|
|
20450
|
+
<span>${escapeHtml34(check.status.toUpperCase())}</span>
|
|
20451
|
+
<h2>${escapeHtml34(check.label)}</h2>
|
|
20452
|
+
${check.detail ? `<p>${escapeHtml34(check.detail)}</p>` : ""}
|
|
20453
|
+
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${escapeHtml34(check.proofSource.href)}">${escapeHtml34(check.proofSource.sourceLabel)}</a>` : escapeHtml34(check.proofSource.sourceLabel)}${check.proofSource.detail ? ` \xB7 ${escapeHtml34(check.proofSource.detail)}` : ""}</p>` : ""}
|
|
20402
20454
|
${actions ? `<p class="actions">${actions}</p>` : ""}
|
|
20403
20455
|
</div>
|
|
20404
|
-
<strong>${
|
|
20405
|
-
${check.href ? `<a href="${
|
|
20456
|
+
<strong>${escapeHtml34(String(check.value ?? check.status))}</strong>
|
|
20457
|
+
${check.href ? `<a href="${escapeHtml34(check.href)}">Open surface</a>` : ""}
|
|
20406
20458
|
</article>`;
|
|
20407
20459
|
}).join("");
|
|
20408
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
20460
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml34(title)}</title><style>body{background:#0c0f14;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1060px;padding:32px}.hero{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}.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}.status.pass,.check.pass{border-color:rgba(34,197,94,.55)}.status.warn,.check.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{color:#a8b0b8;font-size:.78rem;font-weight:900;letter-spacing:.08em}.check h2{margin:.2rem 0}.check p{color:#b9c0c8;margin:.2rem 0 0}.check .proof-source{color:#f9d77e;font-weight:800}.check strong{font-size:1.5rem}.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>${escapeHtml34(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 ${escapeHtml34(report.status)}">Overall: ${escapeHtml34(report.status.toUpperCase())}</p><p>Checked ${escapeHtml34(new Date(report.checkedAt).toLocaleString())}</p></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>`;
|
|
20409
20461
|
};
|
|
20410
20462
|
var createVoiceProductionReadinessRoutes = (options) => {
|
|
20411
20463
|
const path = options.path ?? "/api/production-readiness";
|
|
20412
20464
|
const htmlPath = options.htmlPath ?? "/production-readiness";
|
|
20413
|
-
const routes = new
|
|
20465
|
+
const routes = new Elysia32({
|
|
20414
20466
|
name: options.name ?? "absolutejs-voice-production-readiness"
|
|
20415
20467
|
});
|
|
20416
20468
|
routes.get(path, async ({ query, request }) => buildVoiceProductionReadinessReport(options, { query, request }));
|
|
@@ -20432,7 +20484,7 @@ var createVoiceProductionReadinessRoutes = (options) => {
|
|
|
20432
20484
|
return routes;
|
|
20433
20485
|
};
|
|
20434
20486
|
// src/opsConsoleRoutes.ts
|
|
20435
|
-
import { Elysia as
|
|
20487
|
+
import { Elysia as Elysia33 } from "elysia";
|
|
20436
20488
|
var DEFAULT_LINKS = [
|
|
20437
20489
|
{
|
|
20438
20490
|
description: "Quality gates for CI, deploy checks, and production readiness.",
|
|
@@ -20467,7 +20519,7 @@ var DEFAULT_LINKS = [
|
|
|
20467
20519
|
label: "Handoffs"
|
|
20468
20520
|
}
|
|
20469
20521
|
];
|
|
20470
|
-
var
|
|
20522
|
+
var escapeHtml35 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
20471
20523
|
var countProviderStatuses = (providers) => {
|
|
20472
20524
|
const degradedStatuses = new Set(["degraded", "rate-limited", "suppressed"]);
|
|
20473
20525
|
const healthy = providers.filter((provider) => provider.status === "healthy").length;
|
|
@@ -20536,20 +20588,20 @@ var buildVoiceOpsConsoleReport = async (options) => {
|
|
|
20536
20588
|
trace
|
|
20537
20589
|
};
|
|
20538
20590
|
};
|
|
20539
|
-
var renderMetricCard = (input) => `<article class="metric"><span>${
|
|
20591
|
+
var renderMetricCard = (input) => `<article class="metric"><span>${escapeHtml35(input.label)}</span><strong>${escapeHtml35(String(input.value))}</strong>${input.status ? `<p class="${escapeHtml35(input.status)}">${escapeHtml35(input.status)}</p>` : ""}${input.href ? `<a href="${escapeHtml35(input.href)}">Open</a>` : ""}</article>`;
|
|
20540
20592
|
var renderVoiceOpsConsoleHTML = (report, options = {}) => {
|
|
20541
20593
|
const links = report.links.map((link) => `<article class="surface">
|
|
20542
|
-
<div><h2>${
|
|
20543
|
-
<p><a href="${
|
|
20594
|
+
<div><h2>${escapeHtml35(link.label)}</h2>${link.description ? `<p>${escapeHtml35(link.description)}</p>` : ""}</div>
|
|
20595
|
+
<p><a href="${escapeHtml35(link.href)}">Open ${escapeHtml35(link.label)}</a>${link.statusHref ? ` \xB7 <a href="${escapeHtml35(link.statusHref)}">Status</a>` : ""}</p>
|
|
20544
20596
|
</article>`).join("");
|
|
20545
|
-
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${
|
|
20546
|
-
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${
|
|
20597
|
+
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${escapeHtml35(session.sessionId)}</td><td>${escapeHtml35(session.status)}</td><td>${session.turnCount}</td><td>${session.errorCount}</td><td>${session.replayHref ? `<a href="${escapeHtml35(session.replayHref)}">Replay</a>` : ""}</td></tr>`).join("") : '<tr><td colspan="5">No sessions yet.</td></tr>';
|
|
20598
|
+
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${escapeHtml35(event.kind)}</td><td>${escapeHtml35(event.provider ?? "unknown")}</td><td>${escapeHtml35(event.status ?? "unknown")}</td><td>${event.elapsedMs ?? 0}ms</td><td>${escapeHtml35(event.sessionId)}</td></tr>`).join("") : '<tr><td colspan="5">No provider routing events yet.</td></tr>';
|
|
20547
20599
|
const title = options.title ?? "AbsoluteJS Voice Ops Console";
|
|
20548
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
20600
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml35(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>${escapeHtml35(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 ${escapeHtml35(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>`;
|
|
20549
20601
|
};
|
|
20550
20602
|
var createVoiceOpsConsoleRoutes = (options) => {
|
|
20551
20603
|
const path = options.path ?? "/ops-console";
|
|
20552
|
-
const routes = new
|
|
20604
|
+
const routes = new Elysia33({
|
|
20553
20605
|
name: options.name ?? "absolutejs-voice-ops-console"
|
|
20554
20606
|
});
|
|
20555
20607
|
const getReport = () => buildVoiceOpsConsoleReport(options);
|
|
@@ -20737,19 +20789,19 @@ var summarizeVoiceOpsStatus = async (options) => {
|
|
|
20737
20789
|
};
|
|
20738
20790
|
};
|
|
20739
20791
|
// src/opsStatusRoutes.ts
|
|
20740
|
-
import { Elysia as
|
|
20741
|
-
var
|
|
20792
|
+
import { Elysia as Elysia34 } from "elysia";
|
|
20793
|
+
var escapeHtml36 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
20742
20794
|
var renderVoiceOpsStatusHTML = (report, options = {}) => {
|
|
20743
20795
|
const title = options.title ?? "AbsoluteJS Voice Ops Status";
|
|
20744
20796
|
const surfaces = Object.entries(report.surfaces).map(([key, surface]) => {
|
|
20745
20797
|
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;
|
|
20746
|
-
return `<article class="surface ${
|
|
20798
|
+
return `<article class="surface ${escapeHtml36(surface.status)}"><span>${escapeHtml36(surface.status.toUpperCase())}</span><h2>${escapeHtml36(key)}</h2><strong>${escapeHtml36(value)}</strong></article>`;
|
|
20747
20799
|
}).join("");
|
|
20748
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
20800
|
+
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:#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>${escapeHtml36(title)}</h1><p>Compact pass/fail status for framework widgets, demos, and small customer-facing health badges.</p><p class="status ${escapeHtml36(report.status)}">Overall: ${escapeHtml36(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>`;
|
|
20749
20801
|
};
|
|
20750
20802
|
var createVoiceOpsStatusRoutes = (options) => {
|
|
20751
20803
|
const path = options.path ?? "/api/voice/ops-status";
|
|
20752
|
-
const routes = new
|
|
20804
|
+
const routes = new Elysia34({
|
|
20753
20805
|
name: options.name ?? "absolutejs-voice-ops-status"
|
|
20754
20806
|
});
|
|
20755
20807
|
routes.get(path, async () => summarizeVoiceOpsStatus(options));
|
|
@@ -21182,8 +21234,8 @@ var createVoiceTTSProviderRouter = (options) => {
|
|
|
21182
21234
|
};
|
|
21183
21235
|
};
|
|
21184
21236
|
// src/traceDeliveryRoutes.ts
|
|
21185
|
-
import { Elysia as
|
|
21186
|
-
var
|
|
21237
|
+
import { Elysia as Elysia35 } from "elysia";
|
|
21238
|
+
var escapeHtml37 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21187
21239
|
var getString12 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
21188
21240
|
var getNumber7 = (value) => {
|
|
21189
21241
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -21264,14 +21316,14 @@ var renderSinkResults2 = (delivery) => {
|
|
|
21264
21316
|
if (entries.length === 0) {
|
|
21265
21317
|
return "<p>No sink delivery attempts recorded yet.</p>";
|
|
21266
21318
|
}
|
|
21267
|
-
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${
|
|
21319
|
+
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${escapeHtml37(sinkId)}</strong>: ${escapeHtml37(result.status)}${result.deliveredTo ? ` to ${escapeHtml37(result.deliveredTo)}` : ""}${result.error ? ` (${escapeHtml37(result.error)})` : ""}</li>`).join("")}</ul>`;
|
|
21268
21320
|
};
|
|
21269
|
-
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${
|
|
21321
|
+
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${escapeHtml37(event.type)} <small>${escapeHtml37(event.id)}</small>${event.sessionId ? ` session=${escapeHtml37(event.sessionId)}` : ""}</li>`).join("")}</ul>`;
|
|
21270
21322
|
var renderVoiceTraceDeliveryHTML = (report, options = {}) => {
|
|
21271
21323
|
const title = options.title ?? "AbsoluteJS Voice Trace Deliveries";
|
|
21272
|
-
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${
|
|
21273
|
-
const rows = report.deliveries.map((delivery) => `<article class="delivery ${
|
|
21274
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
21324
|
+
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${escapeHtml37(options.workerPath ?? "/api/voice-trace-deliveries/drain")}"><button type="submit">Drain trace deliveries</button></form>`;
|
|
21325
|
+
const rows = report.deliveries.map((delivery) => `<article class="delivery ${escapeHtml37(delivery.deliveryStatus)}"><div class="head"><div><span>${escapeHtml37(delivery.deliveryStatus)}</span><h2>${escapeHtml37(delivery.id)}</h2><p>${escapeHtml37(new Date(delivery.createdAt).toLocaleString())}${delivery.deliveredAt ? ` \xB7 delivered ${escapeHtml37(new Date(delivery.deliveredAt).toLocaleString())}` : ""}</p></div><strong>${String(delivery.deliveryAttempts ?? 0)} attempt(s)</strong></div>${delivery.deliveryError ? `<p class="error">${escapeHtml37(delivery.deliveryError)}</p>` : ""}<h3>Sinks</h3>${renderSinkResults2(delivery)}<h3>Events</h3>${renderEventList2(delivery)}</article>`).join("");
|
|
21326
|
+
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:#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>${escapeHtml37(title)}</h1><p>Checked ${escapeHtml37(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>`;
|
|
21275
21327
|
};
|
|
21276
21328
|
var createVoiceTraceDeliveryJSONHandler = (options) => async ({ query }) => buildVoiceTraceDeliveryReport(options, resolveVoiceTraceDeliveryFilter(query, options.filter));
|
|
21277
21329
|
var createVoiceTraceDeliveryHTMLHandler = (options) => async ({ query }) => {
|
|
@@ -21291,7 +21343,7 @@ var createVoiceTraceDeliveryRoutes = (options) => {
|
|
|
21291
21343
|
const path = options.path ?? "/api/voice-trace-deliveries";
|
|
21292
21344
|
const htmlPath = options.htmlPath === undefined ? "/traces/deliveries" : options.htmlPath;
|
|
21293
21345
|
const workerPath = options.workerPath === undefined ? `${path}/drain` : options.workerPath;
|
|
21294
|
-
const routes = new
|
|
21346
|
+
const routes = new Elysia35({
|
|
21295
21347
|
name: options.name ?? "absolutejs-voice-trace-deliveries"
|
|
21296
21348
|
}).get(path, createVoiceTraceDeliveryJSONHandler(options));
|
|
21297
21349
|
if (htmlPath !== false) {
|
|
@@ -21309,8 +21361,8 @@ var createVoiceTraceDeliveryRoutes = (options) => {
|
|
|
21309
21361
|
return routes;
|
|
21310
21362
|
};
|
|
21311
21363
|
// src/traceTimeline.ts
|
|
21312
|
-
import { Elysia as
|
|
21313
|
-
var
|
|
21364
|
+
import { Elysia as Elysia36 } from "elysia";
|
|
21365
|
+
var escapeHtml38 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21314
21366
|
var getString13 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
21315
21367
|
var getNumber8 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
21316
21368
|
var firstString3 = (payload, keys) => {
|
|
@@ -21478,20 +21530,20 @@ var summarizeVoiceTraceTimeline = (events, options = {}) => {
|
|
|
21478
21530
|
};
|
|
21479
21531
|
};
|
|
21480
21532
|
var formatMs3 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
21481
|
-
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>${
|
|
21533
|
+
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>${escapeHtml38(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>`;
|
|
21482
21534
|
var renderVoiceTraceTimelineSessionHTML = (session, options = {}) => {
|
|
21483
|
-
const events = session.events.map((event) => `<tr class="${
|
|
21484
|
-
const issues = session.evaluation.issues.length ? session.evaluation.issues.map((issue) => `<li class="${
|
|
21485
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
21535
|
+
const events = session.events.map((event) => `<tr class="${escapeHtml38(event.status ?? "")}"><td>+${String(event.offsetMs)}ms</td><td>${escapeHtml38(event.type)}</td><td>${escapeHtml38(event.label)}</td><td>${escapeHtml38(event.provider ?? "")}</td><td>${escapeHtml38(event.status ?? "")}</td><td>${formatMs3(event.elapsedMs)}</td></tr>`).join("");
|
|
21536
|
+
const issues = session.evaluation.issues.length ? session.evaluation.issues.map((issue) => `<li class="${escapeHtml38(issue.severity)}">${escapeHtml38(issue.code)}: ${escapeHtml38(issue.message)}</li>`).join("") : "<li>none</li>";
|
|
21537
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml38(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>${escapeHtml38(session.sessionId)}</h1><p class="status ${escapeHtml38(session.status)}">${escapeHtml38(session.status)}</p></header><section class="metrics"><article><span>Events</span><strong>${String(session.summary.eventCount)}</strong></article><article><span>Turns</span><strong>${String(session.summary.turnCount)}</strong></article><article><span>Errors</span><strong>${String(session.summary.errorCount)}</strong></article><article><span>Duration</span><strong>${formatMs3(session.summary.callDurationMs)}</strong></article></section><section><h2>Providers</h2>${renderProviderCards2(session)}</section><section><h2>Issues</h2><ul>${issues}</ul></section><section><h2>Timeline</h2><table><thead><tr><th>Offset</th><th>Type</th><th>Event</th><th>Provider</th><th>Status</th><th>Latency</th></tr></thead><tbody>${events}</tbody></table></section></main></body></html>`;
|
|
21486
21538
|
};
|
|
21487
|
-
var renderSessionRows = (report) => report.sessions.length === 0 ? '<tr><td colspan="7">No trace events recorded yet.</td></tr>' : report.sessions.map((session) => `<tr class="${
|
|
21539
|
+
var renderSessionRows = (report) => report.sessions.length === 0 ? '<tr><td colspan="7">No trace events recorded yet.</td></tr>' : report.sessions.map((session) => `<tr class="${escapeHtml38(session.status)}"><td><a href="/traces/${encodeURIComponent(session.sessionId)}">${escapeHtml38(session.sessionId)}</a></td><td>${escapeHtml38(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) => escapeHtml38(provider.provider)).join(", ")}</td></tr>`).join("");
|
|
21488
21540
|
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}}";
|
|
21489
|
-
var renderVoiceTraceTimelineHTML = (report, options = {}) => `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
21541
|
+
var renderVoiceTraceTimelineHTML = (report, options = {}) => `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml38(options.title ?? "Voice Trace Timelines")}</title><style>${timelineCSS}</style></head><body><main><header><p class="eyebrow">Self-hosted voice debugging</p><h1>${escapeHtml38(options.title ?? "Voice Trace Timelines")}</h1><p class="muted">Per-call event timelines with provider latency, fallback, timeout, handoff, and error context.</p></header><section class="metrics"><article><span>Sessions</span><strong>${String(report.total)}</strong></article><article><span>Failed</span><strong>${String(report.failed)}</strong></article><article><span>Warnings</span><strong>${String(report.warnings)}</strong></article></section><table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Turns</th><th>Errors</th><th>Duration</th><th>Providers</th></tr></thead><tbody>${renderSessionRows(report)}</tbody></table></main></body></html>`;
|
|
21490
21542
|
var createVoiceTraceTimelineRoutes = (options) => {
|
|
21491
21543
|
const path = options.path ?? "/api/voice-traces";
|
|
21492
21544
|
const htmlPath = options.htmlPath ?? "/traces";
|
|
21493
21545
|
const title = options.title ?? "AbsoluteJS Voice Trace Timelines";
|
|
21494
|
-
const routes = new
|
|
21546
|
+
const routes = new Elysia36({
|
|
21495
21547
|
name: options.name ?? "absolutejs-voice-trace-timelines"
|
|
21496
21548
|
});
|
|
21497
21549
|
const buildReport = async () => summarizeVoiceTraceTimeline(await options.store.list(), {
|
|
@@ -22144,7 +22196,7 @@ var createVoiceMemoryStore = () => {
|
|
|
22144
22196
|
return { get, getOrCreate, list, remove, set };
|
|
22145
22197
|
};
|
|
22146
22198
|
// src/opsWebhook.ts
|
|
22147
|
-
import { Elysia as
|
|
22199
|
+
import { Elysia as Elysia37 } from "elysia";
|
|
22148
22200
|
var toHex6 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
22149
22201
|
var signVoiceOpsWebhookBody = async (input) => {
|
|
22150
22202
|
const encoder = new TextEncoder;
|
|
@@ -22274,7 +22326,7 @@ var verifyVoiceOpsWebhookSignature = async (input) => {
|
|
|
22274
22326
|
};
|
|
22275
22327
|
var createVoiceOpsWebhookReceiverRoutes = (options = {}) => {
|
|
22276
22328
|
const path = options.path ?? "/api/voice-ops/webhook";
|
|
22277
|
-
return new
|
|
22329
|
+
return new Elysia37().post(path, async ({ body, request, set }) => {
|
|
22278
22330
|
const bodyText = typeof body === "string" ? body : JSON.stringify(body);
|
|
22279
22331
|
if (options.signingSecret) {
|
|
22280
22332
|
const verification = await verifyVoiceOpsWebhookSignature({
|
|
@@ -23219,6 +23271,7 @@ export {
|
|
|
23219
23271
|
renderVoiceEvalBaselineHTML,
|
|
23220
23272
|
renderVoiceDemoReadyHTML,
|
|
23221
23273
|
renderVoiceDeliverySinkHTML,
|
|
23274
|
+
renderVoiceDeliveryRuntimeHTML,
|
|
23222
23275
|
renderVoiceCampaignsHTML,
|
|
23223
23276
|
renderVoiceCampaignObservabilityHTML,
|
|
23224
23277
|
renderVoiceCallReviewMarkdown,
|
|
@@ -23440,6 +23493,7 @@ export {
|
|
|
23440
23493
|
createVoiceDeliverySinkRoutes,
|
|
23441
23494
|
createVoiceDeliverySinkPair,
|
|
23442
23495
|
createVoiceDeliverySinkDescriptor,
|
|
23496
|
+
createVoiceDeliveryRuntimeRoutes,
|
|
23443
23497
|
createVoiceDeliveryRuntime,
|
|
23444
23498
|
createVoiceCampaignWorkerLoop,
|
|
23445
23499
|
createVoiceCampaignWorker,
|
|
@@ -23512,6 +23566,7 @@ export {
|
|
|
23512
23566
|
buildVoiceDiagnosticsMarkdown,
|
|
23513
23567
|
buildVoiceDemoReadyReport,
|
|
23514
23568
|
buildVoiceDeliverySinkReport,
|
|
23569
|
+
buildVoiceDeliveryRuntimeReport,
|
|
23515
23570
|
buildVoiceDataRetentionPlan,
|
|
23516
23571
|
buildVoiceCampaignObservabilityReport,
|
|
23517
23572
|
buildVoiceAuditTrailReport,
|