@absolutejs/voice 0.0.22-beta.135 → 0.0.22-beta.137

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/dist/index.d.ts CHANGED
@@ -18,7 +18,7 @@ export type { VoiceDemoReadyReport, VoiceDemoReadyRoutesOptions, VoiceDemoReadyS
18
18
  export { compareVoiceEvalBaseline, createVoiceFileEvalBaselineStore, createVoiceFileScenarioFixtureStore, createVoiceEvalRoutes, renderVoiceEvalBaselineHTML, renderVoiceEvalHTML, renderVoiceScenarioEvalHTML, renderVoiceScenarioFixtureEvalHTML, runVoiceScenarioEvals, runVoiceScenarioFixtureEvals, runVoiceSessionEvals } from './evalRoutes';
19
19
  export { createVoiceSimulationSuiteRoutes, renderVoiceSimulationSuiteHTML, runVoiceSimulationSuite } from './simulationSuite';
20
20
  export { createVoiceWorkflowContract, createVoiceWorkflowContractHandler, createVoiceWorkflowContractPreset, createVoiceWorkflowScenario, recordVoiceWorkflowContractTrace, validateVoiceWorkflowRouteResult } from './workflowContract';
21
- export { createVoiceSessionListRoutes, createVoiceSessionReplayHTMLHandler, createVoiceSessionReplayJSONHandler, createVoiceSessionReplayRoutes, createVoiceSessionsHTMLHandler, createVoiceSessionsJSONHandler, renderVoiceSessionsHTML, summarizeVoiceSessions, summarizeVoiceSessionReplay } from './sessionReplay';
21
+ export { createVoiceSessionListRoutes, createVoiceSessionReplayHTMLHandler, createVoiceSessionReplayJSONHandler, createVoiceSessionReplayRoutes, createVoiceSessionsHTMLHandler, createVoiceSessionsJSONHandler, renderVoiceSessionsHTML, summarizeVoiceProviderFallbackRecovery, summarizeVoiceSessions, summarizeVoiceSessionReplay } from './sessionReplay';
22
22
  export { createVoiceAgent, createVoiceAgentSquad, createVoiceAgentTool } from './agent';
23
23
  export { assertVoiceAgentSquadContract, runVoiceAgentSquadContract } from './agentSquadContract';
24
24
  export { createVoiceToolIdempotencyKey, createVoiceToolRuntime } from './toolRuntime';
@@ -80,7 +80,7 @@ export type { VoiceDiagnosticsRoutesOptions } from './diagnosticsRoutes';
80
80
  export type { VoiceEvalBaselineComparison, VoiceEvalBaselineComparisonOptions, VoiceEvalBaselineStore, VoiceEvalBaselineSummary, VoiceEvalLink, VoiceEvalReport, VoiceEvalRoutesOptions, VoiceEvalSessionReport, VoiceEvalStatus, VoiceEvalTrendBucket, VoiceScenarioEvalDefinition, VoiceScenarioEvalReport, VoiceScenarioEvalResult, VoiceScenarioEvalSessionResult, VoiceScenarioFixture, VoiceScenarioFixtureEvalReport, VoiceScenarioFixtureEvalResult, VoiceScenarioFixtureStore } from './evalRoutes';
81
81
  export type { VoiceSimulationSuiteEvalRoutesOptions, VoiceSimulationSuiteOptions, VoiceSimulationSuiteReport, VoiceSimulationSuiteRoutesOptions, VoiceSimulationSuiteSectionSummary, VoiceSimulationSuiteStatus } from './simulationSuite';
82
82
  export type { VoiceWorkflowContract, VoiceWorkflowContractDefinition, VoiceWorkflowContractField, VoiceWorkflowContractFieldMatch, VoiceWorkflowContractPresetName, VoiceWorkflowContractPresetOptions, VoiceWorkflowContractTracePayload, VoiceWorkflowContractValidation, VoiceWorkflowContractValidationIssue, VoiceWorkflowOutcome } from './workflowContract';
83
- export type { VoiceSessionListHTMLHandlerOptions, VoiceSessionListItem, VoiceSessionListOptions, VoiceSessionListRoutesOptions, VoiceSessionListStatus, VoiceSessionReplay, VoiceSessionReplayHTMLHandlerOptions, VoiceSessionReplayOptions, VoiceSessionReplayRoutesOptions, VoiceSessionReplayTurn } from './sessionReplay';
83
+ export type { VoiceSessionListHTMLHandlerOptions, VoiceSessionListItem, VoiceSessionListOptions, VoiceSessionListRoutesOptions, VoiceSessionListStatus, VoiceProviderFallbackRecoverySummary, VoiceSessionReplay, VoiceSessionReplayHTMLHandlerOptions, VoiceSessionReplayOptions, VoiceSessionReplayRoutesOptions, VoiceSessionReplayTurn } from './sessionReplay';
84
84
  export type { AnthropicVoiceAssistantModelOptions, GeminiVoiceAssistantModelOptions, OpenAIVoiceAssistantModelOptions, VoiceProviderRouterEvent, VoiceProviderRouterFallbackMode, VoiceProviderRouterHealthOptions, VoiceProviderRouterOptions, VoiceProviderRouterPolicy, VoiceProviderRouterPolicyPreset, VoiceProviderRouterPolicyWeights, VoiceProviderRouterProviderHealth, VoiceProviderRouterProviderProfile, VoiceProviderRouterStrategy, VoiceJSONAssistantModelHandler, VoiceJSONAssistantModelOptions } from './modelAdapters';
85
85
  export type { OpenAIVoiceTTSOptions, OpenAIVoiceTTSVoice } from './openaiTTS';
86
86
  export type { OpenAIRealtimeAdapterOptions, OpenAIRealtimeModel, OpenAIRealtimeNoiseReduction, OpenAIRealtimeResponseMode, OpenAIRealtimeTranscriptionModel, OpenAIRealtimeVoice } from './openaiRealtime';
package/dist/index.js CHANGED
@@ -12450,6 +12450,23 @@ var increment4 = (record, key) => {
12450
12450
  var isProviderErrorEvent = (event) => event.type === "session.error" && (event.payload.providerStatus === "error" || typeof event.payload.error === "string");
12451
12451
  var isRecoveredProviderFallbackEvent = (event) => event.type === "session.error" && (event.payload.providerStatus === "fallback" || event.payload.status === "fallback") && event.payload.recovered === true;
12452
12452
  var turnRecoveryKey = (event) => event.turnId ?? `session:${event.sessionId}`;
12453
+ var summarizeVoiceProviderFallbackRecovery = (events) => {
12454
+ const sorted = filterVoiceTraceEvents(events);
12455
+ const recoveredEvents = sorted.filter(isRecoveredProviderFallbackEvent);
12456
+ const recoveredKeys = new Set(recoveredEvents.map((event) => turnRecoveryKey(event)));
12457
+ const recoveredSessions = new Set(recoveredEvents.map((event) => event.sessionId));
12458
+ const unresolvedErrorEvents = sorted.filter((event) => isProviderErrorEvent(event) && !recoveredKeys.has(turnRecoveryKey(event)));
12459
+ const unresolvedSessions = new Set(unresolvedErrorEvents.map((event) => event.sessionId));
12460
+ return {
12461
+ recovered: recoveredEvents.length,
12462
+ recoveredSessions: recoveredSessions.size,
12463
+ recoveredTurns: recoveredKeys.size,
12464
+ status: unresolvedErrorEvents.length > 0 ? "fail" : "pass",
12465
+ total: recoveredEvents.length + unresolvedErrorEvents.length,
12466
+ unresolvedErrors: unresolvedErrorEvents.length,
12467
+ unresolvedSessions: unresolvedSessions.size
12468
+ };
12469
+ };
12453
12470
  var buildReplayTurns = (events) => {
12454
12471
  const turns = new Map;
12455
12472
  const getTurn = (turnId) => {
@@ -19645,6 +19662,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
19645
19662
  const routingEvents = listVoiceRoutingEvents(events);
19646
19663
  const routingSessions = summarizeVoiceRoutingSessions(routingEvents);
19647
19664
  const liveLatency = summarizeLiveLatency(events, options);
19665
+ const providerRecovery = summarizeVoiceProviderFallbackRecovery(events);
19648
19666
  const [
19649
19667
  quality,
19650
19668
  providers,
@@ -19718,6 +19736,20 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
19718
19736
  }
19719
19737
  ] : []
19720
19738
  },
19739
+ {
19740
+ detail: providerRecovery.unresolvedErrors > 0 ? `${providerRecovery.unresolvedErrors} provider error(s) have no recovered fallback evidence.` : providerRecovery.recovered > 0 ? `${providerRecovery.recovered} provider fallback recovery event(s) kept sessions healthy.` : "No provider fallback recovery was needed in the current trace window.",
19741
+ href: options.links?.resilience ?? "/resilience",
19742
+ label: "Provider fallback recovery",
19743
+ status: providerRecovery.status,
19744
+ value: providerRecovery.total === 0 ? "0 events" : `${providerRecovery.recovered}/${providerRecovery.total}`,
19745
+ actions: providerRecovery.status === "pass" ? [] : [
19746
+ {
19747
+ description: "Open provider resilience traces and inspect unresolved provider errors.",
19748
+ href: options.links?.resilience ?? "/resilience",
19749
+ label: "Open provider recovery"
19750
+ }
19751
+ ]
19752
+ },
19721
19753
  {
19722
19754
  detail: failedSessions === 0 ? sessions.length > 0 ? "Recent sessions have no recorded provider/session failures." : "No sessions have been recorded yet; run a smoke or live session for proof." : `${failedSessions} recent session(s) have failures.`,
19723
19755
  href: options.links?.sessions ?? "/sessions",
@@ -20007,6 +20039,7 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
20007
20039
  degraded: degradedProviders,
20008
20040
  total: providers.length
20009
20041
  },
20042
+ providerRecovery,
20010
20043
  phoneAgentSmokes: phoneAgentSmokeSummary,
20011
20044
  providerRoutingContracts: providerRoutingContractSummary,
20012
20045
  reconnectContracts: reconnectContractSummary,
@@ -20285,6 +20318,7 @@ var summarizeVoiceOpsStatus = async (options) => {
20285
20318
  events
20286
20319
  })
20287
20320
  ]);
