@absolutejs/voice 0.0.22-beta.340 → 0.0.22-beta.342

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.
@@ -3915,6 +3915,22 @@ var aggregateProofTrendRuntimeChannel = (channels) => {
3915
3915
  };
3916
3916
  };
3917
3917
  var readProofTrendProviders = (reports) => aggregateProofTrendProviders(reports.flatMap((report) => report.summary.providers && report.summary.providers.length > 0 ? report.summary.providers : report.cycles.flatMap((cycle) => cycle.providers ?? [])));
3918
+ var exceedsProofTrendBudget = (value, budget) => value !== undefined && (!Number.isFinite(value) || value > budget);
3919
+ var readProofTrendProfileStatus = (profile, budgets) => {
3920
+ const runtimeChannel = profile.runtimeChannel;
3921
+ const hasBudgetEvidence = profile.maxLiveP95Ms !== undefined || profile.maxProviderP95Ms !== undefined || profile.maxTurnP95Ms !== undefined || profile.providers?.some((provider) => provider.p95Ms !== undefined) === true || runtimeChannel?.maxFirstAudioLatencyMs !== undefined || runtimeChannel?.maxInterruptionP95Ms !== undefined || runtimeChannel?.maxJitterMs !== undefined || runtimeChannel?.maxTimestampDriftMs !== undefined || runtimeChannel?.maxBackpressureEvents !== undefined;
3922
+ const budgetFailed = exceedsProofTrendBudget(profile.maxLiveP95Ms, budgets.maxLiveP95Ms) || exceedsProofTrendBudget(profile.maxProviderP95Ms, budgets.maxProviderP95Ms) || exceedsProofTrendBudget(profile.maxTurnP95Ms, budgets.maxTurnP95Ms) || exceedsProofTrendBudget(runtimeChannel?.maxFirstAudioLatencyMs, budgets.maxRuntimeFirstAudioLatencyMs) || exceedsProofTrendBudget(runtimeChannel?.maxInterruptionP95Ms, budgets.maxRuntimeInterruptionP95Ms) || exceedsProofTrendBudget(runtimeChannel?.maxJitterMs, budgets.maxRuntimeJitterMs) || exceedsProofTrendBudget(runtimeChannel?.maxTimestampDriftMs, budgets.maxRuntimeTimestampDriftMs) || exceedsProofTrendBudget(runtimeChannel?.maxBackpressureEvents, 0);
3923
+ if (budgetFailed || !hasBudgetEvidence && profile.status === "fail") {
3924
+ return "fail";
3925
+ }
3926
+ if (profile.status === "warn" || runtimeChannel?.status === "warn" || profile.providers?.some((provider) => provider.status === "warn") === true) {
3927
+ return "warn";
3928
+ }
3929
+ if (hasBudgetEvidence || profile.status === "pass" || runtimeChannel?.status === "pass" || profile.providers?.some((provider) => provider.status === "pass") === true) {
3930
+ return "pass";
3931
+ }
3932
+ return;
3933
+ };
3918
3934
  var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
3919
3935
  const reports = Array.isArray(input) ? input : [input];
3920
3936
  const definitions = options.profiles ?? DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS;
@@ -3925,6 +3941,15 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
3925
3941
  const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
3926
3942
  const runtimeJitterCap = options.maxRuntimeJitterMs ?? 30;
3927
3943
  const runtimeTimestampDriftCap = options.maxRuntimeTimestampDriftMs ?? 800;
3944
+ const budgets = {
3945
+ maxLiveP95Ms: liveCap,
3946
+ maxProviderP95Ms: providerCap,
3947
+ maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
3948
+ maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
3949
+ maxRuntimeJitterMs: runtimeJitterCap,
3950
+ maxRuntimeTimestampDriftMs: runtimeTimestampDriftCap,
3951
+ maxTurnP95Ms: turnCap
3952
+ };
3928
3953
  return definitions.map((definition) => {
3929
3954
  const historicalProfiles = reports.flatMap((report) => report.summary.profiles?.filter((profile) => profile.id === definition.id) ?? []);
3930
3955
  if (historicalProfiles.length > 0) {
@@ -3934,7 +3959,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
3934
3959
  profiles: [definition]
3935
3960
  }) : [];
3936
3961
  const profiles = [...historicalProfiles, ...derivedProfiles];
3937
- return {
3962
+ const aggregatedProfile = {
3938
3963
  description: definition.description ?? profiles.find(Boolean)?.description,
3939
3964
  id: definition.id,
3940
3965
  label: definition.label ?? profiles.find(Boolean)?.label,
@@ -3942,12 +3967,15 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
3942
3967
  maxProviderP95Ms: maxNumber(profiles.map((profile) => profile.maxProviderP95Ms)),
3943
3968
  maxTurnP95Ms: maxNumber(profiles.map((profile) => profile.maxTurnP95Ms)),
3944
3969
  providers: aggregateProofTrendProviders(profiles.flatMap((profile) => profile.providers ?? [])),
3945
- runtimeChannel: aggregateProofTrendRuntimeChannel(profiles.map((profile) => profile.runtimeChannel).filter((channel) => channel !== undefined)),
3946
- status: profiles.some((profile) => profile.status === "fail") ? "fail" : profiles.some((profile) => profile.status === "warn") ? "warn" : profiles.every((profile) => profile.status === "pass") ? "pass" : undefined
3970
+ runtimeChannel: aggregateProofTrendRuntimeChannel(profiles.map((profile) => profile.runtimeChannel).filter((channel) => channel !== undefined))
3971
+ };
3972
+ return {
3973
+ ...aggregatedProfile,
3974
+ status: readProofTrendProfileStatus(aggregatedProfile, budgets)
3947
3975
  };
3948
3976
  }
3949
3977
  const runtimeChannel = aggregateProofTrendRuntimeChannel(reports.map((report) => readProofTrendRuntimeChannel(report)).filter((channel) => Object.values(channel).some((value) => value !== undefined)));
3950
- return {
3978
+ const derivedProfile = {
3951
3979
  description: definition.description,
3952
3980
  id: definition.id,
3953
3981
  label: definition.label,
@@ -3963,9 +3991,95 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
3963
3991
  maxTimestampDriftMs: addProofTrendProfileOffset(runtimeChannel.maxTimestampDriftMs, definition.runtimeOffsetMs, definition.maxRuntimeTimestampDriftMs ?? runtimeTimestampDriftCap),
3964
3992
  samples: runtimeChannel.samples,
3965
3993
  status: runtimeChannel.status
3966
- },
3967
- status: reports.some((report) => report.status === "fail" || !report.ok) ? "fail" : reports.some((report) => report.status === "warn") ? "warn" : reports.every((report) => report.ok) ? "pass" : undefined
3994
+ }
3995
+ };
3996
+ return {
3997
+ ...derivedProfile,
3998
+ status: readProofTrendProfileStatus(derivedProfile, budgets)
3999
+ };
4000
+ });
4001
+ };
4002
+ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
4003
+ const providerCap = options.maxProviderP95Ms ?? 1000;
4004
+ const liveCap = options.maxLiveP95Ms ?? 800;
4005
+ const turnCap = options.maxTurnP95Ms ?? 700;
4006
+ const runtimeFirstAudioCap = options.maxRuntimeFirstAudioLatencyMs ?? 600;
4007
+ const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
4008
+ const runtimeJitterCap = options.maxRuntimeJitterMs ?? 30;
4009
+ const runtimeTimestampDriftCap = options.maxRuntimeTimestampDriftMs ?? 800;
4010
+ const budgets = {
4011
+ maxLiveP95Ms: liveCap,
4012
+ maxProviderP95Ms: providerCap,
4013
+ maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
4014
+ maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
4015
+ maxRuntimeJitterMs: runtimeJitterCap,
4016
+ maxRuntimeTimestampDriftMs: runtimeTimestampDriftCap,
4017
+ maxTurnP95Ms: turnCap
4018
+ };
4019
+ const definitionById = new Map((options.profiles ?? DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS).map((profile) => [profile.id, profile]));
4020
+ for (const evidence of options.evidence) {
4021
+ if (!definitionById.has(evidence.profileId)) {
4022
+ definitionById.set(evidence.profileId, {
4023
+ description: evidence.profileDescription,
4024
+ id: evidence.profileId,
4025
+ label: evidence.profileLabel
4026
+ });
4027
+ }
4028
+ }
4029
+ const profiles = [];
4030
+ for (const definition of definitionById.values()) {
4031
+ const matchingEvidence = options.evidence.filter((evidence) => evidence.profileId === definition.id);
4032
+ if (matchingEvidence.length === 0) {
4033
+ continue;
4034
+ }
4035
+ const profile = {
4036
+ description: definition.description ?? matchingEvidence.find((evidence) => evidence.profileDescription)?.profileDescription,
4037
+ id: definition.id,
4038
+ label: definition.label ?? matchingEvidence.find((evidence) => evidence.profileLabel)?.profileLabel,
4039
+ maxLiveP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.liveP95Ms)),
4040
+ maxProviderP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.providerP95Ms)),
4041
+ maxTurnP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.turnP95Ms)),
4042
+ providers: aggregateProofTrendProviders(matchingEvidence.flatMap((evidence) => evidence.providers ?? [])),
4043
+ runtimeChannel: aggregateProofTrendRuntimeChannel(matchingEvidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined))
3968
4044
  };
