@absolutejs/voice 0.0.22-beta.88 → 0.0.22-beta.89

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.d.ts CHANGED
@@ -62,7 +62,7 @@ export type { VoiceTurnQualityHTMLHandlerOptions, VoiceTurnQualityItem, VoiceTur
62
62
  export type { VoiceOutcomeContractDefinition, VoiceOutcomeContractHTMLHandlerOptions, VoiceOutcomeContractIssue, VoiceOutcomeContractOptions, VoiceOutcomeContractReport, VoiceOutcomeContractRoutesOptions, VoiceOutcomeContractStatus, VoiceOutcomeContractSuiteReport } from './outcomeContract';
63
63
  export type { VoiceTelephonyOutcomeAction, VoiceTelephonyOutcomeDecision, VoiceTelephonyOutcomePolicy, VoiceTelephonyOutcomeProviderEvent, VoiceTelephonyOutcomeRouteResult, VoiceTelephonyOutcomeStatusDecision, VoiceTelephonyWebhookDecision, VoiceTelephonyWebhookHandlerOptions, VoiceTelephonyWebhookIdempotencyStore, VoiceTelephonyWebhookParseInput, VoiceTelephonyWebhookProvider, VoiceTelephonyWebhookRoutesOptions, VoiceTelephonyWebhookVerificationResult, StoredVoiceTelephonyWebhookDecision } from './telephonyOutcome';
64
64
  export type { VoiceOpsConsoleLink, VoiceOpsConsoleReport, VoiceOpsConsoleRoutesOptions } from './opsConsoleRoutes';
65
- export type { VoiceProductionReadinessCheck, VoiceProductionReadinessReport, VoiceProductionReadinessRoutesOptions, VoiceProductionReadinessStatus } from './productionReadiness';
65
+ export type { VoiceProductionReadinessAction, VoiceProductionReadinessCheck, VoiceProductionReadinessReport, VoiceProductionReadinessRoutesOptions, VoiceProductionReadinessStatus } from './productionReadiness';
66
66
  export type { VoiceQualityLink, VoiceQualityMetric, VoiceQualityReport, VoiceQualityRoutesOptions, VoiceQualityStatus, VoiceQualityThresholds } from './qualityRoutes';
67
67
  export type { VoiceResilienceIOSimulator, VoiceResilienceLink, VoiceResiliencePageData, VoiceResilienceRoutesOptions, VoiceResilienceSimulationProvider, VoiceRoutingKindSummary, VoiceRoutingDecisionSummary, VoiceRoutingDecisionSummaryOptions, VoiceRoutingEvent, VoiceRoutingEventKind, VoiceRoutingSessionSummary, VoiceRoutingSessionSummaryOptions } from './resilienceRoutes';
68
68
  export type { VoiceIOProviderRouterEvent, VoiceIOProviderRouterOptions, VoiceIOProviderRouterPolicy, VoiceIOProviderRouterPolicyConfig, VoiceSTTProviderRouterOptions, VoiceTTSProviderRouterOptions } from './providerAdapters';
package/dist/index.js CHANGED
@@ -9641,35 +9641,82 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
9641
9641
  href: options.links?.quality ?? "/quality",
9642
9642
  label: "Quality gates",
9643
9643
  status: quality.status,
9644
- value: quality.status
9644
+ value: quality.status,
9645
+ actions: quality.status === "pass" ? [] : [
9646
+ {
9647
+ description: "Open the quality report to inspect failing gates.",
9648
+ href: options.links?.quality ?? "/quality",
9649
+ label: "Inspect quality gates"
9650
+ }
9651
+ ]
9645
9652
  },
9646
9653
  {
9647
9654
  detail: degradedProviders === 0 ? "No configured providers are currently degraded." : `${degradedProviders} provider(s) are degraded, suppressed, or rate-limited.`,
9648
9655
  href: options.links?.resilience ?? "/resilience",
9649
9656
  label: "Provider health",
9650
9657
  status: degradedProviders > 0 ? "fail" : "pass",
9651
- value: degradedProviders
9658
+ value: degradedProviders,
9659
+ actions: degradedProviders > 0 ? [
9660
+ {
9661
+ description: "Open provider health, fallback state, and recovery controls.",
9662
+ href: options.links?.resilience ?? "/resilience",
9663
+ label: "Open provider recovery"
9664
+ }
9665
+ ] : []
9652
9666
  },
