@absolutejs/voice 0.0.22-beta.155 → 0.0.22-beta.157
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.js +195 -86
- package/dist/client/opsActionHistory.d.ts +19 -0
- package/dist/client/opsActionHistoryWidget.d.ts +11 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +292 -202
- package/dist/opsActionAuditRoutes.d.ts +23 -3
- package/dist/productionReadiness.d.ts +15 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11638,7 +11638,9 @@ var recordVoiceOpsActionAudit = async (record, options) => {
|
|
|
11638
11638
|
};
|
|
11639
11639
|
var createVoiceOpsActionAuditRoutes = (options) => {
|
|
11640
11640
|
const path = options.path ?? "/api/voice/ops-actions/audit";
|
|
11641
|
-
|
|
11641
|
+
const historyPath = options.historyPath === undefined ? "/api/voice/ops-actions/history" : options.historyPath;
|
|
11642
|
+
const historyHtmlPath = options.historyHtmlPath === undefined ? "/voice/ops-actions" : options.historyHtmlPath;
|
|
11643
|
+
const routes = new Elysia12({
|
|
11642
11644
|
name: options.name ?? "absolutejs-voice-ops-action-audit"
|
|
11643
11645
|
}).post(path, async ({ request, set }) => {
|
|
11644
11646
|
try {
|
|
@@ -11652,17 +11654,57 @@ var createVoiceOpsActionAuditRoutes = (options) => {
|
|
|
11652
11654
|
};
|
|
11653
11655
|
}
|
|
11654
11656
|
});
|
|
11657
|
+
if (historyPath !== false) {
|
|
11658
|
+
routes.get(historyPath, async () => buildVoiceOpsActionHistoryReport(options));
|
|
11659
|
+
}
|
|
11660
|
+
if (historyHtmlPath !== false) {
|
|
11661
|
+
routes.get(historyHtmlPath, async () => new Response(renderVoiceOpsActionHistoryHTML(await buildVoiceOpsActionHistoryReport(options)), {
|
|
11662
|
+
headers: {
|
|
11663
|
+
"Content-Type": "text/html; charset=utf-8"
|
|
11664
|
+
}
|
|
11665
|
+
}));
|
|
11666
|
+
}
|
|
11667
|
+
return routes;
|
|
11668
|
+
};
|
|
11669
|
+
var toHistoryEntry = (event) => ({
|
|
11670
|
+
actionId: event.action,
|
|
11671
|
+
at: event.at,
|
|
11672
|
+
error: event.payload && typeof event.payload === "object" && "error" in event.payload && typeof event.payload.error === "string" ? event.payload.error : undefined,
|
|
11673
|
+
eventId: event.id,
|
|
11674
|
+
ok: event.outcome !== "error",
|
|
11675
|
+
status: event.payload && typeof event.payload === "object" && "status" in event.payload && typeof event.payload.status === "number" ? event.payload.status : undefined,
|
|
11676
|
+
traceId: event.traceId
|
|
11677
|
+
});
|
|
11678
|
+
var buildVoiceOpsActionHistoryReport = async (options) => {
|
|
11679
|
+
const events = options.audit ? await options.audit.list({
|
|
11680
|
+
limit: 25,
|
|
11681
|
+
resourceType: "voice.ops.action",
|
|
11682
|
+
type: "operator.action"
|
|
11683
|
+
}) : [];
|
|
11684
|
+
const entries = events.map(toHistoryEntry).sort((left, right) => right.at - left.at);
|
|
11685
|
+
return {
|
|
11686
|
+
checkedAt: Date.now(),
|
|
11687
|
+
entries,
|
|
11688
|
+
failed: entries.filter((entry) => !entry.ok).length,
|
|
11689
|
+
passed: entries.filter((entry) => entry.ok).length,
|
|
11690
|
+
total: entries.length
|
|
11691
|
+
};
|
|
11692
|
+
};
|
|
11693
|
+
var escapeHtml15 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
11694
|
+
var renderVoiceOpsActionHistoryHTML = (report) => {
|
|
11695
|
+
const rows = report.entries.map((entry) => `<article class="${entry.ok ? "ok" : "fail"}"><span>${escapeHtml15(entry.ok ? "success" : "error")}</span><strong>${escapeHtml15(entry.actionId)}</strong><p>${escapeHtml15(new Date(entry.at).toLocaleString())}${entry.status ? ` \xB7 HTTP ${String(entry.status)}` : ""}</p>${entry.error ? `<p>${escapeHtml15(entry.error)}</p>` : ""}</article>`).join("");
|
|
11696
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>Voice Ops Action History</title><style>body{background:#11140f;color:#f7f1df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero,article{background:#181d15;border:1px solid #2c3327;border-radius:24px;padding:20px}.hero{margin-bottom:16px}h1{font-size:clamp(2rem,6vw,4rem);line-height:.95}section{display:grid;gap:12px}article.ok{border-color:rgba(34,197,94,.55)}article.fail{border-color:rgba(239,68,68,.75)}span{color:#facc15;font-weight:900;text-transform:uppercase}p{color:#c8ccb8}</style></head><body><main><section class="hero"><span>Operator proof</span><h1>Voice Ops Action History</h1><p>${String(report.total)} action(s), ${String(report.failed)} failed.</p></section><section>${rows || "<p>No operator actions have been recorded.</p>"}</section></main></body></html>`;
|
|
11655
11697
|
};
|
|
11656
11698
|
// src/deliveryRuntime.ts
|
|
11657
11699
|
import { Elysia as Elysia13 } from "elysia";
|
|
11658
11700
|
import { mkdir } from "fs/promises";
|
|
11659
11701
|
import { dirname, join } from "path";
|
|
11660
|
-
var
|
|
11702
|
+
var escapeHtml16 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
11661
11703
|
var renderSummaryCard = (label, summary) => {
|
|
11662
11704
|
if (!summary) {
|
|
11663
|
-
return `<article><span>${
|
|
11705
|
+
return `<article><span>${escapeHtml16(label)}</span><strong>Disabled</strong><p class="muted">No worker configured.</p></article>`;
|
|
11664
11706
|
}
|
|
11665
|
-
return `<article><span>${
|
|
11707
|
+
return `<article><span>${escapeHtml16(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>`;
|
|
11666
11708
|
};
|
|
11667
11709
|
var resolvePresetLeases = (leases) => ("claim" in leases) ? {
|
|
11668
11710
|
audit: leases,
|
|
@@ -11873,9 +11915,9 @@ var buildVoiceDeliveryRuntimeReport = async (runtime) => ({
|
|
|
11873
11915
|
});
|
|
11874
11916
|
var renderVoiceDeliveryRuntimeHTML = (report, options = {}) => {
|
|
11875
11917
|
const title = options.title ?? "AbsoluteJS Voice Delivery Runtime";
|
|
11876
|
-
const tickForm = options.tickPath === false ? "" : `<form method="post" action="${
|
|
11877
|
-
const requeueForm = options.requeueDeadLettersPath === false ? "" : `<form method="post" action="${
|
|
11878
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
11918
|
+
const tickForm = options.tickPath === false ? "" : `<form method="post" action="${escapeHtml16(options.tickPath ?? "/api/voice-delivery-runtime/tick")}"><button type="submit">Tick delivery workers</button></form>`;
|
|
11919
|
+
const requeueForm = options.requeueDeadLettersPath === false ? "" : `<form method="post" action="${escapeHtml16(options.requeueDeadLettersPath ?? "/api/voice-delivery-runtime/requeue-dead-letters")}"><button type="submit">Requeue dead letters</button></form>`;
|
|
11920
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml16(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}.actions{display:flex;flex-wrap:wrap;gap:10px}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>${escapeHtml16(title)}</h1><p class="muted">Inspect queue summaries, manually tick failed/pending audit and trace deliveries, and requeue dead letters after operator review.</p><p class="status ${report.isRunning ? "running" : ""}">${report.isRunning ? "Running" : "Stopped"}</p><p class="muted">Checked ${escapeHtml16(new Date(report.checkedAt).toLocaleString())}</p><div class="actions">${tickForm}${requeueForm}</div></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 })
|
|
11879
11921
|
|
|
11880
11922
|
await runtime.tick()
|
|
11881
11923
|
await runtime.requeueDeadLetters()
|
|
@@ -12133,7 +12175,7 @@ import { Elysia as Elysia15 } from "elysia";
|
|
|
12133
12175
|
|
|
12134
12176
|
// src/handoffHealth.ts
|
|
12135
12177
|
import { Elysia as Elysia14 } from "elysia";
|
|
12136
|
-
var
|
|
12178
|
+
var escapeHtml17 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
12137
12179
|
var getString6 = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
|
|
12138
12180
|
var isStatus = (value) => value === "delivered" || value === "failed" || value === "skipped";
|
|
12139
12181
|
var increment3 = (record, key) => {
|
|
@@ -12251,10 +12293,10 @@ var renderActionSummary = (summary) => {
|
|
|
12251
12293
|
return [
|
|
12252
12294
|
'<section class="voice-handoff-health-columns">',
|
|
12253
12295
|
"<article><h3>Actions</h3>",
|
|
12254
|
-
actions.length === 0 ? "<p>No handoff actions yet.</p>" : `<ul>${actions.map(([action, count]) => `<li>${
|
|
12296
|
+
actions.length === 0 ? "<p>No handoff actions yet.</p>" : `<ul>${actions.map(([action, count]) => `<li>${escapeHtml17(action)}: ${String(count)}</li>`).join("")}</ul>`,
|
|
12255
12297
|
"</article>",
|
|
12256
12298
|
"<article><h3>Adapters</h3>",
|
|
12257
|
-
adapters.length === 0 ? "<p>No adapter deliveries yet.</p>" : `<ul>${adapters.map(([adapterId, counts]) => `<li>${
|
|
12299
|
+
adapters.length === 0 ? "<p>No adapter deliveries yet.</p>" : `<ul>${adapters.map(([adapterId, counts]) => `<li>${escapeHtml17(adapterId)}: ${String(counts.delivered)} delivered / ${String(counts.failed)} failed / ${String(counts.skipped)} skipped</li>`).join("")}</ul>`,
|
|
12258
12300
|
"</article>",
|
|
12259
12301
|
"</section>"
|
|
12260
12302
|
].join("");
|
|
@@ -12268,22 +12310,22 @@ var renderVoiceHandoffHealthHTML = (summary) => [
|
|
|
12268
12310
|
summary.events.length === 0 ? '<p class="voice-handoff-health-empty">No handoffs found.</p>' : [
|
|
12269
12311
|
'<div class="voice-handoff-health-events">',
|
|
12270
12312
|
...summary.events.map((event) => [
|
|
12271
|
-
`<article class="${
|
|
12313
|
+
`<article class="${escapeHtml17(event.status)}">`,
|
|
12272
12314
|
'<div class="voice-handoff-health-event-header">',
|
|
12273
|
-
`<strong>${
|
|
12274
|
-
`<span>${
|
|
12315
|
+
`<strong>${escapeHtml17(event.action ?? "handoff")}</strong>`,
|
|
12316
|
+
`<span>${escapeHtml17(event.status)}</span>`,
|
|
12275
12317
|
"</div>",
|
|
12276
|
-
`<p><small>${
|
|
12277
|
-
event.target ? `<p>Target: ${
|
|
12278
|
-
event.reason ? `<p>Reason: ${
|
|
12318
|
+
`<p><small>${escapeHtml17(event.sessionId)}</small></p>`,
|
|
12319
|
+
event.target ? `<p>Target: ${escapeHtml17(event.target)}</p>` : "",
|
|
12320
|
+
event.reason ? `<p>Reason: ${escapeHtml17(event.reason)}</p>` : "",
|
|
12279
12321
|
event.deliveries.length ? `<ul>${event.deliveries.map((delivery) => [
|
|
12280
12322
|
"<li>",
|
|
12281
|
-
`${
|
|
12282
|
-
delivery.deliveredTo ? ` to ${
|
|
12283
|
-
delivery.error ? ` (${
|
|
12323
|
+
`${escapeHtml17(delivery.adapterId)}: ${escapeHtml17(delivery.status)}`,
|
|
12324
|
+
delivery.deliveredTo ? ` to ${escapeHtml17(delivery.deliveredTo)}` : "",
|
|
12325
|
+
delivery.error ? ` (${escapeHtml17(delivery.error)})` : "",
|
|
12284
12326
|
"</li>"
|
|
12285
12327
|
].join("")).join("")}</ul>` : "",
|
|
12286
|
-
event.replayHref ? `<p><a href="${
|
|
12328
|
+
event.replayHref ? `<p><a href="${escapeHtml17(event.replayHref)}">Open replay</a></p>` : "",
|
|
12287
12329
|
"</article>"
|
|
12288
12330
|
].join("")),
|
|
12289
12331
|
"</div>"
|
|
@@ -12436,12 +12478,12 @@ var evaluateVoiceQuality = async (input) => {
|
|
|
12436
12478
|
thresholds
|
|
12437
12479
|
};
|
|
12438
12480
|
};
|
|
12439
|
-
var
|
|
12481
|
+
var escapeHtml18 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
12440
12482
|
var formatMetricValue = (metric) => metric.unit === "rate" ? `${(metric.actual * 100).toFixed(2)}%` : metric.unit === "ms" ? `${Math.round(metric.actual)}ms` : String(metric.actual);
|
|
12441
12483
|
var formatThreshold = (metric) => metric.unit === "rate" ? `${(metric.threshold * 100).toFixed(2)}%` : metric.unit === "ms" ? `${Math.round(metric.threshold)}ms` : String(metric.threshold);
|
|
12442
12484
|
var renderVoiceQualityHTML = (report, options = {}) => {
|
|
12443
|
-
const rows = Object.entries(report.metrics).map(([key, metric]) => `<tr class="${metric.pass ? "pass" : "fail"}"><td>${
|
|
12444
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
12485
|
+
const rows = Object.entries(report.metrics).map(([key, metric]) => `<tr class="${metric.pass ? "pass" : "fail"}"><td>${escapeHtml18(metric.label)}</td><td>${escapeHtml18(formatMetricValue(metric))}</td><td>${escapeHtml18(formatThreshold(metric))}</td><td>${metric.pass ? "pass" : "fail"}</td><td><code>${escapeHtml18(key)}</code></td></tr>`).join("");
|
|
12486
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml18(link.href)}">${escapeHtml18(link.label)}</a>`).join("")}</nav>` : "";
|
|
12445
12487
|
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>`;
|
|
12446
12488
|
};
|
|
12447
12489
|
var createVoiceQualityRoutes = (options) => {
|
|
@@ -12475,7 +12517,7 @@ var createVoiceQualityRoutes = (options) => {
|
|
|
12475
12517
|
};
|
|
12476
12518
|
|
|
12477
12519
|
// src/evalRoutes.ts
|
|
12478
|
-
var
|
|
12520
|
+
var escapeHtml19 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
12479
12521
|
var rate2 = (count, total) => count / Math.max(1, total);
|
|
12480
12522
|
var normalizeSearchText = (value) => value.trim().toLowerCase();
|
|
12481
12523
|
var getString8 = (value) => typeof value === "string" ? value : undefined;
|
|
@@ -12784,40 +12826,40 @@ var formatTime = (value) => value === undefined ? "unknown" : new Date(value).to
|
|
|
12784
12826
|
var formatPercent = (value) => `${(value * 100).toFixed(2)}%`;
|
|
12785
12827
|
var renderVoiceEvalHTML = (report, options = {}) => {
|
|
12786
12828
|
const title = options.title ?? "AbsoluteJS Voice Evals";
|
|
12787
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
12788
|
-
const trend = report.trend.length ? report.trend.map((bucket) => `<tr><td>${
|
|
12829
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml19(link.href)}">${escapeHtml19(link.label)}</a>`).join("")}</nav>` : "";
|
|
12830
|
+
const trend = report.trend.length ? report.trend.map((bucket) => `<tr><td>${escapeHtml19(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>';
|
|
12789
12831
|
const sessions = report.sessions.length ? report.sessions.map((session) => {
|
|
12790
12832
|
const failedMetrics = Object.entries(session.quality.metrics).filter(([, metric]) => !metric.pass).map(([, metric]) => metric.label).join(", ");
|
|
12791
|
-
return `<tr class="${session.status}"><td>${
|
|
12833
|
+
return `<tr class="${session.status}"><td>${escapeHtml19(session.sessionId)}</td><td>${escapeHtml19(session.status)}</td><td>${session.eventCount}</td><td>${session.summary.turnCount}</td><td>${session.summary.errorCount}</td><td>${escapeHtml19(formatTime(session.endedAt))}</td><td>${escapeHtml19(failedMetrics || "none")}</td></tr>`;
|
|
12792
12834
|
}).join("") : '<tr><td colspan="7">No sessions found.</td></tr>';
|
|
12793
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
12835
|
+
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{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>${escapeHtml19(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>`;
|
|
12794
12836
|
};
|
|
12795
12837
|
var renderVoiceEvalBaselineHTML = (comparison, options = {}) => {
|
|
12796
12838
|
const title = options.title ?? "AbsoluteJS Voice Eval Baseline";
|
|
12797
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
12798
|
-
const reasons = comparison.reasons.length ? comparison.reasons.map((reason) => `<li>${
|
|
12799
|
-
const newFailures = comparison.newFailedSessionIds.length ? comparison.newFailedSessionIds.map((id) => `<li>${
|
|
12800
|
-
const recovered = comparison.recoveredSessionIds.length ? comparison.recoveredSessionIds.map((id) => `<li>${
|
|
12801
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
12839
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml19(link.href)}">${escapeHtml19(link.label)}</a>`).join("")}</nav>` : "";
|
|
12840
|
+
const reasons = comparison.reasons.length ? comparison.reasons.map((reason) => `<li>${escapeHtml19(reason)}</li>`).join("") : "<li>No baseline regressions detected.</li>";
|
|
12841
|
+
const newFailures = comparison.newFailedSessionIds.length ? comparison.newFailedSessionIds.map((id) => `<li>${escapeHtml19(id)}</li>`).join("") : "<li>none</li>";
|
|
12842
|
+
const recovered = comparison.recoveredSessionIds.length ? comparison.recoveredSessionIds.map((id) => `<li>${escapeHtml19(id)}</li>`).join("") : "<li>none</li>";
|
|
12843
|
+
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{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>${escapeHtml19(title)}</h1><p class="status ${comparison.status}">${comparison.status}</p><div class="grid"><article class="card"><span>Baseline pass rate</span><strong>${escapeHtml19(formatPercent(comparison.baseline.passRate))}</strong></article><article class="card"><span>Current pass rate</span><strong>${escapeHtml19(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>${escapeHtml19(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>`;
|
|
12802
12844
|
};
|
|
12803
12845
|
var renderVoiceScenarioEvalHTML = (report, options = {}) => {
|
|
12804
12846
|
const title = options.title ?? "AbsoluteJS Voice Scenario Evals";
|
|
12805
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
12847
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml19(link.href)}">${escapeHtml19(link.label)}</a>`).join("")}</nav>` : "";
|
|
12806
12848
|
const scenarios = report.scenarios.length ? report.scenarios.map((scenario) => {
|
|
12807
|
-
const scenarioIssues = scenario.issues.length ? `<ul>${scenario.issues.map((issue) => `<li>${
|
|
12808
|
-
const sessions = scenario.sessions.length ? scenario.sessions.map((session) => `<tr class="${session.status}"><td>${
|
|
12809
|
-
return `<section class="scenario ${scenario.status}"><h2>${
|
|
12849
|
+
const scenarioIssues = scenario.issues.length ? `<ul>${scenario.issues.map((issue) => `<li>${escapeHtml19(issue)}</li>`).join("")}</ul>` : "";
|
|
12850
|
+
const sessions = scenario.sessions.length ? scenario.sessions.map((session) => `<tr class="${session.status}"><td>${escapeHtml19(session.sessionId)}</td><td>${escapeHtml19(session.status)}</td><td>${session.eventCount}</td><td>${escapeHtml19(session.issues.join(", ") || "none")}</td></tr>`).join("") : '<tr><td colspan="4">No matching sessions.</td></tr>';
|
|
12851
|
+
return `<section class="scenario ${scenario.status}"><h2>${escapeHtml19(scenario.label)}</h2>${scenario.description ? `<p>${escapeHtml19(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>`;
|
|
12810
12852
|
}).join("") : "<section><p>No scenarios configured.</p></section>";
|
|
12811
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
12853
|
+
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{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>${escapeHtml19(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>`;
|
|
12812
12854
|
};
|
|
12813
12855
|
var renderVoiceScenarioFixtureEvalHTML = (report, options = {}) => {
|
|
12814
12856
|
const title = options.title ?? "AbsoluteJS Voice Fixture Evals";
|
|
12815
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
12857
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml19(link.href)}">${escapeHtml19(link.label)}</a>`).join("")}</nav>` : "";
|
|
12816
12858
|
const fixtures = report.fixtures.length ? report.fixtures.map((fixture) => {
|
|
12817
|
-
const scenarios = fixture.report.scenarios.map((scenario) => `<tr class="${scenario.status}"><td>${
|
|
12818
|
-
return `<section class="${fixture.status}"><h2>${
|
|
12859
|
+
const scenarios = fixture.report.scenarios.map((scenario) => `<tr class="${scenario.status}"><td>${escapeHtml19(scenario.label)}</td><td>${escapeHtml19(scenario.status)}</td><td>${scenario.matchedSessions}</td><td>${escapeHtml19([...scenario.issues, ...scenario.sessions.flatMap((session) => session.issues)].join(", ") || "none")}</td></tr>`).join("");
|
|
12860
|
+
return `<section class="${fixture.status}"><h2>${escapeHtml19(fixture.label)}</h2>${fixture.description ? `<p>${escapeHtml19(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>`;
|
|
12819
12861
|
}).join("") : "<section><p>No scenario fixtures configured.</p></section>";
|
|
12820
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
12862
|
+
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{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>${escapeHtml19(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>`;
|
|
12821
12863
|
};
|
|
12822
12864
|
var createVoiceEvalRoutes = (options) => {
|
|
12823
12865
|
const path = options.path ?? "/evals";
|
|
@@ -12959,7 +13001,7 @@ import { Elysia as Elysia19 } from "elysia";
|
|
|
12959
13001
|
|
|
12960
13002
|
// src/outcomeContract.ts
|
|
12961
13003
|
import { Elysia as Elysia17 } from "elysia";
|
|
12962
|
-
var
|
|
13004
|
+
var escapeHtml20 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
12963
13005
|
var getPayloadString = (event, key) => typeof event.payload[key] === "string" ? event.payload[key] : undefined;
|
|
12964
13006
|
var toList = async (input) => Array.isArray(input) ? input : await input?.list() ?? [];
|
|
12965
13007
|
var hydrateSessions = async (input) => {
|
|
@@ -13067,9 +13109,9 @@ var renderVoiceOutcomeContractHTML = (report, options = {}) => {
|
|
|
13067
13109
|
const contracts = report.contracts.map((contract) => `<section class="contract ${contract.pass ? "pass" : "fail"}">
|
|
13068
13110
|
<div class="contract-header">
|
|
13069
13111
|
<div>
|
|
13070
|
-
<p class="eyebrow">${
|
|
13071
|
-
<h2>${
|
|
13072
|
-
${contract.description ? `<p>${
|
|
13112
|
+
<p class="eyebrow">${escapeHtml20(contract.contractId)}</p>
|
|
13113
|
+
<h2>${escapeHtml20(contract.label ?? contract.contractId)}</h2>
|
|
13114
|
+
${contract.description ? `<p>${escapeHtml20(contract.description)}</p>` : ""}
|
|
13073
13115
|
</div>
|
|
13074
13116
|
<strong>${contract.pass ? "pass" : "fail"}</strong>
|
|
13075
13117
|
</div>
|
|
@@ -13080,9 +13122,9 @@ var renderVoiceOutcomeContractHTML = (report, options = {}) => {
|
|
|
13080
13122
|
<span>handoffs ${String(contract.matched.handoffs)}</span>
|
|
13081
13123
|
<span>events ${String(contract.matched.integrationEvents)}</span>
|
|
13082
13124
|
</div>
|
|
13083
|
-
${contract.issues.length ? `<ul>${contract.issues.map((issue) => `<li>${
|
|
13125
|
+
${contract.issues.length ? `<ul>${contract.issues.map((issue) => `<li>${escapeHtml20(issue.message)}</li>`).join("")}</ul>` : ""}
|
|
13084
13126
|
</section>`).join("");
|
|
13085
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
13127
|
+
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(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>${escapeHtml20(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>`;
|
|
13086
13128
|
};
|
|
13087
13129
|
var createVoiceOutcomeContractJSONHandler = (options) => async () => runVoiceOutcomeContractSuite(options);
|
|
13088
13130
|
var createVoiceOutcomeContractHTMLHandler = (options) => async () => {
|
|
@@ -13317,7 +13359,7 @@ var createDefaultTurn = (caseId) => ({
|
|
|
13317
13359
|
});
|
|
13318
13360
|
var defaultApi = {};
|
|
13319
13361
|
var sameJSON = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
13320
|
-
var
|
|
13362
|
+
var escapeHtml21 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13321
13363
|
var evaluateExpectation = (input) => {
|
|
13322
13364
|
const issues = [];
|
|
13323
13365
|
const expect = input.expect;
|
|
@@ -13483,19 +13525,19 @@ var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
|
13483
13525
|
const title = options.title ?? "Voice Tool Contracts";
|
|
13484
13526
|
const contracts = report.contracts.map((contract) => {
|
|
13485
13527
|
const cases = contract.cases.map((testCase) => `<tr>
|
|
13486
|
-
<td>${
|
|
13528
|
+
<td>${escapeHtml21(testCase.label ?? testCase.caseId)}</td>
|
|
13487
13529
|
<td class="${testCase.pass ? "pass" : "fail"}">${testCase.pass ? "pass" : "fail"}</td>
|
|
13488
|
-
<td>${
|
|
13530
|
+
<td>${escapeHtml21(testCase.status)}</td>
|
|
13489
13531
|
<td>${String(testCase.attempts)}</td>
|
|
13490
13532
|
<td>${String(testCase.elapsedMs)}ms</td>
|
|
13491
13533
|
<td>${testCase.timedOut ? "yes" : "no"}</td>
|
|
13492
|
-
<td>${
|
|
13534
|
+
<td>${escapeHtml21(testCase.issues.map((issue) => issue.message).join(" ") || testCase.error || "")}</td>
|
|
13493
13535
|
</tr>`).join("");
|
|
13494
13536
|
return `<section class="contract ${contract.pass ? "pass" : "fail"}">
|
|
13495
13537
|
<div class="contract-header">
|
|
13496
13538
|
<div>
|
|
13497
|
-
<p class="eyebrow">${
|
|
13498
|
-
<h2>${
|
|
13539
|
+
<p class="eyebrow">${escapeHtml21(contract.toolName)}</p>
|
|
13540
|
+
<h2>${escapeHtml21(contract.label ?? contract.contractId)}</h2>
|
|
13499
13541
|
</div>
|
|
13500
13542
|
<strong class="${contract.pass ? "pass" : "fail"}">${contract.pass ? "Passing" : "Failing"}</strong>
|
|
13501
13543
|
</div>
|
|
@@ -13505,7 +13547,7 @@ var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
|
13505
13547
|
</table>
|
|
13506
13548
|
</section>`;
|
|
13507
13549
|
}).join("");
|
|
13508
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
13550
|
+
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:#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>${escapeHtml21(title)}</h1><div class="summary"><span class="pill ${report.status === "pass" ? "pass" : "fail"}">${escapeHtml21(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>`;
|
|
13509
13551
|
};
|
|
13510
13552
|
var createVoiceToolContractJSONHandler = (options) => () => runVoiceToolContractSuite(options);
|
|
13511
13553
|
var createVoiceToolContractHTMLHandler = (options) => async () => {
|
|
@@ -13532,7 +13574,7 @@ var createVoiceToolContractRoutes = (options) => {
|
|
|
13532
13574
|
};
|
|
13533
13575
|
|
|
13534
13576
|
// src/simulationSuite.ts
|
|
13535
|
-
var
|
|
13577
|
+
var escapeHtml22 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13536
13578
|
var summarizeSection = (report) => ({
|
|
13537
13579
|
failed: report.failed,
|
|
13538
13580
|
passed: report.passed,
|
|
@@ -13668,15 +13710,15 @@ var renderSection = (label, summary) => {
|
|
|
13668
13710
|
if (!summary) {
|
|
13669
13711
|
return "";
|
|
13670
13712
|
}
|
|
13671
|
-
return `<article class="${
|
|
13713
|
+
return `<article class="${escapeHtml22(summary.status)}"><span>${escapeHtml22(label)}</span><strong>${escapeHtml22(summary.status)}</strong><p>${summary.passed}/${summary.total} passed, ${summary.failed} failed.</p></article>`;
|
|
13672
13714
|
};
|
|
13673
13715
|
var renderAction = (action) => {
|
|
13674
|
-
const content = `<strong>${
|
|
13675
|
-
return action.href ? `<a class="action" href="${
|
|
13716
|
+
const content = `<strong>${escapeHtml22(action.label)}</strong><p>${escapeHtml22(action.description)}</p><span>${escapeHtml22(action.section)} / ${escapeHtml22(action.severity)}</span>`;
|
|
13717
|
+
return action.href ? `<a class="action" href="${escapeHtml22(action.href)}">${content}</a>` : `<article class="action">${content}</article>`;
|
|
13676
13718
|
};
|
|
13677
13719
|
var renderVoiceSimulationSuiteHTML = (report, options = {}) => {
|
|
13678
13720
|
const title = options.title ?? "Voice Simulation Suite";
|
|
13679
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
13721
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml22(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>${escapeHtml22(title)}</h1><p>One report for session quality, scenario evals, fixture simulations, tool contracts, and outcome contracts.</p><p class="badge ${escapeHtml22(report.status)}">Status: ${escapeHtml22(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>${escapeHtml22(JSON.stringify({ summary: report.summary, actions: report.actions }, null, 2))}</pre></main></body></html>`;
|
|
13680
13722
|
};
|
|
13681
13723
|
var createVoiceSimulationSuiteRoutes = (options) => {
|
|
13682
13724
|
const path = options.path ?? "/api/voice/simulations";
|
|
@@ -13995,7 +14037,7 @@ var createVoiceWorkflowContractHandler = (input) => {
|
|
|
13995
14037
|
// src/sessionReplay.ts
|
|
13996
14038
|
import { Elysia as Elysia20 } from "elysia";
|
|
13997
14039
|
var getString9 = (value) => typeof value === "string" ? value : undefined;
|
|
13998
|
-
var
|
|
14040
|
+
var escapeHtml23 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13999
14041
|
var increment4 = (record, key) => {
|
|
14000
14042
|
record[key] = (record[key] ?? 0) + 1;
|
|
14001
14043
|
};
|
|
@@ -14175,10 +14217,10 @@ var summarizeVoiceSessions = async (options = {}) => {
|
|
|
14175
14217
|
var renderVoiceSessionsHTML = (sessions) => sessions.length === 0 ? '<p class="voice-sessions-empty">No voice sessions found.</p>' : [
|
|
14176
14218
|
'<div class="voice-sessions-list">',
|
|
14177
14219
|
...sessions.map((session) => [
|
|
14178
|
-
`<article class="voice-session-card ${
|
|
14220
|
+
`<article class="voice-session-card ${escapeHtml23(session.status)}">`,
|
|
14179
14221
|
'<div class="voice-session-card-header">',
|
|
14180
|
-
`<strong>${
|
|
14181
|
-
`<span>${
|
|
14222
|
+
`<strong>${escapeHtml23(session.sessionId)}</strong>`,
|
|
14223
|
+
`<span>${escapeHtml23(session.status)}</span>`,
|
|
14182
14224
|
"</div>",
|
|
14183
14225
|
"<dl>",
|
|
14184
14226
|
`<div><dt>Events</dt><dd>${String(session.eventCount)}</dd></div>`,
|
|
@@ -14186,9 +14228,9 @@ var renderVoiceSessionsHTML = (sessions) => sessions.length === 0 ? '<p class="v
|
|
|
14186
14228
|
`<div><dt>Transcripts</dt><dd>${String(session.transcriptCount)}</dd></div>`,
|
|
14187
14229
|
`<div><dt>Errors</dt><dd>${String(session.errorCount)}</dd></div>`,
|
|
14188
14230
|
"</dl>",
|
|
14189
|
-
session.latestOutcome ? `<p>Outcome: ${
|
|
14190
|
-
session.providers.length ? `<p>Providers: ${session.providers.map(
|
|
14191
|
-
session.replayHref ? `<p><a href="${
|
|
14231
|
+
session.latestOutcome ? `<p>Outcome: ${escapeHtml23(session.latestOutcome)}</p>` : "",
|
|
14232
|
+
session.providers.length ? `<p>Providers: ${session.providers.map(escapeHtml23).join(", ")}</p>` : "",
|
|
14233
|
+
session.replayHref ? `<p><a href="${escapeHtml23(session.replayHref)}">Open replay</a></p>` : "",
|
|
14192
14234
|
"</article>"
|
|
14193
14235
|
].join("")),
|
|
14194
14236
|
"</div>"
|
|
@@ -14417,7 +14459,7 @@ var assertVoiceAgentSquadContract = async (options) => {
|
|
|
14417
14459
|
import { Elysia as Elysia21 } from "elysia";
|
|
14418
14460
|
var DEFAULT_WARN_AFTER_MS = 1800;
|
|
14419
14461
|
var DEFAULT_FAIL_AFTER_MS = 3200;
|
|
14420
|
-
var
|
|
14462
|
+
var escapeHtml24 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
14421
14463
|
var firstNumber = (values) => values.filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
14422
14464
|
var getString10 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
14423
14465
|
var createTraceStageIndex = (events) => {
|
|
@@ -14531,11 +14573,11 @@ var summarizeVoiceTurnLatency = async (options) => {
|
|
|
14531
14573
|
var formatMs = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
|
|
14532
14574
|
var renderVoiceTurnLatencyHTML = (report, options = {}) => {
|
|
14533
14575
|
const title = options.title ?? "Voice Turn Latency";
|
|
14534
|
-
const turns = report.turns.map((turn) => `<article class="turn ${
|
|
14535
|
-
<header><div><p class="eyebrow">${
|
|
14536
|
-
<dl>${turn.stages.map((stage) => `<div><dt>${
|
|
14576
|
+
const turns = report.turns.map((turn) => `<article class="turn ${escapeHtml24(turn.status)}">
|
|
14577
|
+
<header><div><p class="eyebrow">${escapeHtml24(turn.sessionId)} \xB7 ${escapeHtml24(turn.turnId)}</p><h2>${escapeHtml24(turn.text || "Empty turn")}</h2></div><strong>${escapeHtml24(turn.status)}</strong></header>
|
|
14578
|
+
<dl>${turn.stages.map((stage) => `<div><dt>${escapeHtml24(stage.label)}</dt><dd>${escapeHtml24(formatMs(stage.valueMs))}</dd></div>`).join("")}</dl>
|
|
14537
14579
|
</article>`).join("");
|
|
14538
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
14580
|
+
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:#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>${escapeHtml24(title)}</h1><div class="summary"><span class="pill ${escapeHtml24(report.status)}">${escapeHtml24(report.status)}</span><span class="pill">${String(report.total)} turns</span><span class="pill">avg ${escapeHtml24(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>`;
|
|
14539
14581
|
};
|
|
14540
14582
|
var createVoiceTurnLatencyJSONHandler = (options) => async () => summarizeVoiceTurnLatency(options);
|
|
14541
14583
|
var createVoiceTurnLatencyHTMLHandler = (options) => async () => {
|
|
@@ -14562,7 +14604,7 @@ var createVoiceTurnLatencyRoutes = (options) => {
|
|
|
14562
14604
|
};
|
|
14563
14605
|
// src/liveLatency.ts
|
|
14564
14606
|
import { Elysia as Elysia22 } from "elysia";
|
|
14565
|
-
var
|
|
14607
|
+
var escapeHtml25 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
14566
14608
|
var percentile = (values, percentileValue) => {
|
|
14567
14609
|
if (values.length === 0) {
|
|
14568
14610
|
return;
|
|
@@ -14610,8 +14652,8 @@ var summarizeVoiceLiveLatency = async (options) => {
|
|
|
14610
14652
|
var formatMs2 = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
|
|
14611
14653
|
var renderVoiceLiveLatencyHTML = (report, options = {}) => {
|
|
14612
14654
|
const title = options.title ?? "Voice Live Latency";
|
|
14613
|
-
const rows = report.recent.map((sample) => `<tr><td>${
|
|
14614
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
14655
|
+
const rows = report.recent.map((sample) => `<tr><td>${escapeHtml25(sample.sessionId)}</td><td>${escapeHtml25(formatMs2(sample.latencyMs))}</td><td>${escapeHtml25(sample.status ?? "unknown")}</td><td>${escapeHtml25(new Date(sample.at).toLocaleString())}</td></tr>`).join("");
|
|
14656
|
+
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:#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>${escapeHtml25(title)}</h1><p>Recent real browser speech-to-assistant response measurements from persisted <code>client.live_latency</code> traces.</p><p class="status ${escapeHtml25(report.status)}">Status: ${escapeHtml25(report.status)}</p><section class="metrics"><article><span>p50</span><strong>${escapeHtml25(formatMs2(report.p50LatencyMs))}</strong></article><article><span>p95</span><strong>${escapeHtml25(formatMs2(report.p95LatencyMs))}</strong></article><article><span>Average</span><strong>${escapeHtml25(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>`;
|
|
14615
14657
|
};
|
|
14616
14658
|
var createVoiceLiveLatencyRoutes = (options) => {
|
|
14617
14659
|
const path = options.path ?? "/api/live-latency";
|
|
@@ -14635,7 +14677,7 @@ var createVoiceLiveLatencyRoutes = (options) => {
|
|
|
14635
14677
|
// src/turnQuality.ts
|
|
14636
14678
|
import { Elysia as Elysia23 } from "elysia";
|
|
14637
14679
|
var DEFAULT_CONFIDENCE_WARN_THRESHOLD = 0.72;
|
|
14638
|
-
var
|
|
14680
|
+
var escapeHtml26 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
14639
14681
|
var getTurnLatencyMs = (turn) => {
|
|
14640
14682
|
const firstTranscriptAt = turn.transcripts.map((transcript) => transcript.endedAtMs ?? transcript.startedAtMs).filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
14641
14683
|
if (firstTranscriptAt === undefined) {
|
|
@@ -14706,24 +14748,24 @@ var summarizeVoiceTurnQuality = async (options) => {
|
|
|
14706
14748
|
};
|
|
14707
14749
|
var renderVoiceTurnQualityHTML = (report, options = {}) => {
|
|
14708
14750
|
const title = options.title ?? "Voice Turn Quality";
|
|
14709
|
-
const turns = report.turns.map((turn) => `<article class="turn ${
|
|
14751
|
+
const turns = report.turns.map((turn) => `<article class="turn ${escapeHtml26(turn.status)}">
|
|
14710
14752
|
<div class="turn-header">
|
|
14711
14753
|
<div>
|
|
14712
|
-
<p class="eyebrow">${
|
|
14713
|
-
<h2>${
|
|
14754
|
+
<p class="eyebrow">${escapeHtml26(turn.sessionId)} \xB7 ${escapeHtml26(turn.turnId)}</p>
|
|
14755
|
+
<h2>${escapeHtml26(turn.text || "Empty turn")}</h2>
|
|
14714
14756
|
</div>
|
|
14715
|
-
<strong>${
|
|
14757
|
+
<strong>${escapeHtml26(turn.status)}</strong>
|
|
14716
14758
|
</div>
|
|
14717
14759
|
<dl>
|
|
14718
|
-
<div><dt>Source</dt><dd>${
|
|
14760
|
+
<div><dt>Source</dt><dd>${escapeHtml26(turn.source ?? "unknown")}</dd></div>
|
|
14719
14761
|
<div><dt>Confidence</dt><dd>${turn.averageConfidence === undefined ? "n/a" : `${Math.round(turn.averageConfidence * 100)}%`}</dd></div>
|
|
14720
|
-
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${
|
|
14721
|
-
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${
|
|
14762
|
+
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${escapeHtml26(turn.fallbackSelectionReason ?? "selected")})` : "no"}</dd></div>
|
|
14763
|
+
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${escapeHtml26(turn.correctionProvider)}` : ""}` : "none"}</dd></div>
|
|
14722
14764
|
<div><dt>Transcripts</dt><dd>${String(turn.selectedTranscriptCount)} selected \xB7 ${String(turn.finalTranscriptCount)} final \xB7 ${String(turn.partialTranscriptCount)} partial</dd></div>
|
|
14723
14765
|
<div><dt>Cost</dt><dd>${turn.costUnits === undefined ? "n/a" : String(turn.costUnits)}</dd></div>
|
|
14724
14766
|
</dl>
|
|
14725
14767
|
</article>`).join("");
|
|
14726
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
14768
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml26(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>${escapeHtml26(title)}</h1><div class="summary"><span class="pill ${escapeHtml26(report.status)}">${escapeHtml26(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>`;
|
|
14727
14769
|
};
|
|
14728
14770
|
var createVoiceTurnQualityJSONHandler = (options) => async () => summarizeVoiceTurnQuality(options);
|
|
14729
14771
|
var createVoiceTurnQualityHTMLHandler = (options) => async () => {
|
|
@@ -15538,7 +15580,7 @@ var resolveTwilioStreamParameters = async (parameters, input) => {
|
|
|
15538
15580
|
return parameters;
|
|
15539
15581
|
};
|
|
15540
15582
|
var joinUrlPath2 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
15541
|
-
var
|
|
15583
|
+
var escapeHtml27 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
15542
15584
|
var getWebhookVerificationUrl = (webhook, input) => {
|
|
15543
15585
|
if (!webhook?.verificationUrl) {
|
|
15544
15586
|
return;
|
|
@@ -15581,23 +15623,23 @@ var buildTwilioVoiceSetupStatus = async (options, input) => {
|
|
|
15581
15623
|
};
|
|
15582
15624
|
var renderTwilioVoiceSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
15583
15625
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Twilio setup</p>
|
|
15584
|
-
<h1>${
|
|
15626
|
+
<h1>${escapeHtml27(title)}</h1>
|
|
15585
15627
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
15586
15628
|
<section>
|
|
15587
15629
|
<h2>URLs</h2>
|
|
15588
15630
|
<ul>
|
|
15589
|
-
<li><strong>TwiML:</strong> <code>${
|
|
15590
|
-
<li><strong>Media stream:</strong> <code>${
|
|
15591
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
15631
|
+
<li><strong>TwiML:</strong> <code>${escapeHtml27(status.urls.twiml)}</code></li>
|
|
15632
|
+
<li><strong>Media stream:</strong> <code>${escapeHtml27(status.urls.stream)}</code></li>
|
|
15633
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml27(status.urls.webhook)}</code></li>
|
|
15592
15634
|
</ul>
|
|
15593
15635
|
</section>
|
|
15594
15636
|
<section>
|
|
15595
15637
|
<h2>Signing</h2>
|
|
15596
15638
|
<p>Mode: <code>${status.signing.mode}</code></p>
|
|
15597
|
-
${status.signing.verificationUrl ? `<p>Verification URL: <code>${
|
|
15639
|
+
${status.signing.verificationUrl ? `<p>Verification URL: <code>${escapeHtml27(status.signing.verificationUrl)}</code></p>` : ""}
|
|
15598
15640
|
</section>
|
|
15599
|
-
${status.missing.length ? `<section><h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
15600
|
-
${status.warnings.length ? `<section><h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
15641
|
+
${status.missing.length ? `<section><h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml27(name)}</code></li>`).join("")}</ul></section>` : ""}
|
|
15642
|
+
${status.warnings.length ? `<section><h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml27(warning)}</li>`).join("")}</ul></section>` : ""}
|
|
15601
15643
|
</main>`;
|
|
15602
15644
|
var extractTwilioStreamUrl = (twiml) => twiml.match(/<Stream\b[^>]*\surl="([^"]+)"/i)?.[1]?.replaceAll("&", "&");
|
|
15603
15645
|
var createSmokeCheck = (name, status, message, details) => ({
|
|
@@ -15608,20 +15650,20 @@ var createSmokeCheck = (name, status, message, details) => ({
|
|
|
15608
15650
|
});
|
|
15609
15651
|
var renderTwilioVoiceSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
15610
15652
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Twilio smoke test</p>
|
|
15611
|
-
<h1>${
|
|
15653
|
+
<h1>${escapeHtml27(title)}</h1>
|
|
15612
15654
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
15613
15655
|
<section>
|
|
15614
15656
|
<h2>Checks</h2>
|
|
15615
15657
|
<ul>
|
|
15616
|
-
${report.checks.map((check) => `<li><strong>${
|
|
15658
|
+
${report.checks.map((check) => `<li><strong>${escapeHtml27(check.name)}</strong>: ${escapeHtml27(check.status)}${check.message ? ` - ${escapeHtml27(check.message)}` : ""}</li>`).join("")}
|
|
15617
15659
|
</ul>
|
|
15618
15660
|
</section>
|
|
15619
15661
|
<section>
|
|
15620
15662
|
<h2>Observed URLs</h2>
|
|
15621
15663
|
<ul>
|
|
15622
|
-
<li><strong>TwiML:</strong> <code>${
|
|
15623
|
-
<li><strong>Stream:</strong> <code>${
|
|
15624
|
-
<li><strong>Webhook:</strong> <code>${
|
|
15664
|
+
<li><strong>TwiML:</strong> <code>${escapeHtml27(report.setup.urls.twiml)}</code></li>
|
|
15665
|
+
<li><strong>Stream:</strong> <code>${escapeHtml27(report.twiml?.streamUrl ?? report.setup.urls.stream)}</code></li>
|
|
15666
|
+
<li><strong>Webhook:</strong> <code>${escapeHtml27(report.setup.urls.webhook)}</code></li>
|
|
15625
15667
|
</ul>
|
|
15626
15668
|
</section>
|
|
15627
15669
|
</main>`;
|
|
@@ -16218,7 +16260,7 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
16218
16260
|
|
|
16219
16261
|
// src/telephony/plivo.ts
|
|
16220
16262
|
var escapeXml3 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
16221
|
-
var
|
|
16263
|
+
var escapeHtml28 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
16222
16264
|
var joinUrlPath3 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
16223
16265
|
var resolveRequestOrigin2 = (request) => {
|
|
16224
16266
|
const url = new URL(request.url);
|
|
@@ -16469,21 +16511,21 @@ var buildPlivoVoiceSetupStatus = async (options, input) => {
|
|
|
16469
16511
|
};
|
|
16470
16512
|
var renderPlivoSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
16471
16513
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Plivo setup</p>
|
|
16472
|
-
<h1>${
|
|
16514
|
+
<h1>${escapeHtml28(title)}</h1>
|
|
16473
16515
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
16474
16516
|
<ul>
|
|
16475
|
-
<li><strong>Answer XML:</strong> <code>${
|
|
16476
|
-
<li><strong>Audio stream:</strong> <code>${
|
|
16477
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
16517
|
+
<li><strong>Answer XML:</strong> <code>${escapeHtml28(status.urls.answer)}</code></li>
|
|
16518
|
+
<li><strong>Audio stream:</strong> <code>${escapeHtml28(status.urls.stream)}</code></li>
|
|
16519
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml28(status.urls.webhook)}</code></li>
|
|
16478
16520
|
</ul>
|
|
16479
|
-
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
16480
|
-
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
16521
|
+
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml28(name)}</code></li>`).join("")}</ul>` : ""}
|
|
16522
|
+
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml28(warning)}</li>`).join("")}</ul>` : ""}
|
|
16481
16523
|
</main>`;
|
|
16482
16524
|
var renderPlivoSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
16483
16525
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Plivo smoke test</p>
|
|
16484
|
-
<h1>${
|
|
16526
|
+
<h1>${escapeHtml28(title)}</h1>
|
|
16485
16527
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
16486
|
-
<ul>${report.checks.map((check) => `<li><strong>${
|
|
16528
|
+
<ul>${report.checks.map((check) => `<li><strong>${escapeHtml28(check.name)}</strong>: ${escapeHtml28(check.status)}${check.message ? ` - ${escapeHtml28(check.message)}` : ""}</li>`).join("")}</ul>
|
|
16487
16529
|
</main>`;
|
|
16488
16530
|
var runPlivoSmokeTest = async (input) => {
|
|
16489
16531
|
const setup = await buildPlivoVoiceSetupStatus(input.options, input);
|
|
@@ -16691,7 +16733,7 @@ var createPlivoVoiceRoutes = (options = {}) => {
|
|
|
16691
16733
|
import { Buffer as Buffer6 } from "buffer";
|
|
16692
16734
|
import { Elysia as Elysia27 } from "elysia";
|
|
16693
16735
|
var escapeXml4 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
16694
|
-
var
|
|
16736
|
+
var escapeHtml29 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
16695
16737
|
var joinUrlPath4 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
16696
16738
|
var resolveRequestOrigin3 = (request) => {
|
|
16697
16739
|
const url = new URL(request.url);
|
|
@@ -16892,21 +16934,21 @@ var buildTelnyxVoiceSetupStatus = async (options, input) => {
|
|
|
16892
16934
|
};
|
|
16893
16935
|
var renderTelnyxSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
16894
16936
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Telnyx setup</p>
|
|
16895
|
-
<h1>${
|
|
16937
|
+
<h1>${escapeHtml29(title)}</h1>
|
|
16896
16938
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
16897
16939
|
<ul>
|
|
16898
|
-
<li><strong>TeXML:</strong> <code>${
|
|
16899
|
-
<li><strong>Media stream:</strong> <code>${
|
|
16900
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
16940
|
+
<li><strong>TeXML:</strong> <code>${escapeHtml29(status.urls.texml)}</code></li>
|
|
16941
|
+
<li><strong>Media stream:</strong> <code>${escapeHtml29(status.urls.stream)}</code></li>
|
|
16942
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml29(status.urls.webhook)}</code></li>
|
|
16901
16943
|
</ul>
|
|
16902
|
-
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
16903
|
-
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
16944
|
+
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml29(name)}</code></li>`).join("")}</ul>` : ""}
|
|
16945
|
+
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml29(warning)}</li>`).join("")}</ul>` : ""}
|
|
16904
16946
|
</main>`;
|
|
16905
16947
|
var renderTelnyxSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
16906
16948
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Telnyx smoke test</p>
|
|
16907
|
-
<h1>${
|
|
16949
|
+
<h1>${escapeHtml29(title)}</h1>
|
|
16908
16950
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
16909
|
-
<ul>${report.checks.map((check) => `<li><strong>${
|
|
16951
|
+
<ul>${report.checks.map((check) => `<li><strong>${escapeHtml29(check.name)}</strong>: ${escapeHtml29(check.status)}${check.message ? ` - ${escapeHtml29(check.message)}` : ""}</li>`).join("")}</ul>
|
|
16910
16952
|
</main>`;
|
|
16911
16953
|
var runTelnyxSmokeTest = async (input) => {
|
|
16912
16954
|
const setup = await buildTelnyxVoiceSetupStatus(input.options, input);
|
|
@@ -17111,7 +17153,7 @@ var createTelnyxVoiceRoutes = (options = {}) => {
|
|
|
17111
17153
|
|
|
17112
17154
|
// src/telephony/matrix.ts
|
|
17113
17155
|
import { Elysia as Elysia28 } from "elysia";
|
|
17114
|
-
var
|
|
17156
|
+
var escapeHtml30 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
17115
17157
|
var labelForProvider = (provider) => provider.split("-").map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`).join(" ");
|
|
17116
17158
|
var resolveEntryStatus = (contract, setup, smoke) => {
|
|
17117
17159
|
if (!contract.pass || !setup.ready || smoke?.pass === false) {
|
|
@@ -17172,13 +17214,13 @@ var badgeStyles = {
|
|
|
17172
17214
|
};
|
|
17173
17215
|
var renderVoiceTelephonyCarrierMatrixHTML = (matrix, options = {}) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 1040px; margin: 40px auto; padding: 0 20px; color: #172033;">
|
|
17174
17216
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Carrier matrix</p>
|
|
17175
|
-
<h1 style="font-size: 34px; margin: 0 0 8px;">${
|
|
17217
|
+
<h1 style="font-size: 34px; margin: 0 0 8px;">${escapeHtml30(options.title ?? "AbsoluteJS Voice Carrier Matrix")}</h1>
|
|
17176
17218
|
<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>
|
|
17177
17219
|
<section style="display:grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px;">
|
|
17178
17220
|
${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);">
|
|
17179
17221
|
<div style="display:flex; justify-content:space-between; gap:12px; align-items:center;">
|
|
17180
|
-
<h2 style="margin:0; font-size:20px;">${
|
|
17181
|
-
<span style="border:1px solid; border-radius:999px; padding:4px 10px; font-size:12px; font-weight:700; ${badgeStyles[entry.status]}">${
|
|
17222
|
+
<h2 style="margin:0; font-size:20px;">${escapeHtml30(entry.name)}</h2>
|
|
17223
|
+
<span style="border:1px solid; border-radius:999px; padding:4px 10px; font-size:12px; font-weight:700; ${badgeStyles[entry.status]}">${escapeHtml30(entry.status.toUpperCase())}</span>
|
|
17182
17224
|
</div>
|
|
17183
17225
|
<dl style="display:grid; grid-template-columns: 1fr 1fr; gap:8px 12px; margin:16px 0;">
|
|
17184
17226
|
<dt style="color:#64748b;">Setup</dt><dd style="margin:0; font-weight:700;">${entry.ready ? "Ready" : "Needs attention"}</dd>
|
|
@@ -17186,9 +17228,9 @@ ${matrix.entries.map((entry) => `<article style="border:1px solid #d9e2ec; borde
|
|
|
17186
17228
|
<dt style="color:#64748b;">Smoke</dt><dd style="margin:0; font-weight:700;">${entry.smoke ? entry.smoke.pass ? "Pass" : "Fail" : "Missing"}</dd>
|
|
17187
17229
|
<dt style="color:#64748b;">Contract</dt><dd style="margin:0; font-weight:700;">${entry.contract.pass ? "Pass" : "Fail"}</dd>
|
|
17188
17230
|
</dl>
|
|
17189
|
-
<p style="margin:0 0 8px; color:#475569;"><strong>Stream:</strong> <code>${
|
|
17190
|
-
<p style="margin:0 0 12px; color:#475569;"><strong>Webhook:</strong> <code>${
|
|
17191
|
-
${entry.issues.length ? `<ul style="margin:12px 0 0; padding-left:18px;">${entry.issues.map((issue) => `<li>${
|
|
17231
|
+
<p style="margin:0 0 8px; color:#475569;"><strong>Stream:</strong> <code>${escapeHtml30(entry.setup.urls.stream || "missing")}</code></p>
|
|
17232
|
+
<p style="margin:0 0 12px; color:#475569;"><strong>Webhook:</strong> <code>${escapeHtml30(entry.setup.urls.webhook || "missing")}</code></p>
|
|
17233
|
+
${entry.issues.length ? `<ul style="margin:12px 0 0; padding-left:18px;">${entry.issues.map((issue) => `<li>${escapeHtml30(issue.severity)}: ${escapeHtml30(issue.message)}</li>`).join("")}</ul>` : '<p style="margin:12px 0 0; color:#166534;">No contract issues.</p>'}
|
|
17192
17234
|
</article>`).join("")}
|
|
17193
17235
|
</section>
|
|
17194
17236
|
</main>`;
|
|
@@ -17224,7 +17266,7 @@ var defaultRequirements = [
|
|
|
17224
17266
|
"lifecycle-outcome",
|
|
17225
17267
|
"no-session-error"
|
|
17226
17268
|
];
|
|
17227
|
-
var
|
|
17269
|
+
var escapeHtml31 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
17228
17270
|
var payloadType = (event) => typeof event.payload.type === "string" ? event.payload.type : undefined;
|
|
17229
17271
|
var hasTextPayload = (event) => ["text", "assistantText", "transcript"].some((key) => {
|
|
17230
17272
|
const value = event.payload[key];
|
|
@@ -17333,10 +17375,10 @@ var resolveHandlerOptions = async (options, input) => ({
|
|
|
17333
17375
|
});
|
|
17334
17376
|
var renderVoicePhoneAgentProductionSmokeHTML = (report, options = {}) => {
|
|
17335
17377
|
const title = options.title ?? "AbsoluteJS Voice Phone Smoke Contract";
|
|
17336
|
-
const issues = report.issues.map((issue) => `<li><strong>${
|
|
17337
|
-
const outcomes = report.observed.lifecycleOutcomes.map((outcome) => `<span class="pill">${
|
|
17338
|
-
const requirements = report.required.map((requirement) => `<span class="pill">${
|
|
17339
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
17378
|
+
const issues = report.issues.map((issue) => `<li><strong>${escapeHtml31(issue.requirement)}</strong>: ${escapeHtml31(issue.message)}</li>`).join("");
|
|
17379
|
+
const outcomes = report.observed.lifecycleOutcomes.map((outcome) => `<span class="pill">${escapeHtml31(outcome)}</span>`).join("");
|
|
17380
|
+
const requirements = report.required.map((requirement) => `<span class="pill">${escapeHtml31(requirement)}</span>`).join("");
|
|
17381
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml31(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>${escapeHtml31(title)}</h1><p class="status ${report.pass ? "pass" : "fail"}">${report.pass ? "PASS" : "FAIL"}</p><p>Contract <code>${escapeHtml31(report.contractId)}</code>${report.provider ? ` for <code>${escapeHtml31(report.provider)}</code>` : ""}${report.sessionId ? ` on session <code>${escapeHtml31(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>`;
|
|
17340
17382
|
};
|
|
17341
17383
|
var createVoicePhoneAgentProductionSmokeJSONHandler = (options) => async ({
|
|
17342
17384
|
query,
|
|
@@ -17402,7 +17444,7 @@ var PHONE_AGENT_LIFECYCLE_STAGES = [
|
|
|
17402
17444
|
"completed",
|
|
17403
17445
|
"failed"
|
|
17404
17446
|
];
|
|
17405
|
-
var
|
|
17447
|
+
var escapeHtml32 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
17406
17448
|
var loadRouteJson = async (input) => {
|
|
17407
17449
|
const response = await input.app.handle(new Request(new URL(input.path, input.origin).toString(), {
|
|
17408
17450
|
headers: {
|
|
@@ -17462,18 +17504,18 @@ var renderVoicePhoneAgentSetupHTML = (report) => {
|
|
|
17462
17504
|
const entry = report.matrix?.entries.find((candidate) => candidate.provider === carrier.provider && (candidate.name === carrier.name || candidate.name === (carrier.name ?? carrier.provider)));
|
|
17463
17505
|
const urls = entry?.setup.urls;
|
|
17464
17506
|
const primaryUrl = carrier.provider === "plivo" ? urls?.twiml : urls?.twiml;
|
|
17465
|
-
return `<tr><td>${
|
|
17507
|
+
return `<tr><td>${escapeHtml32(carrier.name ?? carrier.provider)}</td><td>${escapeHtml32(carrier.provider)}</td><td><code>${escapeHtml32(carrier.setupPath || "disabled")}</code></td><td><code>${escapeHtml32(carrier.smokePath || "disabled")}</code></td><td>${entry ? `<span class="${escapeHtml32(entry.status)}">${escapeHtml32(entry.status.toUpperCase())}</span>` : "unknown"}</td><td>${primaryUrl ? `<code>${escapeHtml32(primaryUrl)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.webhook ? `<code>${escapeHtml32(urls.webhook)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.stream ? `<code>${escapeHtml32(urls.stream)}</code>` : '<span class="muted">missing</span>'}</td></tr>`;
|
|
17466
17508
|
}).join("");
|
|
17467
|
-
const stageList = report.lifecycleStages.map((stage) => `<li><code>${
|
|
17509
|
+
const stageList = report.lifecycleStages.map((stage) => `<li><code>${escapeHtml32(stage)}</code></li>`).join("");
|
|
17468
17510
|
const checklist = report.carriers.map((carrier) => {
|
|
17469
17511
|
const entry = report.matrix?.entries.find((candidate) => candidate.provider === carrier.provider && (candidate.name === carrier.name || candidate.name === (carrier.name ?? carrier.provider)));
|
|
17470
17512
|
const urls = entry?.setup.urls;
|
|
17471
17513
|
const answerLabel = carrier.provider === "telnyx" ? "TeXML URL" : carrier.provider === "plivo" ? "Answer URL" : "TwiML URL";
|
|
17472
17514
|
const answerUrl = urls?.twiml;
|
|
17473
|
-
const issueList = entry?.issues.map((issue) => `<li>${
|
|
17474
|
-
return `<article><h3>${
|
|
17515
|
+
const issueList = entry?.issues.map((issue) => `<li>${escapeHtml32(issue.severity)}: ${escapeHtml32(issue.message)}</li>`).join("") ?? "";
|
|
17516
|
+
return `<article><h3>${escapeHtml32(carrier.name ?? carrier.provider)}</h3><ol><li>Set ${escapeHtml32(answerLabel)} to <code>${escapeHtml32(answerUrl ?? "missing")}</code>.</li><li>Set status webhook to <code>${escapeHtml32(urls?.webhook ?? "missing")}</code>.</li><li>Allow media stream URL <code>${escapeHtml32(urls?.stream ?? "missing")}</code>.</li><li>Open setup: ${carrier.setupPath ? `<a href="${escapeHtml32(carrier.setupPath)}?format=html">${escapeHtml32(carrier.setupPath)}</a>` : '<span class="muted">disabled</span>'}.</li><li>Run smoke: ${carrier.smokePath ? `<a href="${escapeHtml32(carrier.smokePath)}?format=html">${escapeHtml32(carrier.smokePath)}</a>` : '<span class="muted">disabled</span>'}.</li>${report.productionSmokePath ? `<li>Certify production smoke traces: <a href="${escapeHtml32(report.productionSmokePath.replace("/api/", "/"))}?sessionId=">${escapeHtml32(report.productionSmokePath)}</a>.</li>` : ""}</ol>${issueList ? `<ul class="issues">${issueList}</ul>` : '<p class="pass">No carrier contract issues.</p>'}</article>`;
|
|
17475
17517
|
}).join("");
|
|
17476
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
17518
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml32(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>${escapeHtml32(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="${escapeHtml32(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>`;
|
|
17477
17519
|
};
|
|
17478
17520
|
var createVoicePhoneAgent = (options) => {
|
|
17479
17521
|
const carrierSummaries = options.carriers.map((carrier) => ({
|
|
@@ -19495,7 +19537,7 @@ var createOpenAIVoiceTTS = (options) => {
|
|
|
19495
19537
|
};
|
|
19496
19538
|
// src/providerCapabilities.ts
|
|
19497
19539
|
import { Elysia as Elysia31 } from "elysia";
|
|
19498
|
-
var
|
|
19540
|
+
var escapeHtml33 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
19499
19541
|
var fromProviderList = (kind, providers, options) => (providers ?? []).map((provider) => ({
|
|
19500
19542
|
configured: true,
|
|
19501
19543
|
features: options.features?.[provider],
|
|
@@ -19558,27 +19600,27 @@ var summarizeVoiceProviderCapabilities = async (options) => {
|
|
|
19558
19600
|
var renderVoiceProviderCapabilityHTML = (report, options = {}) => {
|
|
19559
19601
|
const title = options.title ?? "Voice Provider Capabilities";
|
|
19560
19602
|
const cards = report.capabilities.map((capability) => {
|
|
19561
|
-
const features = (capability.features ?? []).map((feature) => `<span class="pill">${
|
|
19562
|
-
return `<article class="card ${
|
|
19603
|
+
const features = (capability.features ?? []).map((feature) => `<span class="pill">${escapeHtml33(feature)}</span>`).join("");
|
|
19604
|
+
return `<article class="card ${escapeHtml33(capability.status)}">
|
|
19563
19605
|
<div class="card-header">
|
|
19564
19606
|
<div>
|
|
19565
|
-
<p class="eyebrow">${
|
|
19566
|
-
<h2>${
|
|
19607
|
+
<p class="eyebrow">${escapeHtml33(capability.kind)}</p>
|
|
19608
|
+
<h2>${escapeHtml33(capability.label ?? capability.provider)}</h2>
|
|
19567
19609
|
</div>
|
|
19568
|
-
<strong>${
|
|
19610
|
+
<strong>${escapeHtml33(capability.status)}</strong>
|
|
19569
19611
|
</div>
|
|
19570
|
-
${capability.description ? `<p>${
|
|
19612
|
+
${capability.description ? `<p>${escapeHtml33(capability.description)}</p>` : ""}
|
|
19571
19613
|
<dl>
|
|
19572
19614
|
<div><dt>Configured</dt><dd>${capability.configured ? "yes" : "no"}</dd></div>
|
|
19573
19615
|
<div><dt>Selected</dt><dd>${capability.selected ? "yes" : "no"}</dd></div>
|
|
19574
|
-
<div><dt>Model</dt><dd>${
|
|
19616
|
+
<div><dt>Model</dt><dd>${escapeHtml33(capability.model ?? "default")}</dd></div>
|
|
19575
19617
|
<div><dt>Runs</dt><dd>${String(capability.health?.runCount ?? 0)}</dd></div>
|
|
19576
19618
|
<div><dt>Errors</dt><dd>${String(capability.health?.errorCount ?? 0)}</dd></div>
|
|
19577
19619
|
</dl>
|
|
19578
19620
|
${features ? `<div class="features">${features}</div>` : ""}
|
|
19579
19621
|
</article>`;
|
|
19580
19622
|
}).join("");
|
|
19581
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
19623
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml33(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>${escapeHtml33(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>`;
|
|
19582
19624
|
};
|
|
19583
19625
|
var createVoiceProviderCapabilityJSONHandler = (options) => async () => summarizeVoiceProviderCapabilities(options);
|
|
19584
19626
|
var createVoiceProviderCapabilityHTMLHandler = (options) => async () => {
|
|
@@ -19605,7 +19647,7 @@ var createVoiceProviderCapabilityRoutes = (options) => {
|
|
|
19605
19647
|
};
|
|
19606
19648
|
// src/resilienceRoutes.ts
|
|
19607
19649
|
import { Elysia as Elysia32 } from "elysia";
|
|
19608
|
-
var
|
|
19650
|
+
var escapeHtml34 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
19609
19651
|
var getString11 = (value) => typeof value === "string" ? value : undefined;
|
|
19610
19652
|
var getNumber6 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
19611
19653
|
var getBoolean2 = (value) => value === true;
|
|
@@ -19752,13 +19794,13 @@ var summarizeRoutingEvents = (events) => {
|
|
|
19752
19794
|
};
|
|
19753
19795
|
var renderProviderCards = (title, providers) => {
|
|
19754
19796
|
if (providers.length === 0) {
|
|
19755
|
-
return `<p class="muted">No ${
|
|
19797
|
+
return `<p class="muted">No ${escapeHtml34(title)} provider health yet.</p>`;
|
|
19756
19798
|
}
|
|
19757
19799
|
return `<div class="provider-grid">${providers.map((provider) => `
|
|
19758
|
-
<article class="card provider ${
|
|
19800
|
+
<article class="card provider ${escapeHtml34(provider.status)}">
|
|
19759
19801
|
<div class="card-header">
|
|
19760
|
-
<strong>${
|
|
19761
|
-
<span>${
|
|
19802
|
+
<strong>${escapeHtml34(provider.provider)}</strong>
|
|
19803
|
+
<span>${escapeHtml34(provider.status)}${provider.recommended ? " \xB7 recommended" : ""}</span>
|
|
19762
19804
|
</div>
|
|
19763
19805
|
<dl>
|
|
19764
19806
|
<div><dt>Runs</dt><dd>${provider.runCount}</dd></div>
|
|
@@ -19767,7 +19809,7 @@ var renderProviderCards = (title, providers) => {
|
|
|
19767
19809
|
<div><dt>Timeouts</dt><dd>${provider.timeoutCount}</dd></div>
|
|
19768
19810
|
<div><dt>Fallbacks</dt><dd>${provider.fallbackCount}</dd></div>
|
|
19769
19811
|
</dl>
|
|
19770
|
-
${provider.lastError ? `<p class="muted">${
|
|
19812
|
+
${provider.lastError ? `<p class="muted">${escapeHtml34(provider.lastError)}</p>` : ""}
|
|
19771
19813
|
</article>
|
|
19772
19814
|
`).join("")}</div>`;
|
|
19773
19815
|
};
|
|
@@ -19776,24 +19818,24 @@ var renderTimeline2 = (events) => {
|
|
|
19776
19818
|
return '<p class="muted">No provider routing events yet. Run the app or simulate provider failover.</p>';
|
|
19777
19819
|
}
|
|
19778
19820
|
return `<div class="timeline">${events.slice(0, 40).map((event) => `
|
|
19779
|
-
<article class="card event ${
|
|
19821
|
+
<article class="card event ${escapeHtml34(event.status ?? "unknown")}">
|
|
19780
19822
|
<div class="card-header">
|
|
19781
|
-
<strong>${
|
|
19823
|
+
<strong>${escapeHtml34(event.kind.toUpperCase())} ${escapeHtml34(event.operation ?? "generate")}</strong>
|
|
19782
19824
|
<span>${new Date(event.at).toLocaleString()}</span>
|
|
19783
19825
|
</div>
|
|
19784
19826
|
<p>
|
|
19785
|
-
<span class="pill">${
|
|
19786
|
-
<span class="pill">provider: ${
|
|
19787
|
-
${event.fallbackProvider ? `<span class="pill">fallback: ${
|
|
19827
|
+
<span class="pill">${escapeHtml34(event.status ?? "unknown")}</span>
|
|
19828
|
+
<span class="pill">provider: ${escapeHtml34(event.provider ?? "unknown")}</span>
|
|
19829
|
+
${event.fallbackProvider ? `<span class="pill">fallback: ${escapeHtml34(event.fallbackProvider)}</span>` : ""}
|
|
19788
19830
|
${event.timedOut ? '<span class="pill danger">timed out</span>' : ""}
|
|
19789
19831
|
</p>
|
|
19790
19832
|
<dl>
|
|
19791
19833
|
<div><dt>Attempt</dt><dd>${event.attempt ?? 0}</dd></div>
|
|
19792
19834
|
<div><dt>Elapsed</dt><dd>${event.elapsedMs ?? 0}ms</dd></div>
|
|
19793
19835
|
<div><dt>Budget</dt><dd>${event.latencyBudgetMs ?? 0}ms</dd></div>
|
|
19794
|
-
<div><dt>Session</dt><dd>${
|
|
19836
|
+
<div><dt>Session</dt><dd>${escapeHtml34(event.sessionId)}</dd></div>
|
|
19795
19837
|
</dl>
|
|
19796
|
-
${event.error ? `<p class="muted">${
|
|
19838
|
+
${event.error ? `<p class="muted">${escapeHtml34(event.error)}</p>` : ""}
|
|
19797
19839
|
</article>
|
|
19798
19840
|
`).join("")}</div>`;
|
|
19799
19841
|
};
|
|
@@ -19803,9 +19845,9 @@ var renderSessionKind = (kind, summary) => {
|
|
|
19803
19845
|
const status = latest?.status ?? "idle";
|
|
19804
19846
|
const fallback = latest?.fallbackProvider && latest.fallbackProvider !== provider ? ` -> ${latest.fallbackProvider}` : "";
|
|
19805
19847
|
return `<div>
|
|
19806
|
-
<dt>${
|
|
19807
|
-
<dd>${
|
|
19808
|
-
<small>${
|
|
19848
|
+
<dt>${escapeHtml34(kind.toUpperCase())}</dt>
|
|
19849
|
+
<dd>${escapeHtml34(provider)}${escapeHtml34(fallback)}</dd>
|
|
19850
|
+
<small>${escapeHtml34(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>
|
|
19809
19851
|
</div>`;
|
|
19810
19852
|
};
|
|
19811
19853
|
var renderSessionSummaries = (sessions) => {
|
|
@@ -19813,10 +19855,10 @@ var renderSessionSummaries = (sessions) => {
|
|
|
19813
19855
|
return '<p class="muted">No call-level routing summaries yet. Run a voice session or provider simulation.</p>';
|
|
19814
19856
|
}
|
|
19815
19857
|
return `<div class="session-grid">${sessions.slice(0, 12).map((session) => `
|
|
19816
|
-
<article class="card session ${
|
|
19858
|
+
<article class="card session ${escapeHtml34(session.status)}">
|
|
19817
19859
|
<div class="card-header">
|
|
19818
|
-
<strong>${
|
|
19819
|
-
<span>${
|
|
19860
|
+
<strong>${escapeHtml34(session.sessionId)}</strong>
|
|
19861
|
+
<span>${escapeHtml34(session.status)}</span>
|
|
19820
19862
|
</div>
|
|
19821
19863
|
<p>
|
|
19822
19864
|
<span class="pill">${session.eventCount} routing events</span>
|
|
@@ -19843,26 +19885,26 @@ var renderSimulationControls = (kind, simulation) => {
|
|
|
19843
19885
|
const pathPrefix = simulation.pathPrefix ?? `/api/${kind}-simulate`;
|
|
19844
19886
|
const failureProviders = simulation.failureProviders ?? configuredProviders.map(({ provider }) => provider);
|
|
19845
19887
|
const canFail = (provider) => configuredProviders.some((entry) => entry.provider === provider) && (!simulation.fallbackRequiredProvider || configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider));
|
|
19846
|
-
return `<div class="simulate-panel" data-sim-kind="${kind}" data-sim-prefix="${
|
|
19847
|
-
<p class="muted">${
|
|
19888
|
+
return `<div class="simulate-panel" data-sim-kind="${kind}" data-sim-prefix="${escapeHtml34(pathPrefix)}">
|
|
19889
|
+
<p class="muted">${escapeHtml34(simulation.failureMessage ?? `Simulate ${kind.toUpperCase()} provider failure without changing provider credentials.`)}</p>
|
|
19848
19890
|
<div class="simulate-actions">
|
|
19849
|
-
${failureProviders.map((provider) => `<button type="button" data-provider-fail="${
|
|
19850
|
-
${configuredProviders.map((provider) => `<button type="button" data-provider-recover="${
|
|
19891
|
+
${failureProviders.map((provider) => `<button type="button" data-provider-fail="${escapeHtml34(provider)}"${canFail(provider) ? "" : " disabled"}>Simulate ${escapeHtml34(provider)} ${kind.toUpperCase()} failure</button>`).join("")}
|
|
19892
|
+
${configuredProviders.map((provider) => `<button type="button" data-provider-recover="${escapeHtml34(provider.provider)}">Mark ${escapeHtml34(provider.provider)} recovered</button>`).join("")}
|
|
19851
19893
|
</div>
|
|
19852
|
-
${simulation.fallbackRequiredProvider && !configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider) ? `<p class="muted">${
|
|
19894
|
+
${simulation.fallbackRequiredProvider && !configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider) ? `<p class="muted">${escapeHtml34(simulation.fallbackRequiredMessage ?? `Configure ${simulation.fallbackRequiredProvider} to enable fallback simulation.`)}</p>` : ""}
|
|
19853
19895
|
<pre class="simulate-output" hidden></pre>
|
|
19854
19896
|
</div>`;
|
|
19855
19897
|
};
|
|
19856
19898
|
var renderVoiceResilienceHTML = (input) => {
|
|
19857
19899
|
const summary = summarizeRoutingEvents(input.routingEvents);
|
|
19858
|
-
const kindCounts = [...summary.byKind.entries()].map(([kind, count]) => `<span class="pill">${
|
|
19859
|
-
const links = input.links?.length ? input.links.map((link) => `<a href="${
|
|
19900
|
+
const kindCounts = [...summary.byKind.entries()].map(([kind, count]) => `<span class="pill">${escapeHtml34(kind)}: ${String(count)}</span>`).join("");
|
|
19901
|
+
const links = input.links?.length ? input.links.map((link) => `<a href="${escapeHtml34(link.href)}">${escapeHtml34(link.label)}</a>`).join(" \xB7 ") : "";
|
|
19860
19902
|
return `<!doctype html>
|
|
19861
19903
|
<html lang="en">
|
|
19862
19904
|
<head>
|
|
19863
19905
|
<meta charset="utf-8" />
|
|
19864
19906
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
19865
|
-
<title>${
|
|
19907
|
+
<title>${escapeHtml34(input.title ?? "AbsoluteJS Voice Resilience")}</title>
|
|
19866
19908
|
<style>
|
|
19867
19909
|
:root { color-scheme: dark; }
|
|
19868
19910
|
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; }
|
|
@@ -20084,7 +20126,7 @@ var assertVoiceProviderRoutingContract = async (options) => {
|
|
|
20084
20126
|
};
|
|
20085
20127
|
// src/productionReadiness.ts
|
|
20086
20128
|
import { Elysia as Elysia33 } from "elysia";
|
|
20087
|
-
var
|
|
20129
|
+
var escapeHtml35 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
20088
20130
|
var rollupStatus2 = (checks) => checks.some((check) => check.status === "fail") ? "fail" : checks.some((check) => check.status === "warn") ? "warn" : "pass";
|
|
20089
20131
|
var carrierStatus = (matrix) => matrix.summary.failing > 0 ? "fail" : matrix.summary.warnings > 0 || matrix.summary.ready < matrix.summary.providers ? "warn" : "pass";
|
|
20090
20132
|
var resolveCarriers = async (options, input) => {
|
|
@@ -20232,6 +20274,31 @@ var summarizeAuditDeliveries = async (options) => {
|
|
|
20232
20274
|
warnPendingAfterMs
|
|
20233
20275
|
};
|
|
20234
20276
|
};
|
|
20277
|
+
var summarizeOpsActionHistory = async (options) => {
|
|
20278
|
+
if (!options.opsActionHistory) {
|
|
20279
|
+
return;
|
|
20280
|
+
}
|
|
20281
|
+
const opsActionHistory = "list" in options.opsActionHistory ? {
|
|
20282
|
+
failOnFailedActions: true,
|
|
20283
|
+
store: options.opsActionHistory,
|
|
20284
|
+
warnWhenEmpty: false
|
|
20285
|
+
} : {
|
|
20286
|
+
failOnFailedActions: options.opsActionHistory.failOnFailedActions ?? true,
|
|
20287
|
+
store: options.opsActionHistory.store,
|
|
20288
|
+
warnWhenEmpty: options.opsActionHistory.warnWhenEmpty ?? false
|
|
20289
|
+
};
|
|
20290
|
+
const report = await buildVoiceOpsActionHistoryReport({
|
|
20291
|
+
audit: opsActionHistory.store
|
|
20292
|
+
});
|
|
20293
|
+
const status = report.failed > 0 ? opsActionHistory.failOnFailedActions ? "fail" : "warn" : report.total === 0 && opsActionHistory.warnWhenEmpty ? "warn" : "pass";
|
|
20294
|
+
return {
|
|
20295
|
+
failed: report.failed,
|
|
20296
|
+
passed: report.passed,
|
|
20297
|
+
status,
|
|
20298
|
+
total: report.total,
|
|
20299
|
+
warnWhenEmpty: opsActionHistory.warnWhenEmpty
|
|
20300
|
+
};
|
|
20301
|
+
};
|
|
20235
20302
|
var summarizeTraceDeliveries = async (options) => {
|
|
20236
20303
|
if (!options.traceDeliveries) {
|
|
20237
20304
|
return;
|
|
@@ -20336,6 +20403,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
20336
20403
|
handoffs,
|
|
20337
20404
|
audit,
|
|
20338
20405
|
auditDeliveries,
|
|
20406
|
+
opsActionHistory,
|
|
20339
20407
|
traceDeliveries,
|
|
20340
20408
|
deliveryRuntimeSummary,
|
|
20341
20409
|
carriers,
|
|
@@ -20365,6 +20433,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
20365
20433
|
summarizeVoiceHandoffHealth({ events }),
|
|
20366
20434
|
summarizeAuditEvidence(options),
|
|
20367
20435
|
summarizeAuditDeliveries(options),
|
|
20436
|
+
summarizeOpsActionHistory(options),
|
|
20368
20437
|
summarizeTraceDeliveries(options),
|
|
20369
20438
|
resolveDeliveryRuntime(options, { query, request }),
|
|
20370
20439
|
resolveCarriers(options, { query, request }),
|
|
@@ -20647,6 +20716,23 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
20647
20716
|
]
|
|
20648
20717
|
});
|
|
20649
20718
|
}
|
|
20719
|
+
if (opsActionHistory) {
|
|
20720
|
+
checks.push({
|
|
20721
|
+
detail: opsActionHistory.status === "pass" ? opsActionHistory.total > 0 ? `${opsActionHistory.passed} operator action(s) completed successfully.` : "No operator action failures are recorded." : opsActionHistory.failed > 0 ? `${opsActionHistory.failed} operator action(s) failed and need review.` : "No operator action history is recorded yet.",
|
|
20722
|
+
href: options.links?.opsActions ?? "/voice/ops-actions",
|
|
20723
|
+
label: "Operator action history",
|
|
20724
|
+
proofSource: proofSource("opsActions", "operatorActions"),
|
|
20725
|
+
status: opsActionHistory.status,
|
|
20726
|
+
value: `${opsActionHistory.passed}/${opsActionHistory.total}`,
|
|
20727
|
+
actions: opsActionHistory.status === "pass" ? [] : [
|
|
20728
|
+
{
|
|
20729
|
+
description: "Open operator action history and inspect failed control-plane actions.",
|
|
20730
|
+
href: options.links?.opsActions ?? "/voice/ops-actions",
|
|
20731
|
+
label: "Open action history"
|
|
20732
|
+
}
|
|
20733
|
+
]
|
|
20734
|
+
});
|
|
20735
|
+
}
|
|
20650
20736
|
if (traceDeliveries) {
|
|
20651
20737
|
checks.push({
|
|
20652
20738
|
detail: traceDeliveries.status === "pass" ? "Trace sink deliveries are clear." : traceDeliveries.staleFailing > 0 ? `${traceDeliveries.staleFailing} trace delivery item(s) are stale past ${Math.round(traceDeliveries.failPendingAfterMs / 1000)}s.` : traceDeliveries.failed > 0 || traceDeliveries.deadLettered > 0 ? `${traceDeliveries.failed} failed and ${traceDeliveries.deadLettered} dead-lettered trace delivery item(s).` : `${traceDeliveries.pending} trace delivery item(s) are pending.`,
|
|
@@ -20710,6 +20796,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
20710
20796
|
handoffs: "/handoffs",
|
|
20711
20797
|
handoffRetry: "/api/voice-handoffs/retry",
|
|
20712
20798
|
liveLatency: "/traces",
|
|
20799
|
+
opsActions: "/voice/ops-actions",
|
|
20713
20800
|
phoneAgentSmoke: "/sessions",
|
|
20714
20801
|
providerRoutingContracts: "/resilience",
|
|
20715
20802
|
quality: "/quality",
|
|
@@ -20733,6 +20820,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
20733
20820
|
total: handoffs.total
|
|
20734
20821
|
},
|
|
20735
20822
|
liveLatency,
|
|
20823
|
+
opsActionHistory,
|
|
20736
20824
|
providers: {
|
|
20737
20825
|
degraded: degradedProviders,
|
|
20738
20826
|
total: providers.length
|
|
@@ -20759,20 +20847,20 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
20759
20847
|
var renderVoiceProductionReadinessHTML = (report, options = {}) => {
|
|
20760
20848
|
const title = options.title ?? "AbsoluteJS Voice Production Readiness";
|
|
20761
20849
|
const checks = report.checks.map((check, index) => {
|
|
20762
|
-
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${
|
|
20763
|
-
return `<article class="check ${
|
|
20850
|
+
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${escapeHtml35(action.href)}">${escapeHtml35(action.label)}</button>` : `<a href="${escapeHtml35(action.href)}">${escapeHtml35(action.label)}</a>`).join("");
|
|
20851
|
+
return `<article class="check ${escapeHtml35(check.status)}">
|
|
20764
20852
|
<div>
|
|
20765
|
-
<span>${
|
|
20766
|
-
<h2>${
|
|
20767
|
-
${check.detail ? `<p>${
|
|
20768
|
-
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${
|
|
20853
|
+
<span>${escapeHtml35(check.status.toUpperCase())}</span>
|
|
20854
|
+
<h2>${escapeHtml35(check.label)}</h2>
|
|
20855
|
+
${check.detail ? `<p>${escapeHtml35(check.detail)}</p>` : ""}
|
|
20856
|
+
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${escapeHtml35(check.proofSource.href)}">${escapeHtml35(check.proofSource.sourceLabel)}</a>` : escapeHtml35(check.proofSource.sourceLabel)}${check.proofSource.detail ? ` \xB7 ${escapeHtml35(check.proofSource.detail)}` : ""}</p>` : ""}
|
|
20769
20857
|
${actions ? `<p class="actions">${actions}</p>` : ""}
|
|
20770
20858
|
</div>
|
|
20771
|
-
<strong>${
|
|
20772
|
-
${check.href ? `<a href="${
|
|
20859
|
+
<strong>${escapeHtml35(String(check.value ?? check.status))}</strong>
|
|
20860
|
+
${check.href ? `<a href="${escapeHtml35(check.href)}">Open surface</a>` : ""}
|
|
20773
20861
|
</article>`;
|
|
20774
20862
|
}).join("");
|
|
20775
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
20863
|
+
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{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>${escapeHtml35(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 ${escapeHtml35(report.status)}">Overall: ${escapeHtml35(report.status.toUpperCase())}</p><p>Checked ${escapeHtml35(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>`;
|
|
20776
20864
|
};
|
|
20777
20865
|
var createVoiceProductionReadinessRoutes = (options) => {
|
|
20778
20866
|
const path = options.path ?? "/api/production-readiness";
|
|
@@ -20834,7 +20922,7 @@ var DEFAULT_LINKS = [
|
|
|
20834
20922
|
label: "Handoffs"
|
|
20835
20923
|
}
|
|
20836
20924
|
];
|
|
20837
|
-
var
|
|
20925
|
+
var escapeHtml36 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
20838
20926
|
var countProviderStatuses = (providers) => {
|
|
20839
20927
|
const degradedStatuses = new Set(["degraded", "rate-limited", "suppressed"]);
|
|
20840
20928
|
const healthy = providers.filter((provider) => provider.status === "healthy").length;
|
|
@@ -20903,16 +20991,16 @@ var buildVoiceOpsConsoleReport = async (options) => {
|
|
|
20903
20991
|
trace
|
|
20904
20992
|
};
|
|
20905
20993
|
};
|
|
20906
|
-
var renderMetricCard = (input) => `<article class="metric"><span>${
|
|
20994
|
+
var renderMetricCard = (input) => `<article class="metric"><span>${escapeHtml36(input.label)}</span><strong>${escapeHtml36(String(input.value))}</strong>${input.status ? `<p class="${escapeHtml36(input.status)}">${escapeHtml36(input.status)}</p>` : ""}${input.href ? `<a href="${escapeHtml36(input.href)}">Open</a>` : ""}</article>`;
|
|
20907
20995
|
var renderVoiceOpsConsoleHTML = (report, options = {}) => {
|
|
20908
20996
|
const links = report.links.map((link) => `<article class="surface">
|
|
20909
|
-
<div><h2>${
|
|
20910
|
-
<p><a href="${
|
|
20997
|
+
<div><h2>${escapeHtml36(link.label)}</h2>${link.description ? `<p>${escapeHtml36(link.description)}</p>` : ""}</div>
|
|
20998
|
+
<p><a href="${escapeHtml36(link.href)}">Open ${escapeHtml36(link.label)}</a>${link.statusHref ? ` \xB7 <a href="${escapeHtml36(link.statusHref)}">Status</a>` : ""}</p>
|
|
20911
20999
|
</article>`).join("");
|
|
20912
|
-
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${
|
|
20913
|
-
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${
|
|
21000
|
+
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${escapeHtml36(session.sessionId)}</td><td>${escapeHtml36(session.status)}</td><td>${session.turnCount}</td><td>${session.errorCount}</td><td>${session.replayHref ? `<a href="${escapeHtml36(session.replayHref)}">Replay</a>` : ""}</td></tr>`).join("") : '<tr><td colspan="5">No sessions yet.</td></tr>';
|
|
21001
|
+
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${escapeHtml36(event.kind)}</td><td>${escapeHtml36(event.provider ?? "unknown")}</td><td>${escapeHtml36(event.status ?? "unknown")}</td><td>${event.elapsedMs ?? 0}ms</td><td>${escapeHtml36(event.sessionId)}</td></tr>`).join("") : '<tr><td colspan="5">No provider routing events yet.</td></tr>';
|
|
20914
21002
|
const title = options.title ?? "AbsoluteJS Voice Ops Console";
|
|
20915
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
21003
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml36(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;background:#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>${escapeHtml36(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 ${escapeHtml36(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>`;
|
|
20916
21004
|
};
|
|
20917
21005
|
var createVoiceOpsConsoleRoutes = (options) => {
|
|
20918
21006
|
const path = options.path ?? "/ops-console";
|
|
@@ -21105,14 +21193,14 @@ var summarizeVoiceOpsStatus = async (options) => {
|
|
|
21105
21193
|
};
|
|
21106
21194
|
// src/opsStatusRoutes.ts
|
|
21107
21195
|
import { Elysia as Elysia35 } from "elysia";
|
|
21108
|
-
var
|
|
21196
|
+
var escapeHtml37 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21109
21197
|
var renderVoiceOpsStatusHTML = (report, options = {}) => {
|
|
21110
21198
|
const title = options.title ?? "AbsoluteJS Voice Ops Status";
|
|
21111
21199
|
const surfaces = Object.entries(report.surfaces).map(([key, surface]) => {
|
|
21112
21200
|
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;
|
|
21113
|
-
return `<article class="surface ${
|
|
21201
|
+
return `<article class="surface ${escapeHtml37(surface.status)}"><span>${escapeHtml37(surface.status.toUpperCase())}</span><h2>${escapeHtml37(key)}</h2><strong>${escapeHtml37(value)}</strong></article>`;
|
|
21114
21202
|
}).join("");
|
|
21115
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
21203
|
+
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:#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>${escapeHtml37(title)}</h1><p>Compact pass/fail status for framework widgets, demos, and small customer-facing health badges.</p><p class="status ${escapeHtml37(report.status)}">Overall: ${escapeHtml37(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>`;
|
|
21116
21204
|
};
|
|
21117
21205
|
var createVoiceOpsStatusRoutes = (options) => {
|
|
21118
21206
|
const path = options.path ?? "/api/voice/ops-status";
|
|
@@ -21550,7 +21638,7 @@ var createVoiceTTSProviderRouter = (options) => {
|
|
|
21550
21638
|
};
|
|
21551
21639
|
// src/traceDeliveryRoutes.ts
|
|
21552
21640
|
import { Elysia as Elysia36 } from "elysia";
|
|
21553
|
-
var
|
|
21641
|
+
var escapeHtml38 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21554
21642
|
var getString12 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
21555
21643
|
var getNumber7 = (value) => {
|
|
21556
21644
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -21631,14 +21719,14 @@ var renderSinkResults2 = (delivery) => {
|
|
|
21631
21719
|
if (entries.length === 0) {
|
|
21632
21720
|
return "<p>No sink delivery attempts recorded yet.</p>";
|
|
21633
21721
|
}
|
|
21634
|
-
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${
|
|
21722
|
+
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${escapeHtml38(sinkId)}</strong>: ${escapeHtml38(result.status)}${result.deliveredTo ? ` to ${escapeHtml38(result.deliveredTo)}` : ""}${result.error ? ` (${escapeHtml38(result.error)})` : ""}</li>`).join("")}</ul>`;
|
|
21635
21723
|
};
|
|
21636
|
-
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${
|
|
21724
|
+
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${escapeHtml38(event.type)} <small>${escapeHtml38(event.id)}</small>${event.sessionId ? ` session=${escapeHtml38(event.sessionId)}` : ""}</li>`).join("")}</ul>`;
|
|
21637
21725
|
var renderVoiceTraceDeliveryHTML = (report, options = {}) => {
|
|
21638
21726
|
const title = options.title ?? "AbsoluteJS Voice Trace Deliveries";
|
|
21639
|
-
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${
|
|
21640
|
-
const rows = report.deliveries.map((delivery) => `<article class="delivery ${
|
|
21641
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
21727
|
+
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${escapeHtml38(options.workerPath ?? "/api/voice-trace-deliveries/drain")}"><button type="submit">Drain trace deliveries</button></form>`;
|
|
21728
|
+
const rows = report.deliveries.map((delivery) => `<article class="delivery ${escapeHtml38(delivery.deliveryStatus)}"><div class="head"><div><span>${escapeHtml38(delivery.deliveryStatus)}</span><h2>${escapeHtml38(delivery.id)}</h2><p>${escapeHtml38(new Date(delivery.createdAt).toLocaleString())}${delivery.deliveredAt ? ` \xB7 delivered ${escapeHtml38(new Date(delivery.deliveredAt).toLocaleString())}` : ""}</p></div><strong>${String(delivery.deliveryAttempts ?? 0)} attempt(s)</strong></div>${delivery.deliveryError ? `<p class="error">${escapeHtml38(delivery.deliveryError)}</p>` : ""}<h3>Sinks</h3>${renderSinkResults2(delivery)}<h3>Events</h3>${renderEventList2(delivery)}</article>`).join("");
|
|
21729
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml38(title)}</title><style>body{background:#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>${escapeHtml38(title)}</h1><p>Checked ${escapeHtml38(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>`;
|
|
21642
21730
|
};
|
|
21643
21731
|
var createVoiceTraceDeliveryJSONHandler = (options) => async ({ query }) => buildVoiceTraceDeliveryReport(options, resolveVoiceTraceDeliveryFilter(query, options.filter));
|
|
21644
21732
|
var createVoiceTraceDeliveryHTMLHandler = (options) => async ({ query }) => {
|
|
@@ -21677,7 +21765,7 @@ var createVoiceTraceDeliveryRoutes = (options) => {
|
|
|
21677
21765
|
};
|
|
21678
21766
|
// src/traceTimeline.ts
|
|
21679
21767
|
import { Elysia as Elysia37 } from "elysia";
|
|
21680
|
-
var
|
|
21768
|
+
var escapeHtml39 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21681
21769
|
var getString13 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
21682
21770
|
var getNumber8 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
21683
21771
|
var firstString3 = (payload, keys) => {
|
|
@@ -21845,15 +21933,15 @@ var summarizeVoiceTraceTimeline = (events, options = {}) => {
|
|
|
21845
21933
|
};
|
|
21846
21934
|
};
|
|
21847
21935
|
var formatMs3 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
21848
|
-
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>${
|
|
21936
|
+
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>${escapeHtml39(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>`;
|
|
21849
21937
|
var renderVoiceTraceTimelineSessionHTML = (session, options = {}) => {
|
|
21850
|
-
const events = session.events.map((event) => `<tr class="${
|
|
21851
|
-
const issues = session.evaluation.issues.length ? session.evaluation.issues.map((issue) => `<li class="${
|
|
21852
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
21938
|
+
const events = session.events.map((event) => `<tr class="${escapeHtml39(event.status ?? "")}"><td>+${String(event.offsetMs)}ms</td><td>${escapeHtml39(event.type)}</td><td>${escapeHtml39(event.label)}</td><td>${escapeHtml39(event.provider ?? "")}</td><td>${escapeHtml39(event.status ?? "")}</td><td>${formatMs3(event.elapsedMs)}</td></tr>`).join("");
|
|
21939
|
+
const issues = session.evaluation.issues.length ? session.evaluation.issues.map((issue) => `<li class="${escapeHtml39(issue.severity)}">${escapeHtml39(issue.code)}: ${escapeHtml39(issue.message)}</li>`).join("") : "<li>none</li>";
|
|
21940
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml39(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>${escapeHtml39(session.sessionId)}</h1><p class="status ${escapeHtml39(session.status)}">${escapeHtml39(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>`;
|
|
21853
21941
|
};
|
|
21854
|
-
var renderSessionRows = (report) => report.sessions.length === 0 ? '<tr><td colspan="7">No trace events recorded yet.</td></tr>' : report.sessions.map((session) => `<tr class="${
|
|
21942
|
+
var renderSessionRows = (report) => report.sessions.length === 0 ? '<tr><td colspan="7">No trace events recorded yet.</td></tr>' : report.sessions.map((session) => `<tr class="${escapeHtml39(session.status)}"><td><a href="/traces/${encodeURIComponent(session.sessionId)}">${escapeHtml39(session.sessionId)}</a></td><td>${escapeHtml39(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) => escapeHtml39(provider.provider)).join(", ")}</td></tr>`).join("");
|
|
21855
21943
|
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}}";
|
|
21856
|
-
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>${
|
|
21944
|
+
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>${escapeHtml39(options.title ?? "Voice Trace Timelines")}</title><style>${timelineCSS}</style></head><body><main><header><p class="eyebrow">Self-hosted voice debugging</p><h1>${escapeHtml39(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>`;
|
|
21857
21945
|
var createVoiceTraceTimelineRoutes = (options) => {
|
|
21858
21946
|
const path = options.path ?? "/api/voice-traces";
|
|
21859
21947
|
const htmlPath = options.htmlPath ?? "/traces";
|
|
@@ -23580,6 +23668,7 @@ export {
|
|
|
23580
23668
|
renderVoiceOutcomeContractHTML,
|
|
23581
23669
|
renderVoiceOpsStatusHTML,
|
|
23582
23670
|
renderVoiceOpsConsoleHTML,
|
|
23671
|
+
renderVoiceOpsActionHistoryHTML,
|
|
23583
23672
|
renderVoiceLiveLatencyHTML,
|
|
23584
23673
|
renderVoiceHandoffHealthHTML,
|
|
23585
23674
|
renderVoiceEvalHTML,
|
|
@@ -23881,6 +23970,7 @@ export {
|
|
|
23881
23970
|
buildVoiceOpsTaskFromSLABreach,
|
|
23882
23971
|
buildVoiceOpsTaskFromReview,
|
|
23883
23972
|
buildVoiceOpsConsoleReport,
|
|
23973
|
+
buildVoiceOpsActionHistoryReport,
|
|
23884
23974
|
buildVoiceDiagnosticsMarkdown,
|
|
23885
23975
|
buildVoiceDemoReadyReport,
|
|
23886
23976
|
buildVoiceDeliverySinkReport,
|