4045
+ profiles.push({
4046
+ ...profile,
4047
+ status: readProofTrendProfileStatus(profile, budgets)
4048
+ });
4049
+ }
4050
+ const cycles = options.evidence.map((evidence, index) => ({
4051
+ at: evidence.generatedAt,
4052
+ cycle: index + 1,
4053
+ liveLatency: evidence.liveP95Ms === undefined ? undefined : { p95Ms: evidence.liveP95Ms, samples: 1 },
4054
+ ok: evidence.ok !== false,
4055
+ providers: evidence.providers,
4056
+ runtimeChannel: evidence.runtimeChannel,
4057
+ turnLatency: evidence.turnP95Ms === undefined ? undefined : {
4058
+ p95Ms: evidence.turnP95Ms,
4059
+ samples: 1,
4060
+ status: evidence.turnP95Ms <= turnCap ? "pass" : "fail"
4061
+ }
4062
+ }));
4063
+ const summary = {
4064
+ cycles: options.evidence.length,
4065
+ maxLiveP95Ms: maxNumber(options.evidence.map((evidence) => evidence.liveP95Ms)),
4066
+ maxProviderP95Ms: maxNumber(options.evidence.map((evidence) => evidence.providerP95Ms)),
4067
+ maxTurnP95Ms: maxNumber(options.evidence.map((evidence) => evidence.turnP95Ms)),
4068
+ profiles,
4069
+ providers: aggregateProofTrendProviders(options.evidence.flatMap((evidence) => evidence.providers ?? [])),
4070
+ runtimeChannel: aggregateProofTrendRuntimeChannel(options.evidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined))
4071
+ };
4072
+ return buildVoiceProofTrendReport({
4073
+ baseUrl: options.baseUrl,
4074
+ cycles,
4075
+ generatedAt: options.generatedAt,
4076
+ maxAgeMs: options.maxAgeMs,
4077
+ now: options.now,
4078
+ ok: options.evidence.length > 0 && options.evidence.every((evidence) => evidence.ok !== false) && profiles.every((profile) => profile.status !== "fail"),
4079
+ outputDir: options.outputDir,
4080
+ runId: options.runId,
4081
+ source: options.source,
4082
+ summary
3969
4083
  });
3970
4084
  };
3971
4085
  var normalizeProviderStatus = (status) => status === "pass" ? "pass" : status === "fail" ? "fail" : "warn";
