@absolutejs/voice 0.0.22-beta.335 → 0.0.22-beta.336

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.
@@ -3924,6 +3924,18 @@ var shouldSwitchProvider = (current, best, options) => {
3924
3924
  const improvementRatio = current.p95Ms > 0 ? improvementMs / current.p95Ms : 0;
3925
3925
  return improvementMs >= minImprovementMs || improvementRatio >= minImprovementRatio;
3926
3926
  };
3927
+ var bestProviderByRole = (providers) => {
3928
+ const best = new Map;
3929
+ for (const provider of providers) {
3930
+ const role = provider.role ?? provider.id;
3931
+ const existing = best.get(role);
3932
+ if (!existing || compareProviders(provider, existing) < 0) {
3933
+ best.set(role, provider);
3934
+ }
3935
+ }
3936
+ return [...best.values()].sort((left, right) => String(left.role ?? left.id).localeCompare(String(right.role ?? right.id)));
3937
+ };
3938
+ var formatProviderMix = (providers) => providers.length === 0 ? "n/a" : providers.map((provider) => provider.role ? `${provider.role.toUpperCase()} ${provider.label ?? provider.id}` : provider.label ?? provider.id).join(", ");
3927
3939
  var evaluateVoiceProofTrendEvidence = (report, input = {}) => {
3928
3940
  const issues = [];
3929
3941
  const requiredStatus = input.requireStatus ?? "pass";
@@ -4040,8 +4052,10 @@ var buildVoiceProofTrendRecommendationReport = (report, options = {}) => {
4040
4052
  const runtimeChannel = readProofTrendRuntimeChannel(report);
4041
4053
  const providers = summarizeProofTrendProviders(report, budgets.maxProviderP95Ms);
4042
4054
  const bestProvider = providers.find((provider) => provider.status === "pass") ?? providers[0];
4055
+ const bestProviders = bestProviderByRole(providers).filter((provider) => provider.status === "pass");
4043
4056
  const currentProvider = options.currentProviderId ? providers.find((provider) => provider.id === options.currentProviderId) : undefined;
4044
- const providerSwitchRecommended = shouldSwitchProvider(currentProvider, bestProvider, options);
4057
+ const bestComparableProvider = currentProvider?.role ? bestProviders.find((provider) => provider.role === currentProvider.role) : bestProvider;
4058
+ const providerSwitchRecommended = shouldSwitchProvider(currentProvider, bestComparableProvider, options);
4045
4059
  const recommendations = [];
4046
4060
  const issues = [];
4047
4061
  if (report.ok !== true) {
@@ -4049,19 +4063,20 @@ var buildVoiceProofTrendRecommendationReport = (report, options = {}) => {
4049
4063
  }
4050
4064
  recommendations.push({
4051
4065
  evidence: {
4052
- bestProviderId: bestProvider?.id,
4053
- bestProviderP95Ms: bestProvider?.p95Ms,
4066
+ bestProviderId: bestComparableProvider?.id ?? bestProvider?.id,
4067
+ bestProviderMix: formatProviderMix(bestProviders),
4068
+ bestProviderP95Ms: bestComparableProvider?.p95Ms ?? bestProvider?.p95Ms,
4054
4069
  budgetMs: budgets.maxProviderP95Ms,
4055
4070
  currentProviderId: currentProvider?.id ?? options.currentProviderId,
4056
4071
  currentProviderP95Ms: currentProvider?.p95Ms,
4057
4072
  providerComparisonCount: providers.length,
4058
4073
  providerP95Ms: maxProviderP95Ms
4059
4074
  },
4060
- nextMove: providers.length > 0 ? providerSwitchRecommended ? `Route latency-sensitive turns to ${bestProvider?.label ?? bestProvider?.id} for this call profile and keep the current path as fallback.` : bestProvider ? `Use ${bestProvider.label ?? bestProvider.id} as the fastest proven provider path for this call profile and keep collecting sustained comparisons.` : "Collect provider-specific sustained samples before making provider-specific routing decisions." : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "Keep the current provider route for latency-sensitive turns and keep collecting sustained proof." : "Route latency-sensitive turns to a faster provider profile or tighten fallback/circuit-breaker budgets before promotion.",
4061
- providerId: bestProvider?.id,
4062
- recommendation: providers.length > 0 ? providerSwitchRecommended ? `Switch latency-sensitive routing to ${bestProvider?.label ?? bestProvider?.id}` : bestProvider ? `Prefer ${bestProvider.label ?? bestProvider.id} for this call profile` : "Collect provider-specific latency samples" : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "Keep current provider path" : "Change provider routing for latency-sensitive traffic",
4063
- role: bestProvider?.role,
4064
- status: providers.length > 0 ? providerSwitchRecommended ? "warn" : bestProvider?.status ?? "fail" : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "pass" : maxProviderP95Ms === undefined ? "fail" : "warn",
4075
+ nextMove: providers.length > 0 ? providerSwitchRecommended ? `Route latency-sensitive ${currentProvider?.role ?? "provider"} traffic to ${bestComparableProvider?.label ?? bestComparableProvider?.id} for this call profile and keep the current path as fallback.` : bestProviders.length > 0 ? `Use the fastest proven provider mix for this call profile: ${formatProviderMix(bestProviders)}.` : "Collect provider-specific sustained samples before making provider-specific routing decisions." : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "Keep the current provider route for latency-sensitive turns and keep collecting sustained proof." : "Route latency-sensitive turns to a faster provider profile or tighten fallback/circuit-breaker budgets before promotion.",
4076
+ providerId: providerSwitchRecommended ? bestComparableProvider?.id : bestProviders.length === 1 ? bestProviders[0]?.id : undefined,
4077
+ recommendation: providers.length > 0 ? providerSwitchRecommended ? `Switch latency-sensitive ${currentProvider?.role ?? "provider"} routing to ${bestComparableProvider?.label ?? bestComparableProvider?.id}` : bestProviders.length > 0 ? "Prefer the fastest proven provider mix for this call profile" : "Collect provider-specific latency samples" : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "Keep current provider path" : "Change provider routing for latency-sensitive traffic",
4078
+ role: bestComparableProvider?.role,
4079
+ status: providers.length > 0 ? providerSwitchRecommended ? "warn" : bestProviders.length > 0 ? "pass" : bestProvider?.status ?? "fail" : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "pass" : maxProviderP95Ms === undefined ? "fail" : "warn",
4065
4080
  surface: "provider-path"
4066
4081
  });
4067
4082
  const runtimePass = withinBudget(runtimeChannel.maxFirstAudioLatencyMs, budgets.maxRuntimeFirstAudioLatencyMs) && withinBudget(runtimeChannel.maxInterruptionP95Ms, budgets.maxRuntimeInterruptionP95Ms) && withinBudget(runtimeChannel.maxJitterMs, budgets.maxRuntimeJitterMs) && withinBudget(runtimeChannel.maxTimestampDriftMs, budgets.maxRuntimeTimestampDriftMs) && withinBudget(runtimeChannel.maxBackpressureEvents, budgets.maxRuntimeBackpressureEvents);
@@ -4105,6 +4120,7 @@ var buildVoiceProofTrendRecommendationReport = (report, options = {}) => {
4105
4120
  const status = issues.length > 0 ? "fail" : worstRecommendationStatus(recommendations);
4106
4121
  return {
4107
4122
  bestProvider,
4123
+ bestProviders,
4108
4124
  generatedAt: new Date().toISOString(),
4109
4125
  issues,
4110
4126
  ok: status !== "fail",
@@ -4128,7 +4144,7 @@ var renderVoiceProofTrendRecommendationMarkdown = (report, title = "Voice Provid
4128
4144
  "",
4129
4145
  `- Status: ${report.status}`,
4130
4146
  `- Source: ${report.source}`,
4131
- `- Best provider: ${report.bestProvider?.label ?? report.bestProvider?.id ?? "n/a"}`,
4147
+ `- Best provider mix: ${formatProviderMix(report.bestProviders)}`,
4132
4148
  `- Provider comparisons: ${String(report.summary.providerComparisonCount)}`,
4133
4149
  `- Recommended actions: ${String(report.summary.recommendedActions)}`,
4134
4150
  "",
@@ -4151,7 +4167,7 @@ var renderVoiceProofTrendRecommendationHTML = (report, title = "Voice Provider R
4151
4167
  const cards = report.recommendations.map((recommendation) => `<article class="${escapeHtml3(recommendation.status)}"><p class="eyebrow">${escapeHtml3(recommendation.surface)} \xB7 ${escapeHtml3(recommendation.status)}</p><h2>${escapeHtml3(recommendation.recommendation)}</h2><p>${escapeHtml3(recommendation.nextMove)}</p><pre>${escapeHtml3(JSON.stringify(recommendation.evidence, null, 2))}</pre></article>`).join("");
4152
4168
  const issues = report.issues.length === 0 ? "<li>None</li>" : report.issues.map((issue) => `<li>${escapeHtml3(issue)}</li>`).join("");
4153
4169
  const providerRows = report.providers.length === 0 ? "<li>No provider-specific samples were present.</li>" : report.providers.map((provider) => `<li><strong>#${String(provider.rank)} ${escapeHtml3(provider.label ?? provider.id)}</strong><span>${escapeHtml3(provider.role ?? "provider")} \xB7 ${escapeHtml3(provider.status)} \xB7 p95 ${escapeHtml3(provider.p95Ms ?? "n/a")}ms \xB7 ${escapeHtml3(provider.samples ?? "n/a")} sample(s)</span><small>${escapeHtml3(provider.nextMove)}</small></li>`).join("");
4154
- return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml3(title)}</title><style>body{background:#101418;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero,article{background:#17201d;border:1px solid #2e3d36;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12))}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.7rem);letter-spacing:-.06em;line-height:.92;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{border:1px solid #42534a;border-radius:999px;padding:8px 12px}.pass{border-color:rgba(34,197,94,.55)}.warn{border-color:rgba(245,158,11,.7)}.fail{border-color:rgba(239,68,68,.75)}pre{background:#0b1110;border-radius:14px;overflow:auto;padding:12px}a{color:#5eead4}li{margin:.45rem 0}li span,li small{display:block;color:#c9d3ca}</style></head><body><main><section class="hero"><p class="eyebrow">Sustained proof recommendations</p><h1>${escapeHtml3(title)}</h1><p>Generated ${escapeHtml3(report.generatedAt)} from ${escapeHtml3(report.source)}.</p><div class="summary"><span class="pill">Status ${escapeHtml3(report.status)}</span><span class="pill">Provider ${report.summary.keepCurrentProviderPath ? "keep" : "change"}</span><span class="pill">Best ${escapeHtml3(report.bestProvider?.label ?? report.bestProvider?.id ?? "n/a")}</span><span class="pill">Runtime ${report.summary.keepCurrentRuntimeChannel ? "keep" : "tune"}</span><span class="pill">${String(report.summary.recommendedActions)} action(s)</span></div></section>${cards}<section class="hero"><h2>Provider Comparison</h2><ul>${providerRows}</ul></section><section class="hero"><h2>Issues</h2><ul>${issues}</ul></section></main></body></html>`;
4170
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml3(title)}</title><style>body{background:#101418;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero,article{background:#17201d;border:1px solid #2e3d36;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12))}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.7rem);letter-spacing:-.06em;line-height:.92;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{border:1px solid #42534a;border-radius:999px;padding:8px 12px}.pass{border-color:rgba(34,197,94,.55)}.warn{border-color:rgba(245,158,11,.7)}.fail{border-color:rgba(239,68,68,.75)}pre{background:#0b1110;border-radius:14px;overflow:auto;padding:12px}a{color:#5eead4}li{margin:.45rem 0}li span,li small{display:block;color:#c9d3ca}</style></head><body><main><section class="hero"><p class="eyebrow">Sustained proof recommendations</p><h1>${escapeHtml3(title)}</h1><p>Generated ${escapeHtml3(report.generatedAt)} from ${escapeHtml3(report.source)}.</p><div class="summary"><span class="pill">Status ${escapeHtml3(report.status)}</span><span class="pill">Provider ${report.summary.keepCurrentProviderPath ? "keep" : "change"}</span><span class="pill">Best mix ${escapeHtml3(formatProviderMix(report.bestProviders))}</span><span class="pill">Runtime ${report.summary.keepCurrentRuntimeChannel ? "keep" : "tune"}</span><span class="pill">${String(report.summary.recommendedActions)} action(s)</span></div></section>${cards}<section class="hero"><h2>Provider Comparison</h2><ul>${providerRows}</ul></section><section class="hero"><h2>Issues</h2><ul>${issues}</ul></section></main></body></html>`;
4155
4171
  };
4156
4172
  var createVoiceProofTrendRecommendationRoutes = (options) => {
4157
4173
  const path = options.path ?? "/api/voice/proof-trend-recommendations";
package/dist/index.js CHANGED
@@ -14641,6 +14641,18 @@ var shouldSwitchProvider = (current, best, options) => {
14641
14641
  const improvementRatio = current.p95Ms > 0 ? improvementMs / current.p95Ms : 0;
14642
14642
  return improvementMs >= minImprovementMs || improvementRatio >= minImprovementRatio;
14643
14643
  };
14644
+ var bestProviderByRole = (providers) => {
14645
+ const best = new Map;
14646
+ for (const provider of providers) {
14647
+ const role = provider.role ?? provider.id;
14648
+ const existing = best.get(role);
14649
+ if (!existing || compareProviders(provider, existing) < 0) {
14650
+ best.set(role, provider);
14651
+ }
14652
+ }
14653
+ return [...best.values()].sort((left, right) => String(left.role ?? left.id).localeCompare(String(right.role ?? right.id)));
14654
+ };
14655
+ var formatProviderMix = (providers) => providers.length === 0 ? "n/a" : providers.map((provider) => provider.role ? `${provider.role.toUpperCase()} ${provider.label ?? provider.id}` : provider.label ?? provider.id).join(", ");
14644
14656
  var evaluateVoiceProofTrendEvidence = (report, input = {}) => {
14645
14657
  const issues = [];
14646
14658
  const requiredStatus = input.requireStatus ?? "pass";
@@ -14757,8 +14769,10 @@ var buildVoiceProofTrendRecommendationReport = (report, options = {}) => {
14757
14769
  const runtimeChannel = readProofTrendRuntimeChannel(report);
14758
14770
  const providers = summarizeProofTrendProviders(report, budgets.maxProviderP95Ms);
14759
14771
  const bestProvider = providers.find((provider) => provider.status === "pass") ?? providers[0];
14772
+ const bestProviders = bestProviderByRole(providers).filter((provider) => provider.status === "pass");
14760
14773
  const currentProvider = options.currentProviderId ? providers.find((provider) => provider.id === options.currentProviderId) : undefined;
14761
- const providerSwitchRecommended = shouldSwitchProvider(currentProvider, bestProvider, options);
14774
+ const bestComparableProvider = currentProvider?.role ? bestProviders.find((provider) => provider.role === currentProvider.role) : bestProvider;
14775
+ const providerSwitchRecommended = shouldSwitchProvider(currentProvider, bestComparableProvider, options);
14762
14776
  const recommendations = [];
14763
14777
  const issues = [];
14764
14778
  if (report.ok !== true) {
@@ -14766,19 +14780,20 @@ var buildVoiceProofTrendRecommendationReport = (report, options = {}) => {
14766
14780
  }
14767
14781
  recommendations.push({
14768
14782
  evidence: {
14769
- bestProviderId: bestProvider?.id,
14770
- bestProviderP95Ms: bestProvider?.p95Ms,
14783
+ bestProviderId: bestComparableProvider?.id ?? bestProvider?.id,
14784
+ bestProviderMix: formatProviderMix(bestProviders),
14785
+ bestProviderP95Ms: bestComparableProvider?.p95Ms ?? bestProvider?.p95Ms,
14771
14786
  budgetMs: budgets.maxProviderP95Ms,
14772
14787
  currentProviderId: currentProvider?.id ?? options.currentProviderId,
14773
14788
  currentProviderP95Ms: currentProvider?.p95Ms,
14774
14789
  providerComparisonCount: providers.length,
14775
14790
  providerP95Ms: maxProviderP95Ms
14776
14791
  },
14777
- nextMove: providers.length > 0 ? providerSwitchRecommended ? `Route latency-sensitive turns to ${bestProvider?.label ?? bestProvider?.id} for this call profile and keep the current path as fallback.` : bestProvider ? `Use ${bestProvider.label ?? bestProvider.id} as the fastest proven provider path for this call profile and keep collecting sustained comparisons.` : "Collect provider-specific sustained samples before making provider-specific routing decisions." : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "Keep the current provider route for latency-sensitive turns and keep collecting sustained proof." : "Route latency-sensitive turns to a faster provider profile or tighten fallback/circuit-breaker budgets before promotion.",
14778
- providerId: bestProvider?.id,
14779
- recommendation: providers.length > 0 ? providerSwitchRecommended ? `Switch latency-sensitive routing to ${bestProvider?.label ?? bestProvider?.id}` : bestProvider ? `Prefer ${bestProvider.label ?? bestProvider.id} for this call profile` : "Collect provider-specific latency samples" : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "Keep current provider path" : "Change provider routing for latency-sensitive traffic",
14780
- role: bestProvider?.role,
14781
- status: providers.length > 0 ? providerSwitchRecommended ? "warn" : bestProvider?.status ?? "fail" : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "pass" : maxProviderP95Ms === undefined ? "fail" : "warn",
14792
+ nextMove: providers.length > 0 ? providerSwitchRecommended ? `Route latency-sensitive ${currentProvider?.role ?? "provider"} traffic to ${bestComparableProvider?.label ?? bestComparableProvider?.id} for this call profile and keep the current path as fallback.` : bestProviders.length > 0 ? `Use the fastest proven provider mix for this call profile: ${formatProviderMix(bestProviders)}.` : "Collect provider-specific sustained samples before making provider-specific routing decisions." : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "Keep the current provider route for latency-sensitive turns and keep collecting sustained proof." : "Route latency-sensitive turns to a faster provider profile or tighten fallback/circuit-breaker budgets before promotion.",
14793
+ providerId: providerSwitchRecommended ? bestComparableProvider?.id : bestProviders.length === 1 ? bestProviders[0]?.id : undefined,
14794
+ recommendation: providers.length > 0 ? providerSwitchRecommended ? `Switch latency-sensitive ${currentProvider?.role ?? "provider"} routing to ${bestComparableProvider?.label ?? bestComparableProvider?.id}` : bestProviders.length > 0 ? "Prefer the fastest proven provider mix for this call profile" : "Collect provider-specific latency samples" : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "Keep current provider path" : "Change provider routing for latency-sensitive traffic",
14795
+ role: bestComparableProvider?.role,
14796
+ status: providers.length > 0 ? providerSwitchRecommended ? "warn" : bestProviders.length > 0 ? "pass" : bestProvider?.status ?? "fail" : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "pass" : maxProviderP95Ms === undefined ? "fail" : "warn",
14782
14797
  surface: "provider-path"
14783
14798
  });
14784
14799
  const runtimePass = withinBudget(runtimeChannel.maxFirstAudioLatencyMs, budgets.maxRuntimeFirstAudioLatencyMs) && withinBudget(runtimeChannel.maxInterruptionP95Ms, budgets.maxRuntimeInterruptionP95Ms) && withinBudget(runtimeChannel.maxJitterMs, budgets.maxRuntimeJitterMs) && withinBudget(runtimeChannel.maxTimestampDriftMs, budgets.maxRuntimeTimestampDriftMs) && withinBudget(runtimeChannel.maxBackpressureEvents, budgets.maxRuntimeBackpressureEvents);
@@ -14822,6 +14837,7 @@ var buildVoiceProofTrendRecommendationReport = (report, options = {}) => {
14822
14837
  const status = issues.length > 0 ? "fail" : worstRecommendationStatus(recommendations);
14823
14838
  return {
14824
14839
  bestProvider,
14840
+ bestProviders,
14825
14841
  generatedAt: new Date().toISOString(),
14826
14842
  issues,
14827
14843
  ok: status !== "fail",
@@ -14845,7 +14861,7 @@ var renderVoiceProofTrendRecommendationMarkdown = (report, title = "Voice Provid
14845
14861
  "",
14846
14862
  `- Status: ${report.status}`,
14847
14863
  `- Source: ${report.source}`,
14848
- `- Best provider: ${report.bestProvider?.label ?? report.bestProvider?.id ?? "n/a"}`,
14864
+ `- Best provider mix: ${formatProviderMix(report.bestProviders)}`,
14849
14865
  `- Provider comparisons: ${String(report.summary.providerComparisonCount)}`,
14850
14866
  `- Recommended actions: ${String(report.summary.recommendedActions)}`,
14851
14867
  "",
@@ -14868,7 +14884,7 @@ var renderVoiceProofTrendRecommendationHTML = (report, title = "Voice Provider R
14868
14884
  const cards = report.recommendations.map((recommendation) => `<article class="${escapeHtml22(recommendation.status)}"><p class="eyebrow">${escapeHtml22(recommendation.surface)} \xB7 ${escapeHtml22(recommendation.status)}</p><h2>${escapeHtml22(recommendation.recommendation)}</h2><p>${escapeHtml22(recommendation.nextMove)}</p><pre>${escapeHtml22(JSON.stringify(recommendation.evidence, null, 2))}</pre></article>`).join("");
14869
14885
  const issues = report.issues.length === 0 ? "<li>None</li>" : report.issues.map((issue) => `<li>${escapeHtml22(issue)}</li>`).join("");
14870
14886
  const providerRows = report.providers.length === 0 ? "<li>No provider-specific samples were present.</li>" : report.providers.map((provider) => `<li><strong>#${String(provider.rank)} ${escapeHtml22(provider.label ?? provider.id)}</strong><span>${escapeHtml22(provider.role ?? "provider")} \xB7 ${escapeHtml22(provider.status)} \xB7 p95 ${escapeHtml22(provider.p95Ms ?? "n/a")}ms \xB7 ${escapeHtml22(provider.samples ?? "n/a")} sample(s)</span><small>${escapeHtml22(provider.nextMove)}</small></li>`).join("");
14871
- return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml22(title)}</title><style>body{background:#101418;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero,article{background:#17201d;border:1px solid #2e3d36;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12))}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.7rem);letter-spacing:-.06em;line-height:.92;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{border:1px solid #42534a;border-radius:999px;padding:8px 12px}.pass{border-color:rgba(34,197,94,.55)}.warn{border-color:rgba(245,158,11,.7)}.fail{border-color:rgba(239,68,68,.75)}pre{background:#0b1110;border-radius:14px;overflow:auto;padding:12px}a{color:#5eead4}li{margin:.45rem 0}li span,li small{display:block;color:#c9d3ca}</style></head><body><main><section class="hero"><p class="eyebrow">Sustained proof recommendations</p><h1>${escapeHtml22(title)}</h1><p>Generated ${escapeHtml22(report.generatedAt)} from ${escapeHtml22(report.source)}.</p><div class="summary"><span class="pill">Status ${escapeHtml22(report.status)}</span><span class="pill">Provider ${report.summary.keepCurrentProviderPath ? "keep" : "change"}</span><span class="pill">Best ${escapeHtml22(report.bestProvider?.label ?? report.bestProvider?.id ?? "n/a")}</span><span class="pill">Runtime ${report.summary.keepCurrentRuntimeChannel ? "keep" : "tune"}</span><span class="pill">${String(report.summary.recommendedActions)} action(s)</span></div></section>${cards}<section class="hero"><h2>Provider Comparison</h2><ul>${providerRows}</ul></section><section class="hero"><h2>Issues</h2><ul>${issues}</ul></section></main></body></html>`;
14887
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml22(title)}</title><style>body{background:#101418;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero,article{background:#17201d;border:1px solid #2e3d36;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12))}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.7rem);letter-spacing:-.06em;line-height:.92;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{border:1px solid #42534a;border-radius:999px;padding:8px 12px}.pass{border-color:rgba(34,197,94,.55)}.warn{border-color:rgba(245,158,11,.7)}.fail{border-color:rgba(239,68,68,.75)}pre{background:#0b1110;border-radius:14px;overflow:auto;padding:12px}a{color:#5eead4}li{margin:.45rem 0}li span,li small{display:block;color:#c9d3ca}</style></head><body><main><section class="hero"><p class="eyebrow">Sustained proof recommendations</p><h1>${escapeHtml22(title)}</h1><p>Generated ${escapeHtml22(report.generatedAt)} from ${escapeHtml22(report.source)}.</p><div class="summary"><span class="pill">Status ${escapeHtml22(report.status)}</span><span class="pill">Provider ${report.summary.keepCurrentProviderPath ? "keep" : "change"}</span><span class="pill">Best mix ${escapeHtml22(formatProviderMix(report.bestProviders))}</span><span class="pill">Runtime ${report.summary.keepCurrentRuntimeChannel ? "keep" : "tune"}</span><span class="pill">${String(report.summary.recommendedActions)} action(s)</span></div></section>${cards}<section class="hero"><h2>Provider Comparison</h2><ul>${providerRows}</ul></section><section class="hero"><h2>Issues</h2><ul>${issues}</ul></section></main></body></html>`;
14872
14888
  };
14873
14889
  var createVoiceProofTrendRecommendationRoutes = (options) => {
14874
14890
  const path = options.path ?? "/api/voice/proof-trend-recommendations";
@@ -145,6 +145,7 @@ export type VoiceProofTrendProviderRecommendation = {
145
145
  };
146
146
  export type VoiceProofTrendRecommendationReport = {
147
147
  bestProvider?: VoiceProofTrendProviderRecommendation;
148
+ bestProviders: VoiceProofTrendProviderRecommendation[];
148
149
  generatedAt: string;
149
150
  issues: string[];
150
151
  ok: boolean;
@@ -1672,6 +1672,18 @@ var shouldSwitchProvider = (current, best, options) => {
1672
1672
  const improvementRatio = current.p95Ms > 0 ? improvementMs / current.p95Ms : 0;
1673
1673
  return improvementMs >= minImprovementMs || improvementRatio >= minImprovementRatio;
1674
1674
  };
1675
+ var bestProviderByRole = (providers) => {
1676
+ const best = new Map;
1677
+ for (const provider of providers) {
1678
+ const role = provider.role ?? provider.id;
1679
+ const existing = best.get(role);
1680
+ if (!existing || compareProviders(provider, existing) < 0) {
1681
+ best.set(role, provider);
1682
+ }
1683
+ }
1684
+ return [...best.values()].sort((left, right) => String(left.role ?? left.id).localeCompare(String(right.role ?? right.id)));
1685
+ };
1686
+ var formatProviderMix = (providers) => providers.length === 0 ? "n/a" : providers.map((provider) => provider.role ? `${provider.role.toUpperCase()} ${provider.label ?? provider.id}` : provider.label ?? provider.id).join(", ");
1675
1687
  var evaluateVoiceProofTrendEvidence = (report, input = {}) => {
1676
1688
  const issues = [];
1677
1689
  const requiredStatus = input.requireStatus ?? "pass";
@@ -1788,8 +1800,10 @@ var buildVoiceProofTrendRecommendationReport = (report, options = {}) => {
1788
1800
  const runtimeChannel = readProofTrendRuntimeChannel(report);
1789
1801
  const providers = summarizeProofTrendProviders(report, budgets.maxProviderP95Ms);
1790
1802
  const bestProvider = providers.find((provider) => provider.status === "pass") ?? providers[0];
1803
+ const bestProviders = bestProviderByRole(providers).filter((provider) => provider.status === "pass");
1791
1804
  const currentProvider = options.currentProviderId ? providers.find((provider) => provider.id === options.currentProviderId) : undefined;
1792
- const providerSwitchRecommended = shouldSwitchProvider(currentProvider, bestProvider, options);
1805
+ const bestComparableProvider = currentProvider?.role ? bestProviders.find((provider) => provider.role === currentProvider.role) : bestProvider;
1806
+ const providerSwitchRecommended = shouldSwitchProvider(currentProvider, bestComparableProvider, options);
1793
1807
  const recommendations = [];
1794
1808
  const issues = [];
1795
1809
  if (report.ok !== true) {
@@ -1797,19 +1811,20 @@ var buildVoiceProofTrendRecommendationReport = (report, options = {}) => {
1797
1811
  }
1798
1812
  recommendations.push({
1799
1813
  evidence: {
1800
- bestProviderId: bestProvider?.id,
1801
- bestProviderP95Ms: bestProvider?.p95Ms,
1814
+ bestProviderId: bestComparableProvider?.id ?? bestProvider?.id,
1815
+ bestProviderMix: formatProviderMix(bestProviders),
1816
+ bestProviderP95Ms: bestComparableProvider?.p95Ms ?? bestProvider?.p95Ms,
1802
1817
  budgetMs: budgets.maxProviderP95Ms,
1803
1818
  currentProviderId: currentProvider?.id ?? options.currentProviderId,
1804
1819
  currentProviderP95Ms: currentProvider?.p95Ms,
1805
1820
  providerComparisonCount: providers.length,
1806
1821
  providerP95Ms: maxProviderP95Ms
1807
1822
  },
1808
- nextMove: providers.length > 0 ? providerSwitchRecommended ? `Route latency-sensitive turns to ${bestProvider?.label ?? bestProvider?.id} for this call profile and keep the current path as fallback.` : bestProvider ? `Use ${bestProvider.label ?? bestProvider.id} as the fastest proven provider path for this call profile and keep collecting sustained comparisons.` : "Collect provider-specific sustained samples before making provider-specific routing decisions." : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "Keep the current provider route for latency-sensitive turns and keep collecting sustained proof." : "Route latency-sensitive turns to a faster provider profile or tighten fallback/circuit-breaker budgets before promotion.",
1809
- providerId: bestProvider?.id,
1810
- recommendation: providers.length > 0 ? providerSwitchRecommended ? `Switch latency-sensitive routing to ${bestProvider?.label ?? bestProvider?.id}` : bestProvider ? `Prefer ${bestProvider.label ?? bestProvider.id} for this call profile` : "Collect provider-specific latency samples" : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "Keep current provider path" : "Change provider routing for latency-sensitive traffic",
1811
- role: bestProvider?.role,
1812
- status: providers.length > 0 ? providerSwitchRecommended ? "warn" : bestProvider?.status ?? "fail" : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "pass" : maxProviderP95Ms === undefined ? "fail" : "warn",
1823
+ nextMove: providers.length > 0 ? providerSwitchRecommended ? `Route latency-sensitive ${currentProvider?.role ?? "provider"} traffic to ${bestComparableProvider?.label ?? bestComparableProvider?.id} for this call profile and keep the current path as fallback.` : bestProviders.length > 0 ? `Use the fastest proven provider mix for this call profile: ${formatProviderMix(bestProviders)}.` : "Collect provider-specific sustained samples before making provider-specific routing decisions." : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "Keep the current provider route for latency-sensitive turns and keep collecting sustained proof." : "Route latency-sensitive turns to a faster provider profile or tighten fallback/circuit-breaker budgets before promotion.",
1824
+ providerId: providerSwitchRecommended ? bestComparableProvider?.id : bestProviders.length === 1 ? bestProviders[0]?.id : undefined,
1825
+ recommendation: providers.length > 0 ? providerSwitchRecommended ? `Switch latency-sensitive ${currentProvider?.role ?? "provider"} routing to ${bestComparableProvider?.label ?? bestComparableProvider?.id}` : bestProviders.length > 0 ? "Prefer the fastest proven provider mix for this call profile" : "Collect provider-specific latency samples" : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "Keep current provider path" : "Change provider routing for latency-sensitive traffic",
1826
+ role: bestComparableProvider?.role,
1827
+ status: providers.length > 0 ? providerSwitchRecommended ? "warn" : bestProviders.length > 0 ? "pass" : bestProvider?.status ?? "fail" : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "pass" : maxProviderP95Ms === undefined ? "fail" : "warn",
1813
1828
  surface: "provider-path"
1814
1829
  });
1815
1830
  const runtimePass = withinBudget(runtimeChannel.maxFirstAudioLatencyMs, budgets.maxRuntimeFirstAudioLatencyMs) && withinBudget(runtimeChannel.maxInterruptionP95Ms, budgets.maxRuntimeInterruptionP95Ms) && withinBudget(runtimeChannel.maxJitterMs, budgets.maxRuntimeJitterMs) && withinBudget(runtimeChannel.maxTimestampDriftMs, budgets.maxRuntimeTimestampDriftMs) && withinBudget(runtimeChannel.maxBackpressureEvents, budgets.maxRuntimeBackpressureEvents);
@@ -1853,6 +1868,7 @@ var buildVoiceProofTrendRecommendationReport = (report, options = {}) => {
1853
1868
  const status = issues.length > 0 ? "fail" : worstRecommendationStatus(recommendations);
1854
1869
  return {
1855
1870
  bestProvider,
1871
+ bestProviders,
1856
1872
  generatedAt: new Date().toISOString(),
1857
1873
  issues,
1858
1874
  ok: status !== "fail",
@@ -1876,7 +1892,7 @@ var renderVoiceProofTrendRecommendationMarkdown = (report, title = "Voice Provid
1876
1892
  "",
1877
1893
  `- Status: ${report.status}`,
1878
1894
  `- Source: ${report.source}`,
1879
- `- Best provider: ${report.bestProvider?.label ?? report.bestProvider?.id ?? "n/a"}`,
1895
+ `- Best provider mix: ${formatProviderMix(report.bestProviders)}`,
1880
1896
  `- Provider comparisons: ${String(report.summary.providerComparisonCount)}`,
1881
1897
  `- Recommended actions: ${String(report.summary.recommendedActions)}`,
1882
1898
  "",
@@ -1899,7 +1915,7 @@ var renderVoiceProofTrendRecommendationHTML = (report, title = "Voice Provider R
1899
1915
  const cards = report.recommendations.map((recommendation) => `<article class="${escapeHtml5(recommendation.status)}"><p class="eyebrow">${escapeHtml5(recommendation.surface)} \xB7 ${escapeHtml5(recommendation.status)}</p><h2>${escapeHtml5(recommendation.recommendation)}</h2><p>${escapeHtml5(recommendation.nextMove)}</p><pre>${escapeHtml5(JSON.stringify(recommendation.evidence, null, 2))}</pre></article>`).join("");
1900
1916
  const issues = report.issues.length === 0 ? "<li>None</li>" : report.issues.map((issue) => `<li>${escapeHtml5(issue)}</li>`).join("");
1901
1917
  const providerRows = report.providers.length === 0 ? "<li>No provider-specific samples were present.</li>" : report.providers.map((provider) => `<li><strong>#${String(provider.rank)} ${escapeHtml5(provider.label ?? provider.id)}</strong><span>${escapeHtml5(provider.role ?? "provider")} \xB7 ${escapeHtml5(provider.status)} \xB7 p95 ${escapeHtml5(provider.p95Ms ?? "n/a")}ms \xB7 ${escapeHtml5(provider.samples ?? "n/a")} sample(s)</span><small>${escapeHtml5(provider.nextMove)}</small></li>`).join("");
1902
- return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml5(title)}</title><style>body{background:#101418;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero,article{background:#17201d;border:1px solid #2e3d36;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12))}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.7rem);letter-spacing:-.06em;line-height:.92;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{border:1px solid #42534a;border-radius:999px;padding:8px 12px}.pass{border-color:rgba(34,197,94,.55)}.warn{border-color:rgba(245,158,11,.7)}.fail{border-color:rgba(239,68,68,.75)}pre{background:#0b1110;border-radius:14px;overflow:auto;padding:12px}a{color:#5eead4}li{margin:.45rem 0}li span,li small{display:block;color:#c9d3ca}</style></head><body><main><section class="hero"><p class="eyebrow">Sustained proof recommendations</p><h1>${escapeHtml5(title)}</h1><p>Generated ${escapeHtml5(report.generatedAt)} from ${escapeHtml5(report.source)}.</p><div class="summary"><span class="pill">Status ${escapeHtml5(report.status)}</span><span class="pill">Provider ${report.summary.keepCurrentProviderPath ? "keep" : "change"}</span><span class="pill">Best ${escapeHtml5(report.bestProvider?.label ?? report.bestProvider?.id ?? "n/a")}</span><span class="pill">Runtime ${report.summary.keepCurrentRuntimeChannel ? "keep" : "tune"}</span><span class="pill">${String(report.summary.recommendedActions)} action(s)</span></div></section>${cards}<section class="hero"><h2>Provider Comparison</h2><ul>${providerRows}</ul></section><section class="hero"><h2>Issues</h2><ul>${issues}</ul></section></main></body></html>`;
1918
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml5(title)}</title><style>body{background:#101418;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero,article{background:#17201d;border:1px solid #2e3d36;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12))}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.7rem);letter-spacing:-.06em;line-height:.92;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{border:1px solid #42534a;border-radius:999px;padding:8px 12px}.pass{border-color:rgba(34,197,94,.55)}.warn{border-color:rgba(245,158,11,.7)}.fail{border-color:rgba(239,68,68,.75)}pre{background:#0b1110;border-radius:14px;overflow:auto;padding:12px}a{color:#5eead4}li{margin:.45rem 0}li span,li small{display:block;color:#c9d3ca}</style></head><body><main><section class="hero"><p class="eyebrow">Sustained proof recommendations</p><h1>${escapeHtml5(title)}</h1><p>Generated ${escapeHtml5(report.generatedAt)} from ${escapeHtml5(report.source)}.</p><div class="summary"><span class="pill">Status ${escapeHtml5(report.status)}</span><span class="pill">Provider ${report.summary.keepCurrentProviderPath ? "keep" : "change"}</span><span class="pill">Best mix ${escapeHtml5(formatProviderMix(report.bestProviders))}</span><span class="pill">Runtime ${report.summary.keepCurrentRuntimeChannel ? "keep" : "tune"}</span><span class="pill">${String(report.summary.recommendedActions)} action(s)</span></div></section>${cards}<section class="hero"><h2>Provider Comparison</h2><ul>${providerRows}</ul></section><section class="hero"><h2>Issues</h2><ul>${issues}</ul></section></main></body></html>`;
1903
1919
  };
1904
1920
  var createVoiceProofTrendRecommendationRoutes = (options) => {
1905
1921
  const path = options.path ?? "/api/voice/proof-trend-recommendations";
package/dist/vue/index.js CHANGED
@@ -1593,6 +1593,18 @@ var shouldSwitchProvider = (current, best, options) => {
1593
1593
  const improvementRatio = current.p95Ms > 0 ? improvementMs / current.p95Ms : 0;
1594
1594
  return improvementMs >= minImprovementMs || improvementRatio >= minImprovementRatio;
1595
1595
  };
1596
+ var bestProviderByRole = (providers) => {
1597
+ const best = new Map;
1598
+ for (const provider of providers) {
1599
+ const role = provider.role ?? provider.id;
1600
+ const existing = best.get(role);
1601
+ if (!existing || compareProviders(provider, existing) < 0) {
1602
+ best.set(role, provider);
1603
+ }
1604
+ }
1605
+ return [...best.values()].sort((left, right) => String(left.role ?? left.id).localeCompare(String(right.role ?? right.id)));
1606
+ };
1607
+ var formatProviderMix = (providers) => providers.length === 0 ? "n/a" : providers.map((provider) => provider.role ? `${provider.role.toUpperCase()} ${provider.label ?? provider.id}` : provider.label ?? provider.id).join(", ");
1596
1608
  var evaluateVoiceProofTrendEvidence = (report, input = {}) => {
1597
1609
  const issues = [];
1598
1610
  const requiredStatus = input.requireStatus ?? "pass";
@@ -1709,8 +1721,10 @@ var buildVoiceProofTrendRecommendationReport = (report, options = {}) => {
1709
1721
  const runtimeChannel = readProofTrendRuntimeChannel(report);
1710
1722
  const providers = summarizeProofTrendProviders(report, budgets.maxProviderP95Ms);
1711
1723
  const bestProvider = providers.find((provider) => provider.status === "pass") ?? providers[0];
1724
+ const bestProviders = bestProviderByRole(providers).filter((provider) => provider.status === "pass");
1712
1725
  const currentProvider = options.currentProviderId ? providers.find((provider) => provider.id === options.currentProviderId) : undefined;
1713
- const providerSwitchRecommended = shouldSwitchProvider(currentProvider, bestProvider, options);
1726
+ const bestComparableProvider = currentProvider?.role ? bestProviders.find((provider) => provider.role === currentProvider.role) : bestProvider;
1727
+ const providerSwitchRecommended = shouldSwitchProvider(currentProvider, bestComparableProvider, options);
1714
1728
  const recommendations = [];
1715
1729
  const issues = [];
1716
1730
  if (report.ok !== true) {
@@ -1718,19 +1732,20 @@ var buildVoiceProofTrendRecommendationReport = (report, options = {}) => {
1718
1732
  }
1719
1733
  recommendations.push({
1720
1734
  evidence: {
1721
- bestProviderId: bestProvider?.id,
1722
- bestProviderP95Ms: bestProvider?.p95Ms,
1735
+ bestProviderId: bestComparableProvider?.id ?? bestProvider?.id,
1736
+ bestProviderMix: formatProviderMix(bestProviders),
1737
+ bestProviderP95Ms: bestComparableProvider?.p95Ms ?? bestProvider?.p95Ms,
1723
1738
  budgetMs: budgets.maxProviderP95Ms,
1724
1739
  currentProviderId: currentProvider?.id ?? options.currentProviderId,
1725
1740
  currentProviderP95Ms: currentProvider?.p95Ms,
1726
1741
  providerComparisonCount: providers.length,
1727
1742
  providerP95Ms: maxProviderP95Ms
1728
1743
  },
1729
- nextMove: providers.length > 0 ? providerSwitchRecommended ? `Route latency-sensitive turns to ${bestProvider?.label ?? bestProvider?.id} for this call profile and keep the current path as fallback.` : bestProvider ? `Use ${bestProvider.label ?? bestProvider.id} as the fastest proven provider path for this call profile and keep collecting sustained comparisons.` : "Collect provider-specific sustained samples before making provider-specific routing decisions." : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "Keep the current provider route for latency-sensitive turns and keep collecting sustained proof." : "Route latency-sensitive turns to a faster provider profile or tighten fallback/circuit-breaker budgets before promotion.",
1730
- providerId: bestProvider?.id,
1731
- recommendation: providers.length > 0 ? providerSwitchRecommended ? `Switch latency-sensitive routing to ${bestProvider?.label ?? bestProvider?.id}` : bestProvider ? `Prefer ${bestProvider.label ?? bestProvider.id} for this call profile` : "Collect provider-specific latency samples" : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "Keep current provider path" : "Change provider routing for latency-sensitive traffic",
1732
- role: bestProvider?.role,
1733
- status: providers.length > 0 ? providerSwitchRecommended ? "warn" : bestProvider?.status ?? "fail" : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "pass" : maxProviderP95Ms === undefined ? "fail" : "warn",
1744
+ nextMove: providers.length > 0 ? providerSwitchRecommended ? `Route latency-sensitive ${currentProvider?.role ?? "provider"} traffic to ${bestComparableProvider?.label ?? bestComparableProvider?.id} for this call profile and keep the current path as fallback.` : bestProviders.length > 0 ? `Use the fastest proven provider mix for this call profile: ${formatProviderMix(bestProviders)}.` : "Collect provider-specific sustained samples before making provider-specific routing decisions." : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "Keep the current provider route for latency-sensitive turns and keep collecting sustained proof." : "Route latency-sensitive turns to a faster provider profile or tighten fallback/circuit-breaker budgets before promotion.",
1745
+ providerId: providerSwitchRecommended ? bestComparableProvider?.id : bestProviders.length === 1 ? bestProviders[0]?.id : undefined,
1746
+ recommendation: providers.length > 0 ? providerSwitchRecommended ? `Switch latency-sensitive ${currentProvider?.role ?? "provider"} routing to ${bestComparableProvider?.label ?? bestComparableProvider?.id}` : bestProviders.length > 0 ? "Prefer the fastest proven provider mix for this call profile" : "Collect provider-specific latency samples" : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "Keep current provider path" : "Change provider routing for latency-sensitive traffic",
1747
+ role: bestComparableProvider?.role,
1748
+ status: providers.length > 0 ? providerSwitchRecommended ? "warn" : bestProviders.length > 0 ? "pass" : bestProvider?.status ?? "fail" : withinBudget(maxProviderP95Ms, budgets.maxProviderP95Ms) ? "pass" : maxProviderP95Ms === undefined ? "fail" : "warn",
1734
1749
  surface: "provider-path"
1735
1750
  });
1736
1751
  const runtimePass = withinBudget(runtimeChannel.maxFirstAudioLatencyMs, budgets.maxRuntimeFirstAudioLatencyMs) && withinBudget(runtimeChannel.maxInterruptionP95Ms, budgets.maxRuntimeInterruptionP95Ms) && withinBudget(runtimeChannel.maxJitterMs, budgets.maxRuntimeJitterMs) && withinBudget(runtimeChannel.maxTimestampDriftMs, budgets.maxRuntimeTimestampDriftMs) && withinBudget(runtimeChannel.maxBackpressureEvents, budgets.maxRuntimeBackpressureEvents);
@@ -1774,6 +1789,7 @@ var buildVoiceProofTrendRecommendationReport = (report, options = {}) => {
1774
1789
  const status = issues.length > 0 ? "fail" : worstRecommendationStatus(recommendations);
1775
1790
  return {
1776
1791
  bestProvider,
1792
+ bestProviders,
1777
1793
  generatedAt: new Date().toISOString(),
1778
1794
  issues,
1779
1795
  ok: status !== "fail",
@@ -1797,7 +1813,7 @@ var renderVoiceProofTrendRecommendationMarkdown = (report, title = "Voice Provid
1797
1813
  "",
1798
1814
  `- Status: ${report.status}`,
1799
1815
  `- Source: ${report.source}`,
1800
- `- Best provider: ${report.bestProvider?.label ?? report.bestProvider?.id ?? "n/a"}`,
1816
+ `- Best provider mix: ${formatProviderMix(report.bestProviders)}`,
1801
1817
  `- Provider comparisons: ${String(report.summary.providerComparisonCount)}`,
1802
1818
  `- Recommended actions: ${String(report.summary.recommendedActions)}`,
1803
1819
  "",
@@ -1820,7 +1836,7 @@ var renderVoiceProofTrendRecommendationHTML = (report, title = "Voice Provider R
1820
1836
  const cards = report.recommendations.map((recommendation) => `<article class="${escapeHtml5(recommendation.status)}"><p class="eyebrow">${escapeHtml5(recommendation.surface)} \xB7 ${escapeHtml5(recommendation.status)}</p><h2>${escapeHtml5(recommendation.recommendation)}</h2><p>${escapeHtml5(recommendation.nextMove)}</p><pre>${escapeHtml5(JSON.stringify(recommendation.evidence, null, 2))}</pre></article>`).join("");
1821
1837
  const issues = report.issues.length === 0 ? "<li>None</li>" : report.issues.map((issue) => `<li>${escapeHtml5(issue)}</li>`).join("");
1822
1838
  const providerRows = report.providers.length === 0 ? "<li>No provider-specific samples were present.</li>" : report.providers.map((provider) => `<li><strong>#${String(provider.rank)} ${escapeHtml5(provider.label ?? provider.id)}</strong><span>${escapeHtml5(provider.role ?? "provider")} \xB7 ${escapeHtml5(provider.status)} \xB7 p95 ${escapeHtml5(provider.p95Ms ?? "n/a")}ms \xB7 ${escapeHtml5(provider.samples ?? "n/a")} sample(s)</span><small>${escapeHtml5(provider.nextMove)}</small></li>`).join("");
1823
- return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml5(title)}</title><style>body{background:#101418;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero,article{background:#17201d;border:1px solid #2e3d36;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12))}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.7rem);letter-spacing:-.06em;line-height:.92;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{border:1px solid #42534a;border-radius:999px;padding:8px 12px}.pass{border-color:rgba(34,197,94,.55)}.warn{border-color:rgba(245,158,11,.7)}.fail{border-color:rgba(239,68,68,.75)}pre{background:#0b1110;border-radius:14px;overflow:auto;padding:12px}a{color:#5eead4}li{margin:.45rem 0}li span,li small{display:block;color:#c9d3ca}</style></head><body><main><section class="hero"><p class="eyebrow">Sustained proof recommendations</p><h1>${escapeHtml5(title)}</h1><p>Generated ${escapeHtml5(report.generatedAt)} from ${escapeHtml5(report.source)}.</p><div class="summary"><span class="pill">Status ${escapeHtml5(report.status)}</span><span class="pill">Provider ${report.summary.keepCurrentProviderPath ? "keep" : "change"}</span><span class="pill">Best ${escapeHtml5(report.bestProvider?.label ?? report.bestProvider?.id ?? "n/a")}</span><span class="pill">Runtime ${report.summary.keepCurrentRuntimeChannel ? "keep" : "tune"}</span><span class="pill">${String(report.summary.recommendedActions)} action(s)</span></div></section>${cards}<section class="hero"><h2>Provider Comparison</h2><ul>${providerRows}</ul></section><section class="hero"><h2>Issues</h2><ul>${issues}</ul></section></main></body></html>`;
1839
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml5(title)}</title><style>body{background:#101418;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero,article{background:#17201d;border:1px solid #2e3d36;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.12))}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.7rem);letter-spacing:-.06em;line-height:.92;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{border:1px solid #42534a;border-radius:999px;padding:8px 12px}.pass{border-color:rgba(34,197,94,.55)}.warn{border-color:rgba(245,158,11,.7)}.fail{border-color:rgba(239,68,68,.75)}pre{background:#0b1110;border-radius:14px;overflow:auto;padding:12px}a{color:#5eead4}li{margin:.45rem 0}li span,li small{display:block;color:#c9d3ca}</style></head><body><main><section class="hero"><p class="eyebrow">Sustained proof recommendations</p><h1>${escapeHtml5(title)}</h1><p>Generated ${escapeHtml5(report.generatedAt)} from ${escapeHtml5(report.source)}.</p><div class="summary"><span class="pill">Status ${escapeHtml5(report.status)}</span><span class="pill">Provider ${report.summary.keepCurrentProviderPath ? "keep" : "change"}</span><span class="pill">Best mix ${escapeHtml5(formatProviderMix(report.bestProviders))}</span><span class="pill">Runtime ${report.summary.keepCurrentRuntimeChannel ? "keep" : "tune"}</span><span class="pill">${String(report.summary.recommendedActions)} action(s)</span></div></section>${cards}<section class="hero"><h2>Provider Comparison</h2><ul>${providerRows}</ul></section><section class="hero"><h2>Issues</h2><ul>${issues}</ul></section></main></body></html>`;
1824
1840
  };
1825
1841
  var createVoiceProofTrendRecommendationRoutes = (options) => {
1826
1842
  const path = options.path ?? "/api/voice/proof-trend-recommendations";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.335",
3
+ "version": "0.0.22-beta.336",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",