@absolutejs/voice 0.0.22-beta.435 → 0.0.22-beta.436

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.
@@ -38,6 +38,31 @@ export type VoiceIncidentRecoveryActionHandlerInput = {
38
38
  request: Request;
39
39
  };
40
40
  export type VoiceIncidentRecoveryActionHandler = (input: VoiceIncidentRecoveryActionHandlerInput) => Promise<VoiceIncidentRecoveryActionResult> | VoiceIncidentRecoveryActionResult;
41
+ export type VoiceIncidentRecoveryOutcome = 'failed' | 'improved' | 'regressed' | 'unchanged';
42
+ export type VoiceIncidentRecoveryOutcomeEntry = {
43
+ actionId: string;
44
+ afterStatus?: VoiceIncidentTimelineStatus;
45
+ at: number;
46
+ beforeStatus?: VoiceIncidentTimelineStatus;
47
+ detail?: string;
48
+ eventId: string;
49
+ outcome: VoiceIncidentRecoveryOutcome;
50
+ status?: number;
51
+ traceId?: string;
52
+ };
53
+ export type VoiceIncidentRecoveryOutcomeReport = {
54
+ checkedAt: number;
55
+ entries: VoiceIncidentRecoveryOutcomeEntry[];
56
+ failed: number;
57
+ improved: number;
58
+ regressed: number;
59
+ total: number;
60
+ unchanged: number;
61
+ };
62
+ export type VoiceIncidentRecoveryOutcomeOptions = {
63
+ audit?: VoiceAuditEventStore;
64
+ limit?: number;
65
+ };
41
66
  export type VoiceIncidentTimelineEvent = {
42
67
  action?: VoiceIncidentTimelineAction;
43
68
  at: number;
@@ -102,9 +127,15 @@ export type VoiceIncidentTimelineRoutesOptions = VoiceIncidentTimelineOptions &
102
127
  name?: string;
103
128
  path?: string;
104
129
  render?: (report: VoiceIncidentTimelineReport) => string | Promise<string>;
130
+ recoveryOutcomeHtmlPath?: false | string;
131
+ recoveryOutcomePath?: false | string;
105
132
  title?: string;
106
133
  trace?: VoiceTraceEventStore;
107
134
  };
135
+ export declare const buildVoiceIncidentRecoveryOutcomeReport: (options: VoiceIncidentRecoveryOutcomeOptions) => Promise<VoiceIncidentRecoveryOutcomeReport>;
136
+ export declare const renderVoiceIncidentRecoveryOutcomeHTML: (report: VoiceIncidentRecoveryOutcomeReport, options?: {
137
+ title?: string;
138
+ }) => string;
108
139
  export declare const buildVoiceIncidentTimelineReport: (options: VoiceIncidentTimelineOptions) => Promise<VoiceIncidentTimelineReport>;
