@absolutejs/voice 0.0.22-beta.370 → 0.0.22-beta.372

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.
package/README.md CHANGED
@@ -1502,6 +1502,23 @@ createVoiceProductionReadinessRoutes({
1502
1502
  });
1503
1503
  ```
1504
1504
 
1505
+ The readiness check includes recovery actions from `buildVoiceRealCallProfileRecoveryActions(...)`, so failed gates can point operators at the profile history report, browser/phone proof, missing provider-role evidence, operations records, and production-readiness refresh instead of only saying "failed."
1506
+
1507
+ Mount `createVoiceRealCallProfileRecoveryActionRoutes(...)` when those actions should be executable. The package owns the route contract and result shape; the app supplies safe handlers:
1508
+
1509
+ ```ts
1510
+ app.use(
1511
+ createVoiceRealCallProfileRecoveryActionRoutes({
1512
+ handlers: {
1513
+ 'collect-browser-proof': async () => runBrowserProfileProof(),
1514
+ 'collect-phone-proof': async () => runPhoneProfileProof(),
1515
+ refresh: async () => refreshReadinessProof()
1516
+ },
1517
+ source: buildRealCallHistory
1518
+ })
1519
+ );
1520
+ ```
1521
+
1505
1522
  Use `createVoiceProfileTraceTagger(...)` when the app already has a trace store and needs every appended trace to carry a benchmark profile label. It wraps any `VoiceTraceEventStore`, preserves the underlying store behavior, and adds `profileId`/`benchmarkProfileId` metadata and payload fields that real-call profile history can ingest later.
1506
1523
 
1507
1524
  ```ts
@@ -4555,11 +4555,84 @@ var buildRealCallProfileReadinessIssues = (report, options) => {
4555
4555
  }
4556
4556
  return { issues, warnings };
4557
4557
  };
