@absolutejs/voice 0.0.22-beta.451 → 0.0.22-beta.452

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.
@@ -6261,6 +6261,7 @@ var readString = (value) => typeof value === "string" && value.trim() ? value :
6261
6261
  var readNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
6262
6262
  var readProofTrendMaxLiveP95 = (report) => report.summary.maxLiveP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.liveLatency?.p95Ms));
6263
6263
  var readProofTrendMaxProviderP95 = (report) => report.summary.maxProviderP95Ms ?? maxNumber((report.summary.providers ?? []).map((provider) => provider.p95Ms)) ?? maxNumber(report.cycles.flatMap((cycle) => (cycle.providers ?? []).map((provider) => provider.p95Ms)));
6264
+ var readProofTrendMaxReconnectP95 = (report) => report.summary.maxReconnectP95Ms ?? report.summary.reconnect?.resumeLatencyP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.reconnect?.resumeLatencyP95Ms));
6264
6265
  var readProofTrendMaxTurnP95 = (report) => report.summary.maxTurnP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.turnLatency?.p95Ms));
6265
6266
  var readRuntimeChannelMetric = (report, key) => {
6266
6267
  const summaryValue = report.summary.runtimeChannel?.[key];
@@ -6322,6 +6323,42 @@ var aggregateProofTrendRuntimeChannel = (channels) => {
6322
6323
  status: channels.some((channel) => channel.status === "fail") ? "fail" : channels.some((channel) => channel.status === "warn") ? "warn" : channels.every((channel) => channel.status === "pass") ? "pass" : undefined
6323
6324
  };
6324
6325
  };
6326
+ var aggregateProofTrendReconnect = (reconnects) => {
6327
+ if (reconnects.length === 0) {
6328
+ return;
6329
+ }
6330
+ const hasFailure = reconnects.some((reconnect) => reconnect.status === "fail" || reconnect.exhausted === true || reconnect.reconnected === false || reconnect.resumed === false);
6331
+ return {
6332
+ attempts: maxNumber(reconnects.map((reconnect) => reconnect.attempts)),
6333
+ exhausted: reconnects.some((reconnect) => reconnect.exhausted === true),
6334
+ maxAttempts: maxNumber(reconnects.map((reconnect) => reconnect.maxAttempts)),
6335
+ resumeLatencyP95Ms: maxNumber(reconnects.map((reconnect) => reconnect.resumeLatencyP95Ms)),
6336
+ reconnected: reconnects.some((reconnect) => reconnect.reconnected === true),
6337
+ resumed: reconnects.some((reconnect) => reconnect.resumed === true),
6338
+ samples: reconnects.reduce((total, reconnect) => total + (reconnect.samples ?? 0), 0),
6339
+ snapshotCount: reconnects.reduce((total, reconnect) => total + (reconnect.snapshotCount ?? 0), 0),
6340
+ status: hasFailure ? "fail" : reconnects.some((reconnect) => reconnect.status === "warn") ? "warn" : "pass"
6341
+ };
6342
+ };
6343
+ var summarizeReconnectTraceEvidence = (events) => {
6344
+ const reconnectEvents = events.filter((event) => event.type === "client.reconnect");
6345
+ if (reconnectEvents.length === 0) {
6346
+ return;
6347
+ }
6348
+ const statuses = reconnectEvents.map((event) => readString(readTraceRecord(event).status)).filter((status) => status !== undefined);
6349
+ const reconnectPayloads = reconnectEvents.map((event) => readTraceRecord(event));
6350
+ return {
6351
+ attempts: maxNumber(reconnectPayloads.map((payload) => readNumber2(payload.attempts))),
6352
+ exhausted: statuses.includes("exhausted"),
6353
+ maxAttempts: maxNumber(reconnectPayloads.map((payload) => readNumber2(payload.maxAttempts))),
6354
+ resumeLatencyP95Ms: percentile(reconnectPayloads.map((payload) => readNumber2(payload.resumeLatencyP95Ms) ?? readNumber2(payload.resumeLatencyMs)).filter((value) => value !== undefined), 95),
6355
+ reconnected: statuses.includes("reconnecting"),
6356
+ resumed: statuses.includes("resumed") || statuses.includes("pass"),
6357
+ samples: reconnectEvents.length,
6358
+ snapshotCount: reconnectEvents.length,
6359
+ status: reconnectEvents.some((event) => isFailingTraceStatus(readTraceStatus(readTraceRecord(event)))) ? "fail" : "pass"
6360
+ };
6361
+ };
6325
6362
  var readTraceRecord = (event) => event.payload;
6326
6363
  var readTraceProfileId = (events, options) => {
6327
6364
  for (const event of events) {
@@ -6469,6 +6506,7 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
6469
6506
  }).filter((value) => value !== undefined);
6470
6507
  const turnP95Ms = summarizeTurnTraceP95(sessionEvents);
6471
6508
  const providerP95Ms = maxNumber(providers.map((provider) => provider.p95Ms));
6509
+ const reconnect = summarizeReconnectTraceEvidence(sessionEvents);
6472
6510
  const runtimeChannel = summarizeRuntimeChannelTraceEvidence(sessionEvents);
6473
6511
  const surfaces = readRealCallProfileTraceSurfaces(sessionEvents);
6474
6512
  if (providers.length === 0 && runtimeChannel === undefined && liveLatencies.length === 0 && surfaces.length === 0) {
@@ -6484,6 +6522,7 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
6484
6522
  profileLabel: options.profileLabels?.[profileId] ?? (profileId === options.defaultProfileId ? options.defaultProfileLabel : undefined),
6485
6523
  providerP95Ms,
6486
6524
  providers,
6525
+ reconnect,
6487
6526
  runtimeChannel,
6488
6527
  sessionId,
6489
6528
  surfaces: surfaces.length > 0 ? surfaces : undefined,
@@ -6491,11 +6530,45 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
6491
6530
  };
6492
6531
  }).filter((evidence) => evidence !== undefined);
6493
6532
  };
6533
+ var normalizeReconnectReport = (report) => ("contract" in report) ? report.contract : report;
6534
+ var buildVoiceRealCallProfileEvidenceFromReconnectProofReports = (input, options = {}) => {
6535
+ const reports = Array.isArray(input) ? input : [input];
6536
+ const profileId = options.profileId ?? "reconnect-resume";
6537
+ return reports.map((report, index) => {
6538
+ const contract = normalizeReconnectReport(report);
6539
+ const generatedAt = "generatedAt" in report ? report.generatedAt : new Date(contract.checkedAt).toISOString();
6540
+ const ok = "ok" in report ? report.ok : contract.pass;
6541
+ const operationsRecordHref = typeof options.operationsRecordHref === "function" ? options.operationsRecordHref(report, index) : options.operationsRecordHref;
6542
+ const sessionId = typeof options.sessionId === "function" ? options.sessionId(report, index) : options.sessionId ?? `reconnect-proof-${String(index + 1)}-${String(contract.checkedAt)}`;
6543
+ return {
6544
+ generatedAt,
6545
+ ok,
6546
+ operationsRecordHref,
6547
+ profileDescription: options.profileDescription,
6548
+ profileId,
6549
+ profileLabel: options.profileLabel,
6550
+ reconnect: {
6551
+ attempts: contract.summary.attempts,
6552
+ exhausted: contract.summary.exhausted,
6553
+ maxAttempts: contract.summary.maxAttempts,
6554
+ resumeLatencyP95Ms: contract.resumeLatencyP95Ms,
6555
+ reconnected: contract.summary.reconnected,
6556
+ resumed: contract.summary.resumed,
6557
+ samples: 1,
6558
+ snapshotCount: contract.snapshotCount,
6559
+ status: ok ? "pass" : "fail"
6560
+ },
6561
+ sessionId,
6562
+ surfaces: [...new Set(["reconnect", ...options.surfaces ?? []])].sort()
6563
+ };
6564
+ });
6565
+ };
6494
6566
  var loadVoiceRealCallProfileEvidenceFromTraceStore = async (options) => buildVoiceRealCallProfileEvidenceFromTraceEvents(await options.store.list({ limit: options.limit ?? 5000 }), options);
6495
6567
  var realCallProfileTraceSignalTypes = new Set([
6496
6568
  "client.barge_in",
6497
6569
  "client.browser_media",
6498
6570
  "client.live_latency",
6571
+ "client.reconnect",
6499
6572
  "client.telephony_media",
6500
6573
  "provider.decision",
6501
6574
  "session.error",
@@ -6558,15 +6631,16 @@ var readProofTrendProviders = (reports) => aggregateProofTrendProviders(reports.
6558
6631
  var exceedsProofTrendBudget = (value, budget) => value !== undefined && (!Number.isFinite(value) || value > budget);
6559
6632
  var readProofTrendProfileStatus = (profile, budgets) => {
6560
6633
  const runtimeChannel = profile.runtimeChannel;
6561
- 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;
6562
- 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);
6634
+ const reconnect = profile.reconnect;
6635
+ const hasBudgetEvidence = profile.maxLiveP95Ms !== undefined || profile.maxProviderP95Ms !== undefined || profile.maxReconnectP95Ms !== 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 || reconnect?.resumeLatencyP95Ms !== undefined || reconnect?.samples !== undefined;
6636
+ const budgetFailed = exceedsProofTrendBudget(profile.maxLiveP95Ms, budgets.maxLiveP95Ms) || exceedsProofTrendBudget(profile.maxProviderP95Ms, budgets.maxProviderP95Ms) || exceedsProofTrendBudget(profile.maxReconnectP95Ms ?? reconnect?.resumeLatencyP95Ms, budgets.maxReconnectP95Ms) || 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) || reconnect?.status === "fail";
6563
6637
  if (budgetFailed || !hasBudgetEvidence && profile.status === "fail") {
6564
6638
  return "fail";
6565
6639
  }
6566
- if (profile.status === "warn" || runtimeChannel?.status === "warn" || profile.providers?.some((provider) => provider.status === "warn") === true) {
6640
+ if (profile.status === "warn" || reconnect?.status === "warn" || runtimeChannel?.status === "warn" || profile.providers?.some((provider) => provider.status === "warn") === true) {
6567
6641
  return "warn";
6568
6642
  }
6569
- if (hasBudgetEvidence || profile.status === "pass" || runtimeChannel?.status === "pass" || profile.providers?.some((provider) => provider.status === "pass") === true) {
6643
+ if (hasBudgetEvidence || profile.status === "pass" || reconnect?.status === "pass" || runtimeChannel?.status === "pass" || profile.providers?.some((provider) => provider.status === "pass") === true) {
6570
6644
  return "pass";
6571
6645
  }
6572
6646
  return;
@@ -6576,6 +6650,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
6576
6650
  const definitions = options.profiles ?? DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS;
6577
6651
  const providerCap = options.maxProviderP95Ms ?? 1000;
6578
6652
  const liveCap = options.maxLiveP95Ms ?? 800;
6653
+ const reconnectCap = options.maxReconnectP95Ms ?? 1500;
6579
6654
  const turnCap = options.maxTurnP95Ms ?? 700;
6580
6655
  const runtimeFirstAudioCap = options.maxRuntimeFirstAudioLatencyMs ?? 600;
6581
6656
  const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
@@ -6584,6 +6659,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
6584
6659
  const budgets = {
6585
6660
  maxLiveP95Ms: liveCap,
6586
6661
  maxProviderP95Ms: providerCap,
6662
+ maxReconnectP95Ms: reconnectCap,
6587
6663
  maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
6588
6664
  maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
6589
6665
  maxRuntimeJitterMs: runtimeJitterCap,
@@ -6606,8 +6682,10 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
6606
6682
  label: definition.label ?? profiles.find(Boolean)?.label,
6607
6683
  maxLiveP95Ms: maxNumber(profiles.map((profile) => profile.maxLiveP95Ms)),
6608
6684
  maxProviderP95Ms: maxNumber(profiles.map((profile) => profile.maxProviderP95Ms)),
6685
+ maxReconnectP95Ms: maxNumber(profiles.map((profile) => profile.maxReconnectP95Ms)),
6609
6686
  maxTurnP95Ms: maxNumber(profiles.map((profile) => profile.maxTurnP95Ms)),
6610
6687
  providers: aggregateProofTrendProviders(profiles.flatMap((profile) => profile.providers ?? [])),
6688
+ reconnect: aggregateProofTrendReconnect(profiles.map((profile) => profile.reconnect).filter((reconnect) => reconnect !== undefined)),
6611
6689
  runtimeChannel: aggregateProofTrendRuntimeChannel(profiles.map((profile) => profile.runtimeChannel).filter((channel) => channel !== undefined)),
6612
6690
  sessionCount: profiles.reduce((total, profile) => total + (profile.sessionCount ?? 0), 0),
6613
6691
  surfaces: [
@@ -6647,6 +6725,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
6647
6725
  var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
6648
6726
  const providerCap = options.maxProviderP95Ms ?? 1000;
6649
6727
  const liveCap = options.maxLiveP95Ms ?? 800;
6728
+ const reconnectCap = options.maxReconnectP95Ms ?? 1500;
6650
6729
  const turnCap = options.maxTurnP95Ms ?? 700;
6651
6730
  const runtimeFirstAudioCap = options.maxRuntimeFirstAudioLatencyMs ?? 600;
6652
6731
  const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
@@ -6655,6 +6734,7 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
6655
6734
  const budgets = {
6656
6735
  maxLiveP95Ms: liveCap,
6657
6736
  maxProviderP95Ms: providerCap,
6737
+ maxReconnectP95Ms: reconnectCap,
6658
6738
  maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
6659
6739
  maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
6660
6740
  maxRuntimeJitterMs: runtimeJitterCap,
@@ -6684,8 +6764,10 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
6684
6764
  label: definition.label ?? matchingEvidence.find((evidence) => evidence.profileLabel)?.profileLabel,
6685
6765
  maxLiveP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.liveP95Ms)),
6686
6766
  maxProviderP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.providerP95Ms)),
6767
+ maxReconnectP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.reconnect?.resumeLatencyP95Ms)),
6687
6768
  maxTurnP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.turnP95Ms)),
