@absolutejs/voice 0.0.22-beta.384 → 0.0.22-beta.385

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.
@@ -4686,6 +4686,136 @@ var appendRealCallRecoveryActionQuery = (href, query) => {
4686
4686
  const search = new URLSearchParams(entries).toString();
4687
4687
  return `${base}${separator}${search}${hash ? `#${hash}` : ""}`;
4688
4688
  };
4689
+ var sleepVoiceRealCallProfileRecoveryLoop = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
4690
+ var describeVoiceRealCallProfileRecoveryLoopAction = (action) => [
4691
+ action.label ?? action.id ?? "recovery action",
4692
+ action.profileId ? `profile=${action.profileId}` : undefined,
4693
+ action.href
4694
+ ].filter(Boolean).join(" ");
4695
+ var defaultVoiceRealCallProfileRecoveryLoopActionFilter = (action, readinessCheckLabel) => action.method?.toUpperCase() === "POST" && action.sourceCheckLabel === readinessCheckLabel && typeof action.href === "string" && action.href.length > 0;
4696
+ var uniqueVoiceRealCallProfileRecoveryLoopActions = (actions) => {
4697
+ const seen = new Set;
4698
+ return actions.filter((action) => {
4699
+ const key = `${action.method?.toUpperCase() ?? "GET"} ${action.href ?? ""}`;
4700
+ if (seen.has(key)) {
4701
+ return false;
4702
+ }
4703
+ seen.add(key);
4704
+ return true;
4705
+ });
4706
+ };
4707
+ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
4708
+ const baseUrl = options.baseUrl.replace(/\/$/, "");
4709
+ const requestTimeoutMs = options.requestTimeoutMs ?? 5000;
4710
+ const jobPollMs = options.jobPollMs ?? 1200;
4711
+ const jobTimeoutMs = options.jobTimeoutMs ?? 600000;
4712
+ const readinessCheckLabel = options.readinessCheckLabel ?? "Real-call profile history";
4713
+ const fetchImpl = options.fetch ?? fetch;
4714
+ const recoveryActionsHref = options.recoveryActionsHref ?? "/api/production-readiness/recovery-actions";
4715
+ const readinessHref = options.readinessHref ?? "/api/production-readiness";
4716
+ const refreshHref = options.refreshHref === undefined ? "/api/voice/real-call-profile-history/refresh" : options.refreshHref;
4717
+ const jobHref = options.jobHref ?? "/api/voice/real-call-profile-history/actions";
4718
+ const toAbsoluteUrl = (href) => new URL(href, baseUrl).toString();
4719
+ const parseJson = async (response) => {
4720
+ const text = await response.text();
4721
+ try {
4722
+ return JSON.parse(text);
4723
+ } catch (error) {
4724
+ throw new Error(`Expected JSON from ${response.url}, got: ${text.slice(0, 300)}`, { cause: error });
4725
+ }
4726
+ };
4727
+ const fetchJson = async (href, init) => {
4728
+ const response = await fetchImpl(toAbsoluteUrl(href), {
4729
+ headers: { accept: "application/json", ...init?.headers },
4730
+ ...init,
4731
+ signal: init?.signal ?? AbortSignal.timeout(requestTimeoutMs)
4732
+ });
4733
+ if (!response.ok) {
4734
+ throw new Error(`${href} returned HTTP ${String(response.status)}.`);
4735
+ }
4736
+ return parseJson(response);
4737
+ };
4738
+ const resolveJobHref = (jobId) => typeof jobHref === "function" ? jobHref(jobId) : `${jobHref.replace(/\/$/, "")}/${jobId}`;
4739
+ const getGate = async (fresh = false) => {
4740
+ const href = fresh ? `${readinessHref}${readinessHref.includes("?") ? "&" : "?"}voiceRecoveryLoopFresh=${String(Date.now())}` : readinessHref;
4741
+ const readiness = await fetchJson(href);
4742
+ return readiness.checks?.find((check) => check.label === readinessCheckLabel) ?? null;
4743
+ };
4744
+ const actionsResponse = await fetchJson(recoveryActionsHref);
4745
+ const actionFilter = options.actionFilter ?? ((action) => defaultVoiceRealCallProfileRecoveryLoopActionFilter(action, readinessCheckLabel));
4746
+ const actions = uniqueVoiceRealCallProfileRecoveryLoopActions((actionsResponse.actions ?? []).filter(actionFilter));
4747
+ if (actions.length === 0) {
4748
+ const realCallProfileGate2 = await getGate();
4749
+ return {
4750
+ actionCount: 0,
4751
+ actions,
4752
+ jobs: [],
4753
+ ok: realCallProfileGate2?.status === "pass",
4754
+ realCallProfileGate: realCallProfileGate2,
4755
+ startFailures: []
4756
+ };
4757
+ }
4758
+ options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel.`);
4759
+ for (const action of actions) {
4760
+ options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
4761
+ }
4762
+ const starts = await Promise.allSettled(actions.map(async (action) => {
4763
+ if (!action.href) {
4764
+ throw new Error("Recovery action is missing href.");
4765
+ }
4766
+ const body = await fetchJson(action.href, { method: "POST" });
4767
+ return { action, ...body };
4768
+ }));
4769
+ const startedJobs = starts.flatMap((result) => {
4770
+ if (result.status === "rejected") {
4771
+ return [];
4772
+ }
4773
+ return result.value.jobId ? [result.value] : [];
4774
+ });
4775
+ const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
4776
+ {
4777
+ action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
4778
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
4779
+ }
4780
+ ] : []);
4781
+ const pollJob = async (jobId) => {
4782
+ const deadline = Date.now() + jobTimeoutMs;
4783
+ while (Date.now() < deadline) {
4784
+ const body = await fetchJson(resolveJobHref(jobId));
4785
+ const job = body.job;
4786
+ if (!job) {
4787
+ throw new Error(`Recovery job ${jobId} was not found.`);
4788
+ }
4789
+ if (job.status === "pass" || job.status === "fail") {
4790
+ return job;
4791
+ }
4792
+ await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
4793
+ }
4794
+ throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
4795
+ };
4796
+ options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
4797
+ const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
4798
+ const jobs = jobResults.map((result, index) => ({
4799
+ action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
4800
+ jobId: startedJobs[index]?.jobId,
4801
+ result: result.status === "fulfilled" ? result.value : {
4802
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
4803
+ status: "fail"
4804
+ }
4805
+ }));
4806
+ if (refreshHref !== false) {
4807
+ await fetchJson(refreshHref, { method: "POST" });
4808
+ }
4809
+ const realCallProfileGate = await getGate(true);
4810
+ return {
4811
+ actionCount: actions.length,
4812
+ actions,
4813
+ jobs,
4814
+ ok: startFailures.length === 0 && jobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
4815
+ realCallProfileGate,
4816
+ startFailures
4817
+ };
4818
+ };
4689
4819
  var buildVoiceRealCallProfileRecoveryActions = (report, options = {}) => {
4690
4820
  const actions = [
4691
4821
  {
package/dist/index.d.ts CHANGED
@@ -30,12 +30,12 @@ export { assertVoicePlatformCoverage, buildVoicePlatformCoverageSummary, createV
30
30
  export { assertVoiceCompetitiveCoverage, buildVoiceCompetitiveCoverageReport, createVoiceCompetitiveCoverageRoutes, evaluateVoiceCompetitiveCoverage, renderVoiceCompetitiveCoverageHTML, renderVoiceCompetitiveCoverageMarkdown } from './competitiveCoverage';
31
31
  export type { VoiceCompetitiveCoverageAssertionInput, VoiceCompetitiveCoverageAssertionReport, VoiceCompetitiveCoverageIssue, VoiceCompetitiveCoverageLevel, VoiceCompetitiveCoverageReport, VoiceCompetitiveCoverageReportInput, VoiceCompetitiveCoverageRoutesOptions, VoiceCompetitiveCoverageStatus, VoiceCompetitiveCoverageSummary, VoiceCompetitiveDepthLevel, VoiceCompetitiveEvidence, VoiceCompetitiveSurface } from './competitiveCoverage';
32
32
  export type { VoicePlatformCoverageAssertionInput, VoicePlatformCoverageAssertionReport, VoicePlatformCoverageEvidence, VoicePlatformCoverageRoutesOptions, VoicePlatformCoverageStatus, VoicePlatformCoverageSummary, VoicePlatformCoverageSummaryInput, VoicePlatformCoverageSurface } from './platformCoverage';
33
- export { assertVoiceProofTrendEvidence, 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, resolveVoiceRealCallProfileProviderRoute } from './proofTrends';
33
+ export { assertVoiceProofTrendEvidence, 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
34
  export { applyVoiceProfileSwitchGuard, buildVoiceProfileSwitchReadinessReport, buildVoiceProfileSwitchLiveDecisionReport, createVoiceProfileSwitchLiveDecisionRoutes, createVoiceProfileSwitchPolicyProofRoutes, createVoiceProfileSwitchReadinessRoutes, recommendVoiceProfileSwitch, renderVoiceProfileSwitchLiveDecisionHTML, renderVoiceProfileSwitchPolicyProofHTML, renderVoiceProfileSwitchReadinessHTML, runVoiceProfileSwitchPolicyProof } from './profileSwitchRecommendation';
35
35
  export type { VoiceProfileSwitchGuardAction, VoiceProfileSwitchGuardDecision, VoiceProfileSwitchGuardMode, VoiceProfileSwitchGuardOptions, VoiceProfileSwitchObservedSignals, VoiceProfileSwitchLiveDecisionEvidence, VoiceProfileSwitchLiveDecisionReport, VoiceProfileSwitchLiveDecisionReportOptions, VoiceProfileSwitchLiveDecisionRoutesOptions, VoiceProfileSwitchLiveDecisionSession, VoiceProfileSwitchPolicyProofCase, VoiceProfileSwitchPolicyProofCaseResult, VoiceProfileSwitchPolicyProofOptions, VoiceProfileSwitchPolicyProofReport, VoiceProfileSwitchPolicyProofRoutesOptions, VoiceProfileSwitchReadinessIssue, VoiceProfileSwitchReadinessOptions, VoiceProfileSwitchReadinessReport, VoiceProfileSwitchReadinessRoutesOptions, VoiceProfileSwitchReadinessStatus, VoiceProfileSwitchRecommendation, VoiceProfileSwitchRecommendationOptions } from './profileSwitchRecommendation';
36
36
  export { buildVoiceProviderDecisionTraceReport, createVoiceProviderDecisionTraceEvent, createVoiceProviderDecisionTraceRoutes, listVoiceProviderDecisionTraces, renderVoiceProviderDecisionTraceHTML, renderVoiceProviderDecisionTraceMarkdown } from './providerDecisionTraces';
37
37
  export type { VoiceProviderDecisionStatus, VoiceProviderDecisionSurfaceReport, VoiceProviderDecisionTrace, VoiceProviderDecisionTraceInput, VoiceProviderDecisionTraceIssue, VoiceProviderDecisionTraceReport, VoiceProviderDecisionTraceReportOptions, VoiceProviderDecisionTraceRoutesOptions } from './providerDecisionTraces';
38
- 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, VoiceSQLiteRealCallProfileRecoveryJobStoreOptions, VoiceRealCallProfileTraceCollector, VoiceRealCallProfileTraceCollectorEvidenceOptions, VoiceRealCallProfileTraceCollectorOptions, VoiceRealCallProfileTraceEvidenceOptions, VoiceRealCallProfileTraceStoreEvidenceOptions } from './proofTrends';
38
+ 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, VoiceSQLiteRealCallProfileRecoveryJobStoreOptions, VoiceRealCallProfileTraceCollector, VoiceRealCallProfileTraceCollectorEvidenceOptions, VoiceRealCallProfileTraceCollectorOptions, VoiceRealCallProfileTraceEvidenceOptions, VoiceRealCallProfileTraceStoreEvidenceOptions } from './proofTrends';
39
39
  export { assertVoiceSloCalibration, buildVoiceSloCalibrationReport, buildVoiceSloReadinessThresholdReport, createVoiceSloReadinessThresholdOptions, createVoiceSloReadinessThresholdRoutes, createVoiceSloThresholdProfile, createVoiceSloCalibrationRoutes, renderVoiceSloCalibrationMarkdown, renderVoiceSloReadinessThresholdHTML, renderVoiceSloReadinessThresholdMarkdown } from './sloCalibration';
40
40
  export type { VoiceSloCalibrationMetricKey, VoiceSloCalibrationOptions, VoiceSloCalibrationReport, VoiceSloCalibrationRoutesOptions, VoiceSloCalibrationSample, VoiceSloCalibrationStatus, VoiceSloCalibrationThreshold, VoiceSloCalibrationThresholds, VoiceSloReadinessThresholdReport, VoiceSloReadinessThresholdReportOptions, VoiceSloReadinessThresholdOptions, VoiceSloReadinessThresholdRoutesOptions, VoiceSloThresholdProfile } from './sloCalibration';
41
41
  export { assertVoiceLiveOpsControlEvidence, assertVoiceLiveOpsEvidence, buildVoiceLiveOpsControlState, createVoiceLiveOpsController, createVoiceLiveOpsRoutes, createVoiceMemoryLiveOpsControlStore, evaluateVoiceLiveOpsControlEvidence, evaluateVoiceLiveOpsEvidence, getVoiceLiveOpsControlStatus, VOICE_LIVE_OPS_ACTIONS } from './liveOps';
package/dist/index.js CHANGED
@@ -16155,6 +16155,136 @@ var appendRealCallRecoveryActionQuery = (href, query) => {
16155
16155
  const search = new URLSearchParams(entries).toString();
16156
16156
  return `${base}${separator}${search}${hash ? `#${hash}` : ""}`;
16157
16157
  };
16158
+ var sleepVoiceRealCallProfileRecoveryLoop = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
16159
+ var describeVoiceRealCallProfileRecoveryLoopAction = (action) => [
16160
+ action.label ?? action.id ?? "recovery action",
16161
+ action.profileId ? `profile=${action.profileId}` : undefined,
16162
+ action.href
16163
+ ].filter(Boolean).join(" ");
16164
+ var defaultVoiceRealCallProfileRecoveryLoopActionFilter = (action, readinessCheckLabel) => action.method?.toUpperCase() === "POST" && action.sourceCheckLabel === readinessCheckLabel && typeof action.href === "string" && action.href.length > 0;
16165
+ var uniqueVoiceRealCallProfileRecoveryLoopActions = (actions) => {
16166
+ const seen = new Set;
16167
+ return actions.filter((action) => {
16168
+ const key = `${action.method?.toUpperCase() ?? "GET"} ${action.href ?? ""}`;
16169
+ if (seen.has(key)) {
16170
+ return false;
16171
+ }
16172
+ seen.add(key);
16173
+ return true;
16174
+ });
16175
+ };
16176
+ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
16177
+ const baseUrl = options.baseUrl.replace(/\/$/, "");
16178
+ const requestTimeoutMs = options.requestTimeoutMs ?? 5000;
16179
+ const jobPollMs = options.jobPollMs ?? 1200;
16180
+ const jobTimeoutMs = options.jobTimeoutMs ?? 600000;
16181
+ const readinessCheckLabel = options.readinessCheckLabel ?? "Real-call profile history";
16182
+ const fetchImpl = options.fetch ?? fetch;
16183
+ const recoveryActionsHref = options.recoveryActionsHref ?? "/api/production-readiness/recovery-actions";
16184
+ const readinessHref = options.readinessHref ?? "/api/production-readiness";
16185
+ const refreshHref = options.refreshHref === undefined ? "/api/voice/real-call-profile-history/refresh" : options.refreshHref;
16186
+ const jobHref = options.jobHref ?? "/api/voice/real-call-profile-history/actions";
16187
+ const toAbsoluteUrl = (href) => new URL(href, baseUrl).toString();
16188
+ const parseJson = async (response) => {
16189
+ const text = await response.text();
16190
+ try {
16191
+ return JSON.parse(text);
16192
+ } catch (error) {
16193
+ throw new Error(`Expected JSON from ${response.url}, got: ${text.slice(0, 300)}`, { cause: error });
16194
+ }
16195
+ };
16196
+ const fetchJson = async (href, init) => {
16197
+ const response = await fetchImpl(toAbsoluteUrl(href), {
16198
+ headers: { accept: "application/json", ...init?.headers },
16199
+ ...init,
16200
+ signal: init?.signal ?? AbortSignal.timeout(requestTimeoutMs)
16201
+ });
16202
+ if (!response.ok) {
16203
+ throw new Error(`${href} returned HTTP ${String(response.status)}.`);
16204
+ }
16205
+ return parseJson(response);
16206
+ };
16207
+ const resolveJobHref = (jobId) => typeof jobHref === "function" ? jobHref(jobId) : `${jobHref.replace(/\/$/, "")}/${jobId}`;
16208
+ const getGate = async (fresh = false) => {
16209
+ const href = fresh ? `${readinessHref}${readinessHref.includes("?") ? "&" : "?"}voiceRecoveryLoopFresh=${String(Date.now())}` : readinessHref;
16210
+ const readiness = await fetchJson(href);
16211
+ return readiness.checks?.find((check) => check.label === readinessCheckLabel) ?? null;
16212
+ };
16213
+ const actionsResponse = await fetchJson(recoveryActionsHref);
16214
+ const actionFilter = options.actionFilter ?? ((action) => defaultVoiceRealCallProfileRecoveryLoopActionFilter(action, readinessCheckLabel));
16215
+ const actions = uniqueVoiceRealCallProfileRecoveryLoopActions((actionsResponse.actions ?? []).filter(actionFilter));
16216
+ if (actions.length === 0) {
16217
+ const realCallProfileGate2 = await getGate();
16218
+ return {
16219
+ actionCount: 0,
16220
+ actions,
16221
+ jobs: [],
16222
+ ok: realCallProfileGate2?.status === "pass",
16223
+ realCallProfileGate: realCallProfileGate2,
16224
+ startFailures: []
16225
+ };
16226
+ }
16227
+ options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel.`);
16228
+ for (const action of actions) {
16229
+ options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
16230
+ }
16231
+ const starts = await Promise.allSettled(actions.map(async (action) => {
16232
+ if (!action.href) {
16233
+ throw new Error("Recovery action is missing href.");
16234
+ }
16235
+ const body = await fetchJson(action.href, { method: "POST" });
16236
+ return { action, ...body };
16237
+ }));
16238
+ const startedJobs = starts.flatMap((result) => {
16239
+ if (result.status === "rejected") {
16240
+ return [];
16241
+ }
16242
+ return result.value.jobId ? [result.value] : [];
16243
+ });
16244
+ const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
16245
+ {
16246
+ action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
16247
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
16248
+ }
16249
+ ] : []);
16250
+ const pollJob = async (jobId) => {
16251
+ const deadline = Date.now() + jobTimeoutMs;
16252
+ while (Date.now() < deadline) {
16253
+ const body = await fetchJson(resolveJobHref(jobId));
16254
+ const job = body.job;
16255
+ if (!job) {
16256
+ throw new Error(`Recovery job ${jobId} was not found.`);
16257
+ }
16258
+ if (job.status === "pass" || job.status === "fail") {
16259
+ return job;
16260
+ }
16261
+ await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
16262
+ }
16263
+ throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
16264
+ };
16265
+ options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
16266
+ const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
16267
+ const jobs = jobResults.map((result, index) => ({
16268
+ action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
16269
+ jobId: startedJobs[index]?.jobId,
16270
+ result: result.status === "fulfilled" ? result.value : {
16271
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
16272
+ status: "fail"
16273
+ }
16274
+ }));
16275
+ if (refreshHref !== false) {
16276
+ await fetchJson(refreshHref, { method: "POST" });
16277
+ }
16278
+ const realCallProfileGate = await getGate(true);
16279
+ return {
16280
+ actionCount: actions.length,
16281
+ actions,
16282
+ jobs,
16283
+ ok: startFailures.length === 0 && jobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
16284
+ realCallProfileGate,
16285
+ startFailures
16286
+ };
16287
+ };
16158
16288
  var buildVoiceRealCallProfileRecoveryActions = (report, options = {}) => {
16159
16289
  const actions = [
16160
16290
  {
@@ -39019,6 +39149,7 @@ export {
39019
39149
  runVoiceScenarioFixtureEvals,
39020
39150
  runVoiceScenarioEvals,
39021
39151
  runVoiceReconnectContract,
39152
+ runVoiceRealCallProfileRecoveryLoop,
39022
39153
  runVoiceProviderRoutingContract,
39023
39154
  runVoiceProfileSwitchPolicyProof,
39024
39155
  runVoicePhoneAgentProductionSmokeContract,
@@ -1,6 +1,6 @@
1
1
  import { Elysia } from 'elysia';
2
2
  import type { Database } from 'bun:sqlite';
3
- import type { VoiceProductionReadinessAction, VoiceProductionReadinessCheck } from './productionReadiness';
3
+ import type { VoiceProductionReadinessAction, VoiceProductionReadinessCheck, VoiceReadinessRecoveryAction } from './productionReadiness';
4
4
  import type { StoredVoiceTraceEvent, VoiceTraceEventStore } from './trace';
5
5
  export type VoiceProofTrendStatus = 'empty' | 'fail' | 'pass' | 'stale';
6
6
  export type VoiceProofTrendSummary = {
@@ -469,6 +469,46 @@ export type VoiceRealCallProfileRecoveryActionRoutesOptions = VoiceRealCallProfi
469
469
  handlers?: Partial<Record<VoiceRealCallProfileRecoveryActionId, VoiceRealCallProfileRecoveryActionHandler>>;
470
470
  jobStore?: VoiceRealCallProfileRecoveryJobStore;
471
471
  };
472
+ export type VoiceRealCallProfileRecoveryLoopAction = Partial<VoiceReadinessRecoveryAction> & Partial<VoiceRealCallProfileRecoveryAction> & {
473
+ href?: string;
474
+ method?: string;
475
+ };
476
+ export type VoiceRealCallProfileRecoveryLoopJob = Partial<VoiceRealCallProfileRecoveryJob> & {
477
+ error?: string;
478
+ id?: string;
479
+ status?: string;
480
+ };
481
+ export type VoiceRealCallProfileRecoveryLoopJobResult = {
482
+ action: string;
483
+ jobId?: string;
484
+ result: VoiceRealCallProfileRecoveryLoopJob;
485
+ };
486
+ export type VoiceRealCallProfileRecoveryLoopStartFailure = {
487
+ action: string;
488
+ error: string;
489
+ };
490
+ export type VoiceRealCallProfileRecoveryLoopReport = {
491
+ actionCount: number;
492
+ actions: VoiceRealCallProfileRecoveryLoopAction[];
493
+ jobs: VoiceRealCallProfileRecoveryLoopJobResult[];
494
+ ok: boolean;
495
+ realCallProfileGate: VoiceProductionReadinessCheck | null;
496
+ startFailures: VoiceRealCallProfileRecoveryLoopStartFailure[];
497
+ };
498
+ export type VoiceRealCallProfileRecoveryLoopOptions = {
499
+ actionFilter?: (action: VoiceRealCallProfileRecoveryLoopAction) => boolean;
500
+ baseUrl: string;
501
+ fetch?: typeof fetch;
502
+ jobHref?: string | ((jobId: string) => string);
503
+ jobPollMs?: number;
504
+ jobTimeoutMs?: number;
505
+ logger?: Pick<Console, 'log'>;
506
+ readinessCheckLabel?: string;
507
+ readinessHref?: string;
508
+ recoveryActionsHref?: string;
509
+ refreshHref?: false | string;
510
+ requestTimeoutMs?: number;
511
+ };
472
512
  export declare const DEFAULT_VOICE_PROOF_TRENDS_MAX_AGE_MS: number;
473
513
  export declare const DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS: ({
474
514
  description: string;
@@ -503,6 +543,7 @@ export declare const createVoiceRealCallProfileTraceCollector: <TEvent extends S
503
543
  export declare const buildVoiceProofTrendProfileSummaries: (input: VoiceProofTrendReport | readonly VoiceProofTrendReport[], options?: VoiceProofTrendProfileSummaryOptions) => VoiceProofTrendProfileSummary[];
504
544
  export declare const buildVoiceProofTrendReportFromRealCallProfiles: (options: VoiceProofTrendRealCallProfileReportOptions) => VoiceProofTrendReport;
505
545
  export declare const buildVoiceRealCallProfileDefaults: (input: VoiceRealCallProfileHistoryReport | VoiceProofTrendReport, options?: VoiceRealCallProfileDefaultsOptions) => VoiceRealCallProfileDefaultsReport;
546
+ export declare const runVoiceRealCallProfileRecoveryLoop: (options: VoiceRealCallProfileRecoveryLoopOptions) => Promise<VoiceRealCallProfileRecoveryLoopReport>;
506
547
  export declare const buildVoiceRealCallProfileRecoveryActions: (report: VoiceRealCallProfileHistoryReport, options?: VoiceRealCallProfileRecoveryActionOptions) => VoiceRealCallProfileRecoveryAction[];
507
548
  export declare const createVoiceInMemoryRealCallProfileRecoveryJobStore: (options?: {
508
549
  idPrefix?: string;
@@ -2273,6 +2273,136 @@ var appendRealCallRecoveryActionQuery = (href, query) => {
2273
2273
  const search = new URLSearchParams(entries).toString();
2274
2274
  return `${base}${separator}${search}${hash ? `#${hash}` : ""}`;
2275
2275
  };
2276
+ var sleepVoiceRealCallProfileRecoveryLoop = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2277
+ var describeVoiceRealCallProfileRecoveryLoopAction = (action) => [
2278
+ action.label ?? action.id ?? "recovery action",
2279
+ action.profileId ? `profile=${action.profileId}` : undefined,
2280
+ action.href
2281
+ ].filter(Boolean).join(" ");
2282
+ var defaultVoiceRealCallProfileRecoveryLoopActionFilter = (action, readinessCheckLabel) => action.method?.toUpperCase() === "POST" && action.sourceCheckLabel === readinessCheckLabel && typeof action.href === "string" && action.href.length > 0;
2283
+ var uniqueVoiceRealCallProfileRecoveryLoopActions = (actions) => {
2284
+ const seen = new Set;
2285
+ return actions.filter((action) => {
2286
+ const key = `${action.method?.toUpperCase() ?? "GET"} ${action.href ?? ""}`;
2287
+ if (seen.has(key)) {
2288
+ return false;
2289
+ }
2290
+ seen.add(key);
2291
+ return true;
2292
+ });
2293
+ };
2294
+ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
2295
+ const baseUrl = options.baseUrl.replace(/\/$/, "");
2296
+ const requestTimeoutMs = options.requestTimeoutMs ?? 5000;
2297
+ const jobPollMs = options.jobPollMs ?? 1200;
2298
+ const jobTimeoutMs = options.jobTimeoutMs ?? 600000;
2299
+ const readinessCheckLabel = options.readinessCheckLabel ?? "Real-call profile history";
2300
+ const fetchImpl = options.fetch ?? fetch;
2301
+ const recoveryActionsHref = options.recoveryActionsHref ?? "/api/production-readiness/recovery-actions";
2302
+ const readinessHref = options.readinessHref ?? "/api/production-readiness";
2303
+ const refreshHref = options.refreshHref === undefined ? "/api/voice/real-call-profile-history/refresh" : options.refreshHref;
2304
+ const jobHref = options.jobHref ?? "/api/voice/real-call-profile-history/actions";
2305
+ const toAbsoluteUrl = (href) => new URL(href, baseUrl).toString();
2306
+ const parseJson = async (response) => {
2307
+ const text = await response.text();
2308
+ try {
2309
+ return JSON.parse(text);
2310
+ } catch (error) {
2311
+ throw new Error(`Expected JSON from ${response.url}, got: ${text.slice(0, 300)}`, { cause: error });
2312
+ }
2313
+ };
2314
+ const fetchJson = async (href, init) => {
2315
+ const response = await fetchImpl(toAbsoluteUrl(href), {
2316
+ headers: { accept: "application/json", ...init?.headers },
2317
+ ...init,
2318
+ signal: init?.signal ?? AbortSignal.timeout(requestTimeoutMs)
2319
+ });
2320
+ if (!response.ok) {
2321
+ throw new Error(`${href} returned HTTP ${String(response.status)}.`);
2322
+ }
2323
+ return parseJson(response);
2324
+ };
2325
+ const resolveJobHref = (jobId) => typeof jobHref === "function" ? jobHref(jobId) : `${jobHref.replace(/\/$/, "")}/${jobId}`;
2326
+ const getGate = async (fresh = false) => {
2327
+ const href = fresh ? `${readinessHref}${readinessHref.includes("?") ? "&" : "?"}voiceRecoveryLoopFresh=${String(Date.now())}` : readinessHref;
2328
+ const readiness = await fetchJson(href);
2329
+ return readiness.checks?.find((check) => check.label === readinessCheckLabel) ?? null;
2330
+ };
2331
+ const actionsResponse = await fetchJson(recoveryActionsHref);
2332
+ const actionFilter = options.actionFilter ?? ((action) => defaultVoiceRealCallProfileRecoveryLoopActionFilter(action, readinessCheckLabel));
2333
+ const actions = uniqueVoiceRealCallProfileRecoveryLoopActions((actionsResponse.actions ?? []).filter(actionFilter));
2334
+ if (actions.length === 0) {
2335
+ const realCallProfileGate2 = await getGate();
2336
+ return {
2337
+ actionCount: 0,
2338
+ actions,
2339
+ jobs: [],
2340
+ ok: realCallProfileGate2?.status === "pass",
2341
+ realCallProfileGate: realCallProfileGate2,
2342
+ startFailures: []
2343
+ };
2344
+ }
2345
+ options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel.`);
2346
+ for (const action of actions) {
2347
+ options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
2348
+ }
2349
+ const starts = await Promise.allSettled(actions.map(async (action) => {
2350
+ if (!action.href) {
2351
+ throw new Error("Recovery action is missing href.");
2352
+ }
2353
+ const body = await fetchJson(action.href, { method: "POST" });
2354
+ return { action, ...body };
2355
+ }));
2356
+ const startedJobs = starts.flatMap((result) => {
2357
+ if (result.status === "rejected") {
2358
+ return [];
2359
+ }
2360
+ return result.value.jobId ? [result.value] : [];
2361
+ });
2362
+ const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
2363
+ {
2364
+ action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
2365
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
2366
+ }
2367
+ ] : []);
2368
+ const pollJob = async (jobId) => {
2369
+ const deadline = Date.now() + jobTimeoutMs;
2370
+ while (Date.now() < deadline) {
2371
+ const body = await fetchJson(resolveJobHref(jobId));
2372
+ const job = body.job;
2373
+ if (!job) {
2374
+ throw new Error(`Recovery job ${jobId} was not found.`);
2375
+ }
2376
+ if (job.status === "pass" || job.status === "fail") {
2377
+ return job;
2378
+ }
2379
+ await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
2380
+ }
2381
+ throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
2382
+ };
2383
+ options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
2384
+ const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
2385
+ const jobs = jobResults.map((result, index) => ({
2386
+ action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
2387
+ jobId: startedJobs[index]?.jobId,
2388
+ result: result.status === "fulfilled" ? result.value : {
2389
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
2390
+ status: "fail"
2391
+ }
2392
+ }));
2393
+ if (refreshHref !== false) {
2394
+ await fetchJson(refreshHref, { method: "POST" });
2395
+ }
2396
+ const realCallProfileGate = await getGate(true);
2397
+ return {
2398
+ actionCount: actions.length,
2399
+ actions,
2400
+ jobs,
2401
+ ok: startFailures.length === 0 && jobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
2402
+ realCallProfileGate,
2403
+ startFailures
2404
+ };
2405
+ };
2276
2406
  var buildVoiceRealCallProfileRecoveryActions = (report, options = {}) => {
2277
2407
  const actions = [
2278
2408
  {
package/dist/vue/index.js CHANGED
@@ -2194,6 +2194,136 @@ var appendRealCallRecoveryActionQuery = (href, query) => {
2194
2194
  const search = new URLSearchParams(entries).toString();
2195
2195
  return `${base}${separator}${search}${hash ? `#${hash}` : ""}`;
2196
2196
  };
2197
+ var sleepVoiceRealCallProfileRecoveryLoop = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
2198
+ var describeVoiceRealCallProfileRecoveryLoopAction = (action) => [
2199
+ action.label ?? action.id ?? "recovery action",
2200
+ action.profileId ? `profile=${action.profileId}` : undefined,
2201
+ action.href
2202
+ ].filter(Boolean).join(" ");
2203
+ var defaultVoiceRealCallProfileRecoveryLoopActionFilter = (action, readinessCheckLabel) => action.method?.toUpperCase() === "POST" && action.sourceCheckLabel === readinessCheckLabel && typeof action.href === "string" && action.href.length > 0;
2204
+ var uniqueVoiceRealCallProfileRecoveryLoopActions = (actions) => {
2205
+ const seen = new Set;
2206
+ return actions.filter((action) => {
2207
+ const key = `${action.method?.toUpperCase() ?? "GET"} ${action.href ?? ""}`;
2208
+ if (seen.has(key)) {
2209
+ return false;
2210
+ }
2211
+ seen.add(key);
2212
+ return true;
2213
+ });
2214
+ };
2215
+ var runVoiceRealCallProfileRecoveryLoop = async (options) => {
2216
+ const baseUrl = options.baseUrl.replace(/\/$/, "");
2217
+ const requestTimeoutMs = options.requestTimeoutMs ?? 5000;
2218
+ const jobPollMs = options.jobPollMs ?? 1200;
2219
+ const jobTimeoutMs = options.jobTimeoutMs ?? 600000;
2220
+ const readinessCheckLabel = options.readinessCheckLabel ?? "Real-call profile history";
2221
+ const fetchImpl = options.fetch ?? fetch;
2222
+ const recoveryActionsHref = options.recoveryActionsHref ?? "/api/production-readiness/recovery-actions";
2223
+ const readinessHref = options.readinessHref ?? "/api/production-readiness";
2224
+ const refreshHref = options.refreshHref === undefined ? "/api/voice/real-call-profile-history/refresh" : options.refreshHref;
2225
+ const jobHref = options.jobHref ?? "/api/voice/real-call-profile-history/actions";
2226
+ const toAbsoluteUrl = (href) => new URL(href, baseUrl).toString();
2227
+ const parseJson = async (response) => {
2228
+ const text = await response.text();
2229
+ try {
2230
+ return JSON.parse(text);
2231
+ } catch (error) {
2232
+ throw new Error(`Expected JSON from ${response.url}, got: ${text.slice(0, 300)}`, { cause: error });
2233
+ }
2234
+ };
2235
+ const fetchJson = async (href, init) => {
2236
+ const response = await fetchImpl(toAbsoluteUrl(href), {
2237
+ headers: { accept: "application/json", ...init?.headers },
2238
+ ...init,
2239
+ signal: init?.signal ?? AbortSignal.timeout(requestTimeoutMs)
2240
+ });
2241
+ if (!response.ok) {
2242
+ throw new Error(`${href} returned HTTP ${String(response.status)}.`);
2243
+ }
2244
+ return parseJson(response);
2245
+ };
2246
+ const resolveJobHref = (jobId) => typeof jobHref === "function" ? jobHref(jobId) : `${jobHref.replace(/\/$/, "")}/${jobId}`;
2247
+ const getGate = async (fresh = false) => {
2248
+ const href = fresh ? `${readinessHref}${readinessHref.includes("?") ? "&" : "?"}voiceRecoveryLoopFresh=${String(Date.now())}` : readinessHref;
2249
+ const readiness = await fetchJson(href);
2250
+ return readiness.checks?.find((check) => check.label === readinessCheckLabel) ?? null;
2251
+ };
2252
+ const actionsResponse = await fetchJson(recoveryActionsHref);
2253
+ const actionFilter = options.actionFilter ?? ((action) => defaultVoiceRealCallProfileRecoveryLoopActionFilter(action, readinessCheckLabel));
2254
+ const actions = uniqueVoiceRealCallProfileRecoveryLoopActions((actionsResponse.actions ?? []).filter(actionFilter));
2255
+ if (actions.length === 0) {
2256
+ const realCallProfileGate2 = await getGate();
2257
+ return {
2258
+ actionCount: 0,
2259
+ actions,
2260
+ jobs: [],
2261
+ ok: realCallProfileGate2?.status === "pass",
2262
+ realCallProfileGate: realCallProfileGate2,
2263
+ startFailures: []
2264
+ };
2265
+ }
2266
+ options.logger?.log(`Running ${String(actions.length)} real-call profile recovery action(s) in parallel.`);
2267
+ for (const action of actions) {
2268
+ options.logger?.log(`- ${describeVoiceRealCallProfileRecoveryLoopAction(action)}`);
2269
+ }
2270
+ const starts = await Promise.allSettled(actions.map(async (action) => {
2271
+ if (!action.href) {
2272
+ throw new Error("Recovery action is missing href.");
2273
+ }
2274
+ const body = await fetchJson(action.href, { method: "POST" });
2275
+ return { action, ...body };
2276
+ }));
2277
+ const startedJobs = starts.flatMap((result) => {
2278
+ if (result.status === "rejected") {
2279
+ return [];
2280
+ }
2281
+ return result.value.jobId ? [result.value] : [];
2282
+ });
2283
+ const startFailures = starts.flatMap((result, index) => result.status === "rejected" ? [
2284
+ {
2285
+ action: describeVoiceRealCallProfileRecoveryLoopAction(actions[index] ?? {}),
2286
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
2287
+ }
2288
+ ] : []);
2289
+ const pollJob = async (jobId) => {
2290
+ const deadline = Date.now() + jobTimeoutMs;
2291
+ while (Date.now() < deadline) {
2292
+ const body = await fetchJson(resolveJobHref(jobId));
2293
+ const job = body.job;
2294
+ if (!job) {
2295
+ throw new Error(`Recovery job ${jobId} was not found.`);
2296
+ }
2297
+ if (job.status === "pass" || job.status === "fail") {
2298
+ return job;
2299
+ }
2300
+ await sleepVoiceRealCallProfileRecoveryLoop(jobPollMs);
2301
+ }
2302
+ throw new Error(`Timed out waiting ${String(jobTimeoutMs)}ms for recovery job ${jobId}.`);
2303
+ };
2304
+ options.logger?.log(`Polling ${String(startedJobs.length)} recovery job(s) in parallel.`);
2305
+ const jobResults = await Promise.allSettled(startedJobs.map((start) => pollJob(start.jobId)));
2306
+ const jobs = jobResults.map((result, index) => ({
2307
+ action: describeVoiceRealCallProfileRecoveryLoopAction(startedJobs[index]?.action ?? {}),
2308
+ jobId: startedJobs[index]?.jobId,
2309
+ result: result.status === "fulfilled" ? result.value : {
2310
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
2311
+ status: "fail"
2312
+ }
2313
+ }));
2314
+ if (refreshHref !== false) {
2315
+ await fetchJson(refreshHref, { method: "POST" });
2316
+ }
2317
+ const realCallProfileGate = await getGate(true);
2318
+ return {
2319
+ actionCount: actions.length,
2320
+ actions,
2321
+ jobs,
2322
+ ok: startFailures.length === 0 && jobs.every((job) => job.result.status === "pass") && realCallProfileGate?.status === "pass",
2323
+ realCallProfileGate,
2324
+ startFailures
2325
+ };
2326
+ };
2197
2327
  var buildVoiceRealCallProfileRecoveryActions = (report, options = {}) => {
2198
2328
  const actions = [
2199
2329
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.384",
3
+ "version": "0.0.22-beta.385",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",