package/dist/index.d.ts CHANGED
@@ -28,10 +28,10 @@ export { assertVoicePlatformCoverage, buildVoicePlatformCoverageSummary, createV
28
28
  export { assertVoiceCompetitiveCoverage, buildVoiceCompetitiveCoverageReport, createVoiceCompetitiveCoverageRoutes, evaluateVoiceCompetitiveCoverage, renderVoiceCompetitiveCoverageHTML, renderVoiceCompetitiveCoverageMarkdown } from './competitiveCoverage';
29
29
  export type { VoiceCompetitiveCoverageAssertionInput, VoiceCompetitiveCoverageAssertionReport, VoiceCompetitiveCoverageIssue, VoiceCompetitiveCoverageLevel, VoiceCompetitiveCoverageReport, VoiceCompetitiveCoverageReportInput, VoiceCompetitiveCoverageRoutesOptions, VoiceCompetitiveCoverageStatus, VoiceCompetitiveCoverageSummary, VoiceCompetitiveDepthLevel, VoiceCompetitiveEvidence, VoiceCompetitiveSurface } from './competitiveCoverage';
30
30
  export type { VoicePlatformCoverageAssertionInput, VoicePlatformCoverageAssertionReport, VoicePlatformCoverageEvidence, VoicePlatformCoverageRoutesOptions, VoicePlatformCoverageStatus, VoicePlatformCoverageSummary, VoicePlatformCoverageSummaryInput, VoicePlatformCoverageSurface } from './platformCoverage';
31
- export { assertVoiceProofTrendEvidence, buildEmptyVoiceProofTrendReport, buildVoiceProofTrendProfileSummaries, buildVoiceProofTrendRecommendationReport, buildVoiceProofTrendReport, createVoiceProofTrendRecommendationRoutes, createVoiceProofTrendRoutes, DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS, DEFAULT_VOICE_PROOF_TRENDS_MAX_AGE_MS, evaluateVoiceProofTrendEvidence, formatVoiceProofTrendAge, normalizeVoiceProofTrendReport, readVoiceProofTrendReportFile, renderVoiceProofTrendRecommendationHTML, renderVoiceProofTrendRecommendationMarkdown } from './proofTrends';
31
+ export { assertVoiceProofTrendEvidence, buildEmptyVoiceProofTrendReport, buildVoiceProofTrendProfileSummaries, buildVoiceProofTrendRecommendationReport, buildVoiceProofTrendReportFromRealCallProfiles, buildVoiceProofTrendReport, createVoiceProofTrendRecommendationRoutes, createVoiceProofTrendRoutes, DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS, DEFAULT_VOICE_PROOF_TRENDS_MAX_AGE_MS, evaluateVoiceProofTrendEvidence, formatVoiceProofTrendAge, normalizeVoiceProofTrendReport, readVoiceProofTrendReportFile, renderVoiceProofTrendRecommendationHTML, renderVoiceProofTrendRecommendationMarkdown } from './proofTrends';
32
32
  export { buildVoiceProviderDecisionTraceReport, createVoiceProviderDecisionTraceEvent, createVoiceProviderDecisionTraceRoutes, listVoiceProviderDecisionTraces, renderVoiceProviderDecisionTraceHTML, renderVoiceProviderDecisionTraceMarkdown } from './providerDecisionTraces';
33
33
  export type { VoiceProviderDecisionStatus, VoiceProviderDecisionSurfaceReport, VoiceProviderDecisionTrace, VoiceProviderDecisionTraceInput, VoiceProviderDecisionTraceIssue, VoiceProviderDecisionTraceReport, VoiceProviderDecisionTraceReportOptions, VoiceProviderDecisionTraceRoutesOptions } from './providerDecisionTraces';
34
- export type { VoiceProofTrendAssertionInput, VoiceProofTrendAssertionReport, VoiceProofTrendCycle, VoiceProofTrendProfileDefinition, VoiceProofTrendProfileRecommendation, VoiceProofTrendProfileSummaryOptions, VoiceProofTrendProfileSummary, VoiceProofTrendProviderRecommendation, VoiceProofTrendProviderSummary, VoiceProofTrendRecommendation, VoiceProofTrendRecommendationOptions, VoiceProofTrendRecommendationReport, VoiceProofTrendRecommendationRoutesOptions, VoiceProofTrendRecommendationStatus, VoiceProofTrendRecommendationSurface, VoiceProofTrendReport, VoiceProofTrendReportInput, VoiceProofTrendRoutesOptions, VoiceProofTrendStatus, VoiceProofTrendSummary } from './proofTrends';
34
+ export type { VoiceProofTrendAssertionInput, VoiceProofTrendAssertionReport, VoiceProofTrendCycle, VoiceProofTrendProfileDefinition, VoiceProofTrendProfileRecommendation, VoiceProofTrendProfileSummaryOptions, VoiceProofTrendProfileSummary, VoiceProofTrendProviderRecommendation, VoiceProofTrendProviderSummary, VoiceProofTrendRecommendation, VoiceProofTrendRecommendationOptions, VoiceProofTrendRecommendationReport, VoiceProofTrendRecommendationRoutesOptions, VoiceProofTrendRecommendationStatus, VoiceProofTrendRecommendationSurface, VoiceProofTrendRealCallProfileEvidence, VoiceProofTrendRealCallProfileReportOptions, VoiceProofTrendReport, VoiceProofTrendReportInput, VoiceProofTrendRoutesOptions, VoiceProofTrendStatus, VoiceProofTrendSummary } from './proofTrends';
35
35
  export { assertVoiceSloCalibration, buildVoiceSloCalibrationReport, buildVoiceSloReadinessThresholdReport, createVoiceSloReadinessThresholdOptions, createVoiceSloReadinessThresholdRoutes, createVoiceSloThresholdProfile, createVoiceSloCalibrationRoutes, renderVoiceSloCalibrationMarkdown, renderVoiceSloReadinessThresholdHTML, renderVoiceSloReadinessThresholdMarkdown } from './sloCalibration';
36
36
  export type { VoiceSloCalibrationMetricKey, VoiceSloCalibrationOptions, VoiceSloCalibrationReport, VoiceSloCalibrationRoutesOptions, VoiceSloCalibrationSample, VoiceSloCalibrationStatus, VoiceSloCalibrationThreshold, VoiceSloCalibrationThresholds, VoiceSloReadinessThresholdReport, VoiceSloReadinessThresholdReportOptions, VoiceSloReadinessThresholdOptions, VoiceSloReadinessThresholdRoutesOptions, VoiceSloThresholdProfile } from './sloCalibration';
37
37
  export { assertVoiceLiveOpsControlEvidence, assertVoiceLiveOpsEvidence, buildVoiceLiveOpsControlState, createVoiceLiveOpsController, createVoiceLiveOpsRoutes, createVoiceMemoryLiveOpsControlStore, evaluateVoiceLiveOpsControlEvidence, evaluateVoiceLiveOpsEvidence, getVoiceLiveOpsControlStatus, VOICE_LIVE_OPS_ACTIONS } from './liveOps';
package/dist/index.js CHANGED
@@ -14632,6 +14632,22 @@ var aggregateProofTrendRuntimeChannel = (channels) => {
14632
14632
  };
14633
14633
  };
14634
14634
  var readProofTrendProviders = (reports) => aggregateProofTrendProviders(reports.flatMap((report) => report.summary.providers && report.summary.providers.length > 0 ? report.summary.providers : report.cycles.flatMap((cycle) => cycle.providers ?? [])));
14635
+ var exceedsProofTrendBudget = (value, budget) => value !== undefined && (!Number.isFinite(value) || value > budget);
14636
+ var readProofTrendProfileStatus = (profile, budgets) => {
14637
+ const runtimeChannel = profile.runtimeChannel;
14638
+ const hasBudgetEvidence = profile.maxLiveP95Ms !== undefined || profile.maxProviderP95Ms !== undefined || profile.maxTurnP95Ms !== undefined || profile.providers?.some((provider) => provider.p95Ms !== undefined) === true || runtimeChannel?.maxFirstAudioLatencyMs !== undefined || runtimeChannel?.maxInterruptionP95Ms !== undefined || runtimeChannel?.maxJitterMs !== undefined || runtimeChannel?.maxTimestampDriftMs !== undefined || runtimeChannel?.maxBackpressureEvents !== undefined;
14639
+ const budgetFailed = exceedsProofTrendBudget(profile.maxLiveP95Ms, budgets.maxLiveP95Ms) || exceedsProofTrendBudget(profile.maxProviderP95Ms, budgets.maxProviderP95Ms) || exceedsProofTrendBudget(profile.maxTurnP95Ms, budgets.maxTurnP95Ms) || exceedsProofTrendBudget(runtimeChannel?.maxFirstAudioLatencyMs, budgets.maxRuntimeFirstAudioLatencyMs) || exceedsProofTrendBudget(runtimeChannel?.maxInterruptionP95Ms, budgets.maxRuntimeInterruptionP95Ms) || exceedsProofTrendBudget(runtimeChannel?.maxJitterMs, budgets.maxRuntimeJitterMs) || exceedsProofTrendBudget(runtimeChannel?.maxTimestampDriftMs, budgets.maxRuntimeTimestampDriftMs) || exceedsProofTrendBudget(runtimeChannel?.maxBackpressureEvents, 0);
14640
+ if (budgetFailed || !hasBudgetEvidence && profile.status === "fail") {
14641
+ return "fail";
14642
+ }
14643
+ if (profile.status === "warn" || runtimeChannel?.status === "warn" || profile.providers?.some((provider) => provider.status === "warn") === true) {
14644
+ return "warn";
14645
+ }
14646
+ if (hasBudgetEvidence || profile.status === "pass" || runtimeChannel?.status === "pass" || profile.providers?.some((provider) => provider.status === "pass") === true) {
14647
+ return "pass";
14648
+ }
14649
+ return;
14650
+ };
14635
14651
  var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
