@absolutejs/voice 0.0.22-beta.386 → 0.0.22-beta.388

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.
@@ -5993,6 +5993,9 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
5993
5993
  const providerP95Ms = maxNumber(providers.map((provider) => provider.p95Ms));
5994
5994
  const runtimeChannel = summarizeRuntimeChannelTraceEvidence(sessionEvents);
5995
5995
  const surfaces = readRealCallProfileTraceSurfaces(sessionEvents);
5996
+ if (providers.length === 0 && runtimeChannel === undefined && liveLatencies.length === 0 && surfaces.length === 0) {
5997
+ return;
5998
+ }
5996
5999
  return {
5997
6000
  generatedAt: new Date(Math.max(...sessionEvents.map((event) => event.at))).toISOString(),
5998
6001
  liveP95Ms: percentile(liveLatencies, 95),
@@ -6554,6 +6557,7 @@ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
6554
6557
  const requestTimeoutMs = options.requestTimeoutMs ?? 5000;
6555
6558
  const jobPollMs = options.jobPollMs ?? 1200;
6556
6559
  const jobTimeoutMs = options.jobTimeoutMs ?? 600000;
6560
+ const maxPasses = typeof options.maxPasses === "number" && Number.isFinite(options.maxPasses) && options.maxPasses > 0 ? Math.floor(options.maxPasses) : 3;
6557
6561
  const readinessCheckLabel = options.readinessCheckLabel ?? "Real-call profile history";
6558
6562
  const fetchImpl = options.fetch ?? fetch;
6559
6563
  const recoveryActionsHref = options.recoveryActionsHref ?? "/api/production-readiness/recovery-actions";
@@ -6595,70 +6599,84 @@ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
6595
6599
  actionCount: 0,
6596
6600
  actions,
6597
6601
  jobs: [],
6602
+ passes: 0,
6598
6603
  ok: realCallProfileGate2?.status === "pass",
6599
6604
  realCallProfileGate: realCallProfileGate2,
6600
6605
  startFailures: []
6601
6606
  };
6602
6607
  }
6603
- options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel.`);
6604
- for (const action of actions) {
6605
- options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
6606
- }
6607
- const starts = await Promise.allSettled(actions.map(async (action) => {
6608
- if (!action.href) {
6609
- throw new Error("Recovery action is missing href.");
6610
- }
6611
- const body = await fetchJson(action.href, { method: "POST" });
6612
- return { action, ...body };
6613
- }));
6614
- const startedJobs = starts.flatMap((result) => {
6615
- if (result.status === "rejected") {
6616
- return [];
6617
- }
6618
- return result.value.jobId ? [result.value] : [];
6619
- });
6620
- const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
6621
- {
6622
- action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
6623
- error: result.reason instanceof Error ? result.reason.message : String(result.reason)
6624
- }
6625
- ] : []);
6626
- const pollJob = async (jobId) => {
6627
- const deadline = Date.now() + jobTimeoutMs;
6628
- while (Date.now() < deadline) {
6629
- const body = await fetchJson(resolveJobHref(jobId));
6630
- const job = body.job;
6631
- if (!job) {
6632
- throw new Error(`Recovery job ${jobId} was not found.`);
6608
+ const allJobs = [];
6609
+ const allStartFailures = [];
6610
+ let realCallProfileGate = null;
6611
+ let passes = 0;
6612
+ for (let pass = 1;pass <= maxPasses; pass += 1) {
6613
+ passes = pass;
6614
+ options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel (pass ${String(pass)}/${String(maxPasses)}).`);
6615
+ for (const action of actions) {
6616
+ options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
6617
+ }
6618
+ const starts = await Promise.allSettled(actions.map(async (action) => {
6619
+ if (!action.href) {
6620
+ throw new Error("Recovery action is missing href.");
6633
6621
  }
6634
- if (job.status === "pass" || job.status === "fail") {
6635
- return job;
6622
+ const body = await fetchJson(action.href, { method: "POST" });
6623
+ return { action, ...body };
6624
+ }));
6625
+ const startedJobs = starts.flatMap((result) => {
6626
+ if (result.status === "rejected") {
6627
+ return [];
6636
6628
  }
6637
- await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
6629
+ return result.value.jobId ? [result.value] : [];
6630
+ });
6631
+ const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
6632
+ {
6633
+ action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
6634
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
6635
+ }
6636
+ ] : []);
6637
+ allStartFailures.push(...startFailures);
6638
+ const pollJob = async (jobId) => {
6639
+ const deadline = Date.now() + jobTimeoutMs;
6640
+ while (Date.now() < deadline) {
6641
+ const body = await fetchJson(resolveJobHref(jobId));
6642
+ const job = body.job;
6643
+ if (!job) {
6644
+ throw new Error(`Recovery job ${jobId} was not found.`);
6645
+ }
6646
+ if (job.status === "pass" || job.status === "fail") {
6647
+ return job;
6648
+ }
6649
+ await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
6650
+ }
6651
+ throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
6652
+ };
6653
+ options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
6654
+ const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
6655
+ const jobs = jobResults.map((result, index) => ({
6656
+ action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
6657
+ jobId: startedJobs[index]?.jobId,
6658
+ result: result.status === "fulfilled" ? result.value : {
6659
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
6660
+ status: "fail"
6661
+ }
6662
+ }));
6663
+ allJobs.push(...jobs);
6664
+ if (refreshHref !== false) {
6665
+ await fetchJson(refreshHref, { method: "POST" });
6638
6666
  }
