@absolutejs/voice 0.0.22-beta.287 → 0.0.22-beta.288
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -2
- package/dist/index.js +395 -264
- package/dist/sloCalibration.d.ts +58 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -12800,7 +12800,66 @@ var createVoiceSloReadinessThresholdOptions = (input, options = {}) => {
|
|
|
12800
12800
|
reconnectResumeFailAfterMs: profile.reconnect.failAfterMs
|
|
12801
12801
|
};
|
|
12802
12802
|
};
|
|
12803
|
+
var buildVoiceSloReadinessThresholdReport = (input, options = {}) => {
|
|
12804
|
+
const report = Array.isArray(input) ? buildVoiceSloCalibrationReport(input, options) : ("thresholds" in input) ? input : undefined;
|
|
12805
|
+
const profile = report === undefined ? input : createVoiceSloThresholdProfile(report, options);
|
|
12806
|
+
return {
|
|
12807
|
+
bargeIn: profile.bargeIn,
|
|
12808
|
+
generatedAt: new Date().toISOString(),
|
|
12809
|
+
issues: profile.issues,
|
|
12810
|
+
liveLatencyMaxAgeMs: options.liveLatencyMaxAgeMs,
|
|
12811
|
+
ok: profile.status !== "fail",
|
|
12812
|
+
providerSlo: profile.providerSlo,
|
|
12813
|
+
readinessOptions: createVoiceSloReadinessThresholdOptions(profile),
|
|
12814
|
+
sources: report?.sources ?? [],
|
|
12815
|
+
status: profile.status
|
|
12816
|
+
};
|
|
12817
|
+
};
|
|
12803
12818
|
var escapeMarkdown = (value) => value.replaceAll("|", "\\|");
|
|
12819
|
+
var escapeHtml16 = (value) => String(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
12820
|
+
var formatMs = (value) => value === undefined ? "n/a" : `${value.toLocaleString()} ms`;
|
|
12821
|
+
var readinessThresholdRows = (report) => [
|
|
12822
|
+
{
|
|
12823
|
+
control: "Provider SLO p95",
|
|
12824
|
+
value: report.providerSlo.llm?.maxP95ElapsedMs,
|
|
12825
|
+
usedBy: "Provider SLO gates for STT, LLM, and TTS"
|
|
12826
|
+
},
|
|
12827
|
+
{
|
|
12828
|
+
control: "Live latency warn",
|
|
12829
|
+
value: report.readinessOptions.liveLatencyWarnAfterMs,
|
|
12830
|
+
usedBy: "Production readiness live latency warning gate"
|
|
12831
|
+
},
|
|
12832
|
+
{
|
|
12833
|
+
control: "Live latency fail",
|
|
12834
|
+
value: report.readinessOptions.liveLatencyFailAfterMs,
|
|
12835
|
+
usedBy: "Production readiness live latency fail gate"
|
|
12836
|
+
},
|
|
12837
|
+
{
|
|
12838
|
+
control: "Live latency freshness",
|
|
12839
|
+
value: report.liveLatencyMaxAgeMs,
|
|
12840
|
+
usedBy: "Maximum age for latency evidence before readiness ignores it"
|
|
12841
|
+
},
|
|
12842
|
+
{
|
|
12843
|
+
control: "Barge-in interruption",
|
|
12844
|
+
value: report.bargeIn.thresholdMs,
|
|
12845
|
+
usedBy: "Runtime interruption classification threshold"
|
|
12846
|
+
},
|
|
12847
|
+
{
|
|
12848
|
+
control: "Reconnect resume p95",
|
|
12849
|
+
value: report.readinessOptions.reconnectResumeFailAfterMs,
|
|
12850
|
+
usedBy: "Production readiness reconnect-resume gate"
|
|
12851
|
+
},
|
|
12852
|
+
{
|
|
12853
|
+
control: "Monitor run elapsed",
|
|
12854
|
+
value: report.readinessOptions.monitoringRunFailAfterMs,
|
|
12855
|
+
usedBy: "Production readiness monitoring run gate"
|
|
12856
|
+
},
|
|
12857
|
+
{
|
|
12858
|
+
control: "Notifier delivery elapsed",
|
|
12859
|
+
value: report.readinessOptions.monitoringNotifierDeliveryFailAfterMs,
|
|
12860
|
+
usedBy: "Production readiness notifier delivery gate"
|
|
12861
|
+
}
|
|
12862
|
+
];
|
|
12804
12863
|
var renderVoiceSloCalibrationMarkdown = (report, options = {}) => {
|
|
12805
12864
|
const rows = Object.values(report.thresholds).map((threshold) => `| ${escapeMarkdown(threshold.metric)} | ${threshold.status} | ${threshold.samples} | ${threshold.baselineP95Ms ?? "n/a"} | ${threshold.warnAfterMs ?? "n/a"} | ${threshold.failAfterMs ?? "n/a"} |`).join(`
|
|
12806
12865
|
`);
|
|
@@ -12822,6 +12881,37 @@ ${report.issues.map((issue) => `- ${issue}`).join(`
|
|
|
12822
12881
|
`) || "- None"}
|
|
12823
12882
|
`;
|
|
12824
12883
|
};
|
|
12884
|
+
var renderVoiceSloReadinessThresholdMarkdown = (report, options = {}) => {
|
|
12885
|
+
const rows = readinessThresholdRows(report).map((row) => `| ${escapeMarkdown(row.control)} | ${formatMs(row.value)} | ${escapeMarkdown(row.usedBy)} |`).join(`
|
|
12886
|
+
`);
|
|
12887
|
+
return `# ${options.title ?? "Calibration -> Active Readiness Gate"}
|
|
12888
|
+
|
|
12889
|
+
Generated: ${report.generatedAt}
|
|
12890
|
+
|
|
12891
|
+
Status: **${report.status}**
|
|
12892
|
+
|
|
12893
|
+
| Threshold | Active value | Used by |
|
|
12894
|
+
| --- | ---: | --- |
|
|
12895
|
+
${rows}
|
|
12896
|
+
|
|
12897
|
+
Sources:
|
|
12898
|
+
|
|
12899
|
+
${report.sources.map((source) => `- ${source}`).join(`
|
|
12900
|
+
`) || "- n/a"}
|
|
12901
|
+
|
|
12902
|
+
Issues:
|
|
12903
|
+
|
|
12904
|
+
${report.issues.map((issue) => `- ${issue}`).join(`
|
|
12905
|
+
`) || "- None"}
|
|
12906
|
+
`;
|
|
12907
|
+
};
|
|
12908
|
+
var renderVoiceSloReadinessThresholdHTML = (report, options = {}) => {
|
|
12909
|
+
const title = options.title ?? "Calibration -> Active Readiness Gate";
|
|
12910
|
+
const rows = readinessThresholdRows(report).map((row) => `<tr><td>${escapeHtml16(row.control)}</td><td>${escapeHtml16(formatMs(row.value))}</td><td>${escapeHtml16(row.usedBy)}</td></tr>`).join("");
|
|
12911
|
+
const issues = report.issues.length === 0 ? "<li>None</li>" : report.issues.map((issue) => `<li>${escapeHtml16(issue)}</li>`).join("");
|
|
12912
|
+
const sources = report.sources.length === 0 ? "<li>n/a</li>" : report.sources.map((source) => `<li><code>${escapeHtml16(source)}</code></li>`).join("");
|
|
12913
|
+
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:#f8f7f2;color:#181713;font-family:ui-sans-serif,system-ui,sans-serif;line-height:1.45;margin:2rem}main{max-width:1040px;margin:auto}.summary{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:1rem 0}.card,table{background:white;border:1px solid #ddd6c8;border-radius:14px}.card{padding:1rem}table{border-collapse:collapse;width:100%;overflow:hidden}td,th{border-bottom:1px solid #eee8dc;padding:.7rem;text-align:left;vertical-align:top}code{white-space:pre-wrap;word-break:break-word}.status{font-size:1.6rem;font-weight:800;text-transform:uppercase}</style></head><body><main><h1>${escapeHtml16(title)}</h1><p>This page shows the calibrated thresholds currently driving production readiness gates.</p><section class="summary"><div class="card"><strong>Status</strong><br><span class="status">${escapeHtml16(report.status)}</span></div><div class="card"><strong>Live evidence max age</strong><br>${escapeHtml16(formatMs(report.liveLatencyMaxAgeMs))}</div><div class="card"><strong>Provider p95 gate</strong><br>${escapeHtml16(formatMs(report.providerSlo.llm?.maxP95ElapsedMs))}</div><div class="card"><strong>Barge-in gate</strong><br>${escapeHtml16(formatMs(report.bargeIn.thresholdMs))}</div></section><h2>Active Readiness Thresholds</h2><table><thead><tr><th>Threshold</th><th>Active value</th><th>Used by</th></tr></thead><tbody>${rows}</tbody></table><h2>Sources</h2><ul>${sources}</ul><h2>Issues</h2><ul>${issues}</ul></main></body></html>`;
|
|
12914
|
+
};
|
|
12825
12915
|
var createVoiceSloCalibrationRoutes = (options) => {
|
|
12826
12916
|
const path = options.path ?? "/api/voice/slo-calibration";
|
|
12827
12917
|
const markdownPath = options.markdownPath === undefined ? "/voice/slo-calibration.md" : options.markdownPath;
|
|
@@ -12845,6 +12935,43 @@ var createVoiceSloCalibrationRoutes = (options) => {
|
|
|
12845
12935
|
}
|
|
12846
12936
|
return routes;
|
|
12847
12937
|
};
|
|
12938
|
+
var createVoiceSloReadinessThresholdRoutes = (options) => {
|
|
12939
|
+
const path = options.path ?? "/api/voice/slo-readiness-thresholds";
|
|
12940
|
+
const htmlPath = options.htmlPath === undefined ? "/voice/slo-readiness-thresholds" : options.htmlPath;
|
|
12941
|
+
const markdownPath = options.markdownPath === undefined ? "/voice/slo-readiness-thresholds.md" : options.markdownPath;
|
|
12942
|
+
const routes = new Elysia15({
|
|
12943
|
+
name: options.name ?? "absolutejs-voice-slo-readiness-thresholds"
|
|
12944
|
+
});
|
|
12945
|
+
const loadReport = async () => buildVoiceSloReadinessThresholdReport(typeof options.source === "function" ? await options.source() : options.source, options);
|
|
12946
|
+
routes.get(path, async () => Response.json(await loadReport(), { headers: options.headers }));
|
|
12947
|
+
if (htmlPath !== false) {
|
|
12948
|
+
routes.get(htmlPath, async () => {
|
|
12949
|
+
const report = await loadReport();
|
|
12950
|
+
return new Response(renderVoiceSloReadinessThresholdHTML(report, {
|
|
12951
|
+
title: options.title
|
|
12952
|
+
}), {
|
|
12953
|
+
headers: {
|
|
12954
|
+
"content-type": "text/html; charset=utf-8",
|
|
12955
|
+
...Object.fromEntries(new Headers(options.headers))
|
|
12956
|
+
}
|
|
12957
|
+
});
|
|
12958
|
+
});
|
|
12959
|
+
}
|
|
12960
|
+
if (markdownPath !== false) {
|
|
12961
|
+
routes.get(markdownPath, async () => {
|
|
12962
|
+
const report = await loadReport();
|
|
12963
|
+
return new Response(renderVoiceSloReadinessThresholdMarkdown(report, {
|
|
12964
|
+
title: options.title
|
|
12965
|
+
}), {
|
|
12966
|
+
headers: {
|
|
12967
|
+
"content-type": "text/markdown; charset=utf-8",
|
|
12968
|
+
...Object.fromEntries(new Headers(options.headers))
|
|
12969
|
+
}
|
|
12970
|
+
});
|
|
12971
|
+
});
|
|
12972
|
+
}
|
|
12973
|
+
return routes;
|
|
12974
|
+
};
|
|
12848
12975
|
// src/liveOps.ts
|
|
12849
12976
|
import { Elysia as Elysia16 } from "elysia";
|
|
12850
12977
|
var VOICE_LIVE_OPS_ACTIONS = [
|
|
@@ -13181,12 +13308,12 @@ var createVoiceLiveOpsRoutes = (options = {}) => {
|
|
|
13181
13308
|
import { Elysia as Elysia17 } from "elysia";
|
|
13182
13309
|
import { mkdir } from "fs/promises";
|
|
13183
13310
|
import { dirname, join } from "path";
|
|
13184
|
-
var
|
|
13311
|
+
var escapeHtml17 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13185
13312
|
var renderSummaryCard = (label, summary) => {
|
|
13186
13313
|
if (!summary) {
|
|
13187
|
-
return `<article><span>${
|
|
13314
|
+
return `<article><span>${escapeHtml17(label)}</span><strong>Disabled</strong><p class="muted">No worker configured.</p></article>`;
|
|
13188
13315
|
}
|
|
13189
|
-
return `<article><span>${
|
|
13316
|
+
return `<article><span>${escapeHtml17(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>`;
|
|
13190
13317
|
};
|
|
13191
13318
|
var resolvePresetLeases = (leases) => ("claim" in leases) ? {
|
|
13192
13319
|
audit: leases,
|
|
@@ -13397,9 +13524,9 @@ var buildVoiceDeliveryRuntimeReport = async (runtime) => ({
|
|
|
13397
13524
|
});
|
|
13398
13525
|
var renderVoiceDeliveryRuntimeHTML = (report, options = {}) => {
|
|
13399
13526
|
const title = options.title ?? "AbsoluteJS Voice Delivery Runtime";
|
|
13400
|
-
const tickForm = options.tickPath === false ? "" : `<form method="post" action="${
|
|
13401
|
-
const requeueForm = options.requeueDeadLettersPath === false ? "" : `<form method="post" action="${
|
|
13402
|
-
const snippet =
|
|
13527
|
+
const tickForm = options.tickPath === false ? "" : `<form method="post" action="${escapeHtml17(options.tickPath ?? "/api/voice-delivery-runtime/tick")}"><button type="submit">Tick delivery workers</button></form>`;
|
|
13528
|
+
const requeueForm = options.requeueDeadLettersPath === false ? "" : `<form method="post" action="${escapeHtml17(options.requeueDeadLettersPath ?? "/api/voice-delivery-runtime/requeue-dead-letters")}"><button type="submit">Requeue dead letters</button></form>`;
|
|
13529
|
+
const snippet = escapeHtml17(`const deliveryRuntime = createVoiceDeliveryRuntime(
|
|
13403
13530
|
createVoiceDeliveryRuntimePresetConfig({
|
|
13404
13531
|
audit: {
|
|
13405
13532
|
deliveries: runtimeStorage.auditDeliveries,
|
|
@@ -13425,7 +13552,7 @@ app.use(
|
|
|
13425
13552
|
traceDeliveries: runtimeStorage.traceDeliveries
|
|
13426
13553
|
})
|
|
13427
13554
|
);`);
|
|
13428
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
13555
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml17(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}.primitive{background:#111a15;border-color:#41604a}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}.primitive p{color:#c8d8ca;line-height:1.55}.primitive code{color:#bbf7d0}</style></head><body><main><p><a href="/delivery-sinks">Delivery sinks</a></p><section class="hero"><p class="eyebrow">Worker control plane</p><h1>${escapeHtml17(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 ${escapeHtml17(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 primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceDeliveryRuntimeRoutes(...)</code> builds this control plane</h2><p>Own the audit and trace delivery queues in your app, mount one runtime route group, and pass the same runtime into production readiness so failed or dead-lettered exports block deploys.</p><pre><code>${snippet}</code></pre></section></main></body></html>`;
|
|
13429
13556
|
};
|
|
13430
13557
|
var createVoiceDeliveryRuntimeRoutes = (options) => {
|
|
13431
13558
|
const path = options.path ?? "/api/voice-delivery-runtime";
|
|
@@ -13707,7 +13834,7 @@ var parseRetentionScopes = (value) => {
|
|
|
13707
13834
|
const allowed = new Set(allRetentionScopes);
|
|
13708
13835
|
return value.split(",").map((entry) => entry.trim()).filter((entry) => allowed.has(entry));
|
|
13709
13836
|
};
|
|
13710
|
-
var
|
|
13837
|
+
var escapeHtml18 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13711
13838
|
var buildStorageSurfaces = (options) => [
|
|
13712
13839
|
{
|
|
13713
13840
|
configured: Boolean(options.session ?? options.sessions),
|
|
@@ -13944,12 +14071,12 @@ var buildVoiceDataControlReport = async (options) => {
|
|
|
13944
14071
|
zeroRetentionAvailable: true
|
|
13945
14072
|
};
|
|
13946
14073
|
};
|
|
13947
|
-
var renderDataRetentionReportRows = (report) => report.scopes.map((scope) => `<tr><td>${
|
|
14074
|
+
var renderDataRetentionReportRows = (report) => report.scopes.map((scope) => `<tr><td>${escapeHtml18(scope.scope)}</td><td>${scope.scannedCount}</td><td>${scope.deletedCount}</td><td>${escapeHtml18(scope.skippedReason ?? "")}</td><td><code>${escapeHtml18(scope.deletedIds.join(", "))}</code></td></tr>`).join("");
|
|
13948
14075
|
var renderVoiceDataControlHTML = (report, options = {}) => {
|
|
13949
14076
|
const title = options.title ?? "Voice Data Control";
|
|
13950
|
-
const storageRows = report.storage.map((surface) => `<tr><td>${
|
|
13951
|
-
const keyRows = report.providerKeys.map((key) => `<tr><td>${
|
|
13952
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${
|
|
14077
|
+
const storageRows = report.storage.map((surface) => `<tr><td>${escapeHtml18(surface.name)}</td><td>${surface.configured ? "Configured" : "Missing"}</td><td>${escapeHtml18(surface.control)}</td><td>${surface.selfHosted ? "Yes" : "No"}</td></tr>`).join("");
|
|
14078
|
+
const keyRows = report.providerKeys.map((key) => `<tr><td>${escapeHtml18(key.name)}</td><td><code>${escapeHtml18(key.env ?? "n/a")}</code></td><td>${key.required ? "Required" : "Optional"}</td><td>${escapeHtml18(key.recommendation)}</td></tr>`).join("");
|
|
14079
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml18(title)}</title><style>body{background:#f8f7f2;color:#181713;font-family:ui-sans-serif,system-ui,sans-serif;line-height:1.45;margin:2rem}main{max-width:1120px;margin:auto}.summary{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:1rem 0}.card,table{background:white;border:1px solid #ddd6c8;border-radius:14px}.card{padding:1rem}table{border-collapse:collapse;width:100%;overflow:hidden}td,th{border-bottom:1px solid #eee8dc;padding:.7rem;text-align:left;vertical-align:top}code{white-space:pre-wrap;word-break:break-word}a{color:#9a3412}</style></head><body><main><h1>${escapeHtml18(title)}</h1><p>Self-hosted data-control proof for retention, redaction, audit export, deletion planning, customer-owned storage, and provider key handling.</p><section class="summary"><div class="card"><strong>Redaction</strong><br>${report.redaction.enabled ? "enabled" : "disabled"}</div><div class="card"><strong>Retention dry-run deletes</strong><br>${report.retentionPlan.deletedCount}</div><div class="card"><strong>Audit export events</strong><br>${report.auditExport?.events.length ?? 0}</div><div class="card"><strong>Zero retention recipe</strong><br>${report.zeroRetentionAvailable ? "available" : "missing"}</div></section><h2>Customer-Owned Storage</h2><table><thead><tr><th>Surface</th><th>Status</th><th>Control</th><th>Self-hosted</th></tr></thead><tbody>${storageRows}</tbody></table><h2>Retention Plan</h2><table><thead><tr><th>Scope</th><th>Scanned</th><th>Would delete</th><th>Skipped</th><th>Ids</th></tr></thead><tbody>${renderDataRetentionReportRows(report.retentionPlan)}</tbody></table><h2>Provider Keys</h2><table><thead><tr><th>Provider</th><th>Env</th><th>Required</th><th>Recommendation</th></tr></thead><tbody>${keyRows}</tbody></table><p><a href="./data-control/audit.md">Redacted audit Markdown</a> \xB7 <a href="./data-control/audit.html">Redacted audit HTML</a></p></main></body></html>`;
|
|
13953
14080
|
};
|
|
13954
14081
|
var renderVoiceDataControlMarkdown = (report, options = {}) => [
|
|
13955
14082
|
`# ${options.title ?? "Voice Data Control"}`,
|
|
@@ -14092,7 +14219,7 @@ import { Elysia as Elysia20 } from "elysia";
|
|
|
14092
14219
|
|
|
14093
14220
|
// src/handoffHealth.ts
|
|
14094
14221
|
import { Elysia as Elysia19 } from "elysia";
|
|
14095
|
-
var
|
|
14222
|
+
var escapeHtml19 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
14096
14223
|
var getString7 = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
|
|
14097
14224
|
var isStatus = (value) => value === "delivered" || value === "failed" || value === "skipped";
|
|
14098
14225
|
var increment3 = (record, key) => {
|
|
@@ -14210,10 +14337,10 @@ var renderActionSummary = (summary) => {
|
|
|
14210
14337
|
return [
|
|
14211
14338
|
'<section class="voice-handoff-health-columns">',
|
|
14212
14339
|
"<article><h3>Actions</h3>",
|
|
14213
|
-
actions.length === 0 ? "<p>No handoff actions yet.</p>" : `<ul>${actions.map(([action, count]) => `<li>${
|
|
14340
|
+
actions.length === 0 ? "<p>No handoff actions yet.</p>" : `<ul>${actions.map(([action, count]) => `<li>${escapeHtml19(action)}: ${String(count)}</li>`).join("")}</ul>`,
|
|
14214
14341
|
"</article>",
|
|
14215
14342
|
"<article><h3>Adapters</h3>",
|
|
14216
|
-
adapters.length === 0 ? "<p>No adapter deliveries yet.</p>" : `<ul>${adapters.map(([adapterId, counts]) => `<li>${
|
|
14343
|
+
adapters.length === 0 ? "<p>No adapter deliveries yet.</p>" : `<ul>${adapters.map(([adapterId, counts]) => `<li>${escapeHtml19(adapterId)}: ${String(counts.delivered)} delivered / ${String(counts.failed)} failed / ${String(counts.skipped)} skipped</li>`).join("")}</ul>`,
|
|
14217
14344
|
"</article>",
|
|
14218
14345
|
"</section>"
|
|
14219
14346
|
].join("");
|
|
@@ -14227,22 +14354,22 @@ var renderVoiceHandoffHealthHTML = (summary) => [
|
|
|
14227
14354
|
summary.events.length === 0 ? '<p class="voice-handoff-health-empty">No handoffs found.</p>' : [
|
|
14228
14355
|
'<div class="voice-handoff-health-events">',
|
|
14229
14356
|
...summary.events.map((event) => [
|
|
14230
|
-
`<article class="${
|
|
14357
|
+
`<article class="${escapeHtml19(event.status)}">`,
|
|
14231
14358
|
'<div class="voice-handoff-health-event-header">',
|
|
14232
|
-
`<strong>${
|
|
14233
|
-
`<span>${
|
|
14359
|
+
`<strong>${escapeHtml19(event.action ?? "handoff")}</strong>`,
|
|
14360
|
+
`<span>${escapeHtml19(event.status)}</span>`,
|
|
14234
14361
|
"</div>",
|
|
14235
|
-
`<p><small>${
|
|
14236
|
-
event.target ? `<p>Target: ${
|
|
14237
|
-
event.reason ? `<p>Reason: ${
|
|
14362
|
+
`<p><small>${escapeHtml19(event.sessionId)}</small></p>`,
|
|
14363
|
+
event.target ? `<p>Target: ${escapeHtml19(event.target)}</p>` : "",
|
|
14364
|
+
event.reason ? `<p>Reason: ${escapeHtml19(event.reason)}</p>` : "",
|
|
14238
14365
|
event.deliveries.length ? `<ul>${event.deliveries.map((delivery) => [
|
|
14239
14366
|
"<li>",
|
|
14240
|
-
`${
|
|
14241
|
-
delivery.deliveredTo ? ` to ${
|
|
14242
|
-
delivery.error ? ` (${
|
|
14367
|
+
`${escapeHtml19(delivery.adapterId)}: ${escapeHtml19(delivery.status)}`,
|
|
14368
|
+
delivery.deliveredTo ? ` to ${escapeHtml19(delivery.deliveredTo)}` : "",
|
|
14369
|
+
delivery.error ? ` (${escapeHtml19(delivery.error)})` : "",
|
|
14243
14370
|
"</li>"
|
|
14244
14371
|
].join("")).join("")}</ul>` : "",
|
|
14245
|
-
event.replayHref ? `<p><a href="${
|
|
14372
|
+
event.replayHref ? `<p><a href="${escapeHtml19(event.replayHref)}">Open replay</a></p>` : "",
|
|
14246
14373
|
"</article>"
|
|
14247
14374
|
].join("")),
|
|
14248
14375
|
"</div>"
|
|
@@ -14395,12 +14522,12 @@ var evaluateVoiceQuality = async (input) => {
|
|
|
14395
14522
|
thresholds
|
|
14396
14523
|
};
|
|
14397
14524
|
};
|
|
14398
|
-
var
|
|
14525
|
+
var escapeHtml20 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
14399
14526
|
var formatMetricValue = (metric) => metric.unit === "rate" ? `${(metric.actual * 100).toFixed(2)}%` : metric.unit === "ms" ? `${Math.round(metric.actual)}ms` : String(metric.actual);
|
|
14400
14527
|
var formatThreshold = (metric) => metric.unit === "rate" ? `${(metric.threshold * 100).toFixed(2)}%` : metric.unit === "ms" ? `${Math.round(metric.threshold)}ms` : String(metric.threshold);
|
|
14401
14528
|
var renderVoiceQualityHTML = (report, options = {}) => {
|
|
14402
|
-
const rows = Object.entries(report.metrics).map(([key, metric]) => `<tr class="${metric.pass ? "pass" : "fail"}"><td>${
|
|
14403
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
14529
|
+
const rows = Object.entries(report.metrics).map(([key, metric]) => `<tr class="${metric.pass ? "pass" : "fail"}"><td>${escapeHtml20(metric.label)}</td><td>${escapeHtml20(formatMetricValue(metric))}</td><td>${escapeHtml20(formatThreshold(metric))}</td><td>${metric.pass ? "pass" : "fail"}</td><td><code>${escapeHtml20(key)}</code></td></tr>`).join("");
|
|
14530
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml20(link.href)}">${escapeHtml20(link.label)}</a>`).join("")}</nav>` : "";
|
|
14404
14531
|
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>`;
|
|
14405
14532
|
};
|
|
14406
14533
|
var createVoiceQualityRoutes = (options) => {
|
|
@@ -14434,7 +14561,7 @@ var createVoiceQualityRoutes = (options) => {
|
|
|
14434
14561
|
};
|
|
14435
14562
|
|
|
14436
14563
|
// src/evalRoutes.ts
|
|
14437
|
-
var
|
|
14564
|
+
var escapeHtml21 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
14438
14565
|
var rate2 = (count, total) => count / Math.max(1, total);
|
|
14439
14566
|
var normalizeSearchText = (value) => value.trim().toLowerCase();
|
|
14440
14567
|
var getString9 = (value) => typeof value === "string" ? value : undefined;
|
|
@@ -14756,7 +14883,7 @@ var createVoiceFileScenarioFixtureStore = (filePath) => ({
|
|
|
14756
14883
|
var formatTime = (value) => value === undefined ? "unknown" : new Date(value).toLocaleString();
|
|
14757
14884
|
var formatPercent = (value) => `${(value * 100).toFixed(2)}%`;
|
|
14758
14885
|
var renderVoiceEvalPrimitiveCopy = () => {
|
|
14759
|
-
const snippet =
|
|
14886
|
+
const snippet = escapeHtml21(`app.use(
|
|
14760
14887
|
createVoiceEvalRoutes({
|
|
14761
14888
|
path: '/evals',
|
|
14762
14889
|
store: traceStore,
|
|
@@ -14777,44 +14904,44 @@ var renderVoiceEvalPrimitiveCopy = () => {
|
|
|
14777
14904
|
};
|
|
14778
14905
|
var renderVoiceEvalHTML = (report, options = {}) => {
|
|
14779
14906
|
const title = options.title ?? "AbsoluteJS Voice Evals";
|
|
14780
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
14781
|
-
const trend = report.trend.length ? report.trend.map((bucket) => `<tr><td>${
|
|
14907
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml21(link.href)}">${escapeHtml21(link.label)}</a>`).join("")}</nav>` : "";
|
|
14908
|
+
const trend = report.trend.length ? report.trend.map((bucket) => `<tr><td>${escapeHtml21(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>';
|
|
14782
14909
|
const sessions = report.sessions.length ? report.sessions.map((session) => {
|
|
14783
14910
|
const failedMetrics = Object.entries(session.quality.metrics).filter(([, metric]) => !metric.pass).map(([, metric]) => metric.label).join(", ");
|
|
14784
|
-
const sessionLabel = session.operationsRecordHref ? `<a href="${
|
|
14785
|
-
return `<tr class="${session.status}"><td>${sessionLabel}</td><td>${
|
|
14911
|
+
const sessionLabel = session.operationsRecordHref ? `<a href="${escapeHtml21(session.operationsRecordHref)}">${escapeHtml21(session.sessionId)}</a>` : escapeHtml21(session.sessionId);
|
|
14912
|
+
return `<tr class="${session.status}"><td>${sessionLabel}</td><td>${escapeHtml21(session.status)}</td><td>${session.eventCount}</td><td>${session.summary.turnCount}</td><td>${session.summary.errorCount}</td><td>${escapeHtml21(formatTime(session.endedAt))}</td><td>${escapeHtml21(failedMetrics || "none")}</td></tr>`;
|
|
14786
14913
|
}).join("") : '<tr><td colspan="7">No sessions found.</td></tr>';
|
|
14787
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
14914
|
+
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{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}.eyebrow{font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.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,.primitive{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.primitive{background:#fffdf7;border-color:#d6c7a3;margin:1rem 0}.primitive p{line-height:1.55}.primitive pre{background:#181713;border-radius:.85rem;color:#fef3c7;overflow:auto;padding:1rem}.primitive code{color:#fef3c7}.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>${escapeHtml21(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>${renderVoiceEvalPrimitiveCopy()}<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>`;
|
|
14788
14915
|
};
|
|
14789
14916
|
var renderVoiceEvalBaselineHTML = (comparison, options = {}) => {
|
|
14790
14917
|
const title = options.title ?? "AbsoluteJS Voice Eval Baseline";
|
|
14791
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
14792
|
-
const reasons = comparison.reasons.length ? comparison.reasons.map((reason) => `<li>${
|
|
14793
|
-
const newFailures = comparison.newFailedSessionIds.length ? comparison.newFailedSessionIds.map((id) => `<li>${
|
|
14794
|
-
const recovered = comparison.recoveredSessionIds.length ? comparison.recoveredSessionIds.map((id) => `<li>${
|
|
14795
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
14918
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml21(link.href)}">${escapeHtml21(link.label)}</a>`).join("")}</nav>` : "";
|
|
14919
|
+
const reasons = comparison.reasons.length ? comparison.reasons.map((reason) => `<li>${escapeHtml21(reason)}</li>`).join("") : "<li>No baseline regressions detected.</li>";
|
|
14920
|
+
const newFailures = comparison.newFailedSessionIds.length ? comparison.newFailedSessionIds.map((id) => `<li>${escapeHtml21(id)}</li>`).join("") : "<li>none</li>";
|
|
14921
|
+
const recovered = comparison.recoveredSessionIds.length ? comparison.recoveredSessionIds.map((id) => `<li>${escapeHtml21(id)}</li>`).join("") : "<li>none</li>";
|
|
14922
|
+
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{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>${escapeHtml21(title)}</h1><p class="status ${comparison.status}">${comparison.status}</p><div class="grid"><article class="card"><span>Baseline pass rate</span><strong>${escapeHtml21(formatPercent(comparison.baseline.passRate))}</strong></article><article class="card"><span>Current pass rate</span><strong>${escapeHtml21(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>${escapeHtml21(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>`;
|
|
14796
14923
|
};
|
|
14797
14924
|
var renderVoiceScenarioEvalHTML = (report, options = {}) => {
|
|
14798
14925
|
const title = options.title ?? "AbsoluteJS Voice Scenario Evals";
|
|
14799
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
14926
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml21(link.href)}">${escapeHtml21(link.label)}</a>`).join("")}</nav>` : "";
|
|
14800
14927
|
const scenarios = report.scenarios.length ? report.scenarios.map((scenario) => {
|
|
14801
|
-
const scenarioIssues = scenario.issues.length ? `<ul>${scenario.issues.map((issue) => `<li>${
|
|
14928
|
+
const scenarioIssues = scenario.issues.length ? `<ul>${scenario.issues.map((issue) => `<li>${escapeHtml21(issue)}</li>`).join("")}</ul>` : "";
|
|
14802
14929
|
const sessions = scenario.sessions.length ? scenario.sessions.map((session) => {
|
|
14803
|
-
const sessionLabel = session.operationsRecordHref ? `<a href="${
|
|
14804
|
-
return `<tr class="${session.status}"><td>${sessionLabel}</td><td>${
|
|
14930
|
+
const sessionLabel = session.operationsRecordHref ? `<a href="${escapeHtml21(session.operationsRecordHref)}">${escapeHtml21(session.sessionId)}</a>` : escapeHtml21(session.sessionId);
|
|
14931
|
+
return `<tr class="${session.status}"><td>${sessionLabel}</td><td>${escapeHtml21(session.status)}</td><td>${session.eventCount}</td><td>${escapeHtml21(session.issues.join(", ") || "none")}</td></tr>`;
|
|
14805
14932
|
}).join("") : '<tr><td colspan="4">No matching sessions.</td></tr>';
|
|
14806
|
-
return `<section class="scenario ${scenario.status}"><h2>${
|
|
14933
|
+
return `<section class="scenario ${scenario.status}"><h2>${escapeHtml21(scenario.label)}</h2>${scenario.description ? `<p>${escapeHtml21(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>`;
|
|
14807
14934
|
}).join("") : "<section><p>No scenarios configured.</p></section>";
|
|
14808
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
14935
|
+
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{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}.eyebrow{font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.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}.primitive{background:#fffdf7;border-color:#d6c7a3}.primitive p{line-height:1.55}.primitive pre{background:#181713;border-radius:.85rem;color:#fef3c7;overflow:auto;padding:1rem}.primitive code{color:#fef3c7}.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>${escapeHtml21(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>${renderVoiceEvalPrimitiveCopy()}${scenarios}</main></body></html>`;
|
|
14809
14936
|
};
|
|
14810
14937
|
var renderVoiceScenarioFixtureEvalHTML = (report, options = {}) => {
|
|
14811
14938
|
const title = options.title ?? "AbsoluteJS Voice Fixture Evals";
|
|
14812
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
14939
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml21(link.href)}">${escapeHtml21(link.label)}</a>`).join("")}</nav>` : "";
|
|
14813
14940
|
const fixtures = report.fixtures.length ? report.fixtures.map((fixture) => {
|
|
14814
|
-
const scenarios = fixture.report.scenarios.map((scenario) => `<tr class="${scenario.status}"><td>${
|
|
14815
|
-
return `<section class="${fixture.status}"><h2>${
|
|
14941
|
+
const scenarios = fixture.report.scenarios.map((scenario) => `<tr class="${scenario.status}"><td>${escapeHtml21(scenario.label)}</td><td>${escapeHtml21(scenario.status)}</td><td>${scenario.matchedSessions}</td><td>${escapeHtml21([...scenario.issues, ...scenario.sessions.flatMap((session) => session.issues)].join(", ") || "none")}</td></tr>`).join("");
|
|
14942
|
+
return `<section class="${fixture.status}"><h2>${escapeHtml21(fixture.label)}</h2>${fixture.description ? `<p>${escapeHtml21(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>`;
|
|
14816
14943
|
}).join("") : "<section><p>No scenario fixtures configured.</p></section>";
|
|
14817
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
14944
|
+
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{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}.eyebrow{font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.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}.primitive{background:#fffdf7;border-color:#d6c7a3}.primitive p{line-height:1.55}.primitive pre{background:#181713;border-radius:.85rem;color:#fef3c7;overflow:auto;padding:1rem}.primitive code{color:#fef3c7}.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>${escapeHtml21(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>${renderVoiceEvalPrimitiveCopy()}${fixtures}</main></body></html>`;
|
|
14818
14945
|
};
|
|
14819
14946
|
var createVoiceEvalRoutes = (options) => {
|
|
14820
14947
|
const path = options.path ?? "/evals";
|
|
@@ -14959,7 +15086,7 @@ import { Elysia as Elysia24 } from "elysia";
|
|
|
14959
15086
|
|
|
14960
15087
|
// src/outcomeContract.ts
|
|
14961
15088
|
import { Elysia as Elysia22 } from "elysia";
|
|
14962
|
-
var
|
|
15089
|
+
var escapeHtml22 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
14963
15090
|
var resolveSessionHref2 = (value, sessionId) => {
|
|
14964
15091
|
if (value === false) {
|
|
14965
15092
|
return;
|
|
@@ -15170,13 +15297,13 @@ var assertVoiceOutcomeContractEvidence = (report, input = {}) => {
|
|
|
15170
15297
|
var renderVoiceOutcomeContractHTML = (report, options = {}) => {
|
|
15171
15298
|
const title = options.title ?? "Voice Outcome Contracts";
|
|
15172
15299
|
const contracts = report.contracts.map((contract) => {
|
|
15173
|
-
const sessionLinks = contract.operationsRecordHrefs.length ? `<p>${contract.operationsRecordHrefs.map((href, index) => `<a href="${
|
|
15300
|
+
const sessionLinks = contract.operationsRecordHrefs.length ? `<p>${contract.operationsRecordHrefs.map((href, index) => `<a href="${escapeHtml22(href)}">${escapeHtml22(contract.sessionIds[index] ?? href)}</a>`).join(" \xB7 ")}</p>` : "";
|
|
15174
15301
|
return `<section class="contract ${contract.pass ? "pass" : "fail"}">
|
|
15175
15302
|
<div class="contract-header">
|
|
15176
15303
|
<div>
|
|
15177
|
-
<p class="eyebrow">${
|
|
15178
|
-
<h2>${
|
|
15179
|
-
${contract.description ? `<p>${
|
|
15304
|
+
<p class="eyebrow">${escapeHtml22(contract.contractId)}</p>
|
|
15305
|
+
<h2>${escapeHtml22(contract.label ?? contract.contractId)}</h2>
|
|
15306
|
+
${contract.description ? `<p>${escapeHtml22(contract.description)}</p>` : ""}
|
|
15180
15307
|
${sessionLinks}
|
|
15181
15308
|
</div>
|
|
15182
15309
|
<strong>${contract.pass ? "pass" : "fail"}</strong>
|
|
@@ -15188,10 +15315,10 @@ var renderVoiceOutcomeContractHTML = (report, options = {}) => {
|
|
|
15188
15315
|
<span>handoffs ${String(contract.matched.handoffs)}</span>
|
|
15189
15316
|
<span>events ${String(contract.matched.integrationEvents)}</span>
|
|
15190
15317
|
</div>
|
|
15191
|
-
${contract.issues.length ? `<ul>${contract.issues.map((issue) => `<li>${
|
|
15318
|
+
${contract.issues.length ? `<ul>${contract.issues.map((issue) => `<li>${escapeHtml22(issue.message)}</li>`).join("")}</ul>` : ""}
|
|
15192
15319
|
</section>`;
|
|
15193
15320
|
}).join("");
|
|
15194
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
15321
|
+
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:#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>${escapeHtml22(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>`;
|
|
15195
15322
|
};
|
|
15196
15323
|
var createVoiceOutcomeContractJSONHandler = (options) => async () => runVoiceOutcomeContractSuite(options);
|
|
15197
15324
|
var createVoiceOutcomeContractHTMLHandler = (options) => async () => {
|
|
@@ -15426,7 +15553,7 @@ var createDefaultTurn = (caseId) => ({
|
|
|
15426
15553
|
});
|
|
15427
15554
|
var defaultApi = {};
|
|
15428
15555
|
var sameJSON = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
15429
|
-
var
|
|
15556
|
+
var escapeHtml23 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
15430
15557
|
var resolveSessionHref3 = (value, sessionId) => {
|
|
15431
15558
|
if (value === false) {
|
|
15432
15559
|
return;
|
|
@@ -15675,7 +15802,7 @@ var assertVoiceToolContractEvidence = (report, input = {}) => {
|
|
|
15675
15802
|
};
|
|
15676
15803
|
var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
15677
15804
|
const title = options.title ?? "Voice Tool Contracts";
|
|
15678
|
-
const snippet =
|
|
15805
|
+
const snippet = escapeHtml23(`app.use(
|
|
15679
15806
|
createVoiceToolContractRoutes({
|
|
15680
15807
|
htmlPath: '/tool-contracts',
|
|
15681
15808
|
path: '/api/tool-contracts',
|
|
@@ -15701,20 +15828,20 @@ var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
|
15701
15828
|
);`);
|
|
15702
15829
|
const contracts = report.contracts.map((contract) => {
|
|
15703
15830
|
const cases = contract.cases.map((testCase) => `<tr>
|
|
15704
|
-
<td>${testCase.operationsRecordHref ? `<a href="${
|
|
15831
|
+
<td>${testCase.operationsRecordHref ? `<a href="${escapeHtml23(testCase.operationsRecordHref)}">${escapeHtml23(testCase.label ?? testCase.caseId)}</a>` : escapeHtml23(testCase.label ?? testCase.caseId)}</td>
|
|
15705
15832
|
<td class="${testCase.pass ? "pass" : "fail"}">${testCase.pass ? "pass" : "fail"}</td>
|
|
15706
|
-
<td>${
|
|
15707
|
-
<td>${
|
|
15833
|
+
<td>${escapeHtml23(testCase.status)}</td>
|
|
15834
|
+
<td>${escapeHtml23(testCase.sessionId)}</td>
|
|
15708
15835
|
<td>${String(testCase.attempts)}</td>
|
|
15709
15836
|
<td>${String(testCase.elapsedMs)}ms</td>
|
|
15710
15837
|
<td>${testCase.timedOut ? "yes" : "no"}</td>
|
|
15711
|
-
<td>${
|
|
15838
|
+
<td>${escapeHtml23(testCase.issues.map((issue) => issue.message).join(" ") || testCase.error || "")}</td>
|
|
15712
15839
|
</tr>`).join("");
|
|
15713
15840
|
return `<section class="contract ${contract.pass ? "pass" : "fail"}">
|
|
15714
15841
|
<div class="contract-header">
|
|
15715
15842
|
<div>
|
|
15716
|
-
<p class="eyebrow">${
|
|
15717
|
-
<h2>${
|
|
15843
|
+
<p class="eyebrow">${escapeHtml23(contract.toolName)}</p>
|
|
15844
|
+
<h2>${escapeHtml23(contract.label ?? contract.contractId)}</h2>
|
|
15718
15845
|
</div>
|
|
15719
15846
|
<strong class="${contract.pass ? "pass" : "fail"}">${contract.pass ? "Passing" : "Failing"}</strong>
|
|
15720
15847
|
</div>
|
|
@@ -15724,7 +15851,7 @@ var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
|
15724
15851
|
</table>
|
|
15725
15852
|
</section>`;
|
|
15726
15853
|
}).join("");
|
|
15727
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
15854
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml23(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.primitive,.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))}.primitive{background:#151b20;border-color:#5a4421}.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)}.primitive p{color:#d8dee6;line-height:1.55}.primitive pre{background:#0f1217;border:1px solid #2a323a;border-radius:16px;color:#fef3c7;overflow:auto;padding:14px}.primitive code{color:#fef3c7}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>${escapeHtml23(title)}</h1><div class="summary"><span class="pill ${report.status === "pass" ? "pass" : "fail"}">${escapeHtml23(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><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceToolContractRoutes(...)</code> certifies tool behavior</h2><p>Define deterministic tool cases for retries, idempotency, timeouts, result shape, and error handling so assistant tools fail in pre-production instead of live calls.</p><pre><code>${snippet}</code></pre></section>${contracts || '<section class="contract"><p>No tool contracts configured.</p></section>'}</main></body></html>`;
|
|
15728
15855
|
};
|
|
15729
15856
|
var createVoiceToolContractJSONHandler = (options) => () => runVoiceToolContractSuite(options);
|
|
15730
15857
|
var createVoiceToolContractHTMLHandler = (options) => async () => {
|
|
@@ -15751,7 +15878,7 @@ var createVoiceToolContractRoutes = (options) => {
|
|
|
15751
15878
|
};
|
|
15752
15879
|
|
|
15753
15880
|
// src/simulationSuite.ts
|
|
15754
|
-
var
|
|
15881
|
+
var escapeHtml24 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
15755
15882
|
var summarizeSection = (report) => ({
|
|
15756
15883
|
failed: report.failed,
|
|
15757
15884
|
passed: report.passed,
|
|
@@ -15947,15 +16074,15 @@ var renderSection = (label, summary) => {
|
|
|
15947
16074
|
if (!summary) {
|
|
15948
16075
|
return "";
|
|
15949
16076
|
}
|
|
15950
|
-
return `<article class="${
|
|
16077
|
+
return `<article class="${escapeHtml24(summary.status)}"><span>${escapeHtml24(label)}</span><strong>${escapeHtml24(summary.status)}</strong><p>${summary.passed}/${summary.total} passed, ${summary.failed} failed.</p></article>`;
|
|
15951
16078
|
};
|
|
15952
16079
|
var renderAction = (action) => {
|
|
15953
|
-
const content = `<strong>${
|
|
15954
|
-
return action.href ? `<a class="action" href="${
|
|
16080
|
+
const content = `<strong>${escapeHtml24(action.label)}</strong><p>${escapeHtml24(action.description)}</p><span>${escapeHtml24(action.section)} / ${escapeHtml24(action.severity)}</span>`;
|
|
16081
|
+
return action.href ? `<a class="action" href="${escapeHtml24(action.href)}">${content}</a>` : `<article class="action">${content}</article>`;
|
|
15955
16082
|
};
|
|
15956
16083
|
var renderVoiceSimulationSuiteHTML = (report, options = {}) => {
|
|
15957
16084
|
const title = options.title ?? "Voice Simulation Suite";
|
|
15958
|
-
const snippet =
|
|
16085
|
+
const snippet = escapeHtml24(`app.use(
|
|
15959
16086
|
createVoiceSimulationSuiteRoutes({
|
|
15960
16087
|
htmlPath: '/voice/simulations',
|
|
15961
16088
|
path: '/api/voice/simulations',
|
|
@@ -15988,7 +16115,7 @@ app.use(
|
|
|
15988
16115
|
store: traceStore
|
|
15989
16116
|
})
|
|
15990
16117
|
);`);
|
|
15991
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
16118
|
+
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:#10151c;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1080px;padding:32px}.hero,.primitive{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}.primitive{background:#151d27;border-color:#355078}.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,.primitive p{color:#d8dee6;line-height:1.55;margin:.3rem 0 .6rem}pre{background:#151d27;border:1px solid #283544;border-radius:18px;overflow:auto;padding:16px}.primitive pre{background:#0b1118;color:#dbeafe}.primitive code{color:#bfdbfe}</style></head><body><main><section class="hero"><p class="eyebrow">Pre-production proof</p><h1>${escapeHtml24(title)}</h1><p>One report for session quality, scenario evals, fixture simulations, tool contracts, and outcome contracts.</p><p class="badge ${escapeHtml24(report.status)}">Status: ${escapeHtml24(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><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceSimulationSuiteRoutes(...)</code> builds this pre-production proof surface</h2><p>Run session quality checks, scenario evals, fixture-backed simulations, tool contracts, and outcome contracts from one route group before live traffic sees a regression.</p><pre><code>${snippet}</code></pre></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>${escapeHtml24(JSON.stringify({ summary: report.summary, actions: report.actions }, null, 2))}</pre></main></body></html>`;
|
|
15992
16119
|
};
|
|
15993
16120
|
var createVoiceSimulationSuiteRoutes = (options) => {
|
|
15994
16121
|
const path = options.path ?? "/api/voice/simulations";
|
|
@@ -16307,7 +16434,7 @@ var createVoiceWorkflowContractHandler = (input) => {
|
|
|
16307
16434
|
// src/sessionReplay.ts
|
|
16308
16435
|
import { Elysia as Elysia25 } from "elysia";
|
|
16309
16436
|
var getString10 = (value) => typeof value === "string" ? value : undefined;
|
|
16310
|
-
var
|
|
16437
|
+
var escapeHtml25 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
16311
16438
|
var increment4 = (record, key) => {
|
|
16312
16439
|
record[key] = (record[key] ?? 0) + 1;
|
|
16313
16440
|
};
|
|
@@ -16501,10 +16628,10 @@ var summarizeVoiceSessions = async (options = {}) => {
|
|
|
16501
16628
|
var renderVoiceSessionsHTML = (sessions) => sessions.length === 0 ? '<p class="voice-sessions-empty">No voice sessions found.</p>' : [
|
|
16502
16629
|
'<div class="voice-sessions-list">',
|
|
16503
16630
|
...sessions.map((session) => [
|
|
16504
|
-
`<article class="voice-session-card ${
|
|
16631
|
+
`<article class="voice-session-card ${escapeHtml25(session.status)}">`,
|
|
16505
16632
|
'<div class="voice-session-card-header">',
|
|
16506
|
-
`<strong>${
|
|
16507
|
-
`<span>${
|
|
16633
|
+
`<strong>${escapeHtml25(session.sessionId)}</strong>`,
|
|
16634
|
+
`<span>${escapeHtml25(session.status)}</span>`,
|
|
16508
16635
|
"</div>",
|
|
16509
16636
|
"<dl>",
|
|
16510
16637
|
`<div><dt>Events</dt><dd>${String(session.eventCount)}</dd></div>`,
|
|
@@ -16512,9 +16639,9 @@ var renderVoiceSessionsHTML = (sessions) => sessions.length === 0 ? '<p class="v
|
|
|
16512
16639
|
`<div><dt>Transcripts</dt><dd>${String(session.transcriptCount)}</dd></div>`,
|
|
16513
16640
|
`<div><dt>Errors</dt><dd>${String(session.errorCount)}</dd></div>`,
|
|
16514
16641
|
"</dl>",
|
|
16515
|
-
session.latestOutcome ? `<p>Outcome: ${
|
|
16516
|
-
session.providers.length ? `<p>Providers: ${session.providers.map(
|
|
16517
|
-
session.replayHref ? `<p>${session.operationsRecordHref ? `<a href="${
|
|
16642
|
+
session.latestOutcome ? `<p>Outcome: ${escapeHtml25(session.latestOutcome)}</p>` : "",
|
|
16643
|
+
session.providers.length ? `<p>Providers: ${session.providers.map(escapeHtml25).join(", ")}</p>` : "",
|
|
16644
|
+
session.replayHref ? `<p>${session.operationsRecordHref ? `<a href="${escapeHtml25(session.operationsRecordHref)}">Open operations record</a> \xB7 ` : ""}<a href="${escapeHtml25(session.replayHref)}">Open replay</a></p>` : "",
|
|
16518
16645
|
"</article>"
|
|
16519
16646
|
].join("")),
|
|
16520
16647
|
"</div>"
|
|
@@ -16890,7 +17017,7 @@ var assertVoiceAgentSquadContractEvidence = (reports, input = {}) => {
|
|
|
16890
17017
|
import { Elysia as Elysia26 } from "elysia";
|
|
16891
17018
|
var DEFAULT_WARN_AFTER_MS = 1800;
|
|
16892
17019
|
var DEFAULT_FAIL_AFTER_MS = 3200;
|
|
16893
|
-
var
|
|
17020
|
+
var escapeHtml26 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
16894
17021
|
var firstNumber = (values) => values.filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
16895
17022
|
var getString11 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
16896
17023
|
var createTraceStageIndex = (events) => {
|
|
@@ -17001,7 +17128,7 @@ var summarizeVoiceTurnLatency = async (options) => {
|
|
|
17001
17128
|
warnings
|
|
17002
17129
|
};
|
|
17003
17130
|
};
|
|
17004
|
-
var
|
|
17131
|
+
var formatMs2 = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
|
|
17005
17132
|
var renderVoiceTurnLatencyHTML = (report, options = {}) => {
|
|
17006
17133
|
const title = options.title ?? "Voice Turn Latency";
|
|
17007
17134
|
const snippet = `app.use(
|
|
@@ -17022,11 +17149,11 @@ await traceStore.append({
|
|
|
17022
17149
|
turnId,
|
|
17023
17150
|
type: 'turn_latency.stage'
|
|
17024
17151
|
});`;
|
|
17025
|
-
const turns = report.turns.map((turn) => `<article class="turn ${
|
|
17026
|
-
<header><div><p class="eyebrow">${
|
|
17027
|
-
<dl>${turn.stages.map((stage) => `<div><dt>${
|
|
17152
|
+
const turns = report.turns.map((turn) => `<article class="turn ${escapeHtml26(turn.status)}">
|
|
17153
|
+
<header><div><p class="eyebrow">${escapeHtml26(turn.sessionId)} \xB7 ${escapeHtml26(turn.turnId)}</p><h2>${escapeHtml26(turn.text || "Empty turn")}</h2></div><strong>${escapeHtml26(turn.status)}</strong></header>
|
|
17154
|
+
<dl>${turn.stages.map((stage) => `<div><dt>${escapeHtml26(stage.label)}</dt><dd>${escapeHtml26(formatMs2(stage.valueMs))}</dd></div>`).join("")}</dl>
|
|
17028
17155
|
</article>`).join("");
|
|
17029
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
17156
|
+
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,.primitive{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}.primitive p{color:#cbd5e1}.primitive pre{background:#0a0d10;border:1px solid #2a323a;border-radius:16px;color:#d9fff7;overflow:auto;padding:16px}.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>${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">avg ${escapeHtml26(formatMs2(report.averageTotalMs))}</span><span class="pill">${String(report.warnings)} warnings</span><span class="pill">${String(report.failed)} failed</span></div></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceTurnLatencyRoutes(...)</code> exposes the full turn waterfall</h2><p>Attach stage traces for speech detection, commit, model response, TTS send, and first audio so teams can prove where latency actually comes from.</p><pre><code>${escapeHtml26(snippet)}</code></pre></section>${turns || '<section class="turn"><p>No committed turns found.</p></section>'}</main></body></html>`;
|
|
17030
17157
|
};
|
|
17031
17158
|
var createVoiceTurnLatencyJSONHandler = (options) => async () => summarizeVoiceTurnLatency(options);
|
|
17032
17159
|
var createVoiceTurnLatencyHTMLHandler = (options) => async () => {
|
|
@@ -17053,7 +17180,7 @@ var createVoiceTurnLatencyRoutes = (options) => {
|
|
|
17053
17180
|
};
|
|
17054
17181
|
// src/liveLatency.ts
|
|
17055
17182
|
import { Elysia as Elysia27 } from "elysia";
|
|
17056
|
-
var
|
|
17183
|
+
var escapeHtml27 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
17057
17184
|
var percentile3 = (values, percentileValue) => {
|
|
17058
17185
|
if (values.length === 0) {
|
|
17059
17186
|
return;
|
|
@@ -17098,7 +17225,7 @@ var summarizeVoiceLiveLatency = async (options) => {
|
|
|
17098
17225
|
warnings
|
|
17099
17226
|
};
|
|
17100
17227
|
};
|
|
17101
|
-
var
|
|
17228
|
+
var formatMs3 = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
|
|
17102
17229
|
var renderVoiceLiveLatencyHTML = (report, options = {}) => {
|
|
17103
17230
|
const title = options.title ?? "Voice Live Latency";
|
|
17104
17231
|
const snippet = `app.use(
|
|
@@ -17120,8 +17247,8 @@ await traceStore.append({
|
|
|
17120
17247
|
sessionId,
|
|
17121
17248
|
type: 'client.live_latency'
|
|
17122
17249
|
});`;
|
|
17123
|
-
const rows = report.recent.map((sample) => `<tr><td>${
|
|
17124
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
17250
|
+
const rows = report.recent.map((sample) => `<tr><td>${escapeHtml27(sample.sessionId)}</td><td>${escapeHtml27(formatMs3(sample.latencyMs))}</td><td>${escapeHtml27(sample.status ?? "unknown")}</td><td>${escapeHtml27(new Date(sample.at).toLocaleString())}</td></tr>`).join("");
|
|
17251
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml27(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,.primitive{background:#141922;border:1px solid #26313d;border-radius:18px}.metrics article,.primitive{padding:16px}.metrics span{color:#a8b0b8}.metrics strong{display:block;font-size:2rem;margin-top:.25rem}.primitive{margin:0 0 18px}.primitive h2{margin:.2rem 0 .5rem}.primitive p{color:#cbd5e1}.primitive pre{background:#080b10;border:1px solid #26313d;border-radius:16px;color:#d9fff7;overflow:auto;padding:16px}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>${escapeHtml27(title)}</h1><p>Recent real browser speech-to-assistant response measurements from persisted <code>client.live_latency</code> traces.</p><p class="status ${escapeHtml27(report.status)}">Status: ${escapeHtml27(report.status)}</p><section class="metrics"><article><span>p50</span><strong>${escapeHtml27(formatMs3(report.p50LatencyMs))}</strong></article><article><span>p95</span><strong>${escapeHtml27(formatMs3(report.p95LatencyMs))}</strong></article><article><span>Average</span><strong>${escapeHtml27(formatMs3(report.averageLatencyMs))}</strong></article><article><span>Samples</span><strong>${String(report.total)}</strong></article></section></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceLiveLatencyRoutes(...)</code> turns real browser timing into a release gate</h2><p>Persist live timing samples into the trace store so readiness, simulations, and trace timelines all point at the same self-hosted proof.</p><pre><code>${escapeHtml27(snippet)}</code></pre></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>`;
|
|
17125
17252
|
};
|
|
17126
17253
|
var createVoiceLiveLatencyRoutes = (options) => {
|
|
17127
17254
|
const path = options.path ?? "/api/live-latency";
|
|
@@ -17447,7 +17574,7 @@ None.
|
|
|
17447
17574
|
// src/turnQuality.ts
|
|
17448
17575
|
import { Elysia as Elysia28 } from "elysia";
|
|
17449
17576
|
var DEFAULT_CONFIDENCE_WARN_THRESHOLD = 0.72;
|
|
17450
|
-
var
|
|
17577
|
+
var escapeHtml28 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
17451
17578
|
var getTurnLatencyMs = (turn) => {
|
|
17452
17579
|
const firstTranscriptAt = turn.transcripts.map((transcript) => transcript.endedAtMs ?? transcript.startedAtMs).filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
17453
17580
|
if (firstTranscriptAt === undefined) {
|
|
@@ -17518,24 +17645,24 @@ var summarizeVoiceTurnQuality = async (options) => {
|
|
|
17518
17645
|
};
|
|
17519
17646
|
var renderVoiceTurnQualityHTML = (report, options = {}) => {
|
|
17520
17647
|
const title = options.title ?? "Voice Turn Quality";
|
|
17521
|
-
const turns = report.turns.map((turn) => `<article class="turn ${
|
|
17648
|
+
const turns = report.turns.map((turn) => `<article class="turn ${escapeHtml28(turn.status)}">
|
|
17522
17649
|
<div class="turn-header">
|
|
17523
17650
|
<div>
|
|
17524
|
-
<p class="eyebrow">${
|
|
17525
|
-
<h2>${
|
|
17651
|
+
<p class="eyebrow">${escapeHtml28(turn.sessionId)} \xB7 ${escapeHtml28(turn.turnId)}</p>
|
|
17652
|
+
<h2>${escapeHtml28(turn.text || "Empty turn")}</h2>
|
|
17526
17653
|
</div>
|
|
17527
|
-
<strong>${
|
|
17654
|
+
<strong>${escapeHtml28(turn.status)}</strong>
|
|
17528
17655
|
</div>
|
|
17529
17656
|
<dl>
|
|
17530
|
-
<div><dt>Source</dt><dd>${
|
|
17657
|
+
<div><dt>Source</dt><dd>${escapeHtml28(turn.source ?? "unknown")}</dd></div>
|
|
17531
17658
|
<div><dt>Confidence</dt><dd>${turn.averageConfidence === undefined ? "n/a" : `${Math.round(turn.averageConfidence * 100)}%`}</dd></div>
|
|
17532
|
-
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${
|
|
17533
|
-
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${
|
|
17659
|
+
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${escapeHtml28(turn.fallbackSelectionReason ?? "selected")})` : "no"}</dd></div>
|
|
17660
|
+
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${escapeHtml28(turn.correctionProvider)}` : ""}` : "none"}</dd></div>
|
|
17534
17661
|
<div><dt>Transcripts</dt><dd>${String(turn.selectedTranscriptCount)} selected \xB7 ${String(turn.finalTranscriptCount)} final \xB7 ${String(turn.partialTranscriptCount)} partial</dd></div>
|
|
17535
17662
|
<div><dt>Cost</dt><dd>${turn.costUnits === undefined ? "n/a" : String(turn.costUnits)}</dd></div>
|
|
17536
17663
|
</dl>
|
|
17537
17664
|
</article>`).join("");
|
|
17538
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
17665
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml28(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>${escapeHtml28(title)}</h1><div class="summary"><span class="pill ${escapeHtml28(report.status)}">${escapeHtml28(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>`;
|
|
17539
17666
|
};
|
|
17540
17667
|
var createVoiceTurnQualityJSONHandler = (options) => async () => summarizeVoiceTurnQuality(options);
|
|
17541
17668
|
var createVoiceTurnQualityHTMLHandler = (options) => async () => {
|
|
@@ -18462,7 +18589,7 @@ var resolveTwilioStreamParameters = async (parameters, input) => {
|
|
|
18462
18589
|
return parameters;
|
|
18463
18590
|
};
|
|
18464
18591
|
var joinUrlPath2 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
18465
|
-
var
|
|
18592
|
+
var escapeHtml29 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
18466
18593
|
var getWebhookVerificationUrl = (webhook, input) => {
|
|
18467
18594
|
if (!webhook?.verificationUrl) {
|
|
18468
18595
|
return;
|
|
@@ -18505,23 +18632,23 @@ var buildTwilioVoiceSetupStatus = async (options, input) => {
|
|
|
18505
18632
|
};
|
|
18506
18633
|
var renderTwilioVoiceSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
18507
18634
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Twilio setup</p>
|
|
18508
|
-
<h1>${
|
|
18635
|
+
<h1>${escapeHtml29(title)}</h1>
|
|
18509
18636
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
18510
18637
|
<section>
|
|
18511
18638
|
<h2>URLs</h2>
|
|
18512
18639
|
<ul>
|
|
18513
|
-
<li><strong>TwiML:</strong> <code>${
|
|
18514
|
-
<li><strong>Media stream:</strong> <code>${
|
|
18515
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
18640
|
+
<li><strong>TwiML:</strong> <code>${escapeHtml29(status.urls.twiml)}</code></li>
|
|
18641
|
+
<li><strong>Media stream:</strong> <code>${escapeHtml29(status.urls.stream)}</code></li>
|
|
18642
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml29(status.urls.webhook)}</code></li>
|
|
18516
18643
|
</ul>
|
|
18517
18644
|
</section>
|
|
18518
18645
|
<section>
|
|
18519
18646
|
<h2>Signing</h2>
|
|
18520
18647
|
<p>Mode: <code>${status.signing.mode}</code></p>
|
|
18521
|
-
${status.signing.verificationUrl ? `<p>Verification URL: <code>${
|
|
18648
|
+
${status.signing.verificationUrl ? `<p>Verification URL: <code>${escapeHtml29(status.signing.verificationUrl)}</code></p>` : ""}
|
|
18522
18649
|
</section>
|
|
18523
|
-
${status.missing.length ? `<section><h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
18524
|
-
${status.warnings.length ? `<section><h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
18650
|
+
${status.missing.length ? `<section><h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml29(name)}</code></li>`).join("")}</ul></section>` : ""}
|
|
18651
|
+
${status.warnings.length ? `<section><h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml29(warning)}</li>`).join("")}</ul></section>` : ""}
|
|
18525
18652
|
</main>`;
|
|
18526
18653
|
var extractTwilioStreamUrl = (twiml) => twiml.match(/<Stream\b[^>]*\surl="([^"]+)"/i)?.[1]?.replaceAll("&", "&");
|
|
18527
18654
|
var createSmokeCheck = (name, status, message, details) => ({
|
|
@@ -18532,20 +18659,20 @@ var createSmokeCheck = (name, status, message, details) => ({
|
|
|
18532
18659
|
});
|
|
18533
18660
|
var renderTwilioVoiceSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
18534
18661
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Twilio smoke test</p>
|
|
18535
|
-
<h1>${
|
|
18662
|
+
<h1>${escapeHtml29(title)}</h1>
|
|
18536
18663
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
18537
18664
|
<section>
|
|
18538
18665
|
<h2>Checks</h2>
|
|
18539
18666
|
<ul>
|
|
18540
|
-
${report.checks.map((check) => `<li><strong>${
|
|
18667
|
+
${report.checks.map((check) => `<li><strong>${escapeHtml29(check.name)}</strong>: ${escapeHtml29(check.status)}${check.message ? ` - ${escapeHtml29(check.message)}` : ""}</li>`).join("")}
|
|
18541
18668
|
</ul>
|
|
18542
18669
|
</section>
|
|
18543
18670
|
<section>
|
|
18544
18671
|
<h2>Observed URLs</h2>
|
|
18545
18672
|
<ul>
|
|
18546
|
-
<li><strong>TwiML:</strong> <code>${
|
|
18547
|
-
<li><strong>Stream:</strong> <code>${
|
|
18548
|
-
<li><strong>Webhook:</strong> <code>${
|
|
18673
|
+
<li><strong>TwiML:</strong> <code>${escapeHtml29(report.setup.urls.twiml)}</code></li>
|
|
18674
|
+
<li><strong>Stream:</strong> <code>${escapeHtml29(report.twiml?.streamUrl ?? report.setup.urls.stream)}</code></li>
|
|
18675
|
+
<li><strong>Webhook:</strong> <code>${escapeHtml29(report.setup.urls.webhook)}</code></li>
|
|
18549
18676
|
</ul>
|
|
18550
18677
|
</section>
|
|
18551
18678
|
</main>`;
|
|
@@ -19142,7 +19269,7 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
19142
19269
|
|
|
19143
19270
|
// src/telephony/plivo.ts
|
|
19144
19271
|
var escapeXml3 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
19145
|
-
var
|
|
19272
|
+
var escapeHtml30 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
19146
19273
|
var joinUrlPath3 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
19147
19274
|
var resolveRequestOrigin2 = (request) => {
|
|
19148
19275
|
const url = new URL(request.url);
|
|
@@ -19572,21 +19699,21 @@ var buildPlivoVoiceSetupStatus = async (options, input) => {
|
|
|
19572
19699
|
};
|
|
19573
19700
|
var renderPlivoSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
19574
19701
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Plivo setup</p>
|
|
19575
|
-
<h1>${
|
|
19702
|
+
<h1>${escapeHtml30(title)}</h1>
|
|
19576
19703
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
19577
19704
|
<ul>
|
|
19578
|
-
<li><strong>Answer XML:</strong> <code>${
|
|
19579
|
-
<li><strong>Audio stream:</strong> <code>${
|
|
19580
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
19705
|
+
<li><strong>Answer XML:</strong> <code>${escapeHtml30(status.urls.answer)}</code></li>
|
|
19706
|
+
<li><strong>Audio stream:</strong> <code>${escapeHtml30(status.urls.stream)}</code></li>
|
|
19707
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml30(status.urls.webhook)}</code></li>
|
|
19581
19708
|
</ul>
|
|
19582
|
-
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
19583
|
-
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
19709
|
+
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml30(name)}</code></li>`).join("")}</ul>` : ""}
|
|
19710
|
+
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml30(warning)}</li>`).join("")}</ul>` : ""}
|
|
19584
19711
|
</main>`;
|
|
19585
19712
|
var renderPlivoSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
19586
19713
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Plivo smoke test</p>
|
|
19587
|
-
<h1>${
|
|
19714
|
+
<h1>${escapeHtml30(title)}</h1>
|
|
19588
19715
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
19589
|
-
<ul>${report.checks.map((check) => `<li><strong>${
|
|
19716
|
+
<ul>${report.checks.map((check) => `<li><strong>${escapeHtml30(check.name)}</strong>: ${escapeHtml30(check.status)}${check.message ? ` - ${escapeHtml30(check.message)}` : ""}</li>`).join("")}</ul>
|
|
19590
19717
|
</main>`;
|
|
19591
19718
|
var runPlivoSmokeTest = async (input) => {
|
|
19592
19719
|
const setup = await buildPlivoVoiceSetupStatus(input.options, input);
|
|
@@ -19790,7 +19917,7 @@ import { Buffer as Buffer6 } from "buffer";
|
|
|
19790
19917
|
import { Database as Database2 } from "bun:sqlite";
|
|
19791
19918
|
import { Elysia as Elysia32 } from "elysia";
|
|
19792
19919
|
var escapeXml4 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
19793
|
-
var
|
|
19920
|
+
var escapeHtml31 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
19794
19921
|
var joinUrlPath4 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
19795
19922
|
var resolveRequestOrigin3 = (request) => {
|
|
19796
19923
|
const url = new URL(request.url);
|
|
@@ -20183,21 +20310,21 @@ var buildTelnyxVoiceSetupStatus = async (options, input) => {
|
|
|
20183
20310
|
};
|
|
20184
20311
|
var renderTelnyxSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
20185
20312
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Telnyx setup</p>
|
|
20186
|
-
<h1>${
|
|
20313
|
+
<h1>${escapeHtml31(title)}</h1>
|
|
20187
20314
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
20188
20315
|
<ul>
|
|
20189
|
-
<li><strong>TeXML:</strong> <code>${
|
|
20190
|
-
<li><strong>Media stream:</strong> <code>${
|
|
20191
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
20316
|
+
<li><strong>TeXML:</strong> <code>${escapeHtml31(status.urls.texml)}</code></li>
|
|
20317
|
+
<li><strong>Media stream:</strong> <code>${escapeHtml31(status.urls.stream)}</code></li>
|
|
20318
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml31(status.urls.webhook)}</code></li>
|
|
20192
20319
|
</ul>
|
|
20193
|
-
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
20194
|
-
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
20320
|
+
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml31(name)}</code></li>`).join("")}</ul>` : ""}
|
|
20321
|
+
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml31(warning)}</li>`).join("")}</ul>` : ""}
|
|
20195
20322
|
</main>`;
|
|
20196
20323
|
var renderTelnyxSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
20197
20324
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Telnyx smoke test</p>
|
|
20198
|
-
<h1>${
|
|
20325
|
+
<h1>${escapeHtml31(title)}</h1>
|
|
20199
20326
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
20200
|
-
<ul>${report.checks.map((check) => `<li><strong>${
|
|
20327
|
+
<ul>${report.checks.map((check) => `<li><strong>${escapeHtml31(check.name)}</strong>: ${escapeHtml31(check.status)}${check.message ? ` - ${escapeHtml31(check.message)}` : ""}</li>`).join("")}</ul>
|
|
20201
20328
|
</main>`;
|
|
20202
20329
|
var runTelnyxSmokeTest = async (input) => {
|
|
20203
20330
|
const setup = await buildTelnyxVoiceSetupStatus(input.options, input);
|
|
@@ -20401,7 +20528,7 @@ var createTelnyxVoiceRoutes = (options = {}) => {
|
|
|
20401
20528
|
|
|
20402
20529
|
// src/telephony/matrix.ts
|
|
20403
20530
|
import { Elysia as Elysia33 } from "elysia";
|
|
20404
|
-
var
|
|
20531
|
+
var escapeHtml32 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
20405
20532
|
var labelForProvider = (provider) => provider.split("-").map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`).join(" ");
|
|
20406
20533
|
var resolveEntryStatus = (contract, setup, smoke) => {
|
|
20407
20534
|
if (!contract.pass || !setup.ready || smoke?.pass === false) {
|
|
@@ -20462,13 +20589,13 @@ var badgeStyles = {
|
|
|
20462
20589
|
};
|
|
20463
20590
|
var renderVoiceTelephonyCarrierMatrixHTML = (matrix, options = {}) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 1040px; margin: 40px auto; padding: 0 20px; color: #172033;">
|
|
20464
20591
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Carrier matrix</p>
|
|
20465
|
-
<h1 style="font-size: 34px; margin: 0 0 8px;">${
|
|
20592
|
+
<h1 style="font-size: 34px; margin: 0 0 8px;">${escapeHtml32(options.title ?? "AbsoluteJS Voice Carrier Matrix")}</h1>
|
|
20466
20593
|
<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>
|
|
20467
20594
|
<section style="display:grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px;">
|
|
20468
20595
|
${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);">
|
|
20469
20596
|
<div style="display:flex; justify-content:space-between; gap:12px; align-items:center;">
|
|
20470
|
-
<h2 style="margin:0; font-size:20px;">${
|
|
20471
|
-
<span style="border:1px solid; border-radius:999px; padding:4px 10px; font-size:12px; font-weight:700; ${badgeStyles[entry.status]}">${
|
|
20597
|
+
<h2 style="margin:0; font-size:20px;">${escapeHtml32(entry.name)}</h2>
|
|
20598
|
+
<span style="border:1px solid; border-radius:999px; padding:4px 10px; font-size:12px; font-weight:700; ${badgeStyles[entry.status]}">${escapeHtml32(entry.status.toUpperCase())}</span>
|
|
20472
20599
|
</div>
|
|
20473
20600
|
<dl style="display:grid; grid-template-columns: 1fr 1fr; gap:8px 12px; margin:16px 0;">
|
|
20474
20601
|
<dt style="color:#64748b;">Setup</dt><dd style="margin:0; font-weight:700;">${entry.ready ? "Ready" : "Needs attention"}</dd>
|
|
@@ -20476,9 +20603,9 @@ ${matrix.entries.map((entry) => `<article style="border:1px solid #d9e2ec; borde
|
|
|
20476
20603
|
<dt style="color:#64748b;">Smoke</dt><dd style="margin:0; font-weight:700;">${entry.smoke ? entry.smoke.pass ? "Pass" : "Fail" : "Missing"}</dd>
|
|
20477
20604
|
<dt style="color:#64748b;">Contract</dt><dd style="margin:0; font-weight:700;">${entry.contract.pass ? "Pass" : "Fail"}</dd>
|
|
20478
20605
|
</dl>
|
|
20479
|
-
<p style="margin:0 0 8px; color:#475569;"><strong>Stream:</strong> <code>${
|
|
20480
|
-
<p style="margin:0 0 12px; color:#475569;"><strong>Webhook:</strong> <code>${
|
|
20481
|
-
${entry.issues.length ? `<ul style="margin:12px 0 0; padding-left:18px;">${entry.issues.map((issue) => `<li>${
|
|
20606
|
+
<p style="margin:0 0 8px; color:#475569;"><strong>Stream:</strong> <code>${escapeHtml32(entry.setup.urls.stream || "missing")}</code></p>
|
|
20607
|
+
<p style="margin:0 0 12px; color:#475569;"><strong>Webhook:</strong> <code>${escapeHtml32(entry.setup.urls.webhook || "missing")}</code></p>
|
|
20608
|
+
${entry.issues.length ? `<ul style="margin:12px 0 0; padding-left:18px;">${entry.issues.map((issue) => `<li>${escapeHtml32(issue.severity)}: ${escapeHtml32(issue.message)}</li>`).join("")}</ul>` : '<p style="margin:12px 0 0; color:#166534;">No contract issues.</p>'}
|
|
20482
20609
|
</article>`).join("")}
|
|
20483
20610
|
</section>
|
|
20484
20611
|
</main>`;
|
|
@@ -20514,7 +20641,7 @@ var defaultRequirements = [
|
|
|
20514
20641
|
"lifecycle-outcome",
|
|
20515
20642
|
"no-session-error"
|
|
20516
20643
|
];
|
|
20517
|
-
var
|
|
20644
|
+
var escapeHtml33 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
20518
20645
|
var payloadType = (event) => typeof event.payload.type === "string" ? event.payload.type : undefined;
|
|
20519
20646
|
var hasTextPayload = (event) => ["text", "assistantText", "transcript"].some((key) => {
|
|
20520
20647
|
const value = event.payload[key];
|
|
@@ -20623,10 +20750,10 @@ var resolveHandlerOptions = async (options, input) => ({
|
|
|
20623
20750
|
});
|
|
20624
20751
|
var renderVoicePhoneAgentProductionSmokeHTML = (report, options = {}) => {
|
|
20625
20752
|
const title = options.title ?? "AbsoluteJS Voice Phone Smoke Contract";
|
|
20626
|
-
const issues = report.issues.map((issue) => `<li><strong>${
|
|
20627
|
-
const outcomes = report.observed.lifecycleOutcomes.map((outcome) => `<span class="pill">${
|
|
20628
|
-
const requirements = report.required.map((requirement) => `<span class="pill">${
|
|
20629
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
20753
|
+
const issues = report.issues.map((issue) => `<li><strong>${escapeHtml33(issue.requirement)}</strong>: ${escapeHtml33(issue.message)}</li>`).join("");
|
|
20754
|
+
const outcomes = report.observed.lifecycleOutcomes.map((outcome) => `<span class="pill">${escapeHtml33(outcome)}</span>`).join("");
|
|
20755
|
+
const requirements = report.required.map((requirement) => `<span class="pill">${escapeHtml33(requirement)}</span>`).join("");
|
|
20756
|
+
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:#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>${escapeHtml33(title)}</h1><p class="status ${report.pass ? "pass" : "fail"}">${report.pass ? "PASS" : "FAIL"}</p><p>Contract <code>${escapeHtml33(report.contractId)}</code>${report.provider ? ` for <code>${escapeHtml33(report.provider)}</code>` : ""}${report.sessionId ? ` on session <code>${escapeHtml33(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>`;
|
|
20630
20757
|
};
|
|
20631
20758
|
var createVoicePhoneAgentProductionSmokeJSONHandler = (options) => async ({
|
|
20632
20759
|
query,
|
|
@@ -20692,7 +20819,7 @@ var PHONE_AGENT_LIFECYCLE_STAGES = [
|
|
|
20692
20819
|
"completed",
|
|
20693
20820
|
"failed"
|
|
20694
20821
|
];
|
|
20695
|
-
var
|
|
20822
|
+
var escapeHtml34 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
20696
20823
|
var loadRouteJson = async (input) => {
|
|
20697
20824
|
const response = await input.app.handle(new Request(new URL(input.path, input.origin).toString(), {
|
|
20698
20825
|
headers: {
|
|
@@ -20930,10 +21057,10 @@ var renderVoicePhoneAgentSetupHTML = (report) => {
|
|
|
20930
21057
|
const entry = findCarrierMatrixEntry(report.matrix, carrier);
|
|
20931
21058
|
const urls = entry?.setup.urls;
|
|
20932
21059
|
const primaryUrl = carrier.provider === "plivo" ? urls?.twiml : urls?.twiml;
|
|
20933
|
-
return `<tr><td>${
|
|
21060
|
+
return `<tr><td>${escapeHtml34(carrier.name ?? carrier.provider)}</td><td>${escapeHtml34(carrier.provider)}</td><td><code>${escapeHtml34(carrier.setupPath || "disabled")}</code></td><td><code>${escapeHtml34(carrier.smokePath || "disabled")}</code></td><td>${entry ? `<span class="${escapeHtml34(entry.status)}">${escapeHtml34(entry.status.toUpperCase())}</span>` : "unknown"}</td><td>${primaryUrl ? `<code>${escapeHtml34(primaryUrl)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.webhook ? `<code>${escapeHtml34(urls.webhook)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.stream ? `<code>${escapeHtml34(urls.stream)}</code>` : '<span class="muted">missing</span>'}</td></tr>`;
|
|
20934
21061
|
}).join("");
|
|
20935
|
-
const stageList = report.lifecycleStages.map((stage) => `<li><code>${
|
|
20936
|
-
const snippet =
|
|
21062
|
+
const stageList = report.lifecycleStages.map((stage) => `<li><code>${escapeHtml34(stage)}</code></li>`).join("");
|
|
21063
|
+
const snippet = escapeHtml34(`const phoneAgent = createVoicePhoneAgent({
|
|
20937
21064
|
carriers: [
|
|
20938
21065
|
{
|
|
20939
21066
|
provider: 'twilio',
|
|
@@ -20967,11 +21094,11 @@ app.use(
|
|
|
20967
21094
|
);`);
|
|
20968
21095
|
const checklist = report.carriers.map((carrier) => {
|
|
20969
21096
|
const instruction = report.setupInstructions.find((candidate) => candidate.provider === carrier.provider && candidate.carrierName === (carrier.name ?? carrier.provider));
|
|
20970
|
-
const issueList = instruction?.issues.map((issue) => `<li>${
|
|
20971
|
-
const steps = instruction?.steps.map((step) => `<li>${
|
|
20972
|
-
return `<article><h3>${
|
|
21097
|
+
const issueList = instruction?.issues.map((issue) => `<li>${escapeHtml34(issue)}</li>`).join("") ?? "";
|
|
21098
|
+
const steps = instruction?.steps.map((step) => `<li>${escapeHtml34(step)}</li>`).join("") ?? "";
|
|
21099
|
+
return `<article><h3>${escapeHtml34(carrier.name ?? carrier.provider)}</h3><ol>${steps}</ol>${issueList ? `<ul class="issues">${issueList}</ul>` : '<p class="pass">No carrier contract issues.</p>'}</article>`;
|
|
20973
21100
|
}).join("");
|
|
20974
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
21101
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml34(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,.primitive{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}.primitive{background:#151d27;border-color:#365a60}.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}.primitive p{color:#cbd5de;line-height:1.55}.primitive pre{background:#0b1118;border:1px solid #283544;border-radius:18px;color:#fef3c7;overflow:auto;padding:16px}.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>${escapeHtml34(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="${escapeHtml34(report.matrixPath)}?format=html">Open carrier matrix</a></p>` : ""}</section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoicePhoneAgent(...)</code> builds this carrier control plane</h2><p>Mount carrier routes once, expose setup and smoke proof, then feed the same carrier matrix and phone-agent smoke reports into production readiness so carrier regressions block deploys.</p><pre><code>${snippet}</code></pre></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>`;
|
|
20975
21102
|
};
|
|
20976
21103
|
var createVoicePhoneAgent = (options) => {
|
|
20977
21104
|
const carrierSummaries = options.carriers.map((carrier) => ({
|
|
@@ -23040,7 +23167,7 @@ var createOpenAIVoiceTTS = (options) => {
|
|
|
23040
23167
|
};
|
|
23041
23168
|
// src/providerCapabilities.ts
|
|
23042
23169
|
import { Elysia as Elysia36 } from "elysia";
|
|
23043
|
-
var
|
|
23170
|
+
var escapeHtml35 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23044
23171
|
var fromProviderList = (kind, providers, options) => (providers ?? []).map((provider) => ({
|
|
23045
23172
|
configured: true,
|
|
23046
23173
|
features: options.features?.[provider],
|
|
@@ -23103,27 +23230,27 @@ var summarizeVoiceProviderCapabilities = async (options) => {
|
|
|
23103
23230
|
var renderVoiceProviderCapabilityHTML = (report, options = {}) => {
|
|
23104
23231
|
const title = options.title ?? "Voice Provider Capabilities";
|
|
23105
23232
|
const cards = report.capabilities.map((capability) => {
|
|
23106
|
-
const features = (capability.features ?? []).map((feature) => `<span class="pill">${
|
|
23107
|
-
return `<article class="card ${
|
|
23233
|
+
const features = (capability.features ?? []).map((feature) => `<span class="pill">${escapeHtml35(feature)}</span>`).join("");
|
|
23234
|
+
return `<article class="card ${escapeHtml35(capability.status)}">
|
|
23108
23235
|
<div class="card-header">
|
|
23109
23236
|
<div>
|
|
23110
|
-
<p class="eyebrow">${
|
|
23111
|
-
<h2>${
|
|
23237
|
+
<p class="eyebrow">${escapeHtml35(capability.kind)}</p>
|
|
23238
|
+
<h2>${escapeHtml35(capability.label ?? capability.provider)}</h2>
|
|
23112
23239
|
</div>
|
|
23113
|
-
<strong>${
|
|
23240
|
+
<strong>${escapeHtml35(capability.status)}</strong>
|
|
23114
23241
|
</div>
|
|
23115
|
-
${capability.description ? `<p>${
|
|
23242
|
+
${capability.description ? `<p>${escapeHtml35(capability.description)}</p>` : ""}
|
|
23116
23243
|
<dl>
|
|
23117
23244
|
<div><dt>Configured</dt><dd>${capability.configured ? "yes" : "no"}</dd></div>
|
|
23118
23245
|
<div><dt>Selected</dt><dd>${capability.selected ? "yes" : "no"}</dd></div>
|
|
23119
|
-
<div><dt>Model</dt><dd>${
|
|
23246
|
+
<div><dt>Model</dt><dd>${escapeHtml35(capability.model ?? "default")}</dd></div>
|
|
23120
23247
|
<div><dt>Runs</dt><dd>${String(capability.health?.runCount ?? 0)}</dd></div>
|
|
23121
23248
|
<div><dt>Errors</dt><dd>${String(capability.health?.errorCount ?? 0)}</dd></div>
|
|
23122
23249
|
</dl>
|
|
23123
23250
|
${features ? `<div class="features">${features}</div>` : ""}
|
|
23124
23251
|
</article>`;
|
|
23125
23252
|
}).join("");
|
|
23126
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
23253
|
+
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:#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>${escapeHtml35(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>`;
|
|
23127
23254
|
};
|
|
23128
23255
|
var createVoiceProviderCapabilityJSONHandler = (options) => async () => summarizeVoiceProviderCapabilities(options);
|
|
23129
23256
|
var createVoiceProviderCapabilityHTMLHandler = (options) => async () => {
|
|
@@ -23150,7 +23277,7 @@ var createVoiceProviderCapabilityRoutes = (options) => {
|
|
|
23150
23277
|
};
|
|
23151
23278
|
// src/resilienceRoutes.ts
|
|
23152
23279
|
import { Elysia as Elysia37 } from "elysia";
|
|
23153
|
-
var
|
|
23280
|
+
var escapeHtml36 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23154
23281
|
var getString13 = (value) => typeof value === "string" ? value : undefined;
|
|
23155
23282
|
var getNumber7 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
23156
23283
|
var getBoolean2 = (value) => value === true;
|
|
@@ -23298,13 +23425,13 @@ var summarizeRoutingEvents = (events) => {
|
|
|
23298
23425
|
};
|
|
23299
23426
|
var renderProviderCards = (title, providers) => {
|
|
23300
23427
|
if (providers.length === 0) {
|
|
23301
|
-
return `<p class="muted">No ${
|
|
23428
|
+
return `<p class="muted">No ${escapeHtml36(title)} provider health yet.</p>`;
|
|
23302
23429
|
}
|
|
23303
23430
|
return `<div class="provider-grid">${providers.map((provider) => `
|
|
23304
|
-
<article class="card provider ${
|
|
23431
|
+
<article class="card provider ${escapeHtml36(provider.status)}">
|
|
23305
23432
|
<div class="card-header">
|
|
23306
|
-
<strong>${
|
|
23307
|
-
<span>${
|
|
23433
|
+
<strong>${escapeHtml36(provider.provider)}</strong>
|
|
23434
|
+
<span>${escapeHtml36(provider.status)}${provider.recommended ? " \xB7 recommended" : ""}</span>
|
|
23308
23435
|
</div>
|
|
23309
23436
|
<dl>
|
|
23310
23437
|
<div><dt>Runs</dt><dd>${provider.runCount}</dd></div>
|
|
@@ -23313,7 +23440,7 @@ var renderProviderCards = (title, providers) => {
|
|
|
23313
23440
|
<div><dt>Timeouts</dt><dd>${provider.timeoutCount}</dd></div>
|
|
23314
23441
|
<div><dt>Fallbacks</dt><dd>${provider.fallbackCount}</dd></div>
|
|
23315
23442
|
</dl>
|
|
23316
|
-
${provider.lastError ? `<p class="muted">${
|
|
23443
|
+
${provider.lastError ? `<p class="muted">${escapeHtml36(provider.lastError)}</p>` : ""}
|
|
23317
23444
|
</article>
|
|
23318
23445
|
`).join("")}</div>`;
|
|
23319
23446
|
};
|
|
@@ -23322,24 +23449,24 @@ var renderTimeline2 = (events) => {
|
|
|
23322
23449
|
return '<p class="muted">No provider routing events yet. Run the app or simulate provider failover.</p>';
|
|
23323
23450
|
}
|
|
23324
23451
|
return `<div class="timeline">${events.slice(0, 40).map((event) => `
|
|
23325
|
-
<article class="card event ${
|
|
23452
|
+
<article class="card event ${escapeHtml36(event.status ?? "unknown")}">
|
|
23326
23453
|
<div class="card-header">
|
|
23327
|
-
<strong>${
|
|
23454
|
+
<strong>${escapeHtml36(event.kind.toUpperCase())} ${escapeHtml36(event.operation ?? "generate")}</strong>
|
|
23328
23455
|
<span>${new Date(event.at).toLocaleString()}</span>
|
|
23329
23456
|
</div>
|
|
23330
23457
|
<p>
|
|
23331
|
-
<span class="pill">${
|
|
23332
|
-
<span class="pill">provider: ${
|
|
23333
|
-
${event.fallbackProvider ? `<span class="pill">fallback: ${
|
|
23458
|
+
<span class="pill">${escapeHtml36(event.status ?? "unknown")}</span>
|
|
23459
|
+
<span class="pill">provider: ${escapeHtml36(event.provider ?? "unknown")}</span>
|
|
23460
|
+
${event.fallbackProvider ? `<span class="pill">fallback: ${escapeHtml36(event.fallbackProvider)}</span>` : ""}
|
|
23334
23461
|
${event.timedOut ? '<span class="pill danger">timed out</span>' : ""}
|
|
23335
23462
|
</p>
|
|
23336
23463
|
<dl>
|
|
23337
23464
|
<div><dt>Attempt</dt><dd>${event.attempt ?? 0}</dd></div>
|
|
23338
23465
|
<div><dt>Elapsed</dt><dd>${event.elapsedMs ?? 0}ms</dd></div>
|
|
23339
23466
|
<div><dt>Budget</dt><dd>${event.latencyBudgetMs ?? 0}ms</dd></div>
|
|
23340
|
-
<div><dt>Session</dt><dd>${
|
|
23467
|
+
<div><dt>Session</dt><dd>${escapeHtml36(event.sessionId)}</dd></div>
|
|
23341
23468
|
</dl>
|
|
23342
|
-
${event.error ? `<p class="muted">${
|
|
23469
|
+
${event.error ? `<p class="muted">${escapeHtml36(event.error)}</p>` : ""}
|
|
23343
23470
|
</article>
|
|
23344
23471
|
`).join("")}</div>`;
|
|
23345
23472
|
};
|
|
@@ -23349,9 +23476,9 @@ var renderSessionKind = (kind, summary) => {
|
|
|
23349
23476
|
const status = latest?.status ?? "idle";
|
|
23350
23477
|
const fallback = latest?.fallbackProvider && latest.fallbackProvider !== provider ? ` -> ${latest.fallbackProvider}` : "";
|
|
23351
23478
|
return `<div>
|
|
23352
|
-
<dt>${
|
|
23353
|
-
<dd>${
|
|
23354
|
-
<small>${
|
|
23479
|
+
<dt>${escapeHtml36(kind.toUpperCase())}</dt>
|
|
23480
|
+
<dd>${escapeHtml36(provider)}${escapeHtml36(fallback)}</dd>
|
|
23481
|
+
<small>${escapeHtml36(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>
|
|
23355
23482
|
</div>`;
|
|
23356
23483
|
};
|
|
23357
23484
|
var renderSessionSummaries = (sessions) => {
|
|
@@ -23359,10 +23486,10 @@ var renderSessionSummaries = (sessions) => {
|
|
|
23359
23486
|
return '<p class="muted">No call-level routing summaries yet. Run a voice session or provider simulation.</p>';
|
|
23360
23487
|
}
|
|
23361
23488
|
return `<div class="session-grid">${sessions.slice(0, 12).map((session) => `
|
|
23362
|
-
<article class="card session ${
|
|
23489
|
+
<article class="card session ${escapeHtml36(session.status)}">
|
|
23363
23490
|
<div class="card-header">
|
|
23364
|
-
<strong>${
|
|
23365
|
-
<span>${
|
|
23491
|
+
<strong>${escapeHtml36(session.sessionId)}</strong>
|
|
23492
|
+
<span>${escapeHtml36(session.status)}</span>
|
|
23366
23493
|
</div>
|
|
23367
23494
|
<p>
|
|
23368
23495
|
<span class="pill">${session.eventCount} routing events</span>
|
|
@@ -23389,21 +23516,21 @@ var renderSimulationControls = (kind, simulation) => {
|
|
|
23389
23516
|
const pathPrefix = simulation.pathPrefix ?? `/api/${kind}-simulate`;
|
|
23390
23517
|
const failureProviders = simulation.failureProviders ?? configuredProviders.map(({ provider }) => provider);
|
|
23391
23518
|
const canFail = (provider) => configuredProviders.some((entry) => entry.provider === provider) && (!simulation.fallbackRequiredProvider || configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider));
|
|
23392
|
-
return `<div class="simulate-panel" data-sim-kind="${kind}" data-sim-prefix="${
|
|
23393
|
-
<p class="muted">${
|
|
23519
|
+
return `<div class="simulate-panel" data-sim-kind="${kind}" data-sim-prefix="${escapeHtml36(pathPrefix)}">
|
|
23520
|
+
<p class="muted">${escapeHtml36(simulation.failureMessage ?? `Simulate ${kind.toUpperCase()} provider failure without changing provider credentials.`)}</p>
|
|
23394
23521
|
<div class="simulate-actions">
|
|
23395
|
-
${failureProviders.map((provider) => `<button type="button" data-provider-fail="${
|
|
23396
|
-
${configuredProviders.map((provider) => `<button type="button" data-provider-recover="${
|
|
23522
|
+
${failureProviders.map((provider) => `<button type="button" data-provider-fail="${escapeHtml36(provider)}"${canFail(provider) ? "" : " disabled"}>Simulate ${escapeHtml36(provider)} ${kind.toUpperCase()} failure</button>`).join("")}
|
|
23523
|
+
${configuredProviders.map((provider) => `<button type="button" data-provider-recover="${escapeHtml36(provider.provider)}">Mark ${escapeHtml36(provider.provider)} recovered</button>`).join("")}
|
|
23397
23524
|
</div>
|
|
23398
|
-
${simulation.fallbackRequiredProvider && !configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider) ? `<p class="muted">${
|
|
23525
|
+
${simulation.fallbackRequiredProvider && !configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider) ? `<p class="muted">${escapeHtml36(simulation.fallbackRequiredMessage ?? `Configure ${simulation.fallbackRequiredProvider} to enable fallback simulation.`)}</p>` : ""}
|
|
23399
23526
|
<pre class="simulate-output" hidden></pre>
|
|
23400
23527
|
</div>`;
|
|
23401
23528
|
};
|
|
23402
23529
|
var renderVoiceResilienceHTML = (input) => {
|
|
23403
23530
|
const summary = summarizeRoutingEvents(input.routingEvents);
|
|
23404
|
-
const kindCounts = [...summary.byKind.entries()].map(([kind, count]) => `<span class="pill">${
|
|
23405
|
-
const links = input.links?.length ? input.links.map((link) => `<a href="${
|
|
23406
|
-
const snippet =
|
|
23531
|
+
const kindCounts = [...summary.byKind.entries()].map(([kind, count]) => `<span class="pill">${escapeHtml36(kind)}: ${String(count)}</span>`).join("");
|
|
23532
|
+
const links = input.links?.length ? input.links.map((link) => `<a href="${escapeHtml36(link.href)}">${escapeHtml36(link.label)}</a>`).join(" \xB7 ") : "";
|
|
23533
|
+
const snippet = escapeHtml36(`const sttSimulator = createVoiceIOProviderFailureSimulator({
|
|
23407
23534
|
kind: 'stt',
|
|
23408
23535
|
providers: ['deepgram', 'assemblyai'],
|
|
23409
23536
|
fallback: ['deepgram', 'assemblyai'],
|
|
@@ -23441,7 +23568,7 @@ app.use(
|
|
|
23441
23568
|
<head>
|
|
23442
23569
|
<meta charset="utf-8" />
|
|
23443
23570
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
23444
|
-
<title>${
|
|
23571
|
+
<title>${escapeHtml36(input.title ?? "AbsoluteJS Voice Resilience")}</title>
|
|
23445
23572
|
<style>
|
|
23446
23573
|
:root { color-scheme: dark; }
|
|
23447
23574
|
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; }
|
|
@@ -23808,7 +23935,7 @@ var statusRank = {
|
|
|
23808
23935
|
warn: 1,
|
|
23809
23936
|
fail: 2
|
|
23810
23937
|
};
|
|
23811
|
-
var
|
|
23938
|
+
var escapeHtml37 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23812
23939
|
var roundMetric3 = (value) => Math.round(value * 1e4) / 1e4;
|
|
23813
23940
|
var rate3 = (count, total) => count / Math.max(1, total);
|
|
23814
23941
|
var uniqueSorted5 = (values) => [
|
|
@@ -24104,11 +24231,11 @@ var renderVoiceProviderSloHTML = (report, options = {}) => {
|
|
|
24104
24231
|
const title = options.title ?? "AbsoluteJS Voice Provider SLOs";
|
|
24105
24232
|
const kindCards = providerKinds.map((kind) => {
|
|
24106
24233
|
const kindReport = report.kinds[kind];
|
|
24107
|
-
const metrics = Object.values(kindReport.metrics).map((metric) => `<div><dt>${
|
|
24234
|
+
const metrics = Object.values(kindReport.metrics).map((metric) => `<div><dt>${escapeHtml37(metric.label)}</dt><dd>${escapeHtml37(formatMetricValue2(metric))}</dd><small>budget ${escapeHtml37(formatMetricThreshold(metric))}</small></div>`).join("");
|
|
24108
24235
|
const providers = kindReport.providers.length ? kindReport.providers.join(", ") : "none recorded";
|
|
24109
|
-
return `<article class="${
|
|
24236
|
+
return `<article class="${escapeHtml37(kindReport.status)}"><h2>${kind.toUpperCase()} <span>${escapeHtml37(kindReport.status)}</span></h2><p>${kindReport.events} routing event(s), ${kindReport.eventsWithLatency} latency sample(s), providers: ${escapeHtml37(providers)}.</p><dl>${metrics}</dl></article>`;
|
|
24110
24237
|
}).join("");
|
|
24111
|
-
const issues = report.issues.length > 0 ? `<ul>${report.issues.map((issue) => `<li class="${
|
|
24238
|
+
const issues = report.issues.length > 0 ? `<ul>${report.issues.map((issue) => `<li class="${escapeHtml37(issue.status)}"><strong>${escapeHtml37(issue.kind ? `${issue.kind.toUpperCase()} ${issue.label}` : issue.label)}</strong><span>${escapeHtml37(issue.detail ?? "")}</span></li>`).join("")}</ul>` : "<p>No provider SLO issues.</p>";
|
|
24112
24239
|
const snippet = `createVoiceProviderSloRoutes({
|
|
24113
24240
|
store: runtimeStorage.traces,
|
|
24114
24241
|
requiredKinds: ['llm', 'stt', 'tts'],
|
|
@@ -24118,7 +24245,7 @@ var renderVoiceProviderSloHTML = (report, options = {}) => {
|
|
|
24118
24245
|
tts: { maxAverageElapsedMs: 1200, maxP95ElapsedMs: 2200 }
|
|
24119
24246
|
}
|
|
24120
24247
|
})`;
|
|
24121
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
24248
|
+
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:#101318;color:#f8f4e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,article,.primitive{background:#171b22;border:1px solid #2c3340;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(14,165,233,.2),rgba(245,158,11,.12))}.eyebrow{color:#7dd3fc;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,4.9rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.status,article h2 span{border:1px solid #475569;border-radius:999px;display:inline-flex;font-size:.85rem;padding:6px 10px}.pass{border-color:rgba(34,197,94,.65)}.warn{border-color:rgba(245,158,11,.7)}.fail{border-color:rgba(239,68,68,.75)}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}dl{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}dt{color:#cbd5e1;font-size:.78rem;text-transform:uppercase}dd{font-size:1.7rem;font-weight:900;margin:0}small{color:#a8b3c2}ul{display:grid;gap:10px;list-style:none;padding:0}li{background:#101318;border:1px solid #2c3340;border-radius:16px;padding:12px}li span{color:#cbd5e1;display:block;margin-top:4px}.primitive{background:#11161d}.primitive code{color:#bae6fd}.primitive pre{background:#070b10;border:1px solid #243041;border-radius:16px;color:#e0f2fe;overflow:auto;padding:16px}</style></head><body><main><section class="hero"><p class="eyebrow">Provider latency and fallback proof</p><h1>${escapeHtml37(title)}</h1><p class="status ${escapeHtml37(report.status)}">${escapeHtml37(report.status)}</p><p>${report.events} provider routing event(s), ${report.eventsWithLatency} latency sample(s).</p></section><section class="grid">${kindCards}</section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceProviderSloRoutes(...)</code> turns provider speed into release evidence</h2><p>Pair this report with production readiness so LLM/STT/TTS latency, timeout, fallback, and unresolved error regressions block deploys.</p><pre><code>${escapeHtml37(snippet)}</code></pre></section><section><h2>Issues</h2>${issues}</section></main></body></html>`;
|
|
24122
24249
|
};
|
|
24123
24250
|
var createVoiceProviderSloRoutes = (options) => {
|
|
24124
24251
|
const path = options.path ?? "/api/voice/provider-slos";
|
|
@@ -24958,7 +25085,7 @@ var createVoiceTelephonyWebhookSecurityPreset = (options = {}) => {
|
|
|
24958
25085
|
|
|
24959
25086
|
// src/opsRecovery.ts
|
|
24960
25087
|
import { Elysia as Elysia40 } from "elysia";
|
|
24961
|
-
var
|
|
25088
|
+
var escapeHtml38 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
24962
25089
|
var getString14 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
24963
25090
|
var hrefForSession = (value, sessionId) => {
|
|
24964
25091
|
if (typeof value === "function") {
|
|
@@ -25172,13 +25299,13 @@ ${failedSessions || "None."}
|
|
|
25172
25299
|
${report.latency ? renderVoiceLatencySLOMarkdown(report.latency, { title: "Latency SLO" }) : "Latency SLO disabled."}
|
|
25173
25300
|
`;
|
|
25174
25301
|
};
|
|
25175
|
-
var renderDeliverySummary = (label, summary) => summary ? `<article><span>${
|
|
25302
|
+
var renderDeliverySummary = (label, summary) => summary ? `<article><span>${escapeHtml38(label)}</span><strong>${String(summary.failed + summary.deadLettered)} failed</strong><small>${String(summary.pending)} pending \xB7 ${String(summary.retryEligible)} retry eligible \xB7 ${String(summary.total)} total</small></article>` : `<article><span>${escapeHtml38(label)}</span><strong>not configured</strong></article>`;
|
|
25176
25303
|
var renderVoiceOpsRecoveryHTML = (report, options = {}) => {
|
|
25177
25304
|
const title = options.title ?? "Voice Ops Recovery";
|
|
25178
|
-
const issues = report.issues.map((issue) => `<tr><td>${
|
|
25179
|
-
const providers = report.providers.providers.map((provider) => `<tr><td>${
|
|
25180
|
-
const failedSessions = report.failedSessions.map((session) => `<li>${session.operationsRecordHref ? `<a href="${
|
|
25181
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
25305
|
+
const issues = report.issues.map((issue) => `<tr><td>${escapeHtml38(issue.severity)}</td><td><code>${escapeHtml38(issue.code)}</code></td><td>${issue.href ? `<a href="${escapeHtml38(issue.href)}">${escapeHtml38(issue.label)}</a>` : escapeHtml38(issue.label)}</td><td>${escapeHtml38(String(issue.value ?? ""))}</td><td>${escapeHtml38(issue.detail ?? "")}</td></tr>`).join("");
|
|
25306
|
+
const providers = report.providers.providers.map((provider) => `<tr><td>${escapeHtml38(provider.provider)}</td><td>${escapeHtml38(provider.status)}</td><td>${String(provider.runCount)}</td><td>${String(provider.errorCount)}</td><td>${String(provider.fallbackCount)}</td><td>${escapeHtml38(provider.lastError ?? "")}</td></tr>`).join("");
|
|
25307
|
+
const failedSessions = report.failedSessions.map((session) => `<li>${session.operationsRecordHref ? `<a href="${escapeHtml38(session.operationsRecordHref)}">${escapeHtml38(session.sessionId)}</a>` : escapeHtml38(session.sessionId)}${session.provider ? ` via ${escapeHtml38(session.provider)}` : ""}${session.error ? `: ${escapeHtml38(session.error)}` : ""}</li>`).join("");
|
|
25308
|
+
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{font-family:ui-sans-serif,system-ui,sans-serif;background:#f8fafc;color:#172033;margin:2rem;line-height:1.45}main{max-width:1180px;margin:auto}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:.75rem;margin:1rem 0}article{background:white;border:1px solid #dbe3ef;border-radius:14px;padding:1rem;box-shadow:0 10px 28px rgba(15,23,42,.05)}article span{display:block;color:#64748b;font-size:.85rem}article strong{display:block;font-size:1.5rem;margin:.2rem 0}article small{color:#64748b}table{border-collapse:collapse;width:100%;background:white;border:1px solid #dbe3ef;border-radius:14px;overflow:hidden}th,td{border-bottom:1px solid #e2e8f0;padding:.7rem;text-align:left;vertical-align:top}code{font-size:.85em}.status{display:inline-flex;border-radius:999px;padding:.35rem .7rem;background:${report.status === "fail" ? "#fee2e2" : report.status === "warn" ? "#fef3c7" : "#dcfce7"};color:${report.status === "fail" ? "#991b1b" : report.status === "warn" ? "#92400e" : "#166534"};font-weight:700}</style></head><body><main><h1>${escapeHtml38(title)}</h1><p><span class="status">${escapeHtml38(report.status)}</span> Checked ${escapeHtml38(new Date(report.checkedAt).toLocaleString())}</p><section class="grid"><article><span>Recovered fallbacks</span><strong>${String(report.providers.recoveredFallbacks)}</strong></article><article><span>Unresolved providers</span><strong>${String(report.providers.unresolvedFailures)}</strong></article><article><span>Operator interventions</span><strong>${String(report.interventions.total)}</strong></article><article><span>Latency status</span><strong>${escapeHtml38(report.latency?.status ?? "disabled")}</strong></article>${renderDeliverySummary("Audit delivery", report.auditDeliveries)}${renderDeliverySummary("Trace delivery", report.traceDeliveries)}${renderDeliverySummary("Handoff delivery", report.handoffDeliveries)}</section><h2>Issues</h2><table><thead><tr><th>Severity</th><th>Code</th><th>Label</th><th>Value</th><th>Detail</th></tr></thead><tbody>${issues || '<tr><td colspan="5">No recovery issues.</td></tr>'}</tbody></table><h2>Providers</h2><table><thead><tr><th>Provider</th><th>Status</th><th>Runs</th><th>Errors</th><th>Fallbacks</th><th>Last error</th></tr></thead><tbody>${providers || '<tr><td colspan="6">No provider activity.</td></tr>'}</tbody></table><h2>Failed Sessions</h2><ul>${failedSessions || "<li>None.</li>"}</ul></main></body></html>`;
|
|
25182
25309
|
};
|
|
25183
25310
|
var createVoiceOpsRecoveryRoutes = (options = {}) => {
|
|
25184
25311
|
const path = options.path ?? "/api/voice/ops-recovery";
|
|
@@ -25225,7 +25352,7 @@ import { Elysia as Elysia42 } from "elysia";
|
|
|
25225
25352
|
|
|
25226
25353
|
// src/traceTimeline.ts
|
|
25227
25354
|
import { Elysia as Elysia41 } from "elysia";
|
|
25228
|
-
var
|
|
25355
|
+
var escapeHtml39 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
25229
25356
|
var getString15 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
25230
25357
|
var getNumber8 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
25231
25358
|
var firstString3 = (payload, keys) => {
|
|
@@ -25407,18 +25534,18 @@ var summarizeVoiceTraceTimeline = (events, options = {}) => {
|
|
|
25407
25534
|
warnings: sessions.filter((session) => session.status === "warning").length
|
|
25408
25535
|
};
|
|
25409
25536
|
};
|
|
25410
|
-
var
|
|
25411
|
-
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>${
|
|
25537
|
+
var formatMs4 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
25538
|
+
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>${formatMs4(provider.averageElapsedMs)}</dd></div><div><dt>Max</dt><dd>${formatMs4(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>`;
|
|
25412
25539
|
var renderVoiceTraceTimelineSessionHTML = (session, options = {}) => {
|
|
25413
|
-
const events = session.events.map((event) => `<tr class="${
|
|
25414
|
-
const issues = session.evaluation.issues.length ? session.evaluation.issues.map((issue) => `<li class="${
|
|
25415
|
-
const supportLinks = session.operationsRecordHref ? `<p><a href="${
|
|
25416
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
25540
|
+
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>${formatMs4(event.elapsedMs)}</td></tr>`).join("");
|
|
25541
|
+
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>";
|
|
25542
|
+
const supportLinks = session.operationsRecordHref ? `<p><a href="${escapeHtml39(session.operationsRecordHref)}">Open operations record</a></p>` : "";
|
|
25543
|
+
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>${supportLinks}</header><section class="metrics"><article><span>Events</span><strong>${String(session.summary.eventCount)}</strong></article><article><span>Turns</span><strong>${String(session.summary.turnCount)}</strong></article><article><span>Errors</span><strong>${String(session.summary.errorCount)}</strong></article><article><span>Duration</span><strong>${formatMs4(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>`;
|
|
25417
25544
|
};
|
|
25418
|
-
var renderSessionRows = (report) => report.sessions.length === 0 ? '<tr><td colspan="7">No trace events recorded yet.</td></tr>' : report.sessions.map((session) => `<tr class="${
|
|
25545
|
+
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>${session.operationsRecordHref ? `<a href="${escapeHtml39(session.operationsRecordHref)}">${escapeHtml39(session.sessionId)}</a>` : `<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>${formatMs4(session.summary.callDurationMs)}</td><td>${session.providers.map((provider) => escapeHtml39(provider.provider)).join(", ")}</td></tr>`).join("");
|
|
25419
25546
|
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}}";
|
|
25420
25547
|
var renderVoiceTraceTimelineHTML = (report, options = {}) => {
|
|
25421
|
-
const snippet =
|
|
25548
|
+
const snippet = escapeHtml39(`const traceStore = createVoiceTraceSinkStore({
|
|
25422
25549
|
store: runtimeStorage.traces,
|
|
25423
25550
|
sinks: [
|
|
25424
25551
|
createVoiceTraceHTTPSink({
|
|
@@ -25444,7 +25571,7 @@ app.use(
|
|
|
25444
25571
|
traceDeliveries: runtimeStorage.traceDeliveries
|
|
25445
25572
|
})
|
|
25446
25573
|
);`);
|
|
25447
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
25574
|
+
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 Timelines")}</title><style>${timelineCSS}.primitive{background:#181f27;border:1px solid #334155;border-radius:20px;margin:20px 0;padding:18px}.primitive p{line-height:1.55}.primitive pre{background:#0b1118;border:1px solid #2b3642;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}.primitive code{color:#bfdbfe}</style></head><body><main><header><p class="eyebrow">Self-hosted voice debugging</p><h1>${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><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceTraceTimelineRoutes(...)</code> makes traces the proof backbone</h2><p class="muted">Mount trace timelines from the same trace store used by readiness, simulations, provider recovery, delivery sinks, and phone-agent smoke proof.</p><pre><code>${snippet}</code></pre></section><table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Turns</th><th>Errors</th><th>Duration</th><th>Providers</th></tr></thead><tbody>${renderSessionRows(report)}</tbody></table></main></body></html>`;
|
|
25448
25575
|
};
|
|
25449
25576
|
var createVoiceTraceTimelineRoutes = (options) => {
|
|
25450
25577
|
const path = options.path ?? "/api/voice-traces";
|
|
@@ -25758,8 +25885,8 @@ var assertVoiceOperationsRecordGuardrails = (record, input = {}) => {
|
|
|
25758
25885
|
}
|
|
25759
25886
|
return report;
|
|
25760
25887
|
};
|
|
25761
|
-
var
|
|
25762
|
-
var
|
|
25888
|
+
var escapeHtml40 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
25889
|
+
var formatMs5 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
25763
25890
|
var outcomeLabels = (outcome) => [
|
|
25764
25891
|
outcome.complete ? "complete" : undefined,
|
|
25765
25892
|
outcome.escalated ? "escalated" : undefined,
|
|
@@ -25775,7 +25902,7 @@ var renderVoiceOperationsRecordIncidentMarkdown = (record) => {
|
|
|
25775
25902
|
`# Voice incident handoff: ${record.sessionId}`,
|
|
25776
25903
|
"",
|
|
25777
25904
|
`- Status: ${record.status}`,
|
|
25778
|
-
`- Duration: ${
|
|
25905
|
+
`- Duration: ${formatMs5(record.summary.callDurationMs)}`,
|
|
25779
25906
|
`- Turns: ${String(record.summary.turnCount)}`,
|
|
25780
25907
|
`- Errors: ${String(record.summary.errorCount)}`,
|
|
25781
25908
|
`- Outcome: ${outcomes.join(", ") || "unknown"}`,
|
|
@@ -25813,19 +25940,19 @@ var renderVoiceOperationsRecordGuardrailMarkdown = (record) => {
|
|
|
25813
25940
|
`);
|
|
25814
25941
|
};
|
|
25815
25942
|
var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
25816
|
-
const providers = record.providers.length ? record.providers.map((provider) => `<article><strong>${
|
|
25817
|
-
const transcript = record.transcript.length ? record.transcript.map((turn) => `<li><strong>${
|
|
25818
|
-
const providerDecisions = record.providerDecisions.length ? record.providerDecisions.map((decision) => `<li><strong>${
|
|
25819
|
-
const handoffs = record.handoffs.length ? record.handoffs.map((handoff) => `<li><strong>${
|
|
25820
|
-
const tools = record.tools.length ? record.tools.map((tool) => `<li><strong>${
|
|
25821
|
-
const reviews = record.reviews?.reviews.length ? record.reviews.reviews.map((review) => `<li><strong>${
|
|
25822
|
-
const tasks = record.tasks?.tasks.length ? record.tasks.tasks.map((task) => `<li><strong>${
|
|
25823
|
-
const integrationEvents = record.integrationEvents?.events.length ? record.integrationEvents.events.map((event) => `<li><strong>${
|
|
25943
|
+
const providers = record.providers.length ? record.providers.map((provider) => `<article><strong>${escapeHtml40(provider.provider)}</strong><span>${String(provider.eventCount)} events</span><span>${formatMs5(provider.averageElapsedMs)} avg</span><span>${String(provider.errorCount)} errors</span></article>`).join("") : '<p class="muted">No provider events recorded.</p>';
|
|
25944
|
+
const transcript = record.transcript.length ? record.transcript.map((turn) => `<li><strong>${escapeHtml40(turn.id)}</strong>${turn.committedText ? `<p><span class="label">Caller</span>${escapeHtml40(turn.committedText)}</p>` : ""}${turn.assistantReplies.map((reply) => `<p><span class="label">Assistant</span>${escapeHtml40(reply)}</p>`).join("")}${turn.errors.map((error) => `<p class="error"><span class="label">Error</span>${escapeHtml40(error)}</p>`).join("")}</li>`).join("") : "<li>No transcript turns recorded.</li>";
|
|
25945
|
+
const providerDecisions = record.providerDecisions.length ? record.providerDecisions.map((decision) => `<li><strong>${escapeHtml40(decision.provider ?? decision.selectedProvider ?? decision.fallbackProvider ?? "provider")}</strong> <span>${escapeHtml40(decision.status ?? decision.type)}</span> ${formatMs5(decision.elapsedMs)}${decision.fallbackProvider ? `<p>Fallback: ${escapeHtml40(decision.fallbackProvider)}</p>` : ""}${decision.error ? `<p class="error">${escapeHtml40(decision.error)}</p>` : ""}${decision.reason ? `<p>${escapeHtml40(decision.reason)}</p>` : ""}</li>`).join("") : "<li>No provider decisions recorded.</li>";
|
|
25946
|
+
const handoffs = record.handoffs.length ? record.handoffs.map((handoff) => `<li><strong>${escapeHtml40(handoff.fromAgentId ?? "unknown")}</strong> to <strong>${escapeHtml40(handoff.targetAgentId ?? "unknown")}</strong> <span>${escapeHtml40(handoff.status ?? "")}</span><p>${escapeHtml40(handoff.summary ?? handoff.reason ?? "")}</p></li>`).join("") : "<li>No agent handoffs recorded.</li>";
|
|
25947
|
+
const tools = record.tools.length ? record.tools.map((tool) => `<li><strong>${escapeHtml40(tool.toolName ?? "tool")}</strong> <span>${escapeHtml40(tool.status ?? "")}</span> ${formatMs5(tool.elapsedMs)} ${tool.error ? `<p>${escapeHtml40(tool.error)}</p>` : ""}</li>`).join("") : "<li>No tool calls recorded.</li>";
|
|
25948
|
+
const reviews = record.reviews?.reviews.length ? record.reviews.reviews.map((review) => `<li><strong>${escapeHtml40(review.title)}</strong> <span>${escapeHtml40(review.summary.outcome ?? "")}</span><p>${escapeHtml40(review.postCall?.summary ?? review.transcript.actual)}</p></li>`).join("") : "<li>No call reviews recorded.</li>";
|
|
25949
|
+
const tasks = record.tasks?.tasks.length ? record.tasks.tasks.map((task) => `<li><strong>${escapeHtml40(task.title)}</strong> <span>${escapeHtml40(task.status)}</span><p>${escapeHtml40(task.recommendedAction)}</p></li>`).join("") : "<li>No ops tasks recorded.</li>";
|
|
25950
|
+
const integrationEvents = record.integrationEvents?.events.length ? record.integrationEvents.events.map((event) => `<li><strong>${escapeHtml40(event.type)}</strong> <span>${escapeHtml40(event.deliveryStatus ?? "local")}</span><p>${escapeHtml40(event.deliveryError ?? event.deliveredTo ?? "")}</p></li>`).join("") : "<li>No integration events recorded.</li>";
|
|
25824
25951
|
const guardrails = record.guardrails.total ? record.guardrails.decisions.map((decision) => {
|
|
25825
25952
|
const findings = decision.findings.map((finding) => finding.label ?? finding.ruleId ?? finding.action).filter((value) => typeof value === "string").join(", ") || "none";
|
|
25826
|
-
return `<li><strong>assistant.guardrail ${
|
|
25953
|
+
return `<li><strong>assistant.guardrail ${escapeHtml40(decision.stage ?? "unknown")}</strong> <span>${escapeHtml40(decision.status ?? "")}</span><p>Allowed: ${escapeHtml40(String(decision.allowed ?? "unknown"))} \xB7 Proof: ${escapeHtml40(decision.proof ?? "runtime")}${decision.turnId ? ` \xB7 Turn: ${escapeHtml40(decision.turnId)}` : ""}</p><p>${escapeHtml40(findings)}</p></li>`;
|
|
25827
25954
|
}).join("") : "<li>No assistant.guardrail events recorded.</li>";
|
|
25828
|
-
const snippet =
|
|
25955
|
+
const snippet = escapeHtml40(`app.use(
|
|
25829
25956
|
createVoiceOperationsRecordRoutes({
|
|
25830
25957
|
audit: auditStore,
|
|
25831
25958
|
integrationEvents: opsEvents,
|
|
@@ -25839,9 +25966,9 @@ var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
|
25839
25966
|
tasks: opsTasks
|
|
25840
25967
|
})
|
|
25841
25968
|
);`);
|
|
25842
|
-
const incidentMarkdown =
|
|
25843
|
-
const incidentLink = options.incidentHref ? `<a href="${
|
|
25844
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
25969
|
+
const incidentMarkdown = escapeHtml40(renderVoiceOperationsRecordIncidentMarkdown(record));
|
|
25970
|
+
const incidentLink = options.incidentHref ? `<a href="${escapeHtml40(options.incidentHref)}">Download incident.md</a>` : "";
|
|
25971
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml40(options.title ?? "Voice Operations Record")}</title><style>body{background:#101417;color:#f9f4e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.eyebrow{color:#fbbf24;font-size:.8rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.8rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #475569;border-radius:999px;display:inline-flex;padding:8px 12px}.healthy{color:#86efac}.warning{color:#fbbf24}.failed,.error{color:#fca5a5}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.card,.primitive{background:#182025;border:1px solid #2d3a43;border-radius:20px;padding:16px}.card span,.muted,.label{color:#a9b4bd}.label{display:block;font-size:.72rem;font-weight:900;letter-spacing:.12em;text-transform:uppercase}.card strong{display:block;font-size:2rem}section{margin-top:28px}article{display:grid;gap:8px}ul{display:grid;gap:10px;list-style:none;padding:0}li{background:#182025;border:1px solid #2d3a43;border-radius:16px;padding:14px}pre{background:#080d10;border:1px solid #2d3a43;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}.hero-actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:16px}.hero-actions a{background:#fbbf24;border-radius:999px;color:#111827;font-weight:900;padding:10px 14px;text-decoration:none}.two-column{display:grid;gap:18px;grid-template-columns:minmax(0,1.15fr) minmax(280px,.85fr)}@media(max-width:860px){main{padding:20px}.two-column{grid-template-columns:1fr}}</style></head><body><main><p class="eyebrow">Call log replacement</p><h1>${escapeHtml40(options.title ?? "Voice Operations Record")}</h1><p class="status ${escapeHtml40(record.status)}">${escapeHtml40(record.status)}</p><div class="hero-actions"><a href="#transcript">Transcript</a><a href="#provider-decisions">Provider decisions</a><a href="#guardrails">Guardrails</a><a href="#incident-handoff">Incident handoff</a>${incidentLink}</div><section class="grid"><div class="card"><span>Events</span><strong>${String(record.summary.eventCount)}</strong></div><div class="card"><span>Turns</span><strong>${String(record.summary.turnCount)}</strong></div><div class="card"><span>Errors</span><strong>${String(record.summary.errorCount)}</strong></div><div class="card"><span>Duration</span><strong>${formatMs5(record.summary.callDurationMs)}</strong></div><div class="card"><span>Guardrails</span><strong>${String(record.guardrails.blocked)}</strong></div><div class="card"><span>Audit</span><strong>${String(record.audit?.total ?? 0)}</strong></div><div class="card"><span>Reviews</span><strong>${String(record.reviews?.total ?? 0)}</strong></div><div class="card"><span>Tasks</span><strong>${String(record.tasks?.total ?? 0)}</strong></div><div class="card"><span>Integrations</span><strong>${String(record.integrationEvents?.total ?? 0)}</strong></div></section><section class="two-column"><div><h2 id="transcript">Transcript</h2><ul>${transcript}</ul></div><div><h2 id="provider-decisions">Provider Decisions</h2><ul>${providerDecisions}</ul></div></section><section id="guardrails"><h2>Guardrail Evidence</h2><p class="muted">Live <code>assistant.guardrail</code> decisions attached to this session.</p><ul>${guardrails}</ul></section><section id="incident-handoff"><h2>Copyable Incident Handoff</h2><p class="muted">Paste this into Slack, Linear, Zendesk, or an incident review. ${incidentLink}</p><pre><code>${incidentMarkdown}</code></pre></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceOperationsRecordRoutes(...)</code> gives every call one debuggable object</h2><p class="muted">Use this as the support/debug payload across traces, provider routing, tools, handoffs, guardrails, audit, latency, replay, reviews, tasks, and webhook delivery.</p><pre><code>${snippet}</code></pre></section><section><h2>Provider Summary</h2><div class="grid">${providers}</div></section><section><h2>Handoffs</h2><ul>${handoffs}</ul></section><section><h2>Tools</h2><ul>${tools}</ul></section><section><h2>Reviews</h2><ul>${reviews}</ul></section><section><h2>Tasks</h2><ul>${tasks}</ul></section><section><h2>Integration Events</h2><ul>${integrationEvents}</ul></section></main></body></html>`;
|
|
25845
25972
|
};
|
|
25846
25973
|
var createVoiceOperationsRecordRoutes = (options) => {
|
|
25847
25974
|
const path = options.path ?? "/api/voice-operations/:sessionId";
|
|
@@ -27385,7 +27512,7 @@ var createVoiceObservabilityExportRoutes = (options = {}) => {
|
|
|
27385
27512
|
};
|
|
27386
27513
|
|
|
27387
27514
|
// src/productionReadiness.ts
|
|
27388
|
-
var
|
|
27515
|
+
var escapeHtml41 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
27389
27516
|
var rollupStatus3 = (checks) => checks.some((check) => check.status === "fail") ? "fail" : checks.some((check) => check.status === "warn") ? "warn" : "pass";
|
|
27390
27517
|
var readinessGateCodes = {
|
|
27391
27518
|
"Agent squad contracts": "voice.readiness.agent_squad_contracts",
|
|
@@ -28728,22 +28855,22 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
28728
28855
|
var buildVoiceProductionReadinessGate = async (options, input = {}) => summarizeVoiceProductionReadinessGate(await buildVoiceProductionReadinessReport(options, input), options.gate || undefined);
|
|
28729
28856
|
var renderVoiceProductionReadinessHTML = (report, options = {}) => {
|
|
28730
28857
|
const title = options.title ?? "AbsoluteJS Voice Production Readiness";
|
|
28731
|
-
const profile = report.profile ? `<section class="profile"><p class="eyebrow">Readiness profile</p><h2>${
|
|
28858
|
+
const profile = report.profile ? `<section class="profile"><p class="eyebrow">Readiness profile</p><h2>${escapeHtml41(report.profile.name)}</h2><p>${escapeHtml41(report.profile.description)}</p><p>${escapeHtml41(report.profile.purpose)}</p><div class="profile-surfaces">${report.profile.surfaces.map((surface) => `<article class="${surface.configured ? "pass" : "warn"}"><span>${surface.configured ? "CONFIGURED" : "EXPECTED"}</span><strong>${surface.href ? `<a href="${escapeHtml41(surface.href)}">${escapeHtml41(surface.label)}</a>` : escapeHtml41(surface.label)}</strong></article>`).join("")}</div></section>` : "";
|
|
28732
28859
|
const checks = report.checks.map((check, index) => {
|
|
28733
|
-
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${
|
|
28734
|
-
return `<article class="check ${
|
|
28860
|
+
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${escapeHtml41(action.href)}">${escapeHtml41(action.label)}</button>` : `<a href="${escapeHtml41(action.href)}">${escapeHtml41(action.label)}</a>`).join("");
|
|
28861
|
+
return `<article class="check ${escapeHtml41(check.status)}">
|
|
28735
28862
|
<div>
|
|
28736
|
-
<span>${
|
|
28737
|
-
<h2>${
|
|
28738
|
-
${check.detail ? `<p>${
|
|
28739
|
-
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${
|
|
28863
|
+
<span>${escapeHtml41(check.status.toUpperCase())}</span>
|
|
28864
|
+
<h2>${escapeHtml41(check.label)}</h2>
|
|
28865
|
+
${check.detail ? `<p>${escapeHtml41(check.detail)}</p>` : ""}
|
|
28866
|
+
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${escapeHtml41(check.proofSource.href)}">${escapeHtml41(check.proofSource.sourceLabel)}</a>` : escapeHtml41(check.proofSource.sourceLabel)}${check.proofSource.detail ? ` \xB7 ${escapeHtml41(check.proofSource.detail)}` : ""}</p>` : ""}
|
|
28740
28867
|
${actions ? `<p class="actions">${actions}</p>` : ""}
|
|
28741
28868
|
</div>
|
|
28742
|
-
<strong>${
|
|
28743
|
-
${check.href ? `<a href="${
|
|
28869
|
+
<strong>${escapeHtml41(String(check.value ?? check.status))}</strong>
|
|
28870
|
+
${check.href ? `<a href="${escapeHtml41(check.href)}">Open surface</a>` : ""}
|
|
28744
28871
|
</article>`;
|
|
28745
28872
|
}).join("");
|
|
28746
|
-
const snippet =
|
|
28873
|
+
const snippet = escapeHtml41(`createVoiceProductionReadinessRoutes({
|
|
28747
28874
|
htmlPath: '/production-readiness',
|
|
28748
28875
|
path: '/api/production-readiness',
|
|
28749
28876
|
gatePath: '/api/production-readiness/gate',
|
|
@@ -28759,7 +28886,7 @@ var renderVoiceProductionReadinessHTML = (report, options = {}) => {
|
|
|
28759
28886
|
providerRoutingContracts: loadProviderRoutingContracts,
|
|
28760
28887
|
store: traceStore
|
|
28761
28888
|
});`);
|
|
28762
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
28889
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml41(title)}</title><style>body{background:#0c0f14;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1060px;padding:32px}.hero,.primitive,.profile{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.primitive,.profile{background:#111722}.primitive{border-color:#3a3f2d}.eyebrow{color:#fbbf24;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.status{display:inline-flex;border:1px solid #3f3f46;border-radius:999px;padding:8px 12px}.primitive code{color:#fde68a}.primitive p{color:#c8ccd3;line-height:1.55;margin:.45rem 0 0}.primitive pre{background:#0b0f16;border:1px solid #2c3440;border-radius:18px;color:#fef3c7;margin:16px 0 0;overflow:auto;padding:16px}.status.pass,.check.pass,.profile-surfaces .pass{border-color:rgba(34,197,94,.55)}.status.warn,.check.warn,.profile-surfaces .warn{border-color:rgba(245,158,11,.65)}.status.fail,.check.fail{border-color:rgba(239,68,68,.75)}.checks{display:grid;gap:14px}.check{align-items:center;background:#141922;border:1px solid #26313d;border-radius:22px;display:grid;gap:16px;grid-template-columns:1fr auto auto;padding:18px}.check span,.profile-surfaces span{color:#a8b0b8;font-size:.78rem;font-weight:900;letter-spacing:.08em}.check h2{margin:.2rem 0}.check p,.profile p{color:#b9c0c8;margin:.2rem 0 0}.check .proof-source{color:#f9d77e;font-weight:800}.check strong{font-size:1.5rem}.profile-surfaces{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));margin-top:16px}.profile-surfaces article{background:#141922;border:1px solid #26313d;border-radius:16px;padding:14px}.profile-surfaces strong{display:block;margin-top:6px}.actions{display:flex;flex-wrap:wrap;gap:10px}.check a,a{color:#fbbf24}button{background:#fbbf24;border:0;border-radius:999px;color:#111827;cursor:pointer;font-weight:800;padding:9px 12px}button:disabled{cursor:wait;opacity:.65}@media(max-width:760px){main{padding:20px}.check{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Self-hosted readiness</p><h1>${escapeHtml41(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 ${escapeHtml41(report.status)}">Overall: ${escapeHtml41(report.status.toUpperCase())}</p><p>Checked ${escapeHtml41(new Date(report.checkedAt).toLocaleString())}</p></section>${profile}<section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceProductionReadinessRoutes(...)</code> builds this deploy gate</h2><p>Mount one package primitive to expose JSON readiness, HTML readiness, and a machine-readable gate route. Feed it the proof stores and contract reports your app already owns.</p><pre><code>${snippet}</code></pre></section><section class="checks">${checks}</section></main><script>document.querySelectorAll("[data-readiness-action]").forEach((button)=>{button.addEventListener("click",async()=>{const url=button.getAttribute("data-action-url");if(!url)return;button.disabled=true;const original=button.textContent;button.textContent="Running...";try{const response=await fetch(url,{method:"POST"});button.textContent=response.ok?"Done. Reloading...":"Failed";if(response.ok)setTimeout(()=>location.reload(),500)}catch{button.textContent="Failed"}finally{setTimeout(()=>{button.disabled=false;button.textContent=original},1500)}})});</script></body></html>`;
|
|
28763
28890
|
};
|
|
28764
28891
|
var createVoiceProductionReadinessRoutes = (options) => {
|
|
28765
28892
|
const path = options.path ?? "/api/production-readiness";
|
|
@@ -28814,7 +28941,7 @@ var createVoiceProductionReadinessRoutes = (options) => {
|
|
|
28814
28941
|
};
|
|
28815
28942
|
// src/voiceMonitoring.ts
|
|
28816
28943
|
import { Elysia as Elysia45 } from "elysia";
|
|
28817
|
-
var
|
|
28944
|
+
var escapeHtml42 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
28818
28945
|
var issueIdForRun = (run) => `voice-monitor:${run.id}:${run.impactedSessions?.[0] ?? "global"}`;
|
|
28819
28946
|
var rollupStatus4 = (runs) => runs.some((run) => run.status === "fail") ? "fail" : runs.some((run) => run.status === "warn") ? "warn" : "pass";
|
|
28820
28947
|
var createVoiceMemoryMonitorIssueStore = (initial = []) => {
|
|
@@ -29067,14 +29194,14 @@ ${rows || "| none | pass | info | | | No monitors configured. |"}
|
|
|
29067
29194
|
};
|
|
29068
29195
|
var renderVoiceMonitorHTML = (report, options = {}) => {
|
|
29069
29196
|
const title = options.title ?? "Voice Monitors";
|
|
29070
|
-
const runs = report.runs.map((run) => `<tr><td>${
|
|
29071
|
-
const issues = report.issues.map((issue) => `<li><strong>${
|
|
29072
|
-
const snippet =
|
|
29197
|
+
const runs = report.runs.map((run) => `<tr><td>${escapeHtml42(run.label)}</td><td class="${escapeHtml42(run.status)}">${escapeHtml42(run.status)}</td><td>${escapeHtml42(run.severity)}</td><td>${escapeHtml42(String(run.value ?? ""))}</td><td>${escapeHtml42(String(run.threshold ?? ""))}</td><td>${escapeHtml42(run.detail ?? "")}</td></tr>`).join("");
|
|
29198
|
+
const issues = report.issues.map((issue) => `<li><strong>${escapeHtml42(issue.label)}</strong> <span class="${escapeHtml42(issue.status)}">${escapeHtml42(issue.status)}</span> ${escapeHtml42(issue.detail ?? "")}</li>`).join("");
|
|
29199
|
+
const snippet = escapeHtml42(`app.use(createVoiceMonitorRoutes({
|
|
29073
29200
|
evidence,
|
|
29074
29201
|
issueStore,
|
|
29075
29202
|
monitors: [defineVoiceMonitor(...)]
|
|
29076
29203
|
}));`);
|
|
29077
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
29204
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml42(title)}</title><style>body{background:#10141b;color:#f8f2df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1100px;padding:32px}.hero,.card{background:#171f2b;border:1px solid #2e3a4b;border-radius:24px;margin-bottom:16px;padding:22px}.eyebrow{color:#93c5fd;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.7rem);line-height:.92;margin:.2rem 0 1rem}.pill{border:1px solid #64748b;border-radius:999px;display:inline-flex;font-weight:900;margin-right:8px;padding:8px 12px}.pass{color:#86efac}.warn,.acknowledged{color:#fde68a}.fail,.open{color:#fca5a5}.resolved,.muted{color:#cbd5e1}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid #2e3a4b;padding:12px;text-align:left;vertical-align:top}pre{background:#0c1118;border:1px solid #2e3a4b;border-radius:16px;color:#dbeafe;overflow:auto;padding:16px}</style></head><body><main><section class="hero"><p class="eyebrow">Code-owned monitoring</p><h1>${escapeHtml42(title)}</h1><p class="pill ${escapeHtml42(report.status)}">Status: ${escapeHtml42(report.status)}</p><p class="pill">Open issues: ${String(report.summary.open)}</p><p class="pill">Critical: ${String(report.summary.criticalOpen)}</p></section><section class="card"><h2>Monitor Runs</h2><table><thead><tr><th>Monitor</th><th>Status</th><th>Severity</th><th>Value</th><th>Threshold</th><th>Detail</th></tr></thead><tbody>${runs}</tbody></table></section><section class="card"><h2>Issues</h2>${issues ? `<ul>${issues}</ul>` : '<p class="pass">No monitor issues.</p>'}</section><section class="card"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceMonitorRoutes(...)</code></h2><pre><code>${snippet}</code></pre></section></main></body></html>`;
|
|
29078
29205
|
};
|
|
29079
29206
|
var actorFromRequest = async (request) => {
|
|
29080
29207
|
if (!request.headers.get("content-type")?.includes("application/json")) {
|
|
@@ -29522,7 +29649,7 @@ var recommendVoiceReadinessProfile = (options) => {
|
|
|
29522
29649
|
};
|
|
29523
29650
|
// src/providerStackRecommendations.ts
|
|
29524
29651
|
import { Elysia as Elysia46 } from "elysia";
|
|
29525
|
-
var
|
|
29652
|
+
var escapeHtml43 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
29526
29653
|
var profileProviderPriorities = {
|
|
29527
29654
|
"meeting-recorder": {
|
|
29528
29655
|
llm: ["openai", "anthropic", "gemini"],
|
|
@@ -29841,17 +29968,17 @@ var resolveProviderContractMatrixInput = async (matrix) => typeof matrix === "fu
|
|
|
29841
29968
|
var renderVoiceProviderContractMatrixHTML = (report, options = {}) => {
|
|
29842
29969
|
const title = options.title ?? "Voice Provider Contract Matrix";
|
|
29843
29970
|
const rows = report.rows.map((row) => {
|
|
29844
|
-
const checks = row.checks.map((check) => `<li class="${
|
|
29845
|
-
return `<article class="row ${
|
|
29971
|
+
const checks = row.checks.map((check) => `<li class="${escapeHtml43(check.status)}"><strong>${escapeHtml43(check.label)}</strong><span>${escapeHtml43(check.detail ?? check.status)}</span>${check.remediation ? `<em>${check.remediation.href ? `<a href="${escapeHtml43(check.remediation.href)}">${escapeHtml43(check.remediation.label)}</a>` : escapeHtml43(check.remediation.label)}: ${escapeHtml43(check.remediation.detail)}</em>` : ""}</li>`).join("");
|
|
29972
|
+
return `<article class="row ${escapeHtml43(row.status)}">
|
|
29846
29973
|
<div>
|
|
29847
|
-
<p class="eyebrow">${
|
|
29848
|
-
<h2>${
|
|
29849
|
-
<p class="status ${
|
|
29974
|
+
<p class="eyebrow">${escapeHtml43(row.kind)}${row.selected ? " \xB7 selected" : ""}</p>
|
|
29975
|
+
<h2>${escapeHtml43(row.provider)}</h2>
|
|
29976
|
+
<p class="status ${escapeHtml43(row.status)}">${escapeHtml43(row.status.toUpperCase())}</p>
|
|
29850
29977
|
</div>
|
|
29851
29978
|
<ul>${checks}</ul>
|
|
29852
29979
|
</article>`;
|
|
29853
29980
|
}).join("");
|
|
29854
|
-
const snippet =
|
|
29981
|
+
const snippet = escapeHtml43(`const providerContracts = () =>
|
|
29855
29982
|
createVoiceProviderContractMatrixPreset('phone-agent', {
|
|
29856
29983
|
env: process.env,
|
|
29857
29984
|
providers: {
|
|
@@ -29872,7 +29999,7 @@ createVoiceProductionReadinessRoutes({
|
|
|
29872
29999
|
providerContractMatrix: () =>
|
|
29873
30000
|
buildVoiceProviderContractMatrix(providerContracts())
|
|
29874
30001
|
});`);
|
|
29875
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
30002
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml43(title)}</title><style>body{background:#0f1412;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.primitive,.row{background:#17201b;border:1px solid #2d3b32;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.16),rgba(125,211,252,.12))}.primitive{background:#111814;border-color:#41604a}.eyebrow{color:#86efac;font-size:.78rem;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill,.status{border:1px solid #3f4f45;border-radius:999px;display:inline-flex;padding:8px 12px}.primitive code{color:#bbf7d0}.primitive p{color:#c8d8ca;line-height:1.55;margin:.45rem 0 0}.primitive pre{background:#08110d;border:1px solid #294132;border-radius:18px;color:#d9f99d;margin:16px 0 0;overflow:auto;padding:16px}.status.pass,.row.pass,.pass{border-color:rgba(34,197,94,.65)}.status.warn,.row.warn,.warn{border-color:rgba(245,158,11,.7)}.status.fail,.row.fail,.fail{border-color:rgba(239,68,68,.75)}.row{display:grid;gap:20px;grid-template-columns:minmax(180px,.45fr) 1fr}.row ul{display:grid;gap:10px;list-style:none;margin:0;padding:0}.row li{background:#111814;border:1px solid #2d3b32;border-radius:16px;display:grid;gap:4px;padding:12px}.row li span{color:#b8c2ba}.row li em{color:#f9d77e;font-style:normal}.row li a{color:#86efac}@media(max-width:760px){main{padding:18px}.row{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Provider contracts</p><h1>${escapeHtml43(title)}</h1><p>Self-hosted provider proof for configured state, required env, latency budgets, fallback, streaming, and declared capabilities.</p><div class="summary"><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.warned)} warning</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} total</span></div></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceProviderContractMatrixPreset(...)</code> builds this matrix</h2><p>Give AbsoluteJS your configured LLM, STT, and TTS providers once. It turns them into deploy-checkable proof for env, fallback, streaming, latency budgets, selected providers, and profile-required capabilities without a hosted dashboard.</p><pre><code>${snippet}</code></pre></section>${rows || '<article class="row"><p>No provider contracts configured.</p></article>'}</main></body></html>`;
|
|
29876
30003
|
};
|
|
29877
30004
|
var createVoiceProviderContractMatrixJSONHandler = (matrix) => async () => buildVoiceProviderContractMatrix(await resolveProviderContractMatrixInput(matrix));
|
|
29878
30005
|
var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
|
|
@@ -30040,7 +30167,7 @@ var DEFAULT_LINKS = [
|
|
|
30040
30167
|
label: "Handoffs"
|
|
30041
30168
|
}
|
|
30042
30169
|
];
|
|
30043
|
-
var
|
|
30170
|
+
var escapeHtml44 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
30044
30171
|
var countProviderStatuses = (providers) => {
|
|
30045
30172
|
const degradedStatuses = new Set(["degraded", "rate-limited", "suppressed"]);
|
|
30046
30173
|
const healthy = providers.filter((provider) => provider.status === "healthy").length;
|
|
@@ -30109,16 +30236,16 @@ var buildVoiceOpsConsoleReport = async (options) => {
|
|
|
30109
30236
|
trace
|
|
30110
30237
|
};
|
|
30111
30238
|
};
|
|
30112
|
-
var renderMetricCard = (input) => `<article class="metric"><span>${
|
|
30239
|
+
var renderMetricCard = (input) => `<article class="metric"><span>${escapeHtml44(input.label)}</span><strong>${escapeHtml44(String(input.value))}</strong>${input.status ? `<p class="${escapeHtml44(input.status)}">${escapeHtml44(input.status)}</p>` : ""}${input.href ? `<a href="${escapeHtml44(input.href)}">Open</a>` : ""}</article>`;
|
|
30113
30240
|
var renderVoiceOpsConsoleHTML = (report, options = {}) => {
|
|
30114
30241
|
const links = report.links.map((link) => `<article class="surface">
|
|
30115
|
-
<div><h2>${
|
|
30116
|
-
<p><a href="${
|
|
30242
|
+
<div><h2>${escapeHtml44(link.label)}</h2>${link.description ? `<p>${escapeHtml44(link.description)}</p>` : ""}</div>
|
|
30243
|
+
<p><a href="${escapeHtml44(link.href)}">Open ${escapeHtml44(link.label)}</a>${link.statusHref ? ` \xB7 <a href="${escapeHtml44(link.statusHref)}">Status</a>` : ""}</p>
|
|
30117
30244
|
</article>`).join("");
|
|
30118
|
-
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${
|
|
30119
|
-
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${
|
|
30245
|
+
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${escapeHtml44(session.sessionId)}</td><td>${escapeHtml44(session.status)}</td><td>${session.turnCount}</td><td>${session.errorCount}</td><td>${session.replayHref ? `<a href="${escapeHtml44(session.replayHref)}">Replay</a>` : ""}</td></tr>`).join("") : '<tr><td colspan="5">No sessions yet.</td></tr>';
|
|
30246
|
+
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${escapeHtml44(event.kind)}</td><td>${escapeHtml44(event.provider ?? "unknown")}</td><td>${escapeHtml44(event.status ?? "unknown")}</td><td>${event.elapsedMs ?? 0}ms</td><td>${escapeHtml44(event.sessionId)}</td></tr>`).join("") : '<tr><td colspan="5">No provider routing events yet.</td></tr>';
|
|
30120
30247
|
const title = options.title ?? "AbsoluteJS Voice Ops Console";
|
|
30121
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
30248
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml44(title)}</title><style>body{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>${escapeHtml44(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 ${escapeHtml44(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>`;
|
|
30122
30249
|
};
|
|
30123
30250
|
var createVoiceOpsConsoleRoutes = (options) => {
|
|
30124
30251
|
const path = options.path ?? "/ops-console";
|
|
@@ -30542,14 +30669,14 @@ var summarizeVoiceOpsStatus = async (options) => {
|
|
|
30542
30669
|
};
|
|
30543
30670
|
// src/opsStatusRoutes.ts
|
|
30544
30671
|
import { Elysia as Elysia49 } from "elysia";
|
|
30545
|
-
var
|
|
30672
|
+
var escapeHtml45 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
30546
30673
|
var renderVoiceOpsStatusHTML = (report, options = {}) => {
|
|
30547
30674
|
const title = options.title ?? "AbsoluteJS Voice Ops Status";
|
|
30548
30675
|
const surfaces = Object.entries(report.surfaces).map(([key, surface]) => {
|
|
30549
30676
|
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;
|
|
30550
|
-
return `<article class="surface ${
|
|
30677
|
+
return `<article class="surface ${escapeHtml45(surface.status)}"><span>${escapeHtml45(surface.status.toUpperCase())}</span><h2>${escapeHtml45(key)}</h2><strong>${escapeHtml45(value)}</strong></article>`;
|
|
30551
30678
|
}).join("");
|
|
30552
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
30679
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml45(title)}</title><style>body{background:#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>${escapeHtml45(title)}</h1><p>Compact pass/fail status for framework widgets, demos, and small customer-facing health badges.</p><p class="status ${escapeHtml45(report.status)}">Overall: ${escapeHtml45(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>`;
|
|
30553
30680
|
};
|
|
30554
30681
|
var createVoiceOpsStatusRoutes = (options) => {
|
|
30555
30682
|
const path = options.path ?? "/api/voice/ops-status";
|
|
@@ -30987,7 +31114,7 @@ var createVoiceTTSProviderRouter = (options) => {
|
|
|
30987
31114
|
};
|
|
30988
31115
|
// src/traceDeliveryRoutes.ts
|
|
30989
31116
|
import { Elysia as Elysia50 } from "elysia";
|
|
30990
|
-
var
|
|
31117
|
+
var escapeHtml46 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
30991
31118
|
var getString19 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
30992
31119
|
var getNumber11 = (value) => {
|
|
30993
31120
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -31068,14 +31195,14 @@ var renderSinkResults2 = (delivery) => {
|
|
|
31068
31195
|
if (entries.length === 0) {
|
|
31069
31196
|
return "<p>No sink delivery attempts recorded yet.</p>";
|
|
31070
31197
|
}
|
|
31071
|
-
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${
|
|
31198
|
+
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${escapeHtml46(sinkId)}</strong>: ${escapeHtml46(result.status)}${result.deliveredTo ? ` to ${escapeHtml46(result.deliveredTo)}` : ""}${result.error ? ` (${escapeHtml46(result.error)})` : ""}</li>`).join("")}</ul>`;
|
|
31072
31199
|
};
|
|
31073
|
-
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${
|
|
31200
|
+
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${escapeHtml46(event.type)} <small>${escapeHtml46(event.id)}</small>${event.sessionId ? ` session=${escapeHtml46(event.sessionId)}` : ""}</li>`).join("")}</ul>`;
|
|
31074
31201
|
var renderVoiceTraceDeliveryHTML = (report, options = {}) => {
|
|
31075
31202
|
const title = options.title ?? "AbsoluteJS Voice Trace Deliveries";
|
|
31076
|
-
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${
|
|
31077
|
-
const rows = report.deliveries.map((delivery) => `<article class="delivery ${
|
|
31078
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
31203
|
+
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${escapeHtml46(options.workerPath ?? "/api/voice-trace-deliveries/drain")}"><button type="submit">Drain trace deliveries</button></form>`;
|
|
31204
|
+
const rows = report.deliveries.map((delivery) => `<article class="delivery ${escapeHtml46(delivery.deliveryStatus)}"><div class="head"><div><span>${escapeHtml46(delivery.deliveryStatus)}</span><h2>${escapeHtml46(delivery.id)}</h2><p>${escapeHtml46(new Date(delivery.createdAt).toLocaleString())}${delivery.deliveredAt ? ` \xB7 delivered ${escapeHtml46(new Date(delivery.deliveredAt).toLocaleString())}` : ""}</p></div><strong>${String(delivery.deliveryAttempts ?? 0)} attempt(s)</strong></div>${delivery.deliveryError ? `<p class="error">${escapeHtml46(delivery.deliveryError)}</p>` : ""}<h3>Sinks</h3>${renderSinkResults2(delivery)}<h3>Events</h3>${renderEventList2(delivery)}</article>`).join("");
|
|
31205
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml46(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>${escapeHtml46(title)}</h1><p>Checked ${escapeHtml46(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>`;
|
|
31079
31206
|
};
|
|
31080
31207
|
var createVoiceTraceDeliveryJSONHandler = (options) => async ({ query }) => buildVoiceTraceDeliveryReport(options, resolveVoiceTraceDeliveryFilter(query, options.filter));
|
|
31081
31208
|
var createVoiceTraceDeliveryHTMLHandler = (options) => async ({ query }) => {
|
|
@@ -32798,6 +32925,8 @@ export {
|
|
|
32798
32925
|
renderVoiceTraceDeliveryHTML,
|
|
32799
32926
|
renderVoiceToolContractHTML,
|
|
32800
32927
|
renderVoiceTelephonyCarrierMatrixHTML,
|
|
32928
|
+
renderVoiceSloReadinessThresholdMarkdown,
|
|
32929
|
+
renderVoiceSloReadinessThresholdHTML,
|
|
32801
32930
|
renderVoiceSloCalibrationMarkdown,
|
|
32802
32931
|
renderVoiceSimulationSuiteHTML,
|
|
32803
32932
|
renderVoiceSessionsHTML,
|
|
@@ -32980,6 +33109,7 @@ export {
|
|
|
32980
33109
|
createVoiceTaskCreatedEvent,
|
|
32981
33110
|
createVoiceTTSProviderRouter,
|
|
32982
33111
|
createVoiceSloThresholdProfile,
|
|
33112
|
+
createVoiceSloReadinessThresholdRoutes,
|
|
32983
33113
|
createVoiceSloReadinessThresholdOptions,
|
|
32984
33114
|
createVoiceSloCalibrationRoutes,
|
|
32985
33115
|
createVoiceSimulationSuiteRoutes,
|
|
@@ -33215,6 +33345,7 @@ export {
|
|
|
33215
33345
|
buildVoiceTraceReplay,
|
|
33216
33346
|
buildVoiceTraceDeliveryReport,
|
|
33217
33347
|
buildVoiceTelephonyWebhookSecurityReport,
|
|
33348
|
+
buildVoiceSloReadinessThresholdReport,
|
|
33218
33349
|
buildVoiceSloCalibrationReport,
|
|
33219
33350
|
buildVoiceProviderSloReport,
|
|
33220
33351
|
buildVoiceProviderContractMatrix,
|