14636
14652
  const reports = Array.isArray(input) ? input : [input];
14637
14653
  const definitions = options.profiles ?? DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS;
@@ -14642,6 +14658,15 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
14642
14658
  const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
14643
14659
  const runtimeJitterCap = options.maxRuntimeJitterMs ?? 30;
14644
14660
  const runtimeTimestampDriftCap = options.maxRuntimeTimestampDriftMs ?? 800;
14661
+ const budgets = {
14662
+ maxLiveP95Ms: liveCap,
14663
+ maxProviderP95Ms: providerCap,
14664
+ maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
14665
+ maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
14666
+ maxRuntimeJitterMs: runtimeJitterCap,
14667
+ maxRuntimeTimestampDriftMs: runtimeTimestampDriftCap,
14668
+ maxTurnP95Ms: turnCap
14669
+ };
14645
14670
  return definitions.map((definition) => {
14646
14671
  const historicalProfiles = reports.flatMap((report) => report.summary.profiles?.filter((profile) => profile.id === definition.id) ?? []);
14647
14672
  if (historicalProfiles.length > 0) {
@@ -14651,7 +14676,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
14651
14676
  profiles: [definition]
14652
14677
  }) : [];
14653
14678
  const profiles = [...historicalProfiles, ...derivedProfiles];
14654
- return {
14679
+ const aggregatedProfile = {
14655
14680
  description: definition.description ?? profiles.find(Boolean)?.description,
14656
14681
  id: definition.id,
14657
14682
  label: definition.label ?? profiles.find(Boolean)?.label,
@@ -14659,12 +14684,15 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
14659
14684
  maxProviderP95Ms: maxNumber(profiles.map((profile) => profile.maxProviderP95Ms)),
14660
14685
  maxTurnP95Ms: maxNumber(profiles.map((profile) => profile.maxTurnP95Ms)),
14661
14686
  providers: aggregateProofTrendProviders(profiles.flatMap((profile) => profile.providers ?? [])),
14662
- runtimeChannel: aggregateProofTrendRuntimeChannel(profiles.map((profile) => profile.runtimeChannel).filter((channel) => channel !== undefined)),
14663
- status: profiles.some((profile) => profile.status === "fail") ? "fail" : profiles.some((profile) => profile.status === "warn") ? "warn" : profiles.every((profile) => profile.status === "pass") ? "pass" : undefined
14687
+ runtimeChannel: aggregateProofTrendRuntimeChannel(profiles.map((profile) => profile.runtimeChannel).filter((channel) => channel !== undefined))
14688
+ };
14689
+ return {
14690
+ ...aggregatedProfile,
14691
+ status: readProofTrendProfileStatus(aggregatedProfile, budgets)
14664
14692
  };
14665
14693
  }
14666
14694
  const runtimeChannel = aggregateProofTrendRuntimeChannel(reports.map((report) => readProofTrendRuntimeChannel(report)).filter((channel) => Object.values(channel).some((value) => value !== undefined)));
14667
- return {
14695
+ const derivedProfile = {
14668
14696
  description: definition.description,
14669
14697
  id: definition.id,
14670
14698
  label: definition.label,
@@ -14680,9 +14708,95 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
14680
14708
  maxTimestampDriftMs: addProofTrendProfileOffset(runtimeChannel.maxTimestampDriftMs, definition.runtimeOffsetMs, definition.maxRuntimeTimestampDriftMs ?? runtimeTimestampDriftCap),
14681
14709
  samples: runtimeChannel.samples,
14682
14710
  status: runtimeChannel.status
14683
- },
14684
- status: reports.some((report) => report.status === "fail" || !report.ok) ? "fail" : reports.some((report) => report.status === "warn") ? "warn" : reports.every((report) => report.ok) ? "pass" : undefined
14711
+ }
14712
+ };
14713
+ return {
14714
+ ...derivedProfile,
14715
+ status: readProofTrendProfileStatus(derivedProfile, budgets)
14716
+ };
14717
+ });
14718
+ };
14719
+ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
14720
+ const providerCap = options.maxProviderP95Ms ?? 1000;
14721
+ const liveCap = options.maxLiveP95Ms ?? 800;
14722
+ const turnCap = options.maxTurnP95Ms ?? 700;
14723
+ const runtimeFirstAudioCap = options.maxRuntimeFirstAudioLatencyMs ?? 600;
14724
+ const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
14725
+ const runtimeJitterCap = options.maxRuntimeJitterMs ?? 30;
14726
+ const runtimeTimestampDriftCap = options.maxRuntimeTimestampDriftMs ?? 800;
14727
+ const budgets = {
14728
+ maxLiveP95Ms: liveCap,
14729
+ maxProviderP95Ms: providerCap,
14730
+ maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
14731
+ maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
14732
+ maxRuntimeJitterMs: runtimeJitterCap,
14733
+ maxRuntimeTimestampDriftMs: runtimeTimestampDriftCap,
14734
+ maxTurnP95Ms: turnCap
14735
+ };
14736
+ const definitionById = new Map((options.profiles ?? DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS).map((profile) => [profile.id, profile]));
14737
+ for (const evidence of options.evidence) {
14738
+ if (!definitionById.has(evidence.profileId)) {
14739
+ definitionById.set(evidence.profileId, {
14740
+ description: evidence.profileDescription,
14741
+ id: evidence.profileId,
14742
+ label: evidence.profileLabel
14743
+ });
14744
+ }
14745
+ }
14746
+ const profiles = [];
14747
+ for (const definition of definitionById.values()) {
14748
+ const matchingEvidence = options.evidence.filter((evidence) => evidence.profileId === definition.id);
14749
+ if (matchingEvidence.length === 0) {
14750
+ continue;
14751
+ }
14752
+ const profile = {
14753
+ description: definition.description ?? matchingEvidence.find((evidence) => evidence.profileDescription)?.profileDescription,
14754
+ id: definition.id,
14755
+ label: definition.label ?? matchingEvidence.find((evidence) => evidence.profileLabel)?.profileLabel,
14756
+ maxLiveP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.liveP95Ms)),
14757
+ maxProviderP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.providerP95Ms)),
14758
+ maxTurnP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.turnP95Ms)),
14759
+ providers: aggregateProofTrendProviders(matchingEvidence.flatMap((evidence) => evidence.providers ?? [])),
14760
+ runtimeChannel: aggregateProofTrendRuntimeChannel(matchingEvidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined))
14685
14761
  };