9653
9667
  {
9654
9668
  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.`,
9655
9669
  href: options.links?.sessions ?? "/sessions",
9656
9670
  label: "Session health",
9657
9671
  status: failedSessions > 0 ? "fail" : sessions.length === 0 ? "warn" : "pass",
9658
- value: `${sessions.length - failedSessions}/${sessions.length}`
9672
+ value: `${sessions.length - failedSessions}/${sessions.length}`,
9673
+ actions: failedSessions > 0 ? [
9674
+ {
9675
+ description: "Open failed sessions and replay traces.",
9676
+ href: `${options.links?.sessions ?? "/sessions"}?status=failed`,
9677
+ label: "Replay failed sessions"
9678
+ }
9679
+ ] : sessions.length === 0 ? [
9680
+ {
9681
+ description: "Open sessions after running a smoke or live call.",
9682
+ href: options.links?.sessions ?? "/sessions",
9683
+ label: "Open sessions"
9684
+ }
9685
+ ] : []
9659
9686
  },
9660
9687
  {
9661
9688
  detail: handoffs.failed === 0 ? "No failed handoff deliveries are recorded." : `${handoffs.failed} handoff delivery failure(s) are recorded.`,
9662
9689
  href: options.links?.handoffs ?? "/handoffs",
9663
9690
  label: "Handoff delivery",
9664
9691
  status: handoffs.failed > 0 ? "fail" : "pass",
9665
- value: `${handoffs.total - handoffs.failed}/${handoffs.total}`
9692
+ value: `${handoffs.total - handoffs.failed}/${handoffs.total}`,
9693
+ actions: handoffs.failed > 0 ? [
9694
+ {
9695
+ description: "Retry queued or failed handoff deliveries.",
9696
+ href: options.links?.handoffRetry ?? "/api/voice-handoffs/retry",
9697
+ label: "Retry handoff deliveries",
9698
+ method: "POST"
9699
+ },
9700
+ {
9701
+ description: "Inspect handoff queue and delivery errors.",
9702
+ href: options.links?.handoffs ?? "/handoffs",
9703
+ label: "Open handoff queue"
9704
+ }
9705
+ ] : []
9666
9706
  },
9667
9707
  {
9668
9708
  detail: routingEvents.length > 0 ? `${routingSessions.length} session(s) have provider routing evidence.` : "No provider routing traces are recorded yet.",
9669
9709
  href: options.links?.resilience ?? "/resilience",
9670
9710
  label: "Routing evidence",
9671
9711
  status: routingEvents.length > 0 ? "pass" : "warn",
9672
- value: routingEvents.length
9712
+ value: routingEvents.length,
9713
+ actions: routingEvents.length > 0 ? [] : [
9714
+ {
9715
+ description: "Open provider routing and run a smoke or simulation to create evidence.",
9716
+ href: options.links?.resilience ?? "/resilience",
9717
+ label: "Open routing evidence"
9718
+ }
9719
+ ]
9673
9720
  }
9674
9721
  ];
9675
9722
  const carrierSummary = carriers ? {
@@ -9685,7 +9732,14 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
9685
9732
  href: options.links?.carriers ?? "/carriers",
9686
9733
  label: "Carrier readiness",
9687
9734
  status: carrierSummary.status,
9688
- value: `${carrierSummary.ready}/${carrierSummary.providers}`
9735
+ value: `${carrierSummary.ready}/${carrierSummary.providers}`,
9736
+ actions: carrierSummary.status === "pass" ? [] : [
9737
+ {
9738
+ description: "Open the carrier matrix for exact missing env, signing, and URL issues.",
9739
+ href: options.links?.carriers ?? "/carriers",
9740
+ label: "Open carrier matrix"
9741
+ }
9742
+ ]
9689
9743
  });
9690
9744
  }
9691
9745
  return {
@@ -9694,6 +9748,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
9694
9748
  links: {
9695
9749
  carriers: "/carriers",
9696
9750
  handoffs: "/handoffs",
9751
+ handoffRetry: "/api/voice-handoffs/retry",
9697
9752
  quality: "/quality",
9698
9753
  resilience: "/resilience",
9699
9754
  sessions: "/sessions",
@@ -9726,16 +9781,20 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
9726
9781
  };
9727
9782
  var renderVoiceProductionReadinessHTML = (report, options = {}) => {
9728
9783
  const title = options.title ?? "AbsoluteJS Voice Production Readiness";
9729
- const checks = report.checks.map((check) => `<article class="check ${escapeHtml15(check.status)}">
9784
+ const checks = report.checks.map((check, index) => {
9785
+ const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${escapeHtml15(action.href)}">${escapeHtml15(action.label)}</button>` : `<a href="${escapeHtml15(action.href)}">${escapeHtml15(action.label)}</a>`).join("");
9786
+ return `<article class="check ${escapeHtml15(check.status)}">
9730
9787
  <div>
9731
9788
  <span>${escapeHtml15(check.status.toUpperCase())}</span>
9732
9789
  <h2>${escapeHtml15(check.label)}</h2>
9733
9790
  ${check.detail ? `<p>${escapeHtml15(check.detail)}</p>` : ""}
9791
+ ${actions ? `<p class="actions">${actions}</p>` : ""}
9734
9792
  </div>
9735
9793
  <strong>${escapeHtml15(String(check.value ?? check.status))}</strong>
9736
9794
  ${check.href ? `<a href="${escapeHtml15(check.href)}">Open surface</a>` : ""}
9737
- </article>`).join("");
9738
- return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml15(title)}</title><style>body{background:#0c0f14;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1060px;padding:32px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#fbbf24;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.status{display:inline-flex;border:1px solid #3f3f46;border-radius:999px;padding:8px 12px}.status.pass,.check.pass{border-color:rgba(34,197,94,.55)}.status.warn,.check.warn{border-color:rgba(245,158,11,.65)}.status.fail,.check.fail{border-color:rgba(239,68,68,.75)}.checks{display:grid;gap:14px}.check{align-items:center;background:#141922;border:1px solid #26313d;border-radius:22px;display:grid;gap:16px;grid-template-columns:1fr auto auto;padding:18px}.check span{color:#a8b0b8;font-size:.78rem;font-weight:900;letter-spacing:.08em}.check h2{margin:.2rem 0}.check p{color:#b9c0c8;margin:.2rem 0 0}.check strong{font-size:1.5rem}.check a,a{color:#fbbf24}@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>${escapeHtml15(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 ${escapeHtml15(report.status)}">Overall: ${escapeHtml15(report.status.toUpperCase())}</p><p>Checked ${escapeHtml15(new Date(report.checkedAt).toLocaleString())}</p></section><section class="checks">${checks}</section></main></body></html>`;
9795
+ </article>`;
9796
+ }).join("");
9797
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml15(title)}</title><style>body{background:#0c0f14;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1060px;padding:32px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#fbbf24;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.status{display:inline-flex;border:1px solid #3f3f46;border-radius:999px;padding:8px 12px}.status.pass,.check.pass{border-color:rgba(34,197,94,.55)}.status.warn,.check.warn{border-color:rgba(245,158,11,.65)}.status.fail,.check.fail{border-color:rgba(239,68,68,.75)}.checks{display:grid;gap:14px}.check{align-items:center;background:#141922;border:1px solid #26313d;border-radius:22px;display:grid;gap:16px;grid-template-columns:1fr auto auto;padding:18px}.check span{color:#a8b0b8;font-size:.78rem;font-weight:900;letter-spacing:.08em}.check h2{margin:.2rem 0}.check p{color:#b9c0c8;margin:.2rem 0 0}.check strong{font-size:1.5rem}.actions{display:flex;flex-wrap:wrap;gap:10px}.check a,a{color:#fbbf24}button{background:#fbbf24;border:0;border-radius:999px;color:#111827;cursor:pointer;font-weight:800;padding:9px 12px}button:disabled{cursor:wait;opacity:.65}@media(max-width:760px){main{padding:20px}.check{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Self-hosted readiness</p><h1>${escapeHtml15(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 ${escapeHtml15(report.status)}">Overall: ${escapeHtml15(report.status.toUpperCase())}</p><p>Checked ${escapeHtml15(new Date(report.checkedAt).toLocaleString())}</p></section><section class="checks">${checks}</section></main><script>document.querySelectorAll("[data-readiness-action]").forEach((button)=>{button.addEventListener("click",async()=>{const url=button.getAttribute("data-action-url");if(!url)return;button.disabled=true;const original=button.textContent;button.textContent="Running...";try{const response=await fetch(url,{method:"POST"});button.textContent=response.ok?"Done. Reloading...":"Failed";if(response.ok)setTimeout(()=>location.reload(),500)}catch{button.textContent="Failed"}finally{setTimeout(()=>{button.disabled=false;button.textContent=original},1500)}})});</script></body></html>`;
9739
9798
  };
9740
9799
  var createVoiceProductionReadinessRoutes = (options) => {
9741
9800
  const path = options.path ?? "/api/production-readiness";
@@ -2,7 +2,14 @@ import { Elysia } from 'elysia';
2
2
  import { type VoiceTelephonyCarrierMatrixInput } from './telephony/matrix';
3
3
  import type { VoiceTraceEventStore } from './trace';
4
4
  export type VoiceProductionReadinessStatus = 'fail' | 'pass' | 'warn';
5
+ export type VoiceProductionReadinessAction = {
6
+ description?: string;
7
+ href: string;
8
+ label: string;
9
+ method?: 'GET' | 'POST';
10
+ };
5
11
  export type VoiceProductionReadinessCheck = {
12
+ actions?: VoiceProductionReadinessAction[];
6
13
  detail?: string;
7
14
  href?: string;
8
15
  label: string;
@@ -15,6 +22,7 @@ export type VoiceProductionReadinessReport = {
15
22
  links: {
16
23
  carriers?: string;
17
24
  handoffs?: string;
25
+ handoffRetry?: string;
18
26
  quality?: string;
19
27
  resilience?: string;
20
28
  sessions?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.88",
3
+ "version": "0.0.22-beta.89",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",