@absolutejs/voice 0.0.22-beta.195 → 0.0.22-beta.197
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 +60 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +397 -93
- package/dist/opsRecovery.d.ts +137 -0
- package/dist/productionReadiness.d.ts +12 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -21745,9 +21745,268 @@ var assertVoiceProviderRoutingContract = async (options) => {
|
|
|
21745
21745
|
return report;
|
|
21746
21746
|
};
|
|
21747
21747
|
// src/productionReadiness.ts
|
|
21748
|
+
import { Elysia as Elysia36 } from "elysia";
|
|
21749
|
+
|
|
21750
|
+
// src/opsRecovery.ts
|
|
21748
21751
|
import { Elysia as Elysia35 } from "elysia";
|
|
21749
21752
|
var escapeHtml36 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21750
|
-
var
|
|
21753
|
+
var getString13 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
21754
|
+
var hrefForSession = (value, sessionId) => {
|
|
21755
|
+
if (typeof value === "function") {
|
|
21756
|
+
return value(sessionId);
|
|
21757
|
+
}
|
|
21758
|
+
if (typeof value !== "string") {
|
|
21759
|
+
return;
|
|
21760
|
+
}
|
|
21761
|
+
const encoded = encodeURIComponent(sessionId);
|
|
21762
|
+
if (value.includes(":sessionId")) {
|
|
21763
|
+
return value.replace(":sessionId", encoded);
|
|
21764
|
+
}
|
|
21765
|
+
return value;
|
|
21766
|
+
};
|
|
21767
|
+
var operationsRecordHrefForSession = (links, sessionId) => hrefForSession(links?.operationsRecords, sessionId);
|
|
21768
|
+
var rollupStatus2 = (issues) => issues.some((issue) => issue.severity === "fail") ? "fail" : issues.some((issue) => issue.severity === "warn") ? "warn" : "pass";
|
|
21769
|
+
var providerUnresolved = (provider) => provider.status === "degraded" || provider.status === "rate-limited" || provider.status === "suppressed";
|
|
21770
|
+
var collectFailedSessions = (events, limit, links) => events.filter((event) => {
|
|
21771
|
+
if (event.type !== "session.error") {
|
|
21772
|
+
return false;
|
|
21773
|
+
}
|
|
21774
|
+
const providerStatus = event.payload.providerStatus;
|
|
21775
|
+
return providerStatus !== "success" && providerStatus !== "fallback";
|
|
21776
|
+
}).sort((left, right) => right.at - left.at).slice(0, limit).map((event) => ({
|
|
21777
|
+
at: event.at,
|
|
21778
|
+
error: getString13(event.payload.error),
|
|
21779
|
+
operationsRecordHref: operationsRecordHrefForSession(links, event.sessionId),
|
|
21780
|
+
provider: getString13(event.payload.provider),
|
|
21781
|
+
sessionId: event.sessionId,
|
|
21782
|
+
traceId: event.traceId
|
|
21783
|
+
}));
|
|
21784
|
+
var collectInterventions = (events, limit) => {
|
|
21785
|
+
const interventionEvents = events.filter((event) => event.type === "operator.action").sort((left, right) => right.at - left.at);
|
|
21786
|
+
return {
|
|
21787
|
+
events: interventionEvents.slice(0, limit).map((event) => ({
|
|
21788
|
+
action: getString13(event.payload.action),
|
|
21789
|
+
at: event.at,
|
|
21790
|
+
operatorId: getString13(event.payload.operatorId) ?? getString13(event.payload.actorId),
|
|
21791
|
+
sessionId: event.sessionId,
|
|
21792
|
+
traceId: event.traceId
|
|
21793
|
+
})),
|
|
21794
|
+
total: interventionEvents.length
|
|
21795
|
+
};
|
|
21796
|
+
};
|
|
21797
|
+
var addDeliveryIssues = (issues, input) => {
|
|
21798
|
+
if (!input.summary) {
|
|
21799
|
+
return;
|
|
21800
|
+
}
|
|
21801
|
+
const failed = input.summary.failed + input.summary.deadLettered;
|
|
21802
|
+
if (failed > 0) {
|
|
21803
|
+
issues.push({
|
|
21804
|
+
code: input.failedCode,
|
|
21805
|
+
detail: `${failed} failed or dead-lettered delivery record(s).`,
|
|
21806
|
+
href: input.href,
|
|
21807
|
+
label: input.failedLabel,
|
|
21808
|
+
severity: "fail",
|
|
21809
|
+
value: failed
|
|
21810
|
+
});
|
|
21811
|
+
}
|
|
21812
|
+
const pending = input.summary.pending + input.summary.retryEligible;
|
|
21813
|
+
if (pending > 0) {
|
|
21814
|
+
issues.push({
|
|
21815
|
+
code: input.pendingCode,
|
|
21816
|
+
detail: `${pending} pending or retry-eligible delivery record(s).`,
|
|
21817
|
+
href: input.href,
|
|
21818
|
+
label: input.pendingLabel,
|
|
21819
|
+
severity: "warn",
|
|
21820
|
+
value: pending
|
|
21821
|
+
});
|
|
21822
|
+
}
|
|
21823
|
+
};
|
|
21824
|
+
var buildVoiceOpsRecoveryReport = async (options = {}) => {
|
|
21825
|
+
const limit = options.limit ?? 50;
|
|
21826
|
+
const events = options.events ?? await options.traces?.list({ limit: Math.max(limit, 500) }) ?? [];
|
|
21827
|
+
const providers = await summarizeVoiceProviderHealth({
|
|
21828
|
+
events,
|
|
21829
|
+
providers: options.providers
|
|
21830
|
+
});
|
|
21831
|
+
const auditDeliveries = options.auditDeliveries ? await summarizeVoiceAuditSinkDeliveries(await options.auditDeliveries.list(), {
|
|
21832
|
+
deadLetters: options.auditDeliveryDeadLetters
|
|
21833
|
+
}) : undefined;
|
|
21834
|
+
const traceDeliveries = options.traceDeliveries ? await summarizeVoiceTraceSinkDeliveries(await options.traceDeliveries.list(), {
|
|
21835
|
+
deadLetters: options.traceDeliveryDeadLetters
|
|
21836
|
+
}) : undefined;
|
|
21837
|
+
const handoffDeliveries = options.handoffDeliveries ? await summarizeVoiceHandoffDeliveries(await options.handoffDeliveries.list(), {
|
|
21838
|
+
deadLetters: options.handoffDeliveryDeadLetters
|
|
21839
|
+
}) : undefined;
|
|
21840
|
+
const latency = options.latency === false ? undefined : await buildVoiceLatencySLOGate({
|
|
21841
|
+
events,
|
|
21842
|
+
...options.latency ?? {}
|
|
21843
|
+
});
|
|
21844
|
+
const failedSessions = collectFailedSessions(events, limit, options.links);
|
|
21845
|
+
const interventions = collectInterventions(events, limit);
|
|
21846
|
+
const issues = [];
|
|
21847
|
+
const unresolvedProviders = providers.filter(providerUnresolved);
|
|
21848
|
+
for (const provider of unresolvedProviders) {
|
|
21849
|
+
const failedSession = failedSessions.find((session) => session.provider === provider.provider);
|
|
21850
|
+
issues.push({
|
|
21851
|
+
code: "voice.ops_recovery.provider_unresolved_failure",
|
|
21852
|
+
detail: provider.lastError ?? `${provider.provider} status is ${provider.status}.`,
|
|
21853
|
+
href: failedSession?.operationsRecordHref ?? options.links?.providers,
|
|
21854
|
+
label: `Provider ${provider.provider} needs recovery`,
|
|
21855
|
+
severity: "fail",
|
|
21856
|
+
value: provider.status
|
|
21857
|
+
});
|
|
21858
|
+
}
|
|
21859
|
+
addDeliveryIssues(issues, {
|
|
21860
|
+
failedCode: "voice.ops_recovery.audit_delivery_failed",
|
|
21861
|
+
failedLabel: "Audit delivery failures",
|
|
21862
|
+
href: options.links?.auditDeliveries,
|
|
21863
|
+
pendingCode: "voice.ops_recovery.audit_delivery_pending",
|
|
21864
|
+
pendingLabel: "Audit delivery backlog",
|
|
21865
|
+
summary: auditDeliveries
|
|
21866
|
+
});
|
|
21867
|
+
addDeliveryIssues(issues, {
|
|
21868
|
+
failedCode: "voice.ops_recovery.trace_delivery_failed",
|
|
21869
|
+
failedLabel: "Trace delivery failures",
|
|
21870
|
+
href: options.links?.traceDeliveries,
|
|
21871
|
+
pendingCode: "voice.ops_recovery.trace_delivery_pending",
|
|
21872
|
+
pendingLabel: "Trace delivery backlog",
|
|
21873
|
+
summary: traceDeliveries
|
|
21874
|
+
});
|
|
21875
|
+
addDeliveryIssues(issues, {
|
|
21876
|
+
failedCode: "voice.ops_recovery.handoff_failed",
|
|
21877
|
+
failedLabel: "Handoff delivery failures",
|
|
21878
|
+
href: options.links?.handoffs,
|
|
21879
|
+
pendingCode: "voice.ops_recovery.handoff_pending",
|
|
21880
|
+
pendingLabel: "Handoff delivery backlog",
|
|
21881
|
+
summary: handoffDeliveries
|
|
21882
|
+
});
|
|
21883
|
+
if (latency?.failed) {
|
|
21884
|
+
const failedMeasurement = latency.measurements.find((measurement) => measurement.status === "fail");
|
|
21885
|
+
issues.push({
|
|
21886
|
+
code: "voice.ops_recovery.latency_slo_failed",
|
|
21887
|
+
detail: `${latency.failed} latency SLO measurement(s) failed.`,
|
|
21888
|
+
href: failedMeasurement ? operationsRecordHrefForSession(options.links, failedMeasurement.sessionId) ?? hrefForSession(options.links?.traces, failedMeasurement.sessionId) : undefined,
|
|
21889
|
+
label: "Latency SLO failures",
|
|
21890
|
+
severity: "fail",
|
|
21891
|
+
value: latency.failed
|
|
21892
|
+
});
|
|
21893
|
+
} else if (latency?.warnings) {
|
|
21894
|
+
issues.push({
|
|
21895
|
+
code: "voice.ops_recovery.latency_slo_warn",
|
|
21896
|
+
detail: `${latency.warnings} latency SLO measurement(s) are warning.`,
|
|
21897
|
+
label: "Latency SLO warnings",
|
|
21898
|
+
severity: "warn",
|
|
21899
|
+
value: latency.warnings
|
|
21900
|
+
});
|
|
21901
|
+
}
|
|
21902
|
+
return {
|
|
21903
|
+
auditDeliveries,
|
|
21904
|
+
checkedAt: Date.now(),
|
|
21905
|
+
failedSessions,
|
|
21906
|
+
handoffDeliveries,
|
|
21907
|
+
interventions,
|
|
21908
|
+
issues,
|
|
21909
|
+
latency,
|
|
21910
|
+
providers: {
|
|
21911
|
+
healthy: providers.filter((provider) => provider.status === "healthy").length,
|
|
21912
|
+
providers,
|
|
21913
|
+
recoveredFallbacks: providers.reduce((total, provider) => total + provider.fallbackCount, 0),
|
|
21914
|
+
unresolvedFailures: unresolvedProviders.length
|
|
21915
|
+
},
|
|
21916
|
+
status: rollupStatus2(issues),
|
|
21917
|
+
traceDeliveries
|
|
21918
|
+
};
|
|
21919
|
+
};
|
|
21920
|
+
var buildVoiceOpsRecoveryReadinessCheck = (report, options = {}) => ({
|
|
21921
|
+
detail: report.status === "pass" ? `${report.providers.recoveredFallbacks} recovered fallback(s), ${report.interventions.total} operator intervention(s), and no unresolved recovery issues.` : `${report.issues.length} recovery issue(s) require attention.`,
|
|
21922
|
+
href: options.href,
|
|
21923
|
+
label: options.label ?? "Ops recovery",
|
|
21924
|
+
status: report.status,
|
|
21925
|
+
value: report.issues.length
|
|
21926
|
+
});
|
|
21927
|
+
var renderVoiceOpsRecoveryMarkdown = (report, options = {}) => {
|
|
21928
|
+
const title = options.title ?? "Voice Ops Recovery";
|
|
21929
|
+
const issueRows = report.issues.map((issue) => `| ${issue.severity} | ${issue.code} | ${issue.href ? `[${issue.label}](${issue.href})` : issue.label} | ${issue.value ?? ""} | ${issue.detail ?? ""} |`).join(`
|
|
21930
|
+
`);
|
|
21931
|
+
const providers = report.providers.providers.map((provider) => `| ${provider.provider} | ${provider.status} | ${provider.runCount} | ${provider.errorCount} | ${provider.fallbackCount} | ${provider.lastError ?? ""} |`).join(`
|
|
21932
|
+
`);
|
|
21933
|
+
const failedSessions = report.failedSessions.map((session) => `- ${session.operationsRecordHref ? `[${session.sessionId}](${session.operationsRecordHref})` : session.sessionId}${session.provider ? ` via ${session.provider}` : ""}${session.error ? `: ${session.error}` : ""}`).join(`
|
|
21934
|
+
`);
|
|
21935
|
+
return `# ${title}
|
|
21936
|
+
|
|
21937
|
+
Status: ${report.status}
|
|
21938
|
+
|
|
21939
|
+
Checked at: ${new Date(report.checkedAt).toISOString()}
|
|
21940
|
+
|
|
21941
|
+
Recovered fallbacks: ${report.providers.recoveredFallbacks}
|
|
21942
|
+
Unresolved provider failures: ${report.providers.unresolvedFailures}
|
|
21943
|
+
Operator interventions: ${report.interventions.total}
|
|
21944
|
+
|
|
21945
|
+
## Issues
|
|
21946
|
+
|
|
21947
|
+
| Severity | Code | Label | Value | Detail |
|
|
21948
|
+
| --- | --- | --- | ---: | --- |
|
|
21949
|
+
${issueRows || "| pass | none | No recovery issues | 0 | |"}
|
|
21950
|
+
|
|
21951
|
+
## Providers
|
|
21952
|
+
|
|
21953
|
+
| Provider | Status | Runs | Errors | Fallbacks | Last error |
|
|
21954
|
+
| --- | --- | ---: | ---: | ---: | --- |
|
|
21955
|
+
${providers || "| none | idle | 0 | 0 | 0 | |"}
|
|
21956
|
+
|
|
21957
|
+
## Failed Sessions
|
|
21958
|
+
|
|
21959
|
+
${failedSessions || "None."}
|
|
21960
|
+
|
|
21961
|
+
## Latency
|
|
21962
|
+
|
|
21963
|
+
${report.latency ? renderVoiceLatencySLOMarkdown(report.latency, { title: "Latency SLO" }) : "Latency SLO disabled."}
|
|
21964
|
+
`;
|
|
21965
|
+
};
|
|
21966
|
+
var renderDeliverySummary = (label, summary) => summary ? `<article><span>${escapeHtml36(label)}</span><strong>${String(summary.failed + summary.deadLettered)} failed</strong><small>${String(summary.pending)} pending \xB7 ${String(summary.retryEligible)} retry eligible \xB7 ${String(summary.total)} total</small></article>` : `<article><span>${escapeHtml36(label)}</span><strong>not configured</strong></article>`;
|
|
21967
|
+
var renderVoiceOpsRecoveryHTML = (report, options = {}) => {
|
|
21968
|
+
const title = options.title ?? "Voice Ops Recovery";
|
|
21969
|
+
const issues = report.issues.map((issue) => `<tr><td>${escapeHtml36(issue.severity)}</td><td><code>${escapeHtml36(issue.code)}</code></td><td>${issue.href ? `<a href="${escapeHtml36(issue.href)}">${escapeHtml36(issue.label)}</a>` : escapeHtml36(issue.label)}</td><td>${escapeHtml36(String(issue.value ?? ""))}</td><td>${escapeHtml36(issue.detail ?? "")}</td></tr>`).join("");
|
|
21970
|
+
const providers = report.providers.providers.map((provider) => `<tr><td>${escapeHtml36(provider.provider)}</td><td>${escapeHtml36(provider.status)}</td><td>${String(provider.runCount)}</td><td>${String(provider.errorCount)}</td><td>${String(provider.fallbackCount)}</td><td>${escapeHtml36(provider.lastError ?? "")}</td></tr>`).join("");
|
|
21971
|
+
const failedSessions = report.failedSessions.map((session) => `<li>${session.operationsRecordHref ? `<a href="${escapeHtml36(session.operationsRecordHref)}">${escapeHtml36(session.sessionId)}</a>` : escapeHtml36(session.sessionId)}${session.provider ? ` via ${escapeHtml36(session.provider)}` : ""}${session.error ? `: ${escapeHtml36(session.error)}` : ""}</li>`).join("");
|
|
21972
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml36(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;background:#f8fafc;color:#172033;margin:2rem;line-height:1.45}main{max-width:1180px;margin:auto}.grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:.75rem;margin:1rem 0}article{background:white;border:1px solid #dbe3ef;border-radius:14px;padding:1rem;box-shadow:0 10px 28px rgba(15,23,42,.05)}article span{display:block;color:#64748b;font-size:.85rem}article strong{display:block;font-size:1.5rem;margin:.2rem 0}article small{color:#64748b}table{border-collapse:collapse;width:100%;background:white;border:1px solid #dbe3ef;border-radius:14px;overflow:hidden}th,td{border-bottom:1px solid #e2e8f0;padding:.7rem;text-align:left;vertical-align:top}code{font-size:.85em}.status{display:inline-flex;border-radius:999px;padding:.35rem .7rem;background:${report.status === "fail" ? "#fee2e2" : report.status === "warn" ? "#fef3c7" : "#dcfce7"};color:${report.status === "fail" ? "#991b1b" : report.status === "warn" ? "#92400e" : "#166534"};font-weight:700}</style></head><body><main><h1>${escapeHtml36(title)}</h1><p><span class="status">${escapeHtml36(report.status)}</span> Checked ${escapeHtml36(new Date(report.checkedAt).toLocaleString())}</p><section class="grid"><article><span>Recovered fallbacks</span><strong>${String(report.providers.recoveredFallbacks)}</strong></article><article><span>Unresolved providers</span><strong>${String(report.providers.unresolvedFailures)}</strong></article><article><span>Operator interventions</span><strong>${String(report.interventions.total)}</strong></article><article><span>Latency status</span><strong>${escapeHtml36(report.latency?.status ?? "disabled")}</strong></article>${renderDeliverySummary("Audit delivery", report.auditDeliveries)}${renderDeliverySummary("Trace delivery", report.traceDeliveries)}${renderDeliverySummary("Handoff delivery", report.handoffDeliveries)}</section><h2>Issues</h2><table><thead><tr><th>Severity</th><th>Code</th><th>Label</th><th>Value</th><th>Detail</th></tr></thead><tbody>${issues || '<tr><td colspan="5">No recovery issues.</td></tr>'}</tbody></table><h2>Providers</h2><table><thead><tr><th>Provider</th><th>Status</th><th>Runs</th><th>Errors</th><th>Fallbacks</th><th>Last error</th></tr></thead><tbody>${providers || '<tr><td colspan="6">No provider activity.</td></tr>'}</tbody></table><h2>Failed Sessions</h2><ul>${failedSessions || "<li>None.</li>"}</ul></main></body></html>`;
|
|
21973
|
+
};
|
|
21974
|
+
var createVoiceOpsRecoveryRoutes = (options = {}) => {
|
|
21975
|
+
const path = options.path ?? "/api/voice/ops-recovery";
|
|
21976
|
+
const htmlPath = options.htmlPath === undefined ? "/ops-recovery" : options.htmlPath;
|
|
21977
|
+
const markdownPath = options.markdownPath === undefined ? `${path}.md` : options.markdownPath;
|
|
21978
|
+
const routes = new Elysia35({
|
|
21979
|
+
name: options.name ?? "absolutejs-voice-ops-recovery"
|
|
21980
|
+
}).get(path, async () => buildVoiceOpsRecoveryReport(options));
|
|
21981
|
+
if (htmlPath) {
|
|
21982
|
+
routes.get(htmlPath, async () => {
|
|
21983
|
+
const report = await buildVoiceOpsRecoveryReport(options);
|
|
21984
|
+
const render = options.render ?? renderVoiceOpsRecoveryHTML;
|
|
21985
|
+
return new Response(await render(report), {
|
|
21986
|
+
headers: {
|
|
21987
|
+
"content-type": "text/html; charset=utf-8",
|
|
21988
|
+
...options.headers
|
|
21989
|
+
}
|
|
21990
|
+
});
|
|
21991
|
+
});
|
|
21992
|
+
}
|
|
21993
|
+
if (markdownPath) {
|
|
21994
|
+
routes.get(markdownPath, async () => {
|
|
21995
|
+
const report = await buildVoiceOpsRecoveryReport(options);
|
|
21996
|
+
return new Response(renderVoiceOpsRecoveryMarkdown(report, { title: options.title }), {
|
|
21997
|
+
headers: {
|
|
21998
|
+
"content-type": "text/markdown; charset=utf-8",
|
|
21999
|
+
...options.headers
|
|
22000
|
+
}
|
|
22001
|
+
});
|
|
22002
|
+
});
|
|
22003
|
+
}
|
|
22004
|
+
return routes;
|
|
22005
|
+
};
|
|
22006
|
+
|
|
22007
|
+
// src/productionReadiness.ts
|
|
22008
|
+
var escapeHtml37 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
22009
|
+
var rollupStatus3 = (checks) => checks.some((check) => check.status === "fail") ? "fail" : checks.some((check) => check.status === "warn") ? "warn" : "pass";
|
|
21751
22010
|
var readinessGateCodes = {
|
|
21752
22011
|
"Agent squad contracts": "voice.readiness.agent_squad_contracts",
|
|
21753
22012
|
"Audit evidence": "voice.readiness.audit_evidence",
|
|
@@ -21760,6 +22019,7 @@ var readinessGateCodes = {
|
|
|
21760
22019
|
"Live latency proof": "voice.readiness.live_latency",
|
|
21761
22020
|
"Operations records": "voice.readiness.operations_records",
|
|
21762
22021
|
"Operator action history": "voice.readiness.operator_action_history",
|
|
22022
|
+
"Ops recovery": "voice.readiness.ops_recovery",
|
|
21763
22023
|
"Phone agent production smoke": "voice.readiness.phone_agent_smoke",
|
|
21764
22024
|
"Provider contract matrix": "voice.readiness.provider_contract_matrix",
|
|
21765
22025
|
"Provider fallback recovery": "voice.readiness.provider_fallback_recovery",
|
|
@@ -22004,6 +22264,15 @@ var summarizeOpsActionHistory = async (options) => {
|
|
|
22004
22264
|
warnWhenEmpty: opsActionHistory.warnWhenEmpty
|
|
22005
22265
|
};
|
|
22006
22266
|
};
|
|
22267
|
+
var resolveOpsRecovery = async (options, input) => {
|
|
22268
|
+
if (!options.opsRecovery) {
|
|
22269
|
+
return;
|
|
22270
|
+
}
|
|
22271
|
+
if (typeof options.opsRecovery === "function") {
|
|
22272
|
+
return options.opsRecovery(input);
|
|
22273
|
+
}
|
|
22274
|
+
return options.opsRecovery;
|
|
22275
|
+
};
|
|
22007
22276
|
var summarizeTraceDeliveries = async (options) => {
|
|
22008
22277
|
if (!options.traceDeliveries) {
|
|
22009
22278
|
return;
|
|
@@ -22093,7 +22362,7 @@ var summarizeLiveLatency = (events, options) => {
|
|
|
22093
22362
|
warnings
|
|
22094
22363
|
};
|
|
22095
22364
|
};
|
|
22096
|
-
var
|
|
22365
|
+
var getString14 = (value) => typeof value === "string" ? value : undefined;
|
|
22097
22366
|
var getNumber8 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
22098
22367
|
var voiceOperationsRecordHref = (base, sessionId) => {
|
|
22099
22368
|
const encoded = encodeURIComponent(sessionId);
|
|
@@ -22105,7 +22374,7 @@ var voiceOperationsRecordHref = (base, sessionId) => {
|
|
|
22105
22374
|
var buildOperationsRecordLinks = (input) => {
|
|
22106
22375
|
const failedSessionSet = new Set(input.failedSessionIds);
|
|
22107
22376
|
const providerErrors = input.events.filter((event) => event.type === "session.error" && (event.payload.providerStatus === "error" || typeof event.payload.error === "string")).map((event) => ({
|
|
22108
|
-
detail:
|
|
22377
|
+
detail: getString14(event.payload.error),
|
|
22109
22378
|
href: voiceOperationsRecordHref(input.base, event.sessionId),
|
|
22110
22379
|
label: "Open provider error operations record",
|
|
22111
22380
|
sessionId: event.sessionId,
|
|
@@ -22160,6 +22429,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
22160
22429
|
reconnectContracts,
|
|
22161
22430
|
bargeInReports,
|
|
22162
22431
|
campaignReadiness,
|
|
22432
|
+
opsRecovery,
|
|
22163
22433
|
proofSources
|
|
22164
22434
|
] = await Promise.all([
|
|
22165
22435
|
evaluateVoiceQuality({ events }),
|
|
@@ -22193,6 +22463,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
22193
22463
|
resolveReconnectContracts(options, { query, request }),
|
|
22194
22464
|
resolveBargeInReports(options, { query, request }),
|
|
22195
22465
|
resolveCampaignReadiness(options, { query, request }),
|
|
22466
|
+
resolveOpsRecovery(options, { query, request }),
|
|
22196
22467
|
resolveProofSources(options, { query, request })
|
|
22197
22468
|
]);
|
|
22198
22469
|
const deliveryRuntime = summarizeDeliveryRuntime(deliveryRuntimeSummary);
|
|
@@ -22256,6 +22527,27 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
22256
22527
|
}
|
|
22257
22528
|
]
|
|
22258
22529
|
},
|
|
22530
|
+
...opsRecovery ? [
|
|
22531
|
+
{
|
|
22532
|
+
...buildVoiceOpsRecoveryReadinessCheck(opsRecovery, {
|
|
22533
|
+
href: options.links?.opsRecovery ?? "/ops-recovery"
|
|
22534
|
+
}),
|
|
22535
|
+
actions: opsRecovery.status === "pass" ? [] : [
|
|
22536
|
+
...opsRecovery.issues[0]?.href ? [
|
|
22537
|
+
{
|
|
22538
|
+
description: "Open the exact impacted operations record for the first recovery issue.",
|
|
22539
|
+
href: opsRecovery.issues[0].href,
|
|
22540
|
+
label: "Open impacted operations record"
|
|
22541
|
+
}
|
|
22542
|
+
] : [],
|
|
22543
|
+
{
|
|
22544
|
+
description: "Open the unified recovery report for provider fallback, delivery, handoff, live-ops, and SLO issues.",
|
|
22545
|
+
href: options.links?.opsRecovery ?? "/ops-recovery",
|
|
22546
|
+
label: "Open ops recovery"
|
|
22547
|
+
}
|
|
22548
|
+
]
|
|
22549
|
+
}
|
|
22550
|
+
] : [],
|
|
22259
22551
|
{
|
|
22260
22552
|
detail: failedSessions === 0 ? sessions.length > 0 ? "Recent sessions have no recorded provider/session failures." : "No sessions have been recorded yet; run a smoke or live session for proof." : `${failedSessions} recent session(s) have failures.`,
|
|
22261
22553
|
href: firstOperationsRecordHref(operationsRecords.failedSessions) ?? options.links?.sessions ?? "/sessions",
|
|
@@ -22641,6 +22933,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
22641
22933
|
liveLatency: "/traces",
|
|
22642
22934
|
operationsRecords: "/voice-operations",
|
|
22643
22935
|
opsActions: "/voice/ops-actions",
|
|
22936
|
+
opsRecovery: "/ops-recovery",
|
|
22644
22937
|
phoneAgentSmoke: "/sessions",
|
|
22645
22938
|
providerContracts: "/provider-contracts",
|
|
22646
22939
|
providerRoutingContracts: "/resilience",
|
|
@@ -22654,7 +22947,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
22654
22947
|
profile: options.profile || undefined,
|
|
22655
22948
|
operationsRecords,
|
|
22656
22949
|
proofSources,
|
|
22657
|
-
status:
|
|
22950
|
+
status: rollupStatus3(checks),
|
|
22658
22951
|
summary: {
|
|
22659
22952
|
agentSquadContracts: agentSquadContractSummary,
|
|
22660
22953
|
audit,
|
|
@@ -22669,6 +22962,12 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
22669
22962
|
},
|
|
22670
22963
|
liveLatency,
|
|
22671
22964
|
opsActionHistory,
|
|
22965
|
+
opsRecovery: opsRecovery ? {
|
|
22966
|
+
issues: opsRecovery.issues.length,
|
|
22967
|
+
recoveredFallbacks: opsRecovery.providers.recoveredFallbacks,
|
|
22968
|
+
status: opsRecovery.status,
|
|
22969
|
+
unresolvedProviderFailures: opsRecovery.providers.unresolvedFailures
|
|
22970
|
+
} : undefined,
|
|
22672
22971
|
providers: {
|
|
22673
22972
|
degraded: degradedProviders,
|
|
22674
22973
|
total: providers.length
|
|
@@ -22697,22 +22996,22 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
22697
22996
|
var buildVoiceProductionReadinessGate = async (options, input = {}) => summarizeVoiceProductionReadinessGate(await buildVoiceProductionReadinessReport(options, input), options.gate || undefined);
|
|
22698
22997
|
var renderVoiceProductionReadinessHTML = (report, options = {}) => {
|
|
22699
22998
|
const title = options.title ?? "AbsoluteJS Voice Production Readiness";
|
|
22700
|
-
const profile = report.profile ? `<section class="profile"><p class="eyebrow">Readiness profile</p><h2>${
|
|
22999
|
+
const profile = report.profile ? `<section class="profile"><p class="eyebrow">Readiness profile</p><h2>${escapeHtml37(report.profile.name)}</h2><p>${escapeHtml37(report.profile.description)}</p><p>${escapeHtml37(report.profile.purpose)}</p><div class="profile-surfaces">${report.profile.surfaces.map((surface) => `<article class="${surface.configured ? "pass" : "warn"}"><span>${surface.configured ? "CONFIGURED" : "EXPECTED"}</span><strong>${surface.href ? `<a href="${escapeHtml37(surface.href)}">${escapeHtml37(surface.label)}</a>` : escapeHtml37(surface.label)}</strong></article>`).join("")}</div></section>` : "";
|
|
22701
23000
|
const checks = report.checks.map((check, index) => {
|
|
22702
|
-
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${
|
|
22703
|
-
return `<article class="check ${
|
|
23001
|
+
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${escapeHtml37(action.href)}">${escapeHtml37(action.label)}</button>` : `<a href="${escapeHtml37(action.href)}">${escapeHtml37(action.label)}</a>`).join("");
|
|
23002
|
+
return `<article class="check ${escapeHtml37(check.status)}">
|
|
22704
23003
|
<div>
|
|
22705
|
-
<span>${
|
|
22706
|
-
<h2>${
|
|
22707
|
-
${check.detail ? `<p>${
|
|
22708
|
-
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${
|
|
23004
|
+
<span>${escapeHtml37(check.status.toUpperCase())}</span>
|
|
23005
|
+
<h2>${escapeHtml37(check.label)}</h2>
|
|
23006
|
+
${check.detail ? `<p>${escapeHtml37(check.detail)}</p>` : ""}
|
|
23007
|
+
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${escapeHtml37(check.proofSource.href)}">${escapeHtml37(check.proofSource.sourceLabel)}</a>` : escapeHtml37(check.proofSource.sourceLabel)}${check.proofSource.detail ? ` \xB7 ${escapeHtml37(check.proofSource.detail)}` : ""}</p>` : ""}
|
|
22709
23008
|
${actions ? `<p class="actions">${actions}</p>` : ""}
|
|
22710
23009
|
</div>
|
|
22711
|
-
<strong>${
|
|
22712
|
-
${check.href ? `<a href="${
|
|
23010
|
+
<strong>${escapeHtml37(String(check.value ?? check.status))}</strong>
|
|
23011
|
+
${check.href ? `<a href="${escapeHtml37(check.href)}">Open surface</a>` : ""}
|
|
22713
23012
|
</article>`;
|
|
22714
23013
|
}).join("");
|
|
22715
|
-
const snippet =
|
|
23014
|
+
const snippet = escapeHtml37(`createVoiceProductionReadinessRoutes({
|
|
22716
23015
|
htmlPath: '/production-readiness',
|
|
22717
23016
|
path: '/api/production-readiness',
|
|
22718
23017
|
gatePath: '/api/production-readiness/gate',
|
|
@@ -22728,13 +23027,13 @@ var renderVoiceProductionReadinessHTML = (report, options = {}) => {
|
|
|
22728
23027
|
providerRoutingContracts: loadProviderRoutingContracts,
|
|
22729
23028
|
store: traceStore
|
|
22730
23029
|
});`);
|
|
22731
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
23030
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml37(title)}</title><style>body{background:#0c0f14;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1060px;padding:32px}.hero,.primitive,.profile{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.primitive,.profile{background:#111722}.primitive{border-color:#3a3f2d}.eyebrow{color:#fbbf24;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.status{display:inline-flex;border:1px solid #3f3f46;border-radius:999px;padding:8px 12px}.primitive code{color:#fde68a}.primitive p{color:#c8ccd3;line-height:1.55;margin:.45rem 0 0}.primitive pre{background:#0b0f16;border:1px solid #2c3440;border-radius:18px;color:#fef3c7;margin:16px 0 0;overflow:auto;padding:16px}.status.pass,.check.pass,.profile-surfaces .pass{border-color:rgba(34,197,94,.55)}.status.warn,.check.warn,.profile-surfaces .warn{border-color:rgba(245,158,11,.65)}.status.fail,.check.fail{border-color:rgba(239,68,68,.75)}.checks{display:grid;gap:14px}.check{align-items:center;background:#141922;border:1px solid #26313d;border-radius:22px;display:grid;gap:16px;grid-template-columns:1fr auto auto;padding:18px}.check span,.profile-surfaces span{color:#a8b0b8;font-size:.78rem;font-weight:900;letter-spacing:.08em}.check h2{margin:.2rem 0}.check p,.profile p{color:#b9c0c8;margin:.2rem 0 0}.check .proof-source{color:#f9d77e;font-weight:800}.check strong{font-size:1.5rem}.profile-surfaces{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));margin-top:16px}.profile-surfaces article{background:#141922;border:1px solid #26313d;border-radius:16px;padding:14px}.profile-surfaces strong{display:block;margin-top:6px}.actions{display:flex;flex-wrap:wrap;gap:10px}.check a,a{color:#fbbf24}button{background:#fbbf24;border:0;border-radius:999px;color:#111827;cursor:pointer;font-weight:800;padding:9px 12px}button:disabled{cursor:wait;opacity:.65}@media(max-width:760px){main{padding:20px}.check{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Self-hosted readiness</p><h1>${escapeHtml37(title)}</h1><p>One deployable pass/fail report for quality gates, provider failover, session health, handoffs, routing evidence, and optional carrier readiness.</p><p class="status ${escapeHtml37(report.status)}">Overall: ${escapeHtml37(report.status.toUpperCase())}</p><p>Checked ${escapeHtml37(new Date(report.checkedAt).toLocaleString())}</p></section>${profile}<section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceProductionReadinessRoutes(...)</code> builds this deploy gate</h2><p>Mount one package primitive to expose JSON readiness, HTML readiness, and a machine-readable gate route. Feed it the proof stores and contract reports your app already owns.</p><pre><code>${snippet}</code></pre></section><section class="checks">${checks}</section></main><script>document.querySelectorAll("[data-readiness-action]").forEach((button)=>{button.addEventListener("click",async()=>{const url=button.getAttribute("data-action-url");if(!url)return;button.disabled=true;const original=button.textContent;button.textContent="Running...";try{const response=await fetch(url,{method:"POST"});button.textContent=response.ok?"Done. Reloading...":"Failed";if(response.ok)setTimeout(()=>location.reload(),500)}catch{button.textContent="Failed"}finally{setTimeout(()=>{button.disabled=false;button.textContent=original},1500)}})});</script></body></html>`;
|
|
22732
23031
|
};
|
|
22733
23032
|
var createVoiceProductionReadinessRoutes = (options) => {
|
|
22734
23033
|
const path = options.path ?? "/api/production-readiness";
|
|
22735
23034
|
const gatePath = options.gatePath === undefined ? "/api/production-readiness/gate" : options.gatePath;
|
|
22736
23035
|
const htmlPath = options.htmlPath ?? "/production-readiness";
|
|
22737
|
-
const routes = new
|
|
23036
|
+
const routes = new Elysia36({
|
|
22738
23037
|
name: options.name ?? "absolutejs-voice-production-readiness"
|
|
22739
23038
|
});
|
|
22740
23039
|
routes.get(path, async ({ query, request }) => buildVoiceProductionReadinessReport(options, { query, request }));
|
|
@@ -23098,8 +23397,8 @@ var recommendVoiceReadinessProfile = (options) => {
|
|
|
23098
23397
|
};
|
|
23099
23398
|
};
|
|
23100
23399
|
// src/providerStackRecommendations.ts
|
|
23101
|
-
import { Elysia as
|
|
23102
|
-
var
|
|
23400
|
+
import { Elysia as Elysia37 } from "elysia";
|
|
23401
|
+
var escapeHtml38 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23103
23402
|
var profileProviderPriorities = {
|
|
23104
23403
|
"meeting-recorder": {
|
|
23105
23404
|
llm: ["openai", "anthropic", "gemini"],
|
|
@@ -23342,17 +23641,17 @@ var resolveProviderContractMatrixInput = async (matrix) => typeof matrix === "fu
|
|
|
23342
23641
|
var renderVoiceProviderContractMatrixHTML = (report, options = {}) => {
|
|
23343
23642
|
const title = options.title ?? "Voice Provider Contract Matrix";
|
|
23344
23643
|
const rows = report.rows.map((row) => {
|
|
23345
|
-
const checks = row.checks.map((check) => `<li class="${
|
|
23346
|
-
return `<article class="row ${
|
|
23644
|
+
const checks = row.checks.map((check) => `<li class="${escapeHtml38(check.status)}"><strong>${escapeHtml38(check.label)}</strong><span>${escapeHtml38(check.detail ?? check.status)}</span>${check.remediation ? `<em>${check.remediation.href ? `<a href="${escapeHtml38(check.remediation.href)}">${escapeHtml38(check.remediation.label)}</a>` : escapeHtml38(check.remediation.label)}: ${escapeHtml38(check.remediation.detail)}</em>` : ""}</li>`).join("");
|
|
23645
|
+
return `<article class="row ${escapeHtml38(row.status)}">
|
|
23347
23646
|
<div>
|
|
23348
|
-
<p class="eyebrow">${
|
|
23349
|
-
<h2>${
|
|
23350
|
-
<p class="status ${
|
|
23647
|
+
<p class="eyebrow">${escapeHtml38(row.kind)}${row.selected ? " \xB7 selected" : ""}</p>
|
|
23648
|
+
<h2>${escapeHtml38(row.provider)}</h2>
|
|
23649
|
+
<p class="status ${escapeHtml38(row.status)}">${escapeHtml38(row.status.toUpperCase())}</p>
|
|
23351
23650
|
</div>
|
|
23352
23651
|
<ul>${checks}</ul>
|
|
23353
23652
|
</article>`;
|
|
23354
23653
|
}).join("");
|
|
23355
|
-
const snippet =
|
|
23654
|
+
const snippet = escapeHtml38(`const providerContracts = () =>
|
|
23356
23655
|
createVoiceProviderContractMatrixPreset('phone-agent', {
|
|
23357
23656
|
env: process.env,
|
|
23358
23657
|
providers: {
|
|
@@ -23373,7 +23672,7 @@ createVoiceProductionReadinessRoutes({
|
|
|
23373
23672
|
providerContractMatrix: () =>
|
|
23374
23673
|
buildVoiceProviderContractMatrix(providerContracts())
|
|
23375
23674
|
});`);
|
|
23376
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
23675
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml38(title)}</title><style>body{background:#0f1412;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.primitive,.row{background:#17201b;border:1px solid #2d3b32;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.16),rgba(125,211,252,.12))}.primitive{background:#111814;border-color:#41604a}.eyebrow{color:#86efac;font-size:.78rem;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}h2{margin:.2rem 0}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill,.status{border:1px solid #3f4f45;border-radius:999px;display:inline-flex;padding:8px 12px}.primitive code{color:#bbf7d0}.primitive p{color:#c8d8ca;line-height:1.55;margin:.45rem 0 0}.primitive pre{background:#08110d;border:1px solid #294132;border-radius:18px;color:#d9f99d;margin:16px 0 0;overflow:auto;padding:16px}.status.pass,.row.pass,.pass{border-color:rgba(34,197,94,.65)}.status.warn,.row.warn,.warn{border-color:rgba(245,158,11,.7)}.status.fail,.row.fail,.fail{border-color:rgba(239,68,68,.75)}.row{display:grid;gap:20px;grid-template-columns:minmax(180px,.45fr) 1fr}.row ul{display:grid;gap:10px;list-style:none;margin:0;padding:0}.row li{background:#111814;border:1px solid #2d3b32;border-radius:16px;display:grid;gap:4px;padding:12px}.row li span{color:#b8c2ba}.row li em{color:#f9d77e;font-style:normal}.row li a{color:#86efac}@media(max-width:760px){main{padding:18px}.row{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Provider contracts</p><h1>${escapeHtml38(title)}</h1><p>Self-hosted provider proof for configured state, required env, latency budgets, fallback, streaming, and declared capabilities.</p><div class="summary"><span class="pill">${String(report.passed)} passing</span><span class="pill">${String(report.warned)} warning</span><span class="pill">${String(report.failed)} failing</span><span class="pill">${String(report.total)} total</span></div></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceProviderContractMatrixPreset(...)</code> builds this matrix</h2><p>Give AbsoluteJS your configured LLM, STT, and TTS providers once. It turns them into deploy-checkable proof for env, fallback, streaming, latency budgets, selected providers, and profile-required capabilities without a hosted dashboard.</p><pre><code>${snippet}</code></pre></section>${rows || '<article class="row"><p>No provider contracts configured.</p></article>'}</main></body></html>`;
|
|
23377
23676
|
};
|
|
23378
23677
|
var createVoiceProviderContractMatrixJSONHandler = (matrix) => async () => buildVoiceProviderContractMatrix(await resolveProviderContractMatrixInput(matrix));
|
|
23379
23678
|
var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
|
|
@@ -23388,7 +23687,7 @@ var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
|
|
|
23388
23687
|
var createVoiceProviderContractMatrixRoutes = (options) => {
|
|
23389
23688
|
const path = options.path ?? "/api/provider-contracts";
|
|
23390
23689
|
const htmlPath = options.htmlPath ?? "/provider-contracts";
|
|
23391
|
-
const routes = new
|
|
23690
|
+
const routes = new Elysia37({
|
|
23392
23691
|
name: options.name ?? "absolutejs-voice-provider-contract-matrix"
|
|
23393
23692
|
});
|
|
23394
23693
|
const jsonHandler = createVoiceProviderContractMatrixJSONHandler(options.matrix);
|
|
@@ -23447,7 +23746,7 @@ var evaluateVoiceProviderStackGaps = (input) => {
|
|
|
23447
23746
|
};
|
|
23448
23747
|
};
|
|
23449
23748
|
// src/opsConsoleRoutes.ts
|
|
23450
|
-
import { Elysia as
|
|
23749
|
+
import { Elysia as Elysia38 } from "elysia";
|
|
23451
23750
|
var DEFAULT_LINKS = [
|
|
23452
23751
|
{
|
|
23453
23752
|
description: "Quality gates for CI, deploy checks, and production readiness.",
|
|
@@ -23482,7 +23781,7 @@ var DEFAULT_LINKS = [
|
|
|
23482
23781
|
label: "Handoffs"
|
|
23483
23782
|
}
|
|
23484
23783
|
];
|
|
23485
|
-
var
|
|
23784
|
+
var escapeHtml39 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23486
23785
|
var countProviderStatuses = (providers) => {
|
|
23487
23786
|
const degradedStatuses = new Set(["degraded", "rate-limited", "suppressed"]);
|
|
23488
23787
|
const healthy = providers.filter((provider) => provider.status === "healthy").length;
|
|
@@ -23551,20 +23850,20 @@ var buildVoiceOpsConsoleReport = async (options) => {
|
|
|
23551
23850
|
trace
|
|
23552
23851
|
};
|
|
23553
23852
|
};
|
|
23554
|
-
var renderMetricCard = (input) => `<article class="metric"><span>${
|
|
23853
|
+
var renderMetricCard = (input) => `<article class="metric"><span>${escapeHtml39(input.label)}</span><strong>${escapeHtml39(String(input.value))}</strong>${input.status ? `<p class="${escapeHtml39(input.status)}">${escapeHtml39(input.status)}</p>` : ""}${input.href ? `<a href="${escapeHtml39(input.href)}">Open</a>` : ""}</article>`;
|
|
23555
23854
|
var renderVoiceOpsConsoleHTML = (report, options = {}) => {
|
|
23556
23855
|
const links = report.links.map((link) => `<article class="surface">
|
|
23557
|
-
<div><h2>${
|
|
23558
|
-
<p><a href="${
|
|
23856
|
+
<div><h2>${escapeHtml39(link.label)}</h2>${link.description ? `<p>${escapeHtml39(link.description)}</p>` : ""}</div>
|
|
23857
|
+
<p><a href="${escapeHtml39(link.href)}">Open ${escapeHtml39(link.label)}</a>${link.statusHref ? ` \xB7 <a href="${escapeHtml39(link.statusHref)}">Status</a>` : ""}</p>
|
|
23559
23858
|
</article>`).join("");
|
|
23560
|
-
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${
|
|
23561
|
-
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${
|
|
23859
|
+
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${escapeHtml39(session.sessionId)}</td><td>${escapeHtml39(session.status)}</td><td>${session.turnCount}</td><td>${session.errorCount}</td><td>${session.replayHref ? `<a href="${escapeHtml39(session.replayHref)}">Replay</a>` : ""}</td></tr>`).join("") : '<tr><td colspan="5">No sessions yet.</td></tr>';
|
|
23860
|
+
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${escapeHtml39(event.kind)}</td><td>${escapeHtml39(event.provider ?? "unknown")}</td><td>${escapeHtml39(event.status ?? "unknown")}</td><td>${event.elapsedMs ?? 0}ms</td><td>${escapeHtml39(event.sessionId)}</td></tr>`).join("") : '<tr><td colspan="5">No provider routing events yet.</td></tr>';
|
|
23562
23861
|
const title = options.title ?? "AbsoluteJS Voice Ops Console";
|
|
23563
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
23862
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml39(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;background:#101316;color:#f6f2e8;margin:0}main{max-width:1180px;margin:auto;padding:32px}a{color:#fbbf24}header{display:flex;justify-content:space-between;gap:24px;align-items:flex-start;margin-bottom:24px}.eyebrow{color:#fbbf24;font-weight:800;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.2rem,5vw,4.5rem);line-height:.95;margin:.2rem 0 1rem}.muted{color:#a8b0b8}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.metric,.surface{background:#181d22;border:1px solid #2a323a;border-radius:20px;padding:18px}.metric strong{display:block;font-size:2.2rem;margin:.25rem 0}.pass,.healthy{color:#86efac}.fail,.failed,.degraded{color:#fca5a5}.surfaces{display:grid;gap:16px;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));margin:24px 0}table{width:100%;border-collapse:collapse;background:#181d22;border-radius:16px;overflow:hidden;margin:12px 0 28px}td,th{border-bottom:1px solid #2a323a;padding:12px;text-align:left}section{margin-top:30px}@media(max-width:700px){main{padding:20px}header{display:block}}</style></head><body><main><header><div><p class="eyebrow">Self-hosted voice operations</p><h1>${escapeHtml39(title)}</h1><p class="muted">One deployable control plane for quality gates, failover, traces, sessions, handoffs, and provider health.</p></div><p class="muted">Checked ${escapeHtml39(new Date(report.checkedAt).toLocaleString())}</p></header><div class="grid">${renderMetricCard({ label: "Quality", value: report.quality.status, status: report.quality.status, href: "/quality" })}${renderMetricCard({ label: "Events", value: report.eventCount, href: "/diagnostics" })}${renderMetricCard({ label: "Sessions", value: report.sessions.total, status: report.sessions.failed > 0 ? "failed" : "healthy", href: "/sessions" })}${renderMetricCard({ label: "Handoffs failed", value: report.handoffs.failed, status: report.handoffs.failed > 0 ? "failed" : "healthy", href: "/handoffs" })}${renderMetricCard({ label: "Providers degraded", value: report.providers.degraded, status: report.providers.degraded > 0 ? "degraded" : "healthy", href: "/resilience" })}</div><section><h2>Operational Surfaces</h2><div class="surfaces">${links}</div></section><section><h2>Recent Sessions</h2><table><thead><tr><th>Session</th><th>Status</th><th>Turns</th><th>Errors</th><th>Replay</th></tr></thead><tbody>${sessions}</tbody></table></section><section><h2>Recent Provider Routing</h2><table><thead><tr><th>Kind</th><th>Provider</th><th>Status</th><th>Elapsed</th><th>Session</th></tr></thead><tbody>${routing}</tbody></table></section></main></body></html>`;
|
|
23564
23863
|
};
|
|
23565
23864
|
var createVoiceOpsConsoleRoutes = (options) => {
|
|
23566
23865
|
const path = options.path ?? "/ops-console";
|
|
23567
|
-
const routes = new
|
|
23866
|
+
const routes = new Elysia38({
|
|
23568
23867
|
name: options.name ?? "absolutejs-voice-ops-console"
|
|
23569
23868
|
});
|
|
23570
23869
|
const getReport = () => buildVoiceOpsConsoleReport(options);
|
|
@@ -23581,16 +23880,16 @@ var createVoiceOpsConsoleRoutes = (options) => {
|
|
|
23581
23880
|
return routes;
|
|
23582
23881
|
};
|
|
23583
23882
|
// src/operationsRecord.ts
|
|
23584
|
-
import { Elysia as
|
|
23883
|
+
import { Elysia as Elysia40 } from "elysia";
|
|
23585
23884
|
|
|
23586
23885
|
// src/traceTimeline.ts
|
|
23587
|
-
import { Elysia as
|
|
23588
|
-
var
|
|
23589
|
-
var
|
|
23886
|
+
import { Elysia as Elysia39 } from "elysia";
|
|
23887
|
+
var escapeHtml40 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23888
|
+
var getString15 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
23590
23889
|
var getNumber9 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
23591
23890
|
var firstString3 = (payload, keys) => {
|
|
23592
23891
|
for (const key of keys) {
|
|
23593
|
-
const value =
|
|
23892
|
+
const value = getString15(payload[key]);
|
|
23594
23893
|
if (value) {
|
|
23595
23894
|
return value;
|
|
23596
23895
|
}
|
|
@@ -23627,15 +23926,15 @@ var timelineLabel = (event) => {
|
|
|
23627
23926
|
case "turn.transcript":
|
|
23628
23927
|
return event.payload.isFinal === true ? "Final transcript" : "Partial transcript";
|
|
23629
23928
|
case "turn.committed":
|
|
23630
|
-
return `Committed turn${
|
|
23929
|
+
return `Committed turn${getString15(event.payload.reason) ? ` (${getString15(event.payload.reason)})` : ""}`;
|
|
23631
23930
|
case "turn.assistant":
|
|
23632
23931
|
return "Assistant reply";
|
|
23633
23932
|
case "agent.model":
|
|
23634
23933
|
return `Model call${eventProvider(event) ? ` via ${eventProvider(event)}` : ""}`;
|
|
23635
23934
|
case "agent.tool":
|
|
23636
|
-
return `Tool ${
|
|
23935
|
+
return `Tool ${getString15(event.payload.toolName) ?? "call"}`;
|
|
23637
23936
|
case "agent.handoff":
|
|
23638
|
-
return `Agent handoff${
|
|
23937
|
+
return `Agent handoff${getString15(event.payload.targetAgentId) ? ` to ${getString15(event.payload.targetAgentId)}` : ""}`;
|
|
23639
23938
|
case "assistant.run":
|
|
23640
23939
|
return `Assistant run${eventProvider(event) ? ` via ${eventProvider(event)}` : ""}`;
|
|
23641
23940
|
case "assistant.guardrail":
|
|
@@ -23645,11 +23944,11 @@ var timelineLabel = (event) => {
|
|
|
23645
23944
|
case "client.live_latency":
|
|
23646
23945
|
return `Live latency${eventElapsedMs2(event) !== undefined ? ` ${eventElapsedMs2(event)}ms` : ""}`;
|
|
23647
23946
|
case "session.error":
|
|
23648
|
-
return `Error${
|
|
23947
|
+
return `Error${getString15(event.payload.error) ? `: ${getString15(event.payload.error)}` : ""}`;
|
|
23649
23948
|
case "turn.cost":
|
|
23650
23949
|
return "Cost telemetry";
|
|
23651
23950
|
case "turn_latency.stage":
|
|
23652
|
-
return `Latency ${
|
|
23951
|
+
return `Latency ${getString15(event.payload.stage) ?? "stage"}`;
|
|
23653
23952
|
case "workflow.contract":
|
|
23654
23953
|
return `Workflow contract ${eventStatus(event) ?? ""}`.trim();
|
|
23655
23954
|
default:
|
|
@@ -23753,16 +24052,16 @@ var summarizeVoiceTraceTimeline = (events, options = {}) => {
|
|
|
23753
24052
|
};
|
|
23754
24053
|
};
|
|
23755
24054
|
var formatMs3 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
23756
|
-
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>${
|
|
24055
|
+
var renderProviderCards2 = (session) => session.providers.length === 0 ? '<p class="muted">No provider events recorded for this session.</p>' : `<div class="providers">${session.providers.map((provider) => `<article><strong>${escapeHtml40(provider.provider)}</strong><dl><div><dt>Events</dt><dd>${String(provider.eventCount)}</dd></div><div><dt>Avg</dt><dd>${formatMs3(provider.averageElapsedMs)}</dd></div><div><dt>Max</dt><dd>${formatMs3(provider.maxElapsedMs)}</dd></div><div><dt>Errors</dt><dd>${String(provider.errorCount)}</dd></div><div><dt>Fallbacks</dt><dd>${String(provider.fallbackCount)}</dd></div><div><dt>Timeouts</dt><dd>${String(provider.timeoutCount)}</dd></div></dl></article>`).join("")}</div>`;
|
|
23757
24056
|
var renderVoiceTraceTimelineSessionHTML = (session, options = {}) => {
|
|
23758
|
-
const events = session.events.map((event) => `<tr class="${
|
|
23759
|
-
const issues = session.evaluation.issues.length ? session.evaluation.issues.map((issue) => `<li class="${
|
|
23760
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
24057
|
+
const events = session.events.map((event) => `<tr class="${escapeHtml40(event.status ?? "")}"><td>+${String(event.offsetMs)}ms</td><td>${escapeHtml40(event.type)}</td><td>${escapeHtml40(event.label)}</td><td>${escapeHtml40(event.provider ?? "")}</td><td>${escapeHtml40(event.status ?? "")}</td><td>${formatMs3(event.elapsedMs)}</td></tr>`).join("");
|
|
24058
|
+
const issues = session.evaluation.issues.length ? session.evaluation.issues.map((issue) => `<li class="${escapeHtml40(issue.severity)}">${escapeHtml40(issue.code)}: ${escapeHtml40(issue.message)}</li>`).join("") : "<li>none</li>";
|
|
24059
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml40(options.title ?? "Voice Trace Timeline")}</title><style>${timelineCSS}</style></head><body><main><a href="/traces">Back to traces</a><header><p class="eyebrow">Call timeline</p><h1>${escapeHtml40(session.sessionId)}</h1><p class="status ${escapeHtml40(session.status)}">${escapeHtml40(session.status)}</p></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>`;
|
|
23761
24060
|
};
|
|
23762
|
-
var renderSessionRows = (report) => report.sessions.length === 0 ? '<tr><td colspan="7">No trace events recorded yet.</td></tr>' : report.sessions.map((session) => `<tr class="${
|
|
24061
|
+
var renderSessionRows = (report) => report.sessions.length === 0 ? '<tr><td colspan="7">No trace events recorded yet.</td></tr>' : report.sessions.map((session) => `<tr class="${escapeHtml40(session.status)}"><td><a href="/traces/${encodeURIComponent(session.sessionId)}">${escapeHtml40(session.sessionId)}</a></td><td>${escapeHtml40(session.status)}</td><td>${String(session.summary.eventCount)}</td><td>${String(session.summary.turnCount)}</td><td>${String(session.summary.errorCount)}</td><td>${formatMs3(session.summary.callDurationMs)}</td><td>${session.providers.map((provider) => escapeHtml40(provider.provider)).join(", ")}</td></tr>`).join("");
|
|
23763
24062
|
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}}";
|
|
23764
24063
|
var renderVoiceTraceTimelineHTML = (report, options = {}) => {
|
|
23765
|
-
const snippet =
|
|
24064
|
+
const snippet = escapeHtml40(`const traceStore = createVoiceTraceSinkStore({
|
|
23766
24065
|
store: runtimeStorage.traces,
|
|
23767
24066
|
sinks: [
|
|
23768
24067
|
createVoiceTraceHTTPSink({
|
|
@@ -23788,13 +24087,13 @@ app.use(
|
|
|
23788
24087
|
traceDeliveries: runtimeStorage.traceDeliveries
|
|
23789
24088
|
})
|
|
23790
24089
|
);`);
|
|
23791
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
24090
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml40(options.title ?? "Voice Trace Timelines")}</title><style>${timelineCSS}.primitive{background:#181f27;border:1px solid #334155;border-radius:20px;margin:20px 0;padding:18px}.primitive p{line-height:1.55}.primitive pre{background:#0b1118;border:1px solid #2b3642;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}.primitive code{color:#bfdbfe}</style></head><body><main><header><p class="eyebrow">Self-hosted voice debugging</p><h1>${escapeHtml40(options.title ?? "Voice Trace Timelines")}</h1><p class="muted">Per-call event timelines with provider latency, fallback, timeout, handoff, and error context.</p></header><section class="metrics"><article><span>Sessions</span><strong>${String(report.total)}</strong></article><article><span>Failed</span><strong>${String(report.failed)}</strong></article><article><span>Warnings</span><strong>${String(report.warnings)}</strong></article></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceTraceTimelineRoutes(...)</code> makes traces the proof backbone</h2><p class="muted">Mount trace timelines from the same trace store used by readiness, simulations, provider recovery, delivery sinks, and phone-agent smoke proof.</p><pre><code>${snippet}</code></pre></section><table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Turns</th><th>Errors</th><th>Duration</th><th>Providers</th></tr></thead><tbody>${renderSessionRows(report)}</tbody></table></main></body></html>`;
|
|
23792
24091
|
};
|
|
23793
24092
|
var createVoiceTraceTimelineRoutes = (options) => {
|
|
23794
24093
|
const path = options.path ?? "/api/voice-traces";
|
|
23795
24094
|
const htmlPath = options.htmlPath ?? "/traces";
|
|
23796
24095
|
const title = options.title ?? "AbsoluteJS Voice Trace Timelines";
|
|
23797
|
-
const routes = new
|
|
24096
|
+
const routes = new Elysia39({
|
|
23798
24097
|
name: options.name ?? "absolutejs-voice-trace-timelines"
|
|
23799
24098
|
});
|
|
23800
24099
|
const buildReport = async () => summarizeVoiceTraceTimeline(await options.store.list(), {
|
|
@@ -23842,7 +24141,7 @@ var createVoiceTraceTimelineRoutes = (options) => {
|
|
|
23842
24141
|
};
|
|
23843
24142
|
|
|
23844
24143
|
// src/operationsRecord.ts
|
|
23845
|
-
var
|
|
24144
|
+
var getString16 = (value) => typeof value === "string" ? value : undefined;
|
|
23846
24145
|
var getNumber10 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
23847
24146
|
var countOutcome = (events, outcome) => events.filter((event) => event.outcome === outcome).length;
|
|
23848
24147
|
var matchesSessionScopedId = (id, sessionId) => id === sessionId || id.startsWith(`${sessionId}:`);
|
|
@@ -23853,21 +24152,21 @@ var hasPayloadValue = (payload, key, values) => {
|
|
|
23853
24152
|
var countIntegrationDeliveryStatus = (events, status) => events.filter((event) => event.deliveryStatus === status).length;
|
|
23854
24153
|
var toHandoff = (event) => ({
|
|
23855
24154
|
at: event.at,
|
|
23856
|
-
fromAgentId:
|
|
24155
|
+
fromAgentId: getString16(event.payload.fromAgentId),
|
|
23857
24156
|
metadata: event.payload.metadata && typeof event.payload.metadata === "object" && !Array.isArray(event.payload.metadata) ? event.payload.metadata : undefined,
|
|
23858
|
-
reason:
|
|
23859
|
-
status:
|
|
23860
|
-
summary:
|
|
23861
|
-
targetAgentId:
|
|
24157
|
+
reason: getString16(event.payload.reason),
|
|
24158
|
+
status: getString16(event.payload.status),
|
|
24159
|
+
summary: getString16(event.payload.summary),
|
|
24160
|
+
targetAgentId: getString16(event.payload.targetAgentId),
|
|
23862
24161
|
turnId: event.turnId
|
|
23863
24162
|
});
|
|
23864
24163
|
var toTool = (event) => ({
|
|
23865
24164
|
at: event.at,
|
|
23866
24165
|
elapsedMs: getNumber10(event.payload.elapsedMs),
|
|
23867
|
-
error:
|
|
23868
|
-
status:
|
|
23869
|
-
toolCallId:
|
|
23870
|
-
toolName:
|
|
24166
|
+
error: getString16(event.payload.error),
|
|
24167
|
+
status: getString16(event.payload.status),
|
|
24168
|
+
toolCallId: getString16(event.payload.toolCallId),
|
|
24169
|
+
toolName: getString16(event.payload.toolName),
|
|
23871
24170
|
turnId: event.turnId
|
|
23872
24171
|
});
|
|
23873
24172
|
var resolveOutcome4 = (events) => {
|
|
@@ -23948,16 +24247,16 @@ var buildVoiceOperationsRecord = async (options) => {
|
|
|
23948
24247
|
traceEvents
|
|
23949
24248
|
};
|
|
23950
24249
|
};
|
|
23951
|
-
var
|
|
24250
|
+
var escapeHtml41 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
23952
24251
|
var formatMs4 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
23953
24252
|
var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
23954
|
-
const providers = record.providers.length ? record.providers.map((provider) => `<article><strong>${
|
|
23955
|
-
const handoffs = record.handoffs.length ? record.handoffs.map((handoff) => `<li><strong>${
|
|
23956
|
-
const tools = record.tools.length ? record.tools.map((tool) => `<li><strong>${
|
|
23957
|
-
const reviews = record.reviews?.reviews.length ? record.reviews.reviews.map((review) => `<li><strong>${
|
|
23958
|
-
const tasks = record.tasks?.tasks.length ? record.tasks.tasks.map((task) => `<li><strong>${
|
|
23959
|
-
const integrationEvents = record.integrationEvents?.events.length ? record.integrationEvents.events.map((event) => `<li><strong>${
|
|
23960
|
-
const snippet =
|
|
24253
|
+
const providers = record.providers.length ? record.providers.map((provider) => `<article><strong>${escapeHtml41(provider.provider)}</strong><span>${String(provider.eventCount)} events</span><span>${formatMs4(provider.averageElapsedMs)} avg</span><span>${String(provider.errorCount)} errors</span></article>`).join("") : '<p class="muted">No provider events recorded.</p>';
|
|
24254
|
+
const handoffs = record.handoffs.length ? record.handoffs.map((handoff) => `<li><strong>${escapeHtml41(handoff.fromAgentId ?? "unknown")}</strong> to <strong>${escapeHtml41(handoff.targetAgentId ?? "unknown")}</strong> <span>${escapeHtml41(handoff.status ?? "")}</span><p>${escapeHtml41(handoff.summary ?? handoff.reason ?? "")}</p></li>`).join("") : "<li>No agent handoffs recorded.</li>";
|
|
24255
|
+
const tools = record.tools.length ? record.tools.map((tool) => `<li><strong>${escapeHtml41(tool.toolName ?? "tool")}</strong> <span>${escapeHtml41(tool.status ?? "")}</span> ${formatMs4(tool.elapsedMs)} ${tool.error ? `<p>${escapeHtml41(tool.error)}</p>` : ""}</li>`).join("") : "<li>No tool calls recorded.</li>";
|
|
24256
|
+
const reviews = record.reviews?.reviews.length ? record.reviews.reviews.map((review) => `<li><strong>${escapeHtml41(review.title)}</strong> <span>${escapeHtml41(review.summary.outcome ?? "")}</span><p>${escapeHtml41(review.postCall?.summary ?? review.transcript.actual)}</p></li>`).join("") : "<li>No call reviews recorded.</li>";
|
|
24257
|
+
const tasks = record.tasks?.tasks.length ? record.tasks.tasks.map((task) => `<li><strong>${escapeHtml41(task.title)}</strong> <span>${escapeHtml41(task.status)}</span><p>${escapeHtml41(task.recommendedAction)}</p></li>`).join("") : "<li>No ops tasks recorded.</li>";
|
|
24258
|
+
const integrationEvents = record.integrationEvents?.events.length ? record.integrationEvents.events.map((event) => `<li><strong>${escapeHtml41(event.type)}</strong> <span>${escapeHtml41(event.deliveryStatus ?? "local")}</span><p>${escapeHtml41(event.deliveryError ?? event.deliveredTo ?? "")}</p></li>`).join("") : "<li>No integration events recorded.</li>";
|
|
24259
|
+
const snippet = escapeHtml41(`app.use(
|
|
23961
24260
|
createVoiceOperationsRecordRoutes({
|
|
23962
24261
|
audit: auditStore,
|
|
23963
24262
|
integrationEvents: opsEvents,
|
|
@@ -23971,12 +24270,12 @@ var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
|
23971
24270
|
tasks: opsTasks
|
|
23972
24271
|
})
|
|
23973
24272
|
);`);
|
|
23974
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
24273
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml41(options.title ?? "Voice Operations Record")}</title><style>body{background:#101417;color:#f9f4e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.eyebrow{color:#fbbf24;font-size:.8rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.8rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #475569;border-radius:999px;display:inline-flex;padding:8px 12px}.healthy{color:#86efac}.warning{color:#fbbf24}.failed,.error{color:#fca5a5}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.card,.primitive{background:#182025;border:1px solid #2d3a43;border-radius:20px;padding:16px}.card span,.muted{color:#a9b4bd}.card strong{display:block;font-size:2rem}section{margin-top:28px}article{display:grid;gap:8px}ul{display:grid;gap:10px;list-style:none;padding:0}li{background:#182025;border:1px solid #2d3a43;border-radius:16px;padding:14px}pre{background:#080d10;border:1px solid #2d3a43;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}</style></head><body><main><p class="eyebrow">Portable production proof</p><h1>${escapeHtml41(options.title ?? "Voice Operations Record")}</h1><p class="status ${escapeHtml41(record.status)}">${escapeHtml41(record.status)}</p><section class="grid"><div class="card"><span>Events</span><strong>${String(record.summary.eventCount)}</strong></div><div class="card"><span>Turns</span><strong>${String(record.summary.turnCount)}</strong></div><div class="card"><span>Errors</span><strong>${String(record.summary.errorCount)}</strong></div><div class="card"><span>Duration</span><strong>${formatMs4(record.summary.callDurationMs)}</strong></div><div class="card"><span>Audit</span><strong>${String(record.audit?.total ?? 0)}</strong></div><div class="card"><span>Reviews</span><strong>${String(record.reviews?.total ?? 0)}</strong></div><div class="card"><span>Tasks</span><strong>${String(record.tasks?.total ?? 0)}</strong></div><div class="card"><span>Integrations</span><strong>${String(record.integrationEvents?.total ?? 0)}</strong></div></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceOperationsRecordRoutes(...)</code> gives every call one debuggable object</h2><p class="muted">Use this as the support/debug payload across traces, provider routing, tools, handoffs, audit, latency, replay, reviews, tasks, and webhook delivery.</p><pre><code>${snippet}</code></pre></section><section><h2>Providers</h2><div class="grid">${providers}</div></section><section><h2>Handoffs</h2><ul>${handoffs}</ul></section><section><h2>Tools</h2><ul>${tools}</ul></section><section><h2>Reviews</h2><ul>${reviews}</ul></section><section><h2>Tasks</h2><ul>${tasks}</ul></section><section><h2>Integration Events</h2><ul>${integrationEvents}</ul></section></main></body></html>`;
|
|
23975
24274
|
};
|
|
23976
24275
|
var createVoiceOperationsRecordRoutes = (options) => {
|
|
23977
24276
|
const path = options.path ?? "/api/voice-operations/:sessionId";
|
|
23978
24277
|
const htmlPath = options.htmlPath === undefined ? "/voice-operations/:sessionId" : options.htmlPath;
|
|
23979
|
-
const routes = new
|
|
24278
|
+
const routes = new Elysia40({
|
|
23980
24279
|
name: options.name ?? "absolutejs-voice-operations-record"
|
|
23981
24280
|
});
|
|
23982
24281
|
const buildRecord = (sessionId) => buildVoiceOperationsRecord({
|
|
@@ -24009,7 +24308,7 @@ var createVoiceOperationsRecordRoutes = (options) => {
|
|
|
24009
24308
|
return routes;
|
|
24010
24309
|
};
|
|
24011
24310
|
// src/incidentBundle.ts
|
|
24012
|
-
import { Elysia as
|
|
24311
|
+
import { Elysia as Elysia41 } from "elysia";
|
|
24013
24312
|
var filterIncidentBundleArtifacts = (artifacts, filter = {}) => artifacts.filter((artifact) => {
|
|
24014
24313
|
if (filter.sessionId && artifact.sessionId !== filter.sessionId) {
|
|
24015
24314
|
return false;
|
|
@@ -24208,7 +24507,7 @@ var buildVoiceIncidentBundle = async (options) => {
|
|
|
24208
24507
|
var createVoiceIncidentBundleRoutes = (options) => {
|
|
24209
24508
|
const path = options.path ?? "/api/voice-incidents/:sessionId";
|
|
24210
24509
|
const markdownPath = options.markdownPath === undefined ? "/voice-incidents/:sessionId/markdown" : options.markdownPath;
|
|
24211
|
-
const routes = new
|
|
24510
|
+
const routes = new Elysia41({
|
|
24212
24511
|
name: options.name ?? "absolutejs-voice-incident-bundle"
|
|
24213
24512
|
});
|
|
24214
24513
|
const getSessionId = (params) => params.sessionId ?? "";
|
|
@@ -24409,19 +24708,19 @@ var summarizeVoiceOpsStatus = async (options) => {
|
|
|
24409
24708
|
};
|
|
24410
24709
|
};
|
|
24411
24710
|
// src/opsStatusRoutes.ts
|
|
24412
|
-
import { Elysia as
|
|
24413
|
-
var
|
|
24711
|
+
import { Elysia as Elysia42 } from "elysia";
|
|
24712
|
+
var escapeHtml42 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
24414
24713
|
var renderVoiceOpsStatusHTML = (report, options = {}) => {
|
|
24415
24714
|
const title = options.title ?? "AbsoluteJS Voice Ops Status";
|
|
24416
24715
|
const surfaces = Object.entries(report.surfaces).map(([key, surface]) => {
|
|
24417
24716
|
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;
|
|
24418
|
-
return `<article class="surface ${
|
|
24717
|
+
return `<article class="surface ${escapeHtml42(surface.status)}"><span>${escapeHtml42(surface.status.toUpperCase())}</span><h2>${escapeHtml42(key)}</h2><strong>${escapeHtml42(value)}</strong></article>`;
|
|
24419
24718
|
}).join("");
|
|
24420
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
24719
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml42(title)}</title><style>body{background:#0d141b;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.2),rgba(245,158,11,.12));border:1px solid #283544;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.surfaces{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.surface{background:#151d26;border:1px solid #283544;border-radius:20px;padding:18px}.surface span{color:#aab5c0;font-size:.78rem;font-weight:900;letter-spacing:.08em}.surface strong{font-size:1.5rem}.pass{border-color:rgba(34,197,94,.55)}.fail{border-color:rgba(239,68,68,.75)}a{color:#5eead4}</style></head><body><main><section class="hero"><p class="eyebrow">Ops status</p><h1>${escapeHtml42(title)}</h1><p>Compact pass/fail status for framework widgets, demos, and small customer-facing health badges.</p><p class="status ${escapeHtml42(report.status)}">Overall: ${escapeHtml42(report.status.toUpperCase())}</p><p>${report.passed}/${report.total} checks passing</p></section><section class="surfaces">${surfaces || '<article class="surface pass"><span>PASS</span><h2>No checks configured</h2><strong>0/0</strong></article>'}</section></main></body></html>`;
|
|
24421
24720
|
};
|
|
24422
24721
|
var createVoiceOpsStatusRoutes = (options) => {
|
|
24423
24722
|
const path = options.path ?? "/api/voice/ops-status";
|
|
24424
|
-
const routes = new
|
|
24723
|
+
const routes = new Elysia42({
|
|
24425
24724
|
name: options.name ?? "absolutejs-voice-ops-status"
|
|
24426
24725
|
});
|
|
24427
24726
|
routes.get(path, async () => summarizeVoiceOpsStatus(options));
|
|
@@ -24854,9 +25153,9 @@ var createVoiceTTSProviderRouter = (options) => {
|
|
|
24854
25153
|
};
|
|
24855
25154
|
};
|
|
24856
25155
|
// src/traceDeliveryRoutes.ts
|
|
24857
|
-
import { Elysia as
|
|
24858
|
-
var
|
|
24859
|
-
var
|
|
25156
|
+
import { Elysia as Elysia43 } from "elysia";
|
|
25157
|
+
var escapeHtml43 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
25158
|
+
var getString17 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
24860
25159
|
var getNumber11 = (value) => {
|
|
24861
25160
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
24862
25161
|
return value;
|
|
@@ -24868,13 +25167,13 @@ var getNumber11 = (value) => {
|
|
|
24868
25167
|
return;
|
|
24869
25168
|
};
|
|
24870
25169
|
var parseStatus2 = (value) => {
|
|
24871
|
-
const text =
|
|
25170
|
+
const text = getString17(value);
|
|
24872
25171
|
return text === "pending" || text === "delivered" || text === "failed" || text === "skipped" || text === "all" ? text : undefined;
|
|
24873
25172
|
};
|
|
24874
25173
|
var resolveVoiceTraceDeliveryFilter = (query = {}, base = {}) => ({
|
|
24875
25174
|
...base,
|
|
24876
25175
|
limit: getNumber11(query.limit) ?? base.limit,
|
|
24877
|
-
q:
|
|
25176
|
+
q: getString17(query.q) ?? base.q,
|
|
24878
25177
|
status: parseStatus2(query.status) ?? base.status
|
|
24879
25178
|
});
|
|
24880
25179
|
var deliverySearchText2 = (delivery) => [
|
|
@@ -24936,14 +25235,14 @@ var renderSinkResults2 = (delivery) => {
|
|
|
24936
25235
|
if (entries.length === 0) {
|
|
24937
25236
|
return "<p>No sink delivery attempts recorded yet.</p>";
|
|
24938
25237
|
}
|
|
24939
|
-
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${
|
|
25238
|
+
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${escapeHtml43(sinkId)}</strong>: ${escapeHtml43(result.status)}${result.deliveredTo ? ` to ${escapeHtml43(result.deliveredTo)}` : ""}${result.error ? ` (${escapeHtml43(result.error)})` : ""}</li>`).join("")}</ul>`;
|
|
24940
25239
|
};
|
|
24941
|
-
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${
|
|
25240
|
+
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${escapeHtml43(event.type)} <small>${escapeHtml43(event.id)}</small>${event.sessionId ? ` session=${escapeHtml43(event.sessionId)}` : ""}</li>`).join("")}</ul>`;
|
|
24942
25241
|
var renderVoiceTraceDeliveryHTML = (report, options = {}) => {
|
|
24943
25242
|
const title = options.title ?? "AbsoluteJS Voice Trace Deliveries";
|
|
24944
|
-
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${
|
|
24945
|
-
const rows = report.deliveries.map((delivery) => `<article class="delivery ${
|
|
24946
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
25243
|
+
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${escapeHtml43(options.workerPath ?? "/api/voice-trace-deliveries/drain")}"><button type="submit">Drain trace deliveries</button></form>`;
|
|
25244
|
+
const rows = report.deliveries.map((delivery) => `<article class="delivery ${escapeHtml43(delivery.deliveryStatus)}"><div class="head"><div><span>${escapeHtml43(delivery.deliveryStatus)}</span><h2>${escapeHtml43(delivery.id)}</h2><p>${escapeHtml43(new Date(delivery.createdAt).toLocaleString())}${delivery.deliveredAt ? ` \xB7 delivered ${escapeHtml43(new Date(delivery.deliveredAt).toLocaleString())}` : ""}</p></div><strong>${String(delivery.deliveryAttempts ?? 0)} attempt(s)</strong></div>${delivery.deliveryError ? `<p class="error">${escapeHtml43(delivery.deliveryError)}</p>` : ""}<h3>Sinks</h3>${renderSinkResults2(delivery)}<h3>Events</h3>${renderEventList2(delivery)}</article>`).join("");
|
|
25245
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml43(title)}</title><style>body{background:#0f1318;color:#f4efe1;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.16),rgba(14,165,233,.14));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#86efac;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,5vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.grid{display:grid;gap:12px;grid-template-columns:repeat(4,1fr);margin-bottom:16px}.grid article,.delivery{background:#151b22;border:1px solid #26313d;border-radius:22px;padding:18px}.grid span,.delivery span{color:#86efac;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.grid strong{display:block;font-size:2rem}.deliveries{display:grid;gap:14px}.delivery.failed{border-color:rgba(239,68,68,.75)}.delivery.pending{border-color:rgba(245,158,11,.7)}.delivery.delivered{border-color:rgba(34,197,94,.55)}.delivery.skipped{border-color:rgba(148,163,184,.6)}.head{align-items:start;display:flex;gap:14px;justify-content:space-between}.delivery h2{font-size:1.05rem;margin:.3rem 0;overflow-wrap:anywhere}.delivery h3{margin:1rem 0 .3rem}.delivery p,.delivery li{color:#c8d0d8}.error{color:#fecaca!important}button{background:#86efac;border:0;border-radius:999px;color:#07111f;cursor:pointer;font-weight:900;margin-top:12px;padding:10px 14px}@media(max-width:760px){main{padding:20px}.grid{grid-template-columns:1fr 1fr}.head{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Trace export health</p><h1>${escapeHtml43(title)}</h1><p>Checked ${escapeHtml43(new Date(report.checkedAt).toLocaleString())}. Showing ${String(report.deliveries.length)} delivery item(s).</p>${drainAction}</section>${renderMetricGrid3(report)}<section class="deliveries">${rows || "<p>No trace deliveries match this filter.</p>"}</section></main></body></html>`;
|
|
24947
25246
|
};
|
|
24948
25247
|
var createVoiceTraceDeliveryJSONHandler = (options) => async ({ query }) => buildVoiceTraceDeliveryReport(options, resolveVoiceTraceDeliveryFilter(query, options.filter));
|
|
24949
25248
|
var createVoiceTraceDeliveryHTMLHandler = (options) => async ({ query }) => {
|
|
@@ -24963,7 +25262,7 @@ var createVoiceTraceDeliveryRoutes = (options) => {
|
|
|
24963
25262
|
const path = options.path ?? "/api/voice-trace-deliveries";
|
|
24964
25263
|
const htmlPath = options.htmlPath === undefined ? "/traces/deliveries" : options.htmlPath;
|
|
24965
25264
|
const workerPath = options.workerPath === undefined ? `${path}/drain` : options.workerPath;
|
|
24966
|
-
const routes = new
|
|
25265
|
+
const routes = new Elysia43({
|
|
24967
25266
|
name: options.name ?? "absolutejs-voice-trace-deliveries"
|
|
24968
25267
|
}).get(path, createVoiceTraceDeliveryJSONHandler(options));
|
|
24969
25268
|
if (htmlPath !== false) {
|
|
@@ -25587,7 +25886,7 @@ var createVoiceMemoryStore = () => {
|
|
|
25587
25886
|
return { get, getOrCreate, list, remove, set };
|
|
25588
25887
|
};
|
|
25589
25888
|
// src/opsWebhook.ts
|
|
25590
|
-
import { Elysia as
|
|
25889
|
+
import { Elysia as Elysia44 } from "elysia";
|
|
25591
25890
|
var toHex6 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
25592
25891
|
var signVoiceOpsWebhookBody = async (input) => {
|
|
25593
25892
|
const encoder = new TextEncoder;
|
|
@@ -25717,7 +26016,7 @@ var verifyVoiceOpsWebhookSignature = async (input) => {
|
|
|
25717
26016
|
};
|
|
25718
26017
|
var createVoiceOpsWebhookReceiverRoutes = (options = {}) => {
|
|
25719
26018
|
const path = options.path ?? "/api/voice-ops/webhook";
|
|
25720
|
-
return new
|
|
26019
|
+
return new Elysia44().post(path, async ({ body, request, set }) => {
|
|
25721
26020
|
const bodyText = typeof body === "string" ? body : JSON.stringify(body);
|
|
25722
26021
|
if (options.signingSecret) {
|
|
25723
26022
|
const verification = await verifyVoiceOpsWebhookSignature({
|
|
@@ -26660,6 +26959,8 @@ export {
|
|
|
26660
26959
|
renderVoicePhoneAgentProductionSmokeHTML,
|
|
26661
26960
|
renderVoiceOutcomeContractHTML,
|
|
26662
26961
|
renderVoiceOpsStatusHTML,
|
|
26962
|
+
renderVoiceOpsRecoveryMarkdown,
|
|
26963
|
+
renderVoiceOpsRecoveryHTML,
|
|
26663
26964
|
renderVoiceOpsConsoleHTML,
|
|
26664
26965
|
renderVoiceOpsActionHistoryHTML,
|
|
26665
26966
|
renderVoiceOperationsRecordHTML,
|
|
@@ -26856,6 +27157,7 @@ export {
|
|
|
26856
27157
|
createVoiceOpsTaskProcessorWorker,
|
|
26857
27158
|
createVoiceOpsStatusRoutes,
|
|
26858
27159
|
createVoiceOpsRuntime,
|
|
27160
|
+
createVoiceOpsRecoveryRoutes,
|
|
26859
27161
|
createVoiceOpsConsoleRoutes,
|
|
26860
27162
|
createVoiceOpsActionAuditRoutes,
|
|
26861
27163
|
createVoiceOperationsRecordRoutes,
|
|
@@ -26989,6 +27291,8 @@ export {
|
|
|
26989
27291
|
buildVoiceProductionReadinessGate,
|
|
26990
27292
|
buildVoiceOpsTaskFromSLABreach,
|
|
26991
27293
|
buildVoiceOpsTaskFromReview,
|
|
27294
|
+
buildVoiceOpsRecoveryReport,
|
|
27295
|
+
buildVoiceOpsRecoveryReadinessCheck,
|
|
26992
27296
|
buildVoiceOpsConsoleReport,
|
|
26993
27297
|
buildVoiceOpsActionHistoryReport,
|
|
26994
27298
|
buildVoiceOperationsRecord,
|