@absolutejs/voice 0.0.22-beta.437 → 0.0.22-beta.439

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -21310,10 +21310,10 @@ var createVoiceDeliveryRuntimeRoutes = (options) => {
21310
21310
  return routes;
21311
21311
  };
21312
21312
  // src/operationalStatus.ts
21313
- import { Elysia as Elysia45 } from "elysia";
21313
+ import { Elysia as Elysia46 } from "elysia";
21314
21314
 
21315
21315
  // src/productionReadiness.ts
21316
- import { Elysia as Elysia44 } from "elysia";
21316
+ import { Elysia as Elysia45 } from "elysia";
21317
21317
 
21318
21318
  // src/handoffHealth.ts
21319
21319
  import { Elysia as Elysia33 } from "elysia";
@@ -26434,111 +26434,807 @@ var createVoiceOpsRecoveryRoutes = (options = {}) => {
26434
26434
  return routes;
26435
26435
  };
26436
26436
 
26437
- // src/observabilityExport.ts
26437
+ // src/incidentTimeline.ts
26438
26438
  import { Elysia as Elysia43 } from "elysia";
26439
- import { Database as Database4 } from "bun:sqlite";
26440
- import { createHash } from "crypto";
26441
- import { mkdir as mkdir2, readFile, stat, unlink } from "fs/promises";
26442
- import { join as join2 } from "path";
26443
- var voiceObservabilityExportSchemaVersion = "1.0.0";
26444
- var voiceObservabilityExportSchemaId = "com.absolutejs.voice.observability-export";
26445
- var createVoiceObservabilityExportSchema = () => ({
26446
- id: voiceObservabilityExportSchemaId,
26447
- version: voiceObservabilityExportSchemaVersion
26448
- });
26449
- var assertVoiceObservabilityExportSchema = (input) => {
26450
- if (input.schema?.id !== voiceObservabilityExportSchemaId || input.schema?.version !== voiceObservabilityExportSchemaVersion) {
26451
- throw new Error(`Unsupported voice observability export schema: ${input.schema?.id ?? "missing"}@${input.schema?.version ?? "missing"}`);
26439
+ var escapeHtml41 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
26440
+ var resolveValue = async (value) => typeof value === "function" ? await value() : value;
26441
+ var linkForSession = (link, sessionId) => {
26442
+ if (!link || !sessionId) {
26443
+ return;
26452
26444
  }
26445
+ return typeof link === "function" ? link(sessionId) : link;
26453
26446
  };
26454
- var isRecord3 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
26455
- var isStatus2 = (value) => value === "fail" || value === "pass" || value === "warn";
26456
- var getRecord2 = (value, key) => isRecord3(value) && isRecord3(value[key]) ? value[key] : undefined;
26457
- var getRecordArray = (value, key) => isRecord3(value) && Array.isArray(value[key]) ? value[key] : undefined;
26458
- var inferVoiceObservabilityExportRecordKind = (record) => {
26459
- if (isRecord3(record.manifest) && isRecord3(record.artifactIndex)) {
26460
- return "database-record";
26461
- }
26462
- if (Array.isArray(record.receipts)) {
26463
- return "delivery-history";
26464
- }
26465
- if (typeof record.runId === "string" && Array.isArray(record.destinations)) {
26466
- return "delivery-receipt";
26467
- }
26468
- if (Array.isArray(record.destinations) && isRecord3(record.summary) && typeof record.exportStatus === "string") {
26469
- return "delivery-report";
26447
+ var statusToSeverity = (status) => status === "fail" || status === "failed" ? "critical" : status === "warn" || status === "warning" || status === "recovered" ? "warn" : "info";
26448
+ var failureReplayStatusToSeverity = (status) => status === "failed" ? "critical" : status === "healthy" ? "info" : "warn";
26449
+ var withinWindow = (event, now, windowMs) => !windowMs || event.at >= now - windowMs;
26450
+ var eventStatus2 = (event) => event.severity === "critical" ? "fail" : event.severity === "warn" ? "warn" : "pass";
26451
+ var defaultIncidentRecoveryActions = (events, links) => {
26452
+ const actions = [];
26453
+ const add = (action) => {
26454
+ const key = `${action.id}:${action.sessionId ?? ""}:${action.href ?? ""}`;
26455
+ if (actions.some((existing) => `${existing.id}:${existing.sessionId ?? ""}:${existing.href ?? ""}` === key)) {
26456
+ return;
26457
+ }
26458
+ actions.push(action);
26459
+ };
26460
+ for (const event of events) {
26461
+ if (event.category === "delivery") {
26462
+ add({
26463
+ detail: "Ask the app to tick delivery workers or retry failed delivery queue work.",
26464
+ eventId: event.id,
26465
+ href: links.deliveryRuntime,
26466
+ id: "delivery.retry",
26467
+ label: "Retry delivery work",
26468
+ method: "POST",
26469
+ sessionId: event.sessionId
26470
+ });
26471
+ }
26472
+ if (event.category === "readiness" || event.category === "operational-status") {
26473
+ add({
26474
+ detail: "Refresh production readiness and proof freshness before declaring the incident resolved.",
26475
+ eventId: event.id,
26476
+ href: links.productionReadiness ?? links.operationalStatus,
26477
+ id: "readiness.refresh",
26478
+ label: "Refresh readiness proof",
26479
+ method: "POST",
26480
+ sessionId: event.sessionId
26481
+ });
26482
+ }
26483
+ if (event.sessionId) {
26484
+ add({
26485
+ detail: "Generate or open a support/debug artifact for the affected call.",
26486
+ eventId: event.id,
26487
+ href: linkForSession(links.supportBundle, event.sessionId) ?? linkForSession(links.callDebugger, event.sessionId),
26488
+ id: "support.bundle",
26489
+ label: "Generate support bundle",
26490
+ method: "POST",
26491
+ sessionId: event.sessionId
26492
+ });
26493
+ }
26470
26494
  }
26471
- if (Array.isArray(record.artifacts) && isRecord3(record.summary)) {
26472
- return Array.isArray(record.envelopes) ? "manifest" : "artifact-index";
26495
+ if (events.some((event) => event.severity !== "info")) {
26496
+ add({
26497
+ detail: "Rerun the app proof pack to confirm the current release evidence is fresh.",
26498
+ href: links.proofPack,
26499
+ id: "proof.rerun",
26500
+ label: "Rerun proof pack",
26501
+ method: "POST"
26502
+ });
26473
26503
  }
26474
- return;
26504
+ return actions;
26475
26505
  };
26476
- var pushValidationIssue = (issues, issue) => {
26477
- issues.push(issue);
26506
+ var worstStatus2 = (statuses) => statuses.includes("fail") ? "fail" : statuses.includes("warn") ? "warn" : "pass";
26507
+ var statusRank6 = (status) => status === "fail" ? 3 : status === "warn" ? 2 : status === "pass" ? 1 : 0;
26508
+ var isRecord3 = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
26509
+ var getIncidentRecoveryBody = (event) => {
26510
+ const payload = isRecord3(event.payload) ? event.payload : {};
26511
+ return isRecord3(payload.body) ? payload.body : {};
26478
26512
  };
26479
- var requireRecordSchema = (issues, record, path) => {
26480
- const schema = getRecord2(record, "schema");
26481
- if (schema?.id !== voiceObservabilityExportSchemaId || schema?.version !== voiceObservabilityExportSchemaVersion) {
26482
- pushValidationIssue(issues, {
26483
- code: "voice.observability.export.unsupported_schema",
26484
- message: `Unsupported voice observability export schema: ${schema?.id ?? "missing"}@${schema?.version ?? "missing"}`,
26485
- path: `${path}.schema`
26486
- });
26487
- }
26488
- return schema;
26513
+ var getIncidentRecoveryStatus = (value) => value === "fail" || value === "pass" || value === "warn" ? value : undefined;
26514
+ var getIncidentRecoveryDetail = (event) => {
26515
+ const payload = isRecord3(event.payload) ? event.payload : {};
26516
+ const body = getIncidentRecoveryBody(event);
26517
+ const result = isRecord3(body.result) ? body.result : {};
26518
+ const detail = result.detail ?? payload.error;
26519
+ return typeof detail === "string" ? detail : undefined;
26489
26520
  };
26490
- var requireArrayField = (issues, record, key, path) => {
26491
- if (!Array.isArray(record[key])) {
26492
- pushValidationIssue(issues, {
26493
- code: "voice.observability.export.missing_field",
26494
- message: `${path}.${key} must be an array.`,
26495
- path: `${path}.${key}`
26496
- });
26497
- }
26521
+ var toIncidentRecoveryOutcomeEntry = (event) => {
26522
+ const body = getIncidentRecoveryBody(event);
26523
+ const beforeStatus = getIncidentRecoveryStatus(body.beforeStatus);
26524
+ const afterStatus = getIncidentRecoveryStatus(body.afterStatus);
26525
+ const beforeRank = statusRank6(beforeStatus);
26526
+ const afterRank = statusRank6(afterStatus);
26527
+ const outcome = event.outcome === "error" ? "failed" : beforeRank > 0 && afterRank > 0 && afterRank < beforeRank ? "improved" : beforeRank > 0 && afterRank > beforeRank ? "regressed" : "unchanged";
26528
+ const payload = isRecord3(event.payload) ? event.payload : {};
26529
+ return {
26530
+ actionId: event.action.replace(/^incident\./, ""),
26531
+ afterStatus,
26532
+ at: event.at,
26533
+ beforeStatus,
26534
+ detail: getIncidentRecoveryDetail(event),
26535
+ eventId: event.id,
26536
+ outcome,
26537
+ status: typeof payload.status === "number" ? payload.status : undefined,
26538
+ traceId: event.traceId
26539
+ };
26498
26540
  };
26499
- var requireNumberField = (issues, record, key, path) => {
26500
- if (typeof record[key] !== "number") {
26501
- pushValidationIssue(issues, {
26502
- code: "voice.observability.export.missing_field",
26503
- message: `${path}.${key} must be a number.`,
26504
- path: `${path}.${key}`
26505
- });
26506
- }
26541
+ var buildVoiceIncidentRecoveryOutcomeReport = async (options) => {
26542
+ const events = options.audit ? await options.audit.list({
26543
+ limit: options.limit ?? 50,
26544
+ resourceType: "voice.ops.action",
26545
+ type: "operator.action"
26546
+ }) : [];
26547
+ const entries = events.filter((event) => event.action.startsWith("incident.")).map(toIncidentRecoveryOutcomeEntry).sort((left, right) => right.at - left.at);
26548
+ return {
26549
+ checkedAt: Date.now(),
26550
+ entries,
26551
+ failed: entries.filter((entry) => entry.outcome === "failed").length,
26552
+ improved: entries.filter((entry) => entry.outcome === "improved").length,
26553
+ regressed: entries.filter((entry) => entry.outcome === "regressed").length,
26554
+ total: entries.length,
26555
+ unchanged: entries.filter((entry) => entry.outcome === "unchanged").length
26556
+ };
26507
26557
  };
26508
- var requireStatusField = (issues, record, key, path) => {
26509
- if (!isStatus2(record[key])) {
26510
- pushValidationIssue(issues, {
26511
- code: "voice.observability.export.missing_field",
26512
- message: `${path}.${key} must be pass, warn, or fail.`,
26513
- path: `${path}.${key}`
26514
- });
26515
- }
26558
+ var renderVoiceIncidentRecoveryOutcomeHTML = (report, options = {}) => {
26559
+ const title = options.title ?? "AbsoluteJS Voice Incident Recovery Outcomes";
26560
+ const rows = report.entries.map((entry) => `<article class="${escapeHtml41(entry.outcome)}"><span>${escapeHtml41(entry.outcome.toUpperCase())}</span><h2>${escapeHtml41(entry.actionId)}</h2><p>${escapeHtml41(new Date(entry.at).toLocaleString())}</p><strong>${escapeHtml41(entry.beforeStatus ?? "unknown")} -> ${escapeHtml41(entry.afterStatus ?? "unknown")}</strong>${entry.detail ? `<p>${escapeHtml41(entry.detail)}</p>` : ""}</article>`).join("");
26561
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml41(title)}</title><style>body{background:#10120d;color:#fbf4df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero,article{background:#181711;border:1px solid #39301d;border-radius:24px;padding:20px}.hero{margin-bottom:16px}h1{font-size:clamp(2rem,6vw,4.5rem);line-height:.95}.summary{display:flex;flex-wrap:wrap;gap:10px}.summary span{border:1px solid #4a3f23;border-radius:999px;padding:8px 12px}section{display:grid;gap:12px}article.improved{border-color:rgba(34,197,94,.65)}article.failed,article.regressed{border-color:rgba(239,68,68,.8)}article.unchanged{border-color:rgba(245,158,11,.7)}article span{color:#fcd34d;font-weight:900;letter-spacing:.08em}article strong{display:block;font-size:1.4rem;margin:.5rem 0}p{color:#cfc5a8}</style></head><body><main><section class="hero"><span>Recovery proof</span><h1>${escapeHtml41(title)}</h1><div class="summary"><span>${String(report.improved)} improved</span><span>${String(report.unchanged)} unchanged</span><span>${String(report.regressed)} regressed</span><span>${String(report.failed)} failed</span><span>${String(report.total)} total</span></div></section><section>${rows || "<p>No incident recovery actions have been recorded.</p>"}</section></main></body></html>`;
26562
+ };
26563
+ var buildVoiceIncidentRecoveryOutcomeReadinessCheck = (report, options = {}) => {
26564
+ const failOnFailed = options.failOnFailed ?? true;
26565
+ const failOnRegressed = options.failOnRegressed ?? true;
26566
+ const warnWhenEmpty = options.warnWhenEmpty ?? false;
26567
+ const maxUnchanged = options.maxUnchanged ?? Number.POSITIVE_INFINITY;
26568
+ const tooManyUnchanged = report.unchanged > maxUnchanged;
26569
+ const status = failOnFailed && report.failed > 0 || failOnRegressed && report.regressed > 0 ? "fail" : report.failed > 0 || report.regressed > 0 || tooManyUnchanged || warnWhenEmpty && report.total === 0 ? "warn" : "pass";
26570
+ return {
26571
+ actions: status === "pass" ? [] : [
26572
+ {
26573
+ description: "Open incident recovery outcomes to inspect failed, regressed, or unchanged operator actions.",
26574
+ href: options.href ?? "/api/voice/incident-timeline/recovery-outcomes",
26575
+ label: "Open recovery outcomes"
26576
+ }
26577
+ ],
26578
+ detail: status === "pass" ? `${report.improved} improved recovery action(s), ${report.unchanged} unchanged, ${report.regressed} regressed, and ${report.failed} failed.` : `${report.failed} failed, ${report.regressed} regressed, and ${report.unchanged} unchanged incident recovery action(s) need review.`,
26579
+ gateExplanation: {
26580
+ evidenceHref: options.href ?? "/api/voice/incident-timeline/recovery-outcomes",
26581
+ observed: `${report.failed} failed, ${report.regressed} regressed, ${report.unchanged} unchanged`,
26582
+ remediation: "Inspect recent incident recovery actions, fix failed or regressed handlers, rerun the recovery, and refresh readiness before deploy.",
26583
+ threshold: `failed <= ${failOnFailed ? 0 : "warn"}, regressed <= ${failOnRegressed ? 0 : "warn"}, unchanged <= ${Number.isFinite(maxUnchanged) ? maxUnchanged : "unbounded"}`,
26584
+ thresholdLabel: "Incident recovery outcome budget",
26585
+ unit: "count"
26586
+ },
26587
+ href: options.href ?? "/api/voice/incident-timeline/recovery-outcomes",
26588
+ label: options.label ?? "Incident recovery outcomes",
26589
+ status,
26590
+ value: `${report.improved}/${report.total} improved`
26591
+ };
26516
26592
  };
26517
- var requireDeliveryDestinationStatusField = (issues, record, key, path) => {
26518
- if (record[key] !== "delivered" && record[key] !== "failed") {
26519
- pushValidationIssue(issues, {
26520
- code: "voice.observability.export.missing_field",
26521
- message: `${path}.${key} must be delivered or failed.`,
26522
- path: `${path}.${key}`
26593
+ var rate3 = (count, total) => total > 0 ? count / total : 0;
26594
+ var toIncidentRecoveryTrendCycle = (report) => ({
26595
+ checkedAt: report.checkedAt,
26596
+ failed: report.failed,
26597
+ failureRate: rate3(report.failed, report.total),
26598
+ improved: report.improved,
26599
+ improvementRate: rate3(report.improved, report.total),
26600
+ regressed: report.regressed,
26601
+ regressionRate: rate3(report.regressed, report.total),
26602
+ total: report.total,
26603
+ unchanged: report.unchanged,
26604
+ unchangedRate: rate3(report.unchanged, report.total)
26605
+ });
26606
+ var buildVoiceIncidentRecoveryTrendReport = (reports = []) => {
26607
+ const cycles = reports.map(toIncidentRecoveryTrendCycle).sort((left, right) => left.checkedAt - right.checkedAt);
26608
+ const totals = cycles.reduce((summary, cycle) => ({
26609
+ failed: summary.failed + cycle.failed,
26610
+ improved: summary.improved + cycle.improved,
26611
+ regressed: summary.regressed + cycle.regressed,
26612
+ total: summary.total + cycle.total,
26613
+ unchanged: summary.unchanged + cycle.unchanged
26614
+ }), { failed: 0, improved: 0, regressed: 0, total: 0, unchanged: 0 });
26615
+ const latest = cycles.at(-1);
26616
+ const previous = cycles.at(-2);
26617
+ const status = cycles.length === 0 ? "empty" : latest && (latest.failed > 0 || latest.regressed > 0) ? "fail" : latest && previous && (latest.improvementRate < previous.improvementRate || latest.unchangedRate > previous.unchangedRate) ? "warn" : "pass";
26618
+ return {
26619
+ checkedAt: Date.now(),
26620
+ cycles,
26621
+ latest,
26622
+ previous,
26623
+ status,
26624
+ summary: {
26625
+ cycles: cycles.length,
26626
+ failed: totals.failed,
26627
+ failureRate: rate3(totals.failed, totals.total),
26628
+ improved: totals.improved,
26629
+ improvementRate: rate3(totals.improved, totals.total),
26630
+ regressed: totals.regressed,
26631
+ regressionRate: rate3(totals.regressed, totals.total),
26632
+ total: totals.total,
26633
+ unchanged: totals.unchanged,
26634
+ unchangedRate: rate3(totals.unchanged, totals.total)
26635
+ },
26636
+ trend: {
26637
+ failureRateDelta: latest && previous ? latest.failureRate - previous.failureRate : undefined,
26638
+ improvementRateDelta: latest && previous ? latest.improvementRate - previous.improvementRate : undefined,
26639
+ regressionRateDelta: latest && previous ? latest.regressionRate - previous.regressionRate : undefined,
26640
+ unchangedRateDelta: latest && previous ? latest.unchangedRate - previous.unchangedRate : undefined
26641
+ }
26642
+ };
26643
+ };
26644
+ var percent = (value) => value === undefined ? "n/a" : `${Math.round(value * 100)}%`;
26645
+ var renderVoiceIncidentRecoveryTrendMarkdown = (report, options = {}) => {
26646
+ const title = options.title ?? "Voice Incident Recovery Trend";
26647
+ const rows = report.cycles.map((cycle) => `| ${new Date(cycle.checkedAt).toISOString()} | ${cycle.total} | ${cycle.improved} | ${cycle.unchanged} | ${cycle.regressed} | ${cycle.failed} | ${percent(cycle.improvementRate)} | ${percent(cycle.regressionRate)} |`).join(`
26648
+ `);
26649
+ return `# ${title}
26650
+
26651
+ Generated: ${new Date(report.checkedAt).toISOString()}
26652
+
26653
+ Status: **${report.status}**
26654
+
26655
+ Cycles: ${report.summary.cycles}
26656
+
26657
+ Total actions: ${report.summary.total}
26658
+
26659
+ Improvement rate: ${percent(report.summary.improvementRate)}
26660
+
26661
+ Regression rate: ${percent(report.summary.regressionRate)}
26662
+
26663
+ Failure rate: ${percent(report.summary.failureRate)}
26664
+
26665
+ Unchanged rate: ${percent(report.summary.unchangedRate)}
26666
+
26667
+ Improvement delta: ${percent(report.trend.improvementRateDelta)}
26668
+
26669
+ Regression delta: ${percent(report.trend.regressionRateDelta)}
26670
+
26671
+ ## Cycles
26672
+
26673
+ | Checked at | Total | Improved | Unchanged | Regressed | Failed | Improve % | Regress % |
26674
+ | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
26675
+ ${rows || "| n/a | 0 | 0 | 0 | 0 | 0 | n/a | n/a |"}
26676
+ `;
26677
+ };
26678
+ var renderVoiceIncidentRecoveryTrendHTML = (report, options = {}) => {
26679
+ const title = options.title ?? "AbsoluteJS Voice Incident Recovery Trend";
26680
+ const rows = report.cycles.map((cycle) => `<tr><td>${escapeHtml41(new Date(cycle.checkedAt).toLocaleString())}</td><td>${String(cycle.total)}</td><td>${String(cycle.improved)}</td><td>${String(cycle.unchanged)}</td><td>${String(cycle.regressed)}</td><td>${String(cycle.failed)}</td><td>${escapeHtml41(percent(cycle.improvementRate))}</td><td>${escapeHtml41(percent(cycle.regressionRate))}</td></tr>`).join("");
26681
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml41(title)}</title><style>body{background:#10120d;color:#fbf4df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1080px;padding:32px}.hero,table{background:#181711;border:1px solid #39301d;border-radius:24px}.hero{margin-bottom:16px;padding:24px}h1{font-size:clamp(2rem,6vw,4.5rem);line-height:.95}.summary{display:flex;flex-wrap:wrap;gap:10px}.summary span{border:1px solid #4a3f23;border-radius:999px;padding:8px 12px}table{border-collapse:collapse;overflow:hidden;width:100%}td,th{border-bottom:1px solid #39301d;padding:12px;text-align:left}.pass{color:#86efac}.warn,.empty{color:#fcd34d}.fail{color:#fca5a5}p{color:#cfc5a8}</style></head><body><main><section class="hero"><span>Recovery trend</span><h1>${escapeHtml41(title)}</h1><p class="${escapeHtml41(report.status)}">Status: ${escapeHtml41(report.status)}</p><div class="summary"><span>${String(report.summary.cycles)} cycles</span><span>${String(report.summary.total)} actions</span><span>${escapeHtml41(percent(report.summary.improvementRate))} improved</span><span>${escapeHtml41(percent(report.summary.regressionRate))} regressed</span><span>${escapeHtml41(percent(report.trend.improvementRateDelta))} improvement delta</span></div></section><table><thead><tr><th>Checked at</th><th>Total</th><th>Improved</th><th>Unchanged</th><th>Regressed</th><th>Failed</th><th>Improve %</th><th>Regress %</th></tr></thead><tbody>${rows || '<tr><td colspan="8">No recovery outcome history has been recorded.</td></tr>'}</tbody></table></main></body></html>`;
26682
+ };
26683
+ var pushOperationalStatusEvents = (events, report, links) => {
26684
+ if (!report) {
26685
+ return;
26686
+ }
26687
+ for (const check of report.checks) {
26688
+ if (check.status === "pass") {
26689
+ continue;
26690
+ }
26691
+ events.push({
26692
+ action: {
26693
+ href: check.href ?? links.operationalStatus,
26694
+ label: "Open source"
26695
+ },
26696
+ at: report.checkedAt,
26697
+ category: check.label.toLowerCase().includes("readiness") ? "readiness" : "operational-status",
26698
+ detail: check.detail,
26699
+ href: check.href ?? links.operationalStatus,
26700
+ id: `operational:${check.label}`,
26701
+ label: check.label,
26702
+ severity: statusToSeverity(check.status),
26703
+ source: "operational-status",
26704
+ value: check.value
26523
26705
  });
26524
26706
  }
26525
26707
  };
26526
- var validateDeliveryDestinations = (issues, destinations, path) => {
26527
- if (!destinations) {
26528
- pushValidationIssue(issues, {
26529
- code: "voice.observability.export.missing_field",
26530
- message: `${path} must be an array.`,
26531
- path
26532
- });
26708
+ var pushOpsRecoveryEvents = (events, report, links) => {
26709
+ if (!report) {
26533
26710
  return;
26534
26711
  }
26535
- destinations.forEach((destination, index) => {
26536
- const destinationPath = `${path}.${index}`;
26537
- if (!isRecord3(destination)) {
26538
- pushValidationIssue(issues, {
26539
- code: "voice.observability.export.invalid_shape",
26540
- message: `${destinationPath} must be an object.`,
26541
- path: destinationPath
26712
+ for (const issue of report.issues) {
26713
+ events.push({
26714
+ action: {
26715
+ href: issue.href ?? links.operationalStatus,
26716
+ label: "Inspect recovery issue"
26717
+ },
26718
+ at: report.checkedAt,
26719
+ category: "recovery",
26720
+ detail: issue.detail,
26721
+ href: issue.href,
26722
+ id: `ops-recovery:${issue.code}`,
26723
+ label: issue.label,
26724
+ severity: issue.severity === "fail" ? "critical" : "warn",
26725
+ source: "ops-recovery",
26726
+ value: issue.value
26727
+ });
26728
+ }
26729
+ for (const session of report.failedSessions) {
26730
+ events.push({
26731
+ action: {
26732
+ href: session.operationsRecordHref ?? linkForSession(links.operationsRecords, session.sessionId) ?? linkForSession(links.callDebugger, session.sessionId),
26733
+ label: "Open affected call"
26734
+ },
26735
+ at: session.at,
26736
+ category: "call",
26737
+ detail: session.error,
26738
+ href: session.operationsRecordHref ?? linkForSession(links.operationsRecords, session.sessionId),
26739
+ id: `failed-session:${session.sessionId}:${session.at}`,
26740
+ label: "Failed session",
26741
+ sessionId: session.sessionId,
26742
+ severity: "critical",
26743
+ source: "ops-recovery",
26744
+ value: session.provider
26745
+ });
26746
+ }
26747
+ };
26748
+ var pushMonitorEvents = (events, issues, links) => {
26749
+ if (!issues) {
26750
+ return;
26751
+ }
26752
+ for (const issue of issues) {
26753
+ if (issue.status === "resolved") {
26754
+ continue;
26755
+ }
26756
+ const sessionId = issue.impactedSessions[0];
26757
+ events.push({
26758
+ action: {
26759
+ href: issue.operationsRecordHrefs[0] ?? linkForSession(links.operationsRecords, sessionId) ?? links.monitorIssues,
26760
+ label: "Open monitor evidence"
26761
+ },
26762
+ at: issue.lastSeenAt,
26763
+ category: "monitor",
26764
+ detail: issue.detail,
26765
+ href: issue.operationsRecordHrefs[0] ?? linkForSession(links.operationsRecords, sessionId) ?? links.monitorIssues,
26766
+ id: `monitor:${issue.id}`,
26767
+ label: issue.label,
26768
+ sessionId,
26769
+ severity: issue.severity === "critical" ? "critical" : issue.severity === "warn" ? "warn" : "info",
26770
+ source: `monitor:${issue.monitorId}`,
26771
+ value: issue.value
26772
+ });
26773
+ }
26774
+ };
26775
+ var pushOperationsRecordEvents = (events, records, links) => {
26776
+ if (!records) {
26777
+ return;
26778
+ }
26779
+ for (const record of records) {
26780
+ if (record.status === "healthy") {
26781
+ continue;
26782
+ }
26783
+ const href = linkForSession(links.operationsRecords, record.sessionId);
26784
+ const debuggerHref = linkForSession(links.callDebugger, record.sessionId);
26785
+ events.push({
26786
+ action: {
26787
+ href: debuggerHref ?? href,
26788
+ label: debuggerHref ? "Open call debugger" : "Open operations record"
26789
+ },
26790
+ at: record.checkedAt,
26791
+ category: "call",
26792
+ detail: record.status === "failed" ? "Call operations record failed." : "Call operations record has warnings.",
26793
+ href,
26794
+ id: `operations-record:${record.sessionId}`,
26795
+ label: `Operations record ${record.status}`,
26796
+ sessionId: record.sessionId,
26797
+ severity: statusToSeverity(record.status),
26798
+ source: "operations-record",
26799
+ value: record.outcome.complete ? "complete" : "incomplete"
26800
+ });
26801
+ }
26802
+ };
26803
+ var pushFailureReplayEvents = (events, replays, links) => {
26804
+ if (!replays) {
26805
+ return;
26806
+ }
26807
+ for (const replay of replays) {
26808
+ if (replay.status === "healthy") {
26809
+ continue;
26810
+ }
26811
+ const href = replay.operationsRecordHref ?? linkForSession(links.failureReplay, replay.sessionId) ?? linkForSession(links.callDebugger, replay.sessionId);
26812
+ events.push({
26813
+ action: {
26814
+ href: linkForSession(links.callDebugger, replay.sessionId) ?? href ?? linkForSession(links.supportBundle, replay.sessionId),
26815
+ label: "Open replay/debug artifact"
26816
+ },
26817
+ at: replay.providers.steps[0]?.at ?? replay.media.steps[0]?.at ?? Date.now(),
26818
+ category: "failure-replay",
26819
+ detail: replay.summary.issues.join("; ") || replay.summary.userHeard.join(" ") || `Failure replay is ${replay.status}.`,
26820
+ href,
26821
+ id: `failure-replay:${replay.sessionId}`,
26822
+ label: `Failure replay ${replay.status}`,
26823
+ sessionId: replay.sessionId,
26824
+ severity: failureReplayStatusToSeverity(replay.status),
26825
+ source: "failure-replay",
26826
+ value: `${replay.providers.errors} provider errors / ${replay.media.errors} media errors`
26827
+ });
26828
+ }
26829
+ };
26830
+ var buildVoiceIncidentTimelineReport = async (options) => {
26831
+ const now = options.now ?? Date.now();
26832
+ const links = options.links ?? {};
26833
+ const [
26834
+ operationalStatus,
26835
+ opsRecovery,
26836
+ monitorIssues,
26837
+ operationsRecords,
26838
+ failureReplays
26839
+ ] = await Promise.all([
26840
+ resolveValue(options.operationalStatus),
26841
+ resolveValue(options.opsRecovery),
26842
+ resolveValue(options.monitorIssues),
26843
+ resolveValue(options.operationsRecords),
26844
+ resolveValue(options.failureReplays)
26845
+ ]);
26846
+ const events = [];
26847
+ pushOperationalStatusEvents(events, operationalStatus, links);
26848
+ pushOpsRecoveryEvents(events, opsRecovery, links);
26849
+ pushMonitorEvents(events, monitorIssues, links);
26850
+ pushOperationsRecordEvents(events, operationsRecords, links);
26851
+ pushFailureReplayEvents(events, failureReplays, links);
26852
+ const filtered = events.filter((event) => withinWindow(event, now, options.windowMs)).sort((left, right) => right.at - left.at).slice(0, options.limit ?? 50);
26853
+ const summary = {
26854
+ critical: filtered.filter((event) => event.severity === "critical").length,
26855
+ info: filtered.filter((event) => event.severity === "info").length,
26856
+ total: filtered.length,
26857
+ warn: filtered.filter((event) => event.severity === "warn").length
26858
+ };
26859
+ const baseReport = {
26860
+ events: filtered,
26861
+ generatedAt: now,
26862
+ links,
26863
+ status: worstStatus2(filtered.map(eventStatus2)),
26864
+ summary,
26865
+ windowMs: options.windowMs
26866
+ };
26867
+ const configuredActions = typeof options.recoveryActions === "function" ? await options.recoveryActions({
26868
+ events: filtered,
26869
+ report: baseReport
26870
+ }) : options.recoveryActions;
26871
+ return {
26872
+ ...baseReport,
26873
+ actions: configuredActions === undefined ? defaultIncidentRecoveryActions(filtered, links) : [...configuredActions]
26874
+ };
26875
+ };
26876
+ var renderVoiceIncidentTimelineMarkdown = (report, options = {}) => {
26877
+ const title = options.title ?? "AbsoluteJS Voice Incident Timeline";
26878
+ const rows = report.events.map((event) => {
26879
+ const when = new Date(event.at).toISOString();
26880
+ const target = event.href ? ` [open](${event.href})` : "";
26881
+ const session = event.sessionId ? ` session=${event.sessionId}` : "";
26882
+ const value = event.value === undefined ? "" : ` value=${event.value}`;
26883
+ return `- ${when} ${event.severity.toUpperCase()} ${event.label}${session}${value}${target}${event.detail ? ` - ${event.detail}` : ""}`;
26884
+ }).join(`
26885
+ `);
26886
+ return `# ${title}
26887
+
26888
+ Status: ${report.status}
26889
+
26890
+ Generated: ${new Date(report.generatedAt).toISOString()}
26891
+
26892
+ Summary: ${report.summary.critical} critical, ${report.summary.warn} warn, ${report.summary.info} info, ${report.summary.total} total.
26893
+
26894
+ ## Events
26895
+
26896
+ ${rows || "- No incident timeline events."}
26897
+
26898
+ ## Recovery Actions
26899
+
26900
+ ${report.actions.map((action) => `- ${action.method ?? "GET"} ${action.id}: ${action.label}${action.href ? ` (${action.href})` : ""}${action.detail ? ` - ${action.detail}` : ""}`).join(`
26901
+ `) || "- No recovery actions."}
26902
+ `;
26903
+ };
26904
+ var renderVoiceIncidentTimelineHTML = (report, options = {}) => {
26905
+ const title = options.title ?? "AbsoluteJS Voice Incident Timeline";
26906
+ const actionPath = options.actionPath ?? "/api/voice/incident-timeline/actions";
26907
+ const events = report.events.map((event) => `<article class="${escapeHtml41(event.severity)}">
26908
+ <span>${escapeHtml41(event.severity.toUpperCase())} / ${escapeHtml41(event.category)}</span>
26909
+ <h2>${escapeHtml41(event.label)}</h2>
26910
+ <p>${escapeHtml41(new Date(event.at).toLocaleString())}${event.sessionId ? ` \xB7 session ${escapeHtml41(event.sessionId)}` : ""}</p>
26911
+ ${event.value === undefined ? "" : `<strong>${escapeHtml41(String(event.value))}</strong>`}
26912
+ ${event.detail ? `<p>${escapeHtml41(event.detail)}</p>` : ""}
26913
+ <div>${event.href ? `<a href="${escapeHtml41(event.href)}">Open source</a>` : ""}${event.action?.href ? `<a href="${escapeHtml41(event.action.href)}">${escapeHtml41(event.action.label)}</a>` : ""}</div>
26914
+ </article>`).join("");
26915
+ const actions = report.actions.map((action) => {
26916
+ const label = escapeHtml41(action.label);
26917
+ const detail = action.detail ? `<p>${escapeHtml41(action.detail)}</p>` : "";
26918
+ const href = action.href ? `<a href="${escapeHtml41(action.href)}">Open target</a>` : "";
26919
+ const control = action.method === "POST" ? `<button type="button" data-voice-incident-action="${escapeHtml41(action.id)}" ${action.disabled ? "disabled" : ""}>${label}</button>` : href;
26920
+ return `<article class="action"><span>${escapeHtml41(action.method ?? "GET")}</span><h2>${label}</h2>${detail}<div>${control}${href && action.method === "POST" ? href : ""}</div></article>`;
26921
+ }).join("");
26922
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml41(title)}</title><style>body{background:#11110d;color:#faf4df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1100px;padding:32px}.hero{background:linear-gradient(135deg,rgba(248,113,113,.2),rgba(245,158,11,.13),rgba(34,197,94,.12));border:1px solid #39301d;border-radius:30px;margin-bottom:18px;padding:28px}.eyebrow{color:#fcd34d;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #575030;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.status.pass{border-color:rgba(34,197,94,.65)}.status.warn{border-color:rgba(245,158,11,.75)}.status.fail{border-color:rgba(239,68,68,.85)}.grid{display:grid;gap:14px}.actions{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));margin:0 0 18px}.summary{display:flex;flex-wrap:wrap;gap:10px}.summary span{background:#181711;border:1px solid #39301d;border-radius:999px;padding:8px 12px}article{background:#181711;border:1px solid #39301d;border-radius:22px;padding:18px}article.critical{border-color:rgba(239,68,68,.85)}article.warn{border-color:rgba(245,158,11,.75)}article.info{border-color:rgba(34,197,94,.55)}article.action{border-color:#5b4a22}article span{color:#fcd34d;font-size:.78rem;font-weight:900;letter-spacing:.08em}article h2{margin:.35rem 0}.muted,article p{color:#cfc5a8}article strong{display:block;font-size:1.3rem;margin:.5rem 0}a{color:#fde68a;margin-right:12px}button{background:#fcd34d;border:0;border-radius:999px;color:#171307;cursor:pointer;font-weight:900;padding:10px 14px}button:disabled{cursor:not-allowed;opacity:.55}</style></head><body><main><section class="hero"><p class="eyebrow">Operational triage</p><h1>${escapeHtml41(title)}</h1><p class="status ${escapeHtml41(report.status)}">Overall: ${escapeHtml41(report.status.toUpperCase())}</p><p class="muted">Generated ${escapeHtml41(new Date(report.generatedAt).toLocaleString())}</p><div class="summary"><span>${String(report.summary.critical)} critical</span><span>${String(report.summary.warn)} warn</span><span>${String(report.summary.info)} info</span><span>${String(report.summary.total)} total</span></div></section><h2>Recovery actions</h2><section class="actions">${actions || '<article class="action"><span>NONE</span><h2>No recovery actions</h2><p>No executable actions are available for this report.</p></article>'}</section><h2>Timeline</h2><section class="grid">${events || '<article class="info"><span>INFO</span><h2>No incident events</h2><p>No non-pass operational events were found in this window.</p></article>'}</section></main><script>const voiceIncidentActionPath=${JSON.stringify(actionPath)};document.querySelectorAll("[data-voice-incident-action]").forEach((button)=>{button.addEventListener("click",async()=>{const id=button.getAttribute("data-voice-incident-action");if(!id)return;button.disabled=true;const original=button.textContent;button.textContent="Running...";try{const response=await fetch(voiceIncidentActionPath+"/"+encodeURIComponent(id),{method:"POST"});button.textContent=response.ok?"Done":"Failed";if(response.ok)setTimeout(()=>location.reload(),700)}catch{button.textContent="Failed"}finally{setTimeout(()=>{button.disabled=false;button.textContent=original},1600)}})});</script></body></html>`;
26923
+ };
26924
+ var createVoiceIncidentTimelineRoutes = (options) => {
26925
+ const path = options.path ?? "/api/voice/incident-timeline";
26926
+ const htmlPath = options.htmlPath === undefined ? "/voice/incident-timeline" : options.htmlPath;
26927
+ const markdownPath = options.markdownPath === undefined ? "/voice/incident-timeline.md" : options.markdownPath;
26928
+ const actionPath = options.actionPath === undefined ? "/api/voice/incident-timeline/actions" : options.actionPath;
26929
+ const recoveryOutcomePath = options.recoveryOutcomePath === undefined ? "/api/voice/incident-timeline/recovery-outcomes" : options.recoveryOutcomePath;
26930
+ const recoveryOutcomeHtmlPath = options.recoveryOutcomeHtmlPath === undefined ? "/voice/incident-recovery-outcomes" : options.recoveryOutcomeHtmlPath;
26931
+ const recoveryTrendPath = options.recoveryTrendPath === undefined ? "/api/voice/incident-timeline/recovery-trends" : options.recoveryTrendPath;
26932
+ const recoveryTrendHtmlPath = options.recoveryTrendHtmlPath === undefined ? "/voice/incident-recovery-trends" : options.recoveryTrendHtmlPath;
26933
+ const recoveryTrendMarkdownPath = options.recoveryTrendMarkdownPath === undefined ? "/voice/incident-recovery-trends.md" : options.recoveryTrendMarkdownPath;
26934
+ const buildRecoveryTrendReport = async () => {
26935
+ const reports = typeof options.recoveryTrendReports === "function" ? await options.recoveryTrendReports() : options.recoveryTrendReports;
26936
+ return buildVoiceIncidentRecoveryTrendReport(reports ?? [
26937
+ await buildVoiceIncidentRecoveryOutcomeReport({
26938
+ audit: options.audit
26939
+ })
26940
+ ]);
26941
+ };
26942
+ const routes = new Elysia43({
26943
+ name: options.name ?? "absolutejs-voice-incident-timeline"
26944
+ }).get(path, async () => {
26945
+ const report = await buildVoiceIncidentTimelineReport(options);
26946
+ return new Response(JSON.stringify(report), {
26947
+ headers: {
26948
+ "Content-Type": "application/json; charset=utf-8",
26949
+ ...options.headers
26950
+ },
26951
+ status: report.status === "fail" ? 503 : 200
26952
+ });
26953
+ });
26954
+ if (htmlPath !== false) {
26955
+ routes.get(htmlPath, async () => {
26956
+ const report = await buildVoiceIncidentTimelineReport(options);
26957
+ const body = await (options.render ?? ((input) => renderVoiceIncidentTimelineHTML(input, {
26958
+ actionPath: actionPath === false ? undefined : actionPath,
26959
+ title: options.title
26960
+ })))(report);
26961
+ return new Response(body, {
26962
+ headers: {
26963
+ "Content-Type": "text/html; charset=utf-8",
26964
+ ...options.headers
26965
+ }
26966
+ });
26967
+ });
26968
+ }
26969
+ if (markdownPath !== false) {
26970
+ routes.get(markdownPath, async () => {
26971
+ const report = await buildVoiceIncidentTimelineReport(options);
26972
+ return new Response(renderVoiceIncidentTimelineMarkdown(report, {
26973
+ title: options.title
26974
+ }), {
26975
+ headers: {
26976
+ "Content-Type": "text/markdown; charset=utf-8",
26977
+ ...options.headers
26978
+ }
26979
+ });
26980
+ });
26981
+ }
26982
+ if (actionPath !== false) {
26983
+ routes.get(actionPath, async () => {
26984
+ const report = await buildVoiceIncidentTimelineReport(options);
26985
+ return new Response(JSON.stringify({
26986
+ actions: report.actions,
26987
+ generatedAt: report.generatedAt,
26988
+ status: report.status
26989
+ }), {
26990
+ headers: {
26991
+ "Content-Type": "application/json; charset=utf-8",
26992
+ ...options.headers
26993
+ }
26994
+ });
26995
+ }).post(`${actionPath}/:actionId`, async ({ params, request }) => {
26996
+ const actionId = params.actionId;
26997
+ const report = await buildVoiceIncidentTimelineReport(options);
26998
+ const action = report.actions.find((item) => item.id === actionId);
26999
+ const handler = options.actionHandlers?.[actionId];
27000
+ if (!action) {
27001
+ return new Response(JSON.stringify({
27002
+ actionId,
27003
+ ok: false,
27004
+ status: "not_found"
27005
+ }), {
27006
+ headers: {
27007
+ "Content-Type": "application/json; charset=utf-8",
27008
+ ...options.headers
27009
+ },
27010
+ status: 404
27011
+ });
27012
+ }
27013
+ if (action.disabled || action.method !== "POST" || !handler) {
27014
+ return new Response(JSON.stringify({
27015
+ actionId,
27016
+ ok: false,
27017
+ status: action.disabled ? "disabled" : "not_executable"
27018
+ }), {
27019
+ headers: {
27020
+ "Content-Type": "application/json; charset=utf-8",
27021
+ ...options.headers
27022
+ },
27023
+ status: 409
27024
+ });
27025
+ }
27026
+ const result = await handler({
27027
+ action,
27028
+ actionId,
27029
+ report,
27030
+ request
27031
+ });
27032
+ const status = result.ok ? 200 : 500;
27033
+ const afterReport = await buildVoiceIncidentTimelineReport(options);
27034
+ const resultWithStatus = {
27035
+ ...result,
27036
+ afterStatus: result.afterStatus ?? afterReport.status,
27037
+ beforeStatus: result.beforeStatus ?? report.status
27038
+ };
27039
+ await recordVoiceOpsActionAudit({
27040
+ actionId: `incident.${actionId}`,
27041
+ body: {
27042
+ action,
27043
+ afterStatus: resultWithStatus.afterStatus,
27044
+ beforeStatus: resultWithStatus.beforeStatus,
27045
+ eventIds: report.events.map((event) => event.id),
27046
+ result
27047
+ },
27048
+ error: result.ok ? undefined : result.detail ?? result.status,
27049
+ ok: result.ok,
27050
+ ranAt: Date.now(),
27051
+ status
27052
+ }, {
27053
+ audit: options.audit,
27054
+ trace: options.trace
27055
+ });
27056
+ return new Response(JSON.stringify(resultWithStatus), {
27057
+ headers: {
27058
+ "Content-Type": "application/json; charset=utf-8",
27059
+ ...options.headers
27060
+ },
27061
+ status
27062
+ });
27063
+ });
27064
+ }
27065
+ if (recoveryOutcomePath !== false) {
27066
+ routes.get(recoveryOutcomePath, async () => {
27067
+ const report = await buildVoiceIncidentRecoveryOutcomeReport({
27068
+ audit: options.audit
27069
+ });
27070
+ return new Response(JSON.stringify(report), {
27071
+ headers: {
27072
+ "Content-Type": "application/json; charset=utf-8",
27073
+ ...options.headers
27074
+ }
27075
+ });
27076
+ });
27077
+ }
27078
+ if (recoveryOutcomeHtmlPath !== false) {
27079
+ routes.get(recoveryOutcomeHtmlPath, async () => {
27080
+ const report = await buildVoiceIncidentRecoveryOutcomeReport({
27081
+ audit: options.audit
27082
+ });
27083
+ return new Response(renderVoiceIncidentRecoveryOutcomeHTML(report, {
27084
+ title: `${options.title ?? "AbsoluteJS Voice Incident Timeline"} Recovery Outcomes`
27085
+ }), {
27086
+ headers: {
27087
+ "Content-Type": "text/html; charset=utf-8",
27088
+ ...options.headers
27089
+ }
27090
+ });
27091
+ });
27092
+ }
27093
+ if (recoveryTrendPath !== false) {
27094
+ routes.get(recoveryTrendPath, async () => {
27095
+ const report = await buildRecoveryTrendReport();
27096
+ return new Response(JSON.stringify(report), {
27097
+ headers: {
27098
+ "Content-Type": "application/json; charset=utf-8",
27099
+ ...options.headers
27100
+ }
27101
+ });
27102
+ });
27103
+ }
27104
+ if (recoveryTrendHtmlPath !== false) {
27105
+ routes.get(recoveryTrendHtmlPath, async () => {
27106
+ const report = await buildRecoveryTrendReport();
27107
+ return new Response(renderVoiceIncidentRecoveryTrendHTML(report, {
27108
+ title: `${options.title ?? "AbsoluteJS Voice Incident Timeline"} Recovery Trend`
27109
+ }), {
27110
+ headers: {
27111
+ "Content-Type": "text/html; charset=utf-8",
27112
+ ...options.headers
27113
+ }
27114
+ });
27115
+ });
27116
+ }
27117
+ if (recoveryTrendMarkdownPath !== false) {
27118
+ routes.get(recoveryTrendMarkdownPath, async () => {
27119
+ const report = await buildRecoveryTrendReport();
27120
+ return new Response(renderVoiceIncidentRecoveryTrendMarkdown(report, {
27121
+ title: `${options.title ?? "AbsoluteJS Voice Incident Timeline"} Recovery Trend`
27122
+ }), {
27123
+ headers: {
27124
+ "Content-Type": "text/markdown; charset=utf-8",
27125
+ ...options.headers
27126
+ }
27127
+ });
27128
+ });
27129
+ }
27130
+ return routes;
27131
+ };
27132
+
27133
+ // src/observabilityExport.ts
27134
+ import { Elysia as Elysia44 } from "elysia";
27135
+ import { Database as Database4 } from "bun:sqlite";
27136
+ import { createHash } from "crypto";
27137
+ import { mkdir as mkdir2, readFile, stat, unlink } from "fs/promises";
27138
+ import { join as join2 } from "path";
27139
+ var voiceObservabilityExportSchemaVersion = "1.0.0";
27140
+ var voiceObservabilityExportSchemaId = "com.absolutejs.voice.observability-export";
27141
+ var createVoiceObservabilityExportSchema = () => ({
27142
+ id: voiceObservabilityExportSchemaId,
27143
+ version: voiceObservabilityExportSchemaVersion
27144
+ });
27145
+ var assertVoiceObservabilityExportSchema = (input) => {
27146
+ if (input.schema?.id !== voiceObservabilityExportSchemaId || input.schema?.version !== voiceObservabilityExportSchemaVersion) {
27147
+ throw new Error(`Unsupported voice observability export schema: ${input.schema?.id ?? "missing"}@${input.schema?.version ?? "missing"}`);
27148
+ }
27149
+ };
27150
+ var isRecord4 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
27151
+ var isStatus2 = (value) => value === "fail" || value === "pass" || value === "warn";
27152
+ var getRecord2 = (value, key) => isRecord4(value) && isRecord4(value[key]) ? value[key] : undefined;
27153
+ var getRecordArray = (value, key) => isRecord4(value) && Array.isArray(value[key]) ? value[key] : undefined;
27154
+ var inferVoiceObservabilityExportRecordKind = (record) => {
27155
+ if (isRecord4(record.manifest) && isRecord4(record.artifactIndex)) {
27156
+ return "database-record";
27157
+ }
27158
+ if (Array.isArray(record.receipts)) {
27159
+ return "delivery-history";
27160
+ }
27161
+ if (typeof record.runId === "string" && Array.isArray(record.destinations)) {
27162
+ return "delivery-receipt";
27163
+ }
27164
+ if (Array.isArray(record.destinations) && isRecord4(record.summary) && typeof record.exportStatus === "string") {
27165
+ return "delivery-report";
27166
+ }
27167
+ if (Array.isArray(record.artifacts) && isRecord4(record.summary)) {
27168
+ return Array.isArray(record.envelopes) ? "manifest" : "artifact-index";
27169
+ }
27170
+ return;
27171
+ };
27172
+ var pushValidationIssue = (issues, issue) => {
27173
+ issues.push(issue);
27174
+ };
27175
+ var requireRecordSchema = (issues, record, path) => {
27176
+ const schema = getRecord2(record, "schema");
27177
+ if (schema?.id !== voiceObservabilityExportSchemaId || schema?.version !== voiceObservabilityExportSchemaVersion) {
27178
+ pushValidationIssue(issues, {
27179
+ code: "voice.observability.export.unsupported_schema",
27180
+ message: `Unsupported voice observability export schema: ${schema?.id ?? "missing"}@${schema?.version ?? "missing"}`,
27181
+ path: `${path}.schema`
27182
+ });
27183
+ }
27184
+ return schema;
27185
+ };
27186
+ var requireArrayField = (issues, record, key, path) => {
27187
+ if (!Array.isArray(record[key])) {
27188
+ pushValidationIssue(issues, {
27189
+ code: "voice.observability.export.missing_field",
27190
+ message: `${path}.${key} must be an array.`,
27191
+ path: `${path}.${key}`
27192
+ });
27193
+ }
27194
+ };
27195
+ var requireNumberField = (issues, record, key, path) => {
27196
+ if (typeof record[key] !== "number") {
27197
+ pushValidationIssue(issues, {
27198
+ code: "voice.observability.export.missing_field",
27199
+ message: `${path}.${key} must be a number.`,
27200
+ path: `${path}.${key}`
27201
+ });
27202
+ }
27203
+ };
27204
+ var requireStatusField = (issues, record, key, path) => {
27205
+ if (!isStatus2(record[key])) {
27206
+ pushValidationIssue(issues, {
27207
+ code: "voice.observability.export.missing_field",
27208
+ message: `${path}.${key} must be pass, warn, or fail.`,
27209
+ path: `${path}.${key}`
27210
+ });
27211
+ }
27212
+ };
27213
+ var requireDeliveryDestinationStatusField = (issues, record, key, path) => {
27214
+ if (record[key] !== "delivered" && record[key] !== "failed") {
27215
+ pushValidationIssue(issues, {
27216
+ code: "voice.observability.export.missing_field",
27217
+ message: `${path}.${key} must be delivered or failed.`,
27218
+ path: `${path}.${key}`
27219
+ });
27220
+ }
27221
+ };
27222
+ var validateDeliveryDestinations = (issues, destinations, path) => {
27223
+ if (!destinations) {
27224
+ pushValidationIssue(issues, {
27225
+ code: "voice.observability.export.missing_field",
27226
+ message: `${path} must be an array.`,
27227
+ path
27228
+ });
27229
+ return;
27230
+ }
27231
+ destinations.forEach((destination, index) => {
27232
+ const destinationPath = `${path}.${index}`;
27233
+ if (!isRecord4(destination)) {
27234
+ pushValidationIssue(issues, {
27235
+ code: "voice.observability.export.invalid_shape",
27236
+ message: `${destinationPath} must be an object.`,
27237
+ path: destinationPath
26542
27238
  });
26543
27239
  return;
26544
27240
  }
@@ -26555,7 +27251,7 @@ var validateDeliveryDestinations = (issues, destinations, path) => {
26555
27251
  };
26556
27252
  var validateVoiceObservabilityExportRecord = (input, options = {}) => {
26557
27253
  const issues = [];
26558
- if (!isRecord3(input)) {
27254
+ if (!isRecord4(input)) {
26559
27255
  return {
26560
27256
  issues: [
26561
27257
  {
@@ -26590,21 +27286,21 @@ var validateVoiceObservabilityExportRecord = (input, options = {}) => {
26590
27286
  requireArrayField(issues, input, "sessionIds", "$");
26591
27287
  requireNumberField(issues, input, "checkedAt", "$");
26592
27288
  requireStatusField(issues, input, "status", "$");
26593
- if (!isRecord3(input.deliveries)) {
27289
+ if (!isRecord4(input.deliveries)) {
26594
27290
  pushValidationIssue(issues, {
26595
27291
  code: "voice.observability.export.missing_field",
26596
27292
  message: "$.deliveries must be an object.",
26597
27293
  path: "$.deliveries"
26598
27294
  });
26599
27295
  }
26600
- if (!isRecord3(input.redaction)) {
27296
+ if (!isRecord4(input.redaction)) {
26601
27297
  pushValidationIssue(issues, {
26602
27298
  code: "voice.observability.export.missing_field",
26603
27299
  message: "$.redaction must be an object.",
26604
27300
  path: "$.redaction"
26605
27301
  });
26606
27302
  }
26607
- if (!isRecord3(input.summary)) {
27303
+ if (!isRecord4(input.summary)) {
26608
27304
  pushValidationIssue(issues, {
26609
27305
  code: "voice.observability.export.missing_field",
26610
27306
  message: "$.summary must be an object.",
@@ -26616,7 +27312,7 @@ var validateVoiceObservabilityExportRecord = (input, options = {}) => {
26616
27312
  requireArrayField(issues, input, "artifacts", "$");
26617
27313
  requireNumberField(issues, input, "checkedAt", "$");
26618
27314
  requireStatusField(issues, input, "status", "$");
26619
- if (!isRecord3(input.summary)) {
27315
+ if (!isRecord4(input.summary)) {
26620
27316
  pushValidationIssue(issues, {
26621
27317
  code: "voice.observability.export.missing_field",
26622
27318
  message: "$.summary must be an object.",
@@ -26628,7 +27324,7 @@ var validateVoiceObservabilityExportRecord = (input, options = {}) => {
26628
27324
  requireNumberField(issues, input, "checkedAt", "$");
26629
27325
  requireStatusField(issues, input, "status", "$");
26630
27326
  requireStatusField(issues, input, "exportStatus", "$");
26631
- if (!isRecord3(input.manifest)) {
27327
+ if (!isRecord4(input.manifest)) {
26632
27328
  pushValidationIssue(issues, {
26633
27329
  code: "voice.observability.export.missing_field",
26634
27330
  message: "$.manifest must be an object.",
@@ -26642,7 +27338,7 @@ var validateVoiceObservabilityExportRecord = (input, options = {}) => {
26642
27338
  path: `$.manifest${issue.path.slice(1)}`
26643
27339
  })));
26644
27340
  }
26645
- if (!isRecord3(input.artifactIndex)) {
27341
+ if (!isRecord4(input.artifactIndex)) {
26646
27342
  pushValidationIssue(issues, {
26647
27343
  code: "voice.observability.export.missing_field",
26648
27344
  message: "$.artifactIndex must be an object.",
@@ -26842,11 +27538,11 @@ var buildObservabilityExportDatabaseRecord = (input) => ({
26842
27538
  });
26843
27539
  var parseObservabilityExportJson = (value) => typeof value === "string" ? JSON.parse(value) : value;
26844
27540
  var collectReplayDeliveryDestinations = (value) => {
26845
- if (!isRecord3(value)) {
27541
+ if (!isRecord4(value)) {
26846
27542
  return [];
26847
27543
  }
26848
27544
  if (Array.isArray(value.destinations)) {
26849
- return value.destinations.filter((destination) => isRecord3(destination));
27545
+ return value.destinations.filter((destination) => isRecord4(destination));
26850
27546
  }
26851
27547
  if (Array.isArray(value.receipts)) {
26852
27548
  return value.receipts.flatMap((receipt) => collectReplayDeliveryDestinations(receipt));
@@ -26855,8 +27551,8 @@ var collectReplayDeliveryDestinations = (value) => {
26855
27551
  };
26856
27552
  var replayIssueSeverity = (status) => status === "fail" ? "fail" : "warn";
26857
27553
  var buildVoiceObservabilityExportReplayReport = (records) => {
26858
- const manifest = records.manifest ?? (isRecord3(records.databaseRecord) ? records.databaseRecord.manifest : undefined);
26859
- const artifactIndex = records.artifactIndex ?? (isRecord3(records.databaseRecord) ? records.databaseRecord.artifactIndex : undefined);
27554
+ const manifest = records.manifest ?? (isRecord4(records.databaseRecord) ? records.databaseRecord.manifest : undefined);
27555
+ const artifactIndex = records.artifactIndex ?? (isRecord4(records.databaseRecord) ? records.databaseRecord.artifactIndex : undefined);
26860
27556
  const validations = {
26861
27557
  artifactIndex: validateVoiceObservabilityExportRecord(artifactIndex, {
26862
27558
  kind: "artifact-index"
@@ -26881,12 +27577,12 @@ var buildVoiceObservabilityExportReplayReport = (records) => {
26881
27577
  kind,
26882
27578
  issue
26883
27579
  })) ?? []);
26884
- const manifestRecord = isRecord3(manifest) ? manifest : undefined;
26885
- const artifactIndexRecord = isRecord3(artifactIndex) ? artifactIndex : undefined;
27580
+ const manifestRecord = isRecord4(manifest) ? manifest : undefined;
27581
+ const artifactIndexRecord = isRecord4(artifactIndex) ? artifactIndex : undefined;
26886
27582
  const artifacts = [
26887
27583
  ...Array.isArray(manifestRecord?.artifacts) ? manifestRecord.artifacts : [],
26888
27584
  ...Array.isArray(artifactIndexRecord?.artifacts) ? artifactIndexRecord.artifacts : []
26889
- ].filter((artifact) => isRecord3(artifact));
27585
+ ].filter((artifact) => isRecord4(artifact));
26890
27586
  const failedArtifacts = artifacts.filter((artifact) => artifact.status === "fail");
26891
27587
  const deliveryDestinations = [
26892
27588
  ...collectReplayDeliveryDestinations(records.deliveryReport),
@@ -26895,7 +27591,7 @@ var buildVoiceObservabilityExportReplayReport = (records) => {
26895
27591
  ];
26896
27592
  const failedDeliveryDestinations = deliveryDestinations.filter((destination) => destination.status === "failed");
26897
27593
  const issues = [
26898
- ...!records.manifest && !isRecord3(records.databaseRecord) ? [
27594
+ ...!records.manifest && !isRecord4(records.databaseRecord) ? [
26899
27595
  {
26900
27596
  code: "voice.observability.export_replay.missing_record",
26901
27597
  label: "Export manifest",
@@ -26903,7 +27599,7 @@ var buildVoiceObservabilityExportReplayReport = (records) => {
26903
27599
  value: "manifest"
26904
27600
  }
26905
27601
  ] : [],
26906
- ...!records.artifactIndex && !isRecord3(records.databaseRecord) ? [
27602
+ ...!records.artifactIndex && !isRecord4(records.databaseRecord) ? [
26907
27603
  {
26908
27604
  code: "voice.observability.export_replay.missing_record",
26909
27605
  label: "Artifact index",
@@ -27081,7 +27777,7 @@ var loadVoiceObservabilityExportReplaySource = async (source) => {
27081
27777
  };
27082
27778
  var replayVoiceObservabilityExport = async (source) => buildVoiceObservabilityExportReplayReport(await loadVoiceObservabilityExportReplaySource(source));
27083
27779
  var escapeObservabilityReplayHtml = (value) => String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
27084
- var isVoiceObservabilityExportReplayReport = (value) => isRecord3(value) && isRecord3(value.summary) && isRecord3(value.records) && Array.isArray(value.issues) && typeof value.checkedAt === "number" && isStatus2(value.status);
27780
+ var isVoiceObservabilityExportReplayReport = (value) => isRecord4(value) && isRecord4(value.summary) && isRecord4(value.records) && Array.isArray(value.issues) && typeof value.checkedAt === "number" && isStatus2(value.status);
27085
27781
  var resolveVoiceObservabilityExportReplayReport = async (input) => {
27086
27782
  const resolved = typeof input === "function" ? await input() : input;
27087
27783
  return isVoiceObservabilityExportReplayReport(resolved) ? resolved : replayVoiceObservabilityExport(resolved);
@@ -27100,7 +27796,7 @@ var createVoiceObservabilityExportReplayRoutes = (options) => {
27100
27796
  ...options.headers ?? {}
27101
27797
  };
27102
27798
  const buildReport = () => resolveVoiceObservabilityExportReplayReport(options.source);
27103
- const app = new Elysia43({
27799
+ const app = new Elysia44({
27104
27800
  name: options.name ?? "absolute-voice-observability-export-replay"
27105
27801
  });
27106
27802
  app.get(path, async () => Response.json(await buildReport(), { headers }));
@@ -27966,7 +28662,7 @@ var createVoiceObservabilityExportRoutes = (options = {}) => {
27966
28662
  artifactDownload: options.links?.artifactDownload ?? (artifactDownloadPath ? (artifact) => `${artifactDownloadPath}/${encodeURIComponent(artifact.id)}` : undefined)
27967
28663
  }
27968
28664
  });
27969
- const app = new Elysia43({
28665
+ const app = new Elysia44({
27970
28666
  name: options.name ?? "absolute-voice-observability-export"
27971
28667
  });
27972
28668
  app.get(path, async () => Response.json(await buildReport(), { headers }));
@@ -28073,7 +28769,7 @@ var buildVoiceReadinessRecoveryActions = (input, options = {}) => {
28073
28769
  sourceChecks: sourceChecks.length
28074
28770
  };
28075
28771
  };
28076
- var escapeHtml41 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
28772
+ var escapeHtml42 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
28077
28773
  var formatVoiceProofFreshnessDuration = (valueMs) => {
28078
28774
  if (valueMs < 1000) {
28079
28775
  return `${Math.max(0, Math.round(valueMs))}ms`;
@@ -28628,6 +29324,15 @@ var resolveOpsRecovery = async (options, input) => {
28628
29324
  }
28629
29325
  return options.opsRecovery;
28630
29326
  };
29327
+ var resolveIncidentRecoveryOutcomes = async (options, input) => {
29328
+ if (!options.incidentRecoveryOutcomes) {
29329
+ return;
29330
+ }
29331
+ if (typeof options.incidentRecoveryOutcomes === "function") {
29332
+ return options.incidentRecoveryOutcomes(input);
29333
+ }
29334
+ return options.incidentRecoveryOutcomes;
29335
+ };
28631
29336
  var resolveObservabilityExport = async (options, input) => {
28632
29337
  if (!options.observabilityExport) {
28633
29338
  return;
@@ -28886,6 +29591,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
28886
29591
  bargeInReports,
28887
29592
  campaignReadiness,
28888
29593
  opsRecovery,
29594
+ incidentRecoveryOutcomes,
28889
29595
  observabilityExport,
28890
29596
  observabilityExportDeliveryHistory,
28891
29597
  observabilityExportReplay,
@@ -28933,6 +29639,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
28933
29639
  time("bargeInReports", () => resolveBargeInReports(options, { query, request })),
28934
29640
  time("campaignReadiness", () => resolveCampaignReadiness(options, { query, request })),
28935
29641
  time("opsRecovery", () => resolveOpsRecovery(options, { query, request })),
29642
+ time("incidentRecoveryOutcomes", () => resolveIncidentRecoveryOutcomes(options, { query, request })),
28936
29643
  time("observabilityExport", () => resolveObservabilityExport(options, { query, request })),
28937
29644
  time("observabilityExportDeliveryHistory", () => resolveObservabilityExportDeliveryHistory(options, { query, request })),
28938
29645
  time("observabilityExportReplay", () => resolveObservabilityExportReplay(options, { query, request })),
@@ -28953,6 +29660,10 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
28953
29660
  mediaPipeline,
28954
29661
  telephonyMedia
28955
29662
  });
29663
+ const incidentRecoveryOutcomeReadiness = incidentRecoveryOutcomes && options.incidentRecoveryOutcomeReadiness !== false ? buildVoiceIncidentRecoveryOutcomeReadinessCheck(incidentRecoveryOutcomes, {
29664
+ href: options.incidentRecoveryOutcomeReadiness?.href ?? "/api/voice/incident-timeline/recovery-outcomes",
29665
+ ...options.incidentRecoveryOutcomeReadiness
29666
+ }) : undefined;
28956
29667
  const checks = [
28957
29668
  {
28958
29669
  detail: quality.status === "pass" ? "Quality gates are passing." : "Quality gates need attention.",
@@ -29024,6 +29735,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
29024
29735
  ]
29025
29736
  }
29026
29737
  ] : [],
29738
+ ...incidentRecoveryOutcomeReadiness ? [incidentRecoveryOutcomeReadiness] : [],
29027
29739
  {
29028
29740
  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.`,
29029
29741
  href: firstOperationsRecordHref(operationsRecords.failedSessions) ?? options.links?.sessions ?? "/sessions",
@@ -29357,6 +30069,14 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
29357
30069
  status: observabilityExport.status,
29358
30070
  traceEvents: observabilityExport.summary.traceEvents
29359
30071
  } : undefined;
30072
+ const incidentRecoveryOutcomeSummary = incidentRecoveryOutcomes && incidentRecoveryOutcomeReadiness ? {
30073
+ failed: incidentRecoveryOutcomes.failed,
30074
+ improved: incidentRecoveryOutcomes.improved,
30075
+ regressed: incidentRecoveryOutcomes.regressed,
30076
+ status: incidentRecoveryOutcomeReadiness.status,
30077
+ total: incidentRecoveryOutcomes.total,
30078
+ unchanged: incidentRecoveryOutcomes.unchanged
30079
+ } : undefined;
29360
30080
  const observabilityExportDeliveryHistorySummary = observabilityExportDeliveryHistory ? (() => {
29361
30081
  const latestSuccess = observabilityExportDeliveryHistory.history.receipts.filter((receipt) => receipt.status === "pass" && receipt.summary.delivered > 0 && receipt.summary.failed === 0).sort((left, right) => right.checkedAt - left.checkedAt)[0];
29362
30082
  const latestSuccessAgeMs = latestSuccess ? Date.now() - latestSuccess.checkedAt : undefined;
@@ -29896,6 +30616,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
29896
30616
  total: handoffs.total
29897
30617
  },
29898
30618
  liveLatency,
30619
+ incidentRecoveryOutcomes: incidentRecoveryOutcomeSummary,
29899
30620
  mediaPipeline: mediaPipelineSummary,
29900
30621
  monitoring: monitoringSummary,
29901
30622
  monitoringNotifierDelivery: monitoringNotifierDeliverySummary,
@@ -29950,25 +30671,25 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
29950
30671
  var buildVoiceProductionReadinessGate = async (options, input = {}) => summarizeVoiceProductionReadinessGate(await buildVoiceProductionReadinessReport(options, input), options.gate || undefined);
29951
30672
  var renderVoiceProductionReadinessHTML = (report, options = {}) => {
29952
30673
  const title = options.title ?? "AbsoluteJS Voice Production Readiness";
29953
- const thresholdLink = report.links.sloReadinessThresholds ? `<p><a href="${escapeHtml41(report.links.sloReadinessThresholds)}">Open Calibration -&gt; Active Readiness Gate</a> to inspect the thresholds currently driving calibrated provider, latency, interruption, reconnect, and monitoring gates.</p>` : "";
29954
- const profile = report.profile ? `<section class="profile"><p class="eyebrow">Readiness profile</p><h2>${escapeHtml41(report.profile.name)}</h2><p>${escapeHtml41(report.profile.description)}</p><p>${escapeHtml41(report.profile.purpose)}</p><div class="profile-surfaces">${report.profile.surfaces.map((surface) => `<article class="${surface.configured ? "pass" : "warn"}"><span>${surface.configured ? "CONFIGURED" : "EXPECTED"}</span><strong>${surface.href ? `<a href="${escapeHtml41(surface.href)}">${escapeHtml41(surface.label)}</a>` : escapeHtml41(surface.label)}</strong></article>`).join("")}</div></section>` : "";
30674
+ const thresholdLink = report.links.sloReadinessThresholds ? `<p><a href="${escapeHtml42(report.links.sloReadinessThresholds)}">Open Calibration -&gt; Active Readiness Gate</a> to inspect the thresholds currently driving calibrated provider, latency, interruption, reconnect, and monitoring gates.</p>` : "";
30675
+ const profile = report.profile ? `<section class="profile"><p class="eyebrow">Readiness profile</p><h2>${escapeHtml42(report.profile.name)}</h2><p>${escapeHtml42(report.profile.description)}</p><p>${escapeHtml42(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="${escapeHtml42(surface.href)}">${escapeHtml42(surface.label)}</a>` : escapeHtml42(surface.label)}</strong></article>`).join("")}</div></section>` : "";
29955
30676
  const checks = report.checks.map((check, index) => {
29956
- const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${escapeHtml41(action.href)}">${escapeHtml41(action.label)}</button>` : `<a href="${escapeHtml41(action.href)}">${escapeHtml41(action.label)}</a>`).join("");
29957
- const explanation = check.gateExplanation ? `<p class="gate-explanation">Why this gate is ${escapeHtml41(check.status)}: observed ${escapeHtml41(String(check.gateExplanation.observed ?? "n/a"))}${check.gateExplanation.unit ? ` ${escapeHtml41(check.gateExplanation.unit)}` : ""}; threshold ${escapeHtml41(String(check.gateExplanation.threshold ?? "n/a"))}${check.gateExplanation.unit ? ` ${escapeHtml41(check.gateExplanation.unit)}` : ""}. ${escapeHtml41(check.gateExplanation.remediation)} ${check.gateExplanation.sourceHref ? `<a href="${escapeHtml41(check.gateExplanation.sourceHref)}">Open threshold source</a>` : ""}</p>` : "";
29958
- return `<article class="check ${escapeHtml41(check.status)}">
30677
+ const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${escapeHtml42(action.href)}">${escapeHtml42(action.label)}</button>` : `<a href="${escapeHtml42(action.href)}">${escapeHtml42(action.label)}</a>`).join("");
30678
+ const explanation = check.gateExplanation ? `<p class="gate-explanation">Why this gate is ${escapeHtml42(check.status)}: observed ${escapeHtml42(String(check.gateExplanation.observed ?? "n/a"))}${check.gateExplanation.unit ? ` ${escapeHtml42(check.gateExplanation.unit)}` : ""}; threshold ${escapeHtml42(String(check.gateExplanation.threshold ?? "n/a"))}${check.gateExplanation.unit ? ` ${escapeHtml42(check.gateExplanation.unit)}` : ""}. ${escapeHtml42(check.gateExplanation.remediation)} ${check.gateExplanation.sourceHref ? `<a href="${escapeHtml42(check.gateExplanation.sourceHref)}">Open threshold source</a>` : ""}</p>` : "";
30679
+ return `<article class="check ${escapeHtml42(check.status)}">
29959
30680
  <div>
29960
- <span>${escapeHtml41(check.status.toUpperCase())}</span>
29961
- <h2>${escapeHtml41(check.label)}</h2>
29962
- ${check.detail ? `<p>${escapeHtml41(check.detail)}</p>` : ""}
30681
+ <span>${escapeHtml42(check.status.toUpperCase())}</span>
30682
+ <h2>${escapeHtml42(check.label)}</h2>
30683
+ ${check.detail ? `<p>${escapeHtml42(check.detail)}</p>` : ""}
29963
30684
  ${explanation}
29964
- ${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${escapeHtml41(check.proofSource.href)}">${escapeHtml41(check.proofSource.sourceLabel)}</a>` : escapeHtml41(check.proofSource.sourceLabel)}${check.proofSource.detail ? ` \xB7 ${escapeHtml41(check.proofSource.detail)}` : ""}</p>` : ""}
30685
+ ${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${escapeHtml42(check.proofSource.href)}">${escapeHtml42(check.proofSource.sourceLabel)}</a>` : escapeHtml42(check.proofSource.sourceLabel)}${check.proofSource.detail ? ` \xB7 ${escapeHtml42(check.proofSource.detail)}` : ""}</p>` : ""}
29965
30686
  ${actions ? `<p class="actions">${actions}</p>` : ""}
29966
30687
  </div>
29967
- <strong>${escapeHtml41(String(check.value ?? check.status))}</strong>
29968
- ${check.href ? `<a href="${escapeHtml41(check.href)}">Open surface</a>` : ""}
30688
+ <strong>${escapeHtml42(String(check.value ?? check.status))}</strong>
30689
+ ${check.href ? `<a href="${escapeHtml42(check.href)}">Open surface</a>` : ""}
29969
30690
  </article>`;
29970
30691
  }).join("");
29971
- const snippet = escapeHtml41(`createVoiceProductionReadinessRoutes({
30692
+ const snippet = escapeHtml42(`createVoiceProductionReadinessRoutes({
29972
30693
  htmlPath: '/production-readiness',
29973
30694
  path: '/api/production-readiness',
29974
30695
  gatePath: '/api/production-readiness/gate',
@@ -29984,13 +30705,13 @@ var renderVoiceProductionReadinessHTML = (report, options = {}) => {
29984
30705
  providerRoutingContracts: loadProviderRoutingContracts,
29985
30706
  store: traceStore
29986
30707
  });`);
29987
- return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml41(title)}</title><style>body{background:#0c0f14;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1060px;padding:32px}.hero,.primitive,.profile{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.primitive,.profile{background:#111722}.primitive{border-color:#3a3f2d}.eyebrow{color:#fbbf24;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.status{display:inline-flex;border:1px solid #3f3f46;border-radius:999px;padding:8px 12px}.primitive code{color:#fde68a}.primitive p{color:#c8ccd3;line-height:1.55;margin:.45rem 0 0}.primitive pre{background:#0b0f16;border:1px solid #2c3440;border-radius:18px;color:#fef3c7;margin:16px 0 0;overflow:auto;padding:16px}.status.pass,.check.pass,.profile-surfaces .pass{border-color:rgba(34,197,94,.55)}.status.warn,.check.warn,.profile-surfaces .warn{border-color:rgba(245,158,11,.65)}.status.fail,.check.fail{border-color:rgba(239,68,68,.75)}.checks{display:grid;gap:14px}.check{align-items:center;background:#141922;border:1px solid #26313d;border-radius:22px;display:grid;gap:16px;grid-template-columns:1fr auto auto;padding:18px}.check span,.profile-surfaces span{color:#a8b0b8;font-size:.78rem;font-weight:900;letter-spacing:.08em}.check h2{margin:.2rem 0}.check p,.profile p{color:#b9c0c8;margin:.2rem 0 0}.check .proof-source{color:#f9d77e;font-weight:800}.check .gate-explanation{background:#0b0f16;border:1px solid #2c3440;border-radius:14px;color:#fef3c7;margin-top:10px;padding:10px}.check strong{font-size:1.5rem}.profile-surfaces{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));margin-top:16px}.profile-surfaces article{background:#141922;border:1px solid #26313d;border-radius:16px;padding:14px}.profile-surfaces strong{display:block;margin-top:6px}.actions{display:flex;flex-wrap:wrap;gap:10px}.check a,a{color:#fbbf24}button{background:#fbbf24;border:0;border-radius:999px;color:#111827;cursor:pointer;font-weight:800;padding:9px 12px}button:disabled{cursor:wait;opacity:.65}@media(max-width:760px){main{padding:20px}.check{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Self-hosted readiness</p><h1>${escapeHtml41(title)}</h1><p>One deployable pass/fail report for quality gates, provider failover, session health, handoffs, routing evidence, and optional carrier readiness.</p><p class="status ${escapeHtml41(report.status)}">Overall: ${escapeHtml41(report.status.toUpperCase())}</p><p>Checked ${escapeHtml41(new Date(report.checkedAt).toLocaleString())}</p>${thresholdLink}</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>`;
30708
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml42(title)}</title><style>body{background:#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 .gate-explanation{background:#0b0f16;border:1px solid #2c3440;border-radius:14px;color:#fef3c7;margin-top:10px;padding:10px}.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>${escapeHtml42(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 ${escapeHtml42(report.status)}">Overall: ${escapeHtml42(report.status.toUpperCase())}</p><p>Checked ${escapeHtml42(new Date(report.checkedAt).toLocaleString())}</p>${thresholdLink}</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>`;
29988
30709
  };
29989
30710
  var createVoiceProductionReadinessRoutes = (options) => {
29990
30711
  const path = options.path ?? "/api/production-readiness";
29991
30712
  const gatePath = options.gatePath === undefined ? "/api/production-readiness/gate" : options.gatePath;
29992
30713
  const htmlPath = options.htmlPath ?? "/production-readiness";
29993
- const routes = new Elysia44({
30714
+ const routes = new Elysia45({
29994
30715
  name: options.name ?? "absolutejs-voice-production-readiness"
29995
30716
  });
29996
30717
  let cachedReport;
@@ -30010,544 +30731,144 @@ var createVoiceProductionReadinessRoutes = (options) => {
30010
30731
  const getReport = async (query, request) => {
30011
30732
  const cacheMs = typeof options.cacheMs === "number" && Number.isFinite(options.cacheMs) && options.cacheMs > 0 ? options.cacheMs : 0;
30012
30733
  const key = reportCacheKey(query, request);
30013
- if (cacheMs > 0 && cachedReport && cachedReport.key === key && Date.now() - cachedReport.loadedAt <= cacheMs) {
30014
- return cachedReport.value;
30015
- }
30016
- const value = (async () => {
30017
- const resolvedOptions = await resolveOptions({ query, request });
30018
- return {
30019
- report: await buildVoiceProductionReadinessReport(resolvedOptions, {
30020
- query,
30021
- request
30022
- }),
30023
- resolvedOptions
30024
- };
30025
- })();
30026
- if (cacheMs > 0) {
30027
- cachedReport = {
30028
- key,
30029
- loadedAt: Date.now(),
30030
- value
30031
- };
30032
- }
30033
- return value;
30034
- };
30035
- routes.get(path, async ({ query, request }) => (await getReport(query, request)).report);
30036
- if (gatePath !== false) {
30037
- routes.get(gatePath, async ({ query, request }) => {
30038
- const { report, resolvedOptions } = await getReport(query, request);
30039
- const gate = summarizeVoiceProductionReadinessGate(report, resolvedOptions.gate || undefined);
30040
- return new Response(JSON.stringify(gate), {
30041
- headers: {
30042
- "Content-Type": "application/json; charset=utf-8",
30043
- ...resolvedOptions.headers
30044
- },
30045
- status: gate.ok ? 200 : 503
30046
- });
30047
- });
30048
- }
30049
- if (htmlPath !== false) {
30050
- routes.get(htmlPath, async ({ query, request }) => {
30051
- const { report, resolvedOptions } = await getReport(query, request);
30052
- const body = await (resolvedOptions.render ?? renderVoiceProductionReadinessHTML)(report);
30053
- return new Response(body, {
30054
- headers: {
30055
- "Content-Type": "text/html; charset=utf-8",
30056
- ...resolvedOptions.headers
30057
- }
30058
- });
30059
- });
30060
- }
30061
- return routes;
30062
- };
30063
-
30064
- // src/operationalStatus.ts
30065
- var escapeHtml42 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
30066
- var resolveValue = async (value) => typeof value === "function" ? await value() : value;
30067
- var isDeliveryRuntime = (value) => Boolean(value && typeof value === "object" && "isRunning" in value && "summarize" in value);
30068
- var worstStatus2 = (statuses) => statuses.includes("fail") ? "fail" : statuses.includes("warn") ? "warn" : "pass";
30069
- var proofPackStatusToCheck = (status, href) => {
30070
- const checkStatus = status.state === "failed" || status.state === "missing" ? "fail" : status.state === "fresh" ? "pass" : "warn";
30071
- const age = typeof status.ageMs === "number" ? `${Math.round(status.ageMs / 1000)}s old` : undefined;
30072
- return {
30073
- detail: status.error ?? `Proof pack is ${status.state}.`,
30074
- href,
30075
- label: "Proof pack freshness",
30076
- status: checkStatus,
30077
- value: age ?? status.state
30078
- };
30079
- };
30080
- var deliveryRuntimeStatusToCheck = (report, href) => {
30081
- const summaries = [report.summary.audit, report.summary.trace].filter(Boolean);
30082
- const failed = summaries.reduce((total, summary) => total + (summary?.failed ?? 0) + (summary?.deadLettered ?? 0), 0);
30083
- const pending = summaries.reduce((total, summary) => total + (summary?.pending ?? 0), 0);
30084
- const status = failed > 0 ? "fail" : pending > 0 || !report.isRunning ? "warn" : "pass";
30085
- return {
30086
- detail: failed > 0 ? "Delivery runtime has failed or dead-lettered work." : pending > 0 ? "Delivery runtime has pending work." : report.isRunning ? "Delivery runtime is running with no backlog." : "Delivery runtime is stopped.",
30087
- href,
30088
- label: "Delivery runtime",
30089
- status,
30090
- value: `${pending} pending / ${failed} failed`
30091
- };
30092
- };
30093
- var productionReadinessStatusToCheck = (report, href) => {
30094
- const gate = summarizeVoiceProductionReadinessGate(report);
30095
- return {
30096
- detail: gate.ok ? "Production readiness gate is open." : `${gate.failures.length} failures and ${gate.warnings.length} warnings.`,
30097
- href,
30098
- label: "Production readiness",
30099
- status: gate.ok ? report.status : "fail",
30100
- value: `${gate.failures.length} failures / ${gate.warnings.length} warnings`
30101
- };
30102
- };
30103
- var buildVoiceOperationalStatusReport = async (options) => {
30104
- const [proofPack, deliveryRuntimeReport, productionReadiness] = await Promise.all([
30105
- resolveValue(options.proofPack),
30106
- isDeliveryRuntime(options.deliveryRuntime) ? buildVoiceDeliveryRuntimeReport(options.deliveryRuntime) : resolveValue(options.deliveryRuntime),
30107
- resolveValue(options.productionReadiness)
30108
- ]);
30109
- const checks = [];
30110
- if (proofPack) {
30111
- checks.push(proofPackStatusToCheck(proofPack, options.links?.proofPack));
30112
- }
30113
- if (deliveryRuntimeReport) {
30114
- checks.push(deliveryRuntimeStatusToCheck(deliveryRuntimeReport, options.links?.deliveryRuntime));
30115
- }
30116
- if (productionReadiness) {
30117
- checks.push(productionReadinessStatusToCheck(productionReadiness, options.links?.productionReadiness));
30118
- }
30119
- const summary = {
30120
- fail: checks.filter((check) => check.status === "fail").length,
30121
- pass: checks.filter((check) => check.status === "pass").length,
30122
- total: checks.length,
30123
- warn: checks.filter((check) => check.status === "warn").length
30124
- };
30125
- return {
30126
- checkedAt: Date.now(),
30127
- checks,
30128
- links: options.links ?? {},
30129
- status: worstStatus2(checks.map((check) => check.status)),
30130
- summary
30131
- };
30132
- };
30133
- var renderVoiceOperationalStatusHTML = (report, options = {}) => {
30134
- const title = options.title ?? "AbsoluteJS Voice Operational Status";
30135
- const checks = report.checks.map((check) => `<article class="${escapeHtml42(check.status)}">
30136
- <span>${escapeHtml42(check.status.toUpperCase())}</span>
30137
- <h2>${escapeHtml42(check.label)}</h2>
30138
- <strong>${escapeHtml42(String(check.value ?? check.status))}</strong>
30139
- ${check.detail ? `<p>${escapeHtml42(check.detail)}</p>` : ""}
30140
- ${check.href ? `<a href="${escapeHtml42(check.href)}">Open surface</a>` : ""}
30141
- </article>`).join("");
30142
- return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml42(title)}</title><style>body{background:#10130f;color:#f8f3df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1040px;padding:32px}.hero{background:linear-gradient(135deg,rgba(132,204,22,.18),rgba(14,165,233,.13));border:1px solid #2c3a28;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#bef264;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.status{border:1px solid #64748b;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}article{background:#171d15;border:1px solid #2c3a28;border-radius:22px;padding:18px}article.pass{border-color:rgba(34,197,94,.65)}article.warn{border-color:rgba(245,158,11,.75)}article.fail{border-color:rgba(239,68,68,.85)}article span{color:#bef264;font-size:.78rem;font-weight:900;letter-spacing:.08em}article.warn span{color:#fbbf24}article.fail span{color:#fca5a5}article strong{display:block;font-size:1.6rem;margin:.4rem 0}article p{color:#c5ceb9}a{color:#bef264}</style></head><body><main><section class="hero"><p class="eyebrow">Operational status</p><h1>${escapeHtml42(title)}</h1><p class="status ${escapeHtml42(report.status)}">Overall: ${escapeHtml42(report.status.toUpperCase())}</p><p>${String(report.summary.pass)}/${String(report.summary.total)} checks passing. Checked ${escapeHtml42(new Date(report.checkedAt).toLocaleString())}.</p></section><section class="grid">${checks || '<article class="pass"><span>PASS</span><h2>No operational checks configured</h2><strong>0/0</strong></article>'}</section></main></body></html>`;
30143
- };
30144
- var createVoiceOperationalStatusRoutes = (options) => {
30145
- const path = options.path ?? "/api/voice/operational-status";
30146
- const htmlPath = options.htmlPath === undefined ? "/voice/operational-status" : options.htmlPath;
30147
- const routes = new Elysia45({
30148
- name: options.name ?? "absolutejs-voice-operational-status"
30149
- }).get(path, async () => {
30150
- const report = await buildVoiceOperationalStatusReport(options);
30151
- return new Response(JSON.stringify(report), {
30152
- headers: {
30153
- "Content-Type": "application/json; charset=utf-8",
30154
- ...options.headers
30155
- },
30156
- status: report.status === "fail" ? 503 : 200
30157
- });
30158
- });
30159
- if (htmlPath !== false) {
30160
- routes.get(htmlPath, async () => {
30161
- const report = await buildVoiceOperationalStatusReport(options);
30162
- const body = await (options.render ?? ((input) => renderVoiceOperationalStatusHTML(input, { title: options.title })))(report);
30163
- return new Response(body, {
30164
- headers: {
30165
- "Content-Type": "text/html; charset=utf-8",
30166
- ...options.headers
30167
- }
30168
- });
30169
- });
30170
- }
30171
- return routes;
30172
- };
30173
- // src/incidentTimeline.ts
30174
- import { Elysia as Elysia46 } from "elysia";
30175
- var escapeHtml43 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
30176
- var resolveValue2 = async (value) => typeof value === "function" ? await value() : value;
30177
- var linkForSession = (link, sessionId) => {
30178
- if (!link || !sessionId) {
30179
- return;
30180
- }
30181
- return typeof link === "function" ? link(sessionId) : link;
30182
- };
30183
- var statusToSeverity = (status) => status === "fail" || status === "failed" ? "critical" : status === "warn" || status === "warning" || status === "recovered" ? "warn" : "info";
30184
- var failureReplayStatusToSeverity = (status) => status === "failed" ? "critical" : status === "healthy" ? "info" : "warn";
30185
- var withinWindow = (event, now, windowMs) => !windowMs || event.at >= now - windowMs;
30186
- var eventStatus2 = (event) => event.severity === "critical" ? "fail" : event.severity === "warn" ? "warn" : "pass";
30187
- var defaultIncidentRecoveryActions = (events, links) => {
30188
- const actions = [];
30189
- const add = (action) => {
30190
- const key = `${action.id}:${action.sessionId ?? ""}:${action.href ?? ""}`;
30191
- if (actions.some((existing) => `${existing.id}:${existing.sessionId ?? ""}:${existing.href ?? ""}` === key)) {
30192
- return;
30193
- }
30194
- actions.push(action);
30195
- };
30196
- for (const event of events) {
30197
- if (event.category === "delivery") {
30198
- add({
30199
- detail: "Ask the app to tick delivery workers or retry failed delivery queue work.",
30200
- eventId: event.id,
30201
- href: links.deliveryRuntime,
30202
- id: "delivery.retry",
30203
- label: "Retry delivery work",
30204
- method: "POST",
30205
- sessionId: event.sessionId
30206
- });
30207
- }
30208
- if (event.category === "readiness" || event.category === "operational-status") {
30209
- add({
30210
- detail: "Refresh production readiness and proof freshness before declaring the incident resolved.",
30211
- eventId: event.id,
30212
- href: links.productionReadiness ?? links.operationalStatus,
30213
- id: "readiness.refresh",
30214
- label: "Refresh readiness proof",
30215
- method: "POST",
30216
- sessionId: event.sessionId
30217
- });
30734
+ if (cacheMs > 0 && cachedReport && cachedReport.key === key && Date.now() - cachedReport.loadedAt <= cacheMs) {
30735
+ return cachedReport.value;
30218
30736
  }
30219
- if (event.sessionId) {
30220
- add({
30221
- detail: "Generate or open a support/debug artifact for the affected call.",
30222
- eventId: event.id,
30223
- href: linkForSession(links.supportBundle, event.sessionId) ?? linkForSession(links.callDebugger, event.sessionId),
30224
- id: "support.bundle",
30225
- label: "Generate support bundle",
30226
- method: "POST",
30227
- sessionId: event.sessionId
30228
- });
30737
+ const value = (async () => {
30738
+ const resolvedOptions = await resolveOptions({ query, request });
30739
+ return {
30740
+ report: await buildVoiceProductionReadinessReport(resolvedOptions, {
30741
+ query,
30742
+ request
30743
+ }),
30744
+ resolvedOptions
30745
+ };
30746
+ })();
30747
+ if (cacheMs > 0) {
30748
+ cachedReport = {
30749
+ key,
30750
+ loadedAt: Date.now(),
30751
+ value
30752
+ };
30229
30753
  }
30754
+ return value;
30755
+ };
30756
+ routes.get(path, async ({ query, request }) => (await getReport(query, request)).report);
30757
+ if (gatePath !== false) {
30758
+ routes.get(gatePath, async ({ query, request }) => {
30759
+ const { report, resolvedOptions } = await getReport(query, request);
30760
+ const gate = summarizeVoiceProductionReadinessGate(report, resolvedOptions.gate || undefined);
30761
+ return new Response(JSON.stringify(gate), {
30762
+ headers: {
30763
+ "Content-Type": "application/json; charset=utf-8",
30764
+ ...resolvedOptions.headers
30765
+ },
30766
+ status: gate.ok ? 200 : 503
30767
+ });
30768
+ });
30230
30769
  }
30231
- if (events.some((event) => event.severity !== "info")) {
30232
- add({
30233
- detail: "Rerun the app proof pack to confirm the current release evidence is fresh.",
30234
- href: links.proofPack,
30235
- id: "proof.rerun",
30236
- label: "Rerun proof pack",
30237
- method: "POST"
30770
+ if (htmlPath !== false) {
30771
+ routes.get(htmlPath, async ({ query, request }) => {
30772
+ const { report, resolvedOptions } = await getReport(query, request);
30773
+ const body = await (resolvedOptions.render ?? renderVoiceProductionReadinessHTML)(report);
30774
+ return new Response(body, {
30775
+ headers: {
30776
+ "Content-Type": "text/html; charset=utf-8",
30777
+ ...resolvedOptions.headers
30778
+ }
30779
+ });
30238
30780
  });
30239
30781
  }
30240
- return actions;
30782
+ return routes;
30241
30783
  };
30784
+
30785
+ // src/operationalStatus.ts
30786
+ var escapeHtml43 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
30787
+ var resolveValue2 = async (value) => typeof value === "function" ? await value() : value;
30788
+ var isDeliveryRuntime = (value) => Boolean(value && typeof value === "object" && "isRunning" in value && "summarize" in value);
30242
30789
  var worstStatus3 = (statuses) => statuses.includes("fail") ? "fail" : statuses.includes("warn") ? "warn" : "pass";
30243
- var statusRank6 = (status) => status === "fail" ? 3 : status === "warn" ? 2 : status === "pass" ? 1 : 0;
30244
- var isRecord4 = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
30245
- var getIncidentRecoveryBody = (event) => {
30246
- const payload = isRecord4(event.payload) ? event.payload : {};
30247
- return isRecord4(payload.body) ? payload.body : {};
30248
- };
30249
- var getIncidentRecoveryStatus = (value) => value === "fail" || value === "pass" || value === "warn" ? value : undefined;
30250
- var getIncidentRecoveryDetail = (event) => {
30251
- const payload = isRecord4(event.payload) ? event.payload : {};
30252
- const body = getIncidentRecoveryBody(event);
30253
- const result = isRecord4(body.result) ? body.result : {};
30254
- const detail = result.detail ?? payload.error;
30255
- return typeof detail === "string" ? detail : undefined;
30256
- };
30257
- var toIncidentRecoveryOutcomeEntry = (event) => {
30258
- const body = getIncidentRecoveryBody(event);
30259
- const beforeStatus = getIncidentRecoveryStatus(body.beforeStatus);
30260
- const afterStatus = getIncidentRecoveryStatus(body.afterStatus);
30261
- const beforeRank = statusRank6(beforeStatus);
30262
- const afterRank = statusRank6(afterStatus);
30263
- const outcome = event.outcome === "error" ? "failed" : beforeRank > 0 && afterRank > 0 && afterRank < beforeRank ? "improved" : beforeRank > 0 && afterRank > beforeRank ? "regressed" : "unchanged";
30264
- const payload = isRecord4(event.payload) ? event.payload : {};
30790
+ var proofPackStatusToCheck = (status, href) => {
30791
+ const checkStatus = status.state === "failed" || status.state === "missing" ? "fail" : status.state === "fresh" ? "pass" : "warn";
30792
+ const age = typeof status.ageMs === "number" ? `${Math.round(status.ageMs / 1000)}s old` : undefined;
30265
30793
  return {
30266
- actionId: event.action.replace(/^incident\./, ""),
30267
- afterStatus,
30268
- at: event.at,
30269
- beforeStatus,
30270
- detail: getIncidentRecoveryDetail(event),
30271
- eventId: event.id,
30272
- outcome,
30273
- status: typeof payload.status === "number" ? payload.status : undefined,
30274
- traceId: event.traceId
30794
+ detail: status.error ?? `Proof pack is ${status.state}.`,
30795
+ href,
30796
+ label: "Proof pack freshness",
30797
+ status: checkStatus,
30798
+ value: age ?? status.state
30275
30799
  };
30276
30800
  };
30277
- var buildVoiceIncidentRecoveryOutcomeReport = async (options) => {
30278
- const events = options.audit ? await options.audit.list({
30279
- limit: options.limit ?? 50,
30280
- resourceType: "voice.ops.action",
30281
- type: "operator.action"
30282
- }) : [];
30283
- const entries = events.filter((event) => event.action.startsWith("incident.")).map(toIncidentRecoveryOutcomeEntry).sort((left, right) => right.at - left.at);
30801
+ var deliveryRuntimeStatusToCheck = (report, href) => {
30802
+ const summaries = [report.summary.audit, report.summary.trace].filter(Boolean);
30803
+ const failed = summaries.reduce((total, summary) => total + (summary?.failed ?? 0) + (summary?.deadLettered ?? 0), 0);
30804
+ const pending = summaries.reduce((total, summary) => total + (summary?.pending ?? 0), 0);
30805
+ const status = failed > 0 ? "fail" : pending > 0 || !report.isRunning ? "warn" : "pass";
30284
30806
  return {
30285
- checkedAt: Date.now(),
30286
- entries,
30287
- failed: entries.filter((entry) => entry.outcome === "failed").length,
30288
- improved: entries.filter((entry) => entry.outcome === "improved").length,
30289
- regressed: entries.filter((entry) => entry.outcome === "regressed").length,
30290
- total: entries.length,
30291
- unchanged: entries.filter((entry) => entry.outcome === "unchanged").length
30807
+ detail: failed > 0 ? "Delivery runtime has failed or dead-lettered work." : pending > 0 ? "Delivery runtime has pending work." : report.isRunning ? "Delivery runtime is running with no backlog." : "Delivery runtime is stopped.",
30808
+ href,
30809
+ label: "Delivery runtime",
30810
+ status,
30811
+ value: `${pending} pending / ${failed} failed`
30292
30812
  };
30293
30813
  };
30294
- var renderVoiceIncidentRecoveryOutcomeHTML = (report, options = {}) => {
30295
- const title = options.title ?? "AbsoluteJS Voice Incident Recovery Outcomes";
30296
- const rows = report.entries.map((entry) => `<article class="${escapeHtml43(entry.outcome)}"><span>${escapeHtml43(entry.outcome.toUpperCase())}</span><h2>${escapeHtml43(entry.actionId)}</h2><p>${escapeHtml43(new Date(entry.at).toLocaleString())}</p><strong>${escapeHtml43(entry.beforeStatus ?? "unknown")} -> ${escapeHtml43(entry.afterStatus ?? "unknown")}</strong>${entry.detail ? `<p>${escapeHtml43(entry.detail)}</p>` : ""}</article>`).join("");
30297
- return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml43(title)}</title><style>body{background:#10120d;color:#fbf4df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero,article{background:#181711;border:1px solid #39301d;border-radius:24px;padding:20px}.hero{margin-bottom:16px}h1{font-size:clamp(2rem,6vw,4.5rem);line-height:.95}.summary{display:flex;flex-wrap:wrap;gap:10px}.summary span{border:1px solid #4a3f23;border-radius:999px;padding:8px 12px}section{display:grid;gap:12px}article.improved{border-color:rgba(34,197,94,.65)}article.failed,article.regressed{border-color:rgba(239,68,68,.8)}article.unchanged{border-color:rgba(245,158,11,.7)}article span{color:#fcd34d;font-weight:900;letter-spacing:.08em}article strong{display:block;font-size:1.4rem;margin:.5rem 0}p{color:#cfc5a8}</style></head><body><main><section class="hero"><span>Recovery proof</span><h1>${escapeHtml43(title)}</h1><div class="summary"><span>${String(report.improved)} improved</span><span>${String(report.unchanged)} unchanged</span><span>${String(report.regressed)} regressed</span><span>${String(report.failed)} failed</span><span>${String(report.total)} total</span></div></section><section>${rows || "<p>No incident recovery actions have been recorded.</p>"}</section></main></body></html>`;
30298
- };
30299
- var pushOperationalStatusEvents = (events, report, links) => {
30300
- if (!report) {
30301
- return;
30302
- }
30303
- for (const check of report.checks) {
30304
- if (check.status === "pass") {
30305
- continue;
30306
- }
30307
- events.push({
30308
- action: {
30309
- href: check.href ?? links.operationalStatus,
30310
- label: "Open source"
30311
- },
30312
- at: report.checkedAt,
30313
- category: check.label.toLowerCase().includes("readiness") ? "readiness" : "operational-status",
30314
- detail: check.detail,
30315
- href: check.href ?? links.operationalStatus,
30316
- id: `operational:${check.label}`,
30317
- label: check.label,
30318
- severity: statusToSeverity(check.status),
30319
- source: "operational-status",
30320
- value: check.value
30321
- });
30322
- }
30323
- };
30324
- var pushOpsRecoveryEvents = (events, report, links) => {
30325
- if (!report) {
30326
- return;
30327
- }
30328
- for (const issue of report.issues) {
30329
- events.push({
30330
- action: {
30331
- href: issue.href ?? links.operationalStatus,
30332
- label: "Inspect recovery issue"
30333
- },
30334
- at: report.checkedAt,
30335
- category: "recovery",
30336
- detail: issue.detail,
30337
- href: issue.href,
30338
- id: `ops-recovery:${issue.code}`,
30339
- label: issue.label,
30340
- severity: issue.severity === "fail" ? "critical" : "warn",
30341
- source: "ops-recovery",
30342
- value: issue.value
30343
- });
30344
- }
30345
- for (const session of report.failedSessions) {
30346
- events.push({
30347
- action: {
30348
- href: session.operationsRecordHref ?? linkForSession(links.operationsRecords, session.sessionId) ?? linkForSession(links.callDebugger, session.sessionId),
30349
- label: "Open affected call"
30350
- },
30351
- at: session.at,
30352
- category: "call",
30353
- detail: session.error,
30354
- href: session.operationsRecordHref ?? linkForSession(links.operationsRecords, session.sessionId),
30355
- id: `failed-session:${session.sessionId}:${session.at}`,
30356
- label: "Failed session",
30357
- sessionId: session.sessionId,
30358
- severity: "critical",
30359
- source: "ops-recovery",
30360
- value: session.provider
30361
- });
30362
- }
30363
- };
30364
- var pushMonitorEvents = (events, issues, links) => {
30365
- if (!issues) {
30366
- return;
30367
- }
30368
- for (const issue of issues) {
30369
- if (issue.status === "resolved") {
30370
- continue;
30371
- }
30372
- const sessionId = issue.impactedSessions[0];
30373
- events.push({
30374
- action: {
30375
- href: issue.operationsRecordHrefs[0] ?? linkForSession(links.operationsRecords, sessionId) ?? links.monitorIssues,
30376
- label: "Open monitor evidence"
30377
- },
30378
- at: issue.lastSeenAt,
30379
- category: "monitor",
30380
- detail: issue.detail,
30381
- href: issue.operationsRecordHrefs[0] ?? linkForSession(links.operationsRecords, sessionId) ?? links.monitorIssues,
30382
- id: `monitor:${issue.id}`,
30383
- label: issue.label,
30384
- sessionId,
30385
- severity: issue.severity === "critical" ? "critical" : issue.severity === "warn" ? "warn" : "info",
30386
- source: `monitor:${issue.monitorId}`,
30387
- value: issue.value
30388
- });
30389
- }
30814
+ var productionReadinessStatusToCheck = (report, href) => {
30815
+ const gate = summarizeVoiceProductionReadinessGate(report);
30816
+ return {
30817
+ detail: gate.ok ? "Production readiness gate is open." : `${gate.failures.length} failures and ${gate.warnings.length} warnings.`,
30818
+ href,
30819
+ label: "Production readiness",
30820
+ status: gate.ok ? report.status : "fail",
30821
+ value: `${gate.failures.length} failures / ${gate.warnings.length} warnings`
30822
+ };
30390
30823
  };
30391
- var pushOperationsRecordEvents = (events, records, links) => {
30392
- if (!records) {
30393
- return;
30394
- }
30395
- for (const record of records) {
30396
- if (record.status === "healthy") {
30397
- continue;
30398
- }
30399
- const href = linkForSession(links.operationsRecords, record.sessionId);
30400
- const debuggerHref = linkForSession(links.callDebugger, record.sessionId);
30401
- events.push({
30402
- action: {
30403
- href: debuggerHref ?? href,
30404
- label: debuggerHref ? "Open call debugger" : "Open operations record"
30405
- },
30406
- at: record.checkedAt,
30407
- category: "call",
30408
- detail: record.status === "failed" ? "Call operations record failed." : "Call operations record has warnings.",
30409
- href,
30410
- id: `operations-record:${record.sessionId}`,
30411
- label: `Operations record ${record.status}`,
30412
- sessionId: record.sessionId,
30413
- severity: statusToSeverity(record.status),
30414
- source: "operations-record",
30415
- value: record.outcome.complete ? "complete" : "incomplete"
30416
- });
30824
+ var buildVoiceOperationalStatusReport = async (options) => {
30825
+ const [proofPack, deliveryRuntimeReport, productionReadiness] = await Promise.all([
30826
+ resolveValue2(options.proofPack),
30827
+ isDeliveryRuntime(options.deliveryRuntime) ? buildVoiceDeliveryRuntimeReport(options.deliveryRuntime) : resolveValue2(options.deliveryRuntime),
30828
+ resolveValue2(options.productionReadiness)
30829
+ ]);
30830
+ const checks = [];
30831
+ if (proofPack) {
30832
+ checks.push(proofPackStatusToCheck(proofPack, options.links?.proofPack));
30417
30833
  }
30418
- };
30419
- var pushFailureReplayEvents = (events, replays, links) => {
30420
- if (!replays) {
30421
- return;
30834
+ if (deliveryRuntimeReport) {
30835
+ checks.push(deliveryRuntimeStatusToCheck(deliveryRuntimeReport, options.links?.deliveryRuntime));
30422
30836
  }
30423
- for (const replay of replays) {
30424
- if (replay.status === "healthy") {
30425
- continue;
30426
- }
30427
- const href = replay.operationsRecordHref ?? linkForSession(links.failureReplay, replay.sessionId) ?? linkForSession(links.callDebugger, replay.sessionId);
30428
- events.push({
30429
- action: {
30430
- href: linkForSession(links.callDebugger, replay.sessionId) ?? href ?? linkForSession(links.supportBundle, replay.sessionId),
30431
- label: "Open replay/debug artifact"
30432
- },
30433
- at: replay.providers.steps[0]?.at ?? replay.media.steps[0]?.at ?? Date.now(),
30434
- category: "failure-replay",
30435
- detail: replay.summary.issues.join("; ") || replay.summary.userHeard.join(" ") || `Failure replay is ${replay.status}.`,
30436
- href,
30437
- id: `failure-replay:${replay.sessionId}`,
30438
- label: `Failure replay ${replay.status}`,
30439
- sessionId: replay.sessionId,
30440
- severity: failureReplayStatusToSeverity(replay.status),
30441
- source: "failure-replay",
30442
- value: `${replay.providers.errors} provider errors / ${replay.media.errors} media errors`
30443
- });
30837
+ if (productionReadiness) {
30838
+ checks.push(productionReadinessStatusToCheck(productionReadiness, options.links?.productionReadiness));
30444
30839
  }
30445
- };
30446
- var buildVoiceIncidentTimelineReport = async (options) => {
30447
- const now = options.now ?? Date.now();
30448
- const links = options.links ?? {};
30449
- const [
30450
- operationalStatus,
30451
- opsRecovery,
30452
- monitorIssues,
30453
- operationsRecords,
30454
- failureReplays
30455
- ] = await Promise.all([
30456
- resolveValue2(options.operationalStatus),
30457
- resolveValue2(options.opsRecovery),
30458
- resolveValue2(options.monitorIssues),
30459
- resolveValue2(options.operationsRecords),
30460
- resolveValue2(options.failureReplays)
30461
- ]);
30462
- const events = [];
30463
- pushOperationalStatusEvents(events, operationalStatus, links);
30464
- pushOpsRecoveryEvents(events, opsRecovery, links);
30465
- pushMonitorEvents(events, monitorIssues, links);
30466
- pushOperationsRecordEvents(events, operationsRecords, links);
30467
- pushFailureReplayEvents(events, failureReplays, links);
30468
- const filtered = events.filter((event) => withinWindow(event, now, options.windowMs)).sort((left, right) => right.at - left.at).slice(0, options.limit ?? 50);
30469
30840
  const summary = {
30470
- critical: filtered.filter((event) => event.severity === "critical").length,
30471
- info: filtered.filter((event) => event.severity === "info").length,
30472
- total: filtered.length,
30473
- warn: filtered.filter((event) => event.severity === "warn").length
30474
- };
30475
- const baseReport = {
30476
- events: filtered,
30477
- generatedAt: now,
30478
- links,
30479
- status: worstStatus3(filtered.map(eventStatus2)),
30480
- summary,
30481
- windowMs: options.windowMs
30841
+ fail: checks.filter((check) => check.status === "fail").length,
30842
+ pass: checks.filter((check) => check.status === "pass").length,
30843
+ total: checks.length,
30844
+ warn: checks.filter((check) => check.status === "warn").length
30482
30845
  };
30483
- const configuredActions = typeof options.recoveryActions === "function" ? await options.recoveryActions({
30484
- events: filtered,
30485
- report: baseReport
30486
- }) : options.recoveryActions;
30487
30846
  return {
30488
- ...baseReport,
30489
- actions: configuredActions === undefined ? defaultIncidentRecoveryActions(filtered, links) : [...configuredActions]
30847
+ checkedAt: Date.now(),
30848
+ checks,
30849
+ links: options.links ?? {},
30850
+ status: worstStatus3(checks.map((check) => check.status)),
30851
+ summary
30490
30852
  };
30491
30853
  };
30492
- var renderVoiceIncidentTimelineMarkdown = (report, options = {}) => {
30493
- const title = options.title ?? "AbsoluteJS Voice Incident Timeline";
30494
- const rows = report.events.map((event) => {
30495
- const when = new Date(event.at).toISOString();
30496
- const target = event.href ? ` [open](${event.href})` : "";
30497
- const session = event.sessionId ? ` session=${event.sessionId}` : "";
30498
- const value = event.value === undefined ? "" : ` value=${event.value}`;
30499
- return `- ${when} ${event.severity.toUpperCase()} ${event.label}${session}${value}${target}${event.detail ? ` - ${event.detail}` : ""}`;
30500
- }).join(`
30501
- `);
30502
- return `# ${title}
30503
-
30504
- Status: ${report.status}
30505
-
30506
- Generated: ${new Date(report.generatedAt).toISOString()}
30507
-
30508
- Summary: ${report.summary.critical} critical, ${report.summary.warn} warn, ${report.summary.info} info, ${report.summary.total} total.
30509
-
30510
- ## Events
30511
-
30512
- ${rows || "- No incident timeline events."}
30513
-
30514
- ## Recovery Actions
30515
-
30516
- ${report.actions.map((action) => `- ${action.method ?? "GET"} ${action.id}: ${action.label}${action.href ? ` (${action.href})` : ""}${action.detail ? ` - ${action.detail}` : ""}`).join(`
30517
- `) || "- No recovery actions."}
30518
- `;
30519
- };
30520
- var renderVoiceIncidentTimelineHTML = (report, options = {}) => {
30521
- const title = options.title ?? "AbsoluteJS Voice Incident Timeline";
30522
- const actionPath = options.actionPath ?? "/api/voice/incident-timeline/actions";
30523
- const events = report.events.map((event) => `<article class="${escapeHtml43(event.severity)}">
30524
- <span>${escapeHtml43(event.severity.toUpperCase())} / ${escapeHtml43(event.category)}</span>
30525
- <h2>${escapeHtml43(event.label)}</h2>
30526
- <p>${escapeHtml43(new Date(event.at).toLocaleString())}${event.sessionId ? ` \xB7 session ${escapeHtml43(event.sessionId)}` : ""}</p>
30527
- ${event.value === undefined ? "" : `<strong>${escapeHtml43(String(event.value))}</strong>`}
30528
- ${event.detail ? `<p>${escapeHtml43(event.detail)}</p>` : ""}
30529
- <div>${event.href ? `<a href="${escapeHtml43(event.href)}">Open source</a>` : ""}${event.action?.href ? `<a href="${escapeHtml43(event.action.href)}">${escapeHtml43(event.action.label)}</a>` : ""}</div>
30854
+ var renderVoiceOperationalStatusHTML = (report, options = {}) => {
30855
+ const title = options.title ?? "AbsoluteJS Voice Operational Status";
30856
+ const checks = report.checks.map((check) => `<article class="${escapeHtml43(check.status)}">
30857
+ <span>${escapeHtml43(check.status.toUpperCase())}</span>
30858
+ <h2>${escapeHtml43(check.label)}</h2>
30859
+ <strong>${escapeHtml43(String(check.value ?? check.status))}</strong>
30860
+ ${check.detail ? `<p>${escapeHtml43(check.detail)}</p>` : ""}
30861
+ ${check.href ? `<a href="${escapeHtml43(check.href)}">Open surface</a>` : ""}
30530
30862
  </article>`).join("");
30531
- const actions = report.actions.map((action) => {
30532
- const label = escapeHtml43(action.label);
30533
- const detail = action.detail ? `<p>${escapeHtml43(action.detail)}</p>` : "";
30534
- const href = action.href ? `<a href="${escapeHtml43(action.href)}">Open target</a>` : "";
30535
- const control = action.method === "POST" ? `<button type="button" data-voice-incident-action="${escapeHtml43(action.id)}" ${action.disabled ? "disabled" : ""}>${label}</button>` : href;
30536
- return `<article class="action"><span>${escapeHtml43(action.method ?? "GET")}</span><h2>${label}</h2>${detail}<div>${control}${href && action.method === "POST" ? href : ""}</div></article>`;
30537
- }).join("");
30538
- return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml43(title)}</title><style>body{background:#11110d;color:#faf4df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1100px;padding:32px}.hero{background:linear-gradient(135deg,rgba(248,113,113,.2),rgba(245,158,11,.13),rgba(34,197,94,.12));border:1px solid #39301d;border-radius:30px;margin-bottom:18px;padding:28px}.eyebrow{color:#fcd34d;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #575030;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.status.pass{border-color:rgba(34,197,94,.65)}.status.warn{border-color:rgba(245,158,11,.75)}.status.fail{border-color:rgba(239,68,68,.85)}.grid{display:grid;gap:14px}.actions{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));margin:0 0 18px}.summary{display:flex;flex-wrap:wrap;gap:10px}.summary span{background:#181711;border:1px solid #39301d;border-radius:999px;padding:8px 12px}article{background:#181711;border:1px solid #39301d;border-radius:22px;padding:18px}article.critical{border-color:rgba(239,68,68,.85)}article.warn{border-color:rgba(245,158,11,.75)}article.info{border-color:rgba(34,197,94,.55)}article.action{border-color:#5b4a22}article span{color:#fcd34d;font-size:.78rem;font-weight:900;letter-spacing:.08em}article h2{margin:.35rem 0}.muted,article p{color:#cfc5a8}article strong{display:block;font-size:1.3rem;margin:.5rem 0}a{color:#fde68a;margin-right:12px}button{background:#fcd34d;border:0;border-radius:999px;color:#171307;cursor:pointer;font-weight:900;padding:10px 14px}button:disabled{cursor:not-allowed;opacity:.55}</style></head><body><main><section class="hero"><p class="eyebrow">Operational triage</p><h1>${escapeHtml43(title)}</h1><p class="status ${escapeHtml43(report.status)}">Overall: ${escapeHtml43(report.status.toUpperCase())}</p><p class="muted">Generated ${escapeHtml43(new Date(report.generatedAt).toLocaleString())}</p><div class="summary"><span>${String(report.summary.critical)} critical</span><span>${String(report.summary.warn)} warn</span><span>${String(report.summary.info)} info</span><span>${String(report.summary.total)} total</span></div></section><h2>Recovery actions</h2><section class="actions">${actions || '<article class="action"><span>NONE</span><h2>No recovery actions</h2><p>No executable actions are available for this report.</p></article>'}</section><h2>Timeline</h2><section class="grid">${events || '<article class="info"><span>INFO</span><h2>No incident events</h2><p>No non-pass operational events were found in this window.</p></article>'}</section></main><script>const voiceIncidentActionPath=${JSON.stringify(actionPath)};document.querySelectorAll("[data-voice-incident-action]").forEach((button)=>{button.addEventListener("click",async()=>{const id=button.getAttribute("data-voice-incident-action");if(!id)return;button.disabled=true;const original=button.textContent;button.textContent="Running...";try{const response=await fetch(voiceIncidentActionPath+"/"+encodeURIComponent(id),{method:"POST"});button.textContent=response.ok?"Done":"Failed";if(response.ok)setTimeout(()=>location.reload(),700)}catch{button.textContent="Failed"}finally{setTimeout(()=>{button.disabled=false;button.textContent=original},1600)}})});</script></body></html>`;
30863
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml43(title)}</title><style>body{background:#10130f;color:#f8f3df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1040px;padding:32px}.hero{background:linear-gradient(135deg,rgba(132,204,22,.18),rgba(14,165,233,.13));border:1px solid #2c3a28;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#bef264;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.status{border:1px solid #64748b;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}article{background:#171d15;border:1px solid #2c3a28;border-radius:22px;padding:18px}article.pass{border-color:rgba(34,197,94,.65)}article.warn{border-color:rgba(245,158,11,.75)}article.fail{border-color:rgba(239,68,68,.85)}article span{color:#bef264;font-size:.78rem;font-weight:900;letter-spacing:.08em}article.warn span{color:#fbbf24}article.fail span{color:#fca5a5}article strong{display:block;font-size:1.6rem;margin:.4rem 0}article p{color:#c5ceb9}a{color:#bef264}</style></head><body><main><section class="hero"><p class="eyebrow">Operational status</p><h1>${escapeHtml43(title)}</h1><p class="status ${escapeHtml43(report.status)}">Overall: ${escapeHtml43(report.status.toUpperCase())}</p><p>${String(report.summary.pass)}/${String(report.summary.total)} checks passing. Checked ${escapeHtml43(new Date(report.checkedAt).toLocaleString())}.</p></section><section class="grid">${checks || '<article class="pass"><span>PASS</span><h2>No operational checks configured</h2><strong>0/0</strong></article>'}</section></main></body></html>`;
30539
30864
  };
30540
- var createVoiceIncidentTimelineRoutes = (options) => {
30541
- const path = options.path ?? "/api/voice/incident-timeline";
30542
- const htmlPath = options.htmlPath === undefined ? "/voice/incident-timeline" : options.htmlPath;
30543
- const markdownPath = options.markdownPath === undefined ? "/voice/incident-timeline.md" : options.markdownPath;
30544
- const actionPath = options.actionPath === undefined ? "/api/voice/incident-timeline/actions" : options.actionPath;
30545
- const recoveryOutcomePath = options.recoveryOutcomePath === undefined ? "/api/voice/incident-timeline/recovery-outcomes" : options.recoveryOutcomePath;
30546
- const recoveryOutcomeHtmlPath = options.recoveryOutcomeHtmlPath === undefined ? "/voice/incident-recovery-outcomes" : options.recoveryOutcomeHtmlPath;
30865
+ var createVoiceOperationalStatusRoutes = (options) => {
30866
+ const path = options.path ?? "/api/voice/operational-status";
30867
+ const htmlPath = options.htmlPath === undefined ? "/voice/operational-status" : options.htmlPath;
30547
30868
  const routes = new Elysia46({
30548
- name: options.name ?? "absolutejs-voice-incident-timeline"
30869
+ name: options.name ?? "absolutejs-voice-operational-status"
30549
30870
  }).get(path, async () => {
30550
- const report = await buildVoiceIncidentTimelineReport(options);
30871
+ const report = await buildVoiceOperationalStatusReport(options);
30551
30872
  return new Response(JSON.stringify(report), {
30552
30873
  headers: {
30553
30874
  "Content-Type": "application/json; charset=utf-8",
@@ -30558,11 +30879,8 @@ var createVoiceIncidentTimelineRoutes = (options) => {
30558
30879
  });
30559
30880
  if (htmlPath !== false) {
30560
30881
  routes.get(htmlPath, async () => {
30561
- const report = await buildVoiceIncidentTimelineReport(options);
30562
- const body = await (options.render ?? ((input) => renderVoiceIncidentTimelineHTML(input, {
30563
- actionPath: actionPath === false ? undefined : actionPath,
30564
- title: options.title
30565
- })))(report);
30882
+ const report = await buildVoiceOperationalStatusReport(options);
30883
+ const body = await (options.render ?? ((input) => renderVoiceOperationalStatusHTML(input, { title: options.title })))(report);
30566
30884
  return new Response(body, {
30567
30885
  headers: {
30568
30886
  "Content-Type": "text/html; charset=utf-8",
@@ -30571,130 +30889,6 @@ var createVoiceIncidentTimelineRoutes = (options) => {
30571
30889
  });
30572
30890
  });
30573
30891
  }
30574
- if (markdownPath !== false) {
30575
- routes.get(markdownPath, async () => {
30576
- const report = await buildVoiceIncidentTimelineReport(options);
30577
- return new Response(renderVoiceIncidentTimelineMarkdown(report, {
30578
- title: options.title
30579
- }), {
30580
- headers: {
30581
- "Content-Type": "text/markdown; charset=utf-8",
30582
- ...options.headers
30583
- }
30584
- });
30585
- });
30586
- }
30587
- if (actionPath !== false) {
30588
- routes.get(actionPath, async () => {
30589
- const report = await buildVoiceIncidentTimelineReport(options);
30590
- return new Response(JSON.stringify({
30591
- actions: report.actions,
30592
- generatedAt: report.generatedAt,
30593
- status: report.status
30594
- }), {
30595
- headers: {
30596
- "Content-Type": "application/json; charset=utf-8",
30597
- ...options.headers
30598
- }
30599
- });
30600
- }).post(`${actionPath}/:actionId`, async ({ params, request }) => {
30601
- const actionId = params.actionId;
30602
- const report = await buildVoiceIncidentTimelineReport(options);
30603
- const action = report.actions.find((item) => item.id === actionId);
30604
- const handler = options.actionHandlers?.[actionId];
30605
- if (!action) {
30606
- return new Response(JSON.stringify({
30607
- actionId,
30608
- ok: false,
30609
- status: "not_found"
30610
- }), {
30611
- headers: {
30612
- "Content-Type": "application/json; charset=utf-8",
30613
- ...options.headers
30614
- },
30615
- status: 404
30616
- });
30617
- }
30618
- if (action.disabled || action.method !== "POST" || !handler) {
30619
- return new Response(JSON.stringify({
30620
- actionId,
30621
- ok: false,
30622
- status: action.disabled ? "disabled" : "not_executable"
30623
- }), {
30624
- headers: {
30625
- "Content-Type": "application/json; charset=utf-8",
30626
- ...options.headers
30627
- },
30628
- status: 409
30629
- });
30630
- }
30631
- const result = await handler({
30632
- action,
30633
- actionId,
30634
- report,
30635
- request
30636
- });
30637
- const status = result.ok ? 200 : 500;
30638
- const afterReport = await buildVoiceIncidentTimelineReport(options);
30639
- const resultWithStatus = {
30640
- ...result,
30641
- afterStatus: result.afterStatus ?? afterReport.status,
30642
- beforeStatus: result.beforeStatus ?? report.status
30643
- };
30644
- await recordVoiceOpsActionAudit({
30645
- actionId: `incident.${actionId}`,
30646
- body: {
30647
- action,
30648
- afterStatus: resultWithStatus.afterStatus,
30649
- beforeStatus: resultWithStatus.beforeStatus,
30650
- eventIds: report.events.map((event) => event.id),
30651
- result
30652
- },
30653
- error: result.ok ? undefined : result.detail ?? result.status,
30654
- ok: result.ok,
30655
- ranAt: Date.now(),
30656
- status
30657
- }, {
30658
- audit: options.audit,
30659
- trace: options.trace
30660
- });
30661
- return new Response(JSON.stringify(resultWithStatus), {
30662
- headers: {
30663
- "Content-Type": "application/json; charset=utf-8",
30664
- ...options.headers
30665
- },
30666
- status
30667
- });
30668
- });
30669
- }
30670
- if (recoveryOutcomePath !== false) {
30671
- routes.get(recoveryOutcomePath, async () => {
30672
- const report = await buildVoiceIncidentRecoveryOutcomeReport({
30673
- audit: options.audit
30674
- });
30675
- return new Response(JSON.stringify(report), {
30676
- headers: {
30677
- "Content-Type": "application/json; charset=utf-8",
30678
- ...options.headers
30679
- }
30680
- });
30681
- });
30682
- }
30683
- if (recoveryOutcomeHtmlPath !== false) {
30684
- routes.get(recoveryOutcomeHtmlPath, async () => {
30685
- const report = await buildVoiceIncidentRecoveryOutcomeReport({
30686
- audit: options.audit
30687
- });
30688
- return new Response(renderVoiceIncidentRecoveryOutcomeHTML(report, {
30689
- title: `${options.title ?? "AbsoluteJS Voice Incident Timeline"} Recovery Outcomes`
30690
- }), {
30691
- headers: {
30692
- "Content-Type": "text/html; charset=utf-8",
30693
- ...options.headers
30694
- }
30695
- });
30696
- });
30697
- }
30698
30892
  return routes;
30699
30893
  };
30700
30894
  // src/dataControl.ts
@@ -31317,7 +31511,7 @@ import { Elysia as Elysia48 } from "elysia";
31317
31511
  import { mkdir as mkdir3 } from "fs/promises";
31318
31512
  import { dirname as dirname2 } from "path";
31319
31513
  var escapeHtml45 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
31320
- var rate3 = (count, total) => count / Math.max(1, total);
31514
+ var rate4 = (count, total) => count / Math.max(1, total);
31321
31515
  var normalizeSearchText = (value) => value.trim().toLowerCase();
31322
31516
  var getString18 = (value) => typeof value === "string" ? value : undefined;
31323
31517
  var resolveSessionHref3 = (value, sessionId) => {
@@ -31565,7 +31759,7 @@ var summarizeEvalBaseline = (report) => {
31565
31759
  return {
31566
31760
  failed: report.failed,
31567
31761
  failedSessionIds,
31568
- passRate: rate3(report.passed, report.total),
31762
+ passRate: rate4(report.passed, report.total),
31569
31763
  passed: report.passed,
31570
31764
  total: report.total
31571
31765
  };
@@ -41561,6 +41755,8 @@ export {
41561
41755
  renderVoiceLatencySLOMarkdown,
41562
41756
  renderVoiceIncidentTimelineMarkdown,
41563
41757
  renderVoiceIncidentTimelineHTML,
41758
+ renderVoiceIncidentRecoveryTrendMarkdown,
41759
+ renderVoiceIncidentRecoveryTrendHTML,
41564
41760
  renderVoiceIncidentRecoveryOutcomeHTML,
41565
41761
  renderVoiceHandoffHealthHTML,
41566
41762
  renderVoiceGuardrailMarkdown,
@@ -42063,7 +42259,9 @@ export {
42063
42259
  buildVoiceLiveOpsControlState,
42064
42260
  buildVoiceLatencySLOGate,
42065
42261
  buildVoiceIncidentTimelineReport,
42262
+ buildVoiceIncidentRecoveryTrendReport,
42066
42263
  buildVoiceIncidentRecoveryOutcomeReport,
42264
+ buildVoiceIncidentRecoveryOutcomeReadinessCheck,
42067
42265
  buildVoiceIncidentBundle,
42068
42266
  buildVoiceIOProviderRouterTraceEvent,
42069
42267
  buildVoiceGuardrailReport,