@absolutejs/voice 0.0.22-beta.196 → 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 CHANGED
@@ -11,7 +11,7 @@ Use it when you want Vapi/Retell/Bland-style voice-agent capability, but you wan
11
11
  - Self-hosted by default: your app owns sessions, traces, reviews, tasks, handoffs, retention, and provider keys.
12
12
  - Provider-neutral: use Deepgram, AssemblyAI, OpenAI, Anthropic, Gemini, ElevenLabs-style TTS, or your own adapters without rewriting app workflow code.
13
13
  - Browser and phone surfaces: mount browser WebSocket voice routes plus Twilio, Telnyx, and Plivo telephony routes from the same package.
14
- - Production proof: ops status, production readiness, turn quality, turn latency, live browser p50/p95 latency, trace timelines, evals, fixtures, and contracts are package primitives.
14
+ - Production proof: ops status, ops recovery, production readiness, operations records, turn quality, turn latency, live browser p50/p95 latency, trace timelines, evals, fixtures, and contracts are package primitives.
15
15
  - Framework parity: React, Vue, Svelte, Angular, HTML, HTMX, and plain client entrypoints share the same core behavior.
16
16
  - No hosted platform tax: AbsoluteJS Voice does not add a mandatory per-minute orchestration fee between your app and your providers.
17
17
 
@@ -23,6 +23,7 @@ Pick the path that matches what you are building:
23
23
  - Phone voice agent: mount Twilio, Telnyx, or Plivo routes, normalize carrier outcomes, inspect carrier readiness, and persist call lifecycle traces.
24
24
  - Outbound campaigns: create self-hosted campaign queues, import CSV/JSON recipients, enforce rate limits/quiet hours/retry backoff, dry-run carrier dialers, and fail production readiness when campaign proof regresses.
25
25
  - Production readiness: mount the status and proof primitives you need, such as `createVoiceOpsStatusRoutes(...)`, `createVoiceProductionReadinessRoutes(...)`, quality routes, trace routes, eval routes, and smoke contracts.
26
+ - Support/debug entrypoint: mount `createVoiceOperationsRecordRoutes(...)` so every problematic session has one call-log object linking traces, replay, provider events, tools, handoffs, audit, reviews, tasks, and delivery attempts.
26
27
  - Provider routing and fallback: use LLM/STT/TTS provider routers, provider health, provider simulation controls, and cost/latency-aware routing policies.
27
28
  - Evals and simulation: mount `createVoiceSimulationSuiteRoutes(...)` to run scenario fixtures, workflow contracts, tool contracts, outcome contracts, baseline comparisons, and saved benchmark artifacts before live traffic.
28
29
 