6688
6769
  providers: aggregateProofTrendProviders(matchingEvidence.flatMap((evidence) => evidence.providers ?? [])),
6770
+ reconnect: aggregateProofTrendReconnect(matchingEvidence.map((evidence) => evidence.reconnect).filter((reconnect) => reconnect !== undefined)),
6689
6771
  runtimeChannel: aggregateProofTrendRuntimeChannel(matchingEvidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined)),
6690
6772
  sessionCount: new Set(matchingEvidence.map((evidence) => evidence.sessionId)).size,
6691
6773
  surfaces: [
@@ -6704,6 +6786,7 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
6704
6786
  ok: evidence.ok !== false,
6705
6787
  providers: evidence.providers,
6706
6788
  runtimeChannel: evidence.runtimeChannel,
6789
+ reconnect: evidence.reconnect,
6707
6790
  turnLatency: evidence.turnP95Ms === undefined ? undefined : {
6708
6791
  p95Ms: evidence.turnP95Ms,
6709
6792
  samples: 1,
@@ -6714,9 +6797,11 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
6714
6797
  cycles: options.evidence.length,
6715
6798
  maxLiveP95Ms: maxNumber(options.evidence.map((evidence) => evidence.liveP95Ms)),
6716
6799
  maxProviderP95Ms: maxNumber(options.evidence.map((evidence) => evidence.providerP95Ms)),
6800
+ maxReconnectP95Ms: maxNumber(options.evidence.map((evidence) => evidence.reconnect?.resumeLatencyP95Ms)),
6717
6801
  maxTurnP95Ms: maxNumber(options.evidence.map((evidence) => evidence.turnP95Ms)),
6718
6802
  profiles,
6719
6803
  providers: aggregateProofTrendProviders(options.evidence.flatMap((evidence) => evidence.providers ?? [])),
6804
+ reconnect: aggregateProofTrendReconnect(options.evidence.map((evidence) => evidence.reconnect).filter((reconnect) => reconnect !== undefined)),
6720
6805
  runtimeChannel: aggregateProofTrendRuntimeChannel(options.evidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined))
6721
6806
  };