14762
+ profiles.push({
14763
+ ...profile,
14764
+ status: readProofTrendProfileStatus(profile, budgets)
14765
+ });
14766
+ }
14767
+ const cycles = options.evidence.map((evidence, index) => ({
14768
+ at: evidence.generatedAt,
14769
+ cycle: index + 1,
14770
+ liveLatency: evidence.liveP95Ms === undefined ? undefined : { p95Ms: evidence.liveP95Ms, samples: 1 },
14771
+ ok: evidence.ok !== false,
14772
+ providers: evidence.providers,
14773
+ runtimeChannel: evidence.runtimeChannel,
14774
+ turnLatency: evidence.turnP95Ms === undefined ? undefined : {
14775
+ p95Ms: evidence.turnP95Ms,
14776
+ samples: 1,
14777
+ status: evidence.turnP95Ms <= turnCap ? "pass" : "fail"
14778
+ }
14779
+ }));
14780
+ const summary = {
14781
+ cycles: options.evidence.length,
14782
+ maxLiveP95Ms: maxNumber(options.evidence.map((evidence) => evidence.liveP95Ms)),
14783
+ maxProviderP95Ms: maxNumber(options.evidence.map((evidence) => evidence.providerP95Ms)),
14784
+ maxTurnP95Ms: maxNumber(options.evidence.map((evidence) => evidence.turnP95Ms)),
14785
+ profiles,
14786
+ providers: aggregateProofTrendProviders(options.evidence.flatMap((evidence) => evidence.providers ?? [])),
14787
+ runtimeChannel: aggregateProofTrendRuntimeChannel(options.evidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined))
14788
+ };
14789
+ return buildVoiceProofTrendReport({
14790
+ baseUrl: options.baseUrl,
14791
+ cycles,
14792
+ generatedAt: options.generatedAt,
14793
+ maxAgeMs: options.maxAgeMs,
14794
+ now: options.now,
14795
+ ok: options.evidence.length > 0 && options.evidence.every((evidence) => evidence.ok !== false) && profiles.every((profile) => profile.status !== "fail"),
14796
+ outputDir: options.outputDir,
14797
+ runId: options.runId,
14798
+ source: options.source,
14799
+ summary
14686
14800
  });
14687
14801
  };
14688
14802
  var normalizeProviderStatus = (status) => status === "pass" ? "pass" : status === "fail" ? "fail" : "warn";