@@ -1362,6 +1363,64 @@ Mount `createVoiceAuditTrailRoutes(...)` to expose `/api/voice-audit` and `/audi
1362
1363
 
1363
1364
  Use `exportVoiceAuditTrail(...)` or `buildVoiceAuditExport(...)` when audit evidence needs to leave the app. Pass `redact: true` to scrub sensitive keys plus common email and phone patterns from payloads and metadata before generating JSON, Markdown, or HTML. Audit trail routes also expose redacted exports at `/api/voice-audit/export`, `/api/voice-audit/export?format=markdown`, `/api/voice-audit/export?format=html`, and `/audit/export`; export routes redact by default unless `redact=false` is passed.
1364
1365
 
1366
+ ## Operations Records And Recovery
1367
+
1368
+ Use operations records as the default support/debug entrypoint. A hosted platform would send an operator to a call log; AbsoluteJS Voice gives the same workflow as a code-owned route:
1369
+
1370
+ ```ts
1371
+ app.use(
1372
+ createVoiceOperationsRecordRoutes({
1373
+ audit: runtimeStorage.audit,
1374
+ htmlPath: '/voice-operations/:sessionId',
1375
+ path: '/api/voice-operations/:sessionId',
1376
+ store: runtimeStorage.traces
1377
+ })
1378
+ );
1379
+ ```
1380
+
1381
+ `createVoiceOperationsRecordRoutes(...)` links the call/session timeline, replay, provider events, tools, handoffs, audit, reviews, ops tasks, integration events, and sink delivery attempts into one debuggable object. Use `/voice-operations/:sessionId` as the first place to investigate failed calls, provider failures, handoff failures, slow turns, and campaign attempts.
1382
+
1383
+ Mount `createVoiceOpsRecoveryRoutes(...)` beside it when operators need one deploy-checkable recovery signal:
1384
+
1385
+ ```ts
1386
+ app.use(
1387
+ createVoiceOpsRecoveryRoutes({
1388
+ auditDeliveries: runtimeStorage.auditDeliveries,
1389
+ handoffDeliveries,
1390
+ links: {
1391
+ operationsRecords: '/voice-operations/:sessionId',
1392
+ traceDeliveries: '/traces/deliveries'
1393
+ },
1394
+ traceDeliveries: runtimeStorage.traceDeliveries,
1395
+ traces: runtimeStorage.traces
1396
+ })
1397
+ );
1398
+ ```
1399
+
1400
+ The recovery report summarizes recovered provider fallback, unresolved provider failures, audit/trace delivery backlog, handoff delivery backlog, operator interventions, failed sessions, and latency SLO issues. When `operationsRecords` is configured, provider and latency recovery issues link directly to the impacted operations record instead of a generic dashboard.
1401
+
1402
+ Pass the same report into production readiness to make recovery issues a deploy gate:
1403
+
1404
+ ```ts
1405
+ const opsRecovery = await buildVoiceOpsRecoveryReport({
1406
+ links: { operationsRecords: '/voice-operations/:sessionId' },
1407
+ traces: runtimeStorage.traces
1408
+ });
1409
+
1410
+ app.use(
1411
+ createVoiceProductionReadinessRoutes({
1412
+ links: {
1413
+ operationsRecords: '/voice-operations/:sessionId',
1414
+ opsRecovery: '/ops-recovery'
1415
+ },
1416
+ opsRecovery,
1417
+ store: runtimeStorage.traces
1418
+ })
1419
+ );
1420
+ ```
1421
+
1422
+ Readiness emits the stable `voice.readiness.ops_recovery` gate code when unresolved recovery issues remain.
1423
+
1365
1424
  ## Production Voice Ops
1366
1425
 
1367
1426
  The recommended production pattern is:
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("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
21750
- var rollupStatus2 = (checks) => checks.some((check) => check.status === "fail") ? "fail" : checks.some((check) => check.status === "warn") ? "warn" : "pass";
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("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
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 getString13 = (value) => typeof value === "string" ? value : undefined;
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: getString13(event.payload.error),
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: rollupStatus2(checks),
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>${escapeHtml36(report.profile.name)}</h2><p>${escapeHtml36(report.profile.description)}</p><p>${escapeHtml36(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="${escapeHtml36(surface.href)}">${escapeHtml36(surface.label)}</a>` : escapeHtml36(surface.label)}</strong></article>`).join("")}</div></section>` : "";
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="${escapeHtml36(action.href)}">${escapeHtml36(action.label)}</button>` : `<a href="${escapeHtml36(action.href)}">${escapeHtml36(action.label)}</a>`).join("");
22703
- return `<article class="check ${escapeHtml36(check.status)}">
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>${escapeHtml36(check.status.toUpperCase())}</span>
22706
- <h2>${escapeHtml36(check.label)}</h2>
22707
- ${check.detail ? `<p>${escapeHtml36(check.detail)}</p>` : ""}
22708
- ${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${escapeHtml36(check.proofSource.href)}">${escapeHtml36(check.proofSource.sourceLabel)}</a>` : escapeHtml36(check.proofSource.sourceLabel)}${check.proofSource.detail ? ` \xB7 ${escapeHtml36(check.proofSource.detail)}` : ""}</p>` : ""}
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>${escapeHtml36(String(check.value ?? check.status))}</strong>
22712
- ${check.href ? `<a href="${escapeHtml36(check.href)}">Open surface</a>` : ""}
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 = escapeHtml36(`createVoiceProductionReadinessRoutes({
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>${escapeHtml36(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>${escapeHtml36(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 ${escapeHtml36(report.status)}">Overall: ${escapeHtml36(report.status.toUpperCase())}</p><p>Checked ${escapeHtml36(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>`;
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 Elysia35({
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 Elysia36 } from "elysia";
23102
- var escapeHtml37 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
23400
+ import { Elysia as Elysia37 } from "elysia";
23401
+ var escapeHtml38 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
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="${escapeHtml37(check.status)}"><strong>${escapeHtml37(check.label)}</strong><span>${escapeHtml37(check.detail ?? check.status)}</span>${check.remediation ? `<em>${check.remediation.href ? `<a href="${escapeHtml37(check.remediation.href)}">${escapeHtml37(check.remediation.label)}</a>` : escapeHtml37(check.remediation.label)}: ${escapeHtml37(check.remediation.detail)}</em>` : ""}</li>`).join("");
23346
- return `<article class="row ${escapeHtml37(row.status)}">
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">${escapeHtml37(row.kind)}${row.selected ? " \xB7 selected" : ""}</p>
23349
- <h2>${escapeHtml37(row.provider)}</h2>
23350
- <p class="status ${escapeHtml37(row.status)}">${escapeHtml37(row.status.toUpperCase())}</p>
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 = escapeHtml37(`const providerContracts = () =>
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>${escapeHtml37(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>${escapeHtml37(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>`;
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 Elysia36({
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 Elysia37 } from "elysia";
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 escapeHtml38 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
23784
+ var escapeHtml39 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
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>${escapeHtml38(input.label)}</span><strong>${escapeHtml38(String(input.value))}</strong>${input.status ? `<p class="${escapeHtml38(input.status)}">${escapeHtml38(input.status)}</p>` : ""}${input.href ? `<a href="${escapeHtml38(input.href)}">Open</a>` : ""}</article>`;
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>${escapeHtml38(link.label)}</h2>${link.description ? `<p>${escapeHtml38(link.description)}</p>` : ""}</div>
23558
- <p><a href="${escapeHtml38(link.href)}">Open ${escapeHtml38(link.label)}</a>${link.statusHref ? ` \xB7 <a href="${escapeHtml38(link.statusHref)}">Status</a>` : ""}</p>
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>${escapeHtml38(session.sessionId)}</td><td>${escapeHtml38(session.status)}</td><td>${session.turnCount}</td><td>${session.errorCount}</td><td>${session.replayHref ? `<a href="${escapeHtml38(session.replayHref)}">Replay</a>` : ""}</td></tr>`).join("") : '<tr><td colspan="5">No sessions yet.</td></tr>';
23561
- const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${escapeHtml38(event.kind)}</td><td>${escapeHtml38(event.provider ?? "unknown")}</td><td>${escapeHtml38(event.status ?? "unknown")}</td><td>${event.elapsedMs ?? 0}ms</td><td>${escapeHtml38(event.sessionId)}</td></tr>`).join("") : '<tr><td colspan="5">No provider routing events yet.</td></tr>';
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>${escapeHtml38(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>${escapeHtml38(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 ${escapeHtml38(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>`;
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 Elysia37({
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 Elysia39 } from "elysia";
23883
+ import { Elysia as Elysia40 } from "elysia";
23585
23884
 
23586
23885
  // src/traceTimeline.ts
23587
- import { Elysia as Elysia38 } from "elysia";
23588
- var escapeHtml39 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
23589
- var getString14 = (value) => typeof value === "string" && value.trim() ? value : undefined;
23886
+ import { Elysia as Elysia39 } from "elysia";
23887
+ var escapeHtml40 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
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 = getString14(payload[key]);
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${getString14(event.payload.reason) ? ` (${getString14(event.payload.reason)})` : ""}`;
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 ${getString14(event.payload.toolName) ?? "call"}`;
23935
+ return `Tool ${getString15(event.payload.toolName) ?? "call"}`;
23637
23936
  case "agent.handoff":
23638
- return `Agent handoff${getString14(event.payload.targetAgentId) ? ` to ${getString14(event.payload.targetAgentId)}` : ""}`;
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${getString14(event.payload.error) ? `: ${getString14(event.payload.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 ${getString14(event.payload.stage) ?? "stage"}`;
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>${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>`;
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="${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("");
23759
- 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>";
23760
- 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>`;
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="${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("");
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 = escapeHtml39(`const traceStore = createVoiceTraceSinkStore({
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>${escapeHtml39(options.title ?? "Voice Trace Timelines")}</title><style>${timelineCSS}.primitive{background:#181f27;border:1px solid #334155;border-radius:20px;margin:20px 0;padding:18px}.primitive p{line-height:1.55}.primitive pre{background:#0b1118;border:1px solid #2b3642;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}.primitive code{color:#bfdbfe}</style></head><body><main><header><p class="eyebrow">Self-hosted voice debugging</p><h1>${escapeHtml39(options.title ?? "Voice Trace Timelines")}</h1><p class="muted">Per-call event timelines with provider latency, fallback, timeout, handoff, and error context.</p></header><section class="metrics"><article><span>Sessions</span><strong>${String(report.total)}</strong></article><article><span>Failed</span><strong>${String(report.failed)}</strong></article><article><span>Warnings</span><strong>${String(report.warnings)}</strong></article></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceTraceTimelineRoutes(...)</code> makes traces the proof backbone</h2><p class="muted">Mount trace timelines from the same trace store used by readiness, simulations, provider recovery, delivery sinks, and phone-agent smoke proof.</p><pre><code>${snippet}</code></pre></section><table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Turns</th><th>Errors</th><th>Duration</th><th>Providers</th></tr></thead><tbody>${renderSessionRows(report)}</tbody></table></main></body></html>`;
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 Elysia38({
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 getString15 = (value) => typeof value === "string" ? value : undefined;
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: getString15(event.payload.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: getString15(event.payload.reason),
23859
- status: getString15(event.payload.status),
23860
- summary: getString15(event.payload.summary),
23861
- targetAgentId: getString15(event.payload.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: getString15(event.payload.error),
23868
- status: getString15(event.payload.status),
23869
- toolCallId: getString15(event.payload.toolCallId),
23870
- toolName: getString15(event.payload.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 escapeHtml40 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
24250
+ var escapeHtml41 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
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>${escapeHtml40(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>';
23955
- const handoffs = record.handoffs.length ? record.handoffs.map((handoff) => `<li><strong>${escapeHtml40(handoff.fromAgentId ?? "unknown")}</strong> to <strong>${escapeHtml40(handoff.targetAgentId ?? "unknown")}</strong> <span>${escapeHtml40(handoff.status ?? "")}</span><p>${escapeHtml40(handoff.summary ?? handoff.reason ?? "")}</p></li>`).join("") : "<li>No agent handoffs recorded.</li>";
23956
- const tools = record.tools.length ? record.tools.map((tool) => `<li><strong>${escapeHtml40(tool.toolName ?? "tool")}</strong> <span>${escapeHtml40(tool.status ?? "")}</span> ${formatMs4(tool.elapsedMs)} ${tool.error ? `<p>${escapeHtml40(tool.error)}</p>` : ""}</li>`).join("") : "<li>No tool calls recorded.</li>";
23957
- const reviews = record.reviews?.reviews.length ? record.reviews.reviews.map((review) => `<li><strong>${escapeHtml40(review.title)}</strong> <span>${escapeHtml40(review.summary.outcome ?? "")}</span><p>${escapeHtml40(review.postCall?.summary ?? review.transcript.actual)}</p></li>`).join("") : "<li>No call reviews recorded.</li>";
23958
- const tasks = record.tasks?.tasks.length ? record.tasks.tasks.map((task) => `<li><strong>${escapeHtml40(task.title)}</strong> <span>${escapeHtml40(task.status)}</span><p>${escapeHtml40(task.recommendedAction)}</p></li>`).join("") : "<li>No ops tasks recorded.</li>";
23959
- const integrationEvents = record.integrationEvents?.events.length ? record.integrationEvents.events.map((event) => `<li><strong>${escapeHtml40(event.type)}</strong> <span>${escapeHtml40(event.deliveryStatus ?? "local")}</span><p>${escapeHtml40(event.deliveryError ?? event.deliveredTo ?? "")}</p></li>`).join("") : "<li>No integration events recorded.</li>";
23960
- const snippet = escapeHtml40(`app.use(
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>${escapeHtml40(options.title ?? "Voice Operations Record")}</title><style>body{background:#101417;color:#f9f4e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.eyebrow{color:#fbbf24;font-size:.8rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.8rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #475569;border-radius:999px;display:inline-flex;padding:8px 12px}.healthy{color:#86efac}.warning{color:#fbbf24}.failed,.error{color:#fca5a5}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.card,.primitive{background:#182025;border:1px solid #2d3a43;border-radius:20px;padding:16px}.card span,.muted{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>${escapeHtml40(options.title ?? "Voice Operations Record")}</h1><p class="status ${escapeHtml40(record.status)}">${escapeHtml40(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>`;
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 Elysia39({
24278
+ const routes = new Elysia40({
23980
24279
  name: options.name ?? "absolutejs-voice-operations-record"
23981
24280
  });
23982
24281
  const buildRecord = (sessionId) => buildVoiceOperationsRecord({
@@ -24008,246 +24307,6 @@ var createVoiceOperationsRecordRoutes = (options) => {
24008
24307
  }
24009
24308
  return routes;
24010
24309
  };
24011
- // src/opsRecovery.ts
24012
- import { Elysia as Elysia40 } from "elysia";
24013
- var escapeHtml41 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
24014
- var getString16 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
24015
- var hrefForSession = (value, sessionId) => typeof value === "function" ? value(sessionId) : value;
24016
- var rollupStatus3 = (issues) => issues.some((issue) => issue.severity === "fail") ? "fail" : issues.some((issue) => issue.severity === "warn") ? "warn" : "pass";
24017
- var providerUnresolved = (provider) => provider.status === "degraded" || provider.status === "rate-limited" || provider.status === "suppressed";
24018
- var collectFailedSessions = (events, limit) => events.filter((event) => {
24019
- if (event.type !== "session.error") {
24020
- return false;
24021
- }
24022
- const providerStatus = event.payload.providerStatus;
24023
- return providerStatus !== "success" && providerStatus !== "fallback";
24024
- }).sort((left, right) => right.at - left.at).slice(0, limit).map((event) => ({
24025
- at: event.at,
24026
- error: getString16(event.payload.error),
24027
- provider: getString16(event.payload.provider),
24028
- sessionId: event.sessionId,
24029
- traceId: event.traceId
24030
- }));
24031
- var collectInterventions = (events, limit) => {
24032
- const interventionEvents = events.filter((event) => event.type === "operator.action").sort((left, right) => right.at - left.at);
24033
- return {
24034
- events: interventionEvents.slice(0, limit).map((event) => ({
24035
- action: getString16(event.payload.action),
24036
- at: event.at,
24037
- operatorId: getString16(event.payload.operatorId) ?? getString16(event.payload.actorId),
24038
- sessionId: event.sessionId,
24039
- traceId: event.traceId
24040
- })),
24041
- total: interventionEvents.length
24042
- };
24043
- };
24044
- var addDeliveryIssues = (issues, input) => {
24045
- if (!input.summary) {
24046
- return;
24047
- }
24048
- const failed = input.summary.failed + input.summary.deadLettered;
24049
- if (failed > 0) {
24050
- issues.push({
24051
- code: input.failedCode,
24052
- detail: `${failed} failed or dead-lettered delivery record(s).`,
24053
- href: input.href,
24054
- label: input.failedLabel,
24055
- severity: "fail",
24056
- value: failed
24057
- });
24058
- }
24059
- const pending = input.summary.pending + input.summary.retryEligible;
24060
- if (pending > 0) {
24061
- issues.push({
24062
- code: input.pendingCode,
24063
- detail: `${pending} pending or retry-eligible delivery record(s).`,
24064
- href: input.href,
24065
- label: input.pendingLabel,
24066
- severity: "warn",
24067
- value: pending
24068
- });
24069
- }
24070
- };
24071
- var buildVoiceOpsRecoveryReport = async (options = {}) => {
24072
- const limit = options.limit ?? 50;
24073
- const events = options.events ?? await options.traces?.list({ limit: Math.max(limit, 500) }) ?? [];
24074
- const providers = await summarizeVoiceProviderHealth({
24075
- events,
24076
- providers: options.providers
24077
- });
24078
- const auditDeliveries = options.auditDeliveries ? await summarizeVoiceAuditSinkDeliveries(await options.auditDeliveries.list(), {
24079
- deadLetters: options.auditDeliveryDeadLetters
24080
- }) : undefined;
24081
- const traceDeliveries = options.traceDeliveries ? await summarizeVoiceTraceSinkDeliveries(await options.traceDeliveries.list(), {
24082
- deadLetters: options.traceDeliveryDeadLetters
24083
- }) : undefined;
24084
- const handoffDeliveries = options.handoffDeliveries ? await summarizeVoiceHandoffDeliveries(await options.handoffDeliveries.list(), {
24085
- deadLetters: options.handoffDeliveryDeadLetters
24086
- }) : undefined;
24087
- const latency = options.latency === false ? undefined : await buildVoiceLatencySLOGate({
24088
- events,
24089
- ...options.latency ?? {}
24090
- });
24091
- const failedSessions = collectFailedSessions(events, limit);
24092
- const interventions = collectInterventions(events, limit);
24093
- const issues = [];
24094
- const unresolvedProviders = providers.filter(providerUnresolved);
24095
- for (const provider of unresolvedProviders) {
24096
- issues.push({
24097
- code: "voice.ops_recovery.provider_unresolved_failure",
24098
- detail: provider.lastError ?? `${provider.provider} status is ${provider.status}.`,
24099
- href: options.links?.providers,
24100
- label: `Provider ${provider.provider} needs recovery`,
24101
- severity: "fail",
24102
- value: provider.status
24103
- });
24104
- }
24105
- addDeliveryIssues(issues, {
24106
- failedCode: "voice.ops_recovery.audit_delivery_failed",
24107
- failedLabel: "Audit delivery failures",
24108
- href: options.links?.auditDeliveries,
24109
- pendingCode: "voice.ops_recovery.audit_delivery_pending",
24110
- pendingLabel: "Audit delivery backlog",
24111
- summary: auditDeliveries
24112
- });
24113
- addDeliveryIssues(issues, {
24114
- failedCode: "voice.ops_recovery.trace_delivery_failed",
24115
- failedLabel: "Trace delivery failures",
24116
- href: options.links?.traceDeliveries,
24117
- pendingCode: "voice.ops_recovery.trace_delivery_pending",
24118
- pendingLabel: "Trace delivery backlog",
24119
- summary: traceDeliveries
24120
- });
24121
- addDeliveryIssues(issues, {
24122
- failedCode: "voice.ops_recovery.handoff_failed",
24123
- failedLabel: "Handoff delivery failures",
24124
- href: options.links?.handoffs,
24125
- pendingCode: "voice.ops_recovery.handoff_pending",
24126
- pendingLabel: "Handoff delivery backlog",
24127
- summary: handoffDeliveries
24128
- });
24129
- if (latency?.failed) {
24130
- issues.push({
24131
- code: "voice.ops_recovery.latency_slo_failed",
24132
- detail: `${latency.failed} latency SLO measurement(s) failed.`,
24133
- href: options.links?.traces ? hrefForSession(options.links.traces, latency.measurements[0]?.sessionId ?? "") : undefined,
24134
- label: "Latency SLO failures",
24135
- severity: "fail",
24136
- value: latency.failed
24137
- });
24138
- } else if (latency?.warnings) {
24139
- issues.push({
24140
- code: "voice.ops_recovery.latency_slo_warn",
24141
- detail: `${latency.warnings} latency SLO measurement(s) are warning.`,
24142
- label: "Latency SLO warnings",
24143
- severity: "warn",
24144
- value: latency.warnings
24145
- });
24146
- }
24147
- return {
24148
- auditDeliveries,
24149
- checkedAt: Date.now(),
24150
- failedSessions,
24151
- handoffDeliveries,
24152
- interventions,
24153
- issues,
24154
- latency,
24155
- providers: {
24156
- healthy: providers.filter((provider) => provider.status === "healthy").length,
24157
- providers,
24158
- recoveredFallbacks: providers.reduce((total, provider) => total + provider.fallbackCount, 0),
24159
- unresolvedFailures: unresolvedProviders.length
24160
- },
24161
- status: rollupStatus3(issues),
24162
- traceDeliveries
24163
- };
24164
- };
24165
- var buildVoiceOpsRecoveryReadinessCheck = (report, options = {}) => ({
24166
- 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.`,
24167
- href: options.href,
24168
- label: options.label ?? "Ops recovery",
24169
- status: report.status,
24170
- value: report.issues.length
24171
- });
24172
- var renderVoiceOpsRecoveryMarkdown = (report, options = {}) => {
24173
- const title = options.title ?? "Voice Ops Recovery";
24174
- const issueRows = report.issues.map((issue) => `| ${issue.severity} | ${issue.code} | ${issue.label} | ${issue.value ?? ""} | ${issue.detail ?? ""} |`).join(`
24175
- `);
24176
- const providers = report.providers.providers.map((provider) => `| ${provider.provider} | ${provider.status} | ${provider.runCount} | ${provider.errorCount} | ${provider.fallbackCount} | ${provider.lastError ?? ""} |`).join(`
24177
- `);
24178
- const failedSessions = report.failedSessions.map((session) => `- ${session.sessionId}${session.provider ? ` via ${session.provider}` : ""}${session.error ? `: ${session.error}` : ""}`).join(`
24179
- `);
24180
- return `# ${title}
24181
-
24182
- Status: ${report.status}
24183
-
24184
- Checked at: ${new Date(report.checkedAt).toISOString()}
24185
-
24186
- Recovered fallbacks: ${report.providers.recoveredFallbacks}
24187
- Unresolved provider failures: ${report.providers.unresolvedFailures}
24188
- Operator interventions: ${report.interventions.total}
24189
-
24190
- ## Issues
24191
-
24192
- | Severity | Code | Label | Value | Detail |
24193
- | --- | --- | --- | ---: | --- |
24194
- ${issueRows || "| pass | none | No recovery issues | 0 | |"}
24195
-
24196
- ## Providers
24197
-
24198
- | Provider | Status | Runs | Errors | Fallbacks | Last error |
24199
- | --- | --- | ---: | ---: | ---: | --- |
24200
- ${providers || "| none | idle | 0 | 0 | 0 | |"}
24201
-
24202
- ## Failed Sessions
24203
-
24204
- ${failedSessions || "None."}
24205
-
24206
- ## Latency
24207
-
24208
- ${report.latency ? renderVoiceLatencySLOMarkdown(report.latency, { title: "Latency SLO" }) : "Latency SLO disabled."}
24209
- `;
24210
- };
24211
- var renderDeliverySummary = (label, summary) => summary ? `<article><span>${escapeHtml41(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>${escapeHtml41(label)}</span><strong>not configured</strong></article>`;
24212
- var renderVoiceOpsRecoveryHTML = (report, options = {}) => {
24213
- const title = options.title ?? "Voice Ops Recovery";
24214
- const issues = report.issues.map((issue) => `<tr><td>${escapeHtml41(issue.severity)}</td><td><code>${escapeHtml41(issue.code)}</code></td><td>${escapeHtml41(issue.label)}</td><td>${escapeHtml41(String(issue.value ?? ""))}</td><td>${escapeHtml41(issue.detail ?? "")}</td></tr>`).join("");
24215
- const providers = report.providers.providers.map((provider) => `<tr><td>${escapeHtml41(provider.provider)}</td><td>${escapeHtml41(provider.status)}</td><td>${String(provider.runCount)}</td><td>${String(provider.errorCount)}</td><td>${String(provider.fallbackCount)}</td><td>${escapeHtml41(provider.lastError ?? "")}</td></tr>`).join("");
24216
- const failedSessions = report.failedSessions.map((session) => `<li>${escapeHtml41(session.sessionId)}${session.provider ? ` via ${escapeHtml41(session.provider)}` : ""}${session.error ? `: ${escapeHtml41(session.error)}` : ""}</li>`).join("");
24217
- return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml41(title)}</title><style>body{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>${escapeHtml41(title)}</h1><p><span class="status">${escapeHtml41(report.status)}</span> Checked ${escapeHtml41(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>${escapeHtml41(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>`;
24218
- };
24219
- var createVoiceOpsRecoveryRoutes = (options = {}) => {
24220
- const path = options.path ?? "/api/voice/ops-recovery";
24221
- const htmlPath = options.htmlPath === undefined ? "/ops-recovery" : options.htmlPath;
24222
- const markdownPath = options.markdownPath === undefined ? `${path}.md` : options.markdownPath;
24223
- const routes = new Elysia40({
24224
- name: options.name ?? "absolutejs-voice-ops-recovery"
24225
- }).get(path, async () => buildVoiceOpsRecoveryReport(options));
24226
- if (htmlPath) {
24227
- routes.get(htmlPath, async () => {
24228
- const report = await buildVoiceOpsRecoveryReport(options);
24229
- const render = options.render ?? renderVoiceOpsRecoveryHTML;
24230
- return new Response(await render(report), {
24231
- headers: {
24232
- "content-type": "text/html; charset=utf-8",
24233
- ...options.headers
24234
- }
24235
- });
24236
- });
24237
- }
24238
- if (markdownPath) {
24239
- routes.get(markdownPath, async () => {
24240
- const report = await buildVoiceOpsRecoveryReport(options);
24241
- return new Response(renderVoiceOpsRecoveryMarkdown(report, { title: options.title }), {
24242
- headers: {
24243
- "content-type": "text/markdown; charset=utf-8",
24244
- ...options.headers
24245
- }
24246
- });
24247
- });
24248
- }
24249
- return routes;
24250
- };
24251
24310
  // src/incidentBundle.ts
24252
24311
  import { Elysia as Elysia41 } from "elysia";
24253
24312
  var filterIncidentBundleArtifacts = (artifacts, filter = {}) => artifacts.filter((artifact) => {
@@ -35,6 +35,7 @@ export type VoiceOpsRecoveryInterventionSummary = {
35
35
  export type VoiceOpsRecoveryFailedSession = {
36
36
  at: number;
37
37
  error?: string;
38
+ operationsRecordHref?: string;
38
39
  provider?: string;
39
40
  sessionId: string;
40
41
  traceId?: string;
@@ -13,6 +13,7 @@ import type { VoiceAuditEventStore, VoiceAuditEventType, VoiceAuditOutcome } fro
13
13
  import { type VoiceAuditSinkDeliveryStore } from './auditSinks';
14
14
  import type { VoiceProviderContractMatrixReport, VoiceProviderStackCapabilityGapReport } from './providerStackRecommendations';
15
15
  import type { VoiceCampaignReadinessProofReport } from './campaign';
16
+ import { type VoiceOpsRecoveryReport } from './opsRecovery';
16
17
  export type VoiceProductionReadinessStatus = 'fail' | 'pass' | 'warn';
17
18
  export type VoiceProductionReadinessAction = {
18
19
  description?: string;
@@ -90,6 +91,7 @@ export type VoiceProductionReadinessReport = {
90
91
  liveLatency?: string;
91
92
  operationsRecords?: string;
92
93
  opsActions?: string;
94
+ opsRecovery?: string;
93
95
  phoneAgentSmoke?: string;
94
96
  providerContracts?: string;
95
97
  providerRoutingContracts?: string;
@@ -145,6 +147,12 @@ export type VoiceProductionReadinessReport = {
145
147
  warnings: number;
146
148
  };
147
149
  opsActionHistory?: VoiceProductionReadinessOpsActionHistorySummary;
150
+ opsRecovery?: {
151
+ issues: number;
152
+ recoveredFallbacks: number;
153
+ status: VoiceProductionReadinessStatus;
154
+ unresolvedProviderFailures: number;
155
+ };
148
156
  providers: {
149
157
  degraded: number;
150
158
  total: number;
@@ -318,6 +326,10 @@ export type VoiceProductionReadinessRoutesOptions = {
318
326
  llmProviders?: readonly string[];
319
327
  name?: string;
320
328
  opsActionHistory?: false | VoiceProductionReadinessOpsActionHistoryOptions;
329
+ opsRecovery?: false | VoiceOpsRecoveryReport | ((input: {
330
+ query: Record<string, unknown>;
331
+ request: Request;
332
+ }) => Promise<VoiceOpsRecoveryReport> | VoiceOpsRecoveryReport);
321
333
  path?: string;
322
334
  phoneAgentSmokes?: false | readonly VoicePhoneAgentProductionSmokeReport[] | ((input: {
323
335
  query: Record<string, unknown>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.196",
3
+ "version": "0.0.22-beta.197",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",