6722
6807
  return buildVoiceProofTrendReport({
@@ -7535,10 +7620,12 @@ var buildVoiceRealCallProfileHistoryReport = (options = {}) => {
7535
7620
  failedReports: history.filter((report) => report.ok !== true).length,
7536
7621
  maxLiveP95Ms: maxNumber(profileHistory.map(readProofTrendMaxLiveP95)),
7537
7622
  maxProviderP95Ms: maxNumber(profileHistory.map(readProofTrendMaxProviderP95)),
7623
+ maxReconnectP95Ms: maxNumber(profileHistory.map(readProofTrendMaxReconnectP95)),
7538
7624
  maxTurnP95Ms: maxNumber(profileHistory.map(readProofTrendMaxTurnP95)),
7539
7625
  profileCount: profiles.length,
7540
7626
  profiles,
7541
7627
  providers: readProofTrendProviders(profileHistory),
7628
+ reconnect: aggregateProofTrendReconnect(profileHistory.map((report) => report.summary.reconnect).filter((reconnect) => reconnect !== undefined)),
7542
7629
  runtimeChannel: aggregateProofTrendRuntimeChannel(profileHistory.map(readProofTrendRuntimeChannel).filter((channel) => channel !== undefined))
7543
7630
  };
7544
7631
  const trend = buildVoiceProofTrendReport({
package/dist/index.d.ts CHANGED
@@ -31,7 +31,7 @@ export { assertVoicePlatformCoverage, buildVoicePlatformCoverageSummary, createV
31
31
  export { assertVoiceCompetitiveCoverage, buildVoiceCompetitiveCoverageReport, createVoiceCompetitiveCoverageRoutes, evaluateVoiceCompetitiveCoverage, renderVoiceCompetitiveCoverageHTML, renderVoiceCompetitiveCoverageMarkdown, } from "./competitiveCoverage";
32
32
  export type { VoiceCompetitiveCoverageAssertionInput, VoiceCompetitiveCoverageAssertionReport, VoiceCompetitiveCoverageIssue, VoiceCompetitiveCoverageLevel, VoiceCompetitiveCoverageReport, VoiceCompetitiveCoverageReportInput, VoiceCompetitiveCoverageRoutesOptions, VoiceCompetitiveCoverageStatus, VoiceCompetitiveCoverageSummary, VoiceCompetitiveDepthLevel, VoiceCompetitiveEvidence, VoiceCompetitiveSurface, } from "./competitiveCoverage";
33
33
  export type { VoicePlatformCoverageAssertionInput, VoicePlatformCoverageAssertionReport, VoicePlatformCoverageEvidence, VoicePlatformCoverageRoutesOptions, VoicePlatformCoverageStatus, VoicePlatformCoverageSummary, VoicePlatformCoverageSummaryInput, VoicePlatformCoverageSurface, } from "./platformCoverage";
34
- export { assertVoiceProofTrendEvidence, appendVoiceRealCallProfileRecoveryEvidence, buildEmptyVoiceProofTrendReport, buildVoiceProofTrendProfileSummaries, buildVoiceProofTrendRecommendationReport, buildVoiceProofTrendReportFromRealCallProfiles, buildVoiceProofTrendReport, buildVoiceRealCallProfileEvidenceFromTraceEvents, buildVoiceRealCallProfileDefaults, buildVoiceRealCallProfileHistoryReport, buildVoiceRealCallProfileReadinessCheck, buildVoiceRealCallProfileRecoveryJobHistoryCheck, buildVoiceRealCallProfileRecoveryActions, createVoiceInMemoryRealCallProfileRecoveryJobStore, createVoiceRealCallProfileTraceCollector, createVoiceSQLiteRealCallProfileRecoveryJobStore, createVoiceProofTrendRecommendationRoutes, createVoiceProofTrendRoutes, createVoiceRealCallProfileHistoryRoutes, createVoiceRealCallProfileRecoveryActionRoutes, DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS, DEFAULT_VOICE_PROOF_TRENDS_MAX_AGE_MS, evaluateVoiceProofTrendEvidence, formatVoiceProofTrendAge, loadVoiceRealCallProfileEvidenceFromTraceStore, normalizeVoiceProofTrendReport, readVoiceProofTrendReportFile, renderVoiceProofTrendRecommendationHTML, renderVoiceProofTrendRecommendationMarkdown, renderVoiceRealCallProfileHistoryHTML, renderVoiceRealCallProfileHistoryMarkdown, runVoiceRealCallProfileRecoveryLoop, resolveVoiceRealCallProfileProviderRoute, } from "./proofTrends";
34
+ export { assertVoiceProofTrendEvidence, appendVoiceRealCallProfileRecoveryEvidence, buildEmptyVoiceProofTrendReport, buildVoiceProofTrendProfileSummaries, buildVoiceProofTrendRecommendationReport, buildVoiceProofTrendReportFromRealCallProfiles, buildVoiceProofTrendReport, buildVoiceRealCallProfileEvidenceFromTraceEvents, buildVoiceRealCallProfileEvidenceFromReconnectProofReports, buildVoiceRealCallProfileDefaults, buildVoiceRealCallProfileHistoryReport, buildVoiceRealCallProfileReadinessCheck, buildVoiceRealCallProfileRecoveryJobHistoryCheck, buildVoiceRealCallProfileRecoveryActions, createVoiceInMemoryRealCallProfileRecoveryJobStore, createVoiceRealCallProfileTraceCollector, createVoiceSQLiteRealCallProfileRecoveryJobStore, createVoiceProofTrendRecommendationRoutes, createVoiceProofTrendRoutes, createVoiceRealCallProfileHistoryRoutes, createVoiceRealCallProfileRecoveryActionRoutes, DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS, DEFAULT_VOICE_PROOF_TRENDS_MAX_AGE_MS, evaluateVoiceProofTrendEvidence, formatVoiceProofTrendAge, loadVoiceRealCallProfileEvidenceFromTraceStore, normalizeVoiceProofTrendReport, readVoiceProofTrendReportFile, renderVoiceProofTrendRecommendationHTML, renderVoiceProofTrendRecommendationMarkdown, renderVoiceRealCallProfileHistoryHTML, renderVoiceRealCallProfileHistoryMarkdown, runVoiceRealCallProfileRecoveryLoop, resolveVoiceRealCallProfileProviderRoute, } from "./proofTrends";
35
35
  export { createVoiceEvidenceAssertion, createVoiceProofAssertion, summarizeVoiceProofAssertions, } from "./proofAssertions";
36
36
  export { buildVoiceSessionSnapshot, buildVoiceSessionSnapshotStatus, createVoiceSessionSnapshotRoutes, parseVoiceSessionSnapshot, } from "./sessionSnapshot";
37
37
  export { buildVoiceCallDebuggerReport, createVoiceCallDebuggerRoutes, renderVoiceCallDebuggerHTML, resolveLatestVoiceCallDebuggerSessionId, } from "./callDebugger";
@@ -46,7 +46,7 @@ export { buildVoiceProviderDecisionTraceReport, createVoiceProviderDecisionTrace
46
46
  export type { VoiceProviderDecisionStatus, VoiceProviderDecisionSurfaceReport, VoiceProviderDecisionTrace, VoiceProviderDecisionTraceInput, VoiceProviderDecisionTraceIssue, VoiceProviderDecisionTraceReport, VoiceProviderDecisionTraceReportOptions, VoiceProviderDecisionTraceRoutesOptions, } from "./providerDecisionTraces";
47
47
  export { appendVoiceIOProviderRouterTraceEvent, appendVoiceProviderRouterTraceEvent, buildVoiceIOProviderRouterTraceEvent, buildVoiceProviderRouterTraceEvent, } from "./providerRouterTraces";
48
48
  export type { VoiceIOProviderRouterTraceAppendOptions, VoiceIOProviderRouterTraceEventOptions, VoiceProviderRouterTraceAppendOptions, VoiceProviderRouterTraceEventOptions, } from "./providerRouterTraces";
49
- export type { VoiceProofTrendAssertionInput, VoiceProofTrendAssertionReport, VoiceProofTrendCycle, VoiceProofTrendProfileDefinition, VoiceProofTrendProfileRecommendation, VoiceProofTrendProfileSummaryOptions, VoiceProofTrendProfileSummary, VoiceProofTrendProviderRecommendation, VoiceProofTrendProviderSummary, VoiceProofTrendRecommendation, VoiceProofTrendRecommendationOptions, VoiceProofTrendRecommendationReport, VoiceProofTrendRecommendationRoutesOptions, VoiceProofTrendRecommendationStatus, VoiceProofTrendRecommendationSurface, VoiceProofTrendRealCallProfileEvidence, VoiceProofTrendRealCallProfileReportOptions, VoiceProofTrendReport, VoiceProofTrendReportInput, VoiceProofTrendRoutesOptions, VoiceProofTrendRuntimeChannelSummary, VoiceProofTrendStatus, VoiceProofTrendSummary, VoiceRealCallProfileDefault, VoiceRealCallProfileDefaultsOptions, VoiceRealCallProfileDefaultsReport, VoiceRealCallProfileHistoryOptions, VoiceRealCallProfileHistoryReport, VoiceRealCallProfileHistoryRoutesOptions, VoiceRealCallProfileProviderRouteOptions, VoiceRealCallProfileReadinessCheckOptions, VoiceRealCallProfileRecoveryActionOptions, VoiceRealCallProfileRecoveryAction, VoiceRealCallProfileRecoveryActionHandler, VoiceRealCallProfileRecoveryActionHandlerInput, VoiceRealCallProfileRecoveryActionId, VoiceRealCallProfileRecoveryJobHistoryCheckOptions, VoiceRealCallProfileRecoveryActionResult, VoiceRealCallProfileRecoveryActionRoutesOptions, VoiceRealCallProfileRecoveryJob, VoiceRealCallProfileRecoveryJobCreateInput, VoiceRealCallProfileRecoveryJobListOptions, VoiceRealCallProfileRecoveryJobStatus, VoiceRealCallProfileRecoveryJobStore, VoiceRealCallProfileRecoveryJobUpdate, VoiceRealCallProfileRecoveryLoopAction, VoiceRealCallProfileRecoveryLoopJob, VoiceRealCallProfileRecoveryLoopJobResult, VoiceRealCallProfileRecoveryLoopOptions, VoiceRealCallProfileRecoveryLoopReport, VoiceRealCallProfileRecoveryLoopStartFailure, VoiceRealCallProfileRecoveryEvidenceOptions, VoiceRealCallProfileRecoveryEvidenceProvider, VoiceRealCallProfileRecoveryEvidenceProviderRole, VoiceRealCallProfileRecoveryEvidenceResult, VoiceSQLiteRealCallProfileRecoveryJobStoreOptions, VoiceRealCallProfileTraceCollector, VoiceRealCallProfileTraceCollectorEvidenceOptions, VoiceRealCallProfileTraceCollectorOptions, VoiceRealCallProfileTraceEvidenceOptions, VoiceRealCallProfileTraceStoreEvidenceOptions, } from "./proofTrends";
49
+ export type { VoiceProofTrendAssertionInput, VoiceProofTrendAssertionReport, VoiceProofTrendCycle, VoiceProofTrendProfileDefinition, VoiceProofTrendProfileRecommendation, VoiceProofTrendProfileSummaryOptions, VoiceProofTrendProfileSummary, VoiceProofTrendProviderRecommendation, VoiceProofTrendProviderSummary, VoiceProofTrendReconnectSummary, VoiceProofTrendRecommendation, VoiceProofTrendRecommendationOptions, VoiceProofTrendRecommendationReport, VoiceProofTrendRecommendationRoutesOptions, VoiceProofTrendRecommendationStatus, VoiceProofTrendRecommendationSurface, VoiceProofTrendRealCallProfileEvidence, VoiceProofTrendRealCallProfileReportOptions, VoiceProofTrendReport, VoiceProofTrendReportInput, VoiceProofTrendRoutesOptions, VoiceProofTrendRuntimeChannelSummary, VoiceProofTrendStatus, VoiceProofTrendSummary, VoiceRealCallProfileDefault, VoiceRealCallProfileDefaultsOptions, VoiceRealCallProfileDefaultsReport, VoiceRealCallProfileHistoryOptions, VoiceRealCallProfileHistoryReport, VoiceRealCallProfileHistoryRoutesOptions, VoiceRealCallProfileProviderRouteOptions, VoiceRealCallProfileReadinessCheckOptions, VoiceRealCallProfileRecoveryActionOptions, VoiceRealCallProfileRecoveryAction, VoiceRealCallProfileRecoveryActionHandler, VoiceRealCallProfileRecoveryActionHandlerInput, VoiceRealCallProfileRecoveryActionId, VoiceRealCallProfileRecoveryJobHistoryCheckOptions, VoiceRealCallProfileRecoveryActionResult, VoiceRealCallProfileRecoveryActionRoutesOptions, VoiceRealCallProfileRecoveryJob, VoiceRealCallProfileRecoveryJobCreateInput, VoiceRealCallProfileRecoveryJobListOptions, VoiceRealCallProfileRecoveryJobStatus, VoiceRealCallProfileRecoveryJobStore, VoiceRealCallProfileRecoveryJobUpdate, VoiceRealCallProfileRecoveryLoopAction, VoiceRealCallProfileRecoveryLoopJob, VoiceRealCallProfileRecoveryLoopJobResult, VoiceRealCallProfileRecoveryLoopOptions, VoiceRealCallProfileRecoveryLoopReport, VoiceRealCallProfileRecoveryLoopStartFailure, VoiceRealCallProfileRecoveryEvidenceOptions, VoiceRealCallProfileRecoveryEvidenceProvider, VoiceRealCallProfileRecoveryEvidenceProviderRole, VoiceRealCallProfileRecoveryEvidenceResult, VoiceSQLiteRealCallProfileRecoveryJobStoreOptions, VoiceRealCallProfileTraceCollector, VoiceRealCallProfileTraceCollectorEvidenceOptions, VoiceRealCallProfileTraceCollectorOptions, VoiceRealCallProfileTraceEvidenceOptions, VoiceRealCallProfileTraceStoreEvidenceOptions, VoiceReconnectRealCallProfileEvidenceOptions, } from "./proofTrends";
50
50
  export { assertVoiceSloCalibration, buildVoiceSloCalibrationReport, buildVoiceSloReadinessThresholdReport, createVoiceSloReadinessThresholdOptions, createVoiceSloReadinessThresholdRoutes, createVoiceSloThresholdProfile, createVoiceSloCalibrationRoutes, renderVoiceSloCalibrationMarkdown, renderVoiceSloReadinessThresholdHTML, renderVoiceSloReadinessThresholdMarkdown, } from "./sloCalibration";
51
51
  export type { VoiceSloCalibrationMetricKey, VoiceSloCalibrationOptions, VoiceSloCalibrationReport, VoiceSloCalibrationRoutesOptions, VoiceSloCalibrationSample, VoiceSloCalibrationStatus, VoiceSloCalibrationThreshold, VoiceSloCalibrationThresholds, VoiceSloReadinessThresholdReport, VoiceSloReadinessThresholdReportOptions, VoiceSloReadinessThresholdOptions, VoiceSloReadinessThresholdRoutesOptions, VoiceSloThresholdProfile, } from "./sloCalibration";
52
52
  export { assertVoiceLiveOpsControlEvidence, assertVoiceLiveOpsEvidence, buildVoiceLiveOpsControlState, createVoiceLiveOpsController, createVoiceLiveOpsRoutes, createVoiceMemoryLiveOpsControlStore, evaluateVoiceLiveOpsControlEvidence, evaluateVoiceLiveOpsEvidence, getVoiceLiveOpsControlStatus, VOICE_LIVE_OPS_ACTIONS, } from "./liveOps";
package/dist/index.js CHANGED
@@ -16525,6 +16525,7 @@ var readString2 = (value) => typeof value === "string" && value.trim() ? value :
16525
16525
  var readNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
16526
16526
  var readProofTrendMaxLiveP95 = (report) => report.summary.maxLiveP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.liveLatency?.p95Ms));
16527
16527
  var readProofTrendMaxProviderP95 = (report) => report.summary.maxProviderP95Ms ?? maxNumber((report.summary.providers ?? []).map((provider) => provider.p95Ms)) ?? maxNumber(report.cycles.flatMap((cycle) => (cycle.providers ?? []).map((provider) => provider.p95Ms)));
16528
+ var readProofTrendMaxReconnectP95 = (report) => report.summary.maxReconnectP95Ms ?? report.summary.reconnect?.resumeLatencyP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.reconnect?.resumeLatencyP95Ms));
16528
16529
  var readProofTrendMaxTurnP95 = (report) => report.summary.maxTurnP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.turnLatency?.p95Ms));
16529
16530
  var readRuntimeChannelMetric = (report, key) => {
16530
16531
  const summaryValue = report.summary.runtimeChannel?.[key];
@@ -16586,6 +16587,42 @@ var aggregateProofTrendRuntimeChannel = (channels) => {
16586
16587
  status: channels.some((channel) => channel.status === "fail") ? "fail" : channels.some((channel) => channel.status === "warn") ? "warn" : channels.every((channel) => channel.status === "pass") ? "pass" : undefined
16587
16588
  };
16588
16589
  };