@@ -37127,6 +37241,7 @@ export {
37127
37241
  buildVoiceProviderOrchestrationReport,
37128
37242
  buildVoiceProviderDecisionTraceReport,
37129
37243
  buildVoiceProviderContractMatrix,
37244
+ buildVoiceProofTrendReportFromRealCallProfiles,
37130
37245
  buildVoiceProofTrendReport,
37131
37246
  buildVoiceProofTrendRecommendationReport,
37132
37247
  buildVoiceProofTrendProfileSummaries,
@@ -120,6 +120,30 @@ export type VoiceProofTrendProfileSummaryOptions = {
120
120
  maxTurnP95Ms?: number;
121
121
  profiles?: readonly VoiceProofTrendProfileDefinition[];
122
122
  };
123
+ export type VoiceProofTrendRealCallProfileEvidence = {
124
+ generatedAt?: string;
125
+ liveP95Ms?: number;
126
+ ok?: boolean;
127
+ operationsRecordHref?: string;
128
+ profileDescription?: string;
129
+ profileId: string;
130
+ profileLabel?: string;
131
+ providerP95Ms?: number;
132
+ providers?: VoiceProofTrendProviderSummary[];
133
+ runtimeChannel?: VoiceProofTrendRuntimeChannelSummary;
134
+ sessionId: string;
135
+ turnP95Ms?: number;
136
+ };
137
+ export type VoiceProofTrendRealCallProfileReportOptions = VoiceProofTrendProfileSummaryOptions & {
138
+ baseUrl?: string;
139
+ evidence: readonly VoiceProofTrendRealCallProfileEvidence[];
140
+ generatedAt?: string;
141
+ maxAgeMs?: number;
142
+ now?: Date | number | string;
143
+ outputDir?: string;
144
+ runId?: string;
145
+ source?: string;
146
+ };
123
147
  export type VoiceProofTrendAssertionInput = {
124
148
  maxAgeMs?: number;
125
149
  maxRuntimeBackpressureEvents?: number;
@@ -261,6 +285,7 @@ export declare const readVoiceProofTrendReportFile: (path: string, options?: {
261
285
  maxAgeMs?: number;
262
286
  }) => Promise<VoiceProofTrendReport>;
263
287
  export declare const buildVoiceProofTrendProfileSummaries: (input: VoiceProofTrendReport | readonly VoiceProofTrendReport[], options?: VoiceProofTrendProfileSummaryOptions) => VoiceProofTrendProfileSummary[];
288
+ export declare const buildVoiceProofTrendReportFromRealCallProfiles: (options: VoiceProofTrendRealCallProfileReportOptions) => VoiceProofTrendReport;
264
289
  export declare const evaluateVoiceProofTrendEvidence: (report: VoiceProofTrendReport, input?: VoiceProofTrendAssertionInput) => VoiceProofTrendAssertionReport;
265
290
  export declare const assertVoiceProofTrendEvidence: (report: VoiceProofTrendReport, input?: VoiceProofTrendAssertionInput) => VoiceProofTrendAssertionReport;
266
291
  export declare const buildVoiceProofTrendRecommendationReport: (report: VoiceProofTrendReport, options?: VoiceProofTrendRecommendationOptions) => VoiceProofTrendRecommendationReport;
@@ -1663,6 +1663,22 @@ var aggregateProofTrendRuntimeChannel = (channels) => {
1663
1663
  };
1664
1664
  };
1665
1665
  var readProofTrendProviders = (reports) => aggregateProofTrendProviders(reports.flatMap((report) => report.summary.providers && report.summary.providers.length > 0 ? report.summary.providers : report.cycles.flatMap((cycle) => cycle.providers ?? [])));
1666
+ var exceedsProofTrendBudget = (value, budget) => value !== undefined && (!Number.isFinite(value) || value > budget);
1667
+ var readProofTrendProfileStatus = (profile, budgets) => {
1668
+ const runtimeChannel = profile.runtimeChannel;
1669
+ const hasBudgetEvidence = profile.maxLiveP95Ms !== undefined || profile.maxProviderP95Ms !== undefined || profile.maxTurnP95Ms !== undefined || profile.providers?.some((provider) => provider.p95Ms !== undefined) === true || runtimeChannel?.maxFirstAudioLatencyMs !== undefined || runtimeChannel?.maxInterruptionP95Ms !== undefined || runtimeChannel?.maxJitterMs !== undefined || runtimeChannel?.maxTimestampDriftMs !== undefined || runtimeChannel?.maxBackpressureEvents !== undefined;
1670
+ const budgetFailed = exceedsProofTrendBudget(profile.maxLiveP95Ms, budgets.maxLiveP95Ms) || exceedsProofTrendBudget(profile.maxProviderP95Ms, budgets.maxProviderP95Ms) || exceedsProofTrendBudget(profile.maxTurnP95Ms, budgets.maxTurnP95Ms) || exceedsProofTrendBudget(runtimeChannel?.maxFirstAudioLatencyMs, budgets.maxRuntimeFirstAudioLatencyMs) || exceedsProofTrendBudget(runtimeChannel?.maxInterruptionP95Ms, budgets.maxRuntimeInterruptionP95Ms) || exceedsProofTrendBudget(runtimeChannel?.maxJitterMs, budgets.maxRuntimeJitterMs) || exceedsProofTrendBudget(runtimeChannel?.maxTimestampDriftMs, budgets.maxRuntimeTimestampDriftMs) || exceedsProofTrendBudget(runtimeChannel?.maxBackpressureEvents, 0);
1671
+ if (budgetFailed || !hasBudgetEvidence && profile.status === "fail") {
1672
+ return "fail";
1673
+ }
1674
+ if (profile.status === "warn" || runtimeChannel?.status === "warn" || profile.providers?.some((provider) => provider.status === "warn") === true) {
1675
+ return "warn";
1676
+ }
1677
+ if (hasBudgetEvidence || profile.status === "pass" || runtimeChannel?.status === "pass" || profile.providers?.some((provider) => provider.status === "pass") === true) {
1678
+ return "pass";
1679
+ }
1680
+ return;
1681
+ };
1666
1682
  var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
1667
1683
  const reports = Array.isArray(input) ? input : [input];
1668
1684
  const definitions = options.profiles ?? DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS;
@@ -1673,6 +1689,15 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
1673
1689
  const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
1674
1690
  const runtimeJitterCap = options.maxRuntimeJitterMs ?? 30;
1675
1691
  const runtimeTimestampDriftCap = options.maxRuntimeTimestampDriftMs ?? 800;
1692
+ const budgets = {
1693
+ maxLiveP95Ms: liveCap,
1694
+ maxProviderP95Ms: providerCap,
1695
+ maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
1696
+ maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
1697
+ maxRuntimeJitterMs: runtimeJitterCap,
1698
+ maxRuntimeTimestampDriftMs: runtimeTimestampDriftCap,
1699
+ maxTurnP95Ms: turnCap
1700
+ };
1676
1701
  return definitions.map((definition) => {
1677
1702
  const historicalProfiles = reports.flatMap((report) => report.summary.profiles?.filter((profile) => profile.id === definition.id) ?? []);
1678
1703
  if (historicalProfiles.length > 0) {
@@ -1682,7 +1707,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
1682
1707
  profiles: [definition]
1683
1708
  }) : [];
1684
1709
  const profiles = [...historicalProfiles, ...derivedProfiles];
1685
- return {
1710
+ const aggregatedProfile = {
1686
1711
  description: definition.description ?? profiles.find(Boolean)?.description,
1687
1712
  id: definition.id,
1688
1713
  label: definition.label ?? profiles.find(Boolean)?.label,
@@ -1690,12 +1715,15 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
1690
1715
  maxProviderP95Ms: maxNumber(profiles.map((profile) => profile.maxProviderP95Ms)),
1691
1716
  maxTurnP95Ms: maxNumber(profiles.map((profile) => profile.maxTurnP95Ms)),
1692
1717
  providers: aggregateProofTrendProviders(profiles.flatMap((profile) => profile.providers ?? [])),
1693
- runtimeChannel: aggregateProofTrendRuntimeChannel(profiles.map((profile) => profile.runtimeChannel).filter((channel) => channel !== undefined)),
1694
- status: profiles.some((profile) => profile.status === "fail") ? "fail" : profiles.some((profile) => profile.status === "warn") ? "warn" : profiles.every((profile) => profile.status === "pass") ? "pass" : undefined
1718
+ runtimeChannel: aggregateProofTrendRuntimeChannel(profiles.map((profile) => profile.runtimeChannel).filter((channel) => channel !== undefined))
1719
+ };
1720
+ return {
1721
+ ...aggregatedProfile,
1722
+ status: readProofTrendProfileStatus(aggregatedProfile, budgets)
1695
1723
  };
1696
1724
  }
1697
1725
  const runtimeChannel = aggregateProofTrendRuntimeChannel(reports.map((report) => readProofTrendRuntimeChannel(report)).filter((channel) => Object.values(channel).some((value) => value !== undefined)));
1698
- return {
1726
+ const derivedProfile = {
1699
1727
  description: definition.description,
1700
1728
  id: definition.id,
1701
1729
  label: definition.label,
@@ -1711,9 +1739,95 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
1711
1739
  maxTimestampDriftMs: addProofTrendProfileOffset(runtimeChannel.maxTimestampDriftMs, definition.runtimeOffsetMs, definition.maxRuntimeTimestampDriftMs ?? runtimeTimestampDriftCap),
1712
1740
  samples: runtimeChannel.samples,
1713
1741
  status: runtimeChannel.status
1714
- },
1715
- status: reports.some((report) => report.status === "fail" || !report.ok) ? "fail" : reports.some((report) => report.status === "warn") ? "warn" : reports.every((report) => report.ok) ? "pass" : undefined
1742
+ }
1716
1743
  };
1744
+ return {
1745
+ ...derivedProfile,
1746
+ status: readProofTrendProfileStatus(derivedProfile, budgets)
1747
+ };
1748
+ });
1749
+ };
1750
+ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
1751
+ const providerCap = options.maxProviderP95Ms ?? 1000;
1752
+ const liveCap = options.maxLiveP95Ms ?? 800;
1753
+ const turnCap = options.maxTurnP95Ms ?? 700;
1754
+ const runtimeFirstAudioCap = options.maxRuntimeFirstAudioLatencyMs ?? 600;
1755
+ const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
1756
+ const runtimeJitterCap = options.maxRuntimeJitterMs ?? 30;
1757
+ const runtimeTimestampDriftCap = options.maxRuntimeTimestampDriftMs ?? 800;
1758
+ const budgets = {
1759
+ maxLiveP95Ms: liveCap,
1760
+ maxProviderP95Ms: providerCap,
1761
+ maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
1762
+ maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
1763
+ maxRuntimeJitterMs: runtimeJitterCap,
1764
+ maxRuntimeTimestampDriftMs: runtimeTimestampDriftCap,
1765
+ maxTurnP95Ms: turnCap
1766
+ };
1767
+ const definitionById = new Map((options.profiles ?? DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS).map((profile) => [profile.id, profile]));
1768
+ for (const evidence of options.evidence) {
1769
+ if (!definitionById.has(evidence.profileId)) {
1770
+ definitionById.set(evidence.profileId, {
1771
+ description: evidence.profileDescription,
1772
+ id: evidence.profileId,
1773
+ label: evidence.profileLabel
1774
+ });
1775
+ }
1776
+ }
1777
+ const profiles = [];
1778
+ for (const definition of definitionById.values()) {
1779
+ const matchingEvidence = options.evidence.filter((evidence) => evidence.profileId === definition.id);
1780
+ if (matchingEvidence.length === 0) {
1781
+ continue;
1782
+ }
1783
+ const profile = {
1784
+ description: definition.description ?? matchingEvidence.find((evidence) => evidence.profileDescription)?.profileDescription,
1785
+ id: definition.id,
1786
+ label: definition.label ?? matchingEvidence.find((evidence) => evidence.profileLabel)?.profileLabel,
1787
+ maxLiveP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.liveP95Ms)),
1788
+ maxProviderP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.providerP95Ms)),
1789
+ maxTurnP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.turnP95Ms)),
1790
+ providers: aggregateProofTrendProviders(matchingEvidence.flatMap((evidence) => evidence.providers ?? [])),
1791
+ runtimeChannel: aggregateProofTrendRuntimeChannel(matchingEvidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined))
1792
+ };
1793
+ profiles.push({
1794
+ ...profile,
1795
+ status: readProofTrendProfileStatus(profile, budgets)
1796
+ });
1797
+ }
1798
+ const cycles = options.evidence.map((evidence, index) => ({
1799
+ at: evidence.generatedAt,
1800
+ cycle: index + 1,
1801
+ liveLatency: evidence.liveP95Ms === undefined ? undefined : { p95Ms: evidence.liveP95Ms, samples: 1 },
1802
+ ok: evidence.ok !== false,
1803
+ providers: evidence.providers,
1804
+ runtimeChannel: evidence.runtimeChannel,
1805
+ turnLatency: evidence.turnP95Ms === undefined ? undefined : {
1806
+ p95Ms: evidence.turnP95Ms,
1807
+ samples: 1,
1808
+ status: evidence.turnP95Ms <= turnCap ? "pass" : "fail"
1809
+ }
1810
+ }));
1811
+ const summary = {
1812
+ cycles: options.evidence.length,
1813
+ maxLiveP95Ms: maxNumber(options.evidence.map((evidence) => evidence.liveP95Ms)),
1814
+ maxProviderP95Ms: maxNumber(options.evidence.map((evidence) => evidence.providerP95Ms)),
1815
+ maxTurnP95Ms: maxNumber(options.evidence.map((evidence) => evidence.turnP95Ms)),
1816
+ profiles,
1817
+ providers: aggregateProofTrendProviders(options.evidence.flatMap((evidence) => evidence.providers ?? [])),
1818
+ runtimeChannel: aggregateProofTrendRuntimeChannel(options.evidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined))
1819
+ };
1820
+ return buildVoiceProofTrendReport({
1821
+ baseUrl: options.baseUrl,
1822
+ cycles,
1823
+ generatedAt: options.generatedAt,
1824
+ maxAgeMs: options.maxAgeMs,
1825
+ now: options.now,
1826
+ ok: options.evidence.length > 0 && options.evidence.every((evidence) => evidence.ok !== false) && profiles.every((profile) => profile.status !== "fail"),
1827
+ outputDir: options.outputDir,
1828
+ runId: options.runId,
1829
+ source: options.source,
1830
+ summary
1717
1831
  });