6639
- throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
6640
- };
6641
- options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
6642
- const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
6643
- const jobs = jobResults.map((result, index) => ({
6644
- action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
6645
- jobId: startedJobs[index]?.jobId,
6646
- result: result.status === "fulfilled" ? result.value : {
6647
- error: result.reason instanceof Error ? result.reason.message : String(result.reason),
6648
- status: "fail"
6667
+ realCallProfileGate = await getGate(true);
6668
+ if (realCallProfileGate?.status === "pass") {
6669
+ break;
6649
6670
  }
6650
- }));
6651
- if (refreshHref !== false) {
6652
- await fetchJson(refreshHref, { method: "POST" });
6653
6671
  }
6654
- const realCallProfileGate = await getGate(true);
6655
6672
  return {
6656
- actionCount: actions.length,
6673
+ actionCount: actions.length * passes,
6657
6674
  actions,
6658
- jobs,
6659
- ok: startFailures.length === 0 && jobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
6675
+ jobs: allJobs,
6676
+ passes,
6677
+ ok: allStartFailures.length === 0 && allJobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
6660
6678
  realCallProfileGate,
6661
- startFailures
6679
+ startFailures: allStartFailures
6662
6680
  };
6663
6681
  };
6664
6682
  var buildVoiceRealCallProfileRecoveryActions = (report, options = {}) => {
package/dist/index.js CHANGED
@@ -16512,6 +16512,9 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
16512
16512
  const providerP95Ms = maxNumber(providers.map((provider) => provider.p95Ms));
16513
16513
  const runtimeChannel = summarizeRuntimeChannelTraceEvidence(sessionEvents);
16514
16514
  const surfaces = readRealCallProfileTraceSurfaces(sessionEvents);
16515
+ if (providers.length === 0 && runtimeChannel === undefined && liveLatencies.length === 0 && surfaces.length === 0) {
16516
+ return;
16517
+ }
16515
16518
  return {
16516
16519
  generatedAt: new Date(Math.max(...sessionEvents.map((event) => event.at))).toISOString(),
16517
16520
  liveP95Ms: percentile2(liveLatencies, 95),
@@ -17073,6 +17076,7 @@ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
17073
17076
  const requestTimeoutMs = options.requestTimeoutMs ?? 5000;
17074
17077
  const jobPollMs = options.jobPollMs ?? 1200;
17075
17078
  const jobTimeoutMs = options.jobTimeoutMs ?? 600000;
17079
+ const maxPasses = typeof options.maxPasses === "number" && Number.isFinite(options.maxPasses) && options.maxPasses > 0 ? Math.floor(options.maxPasses) : 3;
17076
17080
  const readinessCheckLabel = options.readinessCheckLabel ?? "Real-call profile history";
17077
17081
  const fetchImpl = options.fetch ?? fetch;
17078
17082
  const recoveryActionsHref = options.recoveryActionsHref ?? "/api/production-readiness/recovery-actions";
@@ -17114,70 +17118,84 @@ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
17114
17118
  actionCount: 0,
17115
17119
  actions,
17116
17120
  jobs: [],
17121
+ passes: 0,
17117
17122
  ok: realCallProfileGate2?.status === "pass",
17118
17123
  realCallProfileGate: realCallProfileGate2,
17119
17124
  startFailures: []
17120
17125
  };
17121
17126
  }