16590
+ var aggregateProofTrendReconnect = (reconnects) => {
16591
+ if (reconnects.length === 0) {
16592
+ return;
16593
+ }
16594
+ const hasFailure = reconnects.some((reconnect) => reconnect.status === "fail" || reconnect.exhausted === true || reconnect.reconnected === false || reconnect.resumed === false);
16595
+ return {
16596
+ attempts: maxNumber(reconnects.map((reconnect) => reconnect.attempts)),
16597
+ exhausted: reconnects.some((reconnect) => reconnect.exhausted === true),
16598
+ maxAttempts: maxNumber(reconnects.map((reconnect) => reconnect.maxAttempts)),
16599
+ resumeLatencyP95Ms: maxNumber(reconnects.map((reconnect) => reconnect.resumeLatencyP95Ms)),
16600
+ reconnected: reconnects.some((reconnect) => reconnect.reconnected === true),
16601
+ resumed: reconnects.some((reconnect) => reconnect.resumed === true),
16602
+ samples: reconnects.reduce((total, reconnect) => total + (reconnect.samples ?? 0), 0),
16603
+ snapshotCount: reconnects.reduce((total, reconnect) => total + (reconnect.snapshotCount ?? 0), 0),
16604
+ status: hasFailure ? "fail" : reconnects.some((reconnect) => reconnect.status === "warn") ? "warn" : "pass"
16605
+ };
16606
+ };
16607
+ var summarizeReconnectTraceEvidence = (events) => {
16608
+ const reconnectEvents = events.filter((event) => event.type === "client.reconnect");
16609
+ if (reconnectEvents.length === 0) {
16610
+ return;
16611
+ }
16612
+ const statuses = reconnectEvents.map((event) => readString2(readTraceRecord(event).status)).filter((status) => status !== undefined);
16613
+ const reconnectPayloads = reconnectEvents.map((event) => readTraceRecord(event));
16614
+ return {
16615
+ attempts: maxNumber(reconnectPayloads.map((payload) => readNumber2(payload.attempts))),
16616
+ exhausted: statuses.includes("exhausted"),
16617
+ maxAttempts: maxNumber(reconnectPayloads.map((payload) => readNumber2(payload.maxAttempts))),
16618
+ resumeLatencyP95Ms: percentile2(reconnectPayloads.map((payload) => readNumber2(payload.resumeLatencyP95Ms) ?? readNumber2(payload.resumeLatencyMs)).filter((value) => value !== undefined), 95),
16619
+ reconnected: statuses.includes("reconnecting"),
16620
+ resumed: statuses.includes("resumed") || statuses.includes("pass"),
16621
+ samples: reconnectEvents.length,
16622
+ snapshotCount: reconnectEvents.length,
16623
+ status: reconnectEvents.some((event) => isFailingTraceStatus(readTraceStatus(readTraceRecord(event)))) ? "fail" : "pass"
16624
+ };
16625
+ };
16589
16626
  var readTraceRecord = (event) => event.payload;
16590
16627
  var readTraceProfileId = (events, options) => {
16591
16628
  for (const event of events) {
@@ -16733,6 +16770,7 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
16733
16770
  }).filter((value) => value !== undefined);
16734
16771
  const turnP95Ms = summarizeTurnTraceP95(sessionEvents);
16735
16772
  const providerP95Ms = maxNumber(providers.map((provider) => provider.p95Ms));
16773
+ const reconnect = summarizeReconnectTraceEvidence(sessionEvents);
16736
16774
  const runtimeChannel = summarizeRuntimeChannelTraceEvidence(sessionEvents);
16737
16775
  const surfaces = readRealCallProfileTraceSurfaces(sessionEvents);
16738
16776
  if (providers.length === 0 && runtimeChannel === undefined && liveLatencies.length === 0 && surfaces.length === 0) {
@@ -16748,6 +16786,7 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
16748
16786
  profileLabel: options.profileLabels?.[profileId] ?? (profileId === options.defaultProfileId ? options.defaultProfileLabel : undefined),
16749
16787
  providerP95Ms,
16750
16788
  providers,
16789
+ reconnect,
16751
16790
  runtimeChannel,
16752
16791
  sessionId,
16753
16792
  surfaces: surfaces.length > 0 ? surfaces : undefined,
@@ -16755,11 +16794,45 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
16755
16794
  };
16756
16795
  }).filter((evidence) => evidence !== undefined);
16757
16796
  };
16797
+ var normalizeReconnectReport = (report) => ("contract" in report) ? report.contract : report;
16798
+ var buildVoiceRealCallProfileEvidenceFromReconnectProofReports = (input, options = {}) => {
16799
+ const reports = Array.isArray(input) ? input : [input];
16800
+ const profileId = options.profileId ?? "reconnect-resume";
16801
+ return reports.map((report, index) => {
16802
+ const contract = normalizeReconnectReport(report);
16803
+ const generatedAt = "generatedAt" in report ? report.generatedAt : new Date(contract.checkedAt).toISOString();
16804
+ const ok = "ok" in report ? report.ok : contract.pass;
16805
+ const operationsRecordHref = typeof options.operationsRecordHref === "function" ? options.operationsRecordHref(report, index) : options.operationsRecordHref;
16806
+ const sessionId = typeof options.sessionId === "function" ? options.sessionId(report, index) : options.sessionId ?? `reconnect-proof-${String(index + 1)}-${String(contract.checkedAt)}`;
16807
+ return {
16808
+ generatedAt,
16809
+ ok,
16810
+ operationsRecordHref,
16811
+ profileDescription: options.profileDescription,
16812
+ profileId,
16813
+ profileLabel: options.profileLabel,
16814
+ reconnect: {
16815
+ attempts: contract.summary.attempts,
16816
+ exhausted: contract.summary.exhausted,
16817
+ maxAttempts: contract.summary.maxAttempts,
16818
+ resumeLatencyP95Ms: contract.resumeLatencyP95Ms,
16819
+ reconnected: contract.summary.reconnected,
16820
+ resumed: contract.summary.resumed,
16821
+ samples: 1,
16822
+ snapshotCount: contract.snapshotCount,
16823
+ status: ok ? "pass" : "fail"
16824
+ },
16825
+ sessionId,
16826
+ surfaces: [...new Set(["reconnect", ...options.surfaces ?? []])].sort()
16827
+ };
16828
+ });
16829
+ };
16758
16830
  var loadVoiceRealCallProfileEvidenceFromTraceStore = async (options) => buildVoiceRealCallProfileEvidenceFromTraceEvents(await options.store.list({ limit: options.limit ?? 5000 }), options);
16759
16831
  var realCallProfileTraceSignalTypes = new Set([
16760
16832
  "client.barge_in",
16761
16833
  "client.browser_media",
16762
16834
  "client.live_latency",
16835
+ "client.reconnect",
16763
16836
  "client.telephony_media",
16764
16837
  "provider.decision",
16765
16838
  "session.error",
@@ -16822,15 +16895,16 @@ var readProofTrendProviders = (reports) => aggregateProofTrendProviders(reports.
16822
16895
  var exceedsProofTrendBudget = (value, budget) => value !== undefined && (!Number.isFinite(value) || value > budget);
16823
16896
  var readProofTrendProfileStatus = (profile, budgets) => {
16824
16897
  const runtimeChannel = profile.runtimeChannel;
16825
- 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;
16826
- 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);
16898
+ const reconnect = profile.reconnect;
16899
+ const hasBudgetEvidence = profile.maxLiveP95Ms !== undefined || profile.maxProviderP95Ms !== undefined || profile.maxReconnectP95Ms !== 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 || reconnect?.resumeLatencyP95Ms !== undefined || reconnect?.samples !== undefined;
16900
+ const budgetFailed = exceedsProofTrendBudget(profile.maxLiveP95Ms, budgets.maxLiveP95Ms) || exceedsProofTrendBudget(profile.maxProviderP95Ms, budgets.maxProviderP95Ms) || exceedsProofTrendBudget(profile.maxReconnectP95Ms ?? reconnect?.resumeLatencyP95Ms, budgets.maxReconnectP95Ms) || 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) || reconnect?.status === "fail";
16827
16901
  if (budgetFailed || !hasBudgetEvidence && profile.status === "fail") {
16828
16902
  return "fail";
16829
16903
  }
16830
- if (profile.status === "warn" || runtimeChannel?.status === "warn" || profile.providers?.some((provider) => provider.status === "warn") === true) {
16904
+ if (profile.status === "warn" || reconnect?.status === "warn" || runtimeChannel?.status === "warn" || profile.providers?.some((provider) => provider.status === "warn") === true) {
16831
16905
  return "warn";
16832
16906
  }
16833
- if (hasBudgetEvidence || profile.status === "pass" || runtimeChannel?.status === "pass" || profile.providers?.some((provider) => provider.status === "pass") === true) {
16907
+ if (hasBudgetEvidence || profile.status === "pass" || reconnect?.status === "pass" || runtimeChannel?.status === "pass" || profile.providers?.some((provider) => provider.status === "pass") === true) {
16834
16908
  return "pass";
16835
16909
  }
16836
16910
  return;
@@ -16840,6 +16914,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
16840
16914
  const definitions = options.profiles ?? DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS;
16841
16915
  const providerCap = options.maxProviderP95Ms ?? 1000;
16842
16916
  const liveCap = options.maxLiveP95Ms ?? 800;
16917
+ const reconnectCap = options.maxReconnectP95Ms ?? 1500;
16843
16918
  const turnCap = options.maxTurnP95Ms ?? 700;
16844
16919
  const runtimeFirstAudioCap = options.maxRuntimeFirstAudioLatencyMs ?? 600;
16845
16920
  const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
@@ -16848,6 +16923,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
16848
16923
  const budgets = {
16849
16924
  maxLiveP95Ms: liveCap,
16850
16925
  maxProviderP95Ms: providerCap,
16926
+ maxReconnectP95Ms: reconnectCap,
16851
16927
  maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
16852
16928
  maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
16853
16929
  maxRuntimeJitterMs: runtimeJitterCap,
@@ -16870,8 +16946,10 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
16870
16946
  label: definition.label ?? profiles.find(Boolean)?.label,
16871
16947
  maxLiveP95Ms: maxNumber(profiles.map((profile) => profile.maxLiveP95Ms)),
16872
16948
  maxProviderP95Ms: maxNumber(profiles.map((profile) => profile.maxProviderP95Ms)),
16949
+ maxReconnectP95Ms: maxNumber(profiles.map((profile) => profile.maxReconnectP95Ms)),
16873
16950
  maxTurnP95Ms: maxNumber(profiles.map((profile) => profile.maxTurnP95Ms)),
16874
16951
  providers: aggregateProofTrendProviders(profiles.flatMap((profile) => profile.providers ?? [])),
16952
+ reconnect: aggregateProofTrendReconnect(profiles.map((profile) => profile.reconnect).filter((reconnect) => reconnect !== undefined)),
16875
16953
  runtimeChannel: aggregateProofTrendRuntimeChannel(profiles.map((profile) => profile.runtimeChannel).filter((channel) => channel !== undefined)),
16876
16954
  sessionCount: profiles.reduce((total, profile) => total + (profile.sessionCount ?? 0), 0),
16877
16955
  surfaces: [
@@ -16911,6 +16989,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
16911
16989
  var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
16912
16990
  const providerCap = options.maxProviderP95Ms ?? 1000;
16913
16991
  const liveCap = options.maxLiveP95Ms ?? 800;
16992
+ const reconnectCap = options.maxReconnectP95Ms ?? 1500;
16914
16993
  const turnCap = options.maxTurnP95Ms ?? 700;
16915
16994
  const runtimeFirstAudioCap = options.maxRuntimeFirstAudioLatencyMs ?? 600;
16916
16995
  const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
@@ -16919,6 +16998,7 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
16919
16998
  const budgets = {
16920
16999
  maxLiveP95Ms: liveCap,
16921
17000
  maxProviderP95Ms: providerCap,
17001
+ maxReconnectP95Ms: reconnectCap,
16922
17002
  maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
16923
17003
  maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
16924
17004
  maxRuntimeJitterMs: runtimeJitterCap,
@@ -16948,8 +17028,10 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
16948
17028
  label: definition.label ?? matchingEvidence.find((evidence) => evidence.profileLabel)?.profileLabel,
16949
17029
  maxLiveP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.liveP95Ms)),
16950
17030
  maxProviderP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.providerP95Ms)),
17031
+ maxReconnectP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.reconnect?.resumeLatencyP95Ms)),
16951
17032
  maxTurnP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.turnP95Ms)),
16952
17033
  providers: aggregateProofTrendProviders(matchingEvidence.flatMap((evidence) => evidence.providers ?? [])),
17034
+ reconnect: aggregateProofTrendReconnect(matchingEvidence.map((evidence) => evidence.reconnect).filter((reconnect) => reconnect !== undefined)),
16953
17035
  runtimeChannel: aggregateProofTrendRuntimeChannel(matchingEvidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined)),