109
140
  export declare const renderVoiceIncidentTimelineMarkdown: (report: VoiceIncidentTimelineReport, options?: {
110
141
  title?: string;
package/dist/index.d.ts CHANGED
@@ -52,7 +52,7 @@ export { assertVoiceLiveOpsControlEvidence, assertVoiceLiveOpsEvidence, buildVoi
52
52
  export type { VoiceLiveOpsAction, VoiceLiveOpsActionInput, VoiceLiveOpsActionResult, VoiceLiveOpsControllerOptions, VoiceLiveOpsControlState, VoiceLiveOpsControlStatus, VoiceLiveOpsControlStore, VoiceLiveOpsControlEvidenceInput, VoiceLiveOpsControlEvidenceReport, VoiceLiveOpsEvidenceInput, VoiceLiveOpsEvidenceReport, VoiceLiveOpsRoutesOptions } from './liveOps';
53
53
  export { buildVoiceDeliveryRuntimeReport, createVoiceDeliveryRuntime, createVoiceDeliveryRuntimePresetConfig, createVoiceDeliveryRuntimeRoutes, renderVoiceDeliveryRuntimeHTML } from './deliveryRuntime';
54
54
  export { buildVoiceOperationalStatusReport, createVoiceOperationalStatusRoutes, renderVoiceOperationalStatusHTML } from './operationalStatus';
55
- export { buildVoiceIncidentTimelineReport, createVoiceIncidentTimelineRoutes, renderVoiceIncidentTimelineHTML, renderVoiceIncidentTimelineMarkdown } from './incidentTimeline';
55
+ export { buildVoiceIncidentRecoveryOutcomeReport, buildVoiceIncidentTimelineReport, createVoiceIncidentTimelineRoutes, renderVoiceIncidentRecoveryOutcomeHTML, renderVoiceIncidentTimelineHTML, renderVoiceIncidentTimelineMarkdown } from './incidentTimeline';
56
56
  export { applyVoiceDataRetentionPolicy, assertVoiceDataControlEvidence, buildVoiceDataControlReport, buildVoiceDataRetentionPlan, createVoiceDataControlRoutes, createVoiceZeroRetentionPolicy, evaluateVoiceDataControlEvidence, renderVoiceDataControlHTML, renderVoiceDataControlMarkdown, voiceComplianceRedactionDefaults } from './dataControl';
57
57
  export type { VoiceDataControlAssertionInput, VoiceDataControlAssertionReport, VoiceDataControlProviderKeySurface, VoiceDataControlReport, VoiceDataControlRoutesOptions, VoiceDataControlStorageSurface, VoiceDataRetentionPolicy, VoiceDataRetentionReport, VoiceDataRetentionScope, VoiceDataRetentionScopeReport, VoiceDataRetentionStores } from './dataControl';
58
58
  export type { VoiceDemoReadyReport, VoiceDemoReadyRoutesOptions, VoiceDemoReadySection, VoiceDemoReadyStatus } from './demoReadyRoutes';
@@ -60,7 +60,7 @@ export type { VoiceDeliverySinkDescriptor, VoiceDeliverySinkDescriptorInput, Voi
60
60
  export type { VoiceOpsActionAuditRecord, VoiceOpsActionAuditRoutesOptions, VoiceOpsActionHistoryEntry, VoiceOpsActionHistoryReport } from './opsActionAuditRoutes';
61
61
  export type { VoiceDeliveryRuntime, VoiceDeliveryRuntimeAuditConfig, VoiceDeliveryRuntimeConfig, VoiceDeliveryRuntimeFilePresetOptions, VoiceDeliveryRuntimePresetLeaseConfig, VoiceDeliveryRuntimePresetMode, VoiceDeliveryRuntimePresetOptions, VoiceDeliveryRuntimeReport, VoiceDeliveryRuntimeRoutesOptions, VoiceDeliveryRuntimeS3PresetOptions, VoiceDeliveryRuntimeSummary, VoiceDeliveryRuntimeTickResult, VoiceDeliveryRuntimeTraceConfig, VoiceDeliveryRuntimeWebhookPresetOptions } from './deliveryRuntime';
62
62
  export type { VoiceOperationalStatus, VoiceOperationalStatusCheck, VoiceOperationalStatusOptions, VoiceOperationalStatusReport, VoiceOperationalStatusRoutesOptions, VoiceOperationalStatusValue } from './operationalStatus';
63
- export type { VoiceIncidentRecoveryAction, VoiceIncidentRecoveryActionHandler, VoiceIncidentRecoveryActionHandlerInput, VoiceIncidentRecoveryActionResult, VoiceIncidentTimelineAction, VoiceIncidentTimelineEvent, VoiceIncidentTimelineLinks, VoiceIncidentTimelineOptions, VoiceIncidentTimelineReport, VoiceIncidentTimelineRoutesOptions, VoiceIncidentTimelineSeverity, VoiceIncidentTimelineStatus, VoiceIncidentTimelineValue } from './incidentTimeline';
63
+ export type { VoiceIncidentRecoveryAction, VoiceIncidentRecoveryActionHandler, VoiceIncidentRecoveryActionHandlerInput, VoiceIncidentRecoveryActionResult, VoiceIncidentRecoveryOutcome, VoiceIncidentRecoveryOutcomeEntry, VoiceIncidentRecoveryOutcomeOptions, VoiceIncidentRecoveryOutcomeReport, VoiceIncidentTimelineAction, VoiceIncidentTimelineEvent, VoiceIncidentTimelineLinks, VoiceIncidentTimelineOptions, VoiceIncidentTimelineReport, VoiceIncidentTimelineRoutesOptions, VoiceIncidentTimelineSeverity, VoiceIncidentTimelineStatus, VoiceIncidentTimelineValue } from './incidentTimeline';
64
64
  export { compareVoiceEvalBaseline, createVoiceFileEvalBaselineStore, createVoiceFileScenarioFixtureStore, createVoiceEvalRoutes, renderVoiceEvalBaselineHTML, renderVoiceEvalHTML, renderVoiceScenarioEvalHTML, renderVoiceScenarioFixtureEvalHTML, runVoiceScenarioEvals, runVoiceScenarioFixtureEvals, runVoiceSessionEvals } from './evalRoutes';
65
65
  export { assertVoiceSimulationSuiteEvidence, createVoiceSimulationSuiteRoutes, evaluateVoiceSimulationSuiteEvidence, renderVoiceSimulationSuiteHTML, runVoiceSimulationSuite } from './simulationSuite';
66
66
  export { createVoiceWorkflowContract, createVoiceWorkflowContractHandler, createVoiceWorkflowContractPreset, createVoiceWorkflowScenario, recordVoiceWorkflowContractTrace, validateVoiceWorkflowRouteResult } from './workflowContract';
package/dist/index.js CHANGED
@@ -30183,6 +30183,62 @@ var defaultIncidentRecoveryActions = (events, links) => {
30183
30183
  return actions;
30184
30184
  };
30185
30185
  var worstStatus3 = (statuses) => statuses.includes("fail") ? "fail" : statuses.includes("warn") ? "warn" : "pass";
30186
+ var statusRank6 = (status) => status === "fail" ? 3 : status === "warn" ? 2 : status === "pass" ? 1 : 0;
30187
+ var isRecord4 = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
30188
+ var getIncidentRecoveryBody = (event) => {
30189
+ const payload = isRecord4(event.payload) ? event.payload : {};
30190
+ return isRecord4(payload.body) ? payload.body : {};
30191
+ };
30192
+ var getIncidentRecoveryStatus = (value) => value === "fail" || value === "pass" || value === "warn" ? value : undefined;
30193
+ var getIncidentRecoveryDetail = (event) => {
30194
+ const payload = isRecord4(event.payload) ? event.payload : {};
30195
+ const body = getIncidentRecoveryBody(event);
30196
+ const result = isRecord4(body.result) ? body.result : {};
30197
+ const detail = result.detail ?? payload.error;
30198
+ return typeof detail === "string" ? detail : undefined;
30199
+ };
30200
+ var toIncidentRecoveryOutcomeEntry = (event) => {
30201
+ const body = getIncidentRecoveryBody(event);
30202
+ const beforeStatus = getIncidentRecoveryStatus(body.beforeStatus);
30203
+ const afterStatus = getIncidentRecoveryStatus(body.afterStatus);
30204
+ const beforeRank = statusRank6(beforeStatus);
30205
+ const afterRank = statusRank6(afterStatus);
30206
+ const outcome = event.outcome === "error" ? "failed" : beforeRank > 0 && afterRank > 0 && afterRank < beforeRank ? "improved" : beforeRank > 0 && afterRank > beforeRank ? "regressed" : "unchanged";
30207
+ const payload = isRecord4(event.payload) ? event.payload : {};
30208
+ return {
30209
+ actionId: event.action.replace(/^incident\./, ""),
30210
+ afterStatus,
30211
+ at: event.at,
30212
+ beforeStatus,
30213
+ detail: getIncidentRecoveryDetail(event),
30214
+ eventId: event.id,
30215
+ outcome,
30216
+ status: typeof payload.status === "number" ? payload.status : undefined,
30217
+ traceId: event.traceId
30218
+ };
30219
+ };
30220
+ var buildVoiceIncidentRecoveryOutcomeReport = async (options) => {
30221
+ const events = options.audit ? await options.audit.list({
30222
+ limit: options.limit ?? 50,
30223
+ resourceType: "voice.ops.action",
30224
+ type: "operator.action"
30225
+ }) : [];
30226
+ const entries = events.filter((event) => event.action.startsWith("incident.")).map(toIncidentRecoveryOutcomeEntry).sort((left, right) => right.at - left.at);
30227
+ return {
30228
+ checkedAt: Date.now(),
30229
+ entries,
30230
+ failed: entries.filter((entry) => entry.outcome === "failed").length,
30231
+ improved: entries.filter((entry) => entry.outcome === "improved").length,
30232
+ regressed: entries.filter((entry) => entry.outcome === "regressed").length,
30233
+ total: entries.length,
30234
+ unchanged: entries.filter((entry) => entry.outcome === "unchanged").length
30235
+ };
30236
+ };
30237
+ var renderVoiceIncidentRecoveryOutcomeHTML = (report, options = {}) => {
30238
+ const title = options.title ?? "AbsoluteJS Voice Incident Recovery Outcomes";
30239
+ const rows = report.entries.map((entry) => `<article class="${escapeHtml43(entry.outcome)}"><span>${escapeHtml43(entry.outcome.toUpperCase())}</span><h2>${escapeHtml43(entry.actionId)}</h2><p>${escapeHtml43(new Date(entry.at).toLocaleString())}</p><strong>${escapeHtml43(entry.beforeStatus ?? "unknown")} -> ${escapeHtml43(entry.afterStatus ?? "unknown")}</strong>${entry.detail ? `<p>${escapeHtml43(entry.detail)}</p>` : ""}</article>`).join("");
30240
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml43(title)}</title><style>body{background:#10120d;color:#fbf4df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero,article{background:#181711;border:1px solid #39301d;border-radius:24px;padding:20px}.hero{margin-bottom:16px}h1{font-size:clamp(2rem,6vw,4.5rem);line-height:.95}.summary{display:flex;flex-wrap:wrap;gap:10px}.summary span{border:1px solid #4a3f23;border-radius:999px;padding:8px 12px}section{display:grid;gap:12px}article.improved{border-color:rgba(34,197,94,.65)}article.failed,article.regressed{border-color:rgba(239,68,68,.8)}article.unchanged{border-color:rgba(245,158,11,.7)}article span{color:#fcd34d;font-weight:900;letter-spacing:.08em}article strong{display:block;font-size:1.4rem;margin:.5rem 0}p{color:#cfc5a8}</style></head><body><main><section class="hero"><span>Recovery proof</span><h1>${escapeHtml43(title)}</h1><div class="summary"><span>${String(report.improved)} improved</span><span>${String(report.unchanged)} unchanged</span><span>${String(report.regressed)} regressed</span><span>${String(report.failed)} failed</span><span>${String(report.total)} total</span></div></section><section>${rows || "<p>No incident recovery actions have been recorded.</p>"}</section></main></body></html>`;
30241
+ };
30186
30242
  var pushOperationalStatusEvents = (events, report, links) => {
30187
30243
  if (!report) {
30188
30244
  return;
@@ -30429,6 +30485,8 @@ var createVoiceIncidentTimelineRoutes = (options) => {
30429
30485
  const htmlPath = options.htmlPath === undefined ? "/voice/incident-timeline" : options.htmlPath;
30430
30486
  const markdownPath = options.markdownPath === undefined ? "/voice/incident-timeline.md" : options.markdownPath;
30431
30487
  const actionPath = options.actionPath === undefined ? "/api/voice/incident-timeline/actions" : options.actionPath;
30488
+ const recoveryOutcomePath = options.recoveryOutcomePath === undefined ? "/api/voice/incident-timeline/recovery-outcomes" : options.recoveryOutcomePath;
30489
+ const recoveryOutcomeHtmlPath = options.recoveryOutcomeHtmlPath === undefined ? "/voice/incident-recovery-outcomes" : options.recoveryOutcomeHtmlPath;
30432
30490
  const routes = new Elysia46({
30433
30491
  name: options.name ?? "absolutejs-voice-incident-timeline"
30434
30492
  }).get(path, async () => {
@@ -30552,6 +30610,34 @@ var createVoiceIncidentTimelineRoutes = (options) => {
30552
30610
  });
30553
30611
  });
30554
30612
  }
30613
+ if (recoveryOutcomePath !== false) {
30614
+ routes.get(recoveryOutcomePath, async () => {
30615
+ const report = await buildVoiceIncidentRecoveryOutcomeReport({
30616
+ audit: options.audit
30617
+ });
30618
+ return new Response(JSON.stringify(report), {
30619
+ headers: {
30620
+ "Content-Type": "application/json; charset=utf-8",
30621
+ ...options.headers
30622
+ }
30623
+ });
30624
+ });
30625
+ }
30626
+ if (recoveryOutcomeHtmlPath !== false) {
30627
+ routes.get(recoveryOutcomeHtmlPath, async () => {
30628
+ const report = await buildVoiceIncidentRecoveryOutcomeReport({
30629
+ audit: options.audit
30630
+ });
30631
+ return new Response(renderVoiceIncidentRecoveryOutcomeHTML(report, {
30632
+ title: `${options.title ?? "AbsoluteJS Voice Incident Timeline"} Recovery Outcomes`
30633
+ }), {
30634
+ headers: {
30635
+ "Content-Type": "text/html; charset=utf-8",
30636
+ ...options.headers
30637
+ }
30638
+ });
30639
+ });
30640
+ }
30555
30641
  return routes;
30556
30642
  };
30557
30643
  // src/dataControl.ts
@@ -36062,7 +36148,7 @@ var defaultRequirement = {
36062
36148
  requireFallback: false,
36063
36149
  requireTimeoutBudget: false
36064
36150
  };
36065
- var statusRank6 = {
36151
+ var statusRank7 = {
36066
36152
  pass: 0,
36067
36153
  warn: 1,
36068
36154
  fail: 2
@@ -36078,7 +36164,7 @@ var surfaceProviderNames = (surface) => uniqueSorted8([
36078
36164
  ...isProviderList(surface.fallback) ? surface.fallback : [],
36079
36165
  ...isProviderList(surface.allowProviders) ? surface.allowProviders : []
36080
36166
  ]);
36081
- var surfaceStatus = (issues) => issues.reduce((status, issue) => statusRank6[issue.status] > statusRank6[status] ? issue.status : status, "pass");
36167
+ var surfaceStatus = (issues) => issues.reduce((status, issue) => statusRank7[issue.status] > statusRank7[status] ? issue.status : status, "pass");
36082
36168
  var resolvedRequirement = (surface, options) => ({
36083
36169
  ...defaultRequirement,
36084
36170
  ...options.defaultRequirement ?? {},
@@ -37222,12 +37308,12 @@ var recommendVoiceProviderStack = (input) => {
37222
37308
  };
37223
37309
  };
37224
37310
  var rollupContractStatus = (checks) => checks.some((check) => check.status === "fail") ? "fail" : checks.some((check) => check.status === "warn") ? "warn" : "pass";
37225
- var statusRank7 = {
37311
+ var statusRank8 = {
37226
37312
  pass: 0,
37227
37313
  warn: 1,
37228
37314
  fail: 2
37229
37315
  };
37230
- var statusExceeds2 = (actual, max2) => statusRank7[actual] > statusRank7[max2];
37316
+ var statusExceeds2 = (actual, max2) => statusRank8[actual] > statusRank8[max2];
37231
37317
  var buildVoiceProviderContractMatrix = (input) => {
37232
37318
  const rows = input.contracts.map((contract) => {
37233
37319
  const configured = contract.configured !== false;
@@ -41402,6 +41488,7 @@ export {
41402
41488
  renderVoiceLatencySLOMarkdown,
41403
41489
  renderVoiceIncidentTimelineMarkdown,
41404
41490
  renderVoiceIncidentTimelineHTML,
41491
+ renderVoiceIncidentRecoveryOutcomeHTML,
41405
41492
  renderVoiceHandoffHealthHTML,
41406
41493
  renderVoiceGuardrailMarkdown,
41407
41494
  renderVoiceFailureReplayMarkdown,
@@ -41903,6 +41990,7 @@ export {
41903
41990
  buildVoiceLiveOpsControlState,
41904
41991
  buildVoiceLatencySLOGate,
41905
41992
  buildVoiceIncidentTimelineReport,
41993
+ buildVoiceIncidentRecoveryOutcomeReport,
41906
41994
  buildVoiceIncidentBundle,
41907
41995
  buildVoiceIOProviderRouterTraceEvent,
41908
41996
  buildVoiceGuardrailReport,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.435",
3
+ "version": "0.0.22-beta.436",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",