17122
- options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel.`);
17123
- for (const action of actions) {
17124
- options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
17125
- }
17126
- const starts = await Promise.allSettled(actions.map(async (action) => {
17127
- if (!action.href) {
17128
- throw new Error("Recovery action is missing href.");
17129
- }
17130
- const body = await fetchJson(action.href, { method: "POST" });
17131
- return { action, ...body };
17132
- }));
17133
- const startedJobs = starts.flatMap((result) => {
17134
- if (result.status === "rejected") {
17135
- return [];
17127
+ const allJobs = [];
17128
+ const allStartFailures = [];
17129
+ let realCallProfileGate = null;
17130
+ let passes = 0;
17131
+ for (let pass = 1;pass <= maxPasses; pass += 1) {
17132
+ passes = pass;
17133
+ options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel (pass ${String(pass)}/${String(maxPasses)}).`);
17134
+ for (const action of actions) {
17135
+ options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
17136
+ }
17137
+ const starts = await Promise.allSettled(actions.map(async (action) => {
17138
+ if (!action.href) {
17139
+ throw new Error("Recovery action is missing href.");
17140
+ }
17141
+ const body = await fetchJson(action.href, { method: "POST" });
17142
+ return { action, ...body };
17143
+ }));
17144
+ const startedJobs = starts.flatMap((result) => {
17145
+ if (result.status === "rejected") {
17146
+ return [];
17147
+ }
17148
+ return result.value.jobId ? [result.value] : [];
17149
+ });
17150
+ const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
17151
+ {
17152
+ action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
17153
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
17154
+ }
17155
+ ] : []);
17156
+ allStartFailures.push(...startFailures);
17157
+ const pollJob = async (jobId) => {
17158
+ const deadline = Date.now() + jobTimeoutMs;
17159
+ while (Date.now() < deadline) {
17160
+ const body = await fetchJson(resolveJobHref(jobId));
17161
+ const job = body.job;
17162
+ if (!job) {
17163
+ throw new Error(`Recovery job ${jobId} was not found.`);
17164
+ }
17165
+ if (job.status === "pass" || job.status === "fail") {
17166
+ return job;
17167
+ }
17168
+ await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
17169
+ }
17170
+ throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
17171
+ };
17172
+ options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
17173
+ const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
17174
+ const jobs = jobResults.map((result, index) => ({
17175
+ action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
17176
+ jobId: startedJobs[index]?.jobId,
17177
+ result: result.status === "fulfilled" ? result.value : {
17178
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
17179
+ status: "fail"
17180
+ }
17181
+ }));
17182
+ allJobs.push(...jobs);
17183
+ if (refreshHref !== false) {
17184
+ await fetchJson(refreshHref, { method: "POST" });
17136
17185
  }