16954
17036
  sessionCount: new Set(matchingEvidence.map((evidence) => evidence.sessionId)).size,
16955
17037
  surfaces: [
@@ -16968,6 +17050,7 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
16968
17050
  ok: evidence.ok !== false,
16969
17051
  providers: evidence.providers,
16970
17052
  runtimeChannel: evidence.runtimeChannel,
17053
+ reconnect: evidence.reconnect,
16971
17054
  turnLatency: evidence.turnP95Ms === undefined ? undefined : {
16972
17055
  p95Ms: evidence.turnP95Ms,
16973
17056
  samples: 1,
@@ -16978,9 +17061,11 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
16978
17061
  cycles: options.evidence.length,
16979
17062
  maxLiveP95Ms: maxNumber(options.evidence.map((evidence) => evidence.liveP95Ms)),
16980
17063
  maxProviderP95Ms: maxNumber(options.evidence.map((evidence) => evidence.providerP95Ms)),
17064
+ maxReconnectP95Ms: maxNumber(options.evidence.map((evidence) => evidence.reconnect?.resumeLatencyP95Ms)),
16981
17065
  maxTurnP95Ms: maxNumber(options.evidence.map((evidence) => evidence.turnP95Ms)),
16982
17066
  profiles,
16983
17067
  providers: aggregateProofTrendProviders(options.evidence.flatMap((evidence) => evidence.providers ?? [])),
17068
+ reconnect: aggregateProofTrendReconnect(options.evidence.map((evidence) => evidence.reconnect).filter((reconnect) => reconnect !== undefined)),
16984
17069
  runtimeChannel: aggregateProofTrendRuntimeChannel(options.evidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined))
16985
17070
  };