1718
1832
  };
1719
1833
  var normalizeProviderStatus = (status) => status === "pass" ? "pass" : status === "fail" ? "fail" : "warn";
package/dist/vue/index.js CHANGED
@@ -1584,6 +1584,22 @@ var aggregateProofTrendRuntimeChannel = (channels) => {
1584
1584
  };
1585
1585
  };
1586
1586
  var readProofTrendProviders = (reports) => aggregateProofTrendProviders(reports.flatMap((report) => report.summary.providers && report.summary.providers.length > 0 ? report.summary.providers : report.cycles.flatMap((cycle) => cycle.providers ?? [])));
1587
+ var exceedsProofTrendBudget = (value, budget) => value !== undefined && (!Number.isFinite(value) || value > budget);
1588
+ var readProofTrendProfileStatus = (profile, budgets) => {
1589
+ const runtimeChannel = profile.runtimeChannel;
1590
+ const hasBudgetEvidence = profile.maxLiveP95Ms !== undefined || profile.maxProviderP95Ms !== undefined || profile.maxTurnP95Ms !== undefined || profile.providers?.some((provider) => provider.p95Ms !== undefined) === true || runtimeChannel?.maxFirstAudioLatencyMs !== undefined || runtimeChannel?.maxInterruptionP95Ms !== undefined || runtimeChannel?.maxJitterMs !== undefined || runtimeChannel?.maxTimestampDriftMs !== undefined || runtimeChannel?.maxBackpressureEvents !== undefined;
1591
+ const budgetFailed = exceedsProofTrendBudget(profile.maxLiveP95Ms, budgets.maxLiveP95Ms) || exceedsProofTrendBudget(profile.maxProviderP95Ms, budgets.maxProviderP95Ms) || exceedsProofTrendBudget(profile.maxTurnP95Ms, budgets.maxTurnP95Ms) || exceedsProofTrendBudget(runtimeChannel?.maxFirstAudioLatencyMs, budgets.maxRuntimeFirstAudioLatencyMs) || exceedsProofTrendBudget(runtimeChannel?.maxInterruptionP95Ms, budgets.maxRuntimeInterruptionP95Ms) || exceedsProofTrendBudget(runtimeChannel?.maxJitterMs, budgets.maxRuntimeJitterMs) || exceedsProofTrendBudget(runtimeChannel?.maxTimestampDriftMs, budgets.maxRuntimeTimestampDriftMs) || exceedsProofTrendBudget(runtimeChannel?.maxBackpressureEvents, 0);
1592
+ if (budgetFailed || !hasBudgetEvidence && profile.status === "fail") {
1593
+ return "fail";
1594
+ }
1595
+ if (profile.status === "warn" || runtimeChannel?.status === "warn" || profile.providers?.some((provider) => provider.status === "warn") === true) {
1596
+ return "warn";
1597
+ }
1598
+ if (hasBudgetEvidence || profile.status === "pass" || runtimeChannel?.status === "pass" || profile.providers?.some((provider) => provider.status === "pass") === true) {
1599
+ return "pass";
1600
+ }
1601
+ return;
1602
+ };
1587
1603
  var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
1588
1604
  const reports = Array.isArray(input) ? input : [input];
1589
1605
  const definitions = options.profiles ?? DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS;
@@ -1594,6 +1610,15 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
1594
1610
  const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
1595
1611
  const runtimeJitterCap = options.maxRuntimeJitterMs ?? 30;
1596
1612
  const runtimeTimestampDriftCap = options.maxRuntimeTimestampDriftMs ?? 800;