17137
- return result.value.jobId ? [result.value] : [];
17138
- });
17139
- const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
17140
- {
17141
- action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
17142
- error: result.reason instanceof Error ? result.reason.message : String(result.reason)
17143
- }
17144
- ] : []);
17145
- const pollJob = async (jobId) => {
17146
- const deadline = Date.now() + jobTimeoutMs;
17147
- while (Date.now() < deadline) {
17148
- const body = await fetchJson(resolveJobHref(jobId));
17149
- const job = body.job;
17150
- if (!job) {
17151
- throw new Error(`Recovery job ${jobId} was not found.`);
17152
- }
17153
- if (job.status === "pass" || job.status === "fail") {
17154
- return job;
17155
- }
17156
- await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
17157
- }
17158
- throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
17159
- };
17160
- options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
17161
- const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
17162
- const jobs = jobResults.map((result, index) => ({
17163
- action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
17164
- jobId: startedJobs[index]?.jobId,
17165
- result: result.status === "fulfilled" ? result.value : {
17166
- error: result.reason instanceof Error ? result.reason.message : String(result.reason),
17167
- status: "fail"
17186
+ realCallProfileGate = await getGate(true);
17187
+ if (realCallProfileGate?.status === "pass") {
17188
+ break;
17168
17189
  }
17169
- }));
17170
- if (refreshHref !== false) {
17171
- await fetchJson(refreshHref, { method: "POST" });
17172
17190
  }
17173
- const realCallProfileGate = await getGate(true);
17174
17191
  return {
17175
- actionCount: actions.length,
17192
+ actionCount: actions.length * passes,
17176
17193
  actions,
17177
- jobs,
17178
- ok: startFailures.length === 0 && jobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
17194
+ jobs: allJobs,
17195
+ passes,
17196
+ ok: allStartFailures.length === 0 && allJobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
17179
17197
  realCallProfileGate,
17180
- startFailures
17198
+ startFailures: allStartFailures
17181
17199
  };
17182
17200
  };