16986
17071
  return buildVoiceProofTrendReport({
@@ -17799,10 +17884,12 @@ var buildVoiceRealCallProfileHistoryReport = (options = {}) => {
17799
17884
  failedReports: history.filter((report) => report.ok !== true).length,
17800
17885
  maxLiveP95Ms: maxNumber(profileHistory.map(readProofTrendMaxLiveP95)),
17801
17886
  maxProviderP95Ms: maxNumber(profileHistory.map(readProofTrendMaxProviderP95)),
17887
+ maxReconnectP95Ms: maxNumber(profileHistory.map(readProofTrendMaxReconnectP95)),
17802
17888
  maxTurnP95Ms: maxNumber(profileHistory.map(readProofTrendMaxTurnP95)),
17803
17889
  profileCount: profiles.length,
17804
17890
  profiles,
17805
17891
  providers: readProofTrendProviders(profileHistory),
17892
+ reconnect: aggregateProofTrendReconnect(profileHistory.map((report) => report.summary.reconnect).filter((reconnect) => reconnect !== undefined)),
17806
17893
  runtimeChannel: aggregateProofTrendRuntimeChannel(profileHistory.map(readProofTrendRuntimeChannel).filter((channel) => channel !== undefined))
17807
17894
  };
17808
17895
  const trend = buildVoiceProofTrendReport({
@@ -20505,6 +20592,7 @@ var normalizeSample = (input) => {
20505
20592
  interruptionP95Ms: input.summary.runtimeChannel?.maxInterruptionP95Ms,
20506
20593
  ok: input.ok,
20507
20594
  providerP95Ms: input.summary.maxProviderP95Ms,
20595
+ reconnectP95Ms: input.summary.maxReconnectP95Ms ?? input.summary.reconnect?.resumeLatencyP95Ms,
20508
20596
  runId: input.runId,
20509
20597
  source: input.source || input.outputDir,
20510
20598
  turnP95Ms: input.summary.maxTurnP95Ms
@@ -42631,6 +42719,7 @@ export {
42631
42719
  buildVoiceRealCallProfileReadinessCheck,
42632
42720
  buildVoiceRealCallProfileHistoryReport,
42633
42721
  buildVoiceRealCallProfileEvidenceFromTraceEvents,
42722
+ buildVoiceRealCallProfileEvidenceFromReconnectProofReports,
42634
42723
  buildVoiceRealCallProfileDefaults,
42635
42724
  buildVoiceReadinessRecoveryActions,
42636
42725
  buildVoiceProviderSloReport,
@@ -2,14 +2,17 @@ import { Elysia } from "elysia";
2
2
  import type { Database } from "bun:sqlite";
3
3
  import { type VoiceProviderDecisionStatus } from "./providerDecisionTraces";
4
4
  import type { VoiceProductionReadinessAction, VoiceProductionReadinessCheck, VoiceReadinessRecoveryAction } from "./productionReadiness";
5
+ import type { VoiceReconnectContractReport, VoiceReconnectProofReport } from "./reconnectContract";
5
6
  import { StoredVoiceTraceEvent, VoiceTraceEventStore } from "./trace";
6
7
  export type VoiceProofTrendStatus = "empty" | "fail" | "pass" | "stale";
7
8
  export type VoiceProofTrendSummary = {
8
9
  cycles?: number;
9
10
  maxLiveP95Ms?: number;
10
11
  maxProviderP95Ms?: number;
12
+ maxReconnectP95Ms?: number;
11
13
  profiles?: VoiceProofTrendProfileSummary[];
12
14
  providers?: VoiceProofTrendProviderSummary[];
15
+ reconnect?: VoiceProofTrendReconnectSummary;
13
16
  runtimeChannel?: VoiceProofTrendRuntimeChannelSummary;
14
17
  maxTurnP95Ms?: number;
15
18
  };
@@ -20,8 +23,10 @@ export type VoiceProofTrendProfileSummary = {
20
23
  label?: string;
21
24
  maxLiveP95Ms?: number;
22
25
  maxProviderP95Ms?: number;
26
+ maxReconnectP95Ms?: number;
23
27
  maxTurnP95Ms?: number;
24
28
  providers?: VoiceProofTrendProviderSummary[];
29
+ reconnect?: VoiceProofTrendReconnectSummary;
25
30
  runtimeChannel?: VoiceProofTrendRuntimeChannelSummary;
26
31
  sessionCount?: number;
27
32
  status?: string;
@@ -34,6 +39,7 @@ export type VoiceProofTrendProfileDefinition = {
34
39
  liveOffsetMs?: number;
35
40
  maxLiveP95Ms?: number;
36
41
  maxProviderP95Ms?: number;
42
+ maxReconnectP95Ms?: number;
37
43
  maxRuntimeFirstAudioLatencyMs?: number;
38
44
  maxRuntimeInterruptionP95Ms?: number;
39
45
  maxRuntimeJitterMs?: number;
@@ -62,6 +68,17 @@ export type VoiceProofTrendRuntimeChannelSummary = {
62
68
  samples?: number;
63
69
  status?: string;
64
70
  };
71
+ export type VoiceProofTrendReconnectSummary = {
72
+ attempts?: number;
73
+ exhausted?: boolean;
74
+ maxAttempts?: number;
75
+ resumeLatencyP95Ms?: number;
76
+ reconnected?: boolean;
77
+ resumed?: boolean;
78
+ samples?: number;
79
+ snapshotCount?: number;
80
+ status?: string;
81
+ };
65
82
  export type VoiceProofTrendCycle = {
66
83
  at?: string;
67
84
  cycle?: number;
@@ -83,6 +100,7 @@ export type VoiceProofTrendCycle = {
83
100
  status?: string;
84
101
  };
85
102
  providers?: VoiceProofTrendProviderSummary[];
103
+ reconnect?: VoiceProofTrendReconnectSummary;
86
104
  runtimeChannel?: VoiceProofTrendRuntimeChannelSummary;
87
105
  turnLatency?: {
88
106
  p95Ms?: number;
@@ -120,6 +138,7 @@ export type VoiceProofTrendReport = {
120
138
  export type VoiceProofTrendProfileSummaryOptions = {
121
139
  maxLiveP95Ms?: number;
122
140
  maxProviderP95Ms?: number;
141
+ maxReconnectP95Ms?: number;
123
142
  maxRuntimeFirstAudioLatencyMs?: number;
124
143
  maxRuntimeInterruptionP95Ms?: number;
125
144
  maxRuntimeJitterMs?: number;
@@ -137,11 +156,20 @@ export type VoiceProofTrendRealCallProfileEvidence = {
137
156
  profileLabel?: string;
138
157
  providerP95Ms?: number;
139
158
  providers?: VoiceProofTrendProviderSummary[];
159
+ reconnect?: VoiceProofTrendReconnectSummary;
140
160
  runtimeChannel?: VoiceProofTrendRuntimeChannelSummary;
141
161
  sessionId: string;
142
162
  surfaces?: string[];
143
163
  turnP95Ms?: number;
144
164
  };
165
+ export type VoiceReconnectRealCallProfileEvidenceOptions = {
166
+ operationsRecordHref?: string | ((report: VoiceReconnectProofReport | VoiceReconnectContractReport, index: number) => string | undefined);
167
+ profileDescription?: string;
168
+ profileId?: string;
169
+ profileLabel?: string;
170
+ sessionId?: string | ((report: VoiceReconnectProofReport | VoiceReconnectContractReport, index: number) => string);
171
+ surfaces?: readonly string[];
172
+ };
145
173
  export type VoiceRealCallProfileTraceEvidenceOptions = {
146
174
  defaultProfileId?: string;
147
175
  defaultProfileLabel?: string;
@@ -577,6 +605,7 @@ export declare const readVoiceProofTrendReportFile: (path: string, options?: {
577
605
  maxAgeMs?: number;
578
606
  }) => Promise<VoiceProofTrendReport>;
579
607
  export declare const buildVoiceRealCallProfileEvidenceFromTraceEvents: (events: readonly StoredVoiceTraceEvent[], options?: VoiceRealCallProfileTraceEvidenceOptions) => VoiceProofTrendRealCallProfileEvidence[];
608
+ export declare const buildVoiceRealCallProfileEvidenceFromReconnectProofReports: (input: VoiceReconnectProofReport | VoiceReconnectContractReport | readonly (VoiceReconnectProofReport | VoiceReconnectContractReport)[], options?: VoiceReconnectRealCallProfileEvidenceOptions) => VoiceProofTrendRealCallProfileEvidence[];
580
609
  export declare const loadVoiceRealCallProfileEvidenceFromTraceStore: (options: VoiceRealCallProfileTraceStoreEvidenceOptions) => Promise<VoiceProofTrendRealCallProfileEvidence[]>;
581
610
  export declare const createVoiceRealCallProfileTraceCollector: <TEvent extends StoredVoiceTraceEvent = StoredVoiceTraceEvent>(options: VoiceRealCallProfileTraceCollectorOptions<TEvent>) => VoiceRealCallProfileTraceCollector<TEvent>;
582
611
  export declare const buildVoiceProofTrendProfileSummaries: (input: VoiceProofTrendReport | readonly VoiceProofTrendReport[], options?: VoiceProofTrendProfileSummaryOptions) => VoiceProofTrendProfileSummary[];
@@ -3430,6 +3430,7 @@ var readString = (value) => typeof value === "string" && value.trim() ? value :
3430
3430
  var readNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
3431
3431
  var readProofTrendMaxLiveP95 = (report) => report.summary.maxLiveP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.liveLatency?.p95Ms));
3432
3432
  var readProofTrendMaxProviderP95 = (report) => report.summary.maxProviderP95Ms ?? maxNumber((report.summary.providers ?? []).map((provider) => provider.p95Ms)) ?? maxNumber(report.cycles.flatMap((cycle) => (cycle.providers ?? []).map((provider) => provider.p95Ms)));
3433
+ var readProofTrendMaxReconnectP95 = (report) => report.summary.maxReconnectP95Ms ?? report.summary.reconnect?.resumeLatencyP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.reconnect?.resumeLatencyP95Ms));
3433
3434
  var readProofTrendMaxTurnP95 = (report) => report.summary.maxTurnP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.turnLatency?.p95Ms));
3434
3435
  var readRuntimeChannelMetric = (report, key) => {
3435
3436
  const summaryValue = report.summary.runtimeChannel?.[key];
@@ -3491,6 +3492,42 @@ var aggregateProofTrendRuntimeChannel = (channels) => {
3491
3492
  status: channels.some((channel) => channel.status === "fail") ? "fail" : channels.some((channel) => channel.status === "warn") ? "warn" : channels.every((channel) => channel.status === "pass") ? "pass" : undefined
3492
3493
  };
3493
3494
  };
3495
+ var aggregateProofTrendReconnect = (reconnects) => {
3496
+ if (reconnects.length === 0) {
3497
+ return;
3498
+ }
3499
+ const hasFailure = reconnects.some((reconnect) => reconnect.status === "fail" || reconnect.exhausted === true || reconnect.reconnected === false || reconnect.resumed === false);
3500
+ return {
3501
+ attempts: maxNumber(reconnects.map((reconnect) => reconnect.attempts)),
3502
+ exhausted: reconnects.some((reconnect) => reconnect.exhausted === true),
3503
+ maxAttempts: maxNumber(reconnects.map((reconnect) => reconnect.maxAttempts)),
3504
+ resumeLatencyP95Ms: maxNumber(reconnects.map((reconnect) => reconnect.resumeLatencyP95Ms)),
3505
+ reconnected: reconnects.some((reconnect) => reconnect.reconnected === true),
3506
+ resumed: reconnects.some((reconnect) => reconnect.resumed === true),
3507
+ samples: reconnects.reduce((total, reconnect) => total + (reconnect.samples ?? 0), 0),
3508
+ snapshotCount: reconnects.reduce((total, reconnect) => total + (reconnect.snapshotCount ?? 0), 0),
3509
+ status: hasFailure ? "fail" : reconnects.some((reconnect) => reconnect.status === "warn") ? "warn" : "pass"
3510
+ };
3511
+ };
3512
+ var summarizeReconnectTraceEvidence = (events) => {
3513
+ const reconnectEvents = events.filter((event) => event.type === "client.reconnect");
3514
+ if (reconnectEvents.length === 0) {
3515
+ return;
3516
+ }
3517
+ const statuses = reconnectEvents.map((event) => readString(readTraceRecord(event).status)).filter((status) => status !== undefined);
3518
+ const reconnectPayloads = reconnectEvents.map((event) => readTraceRecord(event));
3519
+ return {
3520
+ attempts: maxNumber(reconnectPayloads.map((payload) => readNumber2(payload.attempts))),
3521
+ exhausted: statuses.includes("exhausted"),
3522
+ maxAttempts: maxNumber(reconnectPayloads.map((payload) => readNumber2(payload.maxAttempts))),
3523
+ resumeLatencyP95Ms: percentile(reconnectPayloads.map((payload) => readNumber2(payload.resumeLatencyP95Ms) ?? readNumber2(payload.resumeLatencyMs)).filter((value) => value !== undefined), 95),
3524
+ reconnected: statuses.includes("reconnecting"),
3525
+ resumed: statuses.includes("resumed") || statuses.includes("pass"),
3526
+ samples: reconnectEvents.length,
3527
+ snapshotCount: reconnectEvents.length,
3528
+ status: reconnectEvents.some((event) => isFailingTraceStatus(readTraceStatus(readTraceRecord(event)))) ? "fail" : "pass"
3529
+ };
3530
+ };
3494
3531
  var readTraceRecord = (event) => event.payload;
3495
3532
  var readTraceProfileId = (events, options) => {
3496
3533
  for (const event of events) {
@@ -3638,6 +3675,7 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
3638
3675
  }).filter((value) => value !== undefined);
3639
3676
  const turnP95Ms = summarizeTurnTraceP95(sessionEvents);
3640
3677
  const providerP95Ms = maxNumber(providers.map((provider) => provider.p95Ms));
3678
+ const reconnect = summarizeReconnectTraceEvidence(sessionEvents);
3641
3679
  const runtimeChannel = summarizeRuntimeChannelTraceEvidence(sessionEvents);
3642
3680
  const surfaces = readRealCallProfileTraceSurfaces(sessionEvents);
3643
3681
  if (providers.length === 0 && runtimeChannel === undefined && liveLatencies.length === 0 && surfaces.length === 0) {
@@ -3653,6 +3691,7 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
3653
3691
  profileLabel: options.profileLabels?.[profileId] ?? (profileId === options.defaultProfileId ? options.defaultProfileLabel : undefined),
3654
3692
  providerP95Ms,
3655
3693
  providers,
3694
+ reconnect,
3656
3695
  runtimeChannel,
3657
3696
  sessionId,
3658
3697
  surfaces: surfaces.length > 0 ? surfaces : undefined,
@@ -3660,11 +3699,45 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
3660
3699
  };
3661
3700
  }).filter((evidence) => evidence !== undefined);
3662
3701
  };
3702
+ var normalizeReconnectReport = (report) => ("contract" in report) ? report.contract : report;
3703
+ var buildVoiceRealCallProfileEvidenceFromReconnectProofReports = (input, options = {}) => {
3704
+ const reports = Array.isArray(input) ? input : [input];
3705
+ const profileId = options.profileId ?? "reconnect-resume";
3706
+ return reports.map((report, index) => {
3707
+ const contract = normalizeReconnectReport(report);
3708
+ const generatedAt = "generatedAt" in report ? report.generatedAt : new Date(contract.checkedAt).toISOString();
3709
+ const ok = "ok" in report ? report.ok : contract.pass;
3710
+ const operationsRecordHref = typeof options.operationsRecordHref === "function" ? options.operationsRecordHref(report, index) : options.operationsRecordHref;
3711
+ const sessionId = typeof options.sessionId === "function" ? options.sessionId(report, index) : options.sessionId ?? `reconnect-proof-${String(index + 1)}-${String(contract.checkedAt)}`;
3712
+ return {
3713
+ generatedAt,
3714
+ ok,
3715
+ operationsRecordHref,
3716
+ profileDescription: options.profileDescription,
3717
+ profileId,
3718
+ profileLabel: options.profileLabel,
3719
+ reconnect: {
3720
+ attempts: contract.summary.attempts,
3721
+ exhausted: contract.summary.exhausted,
3722
+ maxAttempts: contract.summary.maxAttempts,
3723
+ resumeLatencyP95Ms: contract.resumeLatencyP95Ms,
3724
+ reconnected: contract.summary.reconnected,
3725
+ resumed: contract.summary.resumed,
3726
+ samples: 1,
3727
+ snapshotCount: contract.snapshotCount,
3728
+ status: ok ? "pass" : "fail"
3729
+ },
3730
+ sessionId,
3731
+ surfaces: [...new Set(["reconnect", ...options.surfaces ?? []])].sort()
3732
+ };
3733
+ });
3734
+ };
3663
3735
  var loadVoiceRealCallProfileEvidenceFromTraceStore = async (options) => buildVoiceRealCallProfileEvidenceFromTraceEvents(await options.store.list({ limit: options.limit ?? 5000 }), options);
3664
3736
  var realCallProfileTraceSignalTypes = new Set([
3665
3737
  "client.barge_in",
3666
3738
  "client.browser_media",
3667
3739
  "client.live_latency",
3740
+ "client.reconnect",
3668
3741
  "client.telephony_media",
3669
3742
  "provider.decision",
3670
3743
  "session.error",
@@ -3727,15 +3800,16 @@ var readProofTrendProviders = (reports) => aggregateProofTrendProviders(reports.
3727
3800
  var exceedsProofTrendBudget = (value, budget) => value !== undefined && (!Number.isFinite(value) || value > budget);
3728
3801
  var readProofTrendProfileStatus = (profile, budgets) => {
3729
3802
  const runtimeChannel = profile.runtimeChannel;
3730
- 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;
3731
- 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);
3803
+ const reconnect = profile.reconnect;
3804
+ const hasBudgetEvidence = profile.maxLiveP95Ms !== undefined || profile.maxProviderP95Ms !== undefined || profile.maxReconnectP95Ms !== 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 || reconnect?.resumeLatencyP95Ms !== undefined || reconnect?.samples !== undefined;
3805
+ const budgetFailed = exceedsProofTrendBudget(profile.maxLiveP95Ms, budgets.maxLiveP95Ms) || exceedsProofTrendBudget(profile.maxProviderP95Ms, budgets.maxProviderP95Ms) || exceedsProofTrendBudget(profile.maxReconnectP95Ms ?? reconnect?.resumeLatencyP95Ms, budgets.maxReconnectP95Ms) || 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) || reconnect?.status === "fail";
3732
3806
  if (budgetFailed || !hasBudgetEvidence && profile.status === "fail") {
3733
3807
  return "fail";
3734
3808
  }
3735
- if (profile.status === "warn" || runtimeChannel?.status === "warn" || profile.providers?.some((provider) => provider.status === "warn") === true) {
3809
+ if (profile.status === "warn" || reconnect?.status === "warn" || runtimeChannel?.status === "warn" || profile.providers?.some((provider) => provider.status === "warn") === true) {
3736
3810
  return "warn";
3737
3811
  }
3738
- if (hasBudgetEvidence || profile.status === "pass" || runtimeChannel?.status === "pass" || profile.providers?.some((provider) => provider.status === "pass") === true) {
3812
+ if (hasBudgetEvidence || profile.status === "pass" || reconnect?.status === "pass" || runtimeChannel?.status === "pass" || profile.providers?.some((provider) => provider.status === "pass") === true) {
3739
3813
  return "pass";
3740
3814
  }
3741
3815
  return;
@@ -3745,6 +3819,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
3745
3819
  const definitions = options.profiles ?? DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS;
3746
3820
  const providerCap = options.maxProviderP95Ms ?? 1000;
3747
3821
  const liveCap = options.maxLiveP95Ms ?? 800;
3822
+ const reconnectCap = options.maxReconnectP95Ms ?? 1500;
3748
3823
  const turnCap = options.maxTurnP95Ms ?? 700;
3749
3824
  const runtimeFirstAudioCap = options.maxRuntimeFirstAudioLatencyMs ?? 600;
3750
3825
  const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
@@ -3753,6 +3828,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
3753
3828
  const budgets = {
3754
3829
  maxLiveP95Ms: liveCap,
3755
3830
  maxProviderP95Ms: providerCap,
3831
+ maxReconnectP95Ms: reconnectCap,
3756
3832
  maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
3757
3833
  maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
3758
3834
  maxRuntimeJitterMs: runtimeJitterCap,
@@ -3775,8 +3851,10 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
3775
3851
  label: definition.label ?? profiles.find(Boolean)?.label,
3776
3852
  maxLiveP95Ms: maxNumber(profiles.map((profile) => profile.maxLiveP95Ms)),
3777
3853
  maxProviderP95Ms: maxNumber(profiles.map((profile) => profile.maxProviderP95Ms)),
3854
+ maxReconnectP95Ms: maxNumber(profiles.map((profile) => profile.maxReconnectP95Ms)),
3778
3855
  maxTurnP95Ms: maxNumber(profiles.map((profile) => profile.maxTurnP95Ms)),
3779
3856
  providers: aggregateProofTrendProviders(profiles.flatMap((profile) => profile.providers ?? [])),
3857
+ reconnect: aggregateProofTrendReconnect(profiles.map((profile) => profile.reconnect).filter((reconnect) => reconnect !== undefined)),
3780
3858
  runtimeChannel: aggregateProofTrendRuntimeChannel(profiles.map((profile) => profile.runtimeChannel).filter((channel) => channel !== undefined)),
3781
3859
  sessionCount: profiles.reduce((total, profile) => total + (profile.sessionCount ?? 0), 0),
3782
3860
  surfaces: [
@@ -3816,6 +3894,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
3816
3894
  var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
3817
3895
  const providerCap = options.maxProviderP95Ms ?? 1000;
3818
3896
  const liveCap = options.maxLiveP95Ms ?? 800;
3897
+ const reconnectCap = options.maxReconnectP95Ms ?? 1500;
3819
3898
  const turnCap = options.maxTurnP95Ms ?? 700;
3820
3899
  const runtimeFirstAudioCap = options.maxRuntimeFirstAudioLatencyMs ?? 600;
3821
3900
  const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
@@ -3824,6 +3903,7 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
3824
3903
  const budgets = {
3825
3904
  maxLiveP95Ms: liveCap,
3826
3905
  maxProviderP95Ms: providerCap,
3906
+ maxReconnectP95Ms: reconnectCap,
3827
3907
  maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
3828
3908
  maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
3829
3909
  maxRuntimeJitterMs: runtimeJitterCap,
@@ -3853,8 +3933,10 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
3853
3933
  label: definition.label ?? matchingEvidence.find((evidence) => evidence.profileLabel)?.profileLabel,
3854
3934
  maxLiveP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.liveP95Ms)),
3855
3935
  maxProviderP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.providerP95Ms)),
3936
+ maxReconnectP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.reconnect?.resumeLatencyP95Ms)),
3856
3937
  maxTurnP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.turnP95Ms)),
3857
3938
  providers: aggregateProofTrendProviders(matchingEvidence.flatMap((evidence) => evidence.providers ?? [])),
3939
+ reconnect: aggregateProofTrendReconnect(matchingEvidence.map((evidence) => evidence.reconnect).filter((reconnect) => reconnect !== undefined)),
3858
3940
  runtimeChannel: aggregateProofTrendRuntimeChannel(matchingEvidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined)),
3859
3941
  sessionCount: new Set(matchingEvidence.map((evidence) => evidence.sessionId)).size,
3860
3942
  surfaces: [
@@ -3873,6 +3955,7 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
3873
3955
  ok: evidence.ok !== false,
3874
3956
  providers: evidence.providers,
3875
3957
  runtimeChannel: evidence.runtimeChannel,
3958
+ reconnect: evidence.reconnect,
3876
3959
  turnLatency: evidence.turnP95Ms === undefined ? undefined : {
3877
3960
  p95Ms: evidence.turnP95Ms,
3878
3961
  samples: 1,
@@ -3883,9 +3966,11 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
3883
3966
  cycles: options.evidence.length,
3884
3967
  maxLiveP95Ms: maxNumber(options.evidence.map((evidence) => evidence.liveP95Ms)),
3885
3968
  maxProviderP95Ms: maxNumber(options.evidence.map((evidence) => evidence.providerP95Ms)),
3969
+ maxReconnectP95Ms: maxNumber(options.evidence.map((evidence) => evidence.reconnect?.resumeLatencyP95Ms)),
3886
3970
  maxTurnP95Ms: maxNumber(options.evidence.map((evidence) => evidence.turnP95Ms)),
3887
3971
  profiles,
3888
3972
  providers: aggregateProofTrendProviders(options.evidence.flatMap((evidence) => evidence.providers ?? [])),
3973
+ reconnect: aggregateProofTrendReconnect(options.evidence.map((evidence) => evidence.reconnect).filter((reconnect) => reconnect !== undefined)),
3889
3974
  runtimeChannel: aggregateProofTrendRuntimeChannel(options.evidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined))
3890
3975
  };
3891
3976
  return buildVoiceProofTrendReport({
@@ -4704,10 +4789,12 @@ var buildVoiceRealCallProfileHistoryReport = (options = {}) => {
4704
4789
  failedReports: history.filter((report) => report.ok !== true).length,
4705
4790
  maxLiveP95Ms: maxNumber(profileHistory.map(readProofTrendMaxLiveP95)),
4706
4791
  maxProviderP95Ms: maxNumber(profileHistory.map(readProofTrendMaxProviderP95)),
4792
+ maxReconnectP95Ms: maxNumber(profileHistory.map(readProofTrendMaxReconnectP95)),
4707
4793
  maxTurnP95Ms: maxNumber(profileHistory.map(readProofTrendMaxTurnP95)),
4708
4794
  profileCount: profiles.length,
4709
4795
  profiles,
4710
4796
  providers: readProofTrendProviders(profileHistory),
4797
+ reconnect: aggregateProofTrendReconnect(profileHistory.map((report) => report.summary.reconnect).filter((reconnect) => reconnect !== undefined)),
4711
4798
  runtimeChannel: aggregateProofTrendRuntimeChannel(profileHistory.map(readProofTrendRuntimeChannel).filter((channel) => channel !== undefined))
4712
4799
  };
4713
4800
  const trend = buildVoiceProofTrendReport({
package/dist/vue/index.js CHANGED
@@ -3351,6 +3351,7 @@ var readString = (value) => typeof value === "string" && value.trim() ? value :
3351
3351
  var readNumber2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
3352
3352
  var readProofTrendMaxLiveP95 = (report) => report.summary.maxLiveP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.liveLatency?.p95Ms));
3353
3353
  var readProofTrendMaxProviderP95 = (report) => report.summary.maxProviderP95Ms ?? maxNumber((report.summary.providers ?? []).map((provider) => provider.p95Ms)) ?? maxNumber(report.cycles.flatMap((cycle) => (cycle.providers ?? []).map((provider) => provider.p95Ms)));
3354
+ var readProofTrendMaxReconnectP95 = (report) => report.summary.maxReconnectP95Ms ?? report.summary.reconnect?.resumeLatencyP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.reconnect?.resumeLatencyP95Ms));
3354
3355
  var readProofTrendMaxTurnP95 = (report) => report.summary.maxTurnP95Ms ?? maxNumber(report.cycles.map((cycle) => cycle.turnLatency?.p95Ms));
3355
3356
  var readRuntimeChannelMetric = (report, key) => {
3356
3357
  const summaryValue = report.summary.runtimeChannel?.[key];
@@ -3412,6 +3413,42 @@ var aggregateProofTrendRuntimeChannel = (channels) => {
3412
3413
  status: channels.some((channel) => channel.status === "fail") ? "fail" : channels.some((channel) => channel.status === "warn") ? "warn" : channels.every((channel) => channel.status === "pass") ? "pass" : undefined
3413
3414
  };
3414
3415
  };
3416
+ var aggregateProofTrendReconnect = (reconnects) => {
3417
+ if (reconnects.length === 0) {
3418
+ return;
3419
+ }
3420
+ const hasFailure = reconnects.some((reconnect) => reconnect.status === "fail" || reconnect.exhausted === true || reconnect.reconnected === false || reconnect.resumed === false);
3421
+ return {
3422
+ attempts: maxNumber(reconnects.map((reconnect) => reconnect.attempts)),
3423
+ exhausted: reconnects.some((reconnect) => reconnect.exhausted === true),
3424
+ maxAttempts: maxNumber(reconnects.map((reconnect) => reconnect.maxAttempts)),
3425
+ resumeLatencyP95Ms: maxNumber(reconnects.map((reconnect) => reconnect.resumeLatencyP95Ms)),
3426
+ reconnected: reconnects.some((reconnect) => reconnect.reconnected === true),
3427
+ resumed: reconnects.some((reconnect) => reconnect.resumed === true),
3428
+ samples: reconnects.reduce((total, reconnect) => total + (reconnect.samples ?? 0), 0),
3429
+ snapshotCount: reconnects.reduce((total, reconnect) => total + (reconnect.snapshotCount ?? 0), 0),
3430
+ status: hasFailure ? "fail" : reconnects.some((reconnect) => reconnect.status === "warn") ? "warn" : "pass"
3431
+ };
3432
+ };
3433
+ var summarizeReconnectTraceEvidence = (events) => {
3434
+ const reconnectEvents = events.filter((event) => event.type === "client.reconnect");
3435
+ if (reconnectEvents.length === 0) {
3436
+ return;
3437
+ }
3438
+ const statuses = reconnectEvents.map((event) => readString(readTraceRecord(event).status)).filter((status) => status !== undefined);
3439
+ const reconnectPayloads = reconnectEvents.map((event) => readTraceRecord(event));
3440
+ return {
3441
+ attempts: maxNumber(reconnectPayloads.map((payload) => readNumber2(payload.attempts))),
3442
+ exhausted: statuses.includes("exhausted"),
3443
+ maxAttempts: maxNumber(reconnectPayloads.map((payload) => readNumber2(payload.maxAttempts))),
3444
+ resumeLatencyP95Ms: percentile(reconnectPayloads.map((payload) => readNumber2(payload.resumeLatencyP95Ms) ?? readNumber2(payload.resumeLatencyMs)).filter((value) => value !== undefined), 95),
3445
+ reconnected: statuses.includes("reconnecting"),
3446
+ resumed: statuses.includes("resumed") || statuses.includes("pass"),
3447
+ samples: reconnectEvents.length,
3448
+ snapshotCount: reconnectEvents.length,
3449
+ status: reconnectEvents.some((event) => isFailingTraceStatus(readTraceStatus(readTraceRecord(event)))) ? "fail" : "pass"
3450
+ };
3451
+ };
3415
3452
  var readTraceRecord = (event) => event.payload;
3416
3453
  var readTraceProfileId = (events, options) => {
3417
3454
  for (const event of events) {
@@ -3559,6 +3596,7 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
3559
3596
  }).filter((value) => value !== undefined);
3560
3597
  const turnP95Ms = summarizeTurnTraceP95(sessionEvents);
3561
3598
  const providerP95Ms = maxNumber(providers.map((provider) => provider.p95Ms));
3599
+ const reconnect = summarizeReconnectTraceEvidence(sessionEvents);
3562
3600
  const runtimeChannel = summarizeRuntimeChannelTraceEvidence(sessionEvents);
3563
3601
  const surfaces = readRealCallProfileTraceSurfaces(sessionEvents);
3564
3602
  if (providers.length === 0 && runtimeChannel === undefined && liveLatencies.length === 0 && surfaces.length === 0) {
@@ -3574,6 +3612,7 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
3574
3612
  profileLabel: options.profileLabels?.[profileId] ?? (profileId === options.defaultProfileId ? options.defaultProfileLabel : undefined),
3575
3613
  providerP95Ms,
3576
3614
  providers,
3615
+ reconnect,
3577
3616
  runtimeChannel,
3578
3617
  sessionId,
3579
3618
  surfaces: surfaces.length > 0 ? surfaces : undefined,
@@ -3581,11 +3620,45 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
3581
3620
  };
3582
3621
  }).filter((evidence) => evidence !== undefined);