1613
+ const budgets = {
1614
+ maxLiveP95Ms: liveCap,
1615
+ maxProviderP95Ms: providerCap,
1616
+ maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
1617
+ maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
1618
+ maxRuntimeJitterMs: runtimeJitterCap,
1619
+ maxRuntimeTimestampDriftMs: runtimeTimestampDriftCap,
1620
+ maxTurnP95Ms: turnCap
1621
+ };
1597
1622
  return definitions.map((definition) => {
1598
1623
  const historicalProfiles = reports.flatMap((report) => report.summary.profiles?.filter((profile) => profile.id === definition.id) ?? []);
1599
1624
  if (historicalProfiles.length > 0) {
@@ -1603,7 +1628,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
1603
1628
  profiles: [definition]
1604
1629
  }) : [];
1605
1630
  const profiles = [...historicalProfiles, ...derivedProfiles];
1606
- return {
1631
+ const aggregatedProfile = {
1607
1632
  description: definition.description ?? profiles.find(Boolean)?.description,
1608
1633
  id: definition.id,
1609
1634
  label: definition.label ?? profiles.find(Boolean)?.label,
@@ -1611,12 +1636,15 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
1611
1636
  maxProviderP95Ms: maxNumber(profiles.map((profile) => profile.maxProviderP95Ms)),
1612
1637
  maxTurnP95Ms: maxNumber(profiles.map((profile) => profile.maxTurnP95Ms)),
1613
1638
  providers: aggregateProofTrendProviders(profiles.flatMap((profile) => profile.providers ?? [])),
1614
- runtimeChannel: aggregateProofTrendRuntimeChannel(profiles.map((profile) => profile.runtimeChannel).filter((channel) => channel !== undefined)),
1615
- status: profiles.some((profile) => profile.status === "fail") ? "fail" : profiles.some((profile) => profile.status === "warn") ? "warn" : profiles.every((profile) => profile.status === "pass") ? "pass" : undefined
1639
+ runtimeChannel: aggregateProofTrendRuntimeChannel(profiles.map((profile) => profile.runtimeChannel).filter((channel) => channel !== undefined))
1640
+ };
1641
+ return {
1642
+ ...aggregatedProfile,
1643
+ status: readProofTrendProfileStatus(aggregatedProfile, budgets)
1616
1644
  };
1617
1645
  }
1618
1646
  const runtimeChannel = aggregateProofTrendRuntimeChannel(reports.map((report) => readProofTrendRuntimeChannel(report)).filter((channel) => Object.values(channel).some((value) => value !== undefined)));
1619
- return {
1647
+ const derivedProfile = {
1620
1648
  description: definition.description,
1621
1649
  id: definition.id,
1622
1650
  label: definition.label,
@@ -1632,9 +1660,95 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
1632
1660
  maxTimestampDriftMs: addProofTrendProfileOffset(runtimeChannel.maxTimestampDriftMs, definition.runtimeOffsetMs, definition.maxRuntimeTimestampDriftMs ?? runtimeTimestampDriftCap),
1633
1661
  samples: runtimeChannel.samples,
1634
1662
  status: runtimeChannel.status
1635
- },
1636
- status: reports.some((report) => report.status === "fail" || !report.ok) ? "fail" : reports.some((report) => report.status === "warn") ? "warn" : reports.every((report) => report.ok) ? "pass" : undefined
1663
+ }
1637
1664
  };
1665
+ return {
1666
+ ...derivedProfile,
1667
+ status: readProofTrendProfileStatus(derivedProfile, budgets)
1668
+ };
1669
+ });
1670
+ };
1671
+ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
1672
+ const providerCap = options.maxProviderP95Ms ?? 1000;
1673
+ const liveCap = options.maxLiveP95Ms ?? 800;
1674
+ const turnCap = options.maxTurnP95Ms ?? 700;
1675
+ const runtimeFirstAudioCap = options.maxRuntimeFirstAudioLatencyMs ?? 600;
1676
+ const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
1677
+ const runtimeJitterCap = options.maxRuntimeJitterMs ?? 30;
1678
+ const runtimeTimestampDriftCap = options.maxRuntimeTimestampDriftMs ?? 800;
1679
+ const budgets = {
1680
+ maxLiveP95Ms: liveCap,
1681
+ maxProviderP95Ms: providerCap,
1682
+ maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
1683
+ maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
1684
+ maxRuntimeJitterMs: runtimeJitterCap,
1685
+ maxRuntimeTimestampDriftMs: runtimeTimestampDriftCap,
1686
+ maxTurnP95Ms: turnCap
1687
+ };
1688
+ const definitionById = new Map((options.profiles ?? DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS).map((profile) => [profile.id, profile]));
1689
+ for (const evidence of options.evidence) {
1690
+ if (!definitionById.has(evidence.profileId)) {
1691
+ definitionById.set(evidence.profileId, {
1692
+ description: evidence.profileDescription,
1693
+ id: evidence.profileId,
1694
+ label: evidence.profileLabel
1695
+ });
1696
+ }
1697
+ }
1698
+ const profiles = [];
1699
+ for (const definition of definitionById.values()) {
1700
+ const matchingEvidence = options.evidence.filter((evidence) => evidence.profileId === definition.id);
1701
+ if (matchingEvidence.length === 0) {
1702
+ continue;
1703
+ }
1704
+ const profile = {
1705
+ description: definition.description ?? matchingEvidence.find((evidence) => evidence.profileDescription)?.profileDescription,
1706
+ id: definition.id,
1707
+ label: definition.label ?? matchingEvidence.find((evidence) => evidence.profileLabel)?.profileLabel,
1708
+ maxLiveP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.liveP95Ms)),
1709
+ maxProviderP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.providerP95Ms)),
1710
+ maxTurnP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.turnP95Ms)),
1711
+ providers: aggregateProofTrendProviders(matchingEvidence.flatMap((evidence) => evidence.providers ?? [])),
1712
+ runtimeChannel: aggregateProofTrendRuntimeChannel(matchingEvidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined))
1713
+ };
1714
+ profiles.push({
1715
+ ...profile,
1716
+ status: readProofTrendProfileStatus(profile, budgets)
1717
+ });
1718
+ }
1719
+ const cycles = options.evidence.map((evidence, index) => ({
1720
+ at: evidence.generatedAt,
1721
+ cycle: index + 1,
1722
+ liveLatency: evidence.liveP95Ms === undefined ? undefined : { p95Ms: evidence.liveP95Ms, samples: 1 },
1723
+ ok: evidence.ok !== false,
1724
+ providers: evidence.providers,
1725
+ runtimeChannel: evidence.runtimeChannel,
1726
+ turnLatency: evidence.turnP95Ms === undefined ? undefined : {
1727
+ p95Ms: evidence.turnP95Ms,
1728
+ samples: 1,
1729
+ status: evidence.turnP95Ms <= turnCap ? "pass" : "fail"
1730
+ }
1731
+ }));
1732
+ const summary = {
1733
+ cycles: options.evidence.length,
1734
+ maxLiveP95Ms: maxNumber(options.evidence.map((evidence) => evidence.liveP95Ms)),
1735
+ maxProviderP95Ms: maxNumber(options.evidence.map((evidence) => evidence.providerP95Ms)),
1736
+ maxTurnP95Ms: maxNumber(options.evidence.map((evidence) => evidence.turnP95Ms)),
1737
+ profiles,
1738
+ providers: aggregateProofTrendProviders(options.evidence.flatMap((evidence) => evidence.providers ?? [])),
1739
+ runtimeChannel: aggregateProofTrendRuntimeChannel(options.evidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined))
1740
+ };
1741
+ return buildVoiceProofTrendReport({
1742
+ baseUrl: options.baseUrl,
1743
+ cycles,
1744
+ generatedAt: options.generatedAt,
1745
+ maxAgeMs: options.maxAgeMs,
1746
+ now: options.now,
1747
+ ok: options.evidence.length > 0 && options.evidence.every((evidence) => evidence.ok !== false) && profiles.every((profile) => profile.status !== "fail"),
1748
+ outputDir: options.outputDir,
1749
+ runId: options.runId,
1750
+ source: options.source,
1751
+ summary
1638
1752
  });
1639
1753
  };
1640
1754
  var normalizeProviderStatus = (status) => status === "pass" ? "pass" : status === "fail" ? "fail" : "warn";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.340",
3
+ "version": "0.0.22-beta.342",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",