20321
+ const providerRecovery = shouldInclude("providerRecovery") ? summarizeVoiceProviderFallbackRecovery(events) : undefined;
20288
20322
  const surfaces = {};
20289
20323
  const statuses = [];
20290
20324
  if (quality) {
@@ -20305,6 +20339,10 @@ var summarizeVoiceOpsStatus = async (options) => {
20305
20339
  };
20306
20340
  statuses.push(status);
20307
20341
  }
20342
+ if (providerRecovery) {
20343
+ surfaces.providerRecovery = providerRecovery;
20344
+ statuses.push(providerRecovery.status);
20345
+ }
20308
20346
  if (sessions) {
20309
20347
  const failed = sessions.filter((session) => session.status === "failed").length;
20310
20348
  const status = failed > 0 ? "fail" : "pass";
@@ -20338,7 +20376,7 @@ var escapeHtml34 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&l
20338
20376
  var renderVoiceOpsStatusHTML = (report, options = {}) => {
20339
20377
  const title = options.title ?? "AbsoluteJS Voice Ops Status";
20340
20378
  const surfaces = Object.entries(report.surfaces).map(([key, surface]) => {
20341
- const value = "total" in surface ? `${Math.max(surface.total - ("failed" in surface ? surface.failed : ("degraded" in surface) ? surface.degraded : 0), 0)}/${surface.total}` : surface.status;
20379
+ const value = "recovered" in surface ? surface.total === 0 ? "0 events" : `${surface.recovered}/${surface.total}` : ("total" in surface) ? `${Math.max(surface.total - ("failed" in surface ? surface.failed : ("degraded" in surface) ? surface.degraded : 0), 0)}/${surface.total}` : surface.status;
20342
20380
  return `<article class="surface ${escapeHtml34(surface.status)}"><span>${escapeHtml34(surface.status.toUpperCase())}</span><h2>${escapeHtml34(key)}</h2><strong>${escapeHtml34(value)}</strong></article>`;
20343
20381
  }).join("");
20344
20382
  return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml34(title)}</title><style>body{background:#0d141b;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.2),rgba(245,158,11,.12));border:1px solid #283544;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #3f3f46;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.surfaces{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.surface{background:#151d26;border:1px solid #283544;border-radius:20px;padding:18px}.surface span{color:#aab5c0;font-size:.78rem;font-weight:900;letter-spacing:.08em}.surface strong{font-size:1.5rem}.pass{border-color:rgba(34,197,94,.55)}.fail{border-color:rgba(239,68,68,.75)}a{color:#5eead4}</style></head><body><main><section class="hero"><p class="eyebrow">Ops status</p><h1>${escapeHtml34(title)}</h1><p>Compact pass/fail status for framework widgets, demos, and small customer-facing health badges.</p><p class="status ${escapeHtml34(report.status)}">Overall: ${escapeHtml34(report.status.toUpperCase())}</p><p>${report.passed}/${report.total} checks passing</p></section><section class="surfaces">${surfaces || '<article class="surface pass"><span>PASS</span><h2>No checks configured</h2><strong>0/0</strong></article>'}</section></main></body></html>`;
@@ -22732,6 +22770,7 @@ export {
22732
22770
  summarizeVoiceRoutingSessions,
22733
22771
  summarizeVoiceRoutingDecision,
22734
22772
  summarizeVoiceProviderHealth,
22773
+ summarizeVoiceProviderFallbackRecovery,
22735
22774
  summarizeVoiceProviderCapabilities,
22736
22775
  summarizeVoiceOpsTasks,
22737
22776
  summarizeVoiceOpsTaskQueue,
@@ -1,5 +1,6 @@
1
1
  import { type VoiceEvalLink, type VoiceEvalRoutesOptions } from './evalRoutes';
2
2
  import { type VoiceQualityRoutesOptions } from './qualityRoutes';
3
+ import { type VoiceProviderFallbackRecoverySummary } from './sessionReplay';
3
4
  import { type VoiceTraceEventStore } from './trace';
4
5
  export type VoiceOpsStatus = 'pass' | 'fail';
5
6
  export type VoiceOpsStatusLink = VoiceEvalLink & {
@@ -11,6 +12,7 @@ export type VoiceOpsStatusOptions<TProvider extends string = string> = {
11
12
  include?: {
12
13
  handoffs?: boolean;
13
14
  providers?: boolean;
15
+ providerRecovery?: boolean;
14
16
  quality?: boolean;
15
17
  sessions?: boolean;
16
18
  workflows?: boolean;
@@ -40,6 +42,7 @@ export type VoiceOpsStatusReport = {
40
42
  status: VoiceOpsStatus;
41
43
  total: number;
42
44
  };
45
+ providerRecovery?: VoiceProviderFallbackRecoverySummary;
43
46
  quality?: {
44
47
  status: VoiceOpsStatus;
45
48
  };
@@ -1,4 +1,5 @@
1
1
  import { Elysia } from 'elysia';
2
+ import { type VoiceProviderFallbackRecoverySummary } from './sessionReplay';
2
3
  import { type VoiceTelephonyCarrierMatrixInput } from './telephony/matrix';
3
4
  import type { VoiceTraceEventStore } from './trace';
4
5
  import type { VoiceTraceSinkDeliveryStore } from './trace';
@@ -83,6 +84,7 @@ export type VoiceProductionReadinessReport = {
83
84
  degraded: number;
84
85
  total: number;
85
86
  };
87
+ providerRecovery: VoiceProviderFallbackRecoverySummary;
86
88
  phoneAgentSmokes?: {
87
89
  failed: number;
88
90
  passed: number;
@@ -43,6 +43,15 @@ export type VoiceSessionListItem = {
43
43
  transcriptCount: number;
44
44
  turnCount: number;
45
45
  };
46
+ export type VoiceProviderFallbackRecoverySummary = {
47
+ recovered: number;
48
+ recoveredSessions: number;
49
+ recoveredTurns: number;
50
+ status: 'fail' | 'pass';
51
+ total: number;
52
+ unresolvedErrors: number;
53
+ unresolvedSessions: number;
54
+ };
46
55
  export type VoiceSessionListOptions = {
47
56
  events?: StoredVoiceTraceEvent[];
48
57
  limit?: number;
@@ -78,6 +87,7 @@ export type VoiceSessionReplayRoutesOptions = VoiceSessionReplayHTMLHandlerOptio
78
87
  name?: string;
79
88
  path?: string;
80
89
  };
90
+ export declare const summarizeVoiceProviderFallbackRecovery: (events: StoredVoiceTraceEvent[]) => VoiceProviderFallbackRecoverySummary;
81
91
  export declare const summarizeVoiceSessionReplay: (options: VoiceSessionReplayOptions) => Promise<VoiceSessionReplay>;
82
92
  export declare const summarizeVoiceSessions: (options?: VoiceSessionListOptions) => Promise<VoiceSessionListItem[]>;
83
93
  export declare const renderVoiceSessionsHTML: (sessions: VoiceSessionListItem[]) => string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.135",
3
+ "version": "0.0.22-beta.137",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",