3583
3622
  };
3623
+ var normalizeReconnectReport = (report) => ("contract" in report) ? report.contract : report;
3624
+ var buildVoiceRealCallProfileEvidenceFromReconnectProofReports = (input, options = {}) => {
3625
+ const reports = Array.isArray(input) ? input : [input];
3626
+ const profileId = options.profileId ?? "reconnect-resume";
3627
+ return reports.map((report, index) => {
3628
+ const contract = normalizeReconnectReport(report);
3629
+ const generatedAt = "generatedAt" in report ? report.generatedAt : new Date(contract.checkedAt).toISOString();
3630
+ const ok = "ok" in report ? report.ok : contract.pass;
3631
+ const operationsRecordHref = typeof options.operationsRecordHref === "function" ? options.operationsRecordHref(report, index) : options.operationsRecordHref;
3632
+ const sessionId = typeof options.sessionId === "function" ? options.sessionId(report, index) : options.sessionId ?? `reconnect-proof-${String(index + 1)}-${String(contract.checkedAt)}`;
3633
+ return {
3634
+ generatedAt,
3635
+ ok,
3636
+ operationsRecordHref,
3637
+ profileDescription: options.profileDescription,
3638
+ profileId,
3639
+ profileLabel: options.profileLabel,
3640
+ reconnect: {
3641
+ attempts: contract.summary.attempts,
3642
+ exhausted: contract.summary.exhausted,
3643
+ maxAttempts: contract.summary.maxAttempts,
3644
+ resumeLatencyP95Ms: contract.resumeLatencyP95Ms,
3645
+ reconnected: contract.summary.reconnected,
3646
+ resumed: contract.summary.resumed,
3647
+ samples: 1,
3648
+ snapshotCount: contract.snapshotCount,
3649
+ status: ok ? "pass" : "fail"
3650
+ },
3651
+ sessionId,
3652
+ surfaces: [...new Set(["reconnect", ...options.surfaces ?? []])].sort()
3653
+ };
3654
+ });
3655
+ };
3584
3656
  var loadVoiceRealCallProfileEvidenceFromTraceStore = async (options) => buildVoiceRealCallProfileEvidenceFromTraceEvents(await options.store.list({ limit: options.limit ?? 5000 }), options);