17183
17201
  var buildVoiceRealCallProfileRecoveryActions = (report, options = {}) => {
@@ -492,6 +492,7 @@ export type VoiceRealCallProfileRecoveryLoopReport = {
492
492
  actionCount: number;
493
493
  actions: VoiceRealCallProfileRecoveryLoopAction[];
494
494
  jobs: VoiceRealCallProfileRecoveryLoopJobResult[];
495
+ passes: number;
495
496
  ok: boolean;
496
497
  realCallProfileGate: VoiceProductionReadinessCheck | null;
497
498
  startFailures: VoiceRealCallProfileRecoveryLoopStartFailure[];
@@ -504,6 +505,7 @@ export type VoiceRealCallProfileRecoveryLoopOptions = {
504
505
  jobPollMs?: number;
505
506
  jobTimeoutMs?: number;
506
507
  logger?: Pick<Console, 'log'>;
508
+ maxPasses?: number;
507
509
  readinessCheckLabel?: string;
508
510
  readinessHref?: string;
509
511
  recoveryActionsHref?: string;
@@ -3580,6 +3580,9 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
3580
3580
  const providerP95Ms = maxNumber(providers.map((provider) => provider.p95Ms));
3581
3581
  const runtimeChannel = summarizeRuntimeChannelTraceEvidence(sessionEvents);
3582
3582
  const surfaces = readRealCallProfileTraceSurfaces(sessionEvents);
3583
+ if (providers.length === 0 && runtimeChannel === undefined && liveLatencies.length === 0 && surfaces.length === 0) {
3584
+ return;
3585
+ }
3583
3586
  return {
3584
3587
  generatedAt: new Date(Math.max(...sessionEvents.map((event) => event.at))).toISOString(),
3585
3588
  liveP95Ms: percentile(liveLatencies, 95),
@@ -4141,6 +4144,7 @@ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
4141
4144
  const requestTimeoutMs = options.requestTimeoutMs ?? 5000;
4142
4145
  const jobPollMs = options.jobPollMs ?? 1200;
4143
4146
  const jobTimeoutMs = options.jobTimeoutMs ?? 600000;
4147
+ const maxPasses = typeof options.maxPasses === "number" && Number.isFinite(options.maxPasses) && options.maxPasses > 0 ? Math.floor(options.maxPasses) : 3;
4144
4148
  const readinessCheckLabel = options.readinessCheckLabel ?? "Real-call profile history";
4145
4149
  const fetchImpl = options.fetch ?? fetch;
4146
4150
  const recoveryActionsHref = options.recoveryActionsHref ?? "/api/production-readiness/recovery-actions";
@@ -4182,70 +4186,84 @@ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
4182
4186
  actionCount: 0,
4183
4187
  actions,
4184
4188
  jobs: [],
4189
+ passes: 0,
4185
4190
  ok: realCallProfileGate2?.status === "pass",
4186
4191
  realCallProfileGate: realCallProfileGate2,
4187
4192
  startFailures: []
4188
4193
  };
4189
4194
  }
4190
- options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel.`);
4191
- for (const action of actions) {
4192
- options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
4193
- }
4194
- const starts = await Promise.allSettled(actions.map(async (action) => {
4195
- if (!action.href) {
4196
- throw new Error("Recovery action is missing href.");
4197
- }
4198
- const body = await fetchJson(action.href, { method: "POST" });
4199
- return { action, ...body };
4200
- }));
4201
- const startedJobs = starts.flatMap((result) => {
4202
- if (result.status === "rejected") {
4203
- return [];
4204
- }
4205
- return result.value.jobId ? [result.value] : [];
4206
- });
4207
- const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
4208
- {
4209
- action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
4210
- error: result.reason instanceof Error ? result.reason.message : String(result.reason)
4211
- }
4212
- ] : []);
4213
- const pollJob = async (jobId) => {
4214
- const deadline = Date.now() + jobTimeoutMs;
4215
- while (Date.now() < deadline) {
4216
- const body = await fetchJson(resolveJobHref(jobId));
4217
- const job = body.job;
4218
- if (!job) {
4219
- throw new Error(`Recovery job ${jobId} was not found.`);
4195
+ const allJobs = [];
4196
+ const allStartFailures = [];
4197
+ let realCallProfileGate = null;
4198
+ let passes = 0;
4199
+ for (let pass = 1;pass <= maxPasses; pass += 1) {
4200
+ passes = pass;
4201
+ options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel (pass ${String(pass)}/${String(maxPasses)}).`);
4202
+ for (const action of actions) {
4203
+ options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
4204
+ }
4205
+ const starts = await Promise.allSettled(actions.map(async (action) => {
4206
+ if (!action.href) {
4207
+ throw new Error("Recovery action is missing href.");
4220
4208
  }
4221
- if (job.status === "pass" || job.status === "fail") {
4222
- return job;
4209
+ const body = await fetchJson(action.href, { method: "POST" });
4210
+ return { action, ...body };
4211
+ }));
4212
+ const startedJobs = starts.flatMap((result) => {
4213
+ if (result.status === "rejected") {
4214
+ return [];
4223
4215
  }
4224
- await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
4216
+ return result.value.jobId ? [result.value] : [];
4217
+ });
4218
+ const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
4219
+ {
4220
+ action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
4221
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
4222
+ }
4223
+ ] : []);
4224
+ allStartFailures.push(...startFailures);
4225
+ const pollJob = async (jobId) => {
4226
+ const deadline = Date.now() + jobTimeoutMs;
4227
+ while (Date.now() < deadline) {
4228
+ const body = await fetchJson(resolveJobHref(jobId));
4229
+ const job = body.job;
4230
+ if (!job) {
4231
+ throw new Error(`Recovery job ${jobId} was not found.`);
4232
+ }
4233
+ if (job.status === "pass" || job.status === "fail") {
4234
+ return job;
4235
+ }
4236
+ await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
4237
+ }
4238
+ throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
4239
+ };
4240
+ options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
4241
+ const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
4242
+ const jobs = jobResults.map((result, index) => ({
4243
+ action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
4244
+ jobId: startedJobs[index]?.jobId,
4245
+ result: result.status === "fulfilled" ? result.value : {
4246
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
4247
+ status: "fail"
4248
+ }
4249
+ }));
4250
+ allJobs.push(...jobs);
4251
+ if (refreshHref !== false) {
4252
+ await fetchJson(refreshHref, { method: "POST" });
4225
4253
  }
