@absolutejs/voice 0.0.22-beta.436 → 0.0.22-beta.438

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,108 +26434,666 @@ 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}`
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
+ };
26557
+ };
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
+ };
26592
+ };
26593
+ var pushOperationalStatusEvents = (events, report, links) => {
26594
+ if (!report) {
26595
+ return;
26596
+ }
26597
+ for (const check of report.checks) {
26598
+ if (check.status === "pass") {
26599
+ continue;
26600
+ }
26601
+ events.push({
26602
+ action: {
26603
+ href: check.href ?? links.operationalStatus,
26604
+ label: "Open source"
26605
+ },
26606
+ at: report.checkedAt,
26607
+ category: check.label.toLowerCase().includes("readiness") ? "readiness" : "operational-status",
26608
+ detail: check.detail,
26609
+ href: check.href ?? links.operationalStatus,
26610
+ id: `operational:${check.label}`,
26611
+ label: check.label,
26612
+ severity: statusToSeverity(check.status),
26613
+ source: "operational-status",
26614
+ value: check.value
26505
26615
  });
26506
26616
  }
26507
26617
  };
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}`
26618
+ var pushOpsRecoveryEvents = (events, report, links) => {
26619
+ if (!report) {
26620
+ return;
26621
+ }
26622
+ for (const issue of report.issues) {
26623
+ events.push({
26624
+ action: {
26625
+ href: issue.href ?? links.operationalStatus,
26626
+ label: "Inspect recovery issue"
26627
+ },
26628
+ at: report.checkedAt,
26629
+ category: "recovery",
26630
+ detail: issue.detail,
26631
+ href: issue.href,
26632
+ id: `ops-recovery:${issue.code}`,
26633
+ label: issue.label,
26634
+ severity: issue.severity === "fail" ? "critical" : "warn",
26635
+ source: "ops-recovery",
26636
+ value: issue.value
26514
26637
  });
26515
26638
  }
26516
- };
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}`
26639
+ for (const session of report.failedSessions) {
26640
+ events.push({
26641
+ action: {
26642
+ href: session.operationsRecordHref ?? linkForSession(links.operationsRecords, session.sessionId) ?? linkForSession(links.callDebugger, session.sessionId),
26643
+ label: "Open affected call"
26644
+ },
26645
+ at: session.at,
26646
+ category: "call",
26647
+ detail: session.error,
26648
+ href: session.operationsRecordHref ?? linkForSession(links.operationsRecords, session.sessionId),
26649
+ id: `failed-session:${session.sessionId}:${session.at}`,
26650
+ label: "Failed session",
26651
+ sessionId: session.sessionId,
26652
+ severity: "critical",
26653
+ source: "ops-recovery",
26654
+ value: session.provider
26523
26655
  });
26524
26656
  }
26525
26657
  };
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
- });
26658
+ var pushMonitorEvents = (events, issues, links) => {
26659
+ if (!issues) {
26533
26660
  return;
26534
26661
  }
26535
- destinations.forEach((destination, index) => {
26536
- const destinationPath = `${path}.${index}`;
26537
- if (!isRecord3(destination)) {
26538
- pushValidationIssue(issues, {
26662
+ for (const issue of issues) {
26663
+ if (issue.status === "resolved") {
26664
+ continue;
26665
+ }
26666
+ const sessionId = issue.impactedSessions[0];
26667
+ events.push({
26668
+ action: {
26669
+ href: issue.operationsRecordHrefs[0] ?? linkForSession(links.operationsRecords, sessionId) ?? links.monitorIssues,
26670
+ label: "Open monitor evidence"
26671
+ },
26672
+ at: issue.lastSeenAt,
26673
+ category: "monitor",
26674
+ detail: issue.detail,
26675
+ href: issue.operationsRecordHrefs[0] ?? linkForSession(links.operationsRecords, sessionId) ?? links.monitorIssues,
26676
+ id: `monitor:${issue.id}`,
26677
+ label: issue.label,
26678
+ sessionId,
26679
+ severity: issue.severity === "critical" ? "critical" : issue.severity === "warn" ? "warn" : "info",
26680
+ source: `monitor:${issue.monitorId}`,
26681
+ value: issue.value
26682
+ });
26683
+ }
26684
+ };
26685
+ var pushOperationsRecordEvents = (events, records, links) => {
26686
+ if (!records) {
26687
+ return;
26688
+ }
26689
+ for (const record of records) {
26690
+ if (record.status === "healthy") {
26691
+ continue;
26692
+ }
26693
+ const href = linkForSession(links.operationsRecords, record.sessionId);
26694
+ const debuggerHref = linkForSession(links.callDebugger, record.sessionId);
26695
+ events.push({
26696
+ action: {
26697
+ href: debuggerHref ?? href,
26698
+ label: debuggerHref ? "Open call debugger" : "Open operations record"
26699
+ },
26700
+ at: record.checkedAt,
26701
+ category: "call",
26702
+ detail: record.status === "failed" ? "Call operations record failed." : "Call operations record has warnings.",
26703
+ href,
26704
+ id: `operations-record:${record.sessionId}`,
26705
+ label: `Operations record ${record.status}`,
26706
+ sessionId: record.sessionId,
26707
+ severity: statusToSeverity(record.status),
26708
+ source: "operations-record",
26709
+ value: record.outcome.complete ? "complete" : "incomplete"
26710
+ });
26711
+ }
26712
+ };
26713
+ var pushFailureReplayEvents = (events, replays, links) => {
26714
+ if (!replays) {
26715
+ return;
26716
+ }
26717
+ for (const replay of replays) {
26718
+ if (replay.status === "healthy") {
26719
+ continue;
26720
+ }
26721
+ const href = replay.operationsRecordHref ?? linkForSession(links.failureReplay, replay.sessionId) ?? linkForSession(links.callDebugger, replay.sessionId);
26722
+ events.push({
26723
+ action: {
26724
+ href: linkForSession(links.callDebugger, replay.sessionId) ?? href ?? linkForSession(links.supportBundle, replay.sessionId),
26725
+ label: "Open replay/debug artifact"
26726
+ },
26727
+ at: replay.providers.steps[0]?.at ?? replay.media.steps[0]?.at ?? Date.now(),
26728
+ category: "failure-replay",
26729
+ detail: replay.summary.issues.join("; ") || replay.summary.userHeard.join(" ") || `Failure replay is ${replay.status}.`,
26730
+ href,
26731
+ id: `failure-replay:${replay.sessionId}`,
26732
+ label: `Failure replay ${replay.status}`,
26733
+ sessionId: replay.sessionId,
26734
+ severity: failureReplayStatusToSeverity(replay.status),
26735
+ source: "failure-replay",
26736
+ value: `${replay.providers.errors} provider errors / ${replay.media.errors} media errors`
26737
+ });
26738
+ }
26739
+ };
26740
+ var buildVoiceIncidentTimelineReport = async (options) => {
26741
+ const now = options.now ?? Date.now();
26742
+ const links = options.links ?? {};
26743
+ const [
26744
+ operationalStatus,
26745
+ opsRecovery,
26746
+ monitorIssues,
26747
+ operationsRecords,
26748
+ failureReplays
26749
+ ] = await Promise.all([
26750
+ resolveValue(options.operationalStatus),
26751
+ resolveValue(options.opsRecovery),
26752
+ resolveValue(options.monitorIssues),
26753
+ resolveValue(options.operationsRecords),
26754
+ resolveValue(options.failureReplays)
26755
+ ]);
26756
+ const events = [];
26757
+ pushOperationalStatusEvents(events, operationalStatus, links);
26758
+ pushOpsRecoveryEvents(events, opsRecovery, links);
26759
+ pushMonitorEvents(events, monitorIssues, links);
26760
+ pushOperationsRecordEvents(events, operationsRecords, links);
26761
+ pushFailureReplayEvents(events, failureReplays, links);
26762
+ const filtered = events.filter((event) => withinWindow(event, now, options.windowMs)).sort((left, right) => right.at - left.at).slice(0, options.limit ?? 50);
26763
+ const summary = {
26764
+ critical: filtered.filter((event) => event.severity === "critical").length,
26765
+ info: filtered.filter((event) => event.severity === "info").length,
26766
+ total: filtered.length,
26767
+ warn: filtered.filter((event) => event.severity === "warn").length
26768
+ };
26769
+ const baseReport = {
26770
+ events: filtered,
26771
+ generatedAt: now,
26772
+ links,
26773
+ status: worstStatus2(filtered.map(eventStatus2)),
26774
+ summary,
26775
+ windowMs: options.windowMs
26776
+ };
26777
+ const configuredActions = typeof options.recoveryActions === "function" ? await options.recoveryActions({
26778
+ events: filtered,
26779
+ report: baseReport
26780
+ }) : options.recoveryActions;
26781
+ return {
26782
+ ...baseReport,
26783
+ actions: configuredActions === undefined ? defaultIncidentRecoveryActions(filtered, links) : [...configuredActions]
26784
+ };
26785
+ };
26786
+ var renderVoiceIncidentTimelineMarkdown = (report, options = {}) => {
26787
+ const title = options.title ?? "AbsoluteJS Voice Incident Timeline";
26788
+ const rows = report.events.map((event) => {
26789
+ const when = new Date(event.at).toISOString();
26790
+ const target = event.href ? ` [open](${event.href})` : "";
26791
+ const session = event.sessionId ? ` session=${event.sessionId}` : "";
26792
+ const value = event.value === undefined ? "" : ` value=${event.value}`;
26793
+ return `- ${when} ${event.severity.toUpperCase()} ${event.label}${session}${value}${target}${event.detail ? ` - ${event.detail}` : ""}`;
26794
+ }).join(`
26795
+ `);
26796
+ return `# ${title}
26797
+
26798
+ Status: ${report.status}
26799
+
26800
+ Generated: ${new Date(report.generatedAt).toISOString()}
26801
+
26802
+ Summary: ${report.summary.critical} critical, ${report.summary.warn} warn, ${report.summary.info} info, ${report.summary.total} total.
26803
+
26804
+ ## Events
26805
+
26806
+ ${rows || "- No incident timeline events."}
26807
+
26808
+ ## Recovery Actions
26809
+
26810
+ ${report.actions.map((action) => `- ${action.method ?? "GET"} ${action.id}: ${action.label}${action.href ? ` (${action.href})` : ""}${action.detail ? ` - ${action.detail}` : ""}`).join(`
26811
+ `) || "- No recovery actions."}
26812
+ `;
26813
+ };
26814
+ var renderVoiceIncidentTimelineHTML = (report, options = {}) => {
26815
+ const title = options.title ?? "AbsoluteJS Voice Incident Timeline";
26816
+ const actionPath = options.actionPath ?? "/api/voice/incident-timeline/actions";
26817
+ const events = report.events.map((event) => `<article class="${escapeHtml41(event.severity)}">
26818
+ <span>${escapeHtml41(event.severity.toUpperCase())} / ${escapeHtml41(event.category)}</span>
26819
+ <h2>${escapeHtml41(event.label)}</h2>
26820
+ <p>${escapeHtml41(new Date(event.at).toLocaleString())}${event.sessionId ? ` \xB7 session ${escapeHtml41(event.sessionId)}` : ""}</p>
26821
+ ${event.value === undefined ? "" : `<strong>${escapeHtml41(String(event.value))}</strong>`}
26822
+ ${event.detail ? `<p>${escapeHtml41(event.detail)}</p>` : ""}
26823
+ <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>
26824
+ </article>`).join("");
26825
+ const actions = report.actions.map((action) => {
26826
+ const label = escapeHtml41(action.label);
26827
+ const detail = action.detail ? `<p>${escapeHtml41(action.detail)}</p>` : "";
26828
+ const href = action.href ? `<a href="${escapeHtml41(action.href)}">Open target</a>` : "";
26829
+ const control = action.method === "POST" ? `<button type="button" data-voice-incident-action="${escapeHtml41(action.id)}" ${action.disabled ? "disabled" : ""}>${label}</button>` : href;
26830
+ return `<article class="action"><span>${escapeHtml41(action.method ?? "GET")}</span><h2>${label}</h2>${detail}<div>${control}${href && action.method === "POST" ? href : ""}</div></article>`;
26831
+ }).join("");
26832
+ 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>`;
26833
+ };
26834
+ var createVoiceIncidentTimelineRoutes = (options) => {
26835
+ const path = options.path ?? "/api/voice/incident-timeline";
26836
+ const htmlPath = options.htmlPath === undefined ? "/voice/incident-timeline" : options.htmlPath;
26837
+ const markdownPath = options.markdownPath === undefined ? "/voice/incident-timeline.md" : options.markdownPath;
26838
+ const actionPath = options.actionPath === undefined ? "/api/voice/incident-timeline/actions" : options.actionPath;
26839
+ const recoveryOutcomePath = options.recoveryOutcomePath === undefined ? "/api/voice/incident-timeline/recovery-outcomes" : options.recoveryOutcomePath;
26840
+ const recoveryOutcomeHtmlPath = options.recoveryOutcomeHtmlPath === undefined ? "/voice/incident-recovery-outcomes" : options.recoveryOutcomeHtmlPath;
26841
+ const routes = new Elysia43({
26842
+ name: options.name ?? "absolutejs-voice-incident-timeline"
26843
+ }).get(path, async () => {
26844
+ const report = await buildVoiceIncidentTimelineReport(options);
26845
+ return new Response(JSON.stringify(report), {
26846
+ headers: {
26847
+ "Content-Type": "application/json; charset=utf-8",
26848
+ ...options.headers
26849
+ },
26850
+ status: report.status === "fail" ? 503 : 200
26851
+ });
26852
+ });
26853
+ if (htmlPath !== false) {
26854
+ routes.get(htmlPath, async () => {
26855
+ const report = await buildVoiceIncidentTimelineReport(options);
26856
+ const body = await (options.render ?? ((input) => renderVoiceIncidentTimelineHTML(input, {
26857
+ actionPath: actionPath === false ? undefined : actionPath,
26858
+ title: options.title
26859
+ })))(report);
26860
+ return new Response(body, {
26861
+ headers: {
26862
+ "Content-Type": "text/html; charset=utf-8",
26863
+ ...options.headers
26864
+ }
26865
+ });
26866
+ });
26867
+ }
26868
+ if (markdownPath !== false) {
26869
+ routes.get(markdownPath, async () => {
26870
+ const report = await buildVoiceIncidentTimelineReport(options);
26871
+ return new Response(renderVoiceIncidentTimelineMarkdown(report, {
26872
+ title: options.title
26873
+ }), {
26874
+ headers: {
26875
+ "Content-Type": "text/markdown; charset=utf-8",
26876
+ ...options.headers
26877
+ }
26878
+ });
26879
+ });
26880
+ }
26881
+ if (actionPath !== false) {
26882
+ routes.get(actionPath, async () => {
26883
+ const report = await buildVoiceIncidentTimelineReport(options);
26884
+ return new Response(JSON.stringify({
26885
+ actions: report.actions,
26886
+ generatedAt: report.generatedAt,
26887
+ status: report.status
26888
+ }), {
26889
+ headers: {
26890
+ "Content-Type": "application/json; charset=utf-8",
26891
+ ...options.headers
26892
+ }
26893
+ });
26894
+ }).post(`${actionPath}/:actionId`, async ({ params, request }) => {
26895
+ const actionId = params.actionId;
26896
+ const report = await buildVoiceIncidentTimelineReport(options);
26897
+ const action = report.actions.find((item) => item.id === actionId);
26898
+ const handler = options.actionHandlers?.[actionId];
26899
+ if (!action) {
26900
+ return new Response(JSON.stringify({
26901
+ actionId,
26902
+ ok: false,
26903
+ status: "not_found"
26904
+ }), {
26905
+ headers: {
26906
+ "Content-Type": "application/json; charset=utf-8",
26907
+ ...options.headers
26908
+ },
26909
+ status: 404
26910
+ });
26911
+ }
26912
+ if (action.disabled || action.method !== "POST" || !handler) {
26913
+ return new Response(JSON.stringify({
26914
+ actionId,
26915
+ ok: false,
26916
+ status: action.disabled ? "disabled" : "not_executable"
26917
+ }), {
26918
+ headers: {
26919
+ "Content-Type": "application/json; charset=utf-8",
26920
+ ...options.headers
26921
+ },
26922
+ status: 409
26923
+ });
26924
+ }
26925
+ const result = await handler({
26926
+ action,
26927
+ actionId,
26928
+ report,
26929
+ request
26930
+ });
26931
+ const status = result.ok ? 200 : 500;
26932
+ const afterReport = await buildVoiceIncidentTimelineReport(options);
26933
+ const resultWithStatus = {
26934
+ ...result,
26935
+ afterStatus: result.afterStatus ?? afterReport.status,
26936
+ beforeStatus: result.beforeStatus ?? report.status
26937
+ };
26938
+ await recordVoiceOpsActionAudit({
26939
+ actionId: `incident.${actionId}`,
26940
+ body: {
26941
+ action,
26942
+ afterStatus: resultWithStatus.afterStatus,
26943
+ beforeStatus: resultWithStatus.beforeStatus,
26944
+ eventIds: report.events.map((event) => event.id),
26945
+ result
26946
+ },
26947
+ error: result.ok ? undefined : result.detail ?? result.status,
26948
+ ok: result.ok,
26949
+ ranAt: Date.now(),
26950
+ status
26951
+ }, {
26952
+ audit: options.audit,
26953
+ trace: options.trace
26954
+ });
26955
+ return new Response(JSON.stringify(resultWithStatus), {
26956
+ headers: {
26957
+ "Content-Type": "application/json; charset=utf-8",
26958
+ ...options.headers
26959
+ },
26960
+ status
26961
+ });
26962
+ });
26963
+ }
26964
+ if (recoveryOutcomePath !== false) {
26965
+ routes.get(recoveryOutcomePath, async () => {
26966
+ const report = await buildVoiceIncidentRecoveryOutcomeReport({
26967
+ audit: options.audit
26968
+ });
26969
+ return new Response(JSON.stringify(report), {
26970
+ headers: {
26971
+ "Content-Type": "application/json; charset=utf-8",
26972
+ ...options.headers
26973
+ }
26974
+ });
26975
+ });
26976
+ }
26977
+ if (recoveryOutcomeHtmlPath !== false) {
26978
+ routes.get(recoveryOutcomeHtmlPath, async () => {
26979
+ const report = await buildVoiceIncidentRecoveryOutcomeReport({
26980
+ audit: options.audit
26981
+ });
26982
+ return new Response(renderVoiceIncidentRecoveryOutcomeHTML(report, {
26983
+ title: `${options.title ?? "AbsoluteJS Voice Incident Timeline"} Recovery Outcomes`
26984
+ }), {
26985
+ headers: {
26986
+ "Content-Type": "text/html; charset=utf-8",
26987
+ ...options.headers
26988
+ }
26989
+ });
26990
+ });
26991
+ }
26992
+ return routes;
26993
+ };
26994
+
26995
+ // src/observabilityExport.ts
26996
+ import { Elysia as Elysia44 } from "elysia";
26997
+ import { Database as Database4 } from "bun:sqlite";
26998
+ import { createHash } from "crypto";
26999
+ import { mkdir as mkdir2, readFile, stat, unlink } from "fs/promises";
27000
+ import { join as join2 } from "path";
27001
+ var voiceObservabilityExportSchemaVersion = "1.0.0";
27002
+ var voiceObservabilityExportSchemaId = "com.absolutejs.voice.observability-export";
27003
+ var createVoiceObservabilityExportSchema = () => ({
27004
+ id: voiceObservabilityExportSchemaId,
27005
+ version: voiceObservabilityExportSchemaVersion
27006
+ });
27007
+ var assertVoiceObservabilityExportSchema = (input) => {
27008
+ if (input.schema?.id !== voiceObservabilityExportSchemaId || input.schema?.version !== voiceObservabilityExportSchemaVersion) {
27009
+ throw new Error(`Unsupported voice observability export schema: ${input.schema?.id ?? "missing"}@${input.schema?.version ?? "missing"}`);
27010
+ }
27011
+ };
27012
+ var isRecord4 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
27013
+ var isStatus2 = (value) => value === "fail" || value === "pass" || value === "warn";
27014
+ var getRecord2 = (value, key) => isRecord4(value) && isRecord4(value[key]) ? value[key] : undefined;
27015
+ var getRecordArray = (value, key) => isRecord4(value) && Array.isArray(value[key]) ? value[key] : undefined;
27016
+ var inferVoiceObservabilityExportRecordKind = (record) => {
27017
+ if (isRecord4(record.manifest) && isRecord4(record.artifactIndex)) {
27018
+ return "database-record";
27019
+ }
27020
+ if (Array.isArray(record.receipts)) {
27021
+ return "delivery-history";
27022
+ }
27023
+ if (typeof record.runId === "string" && Array.isArray(record.destinations)) {
27024
+ return "delivery-receipt";
27025
+ }
27026
+ if (Array.isArray(record.destinations) && isRecord4(record.summary) && typeof record.exportStatus === "string") {
27027
+ return "delivery-report";
27028
+ }
27029
+ if (Array.isArray(record.artifacts) && isRecord4(record.summary)) {
27030
+ return Array.isArray(record.envelopes) ? "manifest" : "artifact-index";
27031
+ }
27032
+ return;
27033
+ };
27034
+ var pushValidationIssue = (issues, issue) => {
27035
+ issues.push(issue);
27036
+ };
27037
+ var requireRecordSchema = (issues, record, path) => {
27038
+ const schema = getRecord2(record, "schema");
27039
+ if (schema?.id !== voiceObservabilityExportSchemaId || schema?.version !== voiceObservabilityExportSchemaVersion) {
27040
+ pushValidationIssue(issues, {
27041
+ code: "voice.observability.export.unsupported_schema",
27042
+ message: `Unsupported voice observability export schema: ${schema?.id ?? "missing"}@${schema?.version ?? "missing"}`,
27043
+ path: `${path}.schema`
27044
+ });
27045
+ }
27046
+ return schema;
27047
+ };
27048
+ var requireArrayField = (issues, record, key, path) => {
27049
+ if (!Array.isArray(record[key])) {
27050
+ pushValidationIssue(issues, {
27051
+ code: "voice.observability.export.missing_field",
27052
+ message: `${path}.${key} must be an array.`,
27053
+ path: `${path}.${key}`
27054
+ });
27055
+ }
27056
+ };
27057
+ var requireNumberField = (issues, record, key, path) => {
27058
+ if (typeof record[key] !== "number") {
27059
+ pushValidationIssue(issues, {
27060
+ code: "voice.observability.export.missing_field",
27061
+ message: `${path}.${key} must be a number.`,
27062
+ path: `${path}.${key}`
27063
+ });
27064
+ }
27065
+ };
27066
+ var requireStatusField = (issues, record, key, path) => {
27067
+ if (!isStatus2(record[key])) {
27068
+ pushValidationIssue(issues, {
27069
+ code: "voice.observability.export.missing_field",
27070
+ message: `${path}.${key} must be pass, warn, or fail.`,
27071
+ path: `${path}.${key}`
27072
+ });
27073
+ }
27074
+ };
27075
+ var requireDeliveryDestinationStatusField = (issues, record, key, path) => {
27076
+ if (record[key] !== "delivered" && record[key] !== "failed") {
27077
+ pushValidationIssue(issues, {
27078
+ code: "voice.observability.export.missing_field",
27079
+ message: `${path}.${key} must be delivered or failed.`,
27080
+ path: `${path}.${key}`
27081
+ });
27082
+ }
27083
+ };
27084
+ var validateDeliveryDestinations = (issues, destinations, path) => {
27085
+ if (!destinations) {
27086
+ pushValidationIssue(issues, {
27087
+ code: "voice.observability.export.missing_field",
27088
+ message: `${path} must be an array.`,
27089
+ path
27090
+ });
27091
+ return;
27092
+ }
27093
+ destinations.forEach((destination, index) => {
27094
+ const destinationPath = `${path}.${index}`;
27095
+ if (!isRecord4(destination)) {
27096
+ pushValidationIssue(issues, {
26539
27097
  code: "voice.observability.export.invalid_shape",
26540
27098
  message: `${destinationPath} must be an object.`,
26541
27099
  path: destinationPath
@@ -26555,7 +27113,7 @@ var validateDeliveryDestinations = (issues, destinations, path) => {
26555
27113
  };
26556
27114
  var validateVoiceObservabilityExportRecord = (input, options = {}) => {
26557
27115
  const issues = [];
26558
- if (!isRecord3(input)) {
27116
+ if (!isRecord4(input)) {
26559
27117
  return {
26560
27118
  issues: [
26561
27119
  {
@@ -26590,21 +27148,21 @@ var validateVoiceObservabilityExportRecord = (input, options = {}) => {
26590
27148
  requireArrayField(issues, input, "sessionIds", "$");
26591
27149
  requireNumberField(issues, input, "checkedAt", "$");
26592
27150
  requireStatusField(issues, input, "status", "$");
26593
- if (!isRecord3(input.deliveries)) {
27151
+ if (!isRecord4(input.deliveries)) {
26594
27152
  pushValidationIssue(issues, {
26595
27153
  code: "voice.observability.export.missing_field",
26596
27154
  message: "$.deliveries must be an object.",
26597
27155
  path: "$.deliveries"
26598
27156
  });
26599
27157
  }
26600
- if (!isRecord3(input.redaction)) {
27158
+ if (!isRecord4(input.redaction)) {
26601
27159
  pushValidationIssue(issues, {
26602
27160
  code: "voice.observability.export.missing_field",
26603
27161
  message: "$.redaction must be an object.",
26604
27162
  path: "$.redaction"
26605
27163
  });
26606
27164
  }
26607
- if (!isRecord3(input.summary)) {
27165
+ if (!isRecord4(input.summary)) {
26608
27166
  pushValidationIssue(issues, {
26609
27167
  code: "voice.observability.export.missing_field",
26610
27168
  message: "$.summary must be an object.",
@@ -26616,7 +27174,7 @@ var validateVoiceObservabilityExportRecord = (input, options = {}) => {
26616
27174
  requireArrayField(issues, input, "artifacts", "$");
26617
27175
  requireNumberField(issues, input, "checkedAt", "$");
26618
27176
  requireStatusField(issues, input, "status", "$");
26619
- if (!isRecord3(input.summary)) {
27177
+ if (!isRecord4(input.summary)) {
26620
27178
  pushValidationIssue(issues, {
26621
27179
  code: "voice.observability.export.missing_field",
26622
27180
  message: "$.summary must be an object.",
@@ -26628,7 +27186,7 @@ var validateVoiceObservabilityExportRecord = (input, options = {}) => {
26628
27186
  requireNumberField(issues, input, "checkedAt", "$");
26629
27187
  requireStatusField(issues, input, "status", "$");
26630
27188
  requireStatusField(issues, input, "exportStatus", "$");
26631
- if (!isRecord3(input.manifest)) {
27189
+ if (!isRecord4(input.manifest)) {
26632
27190
  pushValidationIssue(issues, {
26633
27191
  code: "voice.observability.export.missing_field",
26634
27192
  message: "$.manifest must be an object.",
@@ -26642,7 +27200,7 @@ var validateVoiceObservabilityExportRecord = (input, options = {}) => {
26642
27200
  path: `$.manifest${issue.path.slice(1)}`
26643
27201
  })));
26644
27202
  }
26645
- if (!isRecord3(input.artifactIndex)) {
27203
+ if (!isRecord4(input.artifactIndex)) {
26646
27204
  pushValidationIssue(issues, {
26647
27205
  code: "voice.observability.export.missing_field",
26648
27206
  message: "$.artifactIndex must be an object.",
@@ -26759,6 +27317,41 @@ var createCallDebuggerArtifact = (report, href) => ({
26759
27317
  sessionId: report.sessionId,
26760
27318
  status: "pass"
26761
27319
  });
27320
+ var createIncidentBundleArtifact = (bundle) => ({
27321
+ generatedAt: bundle.exportedAt,
27322
+ id: `incident-bundle:${bundle.sessionId}`,
27323
+ kind: "incident",
27324
+ label: `Incident bundle ${bundle.sessionId}`,
27325
+ metadata: {
27326
+ errors: bundle.summary.errors,
27327
+ recoveryOutcomes: bundle.recoveryOutcomes ? {
27328
+ failed: bundle.recoveryOutcomes.failed,
27329
+ improved: bundle.recoveryOutcomes.improved,
27330
+ regressed: bundle.recoveryOutcomes.regressed,
27331
+ total: bundle.recoveryOutcomes.total,
27332
+ unchanged: bundle.recoveryOutcomes.unchanged
27333
+ } : undefined,
27334
+ status: bundle.summary.status
27335
+ },
27336
+ required: true,
27337
+ sessionId: bundle.sessionId,
27338
+ status: bundle.summary.status === "failed" ? "fail" : bundle.summary.status === "warning" ? "warn" : "pass"
27339
+ });
27340
+ var createIncidentRecoveryOutcomeArtifact = (report) => ({
27341
+ generatedAt: report.checkedAt,
27342
+ id: `incident-recovery-outcomes:${report.checkedAt}`,
27343
+ kind: "incident-recovery-outcomes",
27344
+ label: "Incident recovery outcomes",
27345
+ metadata: {
27346
+ failed: report.failed,
27347
+ improved: report.improved,
27348
+ regressed: report.regressed,
27349
+ total: report.total,
27350
+ unchanged: report.unchanged
27351
+ },
27352
+ required: true,
27353
+ status: report.failed > 0 || report.regressed > 0 ? "fail" : report.unchanged > 0 ? "warn" : "pass"
27354
+ });
26762
27355
  var unique2 = (values) => [...new Set(values)].sort();
26763
27356
  var stripArtifactPathAnchor = (path) => path.split("#")[0] ?? path;
26764
27357
  var toEpochMs = (value) => {
@@ -26807,11 +27400,11 @@ var buildObservabilityExportDatabaseRecord = (input) => ({
26807
27400
  });
26808
27401
  var parseObservabilityExportJson = (value) => typeof value === "string" ? JSON.parse(value) : value;
26809
27402
  var collectReplayDeliveryDestinations = (value) => {
26810
- if (!isRecord3(value)) {
27403
+ if (!isRecord4(value)) {
26811
27404
  return [];
26812
27405
  }
26813
27406
  if (Array.isArray(value.destinations)) {
26814
- return value.destinations.filter((destination) => isRecord3(destination));
27407
+ return value.destinations.filter((destination) => isRecord4(destination));
26815
27408
  }
26816
27409
  if (Array.isArray(value.receipts)) {
26817
27410
  return value.receipts.flatMap((receipt) => collectReplayDeliveryDestinations(receipt));
@@ -26820,8 +27413,8 @@ var collectReplayDeliveryDestinations = (value) => {
26820
27413
  };
26821
27414
  var replayIssueSeverity = (status) => status === "fail" ? "fail" : "warn";
26822
27415
  var buildVoiceObservabilityExportReplayReport = (records) => {
26823
- const manifest = records.manifest ?? (isRecord3(records.databaseRecord) ? records.databaseRecord.manifest : undefined);
26824
- const artifactIndex = records.artifactIndex ?? (isRecord3(records.databaseRecord) ? records.databaseRecord.artifactIndex : undefined);
27416
+ const manifest = records.manifest ?? (isRecord4(records.databaseRecord) ? records.databaseRecord.manifest : undefined);
27417
+ const artifactIndex = records.artifactIndex ?? (isRecord4(records.databaseRecord) ? records.databaseRecord.artifactIndex : undefined);
26825
27418
  const validations = {
26826
27419
  artifactIndex: validateVoiceObservabilityExportRecord(artifactIndex, {
26827
27420
  kind: "artifact-index"
@@ -26846,12 +27439,12 @@ var buildVoiceObservabilityExportReplayReport = (records) => {
26846
27439
  kind,
26847
27440
  issue
26848
27441
  })) ?? []);
26849
- const manifestRecord = isRecord3(manifest) ? manifest : undefined;
26850
- const artifactIndexRecord = isRecord3(artifactIndex) ? artifactIndex : undefined;
27442
+ const manifestRecord = isRecord4(manifest) ? manifest : undefined;
27443
+ const artifactIndexRecord = isRecord4(artifactIndex) ? artifactIndex : undefined;
26851
27444
  const artifacts = [
26852
27445
  ...Array.isArray(manifestRecord?.artifacts) ? manifestRecord.artifacts : [],
26853
27446
  ...Array.isArray(artifactIndexRecord?.artifacts) ? artifactIndexRecord.artifacts : []
26854
- ].filter((artifact) => isRecord3(artifact));
27447
+ ].filter((artifact) => isRecord4(artifact));
26855
27448
  const failedArtifacts = artifacts.filter((artifact) => artifact.status === "fail");
26856
27449
  const deliveryDestinations = [
26857
27450
  ...collectReplayDeliveryDestinations(records.deliveryReport),
@@ -26860,7 +27453,7 @@ var buildVoiceObservabilityExportReplayReport = (records) => {
26860
27453
  ];
26861
27454
  const failedDeliveryDestinations = deliveryDestinations.filter((destination) => destination.status === "failed");
26862
27455
  const issues = [
26863
- ...!records.manifest && !isRecord3(records.databaseRecord) ? [
27456
+ ...!records.manifest && !isRecord4(records.databaseRecord) ? [
26864
27457
  {
26865
27458
  code: "voice.observability.export_replay.missing_record",
26866
27459
  label: "Export manifest",
@@ -26868,7 +27461,7 @@ var buildVoiceObservabilityExportReplayReport = (records) => {
26868
27461
  value: "manifest"
26869
27462
  }
26870
27463
  ] : [],
26871
- ...!records.artifactIndex && !isRecord3(records.databaseRecord) ? [
27464
+ ...!records.artifactIndex && !isRecord4(records.databaseRecord) ? [
26872
27465
  {
26873
27466
  code: "voice.observability.export_replay.missing_record",
26874
27467
  label: "Artifact index",
@@ -27046,7 +27639,7 @@ var loadVoiceObservabilityExportReplaySource = async (source) => {
27046
27639
  };
27047
27640
  var replayVoiceObservabilityExport = async (source) => buildVoiceObservabilityExportReplayReport(await loadVoiceObservabilityExportReplaySource(source));
27048
27641
  var escapeObservabilityReplayHtml = (value) => String(value).replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
27049
- var isVoiceObservabilityExportReplayReport = (value) => isRecord3(value) && isRecord3(value.summary) && isRecord3(value.records) && Array.isArray(value.issues) && typeof value.checkedAt === "number" && isStatus2(value.status);
27642
+ var isVoiceObservabilityExportReplayReport = (value) => isRecord4(value) && isRecord4(value.summary) && isRecord4(value.records) && Array.isArray(value.issues) && typeof value.checkedAt === "number" && isStatus2(value.status);
27050
27643
  var resolveVoiceObservabilityExportReplayReport = async (input) => {
27051
27644
  const resolved = typeof input === "function" ? await input() : input;
27052
27645
  return isVoiceObservabilityExportReplayReport(resolved) ? resolved : replayVoiceObservabilityExport(resolved);
@@ -27065,7 +27658,7 @@ var createVoiceObservabilityExportReplayRoutes = (options) => {
27065
27658
  ...options.headers ?? {}
27066
27659
  };
27067
27660
  const buildReport = () => resolveVoiceObservabilityExportReplayReport(options.source);
27068
- const app = new Elysia43({
27661
+ const app = new Elysia44({
27069
27662
  name: options.name ?? "absolute-voice-observability-export-replay"
27070
27663
  });
27071
27664
  app.get(path, async () => Response.json(await buildReport(), { headers }));
@@ -27419,6 +28012,7 @@ var collectSessionIds = (input) => unique2([
27419
28012
  ...input.events.map((event) => event.sessionId),
27420
28013
  ...input.auditEvents.map((event) => event.sessionId).filter((sessionId) => Boolean(sessionId)),
27421
28014
  ...input.operationsRecords.map((record) => record.sessionId),
28015
+ ...input.incidentBundles.map((bundle) => bundle.sessionId),
27422
28016
  ...input.sessionSnapshots.map((snapshot) => snapshot.sessionId),
27423
28017
  ...input.callDebuggerReports.map((report) => report.sessionId)
27424
28018
  ]);
@@ -27443,6 +28037,15 @@ var collectIssues = (input) => {
27443
28037
  });
27444
28038
  }
27445
28039
  for (const artifact of input.artifacts) {
28040
+ if (artifact.required && (artifact.status === "fail" || artifact.status === "warn")) {
28041
+ issues.push({
28042
+ code: "voice.observability.artifact_failed",
28043
+ detail: `${artifact.label} reported ${artifact.status}.`,
28044
+ label: "Artifact status",
28045
+ severity: artifact.status,
28046
+ value: artifact.id
28047
+ });
28048
+ }
27446
28049
  if (artifact.path && artifact.status !== "pass" && artifact.required && artifact.bytes === undefined && artifact.freshness?.ageMs === undefined) {
27447
28050
  issues.push({
27448
28051
  code: "voice.observability.artifact_missing",
@@ -27541,14 +28144,22 @@ var buildVoiceObservabilityExport = async (options = {}) => {
27541
28144
  const events = await time("events", async () => options.events ?? await options.store?.list() ?? []);
27542
28145
  const auditEvents = await time("auditEvents", async () => options.audit ? await options.audit.list() : []);
27543
28146
  const baseOperationsRecords = options.operationsRecords ?? [];
27544
- const [sessionSnapshots, callDebuggerReports] = await time("supportArtifacts", () => Promise.all([
28147
+ const [
28148
+ sessionSnapshots,
28149
+ callDebuggerReports,
28150
+ incidentBundles,
28151
+ incidentRecoveryOutcomeReports
28152
+ ] = await time("supportArtifacts", () => Promise.all([
27545
28153
  resolveObservabilityExportList(options.sessionSnapshots),
27546
- resolveObservabilityExportList(options.callDebuggerReports)
28154
+ resolveObservabilityExportList(options.callDebuggerReports),
28155
+ resolveObservabilityExportList(options.incidentBundles),
28156
+ resolveObservabilityExportList(options.incidentRecoveryOutcomeReports)
27547
28157
  ]));
27548
28158
  const sessionIds = await time("sessionIds", () => collectSessionIds({
27549
28159
  auditEvents,
27550
28160
  callDebuggerReports,
27551
28161
  events,
28162
+ incidentBundles,
27552
28163
  operationsRecords: baseOperationsRecords,
27553
28164
  sessionIds: options.sessionIds,
27554
28165
  sessionSnapshots
@@ -27575,10 +28186,14 @@ var buildVoiceObservabilityExport = async (options = {}) => {
27575
28186
  const operationArtifacts = await time("operationArtifacts", () => operationsRecords.map((record) => createOperationArtifact(record, options.links?.operationsRecord?.(record.sessionId))));
27576
28187
  const sessionSnapshotArtifacts = await time("sessionSnapshotArtifacts", () => sessionSnapshots.map((snapshot) => createSessionSnapshotArtifact(snapshot, options.links?.sessionSnapshot?.(snapshot.sessionId))));
27577
28188
  const callDebuggerArtifacts = await time("callDebuggerArtifacts", () => callDebuggerReports.map((report) => createCallDebuggerArtifact(report, options.links?.callDebugger?.(report.sessionId))));
28189
+ const incidentBundleArtifacts = await time("incidentBundleArtifacts", () => incidentBundles.map(createIncidentBundleArtifact));
28190
+ const incidentRecoveryOutcomeArtifacts = await time("incidentRecoveryOutcomeArtifacts", () => incidentRecoveryOutcomeReports.map(createIncidentRecoveryOutcomeArtifact));
27578
28191
  const artifacts = await time("artifacts", async () => addArtifactDownloadHrefs(await verifyArtifacts([
27579
28192
  ...operationArtifacts,
27580
28193
  ...sessionSnapshotArtifacts,
27581
28194
  ...callDebuggerArtifacts,
28195
+ ...incidentBundleArtifacts,
28196
+ ...incidentRecoveryOutcomeArtifacts,
27582
28197
  ...options.artifacts ?? []
27583
28198
  ], options.artifactIntegrity), options.links));
27584
28199
  const operationHrefBySessionId = new Map(sessionIds.map((sessionId) => [
@@ -27909,7 +28524,7 @@ var createVoiceObservabilityExportRoutes = (options = {}) => {
27909
28524
  artifactDownload: options.links?.artifactDownload ?? (artifactDownloadPath ? (artifact) => `${artifactDownloadPath}/${encodeURIComponent(artifact.id)}` : undefined)
27910
28525
  }
27911
28526
  });
27912
- const app = new Elysia43({
28527
+ const app = new Elysia44({
27913
28528
  name: options.name ?? "absolute-voice-observability-export"
27914
28529
  });
27915
28530
  app.get(path, async () => Response.json(await buildReport(), { headers }));
@@ -28016,7 +28631,7 @@ var buildVoiceReadinessRecoveryActions = (input, options = {}) => {
28016
28631
  sourceChecks: sourceChecks.length
28017
28632
  };
28018
28633
  };
28019
- var escapeHtml41 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
28634
+ var escapeHtml42 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
28020
28635
  var formatVoiceProofFreshnessDuration = (valueMs) => {
28021
28636
  if (valueMs < 1000) {
28022
28637
  return `${Math.max(0, Math.round(valueMs))}ms`;
@@ -28571,6 +29186,15 @@ var resolveOpsRecovery = async (options, input) => {
28571
29186
  }
28572
29187
  return options.opsRecovery;
28573
29188
  };
29189
+ var resolveIncidentRecoveryOutcomes = async (options, input) => {
29190
+ if (!options.incidentRecoveryOutcomes) {
29191
+ return;
29192
+ }
29193
+ if (typeof options.incidentRecoveryOutcomes === "function") {
29194
+ return options.incidentRecoveryOutcomes(input);
29195
+ }
29196
+ return options.incidentRecoveryOutcomes;
29197
+ };
28574
29198
  var resolveObservabilityExport = async (options, input) => {
28575
29199
  if (!options.observabilityExport) {
28576
29200
  return;
@@ -28829,6 +29453,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
28829
29453
  bargeInReports,
28830
29454
  campaignReadiness,
28831
29455
  opsRecovery,
29456
+ incidentRecoveryOutcomes,
28832
29457
  observabilityExport,
28833
29458
  observabilityExportDeliveryHistory,
28834
29459
  observabilityExportReplay,
@@ -28876,6 +29501,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
28876
29501
  time("bargeInReports", () => resolveBargeInReports(options, { query, request })),
28877
29502
  time("campaignReadiness", () => resolveCampaignReadiness(options, { query, request })),
28878
29503
  time("opsRecovery", () => resolveOpsRecovery(options, { query, request })),
29504
+ time("incidentRecoveryOutcomes", () => resolveIncidentRecoveryOutcomes(options, { query, request })),
28879
29505
  time("observabilityExport", () => resolveObservabilityExport(options, { query, request })),
28880
29506
  time("observabilityExportDeliveryHistory", () => resolveObservabilityExportDeliveryHistory(options, { query, request })),
28881
29507
  time("observabilityExportReplay", () => resolveObservabilityExportReplay(options, { query, request })),
@@ -28896,6 +29522,10 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
28896
29522
  mediaPipeline,
28897
29523
  telephonyMedia
28898
29524
  });
29525
+ const incidentRecoveryOutcomeReadiness = incidentRecoveryOutcomes && options.incidentRecoveryOutcomeReadiness !== false ? buildVoiceIncidentRecoveryOutcomeReadinessCheck(incidentRecoveryOutcomes, {
29526
+ href: options.incidentRecoveryOutcomeReadiness?.href ?? "/api/voice/incident-timeline/recovery-outcomes",
29527
+ ...options.incidentRecoveryOutcomeReadiness
29528
+ }) : undefined;
28899
29529
  const checks = [
28900
29530
  {
28901
29531
  detail: quality.status === "pass" ? "Quality gates are passing." : "Quality gates need attention.",
@@ -28967,6 +29597,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
28967
29597
  ]
28968
29598
  }
28969
29599
  ] : [],
29600
+ ...incidentRecoveryOutcomeReadiness ? [incidentRecoveryOutcomeReadiness] : [],
28970
29601
  {
28971
29602
  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.`,
28972
29603
  href: firstOperationsRecordHref(operationsRecords.failedSessions) ?? options.links?.sessions ?? "/sessions",
@@ -29300,6 +29931,14 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
29300
29931
  status: observabilityExport.status,
29301
29932
  traceEvents: observabilityExport.summary.traceEvents
29302
29933
  } : undefined;
29934
+ const incidentRecoveryOutcomeSummary = incidentRecoveryOutcomes && incidentRecoveryOutcomeReadiness ? {
29935
+ failed: incidentRecoveryOutcomes.failed,
29936
+ improved: incidentRecoveryOutcomes.improved,
29937
+ regressed: incidentRecoveryOutcomes.regressed,
29938
+ status: incidentRecoveryOutcomeReadiness.status,
29939
+ total: incidentRecoveryOutcomes.total,
29940
+ unchanged: incidentRecoveryOutcomes.unchanged
29941
+ } : undefined;
29303
29942
  const observabilityExportDeliveryHistorySummary = observabilityExportDeliveryHistory ? (() => {
29304
29943
  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];
29305
29944
  const latestSuccessAgeMs = latestSuccess ? Date.now() - latestSuccess.checkedAt : undefined;
@@ -29839,6 +30478,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
29839
30478
  total: handoffs.total
29840
30479
  },
29841
30480
  liveLatency,
30481
+ incidentRecoveryOutcomes: incidentRecoveryOutcomeSummary,
29842
30482
  mediaPipeline: mediaPipelineSummary,
29843
30483
  monitoring: monitoringSummary,
29844
30484
  monitoringNotifierDelivery: monitoringNotifierDeliverySummary,
@@ -29893,25 +30533,25 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
29893
30533
  var buildVoiceProductionReadinessGate = async (options, input = {}) => summarizeVoiceProductionReadinessGate(await buildVoiceProductionReadinessReport(options, input), options.gate || undefined);
29894
30534
  var renderVoiceProductionReadinessHTML = (report, options = {}) => {
29895
30535
  const title = options.title ?? "AbsoluteJS Voice Production Readiness";
29896
- 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>` : "";
29897
- 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>` : "";
30536
+ 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>` : "";
30537
+ 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>` : "";
29898
30538
  const checks = report.checks.map((check, index) => {
29899
- 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("");
29900
- 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>` : "";
29901
- return `<article class="check ${escapeHtml41(check.status)}">
30539
+ 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("");
30540
+ 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>` : "";
30541
+ return `<article class="check ${escapeHtml42(check.status)}">
29902
30542
  <div>
29903
- <span>${escapeHtml41(check.status.toUpperCase())}</span>
29904
- <h2>${escapeHtml41(check.label)}</h2>
29905
- ${check.detail ? `<p>${escapeHtml41(check.detail)}</p>` : ""}
30543
+ <span>${escapeHtml42(check.status.toUpperCase())}</span>
30544
+ <h2>${escapeHtml42(check.label)}</h2>
30545
+ ${check.detail ? `<p>${escapeHtml42(check.detail)}</p>` : ""}
29906
30546
  ${explanation}
29907
- ${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>` : ""}
30547
+ ${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>` : ""}
29908
30548
  ${actions ? `<p class="actions">${actions}</p>` : ""}
29909
30549
  </div>
29910
- <strong>${escapeHtml41(String(check.value ?? check.status))}</strong>
29911
- ${check.href ? `<a href="${escapeHtml41(check.href)}">Open surface</a>` : ""}
30550
+ <strong>${escapeHtml42(String(check.value ?? check.status))}</strong>
30551
+ ${check.href ? `<a href="${escapeHtml42(check.href)}">Open surface</a>` : ""}
29912
30552
  </article>`;
29913
30553
  }).join("");
29914
- const snippet = escapeHtml41(`createVoiceProductionReadinessRoutes({
30554
+ const snippet = escapeHtml42(`createVoiceProductionReadinessRoutes({
29915
30555
  htmlPath: '/production-readiness',
29916
30556
  path: '/api/production-readiness',
29917
30557
  gatePath: '/api/production-readiness/gate',
@@ -29927,13 +30567,13 @@ var renderVoiceProductionReadinessHTML = (report, options = {}) => {
29927
30567
  providerRoutingContracts: loadProviderRoutingContracts,
29928
30568
  store: traceStore
29929
30569
  });`);
29930
- 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>`;
30570
+ 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>`;
29931
30571
  };
29932
30572
  var createVoiceProductionReadinessRoutes = (options) => {
29933
30573
  const path = options.path ?? "/api/production-readiness";
29934
30574
  const gatePath = options.gatePath === undefined ? "/api/production-readiness/gate" : options.gatePath;
29935
30575
  const htmlPath = options.htmlPath ?? "/production-readiness";
29936
- const routes = new Elysia44({
30576
+ const routes = new Elysia45({
29937
30577
  name: options.name ?? "absolutejs-voice-production-readiness"
29938
30578
  });
29939
30579
  let cachedReport;
@@ -29956,541 +30596,141 @@ var createVoiceProductionReadinessRoutes = (options) => {
29956
30596
  if (cacheMs > 0 && cachedReport && cachedReport.key === key && Date.now() - cachedReport.loadedAt <= cacheMs) {
29957
30597
  return cachedReport.value;
29958
30598
  }
29959
- const value = (async () => {
29960
- const resolvedOptions = await resolveOptions({ query, request });
29961
- return {
29962
- report: await buildVoiceProductionReadinessReport(resolvedOptions, {
29963
- query,
29964
- request
29965
- }),
29966
- resolvedOptions
29967
- };
29968
- })();
29969
- if (cacheMs > 0) {
29970
- cachedReport = {
29971
- key,
29972
- loadedAt: Date.now(),
29973
- value
29974
- };
29975
- }
29976
- return value;
29977
- };
29978
- routes.get(path, async ({ query, request }) => (await getReport(query, request)).report);
29979
- if (gatePath !== false) {
29980
- routes.get(gatePath, async ({ query, request }) => {
29981
- const { report, resolvedOptions } = await getReport(query, request);
29982
- const gate = summarizeVoiceProductionReadinessGate(report, resolvedOptions.gate || undefined);
29983
- return new Response(JSON.stringify(gate), {
29984
- headers: {
29985
- "Content-Type": "application/json; charset=utf-8",
29986
- ...resolvedOptions.headers
29987
- },
29988
- status: gate.ok ? 200 : 503
29989
- });
29990
- });
29991
- }
29992
- if (htmlPath !== false) {
29993
- routes.get(htmlPath, async ({ query, request }) => {
29994
- const { report, resolvedOptions } = await getReport(query, request);
29995
- const body = await (resolvedOptions.render ?? renderVoiceProductionReadinessHTML)(report);
29996
- return new Response(body, {
29997
- headers: {
29998
- "Content-Type": "text/html; charset=utf-8",
29999
- ...resolvedOptions.headers
30000
- }
30001
- });
30002
- });
30003
- }
30004
- return routes;
30005
- };
30006
-
30007
- // src/operationalStatus.ts
30008
- var escapeHtml42 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
30009
- var resolveValue = async (value) => typeof value === "function" ? await value() : value;
30010
- var isDeliveryRuntime = (value) => Boolean(value && typeof value === "object" && "isRunning" in value && "summarize" in value);
30011
- var worstStatus2 = (statuses) => statuses.includes("fail") ? "fail" : statuses.includes("warn") ? "warn" : "pass";
30012
- var proofPackStatusToCheck = (status, href) => {
30013
- const checkStatus = status.state === "failed" || status.state === "missing" ? "fail" : status.state === "fresh" ? "pass" : "warn";
30014
- const age = typeof status.ageMs === "number" ? `${Math.round(status.ageMs / 1000)}s old` : undefined;
30015
- return {
30016
- detail: status.error ?? `Proof pack is ${status.state}.`,
30017
- href,
30018
- label: "Proof pack freshness",
30019
- status: checkStatus,
30020
- value: age ?? status.state
30021
- };
30022
- };
30023
- var deliveryRuntimeStatusToCheck = (report, href) => {
30024
- const summaries = [report.summary.audit, report.summary.trace].filter(Boolean);
30025
- const failed = summaries.reduce((total, summary) => total + (summary?.failed ?? 0) + (summary?.deadLettered ?? 0), 0);
30026
- const pending = summaries.reduce((total, summary) => total + (summary?.pending ?? 0), 0);
30027
- const status = failed > 0 ? "fail" : pending > 0 || !report.isRunning ? "warn" : "pass";
30028
- return {
30029
- 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.",
30030
- href,
30031
- label: "Delivery runtime",
30032
- status,
30033
- value: `${pending} pending / ${failed} failed`
30034
- };
30035
- };
30036
- var productionReadinessStatusToCheck = (report, href) => {
30037
- const gate = summarizeVoiceProductionReadinessGate(report);
30038
- return {
30039
- detail: gate.ok ? "Production readiness gate is open." : `${gate.failures.length} failures and ${gate.warnings.length} warnings.`,
30040
- href,
30041
- label: "Production readiness",
30042
- status: gate.ok ? report.status : "fail",
30043
- value: `${gate.failures.length} failures / ${gate.warnings.length} warnings`
30044
- };
30045
- };
30046
- var buildVoiceOperationalStatusReport = async (options) => {
30047
- const [proofPack, deliveryRuntimeReport, productionReadiness] = await Promise.all([
30048
- resolveValue(options.proofPack),
30049
- isDeliveryRuntime(options.deliveryRuntime) ? buildVoiceDeliveryRuntimeReport(options.deliveryRuntime) : resolveValue(options.deliveryRuntime),
30050
- resolveValue(options.productionReadiness)
30051
- ]);
30052
- const checks = [];
30053
- if (proofPack) {
30054
- checks.push(proofPackStatusToCheck(proofPack, options.links?.proofPack));
30055
- }
30056
- if (deliveryRuntimeReport) {
30057
- checks.push(deliveryRuntimeStatusToCheck(deliveryRuntimeReport, options.links?.deliveryRuntime));
30058
- }
30059
- if (productionReadiness) {
30060
- checks.push(productionReadinessStatusToCheck(productionReadiness, options.links?.productionReadiness));
30061
- }
30062
- const summary = {
30063
- fail: checks.filter((check) => check.status === "fail").length,
30064
- pass: checks.filter((check) => check.status === "pass").length,
30065
- total: checks.length,
30066
- warn: checks.filter((check) => check.status === "warn").length
30067
- };
30068
- return {
30069
- checkedAt: Date.now(),
30070
- checks,
30071
- links: options.links ?? {},
30072
- status: worstStatus2(checks.map((check) => check.status)),
30073
- summary
30074
- };
30075
- };
30076
- var renderVoiceOperationalStatusHTML = (report, options = {}) => {
30077
- const title = options.title ?? "AbsoluteJS Voice Operational Status";
30078
- const checks = report.checks.map((check) => `<article class="${escapeHtml42(check.status)}">
30079
- <span>${escapeHtml42(check.status.toUpperCase())}</span>
30080
- <h2>${escapeHtml42(check.label)}</h2>
30081
- <strong>${escapeHtml42(String(check.value ?? check.status))}</strong>
30082
- ${check.detail ? `<p>${escapeHtml42(check.detail)}</p>` : ""}
30083
- ${check.href ? `<a href="${escapeHtml42(check.href)}">Open surface</a>` : ""}
30084
- </article>`).join("");
30085
- 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>`;
30086
- };
30087
- var createVoiceOperationalStatusRoutes = (options) => {
30088
- const path = options.path ?? "/api/voice/operational-status";
30089
- const htmlPath = options.htmlPath === undefined ? "/voice/operational-status" : options.htmlPath;
30090
- const routes = new Elysia45({
30091
- name: options.name ?? "absolutejs-voice-operational-status"
30092
- }).get(path, async () => {
30093
- const report = await buildVoiceOperationalStatusReport(options);
30094
- return new Response(JSON.stringify(report), {
30095
- headers: {
30096
- "Content-Type": "application/json; charset=utf-8",
30097
- ...options.headers
30098
- },
30099
- status: report.status === "fail" ? 503 : 200
30100
- });
30101
- });
30102
- if (htmlPath !== false) {
30103
- routes.get(htmlPath, async () => {
30104
- const report = await buildVoiceOperationalStatusReport(options);
30105
- const body = await (options.render ?? ((input) => renderVoiceOperationalStatusHTML(input, { title: options.title })))(report);
30106
- return new Response(body, {
30107
- headers: {
30108
- "Content-Type": "text/html; charset=utf-8",
30109
- ...options.headers
30110
- }
30111
- });
30112
- });
30113
- }
30114
- return routes;
30115
- };
30116
- // src/incidentTimeline.ts
30117
- import { Elysia as Elysia46 } from "elysia";
30118
- var escapeHtml43 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
30119
- var resolveValue2 = async (value) => typeof value === "function" ? await value() : value;
30120
- var linkForSession = (link, sessionId) => {
30121
- if (!link || !sessionId) {
30122
- return;
30123
- }
30124
- return typeof link === "function" ? link(sessionId) : link;
30125
- };
30126
- var statusToSeverity = (status) => status === "fail" || status === "failed" ? "critical" : status === "warn" || status === "warning" || status === "recovered" ? "warn" : "info";
30127
- var failureReplayStatusToSeverity = (status) => status === "failed" ? "critical" : status === "healthy" ? "info" : "warn";
30128
- var withinWindow = (event, now, windowMs) => !windowMs || event.at >= now - windowMs;
30129
- var eventStatus2 = (event) => event.severity === "critical" ? "fail" : event.severity === "warn" ? "warn" : "pass";
30130
- var defaultIncidentRecoveryActions = (events, links) => {
30131
- const actions = [];
30132
- const add = (action) => {
30133
- const key = `${action.id}:${action.sessionId ?? ""}:${action.href ?? ""}`;
30134
- if (actions.some((existing) => `${existing.id}:${existing.sessionId ?? ""}:${existing.href ?? ""}` === key)) {
30135
- return;
30136
- }
30137
- actions.push(action);
30138
- };
30139
- for (const event of events) {
30140
- if (event.category === "delivery") {
30141
- add({
30142
- detail: "Ask the app to tick delivery workers or retry failed delivery queue work.",
30143
- eventId: event.id,
30144
- href: links.deliveryRuntime,
30145
- id: "delivery.retry",
30146
- label: "Retry delivery work",
30147
- method: "POST",
30148
- sessionId: event.sessionId
30149
- });
30150
- }
30151
- if (event.category === "readiness" || event.category === "operational-status") {
30152
- add({
30153
- detail: "Refresh production readiness and proof freshness before declaring the incident resolved.",
30154
- eventId: event.id,
30155
- href: links.productionReadiness ?? links.operationalStatus,
30156
- id: "readiness.refresh",
30157
- label: "Refresh readiness proof",
30158
- method: "POST",
30159
- sessionId: event.sessionId
30160
- });
30599
+ const value = (async () => {
30600
+ const resolvedOptions = await resolveOptions({ query, request });
30601
+ return {
30602
+ report: await buildVoiceProductionReadinessReport(resolvedOptions, {
30603
+ query,
30604
+ request
30605
+ }),
30606
+ resolvedOptions
30607
+ };
30608
+ })();
30609
+ if (cacheMs > 0) {
30610
+ cachedReport = {
30611
+ key,
30612
+ loadedAt: Date.now(),
30613
+ value
30614
+ };
30161
30615
  }
30162
- if (event.sessionId) {
30163
- add({
30164
- detail: "Generate or open a support/debug artifact for the affected call.",
30165
- eventId: event.id,
30166
- href: linkForSession(links.supportBundle, event.sessionId) ?? linkForSession(links.callDebugger, event.sessionId),
30167
- id: "support.bundle",
30168
- label: "Generate support bundle",
30169
- method: "POST",
30170
- sessionId: event.sessionId
30616
+ return value;
30617
+ };
30618
+ routes.get(path, async ({ query, request }) => (await getReport(query, request)).report);
30619
+ if (gatePath !== false) {
30620
+ routes.get(gatePath, async ({ query, request }) => {
30621
+ const { report, resolvedOptions } = await getReport(query, request);
30622
+ const gate = summarizeVoiceProductionReadinessGate(report, resolvedOptions.gate || undefined);
30623
+ return new Response(JSON.stringify(gate), {
30624
+ headers: {
30625
+ "Content-Type": "application/json; charset=utf-8",
30626
+ ...resolvedOptions.headers
30627
+ },
30628
+ status: gate.ok ? 200 : 503
30171
30629
  });
30172
- }
30630
+ });
30173
30631
  }
30174
- if (events.some((event) => event.severity !== "info")) {
30175
- add({
30176
- detail: "Rerun the app proof pack to confirm the current release evidence is fresh.",
30177
- href: links.proofPack,
30178
- id: "proof.rerun",
30179
- label: "Rerun proof pack",
30180
- method: "POST"
30632
+ if (htmlPath !== false) {
30633
+ routes.get(htmlPath, async ({ query, request }) => {
30634
+ const { report, resolvedOptions } = await getReport(query, request);
30635
+ const body = await (resolvedOptions.render ?? renderVoiceProductionReadinessHTML)(report);
30636
+ return new Response(body, {
30637
+ headers: {
30638
+ "Content-Type": "text/html; charset=utf-8",
30639
+ ...resolvedOptions.headers
30640
+ }
30641
+ });
30181
30642
  });
30182
30643
  }
30183
- return actions;
30644
+ return routes;
30184
30645
  };
30646
+
30647
+ // src/operationalStatus.ts
30648
+ var escapeHtml43 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
30649
+ var resolveValue2 = async (value) => typeof value === "function" ? await value() : value;
30650
+ var isDeliveryRuntime = (value) => Boolean(value && typeof value === "object" && "isRunning" in value && "summarize" in value);
30185
30651
  var worstStatus3 = (statuses) => statuses.includes("fail") ? "fail" : statuses.includes("warn") ? "warn" : "pass";
30186
- var statusRank6 = (status) => status === "fail" ? 3 : status === "warn" ? 2 : status === "pass" ? 1 : 0;
30187
- var isRecord4 = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
30188
- var getIncidentRecoveryBody = (event) => {
30189
- const payload = isRecord4(event.payload) ? event.payload : {};
30190
- return isRecord4(payload.body) ? payload.body : {};
30191
- };
30192
- var getIncidentRecoveryStatus = (value) => value === "fail" || value === "pass" || value === "warn" ? value : undefined;
30193
- var getIncidentRecoveryDetail = (event) => {
30194
- const payload = isRecord4(event.payload) ? event.payload : {};
30195
- const body = getIncidentRecoveryBody(event);
30196
- const result = isRecord4(body.result) ? body.result : {};
30197
- const detail = result.detail ?? payload.error;
30198
- return typeof detail === "string" ? detail : undefined;
30199
- };
30200
- var toIncidentRecoveryOutcomeEntry = (event) => {
30201
- const body = getIncidentRecoveryBody(event);
30202
- const beforeStatus = getIncidentRecoveryStatus(body.beforeStatus);
30203
- const afterStatus = getIncidentRecoveryStatus(body.afterStatus);
30204
- const beforeRank = statusRank6(beforeStatus);
30205
- const afterRank = statusRank6(afterStatus);
30206
- const outcome = event.outcome === "error" ? "failed" : beforeRank > 0 && afterRank > 0 && afterRank < beforeRank ? "improved" : beforeRank > 0 && afterRank > beforeRank ? "regressed" : "unchanged";
30207
- const payload = isRecord4(event.payload) ? event.payload : {};
30652
+ var proofPackStatusToCheck = (status, href) => {
30653
+ const checkStatus = status.state === "failed" || status.state === "missing" ? "fail" : status.state === "fresh" ? "pass" : "warn";
30654
+ const age = typeof status.ageMs === "number" ? `${Math.round(status.ageMs / 1000)}s old` : undefined;
30208
30655
  return {
30209
- actionId: event.action.replace(/^incident\./, ""),
30210
- afterStatus,
30211
- at: event.at,
30212
- beforeStatus,
30213
- detail: getIncidentRecoveryDetail(event),
30214
- eventId: event.id,
30215
- outcome,
30216
- status: typeof payload.status === "number" ? payload.status : undefined,
30217
- traceId: event.traceId
30656
+ detail: status.error ?? `Proof pack is ${status.state}.`,
30657
+ href,
30658
+ label: "Proof pack freshness",
30659
+ status: checkStatus,
30660
+ value: age ?? status.state
30218
30661
  };
30219
30662
  };
30220
- var buildVoiceIncidentRecoveryOutcomeReport = async (options) => {
30221
- const events = options.audit ? await options.audit.list({
30222
- limit: options.limit ?? 50,
30223
- resourceType: "voice.ops.action",
30224
- type: "operator.action"
30225
- }) : [];
30226
- const entries = events.filter((event) => event.action.startsWith("incident.")).map(toIncidentRecoveryOutcomeEntry).sort((left, right) => right.at - left.at);
30663
+ var deliveryRuntimeStatusToCheck = (report, href) => {
30664
+ const summaries = [report.summary.audit, report.summary.trace].filter(Boolean);
30665
+ const failed = summaries.reduce((total, summary) => total + (summary?.failed ?? 0) + (summary?.deadLettered ?? 0), 0);
30666
+ const pending = summaries.reduce((total, summary) => total + (summary?.pending ?? 0), 0);
30667
+ const status = failed > 0 ? "fail" : pending > 0 || !report.isRunning ? "warn" : "pass";
30227
30668
  return {
30228
- checkedAt: Date.now(),
30229
- entries,
30230
- failed: entries.filter((entry) => entry.outcome === "failed").length,
30231
- improved: entries.filter((entry) => entry.outcome === "improved").length,
30232
- regressed: entries.filter((entry) => entry.outcome === "regressed").length,
30233
- total: entries.length,
30234
- unchanged: entries.filter((entry) => entry.outcome === "unchanged").length
30669
+ 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.",
30670
+ href,
30671
+ label: "Delivery runtime",
30672
+ status,
30673
+ value: `${pending} pending / ${failed} failed`
30235
30674
  };
30236
30675
  };
30237
- var renderVoiceIncidentRecoveryOutcomeHTML = (report, options = {}) => {
30238
- const title = options.title ?? "AbsoluteJS Voice Incident Recovery Outcomes";
30239
- 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("");
30240
- 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>`;
30241
- };
30242
- var pushOperationalStatusEvents = (events, report, links) => {
30243
- if (!report) {
30244
- return;
30245
- }
30246
- for (const check of report.checks) {
30247
- if (check.status === "pass") {
30248
- continue;
30249
- }
30250
- events.push({
30251
- action: {
30252
- href: check.href ?? links.operationalStatus,
30253
- label: "Open source"
30254
- },
30255
- at: report.checkedAt,
30256
- category: check.label.toLowerCase().includes("readiness") ? "readiness" : "operational-status",
30257
- detail: check.detail,
30258
- href: check.href ?? links.operationalStatus,
30259
- id: `operational:${check.label}`,
30260
- label: check.label,
30261
- severity: statusToSeverity(check.status),
30262
- source: "operational-status",
30263
- value: check.value
30264
- });
30265
- }
30266
- };
30267
- var pushOpsRecoveryEvents = (events, report, links) => {
30268
- if (!report) {
30269
- return;
30270
- }
30271
- for (const issue of report.issues) {
30272
- events.push({
30273
- action: {
30274
- href: issue.href ?? links.operationalStatus,
30275
- label: "Inspect recovery issue"
30276
- },
30277
- at: report.checkedAt,
30278
- category: "recovery",
30279
- detail: issue.detail,
30280
- href: issue.href,
30281
- id: `ops-recovery:${issue.code}`,
30282
- label: issue.label,
30283
- severity: issue.severity === "fail" ? "critical" : "warn",
30284
- source: "ops-recovery",
30285
- value: issue.value
30286
- });
30287
- }
30288
- for (const session of report.failedSessions) {
30289
- events.push({
30290
- action: {
30291
- href: session.operationsRecordHref ?? linkForSession(links.operationsRecords, session.sessionId) ?? linkForSession(links.callDebugger, session.sessionId),
30292
- label: "Open affected call"
30293
- },
30294
- at: session.at,
30295
- category: "call",
30296
- detail: session.error,
30297
- href: session.operationsRecordHref ?? linkForSession(links.operationsRecords, session.sessionId),
30298
- id: `failed-session:${session.sessionId}:${session.at}`,
30299
- label: "Failed session",
30300
- sessionId: session.sessionId,
30301
- severity: "critical",
30302
- source: "ops-recovery",
30303
- value: session.provider
30304
- });
30305
- }
30306
- };
30307
- var pushMonitorEvents = (events, issues, links) => {
30308
- if (!issues) {
30309
- return;
30310
- }
30311
- for (const issue of issues) {
30312
- if (issue.status === "resolved") {
30313
- continue;
30314
- }
30315
- const sessionId = issue.impactedSessions[0];
30316
- events.push({
30317
- action: {
30318
- href: issue.operationsRecordHrefs[0] ?? linkForSession(links.operationsRecords, sessionId) ?? links.monitorIssues,
30319
- label: "Open monitor evidence"
30320
- },
30321
- at: issue.lastSeenAt,
30322
- category: "monitor",
30323
- detail: issue.detail,
30324
- href: issue.operationsRecordHrefs[0] ?? linkForSession(links.operationsRecords, sessionId) ?? links.monitorIssues,
30325
- id: `monitor:${issue.id}`,
30326
- label: issue.label,
30327
- sessionId,
30328
- severity: issue.severity === "critical" ? "critical" : issue.severity === "warn" ? "warn" : "info",
30329
- source: `monitor:${issue.monitorId}`,
30330
- value: issue.value
30331
- });
30332
- }
30676
+ var productionReadinessStatusToCheck = (report, href) => {
30677
+ const gate = summarizeVoiceProductionReadinessGate(report);
30678
+ return {
30679
+ detail: gate.ok ? "Production readiness gate is open." : `${gate.failures.length} failures and ${gate.warnings.length} warnings.`,
30680
+ href,
30681
+ label: "Production readiness",
30682
+ status: gate.ok ? report.status : "fail",
30683
+ value: `${gate.failures.length} failures / ${gate.warnings.length} warnings`
30684
+ };
30333
30685
  };
30334
- var pushOperationsRecordEvents = (events, records, links) => {
30335
- if (!records) {
30336
- return;
30337
- }
30338
- for (const record of records) {
30339
- if (record.status === "healthy") {
30340
- continue;
30341
- }
30342
- const href = linkForSession(links.operationsRecords, record.sessionId);
30343
- const debuggerHref = linkForSession(links.callDebugger, record.sessionId);
30344
- events.push({
30345
- action: {
30346
- href: debuggerHref ?? href,
30347
- label: debuggerHref ? "Open call debugger" : "Open operations record"
30348
- },
30349
- at: record.checkedAt,
30350
- category: "call",
30351
- detail: record.status === "failed" ? "Call operations record failed." : "Call operations record has warnings.",
30352
- href,
30353
- id: `operations-record:${record.sessionId}`,
30354
- label: `Operations record ${record.status}`,
30355
- sessionId: record.sessionId,
30356
- severity: statusToSeverity(record.status),
30357
- source: "operations-record",
30358
- value: record.outcome.complete ? "complete" : "incomplete"
30359
- });
30686
+ var buildVoiceOperationalStatusReport = async (options) => {
30687
+ const [proofPack, deliveryRuntimeReport, productionReadiness] = await Promise.all([
30688
+ resolveValue2(options.proofPack),
30689
+ isDeliveryRuntime(options.deliveryRuntime) ? buildVoiceDeliveryRuntimeReport(options.deliveryRuntime) : resolveValue2(options.deliveryRuntime),
30690
+ resolveValue2(options.productionReadiness)
30691
+ ]);
30692
+ const checks = [];
30693
+ if (proofPack) {
30694
+ checks.push(proofPackStatusToCheck(proofPack, options.links?.proofPack));
30360
30695
  }
30361
- };
30362
- var pushFailureReplayEvents = (events, replays, links) => {
30363
- if (!replays) {
30364
- return;
30696
+ if (deliveryRuntimeReport) {
30697
+ checks.push(deliveryRuntimeStatusToCheck(deliveryRuntimeReport, options.links?.deliveryRuntime));
30365
30698
  }
30366
- for (const replay of replays) {
30367
- if (replay.status === "healthy") {
30368
- continue;
30369
- }
30370
- const href = replay.operationsRecordHref ?? linkForSession(links.failureReplay, replay.sessionId) ?? linkForSession(links.callDebugger, replay.sessionId);
30371
- events.push({
30372
- action: {
30373
- href: linkForSession(links.callDebugger, replay.sessionId) ?? href ?? linkForSession(links.supportBundle, replay.sessionId),
30374
- label: "Open replay/debug artifact"
30375
- },
30376
- at: replay.providers.steps[0]?.at ?? replay.media.steps[0]?.at ?? Date.now(),
30377
- category: "failure-replay",
30378
- detail: replay.summary.issues.join("; ") || replay.summary.userHeard.join(" ") || `Failure replay is ${replay.status}.`,
30379
- href,
30380
- id: `failure-replay:${replay.sessionId}`,
30381
- label: `Failure replay ${replay.status}`,
30382
- sessionId: replay.sessionId,
30383
- severity: failureReplayStatusToSeverity(replay.status),
30384
- source: "failure-replay",
30385
- value: `${replay.providers.errors} provider errors / ${replay.media.errors} media errors`
30386
- });
30699
+ if (productionReadiness) {
30700
+ checks.push(productionReadinessStatusToCheck(productionReadiness, options.links?.productionReadiness));
30387
30701
  }
30388
- };
30389
- var buildVoiceIncidentTimelineReport = async (options) => {
30390
- const now = options.now ?? Date.now();
30391
- const links = options.links ?? {};
30392
- const [
30393
- operationalStatus,
30394
- opsRecovery,
30395
- monitorIssues,
30396
- operationsRecords,
30397
- failureReplays
30398
- ] = await Promise.all([
30399
- resolveValue2(options.operationalStatus),
30400
- resolveValue2(options.opsRecovery),
30401
- resolveValue2(options.monitorIssues),
30402
- resolveValue2(options.operationsRecords),
30403
- resolveValue2(options.failureReplays)
30404
- ]);
30405
- const events = [];
30406
- pushOperationalStatusEvents(events, operationalStatus, links);
30407
- pushOpsRecoveryEvents(events, opsRecovery, links);
30408
- pushMonitorEvents(events, monitorIssues, links);
30409
- pushOperationsRecordEvents(events, operationsRecords, links);
30410
- pushFailureReplayEvents(events, failureReplays, links);
30411
- const filtered = events.filter((event) => withinWindow(event, now, options.windowMs)).sort((left, right) => right.at - left.at).slice(0, options.limit ?? 50);
30412
30702
  const summary = {
30413
- critical: filtered.filter((event) => event.severity === "critical").length,
30414
- info: filtered.filter((event) => event.severity === "info").length,
30415
- total: filtered.length,
30416
- warn: filtered.filter((event) => event.severity === "warn").length
30417
- };
30418
- const baseReport = {
30419
- events: filtered,
30420
- generatedAt: now,
30421
- links,
30422
- status: worstStatus3(filtered.map(eventStatus2)),
30423
- summary,
30424
- windowMs: options.windowMs
30703
+ fail: checks.filter((check) => check.status === "fail").length,
30704
+ pass: checks.filter((check) => check.status === "pass").length,
30705
+ total: checks.length,
30706
+ warn: checks.filter((check) => check.status === "warn").length
30425
30707
  };
30426
- const configuredActions = typeof options.recoveryActions === "function" ? await options.recoveryActions({
30427
- events: filtered,
30428
- report: baseReport
30429
- }) : options.recoveryActions;
30430
30708
  return {
30431
- ...baseReport,
30432
- actions: configuredActions === undefined ? defaultIncidentRecoveryActions(filtered, links) : [...configuredActions]
30709
+ checkedAt: Date.now(),
30710
+ checks,
30711
+ links: options.links ?? {},
30712
+ status: worstStatus3(checks.map((check) => check.status)),
30713
+ summary
30433
30714
  };
30434
30715
  };
30435
- var renderVoiceIncidentTimelineMarkdown = (report, options = {}) => {
30436
- const title = options.title ?? "AbsoluteJS Voice Incident Timeline";
30437
- const rows = report.events.map((event) => {
30438
- const when = new Date(event.at).toISOString();
30439
- const target = event.href ? ` [open](${event.href})` : "";
30440
- const session = event.sessionId ? ` session=${event.sessionId}` : "";
30441
- const value = event.value === undefined ? "" : ` value=${event.value}`;
30442
- return `- ${when} ${event.severity.toUpperCase()} ${event.label}${session}${value}${target}${event.detail ? ` - ${event.detail}` : ""}`;
30443
- }).join(`
30444
- `);
30445
- return `# ${title}
30446
-
30447
- Status: ${report.status}
30448
-
30449
- Generated: ${new Date(report.generatedAt).toISOString()}
30450
-
30451
- Summary: ${report.summary.critical} critical, ${report.summary.warn} warn, ${report.summary.info} info, ${report.summary.total} total.
30452
-
30453
- ## Events
30454
-
30455
- ${rows || "- No incident timeline events."}
30456
-
30457
- ## Recovery Actions
30458
-
30459
- ${report.actions.map((action) => `- ${action.method ?? "GET"} ${action.id}: ${action.label}${action.href ? ` (${action.href})` : ""}${action.detail ? ` - ${action.detail}` : ""}`).join(`
30460
- `) || "- No recovery actions."}
30461
- `;
30462
- };
30463
- var renderVoiceIncidentTimelineHTML = (report, options = {}) => {
30464
- const title = options.title ?? "AbsoluteJS Voice Incident Timeline";
30465
- const actionPath = options.actionPath ?? "/api/voice/incident-timeline/actions";
30466
- const events = report.events.map((event) => `<article class="${escapeHtml43(event.severity)}">
30467
- <span>${escapeHtml43(event.severity.toUpperCase())} / ${escapeHtml43(event.category)}</span>
30468
- <h2>${escapeHtml43(event.label)}</h2>
30469
- <p>${escapeHtml43(new Date(event.at).toLocaleString())}${event.sessionId ? ` \xB7 session ${escapeHtml43(event.sessionId)}` : ""}</p>
30470
- ${event.value === undefined ? "" : `<strong>${escapeHtml43(String(event.value))}</strong>`}
30471
- ${event.detail ? `<p>${escapeHtml43(event.detail)}</p>` : ""}
30472
- <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>
30716
+ var renderVoiceOperationalStatusHTML = (report, options = {}) => {
30717
+ const title = options.title ?? "AbsoluteJS Voice Operational Status";
30718
+ const checks = report.checks.map((check) => `<article class="${escapeHtml43(check.status)}">
30719
+ <span>${escapeHtml43(check.status.toUpperCase())}</span>
30720
+ <h2>${escapeHtml43(check.label)}</h2>
30721
+ <strong>${escapeHtml43(String(check.value ?? check.status))}</strong>
30722
+ ${check.detail ? `<p>${escapeHtml43(check.detail)}</p>` : ""}
30723
+ ${check.href ? `<a href="${escapeHtml43(check.href)}">Open surface</a>` : ""}
30473
30724
  </article>`).join("");
30474
- const actions = report.actions.map((action) => {
30475
- const label = escapeHtml43(action.label);
30476
- const detail = action.detail ? `<p>${escapeHtml43(action.detail)}</p>` : "";
30477
- const href = action.href ? `<a href="${escapeHtml43(action.href)}">Open target</a>` : "";
30478
- const control = action.method === "POST" ? `<button type="button" data-voice-incident-action="${escapeHtml43(action.id)}" ${action.disabled ? "disabled" : ""}>${label}</button>` : href;
30479
- return `<article class="action"><span>${escapeHtml43(action.method ?? "GET")}</span><h2>${label}</h2>${detail}<div>${control}${href && action.method === "POST" ? href : ""}</div></article>`;
30480
- }).join("");
30481
- 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>`;
30725
+ 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>`;
30482
30726
  };
30483
- var createVoiceIncidentTimelineRoutes = (options) => {
30484
- const path = options.path ?? "/api/voice/incident-timeline";
30485
- const htmlPath = options.htmlPath === undefined ? "/voice/incident-timeline" : options.htmlPath;
30486
- const markdownPath = options.markdownPath === undefined ? "/voice/incident-timeline.md" : options.markdownPath;
30487
- const actionPath = options.actionPath === undefined ? "/api/voice/incident-timeline/actions" : options.actionPath;
30488
- const recoveryOutcomePath = options.recoveryOutcomePath === undefined ? "/api/voice/incident-timeline/recovery-outcomes" : options.recoveryOutcomePath;
30489
- const recoveryOutcomeHtmlPath = options.recoveryOutcomeHtmlPath === undefined ? "/voice/incident-recovery-outcomes" : options.recoveryOutcomeHtmlPath;
30727
+ var createVoiceOperationalStatusRoutes = (options) => {
30728
+ const path = options.path ?? "/api/voice/operational-status";
30729
+ const htmlPath = options.htmlPath === undefined ? "/voice/operational-status" : options.htmlPath;
30490
30730
  const routes = new Elysia46({
30491
- name: options.name ?? "absolutejs-voice-incident-timeline"
30731
+ name: options.name ?? "absolutejs-voice-operational-status"
30492
30732
  }).get(path, async () => {
30493
- const report = await buildVoiceIncidentTimelineReport(options);
30733
+ const report = await buildVoiceOperationalStatusReport(options);
30494
30734
  return new Response(JSON.stringify(report), {
30495
30735
  headers: {
30496
30736
  "Content-Type": "application/json; charset=utf-8",
@@ -30501,11 +30741,8 @@ var createVoiceIncidentTimelineRoutes = (options) => {
30501
30741
  });
30502
30742
  if (htmlPath !== false) {
30503
30743
  routes.get(htmlPath, async () => {
30504
- const report = await buildVoiceIncidentTimelineReport(options);
30505
- const body = await (options.render ?? ((input) => renderVoiceIncidentTimelineHTML(input, {
30506
- actionPath: actionPath === false ? undefined : actionPath,
30507
- title: options.title
30508
- })))(report);
30744
+ const report = await buildVoiceOperationalStatusReport(options);
30745
+ const body = await (options.render ?? ((input) => renderVoiceOperationalStatusHTML(input, { title: options.title })))(report);
30509
30746
  return new Response(body, {
30510
30747
  headers: {
30511
30748
  "Content-Type": "text/html; charset=utf-8",
@@ -30514,130 +30751,6 @@ var createVoiceIncidentTimelineRoutes = (options) => {
30514
30751
  });
30515
30752
  });
30516
30753
  }
30517
- if (markdownPath !== false) {
30518
- routes.get(markdownPath, async () => {
30519
- const report = await buildVoiceIncidentTimelineReport(options);
30520
- return new Response(renderVoiceIncidentTimelineMarkdown(report, {
30521
- title: options.title
30522
- }), {
30523
- headers: {
30524
- "Content-Type": "text/markdown; charset=utf-8",
30525
- ...options.headers
30526
- }
30527
- });
30528
- });
30529
- }
30530
- if (actionPath !== false) {
30531
- routes.get(actionPath, async () => {
30532
- const report = await buildVoiceIncidentTimelineReport(options);
30533
- return new Response(JSON.stringify({
30534
- actions: report.actions,
30535
- generatedAt: report.generatedAt,
30536
- status: report.status
30537
- }), {
30538
- headers: {
30539
- "Content-Type": "application/json; charset=utf-8",
30540
- ...options.headers
30541
- }
30542
- });
30543
- }).post(`${actionPath}/:actionId`, async ({ params, request }) => {
30544
- const actionId = params.actionId;
30545
- const report = await buildVoiceIncidentTimelineReport(options);
30546
- const action = report.actions.find((item) => item.id === actionId);
30547
- const handler = options.actionHandlers?.[actionId];
30548
- if (!action) {
30549
- return new Response(JSON.stringify({
30550
- actionId,
30551
- ok: false,
30552
- status: "not_found"
30553
- }), {
30554
- headers: {
30555
- "Content-Type": "application/json; charset=utf-8",
30556
- ...options.headers
30557
- },
30558
- status: 404
30559
- });
30560
- }
30561
- if (action.disabled || action.method !== "POST" || !handler) {
30562
- return new Response(JSON.stringify({
30563
- actionId,
30564
- ok: false,
30565
- status: action.disabled ? "disabled" : "not_executable"
30566
- }), {
30567
- headers: {
30568
- "Content-Type": "application/json; charset=utf-8",
30569
- ...options.headers
30570
- },
30571
- status: 409
30572
- });
30573
- }
30574
- const result = await handler({
30575
- action,
30576
- actionId,
30577
- report,
30578
- request
30579
- });
30580
- const status = result.ok ? 200 : 500;
30581
- const afterReport = await buildVoiceIncidentTimelineReport(options);
30582
- const resultWithStatus = {
30583
- ...result,
30584
- afterStatus: result.afterStatus ?? afterReport.status,
30585
- beforeStatus: result.beforeStatus ?? report.status
30586
- };
30587
- await recordVoiceOpsActionAudit({
30588
- actionId: `incident.${actionId}`,
30589
- body: {
30590
- action,
30591
- afterStatus: resultWithStatus.afterStatus,
30592
- beforeStatus: resultWithStatus.beforeStatus,
30593
- eventIds: report.events.map((event) => event.id),
30594
- result
30595
- },
30596
- error: result.ok ? undefined : result.detail ?? result.status,
30597
- ok: result.ok,
30598
- ranAt: Date.now(),
30599
- status
30600
- }, {
30601
- audit: options.audit,
30602
- trace: options.trace
30603
- });
30604
- return new Response(JSON.stringify(resultWithStatus), {
30605
- headers: {
30606
- "Content-Type": "application/json; charset=utf-8",
30607
- ...options.headers
30608
- },
30609
- status
30610
- });
30611
- });
30612
- }
30613
- if (recoveryOutcomePath !== false) {
30614
- routes.get(recoveryOutcomePath, async () => {
30615
- const report = await buildVoiceIncidentRecoveryOutcomeReport({
30616
- audit: options.audit
30617
- });
30618
- return new Response(JSON.stringify(report), {
30619
- headers: {
30620
- "Content-Type": "application/json; charset=utf-8",
30621
- ...options.headers
30622
- }
30623
- });
30624
- });
30625
- }
30626
- if (recoveryOutcomeHtmlPath !== false) {
30627
- routes.get(recoveryOutcomeHtmlPath, async () => {
30628
- const report = await buildVoiceIncidentRecoveryOutcomeReport({
30629
- audit: options.audit
30630
- });
30631
- return new Response(renderVoiceIncidentRecoveryOutcomeHTML(report, {
30632
- title: `${options.title ?? "AbsoluteJS Voice Incident Timeline"} Recovery Outcomes`
30633
- }), {
30634
- headers: {
30635
- "Content-Type": "text/html; charset=utf-8",
30636
- ...options.headers
30637
- }
30638
- });
30639
- });
30640
- }
30641
30754
  return routes;
30642
30755
  };
30643
30756
  // src/dataControl.ts
@@ -37903,6 +38016,7 @@ var buildSummary = (record) => ({
37903
38016
  turns: record.summary.turnCount
37904
38017
  });
37905
38018
  var renderIncidentMarkdown = (input) => {
38019
+ const recoveryOutcomes = input.recoveryOutcomes;
37906
38020
  const lines = [
37907
38021
  `# ${input.title ?? `Voice Incident ${input.summary.sessionId}`}`,
37908
38022
  "",
@@ -37934,6 +38048,18 @@ var renderIncidentMarkdown = (input) => {
37934
38048
  "",
37935
38049
  ...input.record.tools.length ? input.record.tools.map((tool) => `- ${tool.toolName ?? "tool"} ${tool.status ?? ""} ${tool.elapsedMs === undefined ? "" : `${tool.elapsedMs}ms`} ${tool.error ?? ""}`.trim()) : ["- none"],
37936
38050
  "",
38051
+ "## Recovery Outcomes",
38052
+ "",
38053
+ ...recoveryOutcomes ? [
38054
+ `- Improved: ${recoveryOutcomes.improved}`,
38055
+ `- Unchanged: ${recoveryOutcomes.unchanged}`,
38056
+ `- Regressed: ${recoveryOutcomes.regressed}`,
38057
+ `- Failed: ${recoveryOutcomes.failed}`,
38058
+ `- Total actions: ${recoveryOutcomes.total}`,
38059
+ "",
38060
+ ...recoveryOutcomes.entries.length ? recoveryOutcomes.entries.map((entry) => `- ${entry.outcome}: ${entry.actionId} ${entry.beforeStatus ?? "unknown"} -> ${entry.afterStatus ?? "unknown"}${entry.detail ? ` - ${entry.detail}` : ""}`) : ["- no recovery actions recorded"]
38061
+ ] : ["- no recovery outcome report attached"],
38062
+ "",
37937
38063
  renderVoiceOperationsRecordGuardrailMarkdown(input.record),
37938
38064
  "",
37939
38065
  "## Trace Evidence",
@@ -37983,6 +38109,7 @@ var buildVoiceIncidentBundle = async (options) => {
37983
38109
  traceEvents: redactedTraceEvents
37984
38110
  });
37985
38111
  const summary = buildSummary(redactedRecord);
38112
+ const recoveryOutcomes = options.recoveryOutcomes ? redactRecordValue(options.recoveryOutcomes, redactedTraceEvents, options.redact) : undefined;
37986
38113
  const traceMarkdown = renderVoiceTraceMarkdown(record.traceEvents, {
37987
38114
  evaluation: options.evaluation,
37988
38115
  redact: options.redact,
@@ -37995,6 +38122,7 @@ var buildVoiceIncidentBundle = async (options) => {
37995
38122
  const markdown = renderIncidentMarkdown({
37996
38123
  auditMarkdown,
37997
38124
  record: redactedRecord,
38125
+ recoveryOutcomes,
37998
38126
  summary,
37999
38127
  title: options.title,
38000
38128
  traceMarkdown
@@ -38005,6 +38133,7 @@ var buildVoiceIncidentBundle = async (options) => {
38005
38133
  formatVersion: 1,
38006
38134
  markdown,
38007
38135
  record: redactedRecord,
38136
+ recoveryOutcomes,
38008
38137
  redacted: Boolean(options.redact),
38009
38138
  sessionId: options.sessionId,
38010
38139
  summary,
@@ -41991,6 +42120,7 @@ export {
41991
42120
  buildVoiceLatencySLOGate,
41992
42121
  buildVoiceIncidentTimelineReport,
41993
42122
  buildVoiceIncidentRecoveryOutcomeReport,
42123
+ buildVoiceIncidentRecoveryOutcomeReadinessCheck,
41994
42124
  buildVoiceIncidentBundle,
41995
42125
  buildVoiceIOProviderRouterTraceEvent,
41996
42126
  buildVoiceGuardrailReport,