3585
3657
  var realCallProfileTraceSignalTypes = new Set([
3586
3658
  "client.barge_in",
3587
3659
  "client.browser_media",
3588
3660
  "client.live_latency",
3661
+ "client.reconnect",
3589
3662
  "client.telephony_media",
3590
3663
  "provider.decision",
3591
3664
  "session.error",
@@ -3648,15 +3721,16 @@ var readProofTrendProviders = (reports) => aggregateProofTrendProviders(reports.
3648
3721
  var exceedsProofTrendBudget = (value, budget) => value !== undefined && (!Number.isFinite(value) || value > budget);
3649
3722
  var readProofTrendProfileStatus = (profile, budgets) => {
3650
3723
  const runtimeChannel = profile.runtimeChannel;
3651
- 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;
3652
- 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);
3724
+ const reconnect = profile.reconnect;
3725
+ const hasBudgetEvidence = profile.maxLiveP95Ms !== undefined || profile.maxProviderP95Ms !== undefined || profile.maxReconnectP95Ms !== 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 || reconnect?.resumeLatencyP95Ms !== undefined || reconnect?.samples !== undefined;
3726
+ const budgetFailed = exceedsProofTrendBudget(profile.maxLiveP95Ms, budgets.maxLiveP95Ms) || exceedsProofTrendBudget(profile.maxProviderP95Ms, budgets.maxProviderP95Ms) || exceedsProofTrendBudget(profile.maxReconnectP95Ms ?? reconnect?.resumeLatencyP95Ms, budgets.maxReconnectP95Ms) || 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) || reconnect?.status === "fail";
3653
3727
  if (budgetFailed || !hasBudgetEvidence && profile.status === "fail") {
3654
3728
  return "fail";
3655
3729
  }
3656
- if (profile.status === "warn" || runtimeChannel?.status === "warn" || profile.providers?.some((provider) => provider.status === "warn") === true) {
3730
+ if (profile.status === "warn" || reconnect?.status === "warn" || runtimeChannel?.status === "warn" || profile.providers?.some((provider) => provider.status === "warn") === true) {
3657
3731
  return "warn";
3658
3732
  }
3659
- if (hasBudgetEvidence || profile.status === "pass" || runtimeChannel?.status === "pass" || profile.providers?.some((provider) => provider.status === "pass") === true) {
3733
+ if (hasBudgetEvidence || profile.status === "pass" || reconnect?.status === "pass" || runtimeChannel?.status === "pass" || profile.providers?.some((provider) => provider.status === "pass") === true) {
3660
3734
  return "pass";
3661
3735
  }
3662
3736
  return;
@@ -3666,6 +3740,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
3666
3740
  const definitions = options.profiles ?? DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS;
3667
3741
  const providerCap = options.maxProviderP95Ms ?? 1000;
3668
3742
  const liveCap = options.maxLiveP95Ms ?? 800;
3743
+ const reconnectCap = options.maxReconnectP95Ms ?? 1500;
3669
3744
  const turnCap = options.maxTurnP95Ms ?? 700;
3670
3745
  const runtimeFirstAudioCap = options.maxRuntimeFirstAudioLatencyMs ?? 600;
3671
3746
  const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
@@ -3674,6 +3749,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
3674
3749
  const budgets = {
3675
3750
  maxLiveP95Ms: liveCap,
3676
3751
  maxProviderP95Ms: providerCap,
3752
+ maxReconnectP95Ms: reconnectCap,
3677
3753
  maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
3678
3754
  maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
3679
3755
  maxRuntimeJitterMs: runtimeJitterCap,
@@ -3696,8 +3772,10 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
3696
3772
  label: definition.label ?? profiles.find(Boolean)?.label,
3697
3773
  maxLiveP95Ms: maxNumber(profiles.map((profile) => profile.maxLiveP95Ms)),
3698
3774
  maxProviderP95Ms: maxNumber(profiles.map((profile) => profile.maxProviderP95Ms)),
3775
+ maxReconnectP95Ms: maxNumber(profiles.map((profile) => profile.maxReconnectP95Ms)),
3699
3776
  maxTurnP95Ms: maxNumber(profiles.map((profile) => profile.maxTurnP95Ms)),
3700
3777
  providers: aggregateProofTrendProviders(profiles.flatMap((profile) => profile.providers ?? [])),
3778
+ reconnect: aggregateProofTrendReconnect(profiles.map((profile) => profile.reconnect).filter((reconnect) => reconnect !== undefined)),
3701
3779
  runtimeChannel: aggregateProofTrendRuntimeChannel(profiles.map((profile) => profile.runtimeChannel).filter((channel) => channel !== undefined)),
3702
3780
  sessionCount: profiles.reduce((total, profile) => total + (profile.sessionCount ?? 0), 0),
3703
3781
  surfaces: [
@@ -3737,6 +3815,7 @@ var buildVoiceProofTrendProfileSummaries = (input, options = {}) => {
3737
3815
  var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
3738
3816
  const providerCap = options.maxProviderP95Ms ?? 1000;
3739
3817
  const liveCap = options.maxLiveP95Ms ?? 800;
3818
+ const reconnectCap = options.maxReconnectP95Ms ?? 1500;
3740
3819
  const turnCap = options.maxTurnP95Ms ?? 700;
3741
3820
  const runtimeFirstAudioCap = options.maxRuntimeFirstAudioLatencyMs ?? 600;
3742
3821
  const runtimeInterruptionCap = options.maxRuntimeInterruptionP95Ms ?? 300;
@@ -3745,6 +3824,7 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
3745
3824
  const budgets = {
3746
3825
  maxLiveP95Ms: liveCap,
3747
3826
  maxProviderP95Ms: providerCap,
3827
+ maxReconnectP95Ms: reconnectCap,
3748
3828
  maxRuntimeFirstAudioLatencyMs: runtimeFirstAudioCap,
3749
3829
  maxRuntimeInterruptionP95Ms: runtimeInterruptionCap,
3750
3830
  maxRuntimeJitterMs: runtimeJitterCap,
@@ -3774,8 +3854,10 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
3774
3854
  label: definition.label ?? matchingEvidence.find((evidence) => evidence.profileLabel)?.profileLabel,
3775
3855
  maxLiveP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.liveP95Ms)),
3776
3856
  maxProviderP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.providerP95Ms)),
3857
+ maxReconnectP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.reconnect?.resumeLatencyP95Ms)),
3777
3858
  maxTurnP95Ms: maxNumber(matchingEvidence.map((evidence) => evidence.turnP95Ms)),
3778
3859
  providers: aggregateProofTrendProviders(matchingEvidence.flatMap((evidence) => evidence.providers ?? [])),
3860
+ reconnect: aggregateProofTrendReconnect(matchingEvidence.map((evidence) => evidence.reconnect).filter((reconnect) => reconnect !== undefined)),
3779
3861
  runtimeChannel: aggregateProofTrendRuntimeChannel(matchingEvidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined)),
3780
3862
  sessionCount: new Set(matchingEvidence.map((evidence) => evidence.sessionId)).size,
3781
3863
  surfaces: [
@@ -3794,6 +3876,7 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
3794
3876
  ok: evidence.ok !== false,
3795
3877
  providers: evidence.providers,
3796
3878
  runtimeChannel: evidence.runtimeChannel,
3879
+ reconnect: evidence.reconnect,
3797
3880
  turnLatency: evidence.turnP95Ms === undefined ? undefined : {
3798
3881
  p95Ms: evidence.turnP95Ms,
3799
3882
  samples: 1,
@@ -3804,9 +3887,11 @@ var buildVoiceProofTrendReportFromRealCallProfiles = (options) => {
3804
3887
  cycles: options.evidence.length,
3805
3888
  maxLiveP95Ms: maxNumber(options.evidence.map((evidence) => evidence.liveP95Ms)),
3806
3889
  maxProviderP95Ms: maxNumber(options.evidence.map((evidence) => evidence.providerP95Ms)),
3890
+ maxReconnectP95Ms: maxNumber(options.evidence.map((evidence) => evidence.reconnect?.resumeLatencyP95Ms)),
3807
3891
  maxTurnP95Ms: maxNumber(options.evidence.map((evidence) => evidence.turnP95Ms)),
3808
3892
  profiles,
3809
3893
  providers: aggregateProofTrendProviders(options.evidence.flatMap((evidence) => evidence.providers ?? [])),
3894
+ reconnect: aggregateProofTrendReconnect(options.evidence.map((evidence) => evidence.reconnect).filter((reconnect) => reconnect !== undefined)),
3810
3895
  runtimeChannel: aggregateProofTrendRuntimeChannel(options.evidence.map((evidence) => evidence.runtimeChannel).filter((channel) => channel !== undefined))
3811
3896
  };
3812
3897
  return buildVoiceProofTrendReport({
@@ -4625,10 +4710,12 @@ var buildVoiceRealCallProfileHistoryReport = (options = {}) => {
4625
4710
  failedReports: history.filter((report) => report.ok !== true).length,
4626
4711
  maxLiveP95Ms: maxNumber(profileHistory.map(readProofTrendMaxLiveP95)),
4627
4712
  maxProviderP95Ms: maxNumber(profileHistory.map(readProofTrendMaxProviderP95)),
4713
+ maxReconnectP95Ms: maxNumber(profileHistory.map(readProofTrendMaxReconnectP95)),
4628
4714
  maxTurnP95Ms: maxNumber(profileHistory.map(readProofTrendMaxTurnP95)),
4629
4715
  profileCount: profiles.length,
4630
4716
  profiles,
4631
4717
  providers: readProofTrendProviders(profileHistory),
4718
+ reconnect: aggregateProofTrendReconnect(profileHistory.map((report) => report.summary.reconnect).filter((reconnect) => reconnect !== undefined)),
4632
4719
  runtimeChannel: aggregateProofTrendRuntimeChannel(profileHistory.map(readProofTrendRuntimeChannel).filter((channel) => channel !== undefined))
4633
4720
  };
4634
4721
  const trend = buildVoiceProofTrendReport({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.451",
3
+ "version": "0.0.22-beta.452",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",