@absolutejs/voice 0.0.22-beta.289 → 0.0.22-beta.290

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
@@ -28269,11 +28269,31 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
28269
28269
  label: "Open calibrated gate source"
28270
28270
  }
28271
28271
  ] : [];
28272
+ const calibratedGateExplanation = (input2) => ({
28273
+ ...input2,
28274
+ sourceHref: options.links?.sloReadinessThresholds
28275
+ });
28276
+ const providerSloMetricForIssue = () => {
28277
+ const issue = providerSlo?.issues[0];
28278
+ if (!issue?.kind) {
28279
+ return;
28280
+ }
28281
+ const metrics = providerSlo?.kinds[issue.kind]?.metrics;
28282
+ return Object.values(metrics ?? {}).find((metric) => metric.label === issue.label || issue.code.endsWith(metric.label.toLowerCase().replace(/[^a-z0-9]+/g, "_")));
28283
+ };
28272
28284
  checks.push({
28273
28285
  detail: liveLatency.total === 0 ? "No browser live-latency measurements are recorded yet." : liveLatency.status === "pass" ? `Live browser turn latency averages ${liveLatency.averageLatencyMs}ms.` : `${liveLatency.failed} failed and ${liveLatency.warnings} warned live-latency measurement(s).`,
28274
28286
  href: firstOperationsRecordHref(operationsRecords.failingLatency) ?? options.links?.liveLatency ?? "/traces",
28275
28287
  label: "Live latency proof",
28276
28288
  proofSource: proofSource("liveLatency", "liveLatencyProof"),
28289
+ gateExplanation: liveLatency.status === "pass" ? undefined : calibratedGateExplanation({
28290
+ evidenceHref: firstOperationsRecordHref(operationsRecords.failingLatency) ?? options.links?.liveLatency ?? "/traces",
28291
+ observed: liveLatency.averageLatencyMs,
28292
+ remediation: "Inspect the slow browser turn, reduce provider/turn latency, then rerun live latency proof so readiness uses fresh samples.",
28293
+ threshold: liveLatency.status === "fail" ? options.liveLatencyFailAfterMs ?? 3200 : options.liveLatencyWarnAfterMs ?? 1800,
28294
+ thresholdLabel: liveLatency.status === "fail" ? "Live latency fail after" : "Live latency warn after",
28295
+ unit: "ms"
28296
+ }),
28277
28297
  status: liveLatency.status,
28278
28298
  value: liveLatency.averageLatencyMs === undefined ? `${liveLatency.total} samples` : `${liveLatency.averageLatencyMs}ms avg`,
28279
28299
  actions: liveLatency.status === "pass" ? [] : [
@@ -28436,11 +28456,20 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
28436
28456
  }
28437
28457
  if (providerSloSummary && providerSlo) {
28438
28458
  const firstIssue = providerSlo.issues[0];
28459
+ const firstMetric = providerSloMetricForIssue();
28439
28460
  checks.push({
28440
28461
  detail: providerSloSummary.status === "pass" ? `${providerSloSummary.eventsWithLatency} provider latency sample(s) are inside LLM/STT/TTS SLO budgets.` : firstIssue?.detail ?? `${providerSloSummary.issues} provider SLO issue(s) need review.`,
28441
28462
  href: firstIssue?.sessionId ? voiceOperationsRecordHref(options.links?.operationsRecords ?? "/voice-operations", firstIssue.sessionId) : options.links?.providerSlo ?? options.links?.resilience ?? "/voice/provider-slos",
28442
28463
  label: "Provider SLO gates",
28443
28464
  proofSource: proofSource("providerSlo", "providerSlos"),
28465
+ gateExplanation: providerSloSummary.status === "pass" ? undefined : calibratedGateExplanation({
28466
+ evidenceHref: firstIssue?.sessionId ? voiceOperationsRecordHref(options.links?.operationsRecords ?? "/voice-operations", firstIssue.sessionId) : options.links?.providerSlo ?? options.links?.resilience ?? "/voice/provider-slos",
28467
+ observed: firstMetric?.actual ?? firstIssue?.value,
28468
+ remediation: "Inspect the provider SLO report, fix slow or failing STT/LLM/TTS behavior, then rerun provider proof so the calibrated budget is met.",
28469
+ threshold: firstMetric?.threshold,
28470
+ thresholdLabel: firstMetric?.label ?? firstIssue?.label ?? "Provider SLO gate",
28471
+ unit: firstMetric?.unit
28472
+ }),
28444
28473
  status: providerSloSummary.status,
28445
28474
  value: `${providerSloSummary.eventsWithLatency}/${providerSloSummary.events}`,
28446
28475
  actions: providerSloSummary.status === "pass" ? [] : [
@@ -28577,6 +28606,14 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
28577
28606
  href: options.links?.reconnectContracts ?? options.links?.sessions ?? "/sessions",
28578
28607
  label: "Reconnect recovery contracts",
28579
28608
  proofSource: proofSource("reconnectContracts", "reconnect"),
28609
+ gateExplanation: reconnectContractSummary.status === "pass" ? undefined : calibratedGateExplanation({
28610
+ evidenceHref: options.links?.reconnectContracts ?? options.links?.sessions ?? "/sessions",
28611
+ observed: reconnectContractSummary.resumeLatencyP95Ms,
28612
+ remediation: "Inspect reconnect lifecycle traces, restore faster resume/replay-safe state, then rerun reconnect proof.",
28613
+ threshold: options.reconnectResumeFailAfterMs,
28614
+ thresholdLabel: "Reconnect resume p95 fail after",
28615
+ unit: "ms"
28616
+ }),
28580
28617
  status: reconnectContractSummary.status,
28581
28618
  value: `${reconnectContractSummary.passed}/${reconnectContractSummary.total}`,
28582
28619
  actions: reconnectContractSummary.status === "pass" ? [] : [
@@ -28595,6 +28632,14 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
28595
28632
  href: options.links?.bargeIn ?? "/barge-in",
28596
28633
  label: "Barge-in interruption proof",
28597
28634
  proofSource: proofSource("bargeInReports", "bargeIn"),
28635
+ gateExplanation: bargeInSummary.status === "pass" ? undefined : calibratedGateExplanation({
28636
+ evidenceHref: options.links?.bargeIn ?? "/barge-in",
28637
+ observed: bargeInReports?.map((report) => report.averageLatencyMs).filter((value) => typeof value === "number").sort((left, right) => right - left)[0] ?? `${bargeInSummary.failed} failed`,
28638
+ remediation: "Inspect barge-in proof, confirm playback cancellation is immediate, then rerun interruption proof against the calibrated threshold.",
28639
+ threshold: bargeInReports?.[0]?.thresholdMs,
28640
+ thresholdLabel: "Barge-in interruption threshold",
28641
+ unit: typeof bargeInReports?.[0]?.thresholdMs === "number" ? "ms" : "count"
28642
+ }),
28598
28643
  status: bargeInSummary.status,
28599
28644
  value: `${bargeInSummary.passed}/${bargeInSummary.total}`,
28600
28645
  actions: bargeInSummary.status === "pass" ? [] : [
@@ -28747,6 +28792,14 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
28747
28792
  detail: monitoringSummary.status === "pass" ? `${monitoringSummary.total} monitor(s) are passing with no open issues.` : options.monitoringRunFailAfterMs !== undefined && monitoringSummary.elapsedMs !== undefined && monitoringSummary.elapsedMs > options.monitoringRunFailAfterMs ? `Monitor run took ${monitoringSummary.elapsedMs}ms, above ${options.monitoringRunFailAfterMs}ms.` : `${monitoringSummary.open} monitor issue(s) open, ${monitoringSummary.criticalOpen} critical.`,
28748
28793
  href: options.links?.monitoring ?? "/voice/monitors",
28749
28794
  label: "Monitoring issues",
28795
+ gateExplanation: monitoringSummary.status === "pass" ? undefined : calibratedGateExplanation({
28796
+ evidenceHref: options.links?.monitoring ?? "/voice/monitors",
28797
+ observed: monitoringSummary.elapsedMs ?? `${monitoringSummary.open} open issue(s)`,
28798
+ remediation: "Inspect monitor issues or slow monitor execution, resolve open blockers, then rerun the monitor proof.",
28799
+ threshold: options.monitoringRunFailAfterMs,
28800
+ thresholdLabel: "Monitor run fail after",
28801
+ unit: monitoringSummary.elapsedMs !== undefined ? "ms" : "count"
28802
+ }),
28750
28803
  status: monitoringSummary.status,
28751
28804
  value: `${monitoring.summary.passed}/${monitoringSummary.total}`,
28752
28805
  actions: monitoringSummary.status === "pass" ? [] : [
@@ -28764,6 +28817,14 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
28764
28817
  detail: monitoringNotifierDeliverySummary.status === "pass" ? `${monitoringNotifierDeliverySummary.sent} monitor notification(s) delivered.` : options.monitoringNotifierDeliveryFailAfterMs !== undefined && monitoringNotifierDeliverySummary.elapsedMs !== undefined && monitoringNotifierDeliverySummary.elapsedMs > options.monitoringNotifierDeliveryFailAfterMs ? `Monitor notification delivery took ${monitoringNotifierDeliverySummary.elapsedMs}ms, above ${options.monitoringNotifierDeliveryFailAfterMs}ms.` : `${monitoringNotifierDeliverySummary.failed} monitor notification delivery failure(s).`,
28765
28818
  href: options.links?.monitoringNotifierDelivery ?? "/api/voice/monitor-issues/notifications",
28766
28819
  label: "Monitor notifier delivery",
28820
+ gateExplanation: monitoringNotifierDeliverySummary.status === "pass" ? undefined : calibratedGateExplanation({
28821
+ evidenceHref: options.links?.monitoringNotifierDelivery ?? "/api/voice/monitor-issues/notifications",
28822
+ observed: monitoringNotifierDeliverySummary.elapsedMs ?? `${monitoringNotifierDeliverySummary.failed} failed`,
28823
+ remediation: "Inspect monitor notification receipts, fix webhook/email/Slack delivery, then rerun notifier proof.",
28824
+ threshold: options.monitoringNotifierDeliveryFailAfterMs,
28825
+ thresholdLabel: "Monitor notifier delivery fail after",
28826
+ unit: monitoringNotifierDeliverySummary.elapsedMs !== undefined ? "ms" : "count"
28827
+ }),
28767
28828
  status: monitoringNotifierDeliverySummary.status,
28768
28829
  value: `${monitoringNotifierDeliverySummary.sent}/${monitoringNotifierDeliverySummary.total}`,
28769
28830
  actions: monitoringNotifierDeliverySummary.status === "pass" ? [] : [
@@ -28873,11 +28934,13 @@ var renderVoiceProductionReadinessHTML = (report, options = {}) => {
28873
28934
  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>` : "";
28874
28935
  const checks = report.checks.map((check, index) => {
28875
28936
  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("");
28937
+ 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>` : "";
28876
28938
  return `<article class="check ${escapeHtml41(check.status)}">
28877
28939
  <div>
28878
28940
  <span>${escapeHtml41(check.status.toUpperCase())}</span>
28879
28941
  <h2>${escapeHtml41(check.label)}</h2>
28880
28942
  ${check.detail ? `<p>${escapeHtml41(check.detail)}</p>` : ""}
28943
+ ${explanation}
28881
28944
  ${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>` : ""}
28882
28945
  ${actions ? `<p class="actions">${actions}</p>` : ""}
28883
28946
  </div>
@@ -28901,7 +28964,7 @@ var renderVoiceProductionReadinessHTML = (report, options = {}) => {
28901
28964
  providerRoutingContracts: loadProviderRoutingContracts,
28902
28965
  store: traceStore
28903
28966
  });`);
28904
- 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 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>`;
28967
+ 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>`;
28905
28968
  };
28906
28969
  var createVoiceProductionReadinessRoutes = (options) => {
28907
28970
  const path = options.path ?? "/api/production-readiness";
@@ -32,9 +32,19 @@ export type VoiceProductionReadinessAction = {
32
32
  label: string;
33
33
  method?: 'GET' | 'POST';
34
34
  };
35
+ export type VoiceProductionReadinessGateExplanation = {
36
+ evidenceHref?: string;
37
+ observed?: number | string;
38
+ remediation: string;
39
+ sourceHref?: string;
40
+ threshold?: number | string;
41
+ thresholdLabel?: string;
42
+ unit?: 'count' | 'ms' | 'rate' | 'status';
43
+ };
35
44
  export type VoiceProductionReadinessCheck = {
36
45
  actions?: VoiceProductionReadinessAction[];
37
46
  detail?: string;
47
+ gateExplanation?: VoiceProductionReadinessGateExplanation;
38
48
  href?: string;
39
49
  label: string;
40
50
  proofSource?: VoiceProductionReadinessProofSource;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.289",
3
+ "version": "0.0.22-beta.290",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",