4226
- throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
4227
- };
4228
- options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
4229
- const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
4230
- const jobs = jobResults.map((result, index) => ({
4231
- action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
4232
- jobId: startedJobs[index]?.jobId,
4233
- result: result.status === "fulfilled" ? result.value : {
4234
- error: result.reason instanceof Error ? result.reason.message : String(result.reason),
4235
- status: "fail"
4254
+ realCallProfileGate = await getGate(true);
4255
+ if (realCallProfileGate?.status === "pass") {
4256
+ break;
4236
4257
  }
4237
- }));
4238
- if (refreshHref !== false) {
4239
- await fetchJson(refreshHref, { method: "POST" });
4240
4258
  }
4241
- const realCallProfileGate = await getGate(true);
4242
4259
  return {
4243
- actionCount: actions.length,
4260
+ actionCount: actions.length * passes,
4244
4261
  actions,
4245
- jobs,
4246
- ok: startFailures.length === 0 && jobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
4262
+ jobs: allJobs,
4263
+ passes,
4264
+ ok: allStartFailures.length === 0 && allJobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
4247
4265
  realCallProfileGate,
4248
- startFailures
4266
+ startFailures: allStartFailures
4249
4267
  };
4250
4268
  };
4251
4269
  var buildVoiceRealCallProfileRecoveryActions = (report, options = {}) => {
package/dist/vue/index.js CHANGED
@@ -3501,6 +3501,9 @@ var buildVoiceRealCallProfileEvidenceFromTraceEvents = (events, options = {}) =>
3501
3501
  const providerP95Ms = maxNumber(providers.map((provider) => provider.p95Ms));
3502
3502
  const runtimeChannel = summarizeRuntimeChannelTraceEvidence(sessionEvents);
3503
3503
  const surfaces = readRealCallProfileTraceSurfaces(sessionEvents);
3504
+ if (providers.length === 0 && runtimeChannel === undefined && liveLatencies.length === 0 && surfaces.length === 0) {
3505
+ return;
3506
+ }
3504
3507
  return {
3505
3508
  generatedAt: new Date(Math.max(...sessionEvents.map((event) => event.at))).toISOString(),
3506
3509
  liveP95Ms: percentile(liveLatencies, 95),
@@ -4062,6 +4065,7 @@ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
4062
4065
  const requestTimeoutMs = options.requestTimeoutMs ?? 5000;
4063
4066
  const jobPollMs = options.jobPollMs ?? 1200;
4064
4067
  const jobTimeoutMs = options.jobTimeoutMs ?? 600000;
4068
+ const maxPasses = typeof options.maxPasses === "number" && Number.isFinite(options.maxPasses) && options.maxPasses > 0 ? Math.floor(options.maxPasses) : 3;
4065
4069
  const readinessCheckLabel = options.readinessCheckLabel ?? "Real-call profile history";
4066
4070
  const fetchImpl = options.fetch ?? fetch;
4067
4071
  const recoveryActionsHref = options.recoveryActionsHref ?? "/api/production-readiness/recovery-actions";
@@ -4103,70 +4107,84 @@ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
4103
4107
  actionCount: 0,
4104
4108
  actions,
4105
4109
  jobs: [],
4110
+ passes: 0,
4106
4111
  ok: realCallProfileGate2?.status === "pass",
4107
4112
  realCallProfileGate: realCallProfileGate2,
4108
4113
  startFailures: []
4109
4114
  };
4110
4115
  }
