@absolutejs/voice 0.0.22-beta.154 → 0.0.22-beta.156
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/angular/index.js +29 -0
- package/dist/client/index.d.ts +5 -1
- package/dist/client/index.js +225 -86
- package/dist/client/opsActionCenter.d.ts +4 -0
- package/dist/client/opsActionHistory.d.ts +19 -0
- package/dist/client/opsActionHistoryWidget.d.ts +11 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +389 -254
- package/dist/opsActionAuditRoutes.d.ts +99 -0
- package/dist/react/index.js +29 -0
- package/dist/svelte/index.js +29 -0
- package/dist/trace.d.ts +1 -1
- package/dist/vue/index.js +29 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11564,16 +11564,147 @@ var createVoiceDeliverySinkRoutes = (options) => {
|
|
|
11564
11564
|
}
|
|
11565
11565
|
return routes;
|
|
11566
11566
|
};
|
|
11567
|
-
// src/
|
|
11567
|
+
// src/opsActionAuditRoutes.ts
|
|
11568
11568
|
import { Elysia as Elysia12 } from "elysia";
|
|
11569
|
+
var readRecord = async (request) => {
|
|
11570
|
+
const body = await request.json().catch(() => null);
|
|
11571
|
+
if (!body || typeof body !== "object") {
|
|
11572
|
+
throw new Error("Voice ops action audit requires a JSON body.");
|
|
11573
|
+
}
|
|
11574
|
+
const record = body;
|
|
11575
|
+
if (!record.actionId || typeof record.actionId !== "string") {
|
|
11576
|
+
throw new Error("Voice ops action audit requires actionId.");
|
|
11577
|
+
}
|
|
11578
|
+
return {
|
|
11579
|
+
actionId: record.actionId,
|
|
11580
|
+
body: record.body,
|
|
11581
|
+
error: record.error,
|
|
11582
|
+
ok: Boolean(record.ok),
|
|
11583
|
+
ranAt: typeof record.ranAt === "number" ? record.ranAt : Date.now(),
|
|
11584
|
+
status: typeof record.status === "number" ? record.status : 0
|
|
11585
|
+
};
|
|
11586
|
+
};
|
|
11587
|
+
var recordVoiceOpsActionAudit = async (record, options) => {
|
|
11588
|
+
const traceId = `voice-ops-action:${record.actionId}:${record.ranAt}`;
|
|
11589
|
+
const outcome = record.ok ? "success" : "error";
|
|
11590
|
+
const [audit, trace] = await Promise.all([
|
|
11591
|
+
options.audit?.append(createVoiceAuditEvent({
|
|
11592
|
+
action: record.actionId,
|
|
11593
|
+
actor: {
|
|
11594
|
+
id: "voice-ops-action-center",
|
|
11595
|
+
kind: "operator",
|
|
11596
|
+
name: "Voice Ops Action Center"
|
|
11597
|
+
},
|
|
11598
|
+
at: record.ranAt,
|
|
11599
|
+
metadata: {
|
|
11600
|
+
source: "voice-ops-action-center"
|
|
11601
|
+
},
|
|
11602
|
+
outcome,
|
|
11603
|
+
payload: {
|
|
11604
|
+
body: record.body,
|
|
11605
|
+
error: record.error,
|
|
11606
|
+
status: record.status
|
|
11607
|
+
},
|
|
11608
|
+
resource: {
|
|
11609
|
+
id: record.actionId,
|
|
11610
|
+
type: "voice.ops.action"
|
|
11611
|
+
},
|
|
11612
|
+
sessionId: "voice-ops-action-center",
|
|
11613
|
+
traceId,
|
|
11614
|
+
type: "operator.action"
|
|
11615
|
+
})),
|
|
11616
|
+
options.trace?.append(createVoiceTraceEvent({
|
|
11617
|
+
at: record.ranAt,
|
|
11618
|
+
metadata: {
|
|
11619
|
+
source: "voice-ops-action-center"
|
|
11620
|
+
},
|
|
11621
|
+
payload: {
|
|
11622
|
+
actionId: record.actionId,
|
|
11623
|
+
body: record.body,
|
|
11624
|
+
error: record.error,
|
|
11625
|
+
ok: record.ok,
|
|
11626
|
+
status: record.status
|
|
11627
|
+
},
|
|
11628
|
+
sessionId: "voice-ops-action-center",
|
|
11629
|
+
traceId,
|
|
11630
|
+
type: "operator.action"
|
|
11631
|
+
}))
|
|
11632
|
+
]);
|
|
11633
|
+
return {
|
|
11634
|
+
audit,
|
|
11635
|
+
ok: true,
|
|
11636
|
+
trace
|
|
11637
|
+
};
|
|
11638
|
+
};
|
|
11639
|
+
var createVoiceOpsActionAuditRoutes = (options) => {
|
|
11640
|
+
const path = options.path ?? "/api/voice/ops-actions/audit";
|
|
11641
|
+
const historyPath = options.historyPath === undefined ? "/api/voice/ops-actions/history" : options.historyPath;
|
|
11642
|
+
const historyHtmlPath = options.historyHtmlPath === undefined ? "/voice/ops-actions" : options.historyHtmlPath;
|
|
11643
|
+
const routes = new Elysia12({
|
|
11644
|
+
name: options.name ?? "absolutejs-voice-ops-action-audit"
|
|
11645
|
+
}).post(path, async ({ request, set }) => {
|
|
11646
|
+
try {
|
|
11647
|
+
const record = await readRecord(request);
|
|
11648
|
+
return await recordVoiceOpsActionAudit(record, options);
|
|
11649
|
+
} catch (error) {
|
|
11650
|
+
set.status = 400;
|
|
11651
|
+
return {
|
|
11652
|
+
error: error instanceof Error ? error.message : String(error),
|
|
11653
|
+
ok: false
|
|
11654
|
+
};
|
|
11655
|
+
}
|
|
11656
|
+
});
|
|
11657
|
+
if (historyPath !== false) {
|
|
11658
|
+
routes.get(historyPath, async () => buildVoiceOpsActionHistoryReport(options));
|
|
11659
|
+
}
|
|
11660
|
+
if (historyHtmlPath !== false) {
|
|
11661
|
+
routes.get(historyHtmlPath, async () => new Response(renderVoiceOpsActionHistoryHTML(await buildVoiceOpsActionHistoryReport(options)), {
|
|
11662
|
+
headers: {
|
|
11663
|
+
"Content-Type": "text/html; charset=utf-8"
|
|
11664
|
+
}
|
|
11665
|
+
}));
|
|
11666
|
+
}
|
|
11667
|
+
return routes;
|
|
11668
|
+
};
|
|
11669
|
+
var toHistoryEntry = (event) => ({
|
|
11670
|
+
actionId: event.action,
|
|
11671
|
+
at: event.at,
|
|
11672
|
+
error: event.payload && typeof event.payload === "object" && "error" in event.payload && typeof event.payload.error === "string" ? event.payload.error : undefined,
|
|
11673
|
+
eventId: event.id,
|
|
11674
|
+
ok: event.outcome !== "error",
|
|
11675
|
+
status: event.payload && typeof event.payload === "object" && "status" in event.payload && typeof event.payload.status === "number" ? event.payload.status : undefined,
|
|
11676
|
+
traceId: event.traceId
|
|
11677
|
+
});
|
|
11678
|
+
var buildVoiceOpsActionHistoryReport = async (options) => {
|
|
11679
|
+
const events = options.audit ? await options.audit.list({
|
|
11680
|
+
limit: 25,
|
|
11681
|
+
resourceType: "voice.ops.action",
|
|
11682
|
+
type: "operator.action"
|
|
11683
|
+
}) : [];
|
|
11684
|
+
const entries = events.map(toHistoryEntry).sort((left, right) => right.at - left.at);
|
|
11685
|
+
return {
|
|
11686
|
+
checkedAt: Date.now(),
|
|
11687
|
+
entries,
|
|
11688
|
+
failed: entries.filter((entry) => !entry.ok).length,
|
|
11689
|
+
passed: entries.filter((entry) => entry.ok).length,
|
|
11690
|
+
total: entries.length
|
|
11691
|
+
};
|
|
11692
|
+
};
|
|
11693
|
+
var escapeHtml15 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
11694
|
+
var renderVoiceOpsActionHistoryHTML = (report) => {
|
|
11695
|
+
const rows = report.entries.map((entry) => `<article class="${entry.ok ? "ok" : "fail"}"><span>${escapeHtml15(entry.ok ? "success" : "error")}</span><strong>${escapeHtml15(entry.actionId)}</strong><p>${escapeHtml15(new Date(entry.at).toLocaleString())}${entry.status ? ` \xB7 HTTP ${String(entry.status)}` : ""}</p>${entry.error ? `<p>${escapeHtml15(entry.error)}</p>` : ""}</article>`).join("");
|
|
11696
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>Voice Ops Action History</title><style>body{background:#11140f;color:#f7f1df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero,article{background:#181d15;border:1px solid #2c3327;border-radius:24px;padding:20px}.hero{margin-bottom:16px}h1{font-size:clamp(2rem,6vw,4rem);line-height:.95}section{display:grid;gap:12px}article.ok{border-color:rgba(34,197,94,.55)}article.fail{border-color:rgba(239,68,68,.75)}span{color:#facc15;font-weight:900;text-transform:uppercase}p{color:#c8ccb8}</style></head><body><main><section class="hero"><span>Operator proof</span><h1>Voice Ops Action History</h1><p>${String(report.total)} action(s), ${String(report.failed)} failed.</p></section><section>${rows || "<p>No operator actions have been recorded.</p>"}</section></main></body></html>`;
|
|
11697
|
+
};
|
|
11698
|
+
// src/deliveryRuntime.ts
|
|
11699
|
+
import { Elysia as Elysia13 } from "elysia";
|
|
11569
11700
|
import { mkdir } from "fs/promises";
|
|
11570
11701
|
import { dirname, join } from "path";
|
|
11571
|
-
var
|
|
11702
|
+
var escapeHtml16 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
11572
11703
|
var renderSummaryCard = (label, summary) => {
|
|
11573
11704
|
if (!summary) {
|
|
11574
|
-
return `<article><span>${
|
|
11705
|
+
return `<article><span>${escapeHtml16(label)}</span><strong>Disabled</strong><p class="muted">No worker configured.</p></article>`;
|
|
11575
11706
|
}
|
|
11576
|
-
return `<article><span>${
|
|
11707
|
+
return `<article><span>${escapeHtml16(label)}</span><strong>${String(summary.delivered)}/${String(summary.total)}</strong><p class="muted">${String(summary.pending)} pending · ${String(summary.failed)} failed · ${String(summary.deadLettered)} dead-lettered</p></article>`;
|
|
11577
11708
|
};
|
|
11578
11709
|
var resolvePresetLeases = (leases) => ("claim" in leases) ? {
|
|
11579
11710
|
audit: leases,
|
|
@@ -11784,9 +11915,9 @@ var buildVoiceDeliveryRuntimeReport = async (runtime) => ({
|
|
|
11784
11915
|
});
|
|
11785
11916
|
var renderVoiceDeliveryRuntimeHTML = (report, options = {}) => {
|
|
11786
11917
|
const title = options.title ?? "AbsoluteJS Voice Delivery Runtime";
|
|
11787
|
-
const tickForm = options.tickPath === false ? "" : `<form method="post" action="${
|
|
11788
|
-
const requeueForm = options.requeueDeadLettersPath === false ? "" : `<form method="post" action="${
|
|
11789
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
11918
|
+
const tickForm = options.tickPath === false ? "" : `<form method="post" action="${escapeHtml16(options.tickPath ?? "/api/voice-delivery-runtime/tick")}"><button type="submit">Tick delivery workers</button></form>`;
|
|
11919
|
+
const requeueForm = options.requeueDeadLettersPath === false ? "" : `<form method="post" action="${escapeHtml16(options.requeueDeadLettersPath ?? "/api/voice-delivery-runtime/requeue-dead-letters")}"><button type="submit">Requeue dead letters</button></form>`;
|
|
11920
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml16(title)}</title><style>body{background:#0f1411;color:#f7f2df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1080px;padding:32px}a{color:#86efac;text-decoration:none}.hero{background:linear-gradient(135deg,rgba(34,197,94,.18),rgba(14,165,233,.13));border:1px solid #263a30;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#86efac;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,5vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.status{border:1px solid #64748b;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.status.running{border-color:rgba(34,197,94,.7);color:#bbf7d0}.muted{color:#b9c3b4}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));margin:18px 0}article,.card{background:#151d18;border:1px solid #263a30;border-radius:22px;padding:18px}article span{color:#b9c3b4;display:block;font-weight:800}article strong{display:block;font-size:2.3rem;margin-top:8px}.actions{display:flex;flex-wrap:wrap;gap:10px}button{background:#86efac;border:0;border-radius:999px;color:#07120b;cursor:pointer;font-weight:900;margin-top:12px;padding:10px 14px}pre{background:#09100c;border:1px solid #263a30;border-radius:18px;color:#dcfce7;overflow:auto;padding:16px}</style></head><body><main><p><a href="/delivery-sinks">Delivery sinks</a></p><section class="hero"><p class="eyebrow">Worker control plane</p><h1>${escapeHtml16(title)}</h1><p class="muted">Inspect queue summaries, manually tick failed/pending audit and trace deliveries, and requeue dead letters after operator review.</p><p class="status ${report.isRunning ? "running" : ""}">${report.isRunning ? "Running" : "Stopped"}</p><p class="muted">Checked ${escapeHtml16(new Date(report.checkedAt).toLocaleString())}</p><div class="actions">${tickForm}${requeueForm}</div></section><section class="grid">${renderSummaryCard("Audit", report.summary.audit)}${renderSummaryCard("Trace", report.summary.trace)}</section><section class="card"><h2>Runtime shape</h2><pre>const runtime = createVoiceDeliveryRuntime({ audit, trace })
|
|
11790
11921
|
|
|
11791
11922
|
await runtime.tick()
|
|
11792
11923
|
await runtime.requeueDeadLetters()
|
|
@@ -11797,7 +11928,7 @@ var createVoiceDeliveryRuntimeRoutes = (options) => {
|
|
|
11797
11928
|
const htmlPath = options.htmlPath === undefined ? "/delivery-runtime" : options.htmlPath;
|
|
11798
11929
|
const tickPath = options.tickPath === undefined ? "/api/voice-delivery-runtime/tick" : options.tickPath;
|
|
11799
11930
|
const requeueDeadLettersPath = options.requeueDeadLettersPath === undefined ? "/api/voice-delivery-runtime/requeue-dead-letters" : options.requeueDeadLettersPath;
|
|
11800
|
-
const routes = new
|
|
11931
|
+
const routes = new Elysia13({
|
|
11801
11932
|
name: options.name ?? "absolutejs-voice-delivery-runtime"
|
|
11802
11933
|
}).get(path, () => buildVoiceDeliveryRuntimeReport(options.runtime));
|
|
11803
11934
|
if (tickPath !== false) {
|
|
@@ -12035,16 +12166,16 @@ var applyVoiceDataRetentionPolicy = async (options) => {
|
|
|
12035
12166
|
};
|
|
12036
12167
|
var buildVoiceDataRetentionPlan = (options) => applyVoiceDataRetentionPolicy({ ...options, dryRun: true });
|
|
12037
12168
|
// src/evalRoutes.ts
|
|
12038
|
-
import { Elysia as
|
|
12169
|
+
import { Elysia as Elysia16 } from "elysia";
|
|
12039
12170
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
12040
12171
|
import { dirname as dirname2 } from "path";
|
|
12041
12172
|
|
|
12042
12173
|
// src/qualityRoutes.ts
|
|
12043
|
-
import { Elysia as
|
|
12174
|
+
import { Elysia as Elysia15 } from "elysia";
|
|
12044
12175
|
|
|
12045
12176
|
// src/handoffHealth.ts
|
|
12046
|
-
import { Elysia as
|
|
12047
|
-
var
|
|
12177
|
+
import { Elysia as Elysia14 } from "elysia";
|
|
12178
|
+
var escapeHtml17 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
12048
12179
|
var getString6 = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
|
|
12049
12180
|
var isStatus = (value) => value === "delivered" || value === "failed" || value === "skipped";
|
|
12050
12181
|
var increment3 = (record, key) => {
|
|
@@ -12162,10 +12293,10 @@ var renderActionSummary = (summary) => {
|
|
|
12162
12293
|
return [
|
|
12163
12294
|
'<section class="voice-handoff-health-columns">',
|
|
12164
12295
|
"<article><h3>Actions</h3>",
|
|
12165
|
-
actions.length === 0 ? "<p>No handoff actions yet.</p>" : `<ul>${actions.map(([action, count]) => `<li>${
|
|
12296
|
+
actions.length === 0 ? "<p>No handoff actions yet.</p>" : `<ul>${actions.map(([action, count]) => `<li>${escapeHtml17(action)}: ${String(count)}</li>`).join("")}</ul>`,
|
|
12166
12297
|
"</article>",
|
|
12167
12298
|
"<article><h3>Adapters</h3>",
|
|
12168
|
-
adapters.length === 0 ? "<p>No adapter deliveries yet.</p>" : `<ul>${adapters.map(([adapterId, counts]) => `<li>${
|
|
12299
|
+
adapters.length === 0 ? "<p>No adapter deliveries yet.</p>" : `<ul>${adapters.map(([adapterId, counts]) => `<li>${escapeHtml17(adapterId)}: ${String(counts.delivered)} delivered / ${String(counts.failed)} failed / ${String(counts.skipped)} skipped</li>`).join("")}</ul>`,
|
|
12169
12300
|
"</article>",
|
|
12170
12301
|
"</section>"
|
|
12171
12302
|
].join("");
|
|
@@ -12179,22 +12310,22 @@ var renderVoiceHandoffHealthHTML = (summary) => [
|
|
|
12179
12310
|
summary.events.length === 0 ? '<p class="voice-handoff-health-empty">No handoffs found.</p>' : [
|
|
12180
12311
|
'<div class="voice-handoff-health-events">',
|
|
12181
12312
|
...summary.events.map((event) => [
|
|
12182
|
-
`<article class="${
|
|
12313
|
+
`<article class="${escapeHtml17(event.status)}">`,
|
|
12183
12314
|
'<div class="voice-handoff-health-event-header">',
|
|
12184
|
-
`<strong>${
|
|
12185
|
-
`<span>${
|
|
12315
|
+
`<strong>${escapeHtml17(event.action ?? "handoff")}</strong>`,
|
|
12316
|
+
`<span>${escapeHtml17(event.status)}</span>`,
|
|
12186
12317
|
"</div>",
|
|
12187
|
-
`<p><small>${
|
|
12188
|
-
event.target ? `<p>Target: ${
|
|
12189
|
-
event.reason ? `<p>Reason: ${
|
|
12318
|
+
`<p><small>${escapeHtml17(event.sessionId)}</small></p>`,
|
|
12319
|
+
event.target ? `<p>Target: ${escapeHtml17(event.target)}</p>` : "",
|
|
12320
|
+
event.reason ? `<p>Reason: ${escapeHtml17(event.reason)}</p>` : "",
|
|
12190
12321
|
event.deliveries.length ? `<ul>${event.deliveries.map((delivery) => [
|
|
12191
12322
|
"<li>",
|
|
12192
|
-
`${
|
|
12193
|
-
delivery.deliveredTo ? ` to ${
|
|
12194
|
-
delivery.error ? ` (${
|
|
12323
|
+
`${escapeHtml17(delivery.adapterId)}: ${escapeHtml17(delivery.status)}`,
|
|
12324
|
+
delivery.deliveredTo ? ` to ${escapeHtml17(delivery.deliveredTo)}` : "",
|
|
12325
|
+
delivery.error ? ` (${escapeHtml17(delivery.error)})` : "",
|
|
12195
12326
|
"</li>"
|
|
12196
12327
|
].join("")).join("")}</ul>` : "",
|
|
12197
|
-
event.replayHref ? `<p><a href="${
|
|
12328
|
+
event.replayHref ? `<p><a href="${escapeHtml17(event.replayHref)}">Open replay</a></p>` : "",
|
|
12198
12329
|
"</article>"
|
|
12199
12330
|
].join("")),
|
|
12200
12331
|
"</div>"
|
|
@@ -12226,7 +12357,7 @@ var createVoiceHandoffHealthHTMLHandler = (options = {}) => async ({ query }) =>
|
|
|
12226
12357
|
var createVoiceHandoffHealthRoutes = (options = {}) => {
|
|
12227
12358
|
const path = options.path ?? "/api/voice-handoffs";
|
|
12228
12359
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
12229
|
-
const routes = new
|
|
12360
|
+
const routes = new Elysia14({
|
|
12230
12361
|
name: options.name ?? "absolutejs-voice-handoff-health"
|
|
12231
12362
|
}).get(path, createVoiceHandoffHealthJSONHandler(options));
|
|
12232
12363
|
if (htmlPath) {
|
|
@@ -12347,17 +12478,17 @@ var evaluateVoiceQuality = async (input) => {
|
|
|
12347
12478
|
thresholds
|
|
12348
12479
|
};
|
|
12349
12480
|
};
|
|
12350
|
-
var
|
|
12481
|
+
var escapeHtml18 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
12351
12482
|
var formatMetricValue = (metric) => metric.unit === "rate" ? `${(metric.actual * 100).toFixed(2)}%` : metric.unit === "ms" ? `${Math.round(metric.actual)}ms` : String(metric.actual);
|
|
12352
12483
|
var formatThreshold = (metric) => metric.unit === "rate" ? `${(metric.threshold * 100).toFixed(2)}%` : metric.unit === "ms" ? `${Math.round(metric.threshold)}ms` : String(metric.threshold);
|
|
12353
12484
|
var renderVoiceQualityHTML = (report, options = {}) => {
|
|
12354
|
-
const rows = Object.entries(report.metrics).map(([key, metric]) => `<tr class="${metric.pass ? "pass" : "fail"}"><td>${
|
|
12355
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
12485
|
+
const rows = Object.entries(report.metrics).map(([key, metric]) => `<tr class="${metric.pass ? "pass" : "fail"}"><td>${escapeHtml18(metric.label)}</td><td>${escapeHtml18(formatMetricValue(metric))}</td><td>${escapeHtml18(formatThreshold(metric))}</td><td>${metric.pass ? "pass" : "fail"}</td><td><code>${escapeHtml18(key)}</code></td></tr>`).join("");
|
|
12486
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml18(link.href)}">${escapeHtml18(link.label)}</a>`).join("")}</nav>` : "";
|
|
12356
12487
|
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>AbsoluteJS Voice Quality</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1100px;margin:auto}nav{display:flex;flex-wrap:wrap;gap:.5rem;margin:0 0 1.25rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.status{border-radius:999px;display:inline-flex;padding:.35rem .75rem;font-weight:800}.status.pass{background:#dcfce7;color:#166534}.status.fail{background:#fee2e2;color:#991b1b}table{border-collapse:collapse;width:100%;background:white;margin-top:1rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}.pass td{border-left:4px solid #16a34a}.fail td{border-left:4px solid #dc2626}code{background:#f3f4f6;padding:.15rem .3rem;border-radius:.3rem}</style></head><body><main>${links}<h1>Voice quality gates</h1><p class="status ${report.status}">${report.status}</p><p>${report.eventCount} event(s) checked.</p><table><thead><tr><th>Metric</th><th>Actual</th><th>Threshold</th><th>Status</th><th>Key</th></tr></thead><tbody>${rows}</tbody></table></main></body></html>`;
|
|
12357
12488
|
};
|
|
12358
12489
|
var createVoiceQualityRoutes = (options) => {
|
|
12359
12490
|
const path = options.path ?? "/quality";
|
|
12360
|
-
const routes = new
|
|
12491
|
+
const routes = new Elysia15({
|
|
12361
12492
|
name: options.name ?? "absolutejs-voice-quality"
|
|
12362
12493
|
});
|
|
12363
12494
|
const getReport = () => evaluateVoiceQuality({
|
|
@@ -12386,7 +12517,7 @@ var createVoiceQualityRoutes = (options) => {
|
|
|
12386
12517
|
};
|
|
12387
12518
|
|
|
12388
12519
|
// src/evalRoutes.ts
|
|
12389
|
-
var
|
|
12520
|
+
var escapeHtml19 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
12390
12521
|
var rate2 = (count, total) => count / Math.max(1, total);
|
|
12391
12522
|
var normalizeSearchText = (value) => value.trim().toLowerCase();
|
|
12392
12523
|
var getString8 = (value) => typeof value === "string" ? value : undefined;
|
|
@@ -12695,44 +12826,44 @@ var formatTime = (value) => value === undefined ? "unknown" : new Date(value).to
|
|
|
12695
12826
|
var formatPercent = (value) => `${(value * 100).toFixed(2)}%`;
|
|
12696
12827
|
var renderVoiceEvalHTML = (report, options = {}) => {
|
|
12697
12828
|
const title = options.title ?? "AbsoluteJS Voice Evals";
|
|
12698
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
12699
|
-
const trend = report.trend.length ? report.trend.map((bucket) => `<tr><td>${
|
|
12829
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml19(link.href)}">${escapeHtml19(link.label)}</a>`).join("")}</nav>` : "";
|
|
12830
|
+
const trend = report.trend.length ? report.trend.map((bucket) => `<tr><td>${escapeHtml19(bucket.key)}</td><td>${bucket.total}</td><td>${bucket.passed}</td><td>${bucket.failed}</td></tr>`).join("") : '<tr><td colspan="4">No eval buckets yet.</td></tr>';
|
|
12700
12831
|
const sessions = report.sessions.length ? report.sessions.map((session) => {
|
|
12701
12832
|
const failedMetrics = Object.entries(session.quality.metrics).filter(([, metric]) => !metric.pass).map(([, metric]) => metric.label).join(", ");
|
|
12702
|
-
return `<tr class="${session.status}"><td>${
|
|
12833
|
+
return `<tr class="${session.status}"><td>${escapeHtml19(session.sessionId)}</td><td>${escapeHtml19(session.status)}</td><td>${session.eventCount}</td><td>${session.summary.turnCount}</td><td>${session.summary.errorCount}</td><td>${escapeHtml19(formatTime(session.endedAt))}</td><td>${escapeHtml19(failedMetrics || "none")}</td></tr>`;
|
|
12703
12834
|
}).join("") : '<tr><td colspan="7">No sessions found.</td></tr>';
|
|
12704
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
12835
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml19(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1180px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.pass{color:#166534}.fail{color:#991b1b}.status.pass{background:#dcfce7}.status.fail{background:#fee2e2}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:1rem 0}.card{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.card strong{display:block;font-size:2rem}table{border-collapse:collapse;background:white;width:100%;margin:1rem 0 2rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}tr.fail td{border-left:4px solid #dc2626}tr.pass td{border-left:4px solid #16a34a}</style></head><body><main>${links}<h1>${escapeHtml19(title)}</h1><p class="status ${report.status}">${report.status}</p><div class="grid"><article class="card"><span>Total</span><strong>${report.total}</strong></article><article class="card"><span>Passed</span><strong>${report.passed}</strong></article><article class="card"><span>Failed</span><strong>${report.failed}</strong></article></div><h2>Trend</h2><table><thead><tr><th>Day</th><th>Total</th><th>Passed</th><th>Failed</th></tr></thead><tbody>${trend}</tbody></table><h2>Session Eval Results</h2><table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Turns</th><th>Errors</th><th>Last event</th><th>Failed metrics</th></tr></thead><tbody>${sessions}</tbody></table></main></body></html>`;
|
|
12705
12836
|
};
|
|
12706
12837
|
var renderVoiceEvalBaselineHTML = (comparison, options = {}) => {
|
|
12707
12838
|
const title = options.title ?? "AbsoluteJS Voice Eval Baseline";
|
|
12708
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
12709
|
-
const reasons = comparison.reasons.length ? comparison.reasons.map((reason) => `<li>${
|
|
12710
|
-
const newFailures = comparison.newFailedSessionIds.length ? comparison.newFailedSessionIds.map((id) => `<li>${
|
|
12711
|
-
const recovered = comparison.recoveredSessionIds.length ? comparison.recoveredSessionIds.map((id) => `<li>${
|
|
12712
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
12839
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml19(link.href)}">${escapeHtml19(link.label)}</a>`).join("")}</nav>` : "";
|
|
12840
|
+
const reasons = comparison.reasons.length ? comparison.reasons.map((reason) => `<li>${escapeHtml19(reason)}</li>`).join("") : "<li>No baseline regressions detected.</li>";
|
|
12841
|
+
const newFailures = comparison.newFailedSessionIds.length ? comparison.newFailedSessionIds.map((id) => `<li>${escapeHtml19(id)}</li>`).join("") : "<li>none</li>";
|
|
12842
|
+
const recovered = comparison.recoveredSessionIds.length ? comparison.recoveredSessionIds.map((id) => `<li>${escapeHtml19(id)}</li>`).join("") : "<li>none</li>";
|
|
12843
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml19(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1000px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.pass{background:#dcfce7;color:#166534}.fail{background:#fee2e2;color:#991b1b}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:1rem 0}.card{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.card strong{display:block;font-size:2rem}section{background:white;border:1px solid #e7e5e4;border-radius:1rem;margin:1rem 0;padding:1rem}</style></head><body><main>${links}<h1>${escapeHtml19(title)}</h1><p class="status ${comparison.status}">${comparison.status}</p><div class="grid"><article class="card"><span>Baseline pass rate</span><strong>${escapeHtml19(formatPercent(comparison.baseline.passRate))}</strong></article><article class="card"><span>Current pass rate</span><strong>${escapeHtml19(formatPercent(comparison.current.passRate))}</strong></article><article class="card"><span>Failed delta</span><strong>${comparison.deltas.failed}</strong></article><article class="card"><span>Pass rate delta</span><strong>${escapeHtml19(formatPercent(comparison.deltas.passRate))}</strong></article></div><section><h2>Regression Reasons</h2><ul>${reasons}</ul></section><section><h2>New Failed Sessions</h2><ul>${newFailures}</ul></section><section><h2>Recovered Sessions</h2><ul>${recovered}</ul></section></main></body></html>`;
|
|
12713
12844
|
};
|
|
12714
12845
|
var renderVoiceScenarioEvalHTML = (report, options = {}) => {
|
|
12715
12846
|
const title = options.title ?? "AbsoluteJS Voice Scenario Evals";
|
|
12716
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
12847
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml19(link.href)}">${escapeHtml19(link.label)}</a>`).join("")}</nav>` : "";
|
|
12717
12848
|
const scenarios = report.scenarios.length ? report.scenarios.map((scenario) => {
|
|
12718
|
-
const scenarioIssues = scenario.issues.length ? `<ul>${scenario.issues.map((issue) => `<li>${
|
|
12719
|
-
const sessions = scenario.sessions.length ? scenario.sessions.map((session) => `<tr class="${session.status}"><td>${
|
|
12720
|
-
return `<section class="scenario ${scenario.status}"><h2>${
|
|
12849
|
+
const scenarioIssues = scenario.issues.length ? `<ul>${scenario.issues.map((issue) => `<li>${escapeHtml19(issue)}</li>`).join("")}</ul>` : "";
|
|
12850
|
+
const sessions = scenario.sessions.length ? scenario.sessions.map((session) => `<tr class="${session.status}"><td>${escapeHtml19(session.sessionId)}</td><td>${escapeHtml19(session.status)}</td><td>${session.eventCount}</td><td>${escapeHtml19(session.issues.join(", ") || "none")}</td></tr>`).join("") : '<tr><td colspan="4">No matching sessions.</td></tr>';
|
|
12851
|
+
return `<section class="scenario ${scenario.status}"><h2>${escapeHtml19(scenario.label)}</h2>${scenario.description ? `<p>${escapeHtml19(scenario.description)}</p>` : ""}<p class="status ${scenario.status}">${scenario.status}</p><p>${scenario.passed} passed, ${scenario.failed} failed, ${scenario.matchedSessions} matched.</p>${scenarioIssues}<table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Issues</th></tr></thead><tbody>${sessions}</tbody></table></section>`;
|
|
12721
12852
|
}).join("") : "<section><p>No scenarios configured.</p></section>";
|
|
12722
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
12853
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml19(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1180px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.status.pass{background:#dcfce7;color:#166534}.status.fail{background:#fee2e2;color:#991b1b}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:1rem 0}.card,section{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.card strong{display:block;font-size:2rem}section{margin:1rem 0}table{border-collapse:collapse;width:100%;margin-top:1rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}tr.fail td{border-left:4px solid #dc2626}tr.pass td{border-left:4px solid #16a34a}</style></head><body><main>${links}<h1>${escapeHtml19(title)}</h1><p class="status ${report.status}">${report.status}</p><div class="grid"><article class="card"><span>Total</span><strong>${report.total}</strong></article><article class="card"><span>Passed</span><strong>${report.passed}</strong></article><article class="card"><span>Failed</span><strong>${report.failed}</strong></article></div>${scenarios}</main></body></html>`;
|
|
12723
12854
|
};
|
|
12724
12855
|
var renderVoiceScenarioFixtureEvalHTML = (report, options = {}) => {
|
|
12725
12856
|
const title = options.title ?? "AbsoluteJS Voice Fixture Evals";
|
|
12726
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
12857
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml19(link.href)}">${escapeHtml19(link.label)}</a>`).join("")}</nav>` : "";
|
|
12727
12858
|
const fixtures = report.fixtures.length ? report.fixtures.map((fixture) => {
|
|
12728
|
-
const scenarios = fixture.report.scenarios.map((scenario) => `<tr class="${scenario.status}"><td>${
|
|
12729
|
-
return `<section class="${fixture.status}"><h2>${
|
|
12859
|
+
const scenarios = fixture.report.scenarios.map((scenario) => `<tr class="${scenario.status}"><td>${escapeHtml19(scenario.label)}</td><td>${escapeHtml19(scenario.status)}</td><td>${scenario.matchedSessions}</td><td>${escapeHtml19([...scenario.issues, ...scenario.sessions.flatMap((session) => session.issues)].join(", ") || "none")}</td></tr>`).join("");
|
|
12860
|
+
return `<section class="${fixture.status}"><h2>${escapeHtml19(fixture.label)}</h2>${fixture.description ? `<p>${escapeHtml19(fixture.description)}</p>` : ""}<p class="status ${fixture.status}">${fixture.status}</p><table><thead><tr><th>Scenario</th><th>Status</th><th>Sessions</th><th>Issues</th></tr></thead><tbody>${scenarios}</tbody></table></section>`;
|
|
12730
12861
|
}).join("") : "<section><p>No scenario fixtures configured.</p></section>";
|
|
12731
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
12862
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml19(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1180px;margin:auto}nav{display:flex;gap:.5rem;flex-wrap:wrap;margin-bottom:1rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.status{border-radius:999px;display:inline-flex;font-weight:800;padding:.35rem .75rem}.status.pass{background:#dcfce7;color:#166534}.status.fail{background:#fee2e2;color:#991b1b}.grid{display:grid;gap:1rem;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:1rem 0}.card,section{background:white;border:1px solid #e7e5e4;border-radius:1rem;padding:1rem}.card strong{display:block;font-size:2rem}section{margin:1rem 0}table{border-collapse:collapse;width:100%;margin-top:1rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}tr.fail td{border-left:4px solid #dc2626}tr.pass td{border-left:4px solid #16a34a}</style></head><body><main>${links}<h1>${escapeHtml19(title)}</h1><p class="status ${report.status}">${report.status}</p><div class="grid"><article class="card"><span>Total</span><strong>${report.total}</strong></article><article class="card"><span>Passed</span><strong>${report.passed}</strong></article><article class="card"><span>Failed</span><strong>${report.failed}</strong></article></div>${fixtures}</main></body></html>`;
|
|
12732
12863
|
};
|
|
12733
12864
|
var createVoiceEvalRoutes = (options) => {
|
|
12734
12865
|
const path = options.path ?? "/evals";
|
|
12735
|
-
const routes = new
|
|
12866
|
+
const routes = new Elysia16({
|
|
12736
12867
|
name: options.name ?? "absolutejs-voice-evals"
|
|
12737
12868
|
});
|
|
12738
12869
|
const getReport = () => runVoiceSessionEvals({
|
|
@@ -12866,11 +12997,11 @@ var createVoiceEvalRoutes = (options) => {
|
|
|
12866
12997
|
return routes;
|
|
12867
12998
|
};
|
|
12868
12999
|
// src/simulationSuite.ts
|
|
12869
|
-
import { Elysia as
|
|
13000
|
+
import { Elysia as Elysia19 } from "elysia";
|
|
12870
13001
|
|
|
12871
13002
|
// src/outcomeContract.ts
|
|
12872
|
-
import { Elysia as
|
|
12873
|
-
var
|
|
13003
|
+
import { Elysia as Elysia17 } from "elysia";
|
|
13004
|
+
var escapeHtml20 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
12874
13005
|
var getPayloadString = (event, key) => typeof event.payload[key] === "string" ? event.payload[key] : undefined;
|
|
12875
13006
|
var toList = async (input) => Array.isArray(input) ? input : await input?.list() ?? [];
|
|
12876
13007
|
var hydrateSessions = async (input) => {
|
|
@@ -12978,9 +13109,9 @@ var renderVoiceOutcomeContractHTML = (report, options = {}) => {
|
|
|
12978
13109
|
const contracts = report.contracts.map((contract) => `<section class="contract ${contract.pass ? "pass" : "fail"}">
|
|
12979
13110
|
<div class="contract-header">
|
|
12980
13111
|
<div>
|
|
12981
|
-
<p class="eyebrow">${
|
|
12982
|
-
<h2>${
|
|
12983
|
-
${contract.description ? `<p>${
|
|
13112
|
+
<p class="eyebrow">${escapeHtml20(contract.contractId)}</p>
|
|
13113
|
+
<h2>${escapeHtml20(contract.label ?? contract.contractId)}</h2>
|
|
13114
|
+
${contract.description ? `<p>${escapeHtml20(contract.description)}</p>` : ""}
|
|
12984
13115
|
</div>
|
|
12985
13116
|
<strong>${contract.pass ? "pass" : "fail"}</strong>
|
|
12986
13117
|
</div>
|
|
@@ -12991,9 +13122,9 @@ var renderVoiceOutcomeContractHTML = (report, options = {}) => {
|
|
|
12991
13122
|
<span>handoffs ${String(contract.matched.handoffs)}</span>
|
|
12992
13123
|
<span>events ${String(contract.matched.integrationEvents)}</span>
|
|
12993
13124
|
</div>
|
|
12994
|
-
${contract.issues.length ? `<ul>${contract.issues.map((issue) => `<li>${
|
|
13125
|
+
${contract.issues.length ? `<ul>${contract.issues.map((issue) => `<li>${escapeHtml20(issue.message)}</li>`).join("")}</ul>` : ""}
|
|
12995
13126
|
</section>`).join("");
|
|
12996
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
13127
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml20(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.contract{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.14),rgba(14,165,233,.12))}.eyebrow{color:#7dd3fc;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0}.summary,.grid{display:flex;flex-wrap:wrap;gap:10px}.pill,.grid span{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.contract-header{display:flex;gap:16px;justify-content:space-between}.pass{color:#86efac}.fail{color:#fca5a5}.contract.fail{border-color:rgba(248,113,113,.45)}li{margin:8px 0}@media(max-width:800px){main{padding:18px}.contract-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Business Outcome Verification</p><h1>${escapeHtml20(title)}</h1><div class="summary"><span class="pill ${report.status}">${report.status}</span><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} contracts</span></div></section>${contracts || '<section class="contract"><p>No outcome contracts configured.</p></section>'}</main></body></html>`;
|
|
12997
13128
|
};
|
|
12998
13129
|
var createVoiceOutcomeContractJSONHandler = (options) => async () => runVoiceOutcomeContractSuite(options);
|
|
12999
13130
|
var createVoiceOutcomeContractHTMLHandler = (options) => async () => {
|
|
@@ -13009,7 +13140,7 @@ var createVoiceOutcomeContractHTMLHandler = (options) => async () => {
|
|
|
13009
13140
|
var createVoiceOutcomeContractRoutes = (options) => {
|
|
13010
13141
|
const path = options.path ?? "/api/outcome-contracts";
|
|
13011
13142
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
13012
|
-
const routes = new
|
|
13143
|
+
const routes = new Elysia17({
|
|
13013
13144
|
name: options.name ?? "absolutejs-voice-outcome-contracts"
|
|
13014
13145
|
}).get(path, createVoiceOutcomeContractJSONHandler(options));
|
|
13015
13146
|
if (htmlPath) {
|
|
@@ -13019,7 +13150,7 @@ var createVoiceOutcomeContractRoutes = (options) => {
|
|
|
13019
13150
|
};
|
|
13020
13151
|
|
|
13021
13152
|
// src/toolContract.ts
|
|
13022
|
-
import { Elysia as
|
|
13153
|
+
import { Elysia as Elysia18 } from "elysia";
|
|
13023
13154
|
|
|
13024
13155
|
// src/toolRuntime.ts
|
|
13025
13156
|
var toErrorMessage4 = (error) => error instanceof Error ? error.message : String(error);
|
|
@@ -13228,7 +13359,7 @@ var createDefaultTurn = (caseId) => ({
|
|
|
13228
13359
|
});
|
|
13229
13360
|
var defaultApi = {};
|
|
13230
13361
|
var sameJSON = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
13231
|
-
var
|
|
13362
|
+
var escapeHtml21 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13232
13363
|
var evaluateExpectation = (input) => {
|
|
13233
13364
|
const issues = [];
|
|
13234
13365
|
const expect = input.expect;
|
|
@@ -13394,19 +13525,19 @@ var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
|
13394
13525
|
const title = options.title ?? "Voice Tool Contracts";
|
|
13395
13526
|
const contracts = report.contracts.map((contract) => {
|
|
13396
13527
|
const cases = contract.cases.map((testCase) => `<tr>
|
|
13397
|
-
<td>${
|
|
13528
|
+
<td>${escapeHtml21(testCase.label ?? testCase.caseId)}</td>
|
|
13398
13529
|
<td class="${testCase.pass ? "pass" : "fail"}">${testCase.pass ? "pass" : "fail"}</td>
|
|
13399
|
-
<td>${
|
|
13530
|
+
<td>${escapeHtml21(testCase.status)}</td>
|
|
13400
13531
|
<td>${String(testCase.attempts)}</td>
|
|
13401
13532
|
<td>${String(testCase.elapsedMs)}ms</td>
|
|
13402
13533
|
<td>${testCase.timedOut ? "yes" : "no"}</td>
|
|
13403
|
-
<td>${
|
|
13534
|
+
<td>${escapeHtml21(testCase.issues.map((issue) => issue.message).join(" ") || testCase.error || "")}</td>
|
|
13404
13535
|
</tr>`).join("");
|
|
13405
13536
|
return `<section class="contract ${contract.pass ? "pass" : "fail"}">
|
|
13406
13537
|
<div class="contract-header">
|
|
13407
13538
|
<div>
|
|
13408
|
-
<p class="eyebrow">${
|
|
13409
|
-
<h2>${
|
|
13539
|
+
<p class="eyebrow">${escapeHtml21(contract.toolName)}</p>
|
|
13540
|
+
<h2>${escapeHtml21(contract.label ?? contract.contractId)}</h2>
|
|
13410
13541
|
</div>
|
|
13411
13542
|
<strong class="${contract.pass ? "pass" : "fail"}">${contract.pass ? "Passing" : "Failing"}</strong>
|
|
13412
13543
|
</div>
|
|
@@ -13416,7 +13547,7 @@ var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
|
13416
13547
|
</table>
|
|
13417
13548
|
</section>`;
|
|
13418
13549
|
}).join("");
|
|
13419
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
13550
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml21(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.contract{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.14),rgba(245,158,11,.12))}.eyebrow{color:#fbbf24;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.contract-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}h2{margin:.2rem 0 1rem}.pass{color:#86efac}.fail{color:#fca5a5}.contract.fail{border-color:rgba(248,113,113,.45)}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid #2a323a;padding:12px;text-align:left;vertical-align:top}th{color:#a8b0b8;font-size:.82rem}@media(max-width:800px){main{padding:18px}table{display:block;overflow:auto}.contract-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Tool Reliability</p><h1>${escapeHtml21(title)}</h1><div class="summary"><span class="pill ${report.status === "pass" ? "pass" : "fail"}">${escapeHtml21(report.status)}</span><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} contracts</span></div></section>${contracts || '<section class="contract"><p>No tool contracts configured.</p></section>'}</main></body></html>`;
|
|
13420
13551
|
};
|
|
13421
13552
|
var createVoiceToolContractJSONHandler = (options) => () => runVoiceToolContractSuite(options);
|
|
13422
13553
|
var createVoiceToolContractHTMLHandler = (options) => async () => {
|
|
@@ -13433,7 +13564,7 @@ var createVoiceToolContractHTMLHandler = (options) => async () => {
|
|
|
13433
13564
|
var createVoiceToolContractRoutes = (options) => {
|
|
13434
13565
|
const path = options.path ?? "/api/tool-contracts";
|
|
13435
13566
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
13436
|
-
const routes = new
|
|
13567
|
+
const routes = new Elysia18({
|
|
13437
13568
|
name: options.name ?? "absolutejs-voice-tool-contracts"
|
|
13438
13569
|
}).get(path, createVoiceToolContractJSONHandler(options));
|
|
13439
13570
|
if (htmlPath) {
|
|
@@ -13443,7 +13574,7 @@ var createVoiceToolContractRoutes = (options) => {
|
|
|
13443
13574
|
};
|
|
13444
13575
|
|
|
13445
13576
|
// src/simulationSuite.ts
|
|
13446
|
-
var
|
|
13577
|
+
var escapeHtml22 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13447
13578
|
var summarizeSection = (report) => ({
|
|
13448
13579
|
failed: report.failed,
|
|
13449
13580
|
passed: report.passed,
|
|
@@ -13579,20 +13710,20 @@ var renderSection = (label, summary) => {
|
|
|
13579
13710
|
if (!summary) {
|
|
13580
13711
|
return "";
|
|
13581
13712
|
}
|
|
13582
|
-
return `<article class="${
|
|
13713
|
+
return `<article class="${escapeHtml22(summary.status)}"><span>${escapeHtml22(label)}</span><strong>${escapeHtml22(summary.status)}</strong><p>${summary.passed}/${summary.total} passed, ${summary.failed} failed.</p></article>`;
|
|
13583
13714
|
};
|
|
13584
13715
|
var renderAction = (action) => {
|
|
13585
|
-
const content = `<strong>${
|
|
13586
|
-
return action.href ? `<a class="action" href="${
|
|
13716
|
+
const content = `<strong>${escapeHtml22(action.label)}</strong><p>${escapeHtml22(action.description)}</p><span>${escapeHtml22(action.section)} / ${escapeHtml22(action.severity)}</span>`;
|
|
13717
|
+
return action.href ? `<a class="action" href="${escapeHtml22(action.href)}">${content}</a>` : `<article class="action">${content}</article>`;
|
|
13587
13718
|
};
|
|
13588
13719
|
var renderVoiceSimulationSuiteHTML = (report, options = {}) => {
|
|
13589
13720
|
const title = options.title ?? "Voice Simulation Suite";
|
|
13590
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
13721
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml22(title)}</title><style>body{background:#10151c;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1080px;padding:32px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.18),rgba(59,130,246,.12));border:1px solid #283544;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#93c5fd;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.badge{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;padding:8px 12px}.pass{color:#86efac}.fail{color:#fca5a5}.grid,.actions{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));margin:18px 0}.grid article,.action{background:#151d27;border:1px solid #283544;border-radius:18px;color:inherit;padding:16px;text-decoration:none}.grid span,.action span{color:#aab5c0}.grid strong{display:block;font-size:2rem;margin:.25rem 0;text-transform:uppercase}.action strong{display:block;color:#f8f3e7;margin-bottom:.35rem}.action p{color:#d8dee6;margin:.3rem 0 .6rem}pre{background:#151d27;border:1px solid #283544;border-radius:18px;overflow:auto;padding:16px}</style></head><body><main><section class="hero"><p class="eyebrow">Pre-production proof</p><h1>${escapeHtml22(title)}</h1><p>One report for session quality, scenario evals, fixture simulations, tool contracts, and outcome contracts.</p><p class="badge ${escapeHtml22(report.status)}">Status: ${escapeHtml22(report.status)}</p><section class="grid">${renderSection("Sessions", report.summary.sessions)}${renderSection("Scenarios", report.summary.scenarios)}${renderSection("Fixtures", report.summary.fixtures)}${renderSection("Tools", report.summary.tools)}${renderSection("Outcomes", report.summary.outcomes)}</section></section><h2>Actions</h2><section class="actions">${report.actions.length > 0 ? report.actions.map(renderAction).join("") : '<article class="action"><strong>No action required</strong><p>All enabled simulation sections are passing.</p></article>'}</section><pre>${escapeHtml22(JSON.stringify({ summary: report.summary, actions: report.actions }, null, 2))}</pre></main></body></html>`;
|
|
13591
13722
|
};
|
|
13592
13723
|
var createVoiceSimulationSuiteRoutes = (options) => {
|
|
13593
13724
|
const path = options.path ?? "/api/voice/simulations";
|
|
13594
13725
|
const htmlPath = options.htmlPath === undefined ? "/voice/simulations" : options.htmlPath;
|
|
13595
|
-
const app = new
|
|
13726
|
+
const app = new Elysia19({
|
|
13596
13727
|
name: options.name ?? "absolutejs-voice-simulation-suite"
|
|
13597
13728
|
}).get(path, () => runVoiceSimulationSuite(options));
|
|
13598
13729
|
if (htmlPath) {
|
|
@@ -13904,9 +14035,9 @@ var createVoiceWorkflowContractHandler = (input) => {
|
|
|
13904
14035
|
};
|
|
13905
14036
|
};
|
|
13906
14037
|
// src/sessionReplay.ts
|
|
13907
|
-
import { Elysia as
|
|
14038
|
+
import { Elysia as Elysia20 } from "elysia";
|
|
13908
14039
|
var getString9 = (value) => typeof value === "string" ? value : undefined;
|
|
13909
|
-
var
|
|
14040
|
+
var escapeHtml23 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13910
14041
|
var increment4 = (record, key) => {
|
|
13911
14042
|
record[key] = (record[key] ?? 0) + 1;
|
|
13912
14043
|
};
|
|
@@ -14086,10 +14217,10 @@ var summarizeVoiceSessions = async (options = {}) => {
|
|
|
14086
14217
|
var renderVoiceSessionsHTML = (sessions) => sessions.length === 0 ? '<p class="voice-sessions-empty">No voice sessions found.</p>' : [
|
|
14087
14218
|
'<div class="voice-sessions-list">',
|
|
14088
14219
|
...sessions.map((session) => [
|
|
14089
|
-
`<article class="voice-session-card ${
|
|
14220
|
+
`<article class="voice-session-card ${escapeHtml23(session.status)}">`,
|
|
14090
14221
|
'<div class="voice-session-card-header">',
|
|
14091
|
-
`<strong>${
|
|
14092
|
-
`<span>${
|
|
14222
|
+
`<strong>${escapeHtml23(session.sessionId)}</strong>`,
|
|
14223
|
+
`<span>${escapeHtml23(session.status)}</span>`,
|
|
14093
14224
|
"</div>",
|
|
14094
14225
|
"<dl>",
|
|
14095
14226
|
`<div><dt>Events</dt><dd>${String(session.eventCount)}</dd></div>`,
|
|
@@ -14097,9 +14228,9 @@ var renderVoiceSessionsHTML = (sessions) => sessions.length === 0 ? '<p class="v
|
|
|
14097
14228
|
`<div><dt>Transcripts</dt><dd>${String(session.transcriptCount)}</dd></div>`,
|
|
14098
14229
|
`<div><dt>Errors</dt><dd>${String(session.errorCount)}</dd></div>`,
|
|
14099
14230
|
"</dl>",
|
|
14100
|
-
session.latestOutcome ? `<p>Outcome: ${
|
|
14101
|
-
session.providers.length ? `<p>Providers: ${session.providers.map(
|
|
14102
|
-
session.replayHref ? `<p><a href="${
|
|
14231
|
+
session.latestOutcome ? `<p>Outcome: ${escapeHtml23(session.latestOutcome)}</p>` : "",
|
|
14232
|
+
session.providers.length ? `<p>Providers: ${session.providers.map(escapeHtml23).join(", ")}</p>` : "",
|
|
14233
|
+
session.replayHref ? `<p><a href="${escapeHtml23(session.replayHref)}">Open replay</a></p>` : "",
|
|
14103
14234
|
"</article>"
|
|
14104
14235
|
].join("")),
|
|
14105
14236
|
"</div>"
|
|
@@ -14130,7 +14261,7 @@ var createVoiceSessionsHTMLHandler = (options = {}) => async ({ query }) => {
|
|
|
14130
14261
|
var createVoiceSessionListRoutes = (options = {}) => {
|
|
14131
14262
|
const path = options.path ?? "/api/voice-sessions";
|
|
14132
14263
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
14133
|
-
const routes = new
|
|
14264
|
+
const routes = new Elysia20({
|
|
14134
14265
|
name: options.name ?? "absolutejs-voice-session-list"
|
|
14135
14266
|
}).get(path, createVoiceSessionsJSONHandler(options));
|
|
14136
14267
|
if (htmlPath) {
|
|
@@ -14158,7 +14289,7 @@ var createVoiceSessionReplayHTMLHandler = (options) => async ({ params }) => {
|
|
|
14158
14289
|
var createVoiceSessionReplayRoutes = (options) => {
|
|
14159
14290
|
const path = options.path ?? "/api/voice-sessions/:sessionId/replay";
|
|
14160
14291
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
14161
|
-
const routes = new
|
|
14292
|
+
const routes = new Elysia20({
|
|
14162
14293
|
name: options.name ?? "absolutejs-voice-session-replay"
|
|
14163
14294
|
}).get(path, createVoiceSessionReplayJSONHandler(options));
|
|
14164
14295
|
if (htmlPath) {
|
|
@@ -14325,10 +14456,10 @@ var assertVoiceAgentSquadContract = async (options) => {
|
|
|
14325
14456
|
return report;
|
|
14326
14457
|
};
|
|
14327
14458
|
// src/turnLatency.ts
|
|
14328
|
-
import { Elysia as
|
|
14459
|
+
import { Elysia as Elysia21 } from "elysia";
|
|
14329
14460
|
var DEFAULT_WARN_AFTER_MS = 1800;
|
|
14330
14461
|
var DEFAULT_FAIL_AFTER_MS = 3200;
|
|
14331
|
-
var
|
|
14462
|
+
var escapeHtml24 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
14332
14463
|
var firstNumber = (values) => values.filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
14333
14464
|
var getString10 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
14334
14465
|
var createTraceStageIndex = (events) => {
|
|
@@ -14442,11 +14573,11 @@ var summarizeVoiceTurnLatency = async (options) => {
|
|
|
14442
14573
|
var formatMs = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
|
|
14443
14574
|
var renderVoiceTurnLatencyHTML = (report, options = {}) => {
|
|
14444
14575
|
const title = options.title ?? "Voice Turn Latency";
|
|
14445
|
-
const turns = report.turns.map((turn) => `<article class="turn ${
|
|
14446
|
-
<header><div><p class="eyebrow">${
|
|
14447
|
-
<dl>${turn.stages.map((stage) => `<div><dt>${
|
|
14576
|
+
const turns = report.turns.map((turn) => `<article class="turn ${escapeHtml24(turn.status)}">
|
|
14577
|
+
<header><div><p class="eyebrow">${escapeHtml24(turn.sessionId)} \xB7 ${escapeHtml24(turn.turnId)}</p><h2>${escapeHtml24(turn.text || "Empty turn")}</h2></div><strong>${escapeHtml24(turn.status)}</strong></header>
|
|
14578
|
+
<dl>${turn.stages.map((stage) => `<div><dt>${escapeHtml24(stage.label)}</dt><dd>${escapeHtml24(formatMs(stage.valueMs))}</dd></div>`).join("")}</dl>
|
|
14448
14579
|
</article>`).join("");
|
|
14449
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
14580
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml24(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.turn{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(94,234,212,.16),rgba(251,191,36,.1))}.eyebrow{color:#5eead4;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.turn header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.pass{color:#86efac}.warn,.empty{color:#fde68a}.fail{color:#fca5a5}.turn.fail{border-color:rgba(248,113,113,.45)}dl{display:grid;gap:8px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}dt{color:#a8b0b8;font-size:.8rem}dd{font-weight:900;margin:0}@media(max-width:800px){main{padding:18px}.turn header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">End-to-end responsiveness</p><h1>${escapeHtml24(title)}</h1><div class="summary"><span class="pill ${escapeHtml24(report.status)}">${escapeHtml24(report.status)}</span><span class="pill">${String(report.total)} turns</span><span class="pill">avg ${escapeHtml24(formatMs(report.averageTotalMs))}</span><span class="pill">${String(report.warnings)} warnings</span><span class="pill">${String(report.failed)} failed</span></div></section>${turns || '<section class="turn"><p>No committed turns found.</p></section>'}</main></body></html>`;
|
|
14450
14581
|
};
|
|
14451
14582
|
var createVoiceTurnLatencyJSONHandler = (options) => async () => summarizeVoiceTurnLatency(options);
|
|
14452
14583
|
var createVoiceTurnLatencyHTMLHandler = (options) => async () => {
|
|
@@ -14463,7 +14594,7 @@ var createVoiceTurnLatencyHTMLHandler = (options) => async () => {
|
|
|
14463
14594
|
var createVoiceTurnLatencyRoutes = (options) => {
|
|
14464
14595
|
const path = options.path ?? "/api/turn-latency";
|
|
14465
14596
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
14466
|
-
const routes = new
|
|
14597
|
+
const routes = new Elysia21({
|
|
14467
14598
|
name: options.name ?? "absolutejs-voice-turn-latency"
|
|
14468
14599
|
}).get(path, createVoiceTurnLatencyJSONHandler(options));
|
|
14469
14600
|
if (htmlPath) {
|
|
@@ -14472,8 +14603,8 @@ var createVoiceTurnLatencyRoutes = (options) => {
|
|
|
14472
14603
|
return routes;
|
|
14473
14604
|
};
|
|
14474
14605
|
// src/liveLatency.ts
|
|
14475
|
-
import { Elysia as
|
|
14476
|
-
var
|
|
14606
|
+
import { Elysia as Elysia22 } from "elysia";
|
|
14607
|
+
var escapeHtml25 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
14477
14608
|
var percentile = (values, percentileValue) => {
|
|
14478
14609
|
if (values.length === 0) {
|
|
14479
14610
|
return;
|
|
@@ -14521,13 +14652,13 @@ var summarizeVoiceLiveLatency = async (options) => {
|
|
|
14521
14652
|
var formatMs2 = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
|
|
14522
14653
|
var renderVoiceLiveLatencyHTML = (report, options = {}) => {
|
|
14523
14654
|
const title = options.title ?? "Voice Live Latency";
|
|
14524
|
-
const rows = report.recent.map((sample) => `<tr><td>${
|
|
14525
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
14655
|
+
const rows = report.recent.map((sample) => `<tr><td>${escapeHtml25(sample.sessionId)}</td><td>${escapeHtml25(formatMs2(sample.latencyMs))}</td><td>${escapeHtml25(sample.status ?? "unknown")}</td><td>${escapeHtml25(new Date(sample.at).toLocaleString())}</td></tr>`).join("");
|
|
14656
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml25(title)}</title><style>body{background:#0c0f14;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1060px;padding:32px}.hero{background:linear-gradient(135deg,rgba(94,234,212,.16),rgba(245,158,11,.1));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;padding:8px 12px}.pass{color:#86efac}.warn,.empty{color:#fbbf24}.fail{color:#fca5a5}.metrics{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:18px 0}.metrics article,table{background:#141922;border:1px solid #26313d;border-radius:18px}.metrics article{padding:16px}.metrics span{color:#a8b0b8}.metrics strong{display:block;font-size:2rem;margin-top:.25rem}table{border-collapse:collapse;overflow:hidden;width:100%}td,th{border-bottom:1px solid #26313d;padding:12px;text-align:left}@media(max-width:760px){main{padding:20px}}</style></head><body><main><section class="hero"><p class="eyebrow">Browser proof</p><h1>${escapeHtml25(title)}</h1><p>Recent real browser speech-to-assistant response measurements from persisted <code>client.live_latency</code> traces.</p><p class="status ${escapeHtml25(report.status)}">Status: ${escapeHtml25(report.status)}</p><section class="metrics"><article><span>p50</span><strong>${escapeHtml25(formatMs2(report.p50LatencyMs))}</strong></article><article><span>p95</span><strong>${escapeHtml25(formatMs2(report.p95LatencyMs))}</strong></article><article><span>Average</span><strong>${escapeHtml25(formatMs2(report.averageLatencyMs))}</strong></article><article><span>Samples</span><strong>${String(report.total)}</strong></article></section></section><table><thead><tr><th>Session</th><th>Latency</th><th>Status</th><th>Measured</th></tr></thead><tbody>${rows || '<tr><td colspan="4">No live latency samples yet.</td></tr>'}</tbody></table></main></body></html>`;
|
|
14526
14657
|
};
|
|
14527
14658
|
var createVoiceLiveLatencyRoutes = (options) => {
|
|
14528
14659
|
const path = options.path ?? "/api/live-latency";
|
|
14529
14660
|
const htmlPath = options.htmlPath === undefined ? "/live-latency" : options.htmlPath;
|
|
14530
|
-
const routes = new
|
|
14661
|
+
const routes = new Elysia22({
|
|
14531
14662
|
name: options.name ?? "absolutejs-voice-live-latency"
|
|
14532
14663
|
}).get(path, () => summarizeVoiceLiveLatency(options));
|
|
14533
14664
|
if (htmlPath) {
|
|
@@ -14544,9 +14675,9 @@ var createVoiceLiveLatencyRoutes = (options) => {
|
|
|
14544
14675
|
return routes;
|
|
14545
14676
|
};
|
|
14546
14677
|
// src/turnQuality.ts
|
|
14547
|
-
import { Elysia as
|
|
14678
|
+
import { Elysia as Elysia23 } from "elysia";
|
|
14548
14679
|
var DEFAULT_CONFIDENCE_WARN_THRESHOLD = 0.72;
|
|
14549
|
-
var
|
|
14680
|
+
var escapeHtml26 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
14550
14681
|
var getTurnLatencyMs = (turn) => {
|
|
14551
14682
|
const firstTranscriptAt = turn.transcripts.map((transcript) => transcript.endedAtMs ?? transcript.startedAtMs).filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
14552
14683
|
if (firstTranscriptAt === undefined) {
|
|
@@ -14617,24 +14748,24 @@ var summarizeVoiceTurnQuality = async (options) => {
|
|
|
14617
14748
|
};
|
|
14618
14749
|
var renderVoiceTurnQualityHTML = (report, options = {}) => {
|
|
14619
14750
|
const title = options.title ?? "Voice Turn Quality";
|
|
14620
|
-
const turns = report.turns.map((turn) => `<article class="turn ${
|
|
14751
|
+
const turns = report.turns.map((turn) => `<article class="turn ${escapeHtml26(turn.status)}">
|
|
14621
14752
|
<div class="turn-header">
|
|
14622
14753
|
<div>
|
|
14623
|
-
<p class="eyebrow">${
|
|
14624
|
-
<h2>${
|
|
14754
|
+
<p class="eyebrow">${escapeHtml26(turn.sessionId)} \xB7 ${escapeHtml26(turn.turnId)}</p>
|
|
14755
|
+
<h2>${escapeHtml26(turn.text || "Empty turn")}</h2>
|
|
14625
14756
|
</div>
|
|
14626
|
-
<strong>${
|
|
14757
|
+
<strong>${escapeHtml26(turn.status)}</strong>
|
|
14627
14758
|
</div>
|
|
14628
14759
|
<dl>
|
|
14629
|
-
<div><dt>Source</dt><dd>${
|
|
14760
|
+
<div><dt>Source</dt><dd>${escapeHtml26(turn.source ?? "unknown")}</dd></div>
|
|
14630
14761
|
<div><dt>Confidence</dt><dd>${turn.averageConfidence === undefined ? "n/a" : `${Math.round(turn.averageConfidence * 100)}%`}</dd></div>
|
|
14631
|
-
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${
|
|
14632
|
-
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${
|
|
14762
|
+
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${escapeHtml26(turn.fallbackSelectionReason ?? "selected")})` : "no"}</dd></div>
|
|
14763
|
+
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${escapeHtml26(turn.correctionProvider)}` : ""}` : "none"}</dd></div>
|
|
14633
14764
|
<div><dt>Transcripts</dt><dd>${String(turn.selectedTranscriptCount)} selected \xB7 ${String(turn.finalTranscriptCount)} final \xB7 ${String(turn.partialTranscriptCount)} partial</dd></div>
|
|
14634
14765
|
<div><dt>Cost</dt><dd>${turn.costUnits === undefined ? "n/a" : String(turn.costUnits)}</dd></div>
|
|
14635
14766
|
</dl>
|
|
14636
14767
|
</article>`).join("");
|
|
14637
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
14768
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml26(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.turn{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(251,191,36,.16),rgba(34,197,94,.1))}.eyebrow{color:#fbbf24;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.turn-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.pass{color:#86efac}.warn,.unknown{color:#fde68a}.fail{color:#fca5a5}.turn.fail{border-color:rgba(248,113,113,.45)}dl{display:grid;gap:8px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr))}dt{color:#a8b0b8;font-size:.8rem}dd{margin:0}@media(max-width:800px){main{padding:18px}.turn-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Realtime STT Debugging</p><h1>${escapeHtml26(title)}</h1><div class="summary"><span class="pill ${escapeHtml26(report.status)}">${escapeHtml26(report.status)}</span><span class="pill">${String(report.total)} turns</span><span class="pill">${String(report.warnings)} warnings</span><span class="pill">${String(report.failed)} failed</span><span class="pill">${String(report.sessions)} sessions</span></div></section>${turns || '<section class="turn"><p>No committed turns found.</p></section>'}</main></body></html>`;
|
|
14638
14769
|
};
|
|
14639
14770
|
var createVoiceTurnQualityJSONHandler = (options) => async () => summarizeVoiceTurnQuality(options);
|
|
14640
14771
|
var createVoiceTurnQualityHTMLHandler = (options) => async () => {
|
|
@@ -14651,7 +14782,7 @@ var createVoiceTurnQualityHTMLHandler = (options) => async () => {
|
|
|
14651
14782
|
var createVoiceTurnQualityRoutes = (options) => {
|
|
14652
14783
|
const path = options.path ?? "/api/turn-quality";
|
|
14653
14784
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
14654
|
-
const routes = new
|
|
14785
|
+
const routes = new Elysia23({
|
|
14655
14786
|
name: options.name ?? "absolutejs-voice-turn-quality"
|
|
14656
14787
|
}).get(path, createVoiceTurnQualityJSONHandler(options));
|
|
14657
14788
|
if (htmlPath) {
|
|
@@ -14660,7 +14791,7 @@ var createVoiceTurnQualityRoutes = (options) => {
|
|
|
14660
14791
|
return routes;
|
|
14661
14792
|
};
|
|
14662
14793
|
// src/telephonyOutcome.ts
|
|
14663
|
-
import { Elysia as
|
|
14794
|
+
import { Elysia as Elysia24 } from "elysia";
|
|
14664
14795
|
var DEFAULT_COMPLETED_STATUSES = [
|
|
14665
14796
|
"answered",
|
|
14666
14797
|
"completed",
|
|
@@ -15310,7 +15441,7 @@ var createVoiceTelephonyWebhookHandler = (options = {}) => async (input) => {
|
|
|
15310
15441
|
var createVoiceTelephonyWebhookRoutes = (options = {}) => {
|
|
15311
15442
|
const path = options.path ?? "/api/voice/telephony/webhook";
|
|
15312
15443
|
const handler = createVoiceTelephonyWebhookHandler(options);
|
|
15313
|
-
return new
|
|
15444
|
+
return new Elysia24({
|
|
15314
15445
|
name: options.name ?? "absolutejs-voice-telephony-webhooks"
|
|
15315
15446
|
}).post(path, async ({ query, request }) => {
|
|
15316
15447
|
try {
|
|
@@ -15331,11 +15462,11 @@ var createVoiceTelephonyWebhookRoutes = (options = {}) => {
|
|
|
15331
15462
|
});
|
|
15332
15463
|
};
|
|
15333
15464
|
// src/phoneAgent.ts
|
|
15334
|
-
import { Elysia as
|
|
15465
|
+
import { Elysia as Elysia30 } from "elysia";
|
|
15335
15466
|
|
|
15336
15467
|
// src/telephony/plivo.ts
|
|
15337
15468
|
import { Buffer as Buffer5 } from "buffer";
|
|
15338
|
-
import { Elysia as
|
|
15469
|
+
import { Elysia as Elysia26 } from "elysia";
|
|
15339
15470
|
|
|
15340
15471
|
// src/telephony/contract.ts
|
|
15341
15472
|
var DEFAULT_REQUIREMENTS = [
|
|
@@ -15419,7 +15550,7 @@ var evaluateVoiceTelephonyContract = (input) => {
|
|
|
15419
15550
|
|
|
15420
15551
|
// src/telephony/twilio.ts
|
|
15421
15552
|
import { Buffer as Buffer4 } from "buffer";
|
|
15422
|
-
import { Elysia as
|
|
15553
|
+
import { Elysia as Elysia25 } from "elysia";
|
|
15423
15554
|
var TWILIO_MULAW_SAMPLE_RATE = 8000;
|
|
15424
15555
|
var VOICE_PCM_SAMPLE_RATE = 16000;
|
|
15425
15556
|
var escapeXml2 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
@@ -15449,7 +15580,7 @@ var resolveTwilioStreamParameters = async (parameters, input) => {
|
|
|
15449
15580
|
return parameters;
|
|
15450
15581
|
};
|
|
15451
15582
|
var joinUrlPath2 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
15452
|
-
var
|
|
15583
|
+
var escapeHtml27 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
15453
15584
|
var getWebhookVerificationUrl = (webhook, input) => {
|
|
15454
15585
|
if (!webhook?.verificationUrl) {
|
|
15455
15586
|
return;
|
|
@@ -15492,23 +15623,23 @@ var buildTwilioVoiceSetupStatus = async (options, input) => {
|
|
|
15492
15623
|
};
|
|
15493
15624
|
var renderTwilioVoiceSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
15494
15625
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Twilio setup</p>
|
|
15495
|
-
<h1>${
|
|
15626
|
+
<h1>${escapeHtml27(title)}</h1>
|
|
15496
15627
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
15497
15628
|
<section>
|
|
15498
15629
|
<h2>URLs</h2>
|
|
15499
15630
|
<ul>
|
|
15500
|
-
<li><strong>TwiML:</strong> <code>${
|
|
15501
|
-
<li><strong>Media stream:</strong> <code>${
|
|
15502
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
15631
|
+
<li><strong>TwiML:</strong> <code>${escapeHtml27(status.urls.twiml)}</code></li>
|
|
15632
|
+
<li><strong>Media stream:</strong> <code>${escapeHtml27(status.urls.stream)}</code></li>
|
|
15633
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml27(status.urls.webhook)}</code></li>
|
|
15503
15634
|
</ul>
|
|
15504
15635
|
</section>
|
|
15505
15636
|
<section>
|
|
15506
15637
|
<h2>Signing</h2>
|
|
15507
15638
|
<p>Mode: <code>${status.signing.mode}</code></p>
|
|
15508
|
-
${status.signing.verificationUrl ? `<p>Verification URL: <code>${
|
|
15639
|
+
${status.signing.verificationUrl ? `<p>Verification URL: <code>${escapeHtml27(status.signing.verificationUrl)}</code></p>` : ""}
|
|
15509
15640
|
</section>
|
|
15510
|
-
${status.missing.length ? `<section><h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
15511
|
-
${status.warnings.length ? `<section><h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
15641
|
+
${status.missing.length ? `<section><h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml27(name)}</code></li>`).join("")}</ul></section>` : ""}
|
|
15642
|
+
${status.warnings.length ? `<section><h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml27(warning)}</li>`).join("")}</ul></section>` : ""}
|
|
15512
15643
|
</main>`;
|
|
15513
15644
|
var extractTwilioStreamUrl = (twiml) => twiml.match(/<Stream\b[^>]*\surl="([^"]+)"/i)?.[1]?.replaceAll("&", "&");
|
|
15514
15645
|
var createSmokeCheck = (name, status, message, details) => ({
|
|
@@ -15519,20 +15650,20 @@ var createSmokeCheck = (name, status, message, details) => ({
|
|
|
15519
15650
|
});
|
|
15520
15651
|
var renderTwilioVoiceSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
15521
15652
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Twilio smoke test</p>
|
|
15522
|
-
<h1>${
|
|
15653
|
+
<h1>${escapeHtml27(title)}</h1>
|
|
15523
15654
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
15524
15655
|
<section>
|
|
15525
15656
|
<h2>Checks</h2>
|
|
15526
15657
|
<ul>
|
|
15527
|
-
${report.checks.map((check) => `<li><strong>${
|
|
15658
|
+
${report.checks.map((check) => `<li><strong>${escapeHtml27(check.name)}</strong>: ${escapeHtml27(check.status)}${check.message ? ` - ${escapeHtml27(check.message)}` : ""}</li>`).join("")}
|
|
15528
15659
|
</ul>
|
|
15529
15660
|
</section>
|
|
15530
15661
|
<section>
|
|
15531
15662
|
<h2>Observed URLs</h2>
|
|
15532
15663
|
<ul>
|
|
15533
|
-
<li><strong>TwiML:</strong> <code>${
|
|
15534
|
-
<li><strong>Stream:</strong> <code>${
|
|
15535
|
-
<li><strong>Webhook:</strong> <code>${
|
|
15664
|
+
<li><strong>TwiML:</strong> <code>${escapeHtml27(report.setup.urls.twiml)}</code></li>
|
|
15665
|
+
<li><strong>Stream:</strong> <code>${escapeHtml27(report.twiml?.streamUrl ?? report.setup.urls.stream)}</code></li>
|
|
15666
|
+
<li><strong>Webhook:</strong> <code>${escapeHtml27(report.setup.urls.webhook)}</code></li>
|
|
15536
15667
|
</ul>
|
|
15537
15668
|
</section>
|
|
15538
15669
|
</main>`;
|
|
@@ -15992,7 +16123,7 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
15992
16123
|
const smokePath = options.smoke?.path === false ? false : options.smoke?.path ?? "/api/voice/twilio/smoke";
|
|
15993
16124
|
const bridges = new WeakMap;
|
|
15994
16125
|
const webhookPolicy = options.webhook?.policy ?? options.outcomePolicy ?? createVoiceTelephonyOutcomePolicy();
|
|
15995
|
-
const app = new
|
|
16126
|
+
const app = new Elysia25({
|
|
15996
16127
|
name: options.name ?? "absolutejs-voice-twilio"
|
|
15997
16128
|
}).get(twimlPath, async ({ query, request }) => {
|
|
15998
16129
|
const streamUrl = await resolveTwilioStreamUrl(options, {
|
|
@@ -16129,7 +16260,7 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
16129
16260
|
|
|
16130
16261
|
// src/telephony/plivo.ts
|
|
16131
16262
|
var escapeXml3 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
16132
|
-
var
|
|
16263
|
+
var escapeHtml28 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
16133
16264
|
var joinUrlPath3 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
16134
16265
|
var resolveRequestOrigin2 = (request) => {
|
|
16135
16266
|
const url = new URL(request.url);
|
|
@@ -16380,21 +16511,21 @@ var buildPlivoVoiceSetupStatus = async (options, input) => {
|
|
|
16380
16511
|
};
|
|
16381
16512
|
var renderPlivoSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
16382
16513
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Plivo setup</p>
|
|
16383
|
-
<h1>${
|
|
16514
|
+
<h1>${escapeHtml28(title)}</h1>
|
|
16384
16515
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
16385
16516
|
<ul>
|
|
16386
|
-
<li><strong>Answer XML:</strong> <code>${
|
|
16387
|
-
<li><strong>Audio stream:</strong> <code>${
|
|
16388
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
16517
|
+
<li><strong>Answer XML:</strong> <code>${escapeHtml28(status.urls.answer)}</code></li>
|
|
16518
|
+
<li><strong>Audio stream:</strong> <code>${escapeHtml28(status.urls.stream)}</code></li>
|
|
16519
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml28(status.urls.webhook)}</code></li>
|
|
16389
16520
|
</ul>
|
|
16390
|
-
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
16391
|
-
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
16521
|
+
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml28(name)}</code></li>`).join("")}</ul>` : ""}
|
|
16522
|
+
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml28(warning)}</li>`).join("")}</ul>` : ""}
|
|
16392
16523
|
</main>`;
|
|
16393
16524
|
var renderPlivoSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
16394
16525
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Plivo smoke test</p>
|
|
16395
|
-
<h1>${
|
|
16526
|
+
<h1>${escapeHtml28(title)}</h1>
|
|
16396
16527
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
16397
|
-
<ul>${report.checks.map((check) => `<li><strong>${
|
|
16528
|
+
<ul>${report.checks.map((check) => `<li><strong>${escapeHtml28(check.name)}</strong>: ${escapeHtml28(check.status)}${check.message ? ` - ${escapeHtml28(check.message)}` : ""}</li>`).join("")}</ul>
|
|
16398
16529
|
</main>`;
|
|
16399
16530
|
var runPlivoSmokeTest = async (input) => {
|
|
16400
16531
|
const setup = await buildPlivoVoiceSetupStatus(input.options, input);
|
|
@@ -16489,7 +16620,7 @@ var createPlivoVoiceRoutes = (options = {}) => {
|
|
|
16489
16620
|
request: input.request
|
|
16490
16621
|
}) : verificationUrl ?? input.request.url
|
|
16491
16622
|
}) : undefined);
|
|
16492
|
-
const app = new
|
|
16623
|
+
const app = new Elysia26({
|
|
16493
16624
|
name: options.name ?? "absolutejs-voice-plivo"
|
|
16494
16625
|
}).get(answerPath, async ({ query, request }) => {
|
|
16495
16626
|
const streamUrl = await resolvePlivoStreamUrl(options, {
|
|
@@ -16600,9 +16731,9 @@ var createPlivoVoiceRoutes = (options = {}) => {
|
|
|
16600
16731
|
|
|
16601
16732
|
// src/telephony/telnyx.ts
|
|
16602
16733
|
import { Buffer as Buffer6 } from "buffer";
|
|
16603
|
-
import { Elysia as
|
|
16734
|
+
import { Elysia as Elysia27 } from "elysia";
|
|
16604
16735
|
var escapeXml4 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
16605
|
-
var
|
|
16736
|
+
var escapeHtml29 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
16606
16737
|
var joinUrlPath4 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
16607
16738
|
var resolveRequestOrigin3 = (request) => {
|
|
16608
16739
|
const url = new URL(request.url);
|
|
@@ -16803,21 +16934,21 @@ var buildTelnyxVoiceSetupStatus = async (options, input) => {
|
|
|
16803
16934
|
};
|
|
16804
16935
|
var renderTelnyxSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
16805
16936
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Telnyx setup</p>
|
|
16806
|
-
<h1>${
|
|
16937
|
+
<h1>${escapeHtml29(title)}</h1>
|
|
16807
16938
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
16808
16939
|
<ul>
|
|
16809
|
-
<li><strong>TeXML:</strong> <code>${
|
|
16810
|
-
<li><strong>Media stream:</strong> <code>${
|
|
16811
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
16940
|
+
<li><strong>TeXML:</strong> <code>${escapeHtml29(status.urls.texml)}</code></li>
|
|
16941
|
+
<li><strong>Media stream:</strong> <code>${escapeHtml29(status.urls.stream)}</code></li>
|
|
16942
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml29(status.urls.webhook)}</code></li>
|
|
16812
16943
|
</ul>
|
|
16813
|
-
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
16814
|
-
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
16944
|
+
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml29(name)}</code></li>`).join("")}</ul>` : ""}
|
|
16945
|
+
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml29(warning)}</li>`).join("")}</ul>` : ""}
|
|
16815
16946
|
</main>`;
|
|
16816
16947
|
var renderTelnyxSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
16817
16948
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Telnyx smoke test</p>
|
|
16818
|
-
<h1>${
|
|
16949
|
+
<h1>${escapeHtml29(title)}</h1>
|
|
16819
16950
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
16820
|
-
<ul>${report.checks.map((check) => `<li><strong>${
|
|
16951
|
+
<ul>${report.checks.map((check) => `<li><strong>${escapeHtml29(check.name)}</strong>: ${escapeHtml29(check.status)}${check.message ? ` - ${escapeHtml29(check.message)}` : ""}</li>`).join("")}</ul>
|
|
16821
16952
|
</main>`;
|
|
16822
16953
|
var runTelnyxSmokeTest = async (input) => {
|
|
16823
16954
|
const setup = await buildTelnyxVoiceSetupStatus(input.options, input);
|
|
@@ -16911,7 +17042,7 @@ var createTelnyxVoiceRoutes = (options = {}) => {
|
|
|
16911
17042
|
publicKey: options.webhook?.publicKey,
|
|
16912
17043
|
toleranceSeconds: options.webhook?.toleranceSeconds
|
|
16913
17044
|
}) : undefined);
|
|
16914
|
-
const app = new
|
|
17045
|
+
const app = new Elysia27({
|
|
16915
17046
|
name: options.name ?? "absolutejs-voice-telnyx"
|
|
16916
17047
|
}).get(texmlPath, async ({ query, request }) => {
|
|
16917
17048
|
const streamUrl = await resolveTelnyxStreamUrl(options, {
|
|
@@ -17021,8 +17152,8 @@ var createTelnyxVoiceRoutes = (options = {}) => {
|
|
|
17021
17152
|
};
|
|
17022
17153
|
|
|
17023
17154
|
// src/telephony/matrix.ts
|
|
17024
|
-
import { Elysia as
|
|
17025
|
-
var
|
|
17155
|
+
import { Elysia as Elysia28 } from "elysia";
|
|
17156
|
+
var escapeHtml30 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
17026
17157
|
var labelForProvider = (provider) => provider.split("-").map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`).join(" ");
|
|
17027
17158
|
var resolveEntryStatus = (contract, setup, smoke) => {
|
|
17028
17159
|
if (!contract.pass || !setup.ready || smoke?.pass === false) {
|
|
@@ -17083,13 +17214,13 @@ var badgeStyles = {
|
|
|
17083
17214
|
};
|
|
17084
17215
|
var renderVoiceTelephonyCarrierMatrixHTML = (matrix, options = {}) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 1040px; margin: 40px auto; padding: 0 20px; color: #172033;">
|
|
17085
17216
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Carrier matrix</p>
|
|
17086
|
-
<h1 style="font-size: 34px; margin: 0 0 8px;">${
|
|
17217
|
+
<h1 style="font-size: 34px; margin: 0 0 8px;">${escapeHtml30(options.title ?? "AbsoluteJS Voice Carrier Matrix")}</h1>
|
|
17087
17218
|
<p style="color:#52606d; margin: 0 0 24px;">${matrix.summary.ready}/${matrix.summary.providers} ready, ${matrix.summary.contractsPassing}/${matrix.summary.providers} contract passing, ${matrix.summary.smokePassing}/${matrix.summary.providers} smoke passing.</p>
|
|
17088
17219
|
<section style="display:grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px;">
|
|
17089
17220
|
${matrix.entries.map((entry) => `<article style="border:1px solid #d9e2ec; border-radius:18px; padding:18px; background:#fff; box-shadow:0 18px 48px rgba(15,23,42,.08);">
|
|
17090
17221
|
<div style="display:flex; justify-content:space-between; gap:12px; align-items:center;">
|
|
17091
|
-
<h2 style="margin:0; font-size:20px;">${
|
|
17092
|
-
<span style="border:1px solid; border-radius:999px; padding:4px 10px; font-size:12px; font-weight:700; ${badgeStyles[entry.status]}">${
|
|
17222
|
+
<h2 style="margin:0; font-size:20px;">${escapeHtml30(entry.name)}</h2>
|
|
17223
|
+
<span style="border:1px solid; border-radius:999px; padding:4px 10px; font-size:12px; font-weight:700; ${badgeStyles[entry.status]}">${escapeHtml30(entry.status.toUpperCase())}</span>
|
|
17093
17224
|
</div>
|
|
17094
17225
|
<dl style="display:grid; grid-template-columns: 1fr 1fr; gap:8px 12px; margin:16px 0;">
|
|
17095
17226
|
<dt style="color:#64748b;">Setup</dt><dd style="margin:0; font-weight:700;">${entry.ready ? "Ready" : "Needs attention"}</dd>
|
|
@@ -17097,15 +17228,15 @@ ${matrix.entries.map((entry) => `<article style="border:1px solid #d9e2ec; borde
|
|
|
17097
17228
|
<dt style="color:#64748b;">Smoke</dt><dd style="margin:0; font-weight:700;">${entry.smoke ? entry.smoke.pass ? "Pass" : "Fail" : "Missing"}</dd>
|
|
17098
17229
|
<dt style="color:#64748b;">Contract</dt><dd style="margin:0; font-weight:700;">${entry.contract.pass ? "Pass" : "Fail"}</dd>
|
|
17099
17230
|
</dl>
|
|
17100
|
-
<p style="margin:0 0 8px; color:#475569;"><strong>Stream:</strong> <code>${
|
|
17101
|
-
<p style="margin:0 0 12px; color:#475569;"><strong>Webhook:</strong> <code>${
|
|
17102
|
-
${entry.issues.length ? `<ul style="margin:12px 0 0; padding-left:18px;">${entry.issues.map((issue) => `<li>${
|
|
17231
|
+
<p style="margin:0 0 8px; color:#475569;"><strong>Stream:</strong> <code>${escapeHtml30(entry.setup.urls.stream || "missing")}</code></p>
|
|
17232
|
+
<p style="margin:0 0 12px; color:#475569;"><strong>Webhook:</strong> <code>${escapeHtml30(entry.setup.urls.webhook || "missing")}</code></p>
|
|
17233
|
+
${entry.issues.length ? `<ul style="margin:12px 0 0; padding-left:18px;">${entry.issues.map((issue) => `<li>${escapeHtml30(issue.severity)}: ${escapeHtml30(issue.message)}</li>`).join("")}</ul>` : '<p style="margin:12px 0 0; color:#166534;">No contract issues.</p>'}
|
|
17103
17234
|
</article>`).join("")}
|
|
17104
17235
|
</section>
|
|
17105
17236
|
</main>`;
|
|
17106
17237
|
var createVoiceTelephonyCarrierMatrixRoutes = (options) => {
|
|
17107
17238
|
const path = options.path ?? "/api/voice/telephony/carriers";
|
|
17108
|
-
return new
|
|
17239
|
+
return new Elysia28({
|
|
17109
17240
|
name: options.name ?? "absolutejs-voice-telephony-carrier-matrix"
|
|
17110
17241
|
}).get(path, async ({ query, request }) => {
|
|
17111
17242
|
const providers = await options.load({ query, request });
|
|
@@ -17127,7 +17258,7 @@ var createVoiceTelephonyCarrierMatrixRoutes = (options) => {
|
|
|
17127
17258
|
};
|
|
17128
17259
|
|
|
17129
17260
|
// src/phoneAgentProductionSmoke.ts
|
|
17130
|
-
import { Elysia as
|
|
17261
|
+
import { Elysia as Elysia29 } from "elysia";
|
|
17131
17262
|
var defaultRequirements = [
|
|
17132
17263
|
"media-started",
|
|
17133
17264
|
"transcript",
|
|
@@ -17135,7 +17266,7 @@ var defaultRequirements = [
|
|
|
17135
17266
|
"lifecycle-outcome",
|
|
17136
17267
|
"no-session-error"
|
|
17137
17268
|
];
|
|
17138
|
-
var
|
|
17269
|
+
var escapeHtml31 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
17139
17270
|
var payloadType = (event) => typeof event.payload.type === "string" ? event.payload.type : undefined;
|
|
17140
17271
|
var hasTextPayload = (event) => ["text", "assistantText", "transcript"].some((key) => {
|
|
17141
17272
|
const value = event.payload[key];
|
|
@@ -17244,10 +17375,10 @@ var resolveHandlerOptions = async (options, input) => ({
|
|
|
17244
17375
|
});
|
|
17245
17376
|
var renderVoicePhoneAgentProductionSmokeHTML = (report, options = {}) => {
|
|
17246
17377
|
const title = options.title ?? "AbsoluteJS Voice Phone Smoke Contract";
|
|
17247
|
-
const issues = report.issues.map((issue) => `<li><strong>${
|
|
17248
|
-
const outcomes = report.observed.lifecycleOutcomes.map((outcome) => `<span class="pill">${
|
|
17249
|
-
const requirements = report.required.map((requirement) => `<span class="pill">${
|
|
17250
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
17378
|
+
const issues = report.issues.map((issue) => `<li><strong>${escapeHtml31(issue.requirement)}</strong>: ${escapeHtml31(issue.message)}</li>`).join("");
|
|
17379
|
+
const outcomes = report.observed.lifecycleOutcomes.map((outcome) => `<span class="pill">${escapeHtml31(outcome)}</span>`).join("");
|
|
17380
|
+
const requirements = report.required.map((requirement) => `<span class="pill">${escapeHtml31(requirement)}</span>`).join("");
|
|
17381
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml31(title)}</title><style>body{background:#0e141b;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1050px;padding:32px}.hero,.panel{background:#151d26;border:1px solid #283544;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12))}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.status{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.pass{color:#86efac}.fail{color:#fca5a5}.grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.metric{background:#0f151d;border:1px solid #283544;border-radius:16px;padding:14px}.metric strong{display:block;font-size:1.8rem}.pill{background:#0f151d;border:1px solid #3f3f46;border-radius:999px;display:inline-flex;margin:4px;padding:7px 10px}.issues{color:#fca5a5}code{color:#fde68a}@media(max-width:720px){main{padding:18px}}</style></head><body><main><section class="hero"><p class="eyebrow">Phone agent production smoke</p><h1>${escapeHtml31(title)}</h1><p class="status ${report.pass ? "pass" : "fail"}">${report.pass ? "PASS" : "FAIL"}</p><p>Contract <code>${escapeHtml31(report.contractId)}</code>${report.provider ? ` for <code>${escapeHtml31(report.provider)}</code>` : ""}${report.sessionId ? ` on session <code>${escapeHtml31(report.sessionId)}</code>` : ""}.</p></section><section class="panel"><h2>Observed Trace Evidence</h2><div class="grid"><div class="metric"><span>Media starts</span><strong>${String(report.observed.mediaStarts)}</strong></div><div class="metric"><span>Transcripts</span><strong>${String(report.observed.transcripts)}</strong></div><div class="metric"><span>Assistant responses</span><strong>${String(report.observed.assistantResponses)}</strong></div><div class="metric"><span>Session errors</span><strong>${String(report.observed.sessionErrors)}</strong></div></div><p>${outcomes || '<span class="pill">No lifecycle outcome</span>'}</p></section><section class="panel"><h2>Requirements</h2><p>${requirements}</p>${issues ? `<ul class="issues">${issues}</ul>` : '<p class="pass">All required phone-agent smoke evidence is present.</p>'}</section></main></body></html>`;
|
|
17251
17382
|
};
|
|
17252
17383
|
var createVoicePhoneAgentProductionSmokeJSONHandler = (options) => async ({
|
|
17253
17384
|
query,
|
|
@@ -17270,7 +17401,7 @@ var createVoicePhoneAgentProductionSmokeHTMLHandler = (options) => async ({
|
|
|
17270
17401
|
var createVoicePhoneAgentProductionSmokeRoutes = (options) => {
|
|
17271
17402
|
const path = options.path ?? "/api/voice/phone/smoke-contract";
|
|
17272
17403
|
const htmlPath = options.htmlPath === undefined ? "/voice/phone/smoke-contract" : options.htmlPath;
|
|
17273
|
-
const routes = new
|
|
17404
|
+
const routes = new Elysia29({
|
|
17274
17405
|
name: options.name ?? "absolutejs-voice-phone-smoke-contract"
|
|
17275
17406
|
}).get(path, createVoicePhoneAgentProductionSmokeJSONHandler(options));
|
|
17276
17407
|
if (htmlPath) {
|
|
@@ -17313,7 +17444,7 @@ var PHONE_AGENT_LIFECYCLE_STAGES = [
|
|
|
17313
17444
|
"completed",
|
|
17314
17445
|
"failed"
|
|
17315
17446
|
];
|
|
17316
|
-
var
|
|
17447
|
+
var escapeHtml32 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
17317
17448
|
var loadRouteJson = async (input) => {
|
|
17318
17449
|
const response = await input.app.handle(new Request(new URL(input.path, input.origin).toString(), {
|
|
17319
17450
|
headers: {
|
|
@@ -17373,18 +17504,18 @@ var renderVoicePhoneAgentSetupHTML = (report) => {
|
|
|
17373
17504
|
const entry = report.matrix?.entries.find((candidate) => candidate.provider === carrier.provider && (candidate.name === carrier.name || candidate.name === (carrier.name ?? carrier.provider)));
|
|
17374
17505
|
const urls = entry?.setup.urls;
|
|
17375
17506
|
const primaryUrl = carrier.provider === "plivo" ? urls?.twiml : urls?.twiml;
|
|
17376
|
-
return `<tr><td>${
|
|
17507
|
+
return `<tr><td>${escapeHtml32(carrier.name ?? carrier.provider)}</td><td>${escapeHtml32(carrier.provider)}</td><td><code>${escapeHtml32(carrier.setupPath || "disabled")}</code></td><td><code>${escapeHtml32(carrier.smokePath || "disabled")}</code></td><td>${entry ? `<span class="${escapeHtml32(entry.status)}">${escapeHtml32(entry.status.toUpperCase())}</span>` : "unknown"}</td><td>${primaryUrl ? `<code>${escapeHtml32(primaryUrl)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.webhook ? `<code>${escapeHtml32(urls.webhook)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.stream ? `<code>${escapeHtml32(urls.stream)}</code>` : '<span class="muted">missing</span>'}</td></tr>`;
|
|
17377
17508
|
}).join("");
|
|
17378
|
-
const stageList = report.lifecycleStages.map((stage) => `<li><code>${
|
|
17509
|
+
const stageList = report.lifecycleStages.map((stage) => `<li><code>${escapeHtml32(stage)}</code></li>`).join("");
|
|
17379
17510
|
const checklist = report.carriers.map((carrier) => {
|
|
17380
17511
|
const entry = report.matrix?.entries.find((candidate) => candidate.provider === carrier.provider && (candidate.name === carrier.name || candidate.name === (carrier.name ?? carrier.provider)));
|
|
17381
17512
|
const urls = entry?.setup.urls;
|
|
17382
17513
|
const answerLabel = carrier.provider === "telnyx" ? "TeXML URL" : carrier.provider === "plivo" ? "Answer URL" : "TwiML URL";
|
|
17383
17514
|
const answerUrl = urls?.twiml;
|
|
17384
|
-
const issueList = entry?.issues.map((issue) => `<li>${
|
|
17385
|
-
return `<article><h3>${
|
|
17515
|
+
const issueList = entry?.issues.map((issue) => `<li>${escapeHtml32(issue.severity)}: ${escapeHtml32(issue.message)}</li>`).join("") ?? "";
|
|
17516
|
+
return `<article><h3>${escapeHtml32(carrier.name ?? carrier.provider)}</h3><ol><li>Set ${escapeHtml32(answerLabel)} to <code>${escapeHtml32(answerUrl ?? "missing")}</code>.</li><li>Set status webhook to <code>${escapeHtml32(urls?.webhook ?? "missing")}</code>.</li><li>Allow media stream URL <code>${escapeHtml32(urls?.stream ?? "missing")}</code>.</li><li>Open setup: ${carrier.setupPath ? `<a href="${escapeHtml32(carrier.setupPath)}?format=html">${escapeHtml32(carrier.setupPath)}</a>` : '<span class="muted">disabled</span>'}.</li><li>Run smoke: ${carrier.smokePath ? `<a href="${escapeHtml32(carrier.smokePath)}?format=html">${escapeHtml32(carrier.smokePath)}</a>` : '<span class="muted">disabled</span>'}.</li>${report.productionSmokePath ? `<li>Certify production smoke traces: <a href="${escapeHtml32(report.productionSmokePath.replace("/api/", "/"))}?sessionId=">${escapeHtml32(report.productionSmokePath)}</a>.</li>` : ""}</ol>${issueList ? `<ul class="issues">${issueList}</ul>` : '<p class="pass">No carrier contract issues.</p>'}</article>`;
|
|
17386
17517
|
}).join("");
|
|
17387
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
17518
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml32(report.title)}</title><style>body{background:#10151c;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.2),rgba(245,158,11,.12));border:1px solid #283544;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.badge{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;padding:8px 12px}.pass{color:#86efac}.fail{color:#fca5a5}.warn{color:#fde68a}.muted{color:#aab5c0}table{background:#151d27;border:1px solid #283544;border-collapse:collapse;border-radius:18px;display:block;overflow:auto;width:100%}td,th{border-bottom:1px solid #283544;padding:12px;text-align:left;vertical-align:top}code{color:#fde68a;overflow-wrap:anywhere}.checklist{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));margin:18px 0}.checklist article{background:#151d27;border:1px solid #283544;border-radius:18px;padding:18px}.checklist ol{padding-left:20px}.issues{color:#fca5a5}.stages{display:grid;gap:8px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));padding-left:18px}a{color:#5eead4}</style></head><body><main><section class="hero"><p class="eyebrow">Phone agent setup</p><h1>${escapeHtml32(report.title)}</h1><p>One self-hosted entrypoint for carrier routes, setup reports, smoke checks, and normalized call lifecycle stages.</p><p class="badge ${report.ready ? "pass" : "fail"}">Ready: ${String(report.ready)}</p>${report.matrixPath ? `<p><a href="${escapeHtml32(report.matrixPath)}?format=html">Open carrier matrix</a></p>` : ""}</section><h2>Carrier Setup Checklist</h2><section class="checklist">${checklist}</section><h2>Carrier URLs</h2><table><thead><tr><th>Name</th><th>Provider</th><th>Setup</th><th>Smoke</th><th>Status</th><th>Answer/TwiML/TeXML</th><th>Webhook</th><th>Stream</th></tr></thead><tbody>${carrierRows}</tbody></table><h2>Lifecycle Schema</h2><ul class="stages">${stageList}</ul></main></body></html>`;
|
|
17388
17519
|
};
|
|
17389
17520
|
var createVoicePhoneAgent = (options) => {
|
|
17390
17521
|
const carrierSummaries = options.carriers.map((carrier) => ({
|
|
@@ -17393,7 +17524,7 @@ var createVoicePhoneAgent = (options) => {
|
|
|
17393
17524
|
setupPath: resolveSetupPath(carrier),
|
|
17394
17525
|
smokePath: resolveSmokePath(carrier)
|
|
17395
17526
|
}));
|
|
17396
|
-
const app = new
|
|
17527
|
+
const app = new Elysia30({
|
|
17397
17528
|
name: options.name ?? "absolutejs-voice-phone-agent"
|
|
17398
17529
|
});
|
|
17399
17530
|
for (const carrier of options.carriers) {
|
|
@@ -19405,8 +19536,8 @@ var createOpenAIVoiceTTS = (options) => {
|
|
|
19405
19536
|
};
|
|
19406
19537
|
};
|
|
19407
19538
|
// src/providerCapabilities.ts
|
|
19408
|
-
import { Elysia as
|
|
19409
|
-
var
|
|
19539
|
+
import { Elysia as Elysia31 } from "elysia";
|
|
19540
|
+
var escapeHtml33 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
19410
19541
|
var fromProviderList = (kind, providers, options) => (providers ?? []).map((provider) => ({
|
|
19411
19542
|
configured: true,
|
|
19412
19543
|
features: options.features?.[provider],
|
|
@@ -19469,27 +19600,27 @@ var summarizeVoiceProviderCapabilities = async (options) => {
|
|
|
19469
19600
|
var renderVoiceProviderCapabilityHTML = (report, options = {}) => {
|
|
19470
19601
|
const title = options.title ?? "Voice Provider Capabilities";
|
|
19471
19602
|
const cards = report.capabilities.map((capability) => {
|
|
19472
|
-
const features = (capability.features ?? []).map((feature) => `<span class="pill">${
|
|
19473
|
-
return `<article class="card ${
|
|
19603
|
+
const features = (capability.features ?? []).map((feature) => `<span class="pill">${escapeHtml33(feature)}</span>`).join("");
|
|
19604
|
+
return `<article class="card ${escapeHtml33(capability.status)}">
|
|
19474
19605
|
<div class="card-header">
|
|
19475
19606
|
<div>
|
|
19476
|
-
<p class="eyebrow">${
|
|
19477
|
-
<h2>${
|
|
19607
|
+
<p class="eyebrow">${escapeHtml33(capability.kind)}</p>
|
|
19608
|
+
<h2>${escapeHtml33(capability.label ?? capability.provider)}</h2>
|
|
19478
19609
|
</div>
|
|
19479
|
-
<strong>${
|
|
19610
|
+
<strong>${escapeHtml33(capability.status)}</strong>
|
|
19480
19611
|
</div>
|
|
19481
|
-
${capability.description ? `<p>${
|
|
19612
|
+
${capability.description ? `<p>${escapeHtml33(capability.description)}</p>` : ""}
|
|
19482
19613
|
<dl>
|
|
19483
19614
|
<div><dt>Configured</dt><dd>${capability.configured ? "yes" : "no"}</dd></div>
|
|
19484
19615
|
<div><dt>Selected</dt><dd>${capability.selected ? "yes" : "no"}</dd></div>
|
|
19485
|
-
<div><dt>Model</dt><dd>${
|
|
19616
|
+
<div><dt>Model</dt><dd>${escapeHtml33(capability.model ?? "default")}</dd></div>
|
|
19486
19617
|
<div><dt>Runs</dt><dd>${String(capability.health?.runCount ?? 0)}</dd></div>
|
|
19487
19618
|
<div><dt>Errors</dt><dd>${String(capability.health?.errorCount ?? 0)}</dd></div>
|
|
19488
19619
|
</dl>
|
|
19489
19620
|
${features ? `<div class="features">${features}</div>` : ""}
|
|
19490
19621
|
</article>`;
|
|
19491
19622
|
}).join("");
|
|
19492
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
19623
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml33(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.card{background:#181d22;border:1px solid #2a323a;border-radius:20px;margin-bottom:16px;padding:20px}.hero{background:linear-gradient(135deg,rgba(14,165,233,.16),rgba(34,197,94,.12))}.eyebrow{color:#7dd3fc;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0 1rem}.summary,.features{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0f1217;border:1px solid #3f3f46;border-radius:999px;padding:7px 10px}.grid{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}.card-header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.selected,.healthy{color:#86efac}.unconfigured,.degraded,.rate-limited,.suppressed{color:#fca5a5}.idle,.recoverable{color:#fde68a}dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr))}dt{color:#a8b0b8;font-size:.8rem}dd{margin:0}@media(max-width:800px){main{padding:18px}.card-header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Provider Discovery</p><h1>${escapeHtml33(title)}</h1><div class="summary"><span class="pill">${String(report.configured)} configured</span><span class="pill">${String(report.selected)} selected</span><span class="pill">${String(report.unconfigured)} missing</span><span class="pill">${String(report.total)} total</span></div></section><section class="grid">${cards || '<article class="card"><p>No provider capabilities configured.</p></article>'}</section></main></body></html>`;
|
|
19493
19624
|
};
|
|
19494
19625
|
var createVoiceProviderCapabilityJSONHandler = (options) => async () => summarizeVoiceProviderCapabilities(options);
|
|
19495
19626
|
var createVoiceProviderCapabilityHTMLHandler = (options) => async () => {
|
|
@@ -19506,7 +19637,7 @@ var createVoiceProviderCapabilityHTMLHandler = (options) => async () => {
|
|
|
19506
19637
|
var createVoiceProviderCapabilityRoutes = (options) => {
|
|
19507
19638
|
const path = options.path ?? "/api/provider-capabilities";
|
|
19508
19639
|
const htmlPath = options.htmlPath === undefined ? `${path}/htmx` : options.htmlPath;
|
|
19509
|
-
const routes = new
|
|
19640
|
+
const routes = new Elysia31({
|
|
19510
19641
|
name: options.name ?? "absolutejs-voice-provider-capabilities"
|
|
19511
19642
|
}).get(path, createVoiceProviderCapabilityJSONHandler(options));
|
|
19512
19643
|
if (htmlPath) {
|
|
@@ -19515,8 +19646,8 @@ var createVoiceProviderCapabilityRoutes = (options) => {
|
|
|
19515
19646
|
return routes;
|
|
19516
19647
|
};
|
|
19517
19648
|
// src/resilienceRoutes.ts
|
|
19518
|
-
import { Elysia as
|
|
19519
|
-
var
|
|
19649
|
+
import { Elysia as Elysia32 } from "elysia";
|
|
19650
|
+
var escapeHtml34 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
19520
19651
|
var getString11 = (value) => typeof value === "string" ? value : undefined;
|
|
19521
19652
|
var getNumber6 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
19522
19653
|
var getBoolean2 = (value) => value === true;
|
|
@@ -19663,13 +19794,13 @@ var summarizeRoutingEvents = (events) => {
|
|
|
19663
19794
|
};
|
|
19664
19795
|
var renderProviderCards = (title, providers) => {
|
|
19665
19796
|
if (providers.length === 0) {
|
|
19666
|
-
return `<p class="muted">No ${
|
|
19797
|
+
return `<p class="muted">No ${escapeHtml34(title)} provider health yet.</p>`;
|
|
19667
19798
|
}
|
|
19668
19799
|
return `<div class="provider-grid">${providers.map((provider) => `
|
|
19669
|
-
<article class="card provider ${
|
|
19800
|
+
<article class="card provider ${escapeHtml34(provider.status)}">
|
|
19670
19801
|
<div class="card-header">
|
|
19671
|
-
<strong>${
|
|
19672
|
-
<span>${
|
|
19802
|
+
<strong>${escapeHtml34(provider.provider)}</strong>
|
|
19803
|
+
<span>${escapeHtml34(provider.status)}${provider.recommended ? " \xB7 recommended" : ""}</span>
|
|
19673
19804
|
</div>
|
|
19674
19805
|
<dl>
|
|
19675
19806
|
<div><dt>Runs</dt><dd>${provider.runCount}</dd></div>
|
|
@@ -19678,7 +19809,7 @@ var renderProviderCards = (title, providers) => {
|
|
|
19678
19809
|
<div><dt>Timeouts</dt><dd>${provider.timeoutCount}</dd></div>
|
|
19679
19810
|
<div><dt>Fallbacks</dt><dd>${provider.fallbackCount}</dd></div>
|
|
19680
19811
|
</dl>
|
|
19681
|
-
${provider.lastError ? `<p class="muted">${
|
|
19812
|
+
${provider.lastError ? `<p class="muted">${escapeHtml34(provider.lastError)}</p>` : ""}
|
|
19682
19813
|
</article>
|
|
19683
19814
|
`).join("")}</div>`;
|
|
19684
19815
|
};
|
|
@@ -19687,24 +19818,24 @@ var renderTimeline2 = (events) => {
|
|
|
19687
19818
|
return '<p class="muted">No provider routing events yet. Run the app or simulate provider failover.</p>';
|
|
19688
19819
|
}
|
|
19689
19820
|
return `<div class="timeline">${events.slice(0, 40).map((event) => `
|
|
19690
|
-
<article class="card event ${
|
|
19821
|
+
<article class="card event ${escapeHtml34(event.status ?? "unknown")}">
|
|
19691
19822
|
<div class="card-header">
|
|
19692
|
-
<strong>${
|
|
19823
|
+
<strong>${escapeHtml34(event.kind.toUpperCase())} ${escapeHtml34(event.operation ?? "generate")}</strong>
|
|
19693
19824
|
<span>${new Date(event.at).toLocaleString()}</span>
|
|
19694
19825
|
</div>
|
|
19695
19826
|
<p>
|
|
19696
|
-
<span class="pill">${
|
|
19697
|
-
<span class="pill">provider: ${
|
|
19698
|
-
${event.fallbackProvider ? `<span class="pill">fallback: ${
|
|
19827
|
+
<span class="pill">${escapeHtml34(event.status ?? "unknown")}</span>
|
|
19828
|
+
<span class="pill">provider: ${escapeHtml34(event.provider ?? "unknown")}</span>
|
|
19829
|
+
${event.fallbackProvider ? `<span class="pill">fallback: ${escapeHtml34(event.fallbackProvider)}</span>` : ""}
|
|
19699
19830
|
${event.timedOut ? '<span class="pill danger">timed out</span>' : ""}
|
|
19700
19831
|
</p>
|
|
19701
19832
|
<dl>
|
|
19702
19833
|
<div><dt>Attempt</dt><dd>${event.attempt ?? 0}</dd></div>
|
|
19703
19834
|
<div><dt>Elapsed</dt><dd>${event.elapsedMs ?? 0}ms</dd></div>
|
|
19704
19835
|
<div><dt>Budget</dt><dd>${event.latencyBudgetMs ?? 0}ms</dd></div>
|
|
19705
|
-
<div><dt>Session</dt><dd>${
|
|
19836
|
+
<div><dt>Session</dt><dd>${escapeHtml34(event.sessionId)}</dd></div>
|
|
19706
19837
|
</dl>
|
|
19707
|
-
${event.error ? `<p class="muted">${
|
|
19838
|
+
${event.error ? `<p class="muted">${escapeHtml34(event.error)}</p>` : ""}
|
|
19708
19839
|
</article>
|
|
19709
19840
|
`).join("")}</div>`;
|
|
19710
19841
|
};
|
|
@@ -19714,9 +19845,9 @@ var renderSessionKind = (kind, summary) => {
|
|
|
19714
19845
|
const status = latest?.status ?? "idle";
|
|
19715
19846
|
const fallback = latest?.fallbackProvider && latest.fallbackProvider !== provider ? ` -> ${latest.fallbackProvider}` : "";
|
|
19716
19847
|
return `<div>
|
|
19717
|
-
<dt>${
|
|
19718
|
-
<dd>${
|
|
19719
|
-
<small>${
|
|
19848
|
+
<dt>${escapeHtml34(kind.toUpperCase())}</dt>
|
|
19849
|
+
<dd>${escapeHtml34(provider)}${escapeHtml34(fallback)}</dd>
|
|
19850
|
+
<small>${escapeHtml34(status)} \xB7 ${summary.runCount} event${summary.runCount === 1 ? "" : "s"} \xB7 ${summary.errorCount} error${summary.errorCount === 1 ? "" : "s"} \xB7 ${summary.fallbackCount} fallback${summary.fallbackCount === 1 ? "" : "s"}</small>
|
|
19720
19851
|
</div>`;
|
|
19721
19852
|
};
|
|
19722
19853
|
var renderSessionSummaries = (sessions) => {
|
|
@@ -19724,10 +19855,10 @@ var renderSessionSummaries = (sessions) => {
|
|
|
19724
19855
|
return '<p class="muted">No call-level routing summaries yet. Run a voice session or provider simulation.</p>';
|
|
19725
19856
|
}
|
|
19726
19857
|
return `<div class="session-grid">${sessions.slice(0, 12).map((session) => `
|
|
19727
|
-
<article class="card session ${
|
|
19858
|
+
<article class="card session ${escapeHtml34(session.status)}">
|
|
19728
19859
|
<div class="card-header">
|
|
19729
|
-
<strong>${
|
|
19730
|
-
<span>${
|
|
19860
|
+
<strong>${escapeHtml34(session.sessionId)}</strong>
|
|
19861
|
+
<span>${escapeHtml34(session.status)}</span>
|
|
19731
19862
|
</div>
|
|
19732
19863
|
<p>
|
|
19733
19864
|
<span class="pill">${session.eventCount} routing events</span>
|
|
@@ -19754,26 +19885,26 @@ var renderSimulationControls = (kind, simulation) => {
|
|
|
19754
19885
|
const pathPrefix = simulation.pathPrefix ?? `/api/${kind}-simulate`;
|
|
19755
19886
|
const failureProviders = simulation.failureProviders ?? configuredProviders.map(({ provider }) => provider);
|
|
19756
19887
|
const canFail = (provider) => configuredProviders.some((entry) => entry.provider === provider) && (!simulation.fallbackRequiredProvider || configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider));
|
|
19757
|
-
return `<div class="simulate-panel" data-sim-kind="${kind}" data-sim-prefix="${
|
|
19758
|
-
<p class="muted">${
|
|
19888
|
+
return `<div class="simulate-panel" data-sim-kind="${kind}" data-sim-prefix="${escapeHtml34(pathPrefix)}">
|
|
19889
|
+
<p class="muted">${escapeHtml34(simulation.failureMessage ?? `Simulate ${kind.toUpperCase()} provider failure without changing provider credentials.`)}</p>
|
|
19759
19890
|
<div class="simulate-actions">
|
|
19760
|
-
${failureProviders.map((provider) => `<button type="button" data-provider-fail="${
|
|
19761
|
-
${configuredProviders.map((provider) => `<button type="button" data-provider-recover="${
|
|
19891
|
+
${failureProviders.map((provider) => `<button type="button" data-provider-fail="${escapeHtml34(provider)}"${canFail(provider) ? "" : " disabled"}>Simulate ${escapeHtml34(provider)} ${kind.toUpperCase()} failure</button>`).join("")}
|
|
19892
|
+
${configuredProviders.map((provider) => `<button type="button" data-provider-recover="${escapeHtml34(provider.provider)}">Mark ${escapeHtml34(provider.provider)} recovered</button>`).join("")}
|
|
19762
19893
|
</div>
|
|
19763
|
-
${simulation.fallbackRequiredProvider && !configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider) ? `<p class="muted">${
|
|
19894
|
+
${simulation.fallbackRequiredProvider && !configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider) ? `<p class="muted">${escapeHtml34(simulation.fallbackRequiredMessage ?? `Configure ${simulation.fallbackRequiredProvider} to enable fallback simulation.`)}</p>` : ""}
|
|
19764
19895
|
<pre class="simulate-output" hidden></pre>
|
|
19765
19896
|
</div>`;
|
|
19766
19897
|
};
|
|
19767
19898
|
var renderVoiceResilienceHTML = (input) => {
|
|
19768
19899
|
const summary = summarizeRoutingEvents(input.routingEvents);
|
|
19769
|
-
const kindCounts = [...summary.byKind.entries()].map(([kind, count]) => `<span class="pill">${
|
|
19770
|
-
const links = input.links?.length ? input.links.map((link) => `<a href="${
|
|
19900
|
+
const kindCounts = [...summary.byKind.entries()].map(([kind, count]) => `<span class="pill">${escapeHtml34(kind)}: ${String(count)}</span>`).join("");
|
|
19901
|
+
const links = input.links?.length ? input.links.map((link) => `<a href="${escapeHtml34(link.href)}">${escapeHtml34(link.label)}</a>`).join(" \xB7 ") : "";
|
|
19771
19902
|
return `<!doctype html>
|
|
19772
19903
|
<html lang="en">
|
|
19773
19904
|
<head>
|
|
19774
19905
|
<meta charset="utf-8" />
|
|
19775
19906
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
19776
|
-
<title>${
|
|
19907
|
+
<title>${escapeHtml34(input.title ?? "AbsoluteJS Voice Resilience")}</title>
|
|
19777
19908
|
<style>
|
|
19778
19909
|
:root { color-scheme: dark; }
|
|
19779
19910
|
body { background: radial-gradient(circle at top left, #172554, #09090b 36%, #050505); color: #f4f4f5; font-family: ui-sans-serif, system-ui, sans-serif; margin: 0; padding: 24px; }
|
|
@@ -19916,7 +20047,7 @@ var registerSimulationRoutes = (routes, simulation, defaultPathPrefix) => {
|
|
|
19916
20047
|
};
|
|
19917
20048
|
var createVoiceResilienceRoutes = (options) => {
|
|
19918
20049
|
const path = options.path ?? "/resilience";
|
|
19919
|
-
const routes = new
|
|
20050
|
+
const routes = new Elysia32({
|
|
19920
20051
|
name: options.name ?? "absolutejs-voice-resilience"
|
|
19921
20052
|
}).get(path, async () => {
|
|
19922
20053
|
const events = await options.store.list();
|
|
@@ -19994,8 +20125,8 @@ var assertVoiceProviderRoutingContract = async (options) => {
|
|
|
19994
20125
|
return report;
|
|
19995
20126
|
};
|
|
19996
20127
|
// src/productionReadiness.ts
|
|
19997
|
-
import { Elysia as
|
|
19998
|
-
var
|
|
20128
|
+
import { Elysia as Elysia33 } from "elysia";
|
|
20129
|
+
var escapeHtml35 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
19999
20130
|
var rollupStatus2 = (checks) => checks.some((check) => check.status === "fail") ? "fail" : checks.some((check) => check.status === "warn") ? "warn" : "pass";
|
|
20000
20131
|
var carrierStatus = (matrix) => matrix.summary.failing > 0 ? "fail" : matrix.summary.warnings > 0 || matrix.summary.ready < matrix.summary.providers ? "warn" : "pass";
|
|
20001
20132
|
var resolveCarriers = async (options, input) => {
|
|
@@ -20670,25 +20801,25 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
20670
20801
|
var renderVoiceProductionReadinessHTML = (report, options = {}) => {
|
|
20671
20802
|
const title = options.title ?? "AbsoluteJS Voice Production Readiness";
|
|
20672
20803
|
const checks = report.checks.map((check, index) => {
|
|
20673
|
-
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${
|
|
20674
|
-
return `<article class="check ${
|
|
20804
|
+
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${escapeHtml35(action.href)}">${escapeHtml35(action.label)}</button>` : `<a href="${escapeHtml35(action.href)}">${escapeHtml35(action.label)}</a>`).join("");
|
|
20805
|
+
return `<article class="check ${escapeHtml35(check.status)}">
|
|
20675
20806
|
<div>
|
|
20676
|
-
<span>${
|
|
20677
|
-
<h2>${
|
|
20678
|
-
${check.detail ? `<p>${
|
|
20679
|
-
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${
|
|
20807
|
+
<span>${escapeHtml35(check.status.toUpperCase())}</span>
|
|
20808
|
+
<h2>${escapeHtml35(check.label)}</h2>
|
|
20809
|
+
${check.detail ? `<p>${escapeHtml35(check.detail)}</p>` : ""}
|
|
20810
|
+
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${escapeHtml35(check.proofSource.href)}">${escapeHtml35(check.proofSource.sourceLabel)}</a>` : escapeHtml35(check.proofSource.sourceLabel)}${check.proofSource.detail ? ` \xB7 ${escapeHtml35(check.proofSource.detail)}` : ""}</p>` : ""}
|
|
20680
20811
|
${actions ? `<p class="actions">${actions}</p>` : ""}
|
|
20681
20812
|
</div>
|
|
20682
|
-
<strong>${
|
|
20683
|
-
${check.href ? `<a href="${
|
|
20813
|
+
<strong>${escapeHtml35(String(check.value ?? check.status))}</strong>
|
|
20814
|
+
${check.href ? `<a href="${escapeHtml35(check.href)}">Open surface</a>` : ""}
|
|
20684
20815
|
</article>`;
|
|
20685
20816
|
}).join("");
|
|
20686
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
20817
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml35(title)}</title><style>body{background:#0c0f14;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1060px;padding:32px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#fbbf24;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.status{display:inline-flex;border:1px solid #3f3f46;border-radius:999px;padding:8px 12px}.status.pass,.check.pass{border-color:rgba(34,197,94,.55)}.status.warn,.check.warn{border-color:rgba(245,158,11,.65)}.status.fail,.check.fail{border-color:rgba(239,68,68,.75)}.checks{display:grid;gap:14px}.check{align-items:center;background:#141922;border:1px solid #26313d;border-radius:22px;display:grid;gap:16px;grid-template-columns:1fr auto auto;padding:18px}.check span{color:#a8b0b8;font-size:.78rem;font-weight:900;letter-spacing:.08em}.check h2{margin:.2rem 0}.check p{color:#b9c0c8;margin:.2rem 0 0}.check .proof-source{color:#f9d77e;font-weight:800}.check strong{font-size:1.5rem}.actions{display:flex;flex-wrap:wrap;gap:10px}.check a,a{color:#fbbf24}button{background:#fbbf24;border:0;border-radius:999px;color:#111827;cursor:pointer;font-weight:800;padding:9px 12px}button:disabled{cursor:wait;opacity:.65}@media(max-width:760px){main{padding:20px}.check{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Self-hosted readiness</p><h1>${escapeHtml35(title)}</h1><p>One deployable pass/fail report for quality gates, provider failover, session health, handoffs, routing evidence, and optional carrier readiness.</p><p class="status ${escapeHtml35(report.status)}">Overall: ${escapeHtml35(report.status.toUpperCase())}</p><p>Checked ${escapeHtml35(new Date(report.checkedAt).toLocaleString())}</p></section><section class="checks">${checks}</section></main><script>document.querySelectorAll("[data-readiness-action]").forEach((button)=>{button.addEventListener("click",async()=>{const url=button.getAttribute("data-action-url");if(!url)return;button.disabled=true;const original=button.textContent;button.textContent="Running...";try{const response=await fetch(url,{method:"POST"});button.textContent=response.ok?"Done. Reloading...":"Failed";if(response.ok)setTimeout(()=>location.reload(),500)}catch{button.textContent="Failed"}finally{setTimeout(()=>{button.disabled=false;button.textContent=original},1500)}})});</script></body></html>`;
|
|
20687
20818
|
};
|
|
20688
20819
|
var createVoiceProductionReadinessRoutes = (options) => {
|
|
20689
20820
|
const path = options.path ?? "/api/production-readiness";
|
|
20690
20821
|
const htmlPath = options.htmlPath ?? "/production-readiness";
|
|
20691
|
-
const routes = new
|
|
20822
|
+
const routes = new Elysia33({
|
|
20692
20823
|
name: options.name ?? "absolutejs-voice-production-readiness"
|
|
20693
20824
|
});
|
|
20694
20825
|
routes.get(path, async ({ query, request }) => buildVoiceProductionReadinessReport(options, { query, request }));
|
|
@@ -20710,7 +20841,7 @@ var createVoiceProductionReadinessRoutes = (options) => {
|
|
|
20710
20841
|
return routes;
|
|
20711
20842
|
};
|
|
20712
20843
|
// src/opsConsoleRoutes.ts
|
|
20713
|
-
import { Elysia as
|
|
20844
|
+
import { Elysia as Elysia34 } from "elysia";
|
|
20714
20845
|
var DEFAULT_LINKS = [
|
|
20715
20846
|
{
|
|
20716
20847
|
description: "Quality gates for CI, deploy checks, and production readiness.",
|
|
@@ -20745,7 +20876,7 @@ var DEFAULT_LINKS = [
|
|
|
20745
20876
|
label: "Handoffs"
|
|
20746
20877
|
}
|
|
20747
20878
|
];
|
|
20748
|
-
var
|
|
20879
|
+
var escapeHtml36 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
20749
20880
|
var countProviderStatuses = (providers) => {
|
|
20750
20881
|
const degradedStatuses = new Set(["degraded", "rate-limited", "suppressed"]);
|
|
20751
20882
|
const healthy = providers.filter((provider) => provider.status === "healthy").length;
|
|
@@ -20814,20 +20945,20 @@ var buildVoiceOpsConsoleReport = async (options) => {
|
|
|
20814
20945
|
trace
|
|
20815
20946
|
};
|
|
20816
20947
|
};
|
|
20817
|
-
var renderMetricCard = (input) => `<article class="metric"><span>${
|
|
20948
|
+
var renderMetricCard = (input) => `<article class="metric"><span>${escapeHtml36(input.label)}</span><strong>${escapeHtml36(String(input.value))}</strong>${input.status ? `<p class="${escapeHtml36(input.status)}">${escapeHtml36(input.status)}</p>` : ""}${input.href ? `<a href="${escapeHtml36(input.href)}">Open</a>` : ""}</article>`;
|
|
20818
20949
|
var renderVoiceOpsConsoleHTML = (report, options = {}) => {
|
|
20819
20950
|
const links = report.links.map((link) => `<article class="surface">
|
|
20820
|
-
<div><h2>${
|
|
20821
|
-
<p><a href="${
|
|
20951
|
+
<div><h2>${escapeHtml36(link.label)}</h2>${link.description ? `<p>${escapeHtml36(link.description)}</p>` : ""}</div>
|
|
20952
|
+
<p><a href="${escapeHtml36(link.href)}">Open ${escapeHtml36(link.label)}</a>${link.statusHref ? ` \xB7 <a href="${escapeHtml36(link.statusHref)}">Status</a>` : ""}</p>
|
|
20822
20953
|
</article>`).join("");
|
|
20823
|
-
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${
|
|
20824
|
-
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${
|
|
20954
|
+
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${escapeHtml36(session.sessionId)}</td><td>${escapeHtml36(session.status)}</td><td>${session.turnCount}</td><td>${session.errorCount}</td><td>${session.replayHref ? `<a href="${escapeHtml36(session.replayHref)}">Replay</a>` : ""}</td></tr>`).join("") : '<tr><td colspan="5">No sessions yet.</td></tr>';
|
|
20955
|
+
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${escapeHtml36(event.kind)}</td><td>${escapeHtml36(event.provider ?? "unknown")}</td><td>${escapeHtml36(event.status ?? "unknown")}</td><td>${event.elapsedMs ?? 0}ms</td><td>${escapeHtml36(event.sessionId)}</td></tr>`).join("") : '<tr><td colspan="5">No provider routing events yet.</td></tr>';
|
|
20825
20956
|
const title = options.title ?? "AbsoluteJS Voice Ops Console";
|
|
20826
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
20957
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml36(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;background:#101316;color:#f6f2e8;margin:0}main{max-width:1180px;margin:auto;padding:32px}a{color:#fbbf24}header{display:flex;justify-content:space-between;gap:24px;align-items:flex-start;margin-bottom:24px}.eyebrow{color:#fbbf24;font-weight:800;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.2rem,5vw,4.5rem);line-height:.95;margin:.2rem 0 1rem}.muted{color:#a8b0b8}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.metric,.surface{background:#181d22;border:1px solid #2a323a;border-radius:20px;padding:18px}.metric strong{display:block;font-size:2.2rem;margin:.25rem 0}.pass,.healthy{color:#86efac}.fail,.failed,.degraded{color:#fca5a5}.surfaces{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));margin:24px 0}table{width:100%;border-collapse:collapse;background:#181d22;border-radius:16px;overflow:hidden;margin:12px 0 28px}td,th{border-bottom:1px solid #2a323a;padding:12px;text-align:left}section{margin-top:30px}@media(max-width:700px){main{padding:20px}header{display:block}}</style></head><body><main><header><div><p class="eyebrow">Self-hosted voice operations</p><h1>${escapeHtml36(title)}</h1><p class="muted">One deployable control plane for quality gates, failover, traces, sessions, handoffs, and provider health.</p></div><p class="muted">Checked ${escapeHtml36(new Date(report.checkedAt).toLocaleString())}</p></header><div class="grid">${renderMetricCard({ label: "Quality", value: report.quality.status, status: report.quality.status, href: "/quality" })}${renderMetricCard({ label: "Events", value: report.eventCount, href: "/diagnostics" })}${renderMetricCard({ label: "Sessions", value: report.sessions.total, status: report.sessions.failed > 0 ? "failed" : "healthy", href: "/sessions" })}${renderMetricCard({ label: "Handoffs failed", value: report.handoffs.failed, status: report.handoffs.failed > 0 ? "failed" : "healthy", href: "/handoffs" })}${renderMetricCard({ label: "Providers degraded", value: report.providers.degraded, status: report.providers.degraded > 0 ? "degraded" : "healthy", href: "/resilience" })}</div><section><h2>Operational Surfaces</h2><div class="surfaces">${links}</div></section><section><h2>Recent Sessions</h2><table><thead><tr><th>Session</th><th>Status</th><th>Turns</th><th>Errors</th><th>Replay</th></tr></thead><tbody>${sessions}</tbody></table></section><section><h2>Recent Provider Routing</h2><table><thead><tr><th>Kind</th><th>Provider</th><th>Status</th><th>Elapsed</th><th>Session</th></tr></thead><tbody>${routing}</tbody></table></section></main></body></html>`;
|
|
20827
20958
|
};
|
|
20828
20959
|
var createVoiceOpsConsoleRoutes = (options) => {
|
|
20829
20960
|
const path = options.path ?? "/ops-console";
|
|
20830
|
-
const routes = new
|
|
20961
|
+
const routes = new Elysia34({
|
|
20831
20962
|
name: options.name ?? "absolutejs-voice-ops-console"
|
|
20832
20963
|
});
|
|
20833
20964
|
const getReport = () => buildVoiceOpsConsoleReport(options);
|
|
@@ -21015,19 +21146,19 @@ var summarizeVoiceOpsStatus = async (options) => {
|
|
|
21015
21146
|
};
|
|
21016
21147
|
};
|
|
21017
21148
|
// src/opsStatusRoutes.ts
|
|
21018
|
-
import { Elysia as
|
|
21019
|
-
var
|
|
21149
|
+
import { Elysia as Elysia35 } from "elysia";
|
|
21150
|
+
var escapeHtml37 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21020
21151
|
var renderVoiceOpsStatusHTML = (report, options = {}) => {
|
|
21021
21152
|
const title = options.title ?? "AbsoluteJS Voice Ops Status";
|
|
21022
21153
|
const surfaces = Object.entries(report.surfaces).map(([key, surface]) => {
|
|
21023
21154
|
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;
|
|
21024
|
-
return `<article class="surface ${
|
|
21155
|
+
return `<article class="surface ${escapeHtml37(surface.status)}"><span>${escapeHtml37(surface.status.toUpperCase())}</span><h2>${escapeHtml37(key)}</h2><strong>${escapeHtml37(value)}</strong></article>`;
|
|
21025
21156
|
}).join("");
|
|
21026
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
21157
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml37(title)}</title><style>body{background:#0d141b;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.2),rgba(245,158,11,.12));border:1px solid #283544;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.surfaces{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.surface{background:#151d26;border:1px solid #283544;border-radius:20px;padding:18px}.surface span{color:#aab5c0;font-size:.78rem;font-weight:900;letter-spacing:.08em}.surface strong{font-size:1.5rem}.pass{border-color:rgba(34,197,94,.55)}.fail{border-color:rgba(239,68,68,.75)}a{color:#5eead4}</style></head><body><main><section class="hero"><p class="eyebrow">Ops status</p><h1>${escapeHtml37(title)}</h1><p>Compact pass/fail status for framework widgets, demos, and small customer-facing health badges.</p><p class="status ${escapeHtml37(report.status)}">Overall: ${escapeHtml37(report.status.toUpperCase())}</p><p>${report.passed}/${report.total} checks passing</p></section><section class="surfaces">${surfaces || '<article class="surface pass"><span>PASS</span><h2>No checks configured</h2><strong>0/0</strong></article>'}</section></main></body></html>`;
|
|
21027
21158
|
};
|
|
21028
21159
|
var createVoiceOpsStatusRoutes = (options) => {
|
|
21029
21160
|
const path = options.path ?? "/api/voice/ops-status";
|
|
21030
|
-
const routes = new
|
|
21161
|
+
const routes = new Elysia35({
|
|
21031
21162
|
name: options.name ?? "absolutejs-voice-ops-status"
|
|
21032
21163
|
});
|
|
21033
21164
|
routes.get(path, async () => summarizeVoiceOpsStatus(options));
|
|
@@ -21460,8 +21591,8 @@ var createVoiceTTSProviderRouter = (options) => {
|
|
|
21460
21591
|
};
|
|
21461
21592
|
};
|
|
21462
21593
|
// src/traceDeliveryRoutes.ts
|
|
21463
|
-
import { Elysia as
|
|
21464
|
-
var
|
|
21594
|
+
import { Elysia as Elysia36 } from "elysia";
|
|
21595
|
+
var escapeHtml38 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21465
21596
|
var getString12 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
21466
21597
|
var getNumber7 = (value) => {
|
|
21467
21598
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -21542,14 +21673,14 @@ var renderSinkResults2 = (delivery) => {
|
|
|
21542
21673
|
if (entries.length === 0) {
|
|
21543
21674
|
return "<p>No sink delivery attempts recorded yet.</p>";
|
|
21544
21675
|
}
|
|
21545
|
-
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${
|
|
21676
|
+
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${escapeHtml38(sinkId)}</strong>: ${escapeHtml38(result.status)}${result.deliveredTo ? ` to ${escapeHtml38(result.deliveredTo)}` : ""}${result.error ? ` (${escapeHtml38(result.error)})` : ""}</li>`).join("")}</ul>`;
|
|
21546
21677
|
};
|
|
21547
|
-
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${
|
|
21678
|
+
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${escapeHtml38(event.type)} <small>${escapeHtml38(event.id)}</small>${event.sessionId ? ` session=${escapeHtml38(event.sessionId)}` : ""}</li>`).join("")}</ul>`;
|
|
21548
21679
|
var renderVoiceTraceDeliveryHTML = (report, options = {}) => {
|
|
21549
21680
|
const title = options.title ?? "AbsoluteJS Voice Trace Deliveries";
|
|
21550
|
-
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${
|
|
21551
|
-
const rows = report.deliveries.map((delivery) => `<article class="delivery ${
|
|
21552
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
21681
|
+
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${escapeHtml38(options.workerPath ?? "/api/voice-trace-deliveries/drain")}"><button type="submit">Drain trace deliveries</button></form>`;
|
|
21682
|
+
const rows = report.deliveries.map((delivery) => `<article class="delivery ${escapeHtml38(delivery.deliveryStatus)}"><div class="head"><div><span>${escapeHtml38(delivery.deliveryStatus)}</span><h2>${escapeHtml38(delivery.id)}</h2><p>${escapeHtml38(new Date(delivery.createdAt).toLocaleString())}${delivery.deliveredAt ? ` \xB7 delivered ${escapeHtml38(new Date(delivery.deliveredAt).toLocaleString())}` : ""}</p></div><strong>${String(delivery.deliveryAttempts ?? 0)} attempt(s)</strong></div>${delivery.deliveryError ? `<p class="error">${escapeHtml38(delivery.deliveryError)}</p>` : ""}<h3>Sinks</h3>${renderSinkResults2(delivery)}<h3>Events</h3>${renderEventList2(delivery)}</article>`).join("");
|
|
21683
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml38(title)}</title><style>body{background:#0f1318;color:#f4efe1;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.16),rgba(14,165,233,.14));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#86efac;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,5vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.grid{display:grid;gap:12px;grid-template-columns:repeat(4,1fr);margin-bottom:16px}.grid article,.delivery{background:#151b22;border:1px solid #26313d;border-radius:22px;padding:18px}.grid span,.delivery span{color:#86efac;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.grid strong{display:block;font-size:2rem}.deliveries{display:grid;gap:14px}.delivery.failed{border-color:rgba(239,68,68,.75)}.delivery.pending{border-color:rgba(245,158,11,.7)}.delivery.delivered{border-color:rgba(34,197,94,.55)}.delivery.skipped{border-color:rgba(148,163,184,.6)}.head{align-items:start;display:flex;gap:14px;justify-content:space-between}.delivery h2{font-size:1.05rem;margin:.3rem 0;overflow-wrap:anywhere}.delivery h3{margin:1rem 0 .3rem}.delivery p,.delivery li{color:#c8d0d8}.error{color:#fecaca!important}button{background:#86efac;border:0;border-radius:999px;color:#07111f;cursor:pointer;font-weight:900;margin-top:12px;padding:10px 14px}@media(max-width:760px){main{padding:20px}.grid{grid-template-columns:1fr 1fr}.head{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Trace export health</p><h1>${escapeHtml38(title)}</h1><p>Checked ${escapeHtml38(new Date(report.checkedAt).toLocaleString())}. Showing ${String(report.deliveries.length)} delivery item(s).</p>${drainAction}</section>${renderMetricGrid3(report)}<section class="deliveries">${rows || "<p>No trace deliveries match this filter.</p>"}</section></main></body></html>`;
|
|
21553
21684
|
};
|
|
21554
21685
|
var createVoiceTraceDeliveryJSONHandler = (options) => async ({ query }) => buildVoiceTraceDeliveryReport(options, resolveVoiceTraceDeliveryFilter(query, options.filter));
|
|
21555
21686
|
var createVoiceTraceDeliveryHTMLHandler = (options) => async ({ query }) => {
|
|
@@ -21569,7 +21700,7 @@ var createVoiceTraceDeliveryRoutes = (options) => {
|
|
|
21569
21700
|
const path = options.path ?? "/api/voice-trace-deliveries";
|
|
21570
21701
|
const htmlPath = options.htmlPath === undefined ? "/traces/deliveries" : options.htmlPath;
|
|
21571
21702
|
const workerPath = options.workerPath === undefined ? `${path}/drain` : options.workerPath;
|
|
21572
|
-
const routes = new
|
|
21703
|
+
const routes = new Elysia36({
|
|
21573
21704
|
name: options.name ?? "absolutejs-voice-trace-deliveries"
|
|
21574
21705
|
}).get(path, createVoiceTraceDeliveryJSONHandler(options));
|
|
21575
21706
|
if (htmlPath !== false) {
|
|
@@ -21587,8 +21718,8 @@ var createVoiceTraceDeliveryRoutes = (options) => {
|
|
|
21587
21718
|
return routes;
|
|
21588
21719
|
};
|
|
21589
21720
|
// src/traceTimeline.ts
|
|
21590
|
-
import { Elysia as
|
|
21591
|
-
var
|
|
21721
|
+
import { Elysia as Elysia37 } from "elysia";
|
|
21722
|
+
var escapeHtml39 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21592
21723
|
var getString13 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
21593
21724
|
var getNumber8 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
21594
21725
|
var firstString3 = (payload, keys) => {
|
|
@@ -21756,20 +21887,20 @@ var summarizeVoiceTraceTimeline = (events, options = {}) => {
|
|
|
21756
21887
|
};
|
|
21757
21888
|
};
|
|
21758
21889
|
var formatMs3 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
21759
|
-
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>${
|
|
21890
|
+
var renderProviderCards2 = (session) => session.providers.length === 0 ? '<p class="muted">No provider events recorded for this session.</p>' : `<div class="providers">${session.providers.map((provider) => `<article><strong>${escapeHtml39(provider.provider)}</strong><dl><div><dt>Events</dt><dd>${String(provider.eventCount)}</dd></div><div><dt>Avg</dt><dd>${formatMs3(provider.averageElapsedMs)}</dd></div><div><dt>Max</dt><dd>${formatMs3(provider.maxElapsedMs)}</dd></div><div><dt>Errors</dt><dd>${String(provider.errorCount)}</dd></div><div><dt>Fallbacks</dt><dd>${String(provider.fallbackCount)}</dd></div><div><dt>Timeouts</dt><dd>${String(provider.timeoutCount)}</dd></div></dl></article>`).join("")}</div>`;
|
|
21760
21891
|
var renderVoiceTraceTimelineSessionHTML = (session, options = {}) => {
|
|
21761
|
-
const events = session.events.map((event) => `<tr class="${
|
|
21762
|
-
const issues = session.evaluation.issues.length ? session.evaluation.issues.map((issue) => `<li class="${
|
|
21763
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
21892
|
+
const events = session.events.map((event) => `<tr class="${escapeHtml39(event.status ?? "")}"><td>+${String(event.offsetMs)}ms</td><td>${escapeHtml39(event.type)}</td><td>${escapeHtml39(event.label)}</td><td>${escapeHtml39(event.provider ?? "")}</td><td>${escapeHtml39(event.status ?? "")}</td><td>${formatMs3(event.elapsedMs)}</td></tr>`).join("");
|
|
21893
|
+
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>";
|
|
21894
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml39(options.title ?? "Voice Trace Timeline")}</title><style>${timelineCSS}</style></head><body><main><a href="/traces">Back to traces</a><header><p class="eyebrow">Call timeline</p><h1>${escapeHtml39(session.sessionId)}</h1><p class="status ${escapeHtml39(session.status)}">${escapeHtml39(session.status)}</p></header><section class="metrics"><article><span>Events</span><strong>${String(session.summary.eventCount)}</strong></article><article><span>Turns</span><strong>${String(session.summary.turnCount)}</strong></article><article><span>Errors</span><strong>${String(session.summary.errorCount)}</strong></article><article><span>Duration</span><strong>${formatMs3(session.summary.callDurationMs)}</strong></article></section><section><h2>Providers</h2>${renderProviderCards2(session)}</section><section><h2>Issues</h2><ul>${issues}</ul></section><section><h2>Timeline</h2><table><thead><tr><th>Offset</th><th>Type</th><th>Event</th><th>Provider</th><th>Status</th><th>Latency</th></tr></thead><tbody>${events}</tbody></table></section></main></body></html>`;
|
|
21764
21895
|
};
|
|
21765
|
-
var renderSessionRows = (report) => report.sessions.length === 0 ? '<tr><td colspan="7">No trace events recorded yet.</td></tr>' : report.sessions.map((session) => `<tr class="${
|
|
21896
|
+
var renderSessionRows = (report) => report.sessions.length === 0 ? '<tr><td colspan="7">No trace events recorded yet.</td></tr>' : report.sessions.map((session) => `<tr class="${escapeHtml39(session.status)}"><td><a href="/traces/${encodeURIComponent(session.sessionId)}">${escapeHtml39(session.sessionId)}</a></td><td>${escapeHtml39(session.status)}</td><td>${String(session.summary.eventCount)}</td><td>${String(session.summary.turnCount)}</td><td>${String(session.summary.errorCount)}</td><td>${formatMs3(session.summary.callDurationMs)}</td><td>${session.providers.map((provider) => escapeHtml39(provider.provider)).join(", ")}</td></tr>`).join("");
|
|
21766
21897
|
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}}";
|
|
21767
|
-
var renderVoiceTraceTimelineHTML = (report, options = {}) => `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
21898
|
+
var renderVoiceTraceTimelineHTML = (report, options = {}) => `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml39(options.title ?? "Voice Trace Timelines")}</title><style>${timelineCSS}</style></head><body><main><header><p class="eyebrow">Self-hosted voice debugging</p><h1>${escapeHtml39(options.title ?? "Voice Trace Timelines")}</h1><p class="muted">Per-call event timelines with provider latency, fallback, timeout, handoff, and error context.</p></header><section class="metrics"><article><span>Sessions</span><strong>${String(report.total)}</strong></article><article><span>Failed</span><strong>${String(report.failed)}</strong></article><article><span>Warnings</span><strong>${String(report.warnings)}</strong></article></section><table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Turns</th><th>Errors</th><th>Duration</th><th>Providers</th></tr></thead><tbody>${renderSessionRows(report)}</tbody></table></main></body></html>`;
|
|
21768
21899
|
var createVoiceTraceTimelineRoutes = (options) => {
|
|
21769
21900
|
const path = options.path ?? "/api/voice-traces";
|
|
21770
21901
|
const htmlPath = options.htmlPath ?? "/traces";
|
|
21771
21902
|
const title = options.title ?? "AbsoluteJS Voice Trace Timelines";
|
|
21772
|
-
const routes = new
|
|
21903
|
+
const routes = new Elysia37({
|
|
21773
21904
|
name: options.name ?? "absolutejs-voice-trace-timelines"
|
|
21774
21905
|
});
|
|
21775
21906
|
const buildReport = async () => summarizeVoiceTraceTimeline(await options.store.list(), {
|
|
@@ -22422,7 +22553,7 @@ var createVoiceMemoryStore = () => {
|
|
|
22422
22553
|
return { get, getOrCreate, list, remove, set };
|
|
22423
22554
|
};
|
|
22424
22555
|
// src/opsWebhook.ts
|
|
22425
|
-
import { Elysia as
|
|
22556
|
+
import { Elysia as Elysia38 } from "elysia";
|
|
22426
22557
|
var toHex6 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
22427
22558
|
var signVoiceOpsWebhookBody = async (input) => {
|
|
22428
22559
|
const encoder = new TextEncoder;
|
|
@@ -22552,7 +22683,7 @@ var verifyVoiceOpsWebhookSignature = async (input) => {
|
|
|
22552
22683
|
};
|
|
22553
22684
|
var createVoiceOpsWebhookReceiverRoutes = (options = {}) => {
|
|
22554
22685
|
const path = options.path ?? "/api/voice-ops/webhook";
|
|
22555
|
-
return new
|
|
22686
|
+
return new Elysia38().post(path, async ({ body, request, set }) => {
|
|
22556
22687
|
const bodyText = typeof body === "string" ? body : JSON.stringify(body);
|
|
22557
22688
|
if (options.signingSecret) {
|
|
22558
22689
|
const verification = await verifyVoiceOpsWebhookSignature({
|
|
@@ -23491,6 +23622,7 @@ export {
|
|
|
23491
23622
|
renderVoiceOutcomeContractHTML,
|
|
23492
23623
|
renderVoiceOpsStatusHTML,
|
|
23493
23624
|
renderVoiceOpsConsoleHTML,
|
|
23625
|
+
renderVoiceOpsActionHistoryHTML,
|
|
23494
23626
|
renderVoiceLiveLatencyHTML,
|
|
23495
23627
|
renderVoiceHandoffHealthHTML,
|
|
23496
23628
|
renderVoiceEvalHTML,
|
|
@@ -23518,6 +23650,7 @@ export {
|
|
|
23518
23650
|
recordVoiceRuntimeOps,
|
|
23519
23651
|
recordVoiceRetentionAuditEvent,
|
|
23520
23652
|
recordVoiceProviderAuditEvent,
|
|
23653
|
+
recordVoiceOpsActionAudit,
|
|
23521
23654
|
recordVoiceOperatorAuditEvent,
|
|
23522
23655
|
recordVoiceHandoffAuditEvent,
|
|
23523
23656
|
recordVoiceAuditEvent,
|
|
@@ -23669,6 +23802,7 @@ export {
|
|
|
23669
23802
|
createVoiceOpsStatusRoutes,
|
|
23670
23803
|
createVoiceOpsRuntime,
|
|
23671
23804
|
createVoiceOpsConsoleRoutes,
|
|
23805
|
+
createVoiceOpsActionAuditRoutes,
|
|
23672
23806
|
createVoiceMemoryTraceSinkDeliveryStore,
|
|
23673
23807
|
createVoiceMemoryTraceEventStore,
|
|
23674
23808
|
createVoiceMemoryStore,
|
|
@@ -23790,6 +23924,7 @@ export {
|
|
|
23790
23924
|
buildVoiceOpsTaskFromSLABreach,
|
|
23791
23925
|
buildVoiceOpsTaskFromReview,
|
|
23792
23926
|
buildVoiceOpsConsoleReport,
|
|
23927
|
+
buildVoiceOpsActionHistoryReport,
|
|
23793
23928
|
buildVoiceDiagnosticsMarkdown,
|
|
23794
23929
|
buildVoiceDemoReadyReport,
|
|
23795
23930
|
buildVoiceDeliverySinkReport,
|