4558
+ var uniqueRealCallProfileActions = (actions) => {
4559
+ const seen = new Set;
4560
+ return actions.filter((action) => {
4561
+ const key = `${action.method ?? "GET"}:${action.href}:${action.label}`;
4562
+ if (seen.has(key)) {
4563
+ return false;
4564
+ }
4565
+ seen.add(key);
4566
+ return true;
4567
+ });
4568
+ };
4569
+ var buildVoiceRealCallProfileRecoveryActions = (report, options = {}) => {
4570
+ const actions = [
4571
+ {
4572
+ description: "Open the current real-call profile history report and profile defaults.",
4573
+ href: options.href ?? "/voice/real-call-profile-history",
4574
+ id: "refresh",
4575
+ label: "Open real-call profile history"
4576
+ },
4577
+ {
4578
+ description: "Refresh production readiness after collecting or replaying profile evidence.",
4579
+ href: options.productionReadinessHref ?? "/production-readiness",
4580
+ id: "refresh",
4581
+ label: "Refresh production readiness"
4582
+ }
4583
+ ];
4584
+ const requiredProfiles = new Set(options.requiredProfileIds ?? []);
4585
+ const profilesById = new Map(report.defaults.profiles.map((profile) => [profile.profileId, profile]));
4586
+ const missingProfiles = [...requiredProfiles].filter((profileId) => !profilesById.has(profileId));
4587
+ const warningProfiles = report.defaults.profiles.filter((profile) => (requiredProfiles.size === 0 || requiredProfiles.has(profile.profileId)) && profile.status !== "pass");
4588
+ const missingRoleProfiles = report.defaults.profiles.filter((profile) => (options.requiredProviderRoles ?? []).some((role) => !profile.providerRoutes[role]));
4589
+ const ageMs = report.trend.ageMs ?? (report.generatedAt ? Date.now() - new Date(report.generatedAt).getTime() : undefined);
4590
+ if (missingProfiles.length > 0 || warningProfiles.length > 0 || missingRoleProfiles.length > 0 || options.minCycles !== undefined && (report.summary.cycles ?? 0) < options.minCycles || options.minActionableProfiles !== undefined && report.defaults.summary.actionableProfiles < options.minActionableProfiles) {
4591
+ actions.push({
4592
+ description: "Run browser profile proof to collect microphone, WebSocket, live-latency, and provider traces for missing profiles.",
4593
+ href: options.browserProofHref ?? "/voice/browser-call-profiles",
4594
+ id: "collect-browser-proof",
4595
+ label: "Run browser profile proof"
4596
+ });
4597
+ actions.push({
4598
+ description: "Run phone profile proof when required profiles depend on carrier, telephony media, or noisy-call evidence.",
4599
+ href: options.phoneProofHref ?? "/api/voice/phone/smoke",
4600
+ id: "collect-phone-proof",
4601
+ label: "Run phone profile proof"
4602
+ });
4603
+ }
4604
+ if (options.maxAgeMs !== undefined && (ageMs === undefined || ageMs > options.maxAgeMs)) {
4605
+ actions.push({
4606
+ description: "Collect fresh real-call profile traces because the current history artifact is stale.",
4607
+ href: options.browserProofHref ?? "/voice/browser-call-profiles",
4608
+ id: "collect-browser-proof",
4609
+ label: "Collect fresh profile evidence"
4610
+ });
4611
+ }
4612
+ if (missingRoleProfiles.length > 0 || report.defaults.summary.actionableProfiles < (options.minActionableProfiles ?? 1)) {
4613
+ actions.push({
4614
+ description: "Collect missing LLM/STT/TTS provider-role evidence so profile defaults can become actionable.",
4615
+ href: options.sourceHref ?? "/api/voice/real-call-profile-history",
4616
+ id: "collect-provider-role-evidence",
4617
+ label: "Collect missing provider-role evidence"
4618
+ });
4619
+ }
4620
+ if (report.recommendations.profiles.some((profile) => profile.status !== "pass") || report.defaults.profiles.some((profile) => profile.status !== "pass")) {
4621
+ actions.push({
4622
+ description: "Open operations records to inspect the sessions behind failing or warning profile evidence.",
4623
+ href: options.operationsRecordsHref ?? "/voice-operations",
4624
+ id: "refresh",
4625
+ label: "Open operations records"
4626
+ });
4627
+ }
4628
+ return uniqueRealCallProfileActions(actions);
4629
+ };
4558
4630
  var buildVoiceRealCallProfileReadinessCheck = (report, options = {}) => {
4559
4631
  const { issues, warnings } = buildRealCallProfileReadinessIssues(report, options);
4560
4632
  const status = issues.length > 0 ? "fail" : warnings.length > 0 && options.failOnWarnings === true ? "fail" : warnings.length > 0 ? "warn" : "pass";
4561
4633
  const detail = status === "pass" ? `${String(report.summary.profileCount)} profile(s), ${String(report.summary.cycles ?? 0)} cycle(s), ${String(report.defaults.summary.actionableProfiles)} actionable default(s).` : [...issues, ...warnings].join(" ");
4562
4634
  return {
4635
+ actions: buildVoiceRealCallProfileRecoveryActions(report, options),
4563
4636
  detail,
4564
4637
  gateExplanation: {
4565
4638
  evidenceHref: options.href ?? "/api/voice/real-call-profile-history",
@@ -5151,6 +5224,76 @@ var createVoiceRealCallProfileHistoryRoutes = (options = {}) => {
5151
5224
  }
5152
5225
  return routes;
5153
5226
  };
5227
+ var realCallProfileActionPaths = {
5228
+ "collect-browser-proof": "/collect-browser-proof",
5229
+ "collect-phone-proof": "/collect-phone-proof",
5230
+ "collect-provider-role-evidence": "/collect-provider-role-evidence",
5231
+ refresh: "/refresh"
5232
+ };
5233
+ var loadVoiceRealCallProfileHistoryRouteReport = async (options) => {
5234
+ const { source, ...routeOptions } = options;
5235
+ const sourceOptions = source === undefined ? routeOptions : typeof source === "function" ? await source() : source;
5236
+ return buildVoiceRealCallProfileHistoryReport({
5237
+ ...routeOptions,
5238
+ ...sourceOptions
5239
+ });
5240
+ };
5241
+ var createVoiceRealCallProfileRecoveryActionRoutes = (options = {}) => {
5242
+ const path = options.path ?? "/api/voice/real-call-profile-history";
5243
+ const routes = new Elysia({
5244
+ name: options.name ?? "absolutejs-voice-real-call-profile-recovery-actions"
5245
+ });
5246
+ const actionPath = (actionId) => `${path}${realCallProfileActionPaths[actionId]}`;
5247
+ const loadReport = () => loadVoiceRealCallProfileHistoryRouteReport(options);
5248
+ const listActions = async () => {
5249
+ const report = await loadReport();
5250
+ const actions = buildVoiceRealCallProfileRecoveryActions(report, {
5251
+ ...options,
5252
+ browserProofHref: options.browserProofHref ?? actionPath("collect-browser-proof"),
5253
+ phoneProofHref: options.phoneProofHref ?? actionPath("collect-phone-proof"),
5254
+ sourceHref: options.sourceHref ?? actionPath("collect-provider-role-evidence"),
5255
+ productionReadinessHref: options.productionReadinessHref ?? actionPath("refresh")
5256
+ }).map((action) => ({
5257
+ ...action,
5258
+ href: action.id === "collect-browser-proof" ? actionPath("collect-browser-proof") : action.id === "collect-phone-proof" ? actionPath("collect-phone-proof") : action.id === "collect-provider-role-evidence" ? actionPath("collect-provider-role-evidence") : action.href,
5259
+ method: action.id === "refresh" && (action.label === "Open real-call profile history" || action.label === "Open operations records") ? "GET" : "POST"
5260
+ }));
5261
+ return { actions, generatedAt: new Date().toISOString(), report };
5262
+ };
5263
+ const runAction = async (actionId) => {
5264
+ const report = await loadReport();
5265
+ const handler = options.handlers?.[actionId];
5266
+ if (!handler) {
5267
+ return {
5268
+ actionId,
5269
+ generatedAt: new Date().toISOString(),
5270
+ message: `No handler configured for real-call profile recovery action: ${actionId}.`,
5271
+ ok: false,
5272
+ status: "fail"
5273
+ };
5274
+ }
5275
+ const result = await handler({ actionId, report });
5276
+ return {
5277
+ actionId,
5278
+ generatedAt: new Date().toISOString(),
5279
+ message: result?.message,
5280
+ ok: result?.ok ?? true,
5281
+ report: result?.report,
5282
+ status: result?.status ?? "pass"
5283
+ };
5284
+ };
5285
+ routes.get(`${path}/actions`, async () => Response.json(await listActions(), { headers: options.headers }));
5286
+ for (const actionId of Object.keys(realCallProfileActionPaths)) {
5287
+ routes.post(actionPath(actionId), async ({ set }) => {
5288
+ const result = await runAction(actionId);
5289
+ if (!result.ok) {
5290
+ set.status = 501;
5291
+ }
5292
+ return Response.json(result, { headers: options.headers });
5293
+ });
5294
+ }
5295
+ return routes;
5296
+ };
5154
5297
  var createVoiceProofTrendRoutes = (options) => {
5155
5298
  const path = options.path ?? "/api/voice/proof-trends";
5156
5299
  const routes = new Elysia({
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, createVoiceProofTrendRecommendationRoutes, createVoiceProofTrendRoutes, createVoiceRealCallProfileHistoryRoutes, 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, buildVoiceRealCallProfileRecoveryActions, 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';
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, 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, VoiceRealCallProfileRecoveryActionResult, VoiceRealCallProfileRecoveryActionRoutesOptions, 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
@@ -16024,11 +16024,84 @@ var buildRealCallProfileReadinessIssues = (report, options) => {
16024
16024
  }
16025
16025
  return { issues, warnings };
16026
16026
  };
16027
+ var uniqueRealCallProfileActions = (actions) => {
16028
+ const seen = new Set;
16029
+ return actions.filter((action) => {
16030
+ const key = `${action.method ?? "GET"}:${action.href}:${action.label}`;
16031
+ if (seen.has(key)) {
16032
+ return false;
16033
+ }
16034
+ seen.add(key);
16035
+ return true;
16036
+ });
16037
+ };
16038
+ var buildVoiceRealCallProfileRecoveryActions = (report, options = {}) => {
16039
+ const actions = [
16040
+ {
16041
+ description: "Open the current real-call profile history report and profile defaults.",
16042
+ href: options.href ?? "/voice/real-call-profile-history",
16043
+ id: "refresh",
16044
+ label: "Open real-call profile history"
16045
+ },
16046
+ {
16047
+ description: "Refresh production readiness after collecting or replaying profile evidence.",
16048
+ href: options.productionReadinessHref ?? "/production-readiness",
16049
+ id: "refresh",
16050
+ label: "Refresh production readiness"
16051
+ }
16052
+ ];
16053
+ const requiredProfiles = new Set(options.requiredProfileIds ?? []);
16054
+ const profilesById = new Map(report.defaults.profiles.map((profile) => [profile.profileId, profile]));
16055
+ const missingProfiles = [...requiredProfiles].filter((profileId) => !profilesById.has(profileId));
16056
+ const warningProfiles = report.defaults.profiles.filter((profile) => (requiredProfiles.size === 0 || requiredProfiles.has(profile.profileId)) && profile.status !== "pass");
16057
+ const missingRoleProfiles = report.defaults.profiles.filter((profile) => (options.requiredProviderRoles ?? []).some((role) => !profile.providerRoutes[role]));
16058
+ const ageMs = report.trend.ageMs ?? (report.generatedAt ? Date.now() - new Date(report.generatedAt).getTime() : undefined);
16059
+ if (missingProfiles.length > 0 || warningProfiles.length > 0 || missingRoleProfiles.length > 0 || options.minCycles !== undefined && (report.summary.cycles ?? 0) < options.minCycles || options.minActionableProfiles !== undefined && report.defaults.summary.actionableProfiles < options.minActionableProfiles) {
16060
+ actions.push({
16061
+ description: "Run browser profile proof to collect microphone, WebSocket, live-latency, and provider traces for missing profiles.",
16062
+ href: options.browserProofHref ?? "/voice/browser-call-profiles",
16063
+ id: "collect-browser-proof",
16064
+ label: "Run browser profile proof"
16065
+ });
16066
+ actions.push({
16067
+ description: "Run phone profile proof when required profiles depend on carrier, telephony media, or noisy-call evidence.",
16068
+ href: options.phoneProofHref ?? "/api/voice/phone/smoke",
16069
+ id: "collect-phone-proof",
16070
+ label: "Run phone profile proof"
16071
+ });
16072
+ }
16073
+ if (options.maxAgeMs !== undefined && (ageMs === undefined || ageMs > options.maxAgeMs)) {
16074
+ actions.push({
16075
+ description: "Collect fresh real-call profile traces because the current history artifact is stale.",
16076
+ href: options.browserProofHref ?? "/voice/browser-call-profiles",
16077
+ id: "collect-browser-proof",
16078
+ label: "Collect fresh profile evidence"
16079
+ });
16080
+ }
16081
+ if (missingRoleProfiles.length > 0 || report.defaults.summary.actionableProfiles < (options.minActionableProfiles ?? 1)) {
16082
+ actions.push({
16083
+ description: "Collect missing LLM/STT/TTS provider-role evidence so profile defaults can become actionable.",
16084
+ href: options.sourceHref ?? "/api/voice/real-call-profile-history",
16085
+ id: "collect-provider-role-evidence",
16086
+ label: "Collect missing provider-role evidence"
16087
+ });
16088
+ }
16089
+ if (report.recommendations.profiles.some((profile) => profile.status !== "pass") || report.defaults.profiles.some((profile) => profile.status !== "pass")) {
16090
+ actions.push({
16091
+ description: "Open operations records to inspect the sessions behind failing or warning profile evidence.",
16092
+ href: options.operationsRecordsHref ?? "/voice-operations",
16093
+ id: "refresh",
16094
+ label: "Open operations records"
16095
+ });
16096
+ }
16097
+ return uniqueRealCallProfileActions(actions);
16098
+ };
16027
16099
  var buildVoiceRealCallProfileReadinessCheck = (report, options = {}) => {
16028
16100
  const { issues, warnings } = buildRealCallProfileReadinessIssues(report, options);
16029
16101
  const status = issues.length > 0 ? "fail" : warnings.length > 0 && options.failOnWarnings === true ? "fail" : warnings.length > 0 ? "warn" : "pass";
16030
16102
  const detail = status === "pass" ? `${String(report.summary.profileCount)} profile(s), ${String(report.summary.cycles ?? 0)} cycle(s), ${String(report.defaults.summary.actionableProfiles)} actionable default(s).` : [...issues, ...warnings].join(" ");
16031
16103
  return {
16104
+ actions: buildVoiceRealCallProfileRecoveryActions(report, options),
16032
16105
  detail,
16033
16106
  gateExplanation: {
16034
16107
  evidenceHref: options.href ?? "/api/voice/real-call-profile-history",
@@ -16620,6 +16693,76 @@ var createVoiceRealCallProfileHistoryRoutes = (options = {}) => {
16620
16693
  }
16621
16694
  return routes;
16622
16695
  };
16696
+ var realCallProfileActionPaths = {
16697
+ "collect-browser-proof": "/collect-browser-proof",
16698
+ "collect-phone-proof": "/collect-phone-proof",
16699
+ "collect-provider-role-evidence": "/collect-provider-role-evidence",
16700
+ refresh: "/refresh"
16701
+ };
16702
+ var loadVoiceRealCallProfileHistoryRouteReport = async (options) => {
16703
+ const { source, ...routeOptions } = options;
16704
+ const sourceOptions = source === undefined ? routeOptions : typeof source === "function" ? await source() : source;
16705
+ return buildVoiceRealCallProfileHistoryReport({
16706
+ ...routeOptions,
16707
+ ...sourceOptions
16708
+ });
16709
+ };
16710
+ var createVoiceRealCallProfileRecoveryActionRoutes = (options = {}) => {
16711
+ const path = options.path ?? "/api/voice/real-call-profile-history";
16712
+ const routes = new Elysia22({
16713
+ name: options.name ?? "absolutejs-voice-real-call-profile-recovery-actions"
16714
+ });
16715
+ const actionPath = (actionId) => `${path}${realCallProfileActionPaths[actionId]}`;
16716
+ const loadReport = () => loadVoiceRealCallProfileHistoryRouteReport(options);
16717
+ const listActions = async () => {
16718
+ const report = await loadReport();
16719
+ const actions = buildVoiceRealCallProfileRecoveryActions(report, {
16720
+ ...options,
16721
+ browserProofHref: options.browserProofHref ?? actionPath("collect-browser-proof"),
16722
+ phoneProofHref: options.phoneProofHref ?? actionPath("collect-phone-proof"),
16723
+ sourceHref: options.sourceHref ?? actionPath("collect-provider-role-evidence"),
16724
+ productionReadinessHref: options.productionReadinessHref ?? actionPath("refresh")
16725
+ }).map((action) => ({
16726
+ ...action,
16727
+ href: action.id === "collect-browser-proof" ? actionPath("collect-browser-proof") : action.id === "collect-phone-proof" ? actionPath("collect-phone-proof") : action.id === "collect-provider-role-evidence" ? actionPath("collect-provider-role-evidence") : action.href,
16728
+ method: action.id === "refresh" && (action.label === "Open real-call profile history" || action.label === "Open operations records") ? "GET" : "POST"
16729
+ }));
16730
+ return { actions, generatedAt: new Date().toISOString(), report };
16731
+ };
16732
+ const runAction = async (actionId) => {
16733
+ const report = await loadReport();
16734
+ const handler = options.handlers?.[actionId];
16735
+ if (!handler) {
16736
+ return {
16737
+ actionId,
16738
+ generatedAt: new Date().toISOString(),
16739
+ message: `No handler configured for real-call profile recovery action: ${actionId}.`,
16740
+ ok: false,
16741
+ status: "fail"
16742
+ };
16743
+ }
16744
+ const result = await handler({ actionId, report });
16745
+ return {
16746
+ actionId,
16747
+ generatedAt: new Date().toISOString(),
16748
+ message: result?.message,
16749
+ ok: result?.ok ?? true,
16750
+ report: result?.report,
16751
+ status: result?.status ?? "pass"
16752
+ };
16753
+ };
16754
+ routes.get(`${path}/actions`, async () => Response.json(await listActions(), { headers: options.headers }));
16755
+ for (const actionId of Object.keys(realCallProfileActionPaths)) {
16756
+ routes.post(actionPath(actionId), async ({ set }) => {
16757
+ const result = await runAction(actionId);
16758
+ if (!result.ok) {
16759
+ set.status = 501;
16760
+ }
16761
+ return Response.json(result, { headers: options.headers });
16762
+ });
16763
+ }
16764
+ return routes;
16765
+ };
16623
16766
  var createVoiceProofTrendRoutes = (options) => {
16624
16767
  const path = options.path ?? "/api/voice/proof-trends";
16625
16768
  const routes = new Elysia22({
@@ -38691,6 +38834,7 @@ export {
38691
38834
  createVoiceRealtimeProviderContractRoutes,
38692
38835
  createVoiceRealtimeProviderContractMatrixPreset,
38693
38836
  createVoiceRealtimeChannelRoutes,
38837
+ createVoiceRealCallProfileRecoveryActionRoutes,
38694
38838
  createVoiceRealCallProfileHistoryRoutes,
38695
38839
  createVoiceReadinessProfile,
38696
38840
  createVoiceQualityRoutes,
@@ -38907,6 +39051,7 @@ export {
38907
39051
  buildVoiceRealtimeProviderContractMatrix,
38908
39052
  buildVoiceRealtimeChannelRuntimeSamplesFromTrace,
38909
39053
  buildVoiceRealtimeChannelReport,
39054
+ buildVoiceRealCallProfileRecoveryActions,
38910
39055
  buildVoiceRealCallProfileReadinessCheck,
38911
39056
  buildVoiceRealCallProfileHistoryReport,
38912
39057
  buildVoiceRealCallProfileEvidenceFromTraceEvents,
@@ -1,5 +1,5 @@
1
1
  import { Elysia } from 'elysia';
2
- import type { VoiceProductionReadinessCheck } from './productionReadiness';
2
+ import type { VoiceProductionReadinessAction, VoiceProductionReadinessCheck } from './productionReadiness';
3
3
  import type { StoredVoiceTraceEvent, VoiceTraceEventStore } from './trace';
4
4
  export type VoiceProofTrendStatus = 'empty' | 'fail' | 'pass' | 'stale';
5
5
  export type VoiceProofTrendSummary = {
@@ -356,7 +356,12 @@ export type VoiceRealCallProfileHistoryRoutesOptions = Omit<VoiceRealCallProfile
356
356
  source?: (() => Promise<VoiceRealCallProfileHistoryOptions> | VoiceRealCallProfileHistoryOptions) | VoiceRealCallProfileHistoryOptions;
357
357
  title?: string;
358
358
  };
359
+ export type VoiceRealCallProfileRecoveryActionId = 'collect-browser-proof' | 'collect-phone-proof' | 'collect-provider-role-evidence' | 'refresh';
360
+ export type VoiceRealCallProfileRecoveryAction = VoiceProductionReadinessAction & {
361
+ id: VoiceRealCallProfileRecoveryActionId;
362
+ };
359
363
  export type VoiceRealCallProfileReadinessCheckOptions = {
364
+ browserProofHref?: string;
360
365
  failOnWarnings?: boolean;
361
366
  href?: string;
362
367
  label?: string;
@@ -366,8 +371,28 @@ export type VoiceRealCallProfileReadinessCheckOptions = {
366
371
  minProfiles?: number;
367
372
  requiredProfileIds?: readonly string[];
368
373
  requiredProviderRoles?: readonly string[];
374
+ operationsRecordsHref?: string;
375
+ phoneProofHref?: string;
376
+ productionReadinessHref?: string;
369
377
  sourceHref?: string;
370
378
  };
379
+ export type VoiceRealCallProfileRecoveryActionOptions = VoiceRealCallProfileReadinessCheckOptions;
380
+ export type VoiceRealCallProfileRecoveryActionHandlerInput = {
381
+ actionId: VoiceRealCallProfileRecoveryActionId;
382
+ report: VoiceRealCallProfileHistoryReport;
383
+ };
384
+ export type VoiceRealCallProfileRecoveryActionResult = {
385
+ actionId: VoiceRealCallProfileRecoveryActionId;
386
+ generatedAt: string;
387
+ message?: string;
388
+ ok: boolean;
389
+ report?: VoiceRealCallProfileHistoryReport;
390
+ status: VoiceProofTrendStatus;
391
+ };
392
+ export type VoiceRealCallProfileRecoveryActionHandler = (input: VoiceRealCallProfileRecoveryActionHandlerInput) => Promise<Partial<VoiceRealCallProfileRecoveryActionResult> | void> | Partial<VoiceRealCallProfileRecoveryActionResult> | void;
393
+ export type VoiceRealCallProfileRecoveryActionRoutesOptions = VoiceRealCallProfileRecoveryActionOptions & Omit<VoiceRealCallProfileHistoryRoutesOptions, 'htmlPath' | 'markdownPath'> & {
394
+ handlers?: Partial<Record<VoiceRealCallProfileRecoveryActionId, VoiceRealCallProfileRecoveryActionHandler>>;
395
+ };
371
396
  export declare const DEFAULT_VOICE_PROOF_TRENDS_MAX_AGE_MS: number;
372
397
  export declare const DEFAULT_VOICE_PROOF_TREND_PROFILE_DEFINITIONS: ({
373
398
  description: string;
@@ -401,6 +426,7 @@ export declare const loadVoiceRealCallProfileEvidenceFromTraceStore: (options: V
401
426
  export declare const buildVoiceProofTrendProfileSummaries: (input: VoiceProofTrendReport | readonly VoiceProofTrendReport[], options?: VoiceProofTrendProfileSummaryOptions) => VoiceProofTrendProfileSummary[];
402
427
  export declare const buildVoiceProofTrendReportFromRealCallProfiles: (options: VoiceProofTrendRealCallProfileReportOptions) => VoiceProofTrendReport;
403
428
  export declare const buildVoiceRealCallProfileDefaults: (input: VoiceRealCallProfileHistoryReport | VoiceProofTrendReport, options?: VoiceRealCallProfileDefaultsOptions) => VoiceRealCallProfileDefaultsReport;
429
+ export declare const buildVoiceRealCallProfileRecoveryActions: (report: VoiceRealCallProfileHistoryReport, options?: VoiceRealCallProfileRecoveryActionOptions) => VoiceRealCallProfileRecoveryAction[];
404
430
  export declare const buildVoiceRealCallProfileReadinessCheck: (report: VoiceRealCallProfileHistoryReport, options?: VoiceRealCallProfileReadinessCheckOptions) => VoiceProductionReadinessCheck;
405
431
  export declare const resolveVoiceRealCallProfileProviderRoute: <TProvider extends string = string>(options: VoiceRealCallProfileProviderRouteOptions<TProvider>) => TProvider | undefined;
406
432
  export declare const buildVoiceRealCallProfileHistoryReport: (options?: VoiceRealCallProfileHistoryOptions) => VoiceRealCallProfileHistoryReport;
@@ -467,6 +493,34 @@ export declare const createVoiceRealCallProfileHistoryRoutes: (options?: VoiceRe
467
493
  standaloneSchema: {};
468
494
  response: {};
469
495
  }>;
496
+ export declare const createVoiceRealCallProfileRecoveryActionRoutes: (options?: VoiceRealCallProfileRecoveryActionRoutesOptions) => Elysia<"", {
497
+ decorator: {};
498
+ store: {};
499
+ derive: {};
500
+ resolve: {};
501
+ }, {
502
+ typebox: {};
503
+ error: {};
504
+ }, {
505
+ schema: {};
506
+ standaloneSchema: {};
507
+ macro: {};
508
+ macroFn: {};
509
+ parser: {};
510
+ response: {};
511
+ }, {}, {
512
+ derive: {};
513
+ resolve: {};
514
+ schema: {};
515
+ standaloneSchema: {};
516
+ response: {};
517
+ }, {
518
+ derive: {};
519
+ resolve: {};
520
+ schema: {};
521
+ standaloneSchema: {};
522
+ response: {};
523
+ }>;
470
524
  export declare const createVoiceProofTrendRoutes: (options: VoiceProofTrendRoutesOptions) => Elysia<"", {
471
525
  decorator: {};
472
526
  store: {};
@@ -2142,11 +2142,84 @@ var buildRealCallProfileReadinessIssues = (report, options) => {
2142
2142
  }
2143
2143
  return { issues, warnings };
2144
2144
  };
2145
+ var uniqueRealCallProfileActions = (actions) => {
2146
+ const seen = new Set;
2147
+ return actions.filter((action) => {
2148
+ const key = `${action.method ?? "GET"}:${action.href}:${action.label}`;
2149
+ if (seen.has(key)) {
2150
+ return false;
2151
+ }
2152
+ seen.add(key);
2153
+ return true;
2154
+ });
2155
+ };
2156
+ var buildVoiceRealCallProfileRecoveryActions = (report, options = {}) => {
2157
+ const actions = [
2158
+ {
2159
+ description: "Open the current real-call profile history report and profile defaults.",
2160
+ href: options.href ?? "/voice/real-call-profile-history",
2161
+ id: "refresh",
2162
+ label: "Open real-call profile history"
2163
+ },
2164
+ {
2165
+ description: "Refresh production readiness after collecting or replaying profile evidence.",
2166
+ href: options.productionReadinessHref ?? "/production-readiness",
2167
+ id: "refresh",
2168
+ label: "Refresh production readiness"
2169
+ }
2170
+ ];
2171
+ const requiredProfiles = new Set(options.requiredProfileIds ?? []);
2172
+ const profilesById = new Map(report.defaults.profiles.map((profile) => [profile.profileId, profile]));
2173
+ const missingProfiles = [...requiredProfiles].filter((profileId) => !profilesById.has(profileId));
2174
+ const warningProfiles = report.defaults.profiles.filter((profile) => (requiredProfiles.size === 0 || requiredProfiles.has(profile.profileId)) && profile.status !== "pass");
2175
+ const missingRoleProfiles = report.defaults.profiles.filter((profile) => (options.requiredProviderRoles ?? []).some((role) => !profile.providerRoutes[role]));
2176
+ const ageMs = report.trend.ageMs ?? (report.generatedAt ? Date.now() - new Date(report.generatedAt).getTime() : undefined);
2177
+ if (missingProfiles.length > 0 || warningProfiles.length > 0 || missingRoleProfiles.length > 0 || options.minCycles !== undefined && (report.summary.cycles ?? 0) < options.minCycles || options.minActionableProfiles !== undefined && report.defaults.summary.actionableProfiles < options.minActionableProfiles) {
2178
+ actions.push({
2179
+ description: "Run browser profile proof to collect microphone, WebSocket, live-latency, and provider traces for missing profiles.",
2180
+ href: options.browserProofHref ?? "/voice/browser-call-profiles",
2181
+ id: "collect-browser-proof",
2182
+ label: "Run browser profile proof"
2183
+ });
2184
+ actions.push({
2185
+ description: "Run phone profile proof when required profiles depend on carrier, telephony media, or noisy-call evidence.",
2186
+ href: options.phoneProofHref ?? "/api/voice/phone/smoke",
2187
+ id: "collect-phone-proof",
2188
+ label: "Run phone profile proof"
2189
+ });
2190
+ }
2191
+ if (options.maxAgeMs !== undefined && (ageMs === undefined || ageMs > options.maxAgeMs)) {
2192
+ actions.push({
2193
+ description: "Collect fresh real-call profile traces because the current history artifact is stale.",
2194
+ href: options.browserProofHref ?? "/voice/browser-call-profiles",
2195
+ id: "collect-browser-proof",
2196
+ label: "Collect fresh profile evidence"
2197
+ });
2198
+ }
2199
+ if (missingRoleProfiles.length > 0 || report.defaults.summary.actionableProfiles < (options.minActionableProfiles ?? 1)) {
2200
+ actions.push({
2201
+ description: "Collect missing LLM/STT/TTS provider-role evidence so profile defaults can become actionable.",
2202
+ href: options.sourceHref ?? "/api/voice/real-call-profile-history",
2203
+ id: "collect-provider-role-evidence",
2204
+ label: "Collect missing provider-role evidence"
2205
+ });
2206
+ }
2207
+ if (report.recommendations.profiles.some((profile) => profile.status !== "pass") || report.defaults.profiles.some((profile) => profile.status !== "pass")) {
2208
+ actions.push({
2209
+ description: "Open operations records to inspect the sessions behind failing or warning profile evidence.",
2210
+ href: options.operationsRecordsHref ?? "/voice-operations",
2211
+ id: "refresh",
2212
+ label: "Open operations records"
2213
+ });
2214
+ }
2215
+ return uniqueRealCallProfileActions(actions);
2216
+ };
2145
2217
  var buildVoiceRealCallProfileReadinessCheck = (report, options = {}) => {
2146
2218
  const { issues, warnings } = buildRealCallProfileReadinessIssues(report, options);
2147
2219
  const status = issues.length > 0 ? "fail" : warnings.length > 0 && options.failOnWarnings === true ? "fail" : warnings.length > 0 ? "warn" : "pass";
2148
2220
  const detail = status === "pass" ? `${String(report.summary.profileCount)} profile(s), ${String(report.summary.cycles ?? 0)} cycle(s), ${String(report.defaults.summary.actionableProfiles)} actionable default(s).` : [...issues, ...warnings].join(" ");
2149
2221
  return {
2222
+ actions: buildVoiceRealCallProfileRecoveryActions(report, options),
2150
2223
  detail,
2151
2224
  gateExplanation: {
2152
2225
  evidenceHref: options.href ?? "/api/voice/real-call-profile-history",
@@ -2738,6 +2811,76 @@ var createVoiceRealCallProfileHistoryRoutes = (options = {}) => {
2738
2811
  }
2739
2812
  return routes;
2740
2813
  };
2814
+ var realCallProfileActionPaths = {
2815
+ "collect-browser-proof": "/collect-browser-proof",
2816
+ "collect-phone-proof": "/collect-phone-proof",
2817
+ "collect-provider-role-evidence": "/collect-provider-role-evidence",
2818
+ refresh: "/refresh"
2819
+ };
2820
+ var loadVoiceRealCallProfileHistoryRouteReport = async (options) => {
2821
+ const { source, ...routeOptions } = options;
2822
+ const sourceOptions = source === undefined ? routeOptions : typeof source === "function" ? await source() : source;
2823
+ return buildVoiceRealCallProfileHistoryReport({
2824
+ ...routeOptions,
2825
+ ...sourceOptions
2826
+ });
2827
+ };
2828
+ var createVoiceRealCallProfileRecoveryActionRoutes = (options = {}) => {
2829
+ const path = options.path ?? "/api/voice/real-call-profile-history";
2830
+ const routes = new Elysia({
2831
+ name: options.name ?? "absolutejs-voice-real-call-profile-recovery-actions"
2832
+ });
2833
+ const actionPath = (actionId) => `${path}${realCallProfileActionPaths[actionId]}`;
2834
+ const loadReport = () => loadVoiceRealCallProfileHistoryRouteReport(options);
2835
+ const listActions = async () => {
2836
+ const report = await loadReport();
2837
+ const actions = buildVoiceRealCallProfileRecoveryActions(report, {
2838
+ ...options,
2839
+ browserProofHref: options.browserProofHref ?? actionPath("collect-browser-proof"),
2840
+ phoneProofHref: options.phoneProofHref ?? actionPath("collect-phone-proof"),
2841
+ sourceHref: options.sourceHref ?? actionPath("collect-provider-role-evidence"),
2842
+ productionReadinessHref: options.productionReadinessHref ?? actionPath("refresh")
2843
+ }).map((action) => ({
2844
+ ...action,
2845
+ href: action.id === "collect-browser-proof" ? actionPath("collect-browser-proof") : action.id === "collect-phone-proof" ? actionPath("collect-phone-proof") : action.id === "collect-provider-role-evidence" ? actionPath("collect-provider-role-evidence") : action.href,
2846
+ method: action.id === "refresh" && (action.label === "Open real-call profile history" || action.label === "Open operations records") ? "GET" : "POST"
2847
+ }));
2848
+ return { actions, generatedAt: new Date().toISOString(), report };
2849
+ };
2850
+ const runAction = async (actionId) => {
2851
+ const report = await loadReport();
2852
+ const handler = options.handlers?.[actionId];
2853
+ if (!handler) {
2854
+ return {
2855
+ actionId,
2856
+ generatedAt: new Date().toISOString(),
2857
+ message: `No handler configured for real-call profile recovery action: ${actionId}.`,
2858
+ ok: false,
2859
+ status: "fail"
2860
+ };
2861
+ }
2862
+ const result = await handler({ actionId, report });
2863
+ return {
2864
+ actionId,
2865
+ generatedAt: new Date().toISOString(),
2866
+ message: result?.message,
2867
+ ok: result?.ok ?? true,
2868
+ report: result?.report,
2869
+ status: result?.status ?? "pass"
2870
+ };
2871
+ };
2872
+ routes.get(`${path}/actions`, async () => Response.json(await listActions(), { headers: options.headers }));
2873
+ for (const actionId of Object.keys(realCallProfileActionPaths)) {
2874
+ routes.post(actionPath(actionId), async ({ set }) => {
2875
+ const result = await runAction(actionId);
2876
+ if (!result.ok) {
2877
+ set.status = 501;
2878
+ }
2879
+ return Response.json(result, { headers: options.headers });
2880
+ });
2881
+ }
2882
+ return routes;
2883
+ };
2741
2884
  var createVoiceProofTrendRoutes = (options) => {
2742
2885
  const path = options.path ?? "/api/voice/proof-trends";
2743
2886
  const routes = new Elysia({
package/dist/vue/index.js CHANGED
@@ -2063,11 +2063,84 @@ var buildRealCallProfileReadinessIssues = (report, options) => {
2063
2063
  }
2064
2064
  return { issues, warnings };
2065
2065
  };
2066
+ var uniqueRealCallProfileActions = (actions) => {
2067
+ const seen = new Set;
2068
+ return actions.filter((action) => {
2069
+ const key = `${action.method ?? "GET"}:${action.href}:${action.label}`;
2070
+ if (seen.has(key)) {
2071
+ return false;
2072
+ }
2073
+ seen.add(key);
2074
+ return true;
2075
+ });
2076
+ };
2077
+ var buildVoiceRealCallProfileRecoveryActions = (report, options = {}) => {
2078
+ const actions = [
2079
+ {
2080
+ description: "Open the current real-call profile history report and profile defaults.",
2081
+ href: options.href ?? "/voice/real-call-profile-history",
2082
+ id: "refresh",
2083
+ label: "Open real-call profile history"
2084
+ },
2085
+ {
2086
+ description: "Refresh production readiness after collecting or replaying profile evidence.",
2087
+ href: options.productionReadinessHref ?? "/production-readiness",
2088
+ id: "refresh",
2089
+ label: "Refresh production readiness"
2090
+ }
2091
+ ];
2092
+ const requiredProfiles = new Set(options.requiredProfileIds ?? []);
2093
+ const profilesById = new Map(report.defaults.profiles.map((profile) => [profile.profileId, profile]));
2094
+ const missingProfiles = [...requiredProfiles].filter((profileId) => !profilesById.has(profileId));
2095
+ const warningProfiles = report.defaults.profiles.filter((profile) => (requiredProfiles.size === 0 || requiredProfiles.has(profile.profileId)) && profile.status !== "pass");
2096
+ const missingRoleProfiles = report.defaults.profiles.filter((profile) => (options.requiredProviderRoles ?? []).some((role) => !profile.providerRoutes[role]));
2097
+ const ageMs = report.trend.ageMs ?? (report.generatedAt ? Date.now() - new Date(report.generatedAt).getTime() : undefined);
2098
+ if (missingProfiles.length > 0 || warningProfiles.length > 0 || missingRoleProfiles.length > 0 || options.minCycles !== undefined && (report.summary.cycles ?? 0) < options.minCycles || options.minActionableProfiles !== undefined && report.defaults.summary.actionableProfiles < options.minActionableProfiles) {
2099
+ actions.push({
2100
+ description: "Run browser profile proof to collect microphone, WebSocket, live-latency, and provider traces for missing profiles.",
2101
+ href: options.browserProofHref ?? "/voice/browser-call-profiles",
2102
+ id: "collect-browser-proof",
2103
+ label: "Run browser profile proof"
2104
+ });
2105
+ actions.push({
2106
+ description: "Run phone profile proof when required profiles depend on carrier, telephony media, or noisy-call evidence.",
2107
+ href: options.phoneProofHref ?? "/api/voice/phone/smoke",
2108
+ id: "collect-phone-proof",
2109
+ label: "Run phone profile proof"
2110
+ });
2111
+ }
2112
+ if (options.maxAgeMs !== undefined && (ageMs === undefined || ageMs > options.maxAgeMs)) {
2113
+ actions.push({
2114
+ description: "Collect fresh real-call profile traces because the current history artifact is stale.",
2115
+ href: options.browserProofHref ?? "/voice/browser-call-profiles",
2116
+ id: "collect-browser-proof",
2117
+ label: "Collect fresh profile evidence"
2118
+ });
2119
+ }
2120
+ if (missingRoleProfiles.length > 0 || report.defaults.summary.actionableProfiles < (options.minActionableProfiles ?? 1)) {
2121
+ actions.push({
2122
+ description: "Collect missing LLM/STT/TTS provider-role evidence so profile defaults can become actionable.",
2123
+ href: options.sourceHref ?? "/api/voice/real-call-profile-history",
2124
+ id: "collect-provider-role-evidence",
2125
+ label: "Collect missing provider-role evidence"
2126
+ });
2127
+ }
2128
+ if (report.recommendations.profiles.some((profile) => profile.status !== "pass") || report.defaults.profiles.some((profile) => profile.status !== "pass")) {
2129
+ actions.push({
2130
+ description: "Open operations records to inspect the sessions behind failing or warning profile evidence.",
2131
+ href: options.operationsRecordsHref ?? "/voice-operations",
2132
+ id: "refresh",
2133
+ label: "Open operations records"
2134
+ });
2135
+ }
2136
+ return uniqueRealCallProfileActions(actions);
2137
+ };
2066
2138
  var buildVoiceRealCallProfileReadinessCheck = (report, options = {}) => {
2067
2139
  const { issues, warnings } = buildRealCallProfileReadinessIssues(report, options);
2068
2140
  const status = issues.length > 0 ? "fail" : warnings.length > 0 && options.failOnWarnings === true ? "fail" : warnings.length > 0 ? "warn" : "pass";
2069
2141
  const detail = status === "pass" ? `${String(report.summary.profileCount)} profile(s), ${String(report.summary.cycles ?? 0)} cycle(s), ${String(report.defaults.summary.actionableProfiles)} actionable default(s).` : [...issues, ...warnings].join(" ");
2070
2142
  return {
2143
+ actions: buildVoiceRealCallProfileRecoveryActions(report, options),
2071
2144
  detail,
2072
2145
  gateExplanation: {
2073
2146
  evidenceHref: options.href ?? "/api/voice/real-call-profile-history",
@@ -2659,6 +2732,76 @@ var createVoiceRealCallProfileHistoryRoutes = (options = {}) => {
2659
2732
  }
2660
2733
  return routes;
2661
2734
  };
2735
+ var realCallProfileActionPaths = {
2736
+ "collect-browser-proof": "/collect-browser-proof",
2737
+ "collect-phone-proof": "/collect-phone-proof",
2738
+ "collect-provider-role-evidence": "/collect-provider-role-evidence",
2739
+ refresh: "/refresh"
2740
+ };
2741
+ var loadVoiceRealCallProfileHistoryRouteReport = async (options) => {
2742
+ const { source, ...routeOptions } = options;
2743
+ const sourceOptions = source === undefined ? routeOptions : typeof source === "function" ? await source() : source;
2744
+ return buildVoiceRealCallProfileHistoryReport({
2745
+ ...routeOptions,
2746
+ ...sourceOptions
2747
+ });
2748
+ };
2749
+ var createVoiceRealCallProfileRecoveryActionRoutes = (options = {}) => {
2750
+ const path = options.path ?? "/api/voice/real-call-profile-history";
2751
+ const routes = new Elysia({
2752
+ name: options.name ?? "absolutejs-voice-real-call-profile-recovery-actions"
2753
+ });
2754
+ const actionPath = (actionId) => `${path}${realCallProfileActionPaths[actionId]}`;
2755
+ const loadReport = () => loadVoiceRealCallProfileHistoryRouteReport(options);
2756
+ const listActions = async () => {
2757
+ const report = await loadReport();
2758
+ const actions = buildVoiceRealCallProfileRecoveryActions(report, {
2759
+ ...options,
2760
+ browserProofHref: options.browserProofHref ?? actionPath("collect-browser-proof"),
2761
+ phoneProofHref: options.phoneProofHref ?? actionPath("collect-phone-proof"),
2762
+ sourceHref: options.sourceHref ?? actionPath("collect-provider-role-evidence"),
2763
+ productionReadinessHref: options.productionReadinessHref ?? actionPath("refresh")
2764
+ }).map((action) => ({
2765
+ ...action,
2766
+ href: action.id === "collect-browser-proof" ? actionPath("collect-browser-proof") : action.id === "collect-phone-proof" ? actionPath("collect-phone-proof") : action.id === "collect-provider-role-evidence" ? actionPath("collect-provider-role-evidence") : action.href,
2767
+ method: action.id === "refresh" && (action.label === "Open real-call profile history" || action.label === "Open operations records") ? "GET" : "POST"
2768
+ }));
2769
+ return { actions, generatedAt: new Date().toISOString(), report };
2770
+ };
2771
+ const runAction = async (actionId) => {
2772
+ const report = await loadReport();
2773
+ const handler = options.handlers?.[actionId];
2774
+ if (!handler) {
2775
+ return {
2776
+ actionId,
2777
+ generatedAt: new Date().toISOString(),
2778
+ message: `No handler configured for real-call profile recovery action: ${actionId}.`,
2779
+ ok: false,
2780
+ status: "fail"
2781
+ };
2782
+ }
2783
+ const result = await handler({ actionId, report });
2784
+ return {
2785
+ actionId,
2786
+ generatedAt: new Date().toISOString(),
2787
+ message: result?.message,
2788
+ ok: result?.ok ?? true,
2789
+ report: result?.report,
2790
+ status: result?.status ?? "pass"
2791
+ };
2792
+ };
2793
+ routes.get(`${path}/actions`, async () => Response.json(await listActions(), { headers: options.headers }));
2794
+ for (const actionId of Object.keys(realCallProfileActionPaths)) {
2795
+ routes.post(actionPath(actionId), async ({ set }) => {
2796
+ const result = await runAction(actionId);
2797
+ if (!result.ok) {
2798
+ set.status = 501;
2799
+ }
2800
+ return Response.json(result, { headers: options.headers });
2801
+ });
2802
+ }
2803
+ return routes;
2804
+ };
2662
2805
  var createVoiceProofTrendRoutes = (options) => {
2663
2806
  const path = options.path ?? "/api/voice/proof-trends";
2664
2807
  const routes = new Elysia({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.370",
3
+ "version": "0.0.22-beta.372",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",