4111
- options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel.`);
4112
- for (const action of actions) {
4113
- options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
4114
- }
4115
- const starts = await Promise.allSettled(actions.map(async (action) => {
4116
- if (!action.href) {
4117
- throw new Error("Recovery action is missing href.");
4118
- }
4119
- const body = await fetchJson(action.href, { method: "POST" });
4120
- return { action, ...body };
4121
- }));
4122
- const startedJobs = starts.flatMap((result) => {
4123
- if (result.status === "rejected") {
4124
- return [];
4125
- }
4126
- return result.value.jobId ? [result.value] : [];
4127
- });
4128
- const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
4129
- {
4130
- action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
4131
- error: result.reason instanceof Error ? result.reason.message : String(result.reason)
4132
- }
4133
- ] : []);
4134
- const pollJob = async (jobId) => {
4135
- const deadline = Date.now() + jobTimeoutMs;
4136
- while (Date.now() < deadline) {
4137
- const body = await fetchJson(resolveJobHref(jobId));
4138
- const job = body.job;
4139
- if (!job) {
4140
- throw new Error(`Recovery job ${jobId} was not found.`);
4116
+ const allJobs = [];
4117
+ const allStartFailures = [];
4118
+ let realCallProfileGate = null;
4119
+ let passes = 0;
4120
+ for (let pass = 1;pass <= maxPasses; pass += 1) {
4121
+ passes = pass;
4122
+ options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel (pass ${String(pass)}/${String(maxPasses)}).`);
4123
+ for (const action of actions) {
4124
+ options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
4125
+ }
4126
+ const starts = await Promise.allSettled(actions.map(async (action) => {
4127
+ if (!action.href) {
4128
+ throw new Error("Recovery action is missing href.");
4141
4129
  }
4142
- if (job.status === "pass" || job.status === "fail") {
4143
- return job;
4130
+ const body = await fetchJson(action.href, { method: "POST" });
4131
+ return { action, ...body };
4132
+ }));
4133
+ const startedJobs = starts.flatMap((result) => {
4134
+ if (result.status === "rejected") {
4135
+ return [];
4144
4136
  }
4145
- await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
4137
+ return result.value.jobId ? [result.value] : [];
4138
+ });
4139
+ const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
4140
+ {
4141
+ action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
4142
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
4143
+ }
4144
+ ] : []);
4145
+ allStartFailures.push(...startFailures);
4146
+ const pollJob = async (jobId) => {
4147
+ const deadline = Date.now() + jobTimeoutMs;
4148
+ while (Date.now() < deadline) {
4149
+ const body = await fetchJson(resolveJobHref(jobId));
4150
+ const job = body.job;
4151
+ if (!job) {
4152
+ throw new Error(`Recovery job ${jobId} was not found.`);
4153
+ }
4154
+ if (job.status === "pass" || job.status === "fail") {
4155
+ return job;
4156
+ }
4157
+ await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
4158
+ }
4159
+ throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
4160
+ };
4161
+ options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
4162
+ const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
4163
+ const jobs = jobResults.map((result, index) => ({
4164
+ action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
4165
+ jobId: startedJobs[index]?.jobId,
4166
+ result: result.status === "fulfilled" ? result.value : {
4167
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
4168
+ status: "fail"
4169
+ }
4170
+ }));
4171
+ allJobs.push(...jobs);
4172
+ if (refreshHref !== false) {
4173
+ await fetchJson(refreshHref, { method: "POST" });
4146
4174
  }
4147
- throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
4148
- };
4149
- options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
4150
- const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
4151
- const jobs = jobResults.map((result, index) => ({
4152
- action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
4153
- jobId: startedJobs[index]?.jobId,
4154
- result: result.status === "fulfilled" ? result.value : {
4155
- error: result.reason instanceof Error ? result.reason.message : String(result.reason),
4156
- status: "fail"
4175
+ realCallProfileGate = await getGate(true);
4176
+ if (realCallProfileGate?.status === "pass") {
4177
+ break;
4157
4178
  }
4158
- }));
4159
- if (refreshHref !== false) {
4160
- await fetchJson(refreshHref, { method: "POST" });
4161
4179
  }
4162
- const realCallProfileGate = await getGate(true);
4163
4180
  return {
4164
- actionCount: actions.length,
4181
+ actionCount: actions.length * passes,
4165
4182
  actions,
4166
- jobs,
4167
- ok: startFailures.length === 0 && jobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
4183
+ jobs: allJobs,
4184
+ passes,
4185
+ ok: allStartFailures.length === 0 && allJobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
4168
4186
  realCallProfileGate,
4169
- startFailures
4187
+ startFailures: allStartFailures
4170
4188
  };
4171
4189
  };
4172
4190
  var buildVoiceRealCallProfileRecoveryActions = (report, options = {}) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.386",
3
+ "version": "0.0.22-beta.388",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",