@absolutejs/voice 0.0.22-beta.435 → 0.0.22-beta.437
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/incidentBundle.d.ts +3 -0
- package/dist/incidentTimeline.d.ts +31 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +167 -6
- package/dist/observabilityExport.d.ts +6 -2
- package/package.json +1 -1
package/dist/incidentBundle.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Elysia } from 'elysia';
|
|
2
2
|
import { type VoiceOperationsRecord, type VoiceOperationsRecordOptions } from './operationsRecord';
|
|
3
|
+
import type { VoiceIncidentRecoveryOutcomeReport } from './incidentTimeline';
|
|
3
4
|
import { type VoiceTraceRedactionConfig } from './trace';
|
|
4
5
|
export type VoiceIncidentBundleFormat = 'json' | 'markdown';
|
|
5
6
|
export type VoiceIncidentBundle = {
|
|
@@ -8,6 +9,7 @@ export type VoiceIncidentBundle = {
|
|
|
8
9
|
formatVersion: 1;
|
|
9
10
|
markdown: string;
|
|
10
11
|
record: VoiceOperationsRecord;
|
|
12
|
+
recoveryOutcomes?: VoiceIncidentRecoveryOutcomeReport;
|
|
11
13
|
redacted: boolean;
|
|
12
14
|
sessionId: string;
|
|
13
15
|
summary: VoiceIncidentBundleSummary;
|
|
@@ -46,6 +48,7 @@ export type VoiceIncidentBundleSummary = {
|
|
|
46
48
|
};
|
|
47
49
|
export type VoiceIncidentBundleOptions = VoiceOperationsRecordOptions & {
|
|
48
50
|
redact?: VoiceTraceRedactionConfig;
|
|
51
|
+
recoveryOutcomes?: VoiceIncidentRecoveryOutcomeReport;
|
|
49
52
|
title?: string;
|
|
50
53
|
};
|
|
51
54
|
export type VoiceIncidentBundleRoutesOptions = Omit<VoiceIncidentBundleOptions, 'sessionId'> & {
|
|
@@ -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
|
@@ -26759,6 +26759,41 @@ var createCallDebuggerArtifact = (report, href) => ({
|
|
|
26759
26759
|
sessionId: report.sessionId,
|
|
26760
26760
|
status: "pass"
|
|
26761
26761
|
});
|
|
26762
|
+
var createIncidentBundleArtifact = (bundle) => ({
|
|
26763
|
+
generatedAt: bundle.exportedAt,
|
|
26764
|
+
id: `incident-bundle:${bundle.sessionId}`,
|
|
26765
|
+
kind: "incident",
|
|
26766
|
+
label: `Incident bundle ${bundle.sessionId}`,
|
|
26767
|
+
metadata: {
|
|
26768
|
+
errors: bundle.summary.errors,
|
|
26769
|
+
recoveryOutcomes: bundle.recoveryOutcomes ? {
|
|
26770
|
+
failed: bundle.recoveryOutcomes.failed,
|
|
26771
|
+
improved: bundle.recoveryOutcomes.improved,
|
|
26772
|
+
regressed: bundle.recoveryOutcomes.regressed,
|
|
26773
|
+
total: bundle.recoveryOutcomes.total,
|
|
26774
|
+
unchanged: bundle.recoveryOutcomes.unchanged
|
|
26775
|
+
} : undefined,
|
|
26776
|
+
status: bundle.summary.status
|
|
26777
|
+
},
|
|
26778
|
+
required: true,
|
|
26779
|
+
sessionId: bundle.sessionId,
|
|
26780
|
+
status: bundle.summary.status === "failed" ? "fail" : bundle.summary.status === "warning" ? "warn" : "pass"
|
|
26781
|
+
});
|
|
26782
|
+
var createIncidentRecoveryOutcomeArtifact = (report) => ({
|
|
26783
|
+
generatedAt: report.checkedAt,
|
|
26784
|
+
id: `incident-recovery-outcomes:${report.checkedAt}`,
|
|
26785
|
+
kind: "incident-recovery-outcomes",
|
|
26786
|
+
label: "Incident recovery outcomes",
|
|
26787
|
+
metadata: {
|
|
26788
|
+
failed: report.failed,
|
|
26789
|
+
improved: report.improved,
|
|
26790
|
+
regressed: report.regressed,
|
|
26791
|
+
total: report.total,
|
|
26792
|
+
unchanged: report.unchanged
|
|
26793
|
+
},
|
|
26794
|
+
required: true,
|
|
26795
|
+
status: report.failed > 0 || report.regressed > 0 ? "fail" : report.unchanged > 0 ? "warn" : "pass"
|
|
26796
|
+
});
|
|
26762
26797
|
var unique2 = (values) => [...new Set(values)].sort();
|
|
26763
26798
|
var stripArtifactPathAnchor = (path) => path.split("#")[0] ?? path;
|
|
26764
26799
|
var toEpochMs = (value) => {
|
|
@@ -27419,6 +27454,7 @@ var collectSessionIds = (input) => unique2([
|
|
|
27419
27454
|
...input.events.map((event) => event.sessionId),
|
|
27420
27455
|
...input.auditEvents.map((event) => event.sessionId).filter((sessionId) => Boolean(sessionId)),
|
|
27421
27456
|
...input.operationsRecords.map((record) => record.sessionId),
|
|
27457
|
+
...input.incidentBundles.map((bundle) => bundle.sessionId),
|
|
27422
27458
|
...input.sessionSnapshots.map((snapshot) => snapshot.sessionId),
|
|
27423
27459
|
...input.callDebuggerReports.map((report) => report.sessionId)
|
|
27424
27460
|
]);
|
|
@@ -27443,6 +27479,15 @@ var collectIssues = (input) => {
|
|
|
27443
27479
|
});
|
|
27444
27480
|
}
|
|
27445
27481
|
for (const artifact of input.artifacts) {
|
|
27482
|
+
if (artifact.required && (artifact.status === "fail" || artifact.status === "warn")) {
|
|
27483
|
+
issues.push({
|
|
27484
|
+
code: "voice.observability.artifact_failed",
|
|
27485
|
+
detail: `${artifact.label} reported ${artifact.status}.`,
|
|
27486
|
+
label: "Artifact status",
|
|
27487
|
+
severity: artifact.status,
|
|
27488
|
+
value: artifact.id
|
|
27489
|
+
});
|
|
27490
|
+
}
|
|
27446
27491
|
if (artifact.path && artifact.status !== "pass" && artifact.required && artifact.bytes === undefined && artifact.freshness?.ageMs === undefined) {
|
|
27447
27492
|
issues.push({
|
|
27448
27493
|
code: "voice.observability.artifact_missing",
|
|
@@ -27541,14 +27586,22 @@ var buildVoiceObservabilityExport = async (options = {}) => {
|
|
|
27541
27586
|
const events = await time("events", async () => options.events ?? await options.store?.list() ?? []);
|
|
27542
27587
|
const auditEvents = await time("auditEvents", async () => options.audit ? await options.audit.list() : []);
|
|
27543
27588
|
const baseOperationsRecords = options.operationsRecords ?? [];
|
|
27544
|
-
const [
|
|
27589
|
+
const [
|
|
27590
|
+
sessionSnapshots,
|
|
27591
|
+
callDebuggerReports,
|
|
27592
|
+
incidentBundles,
|
|
27593
|
+
incidentRecoveryOutcomeReports
|
|
27594
|
+
] = await time("supportArtifacts", () => Promise.all([
|
|
27545
27595
|
resolveObservabilityExportList(options.sessionSnapshots),
|
|
27546
|
-
resolveObservabilityExportList(options.callDebuggerReports)
|
|
27596
|
+
resolveObservabilityExportList(options.callDebuggerReports),
|
|
27597
|
+
resolveObservabilityExportList(options.incidentBundles),
|
|
27598
|
+
resolveObservabilityExportList(options.incidentRecoveryOutcomeReports)
|
|
27547
27599
|
]));
|
|
27548
27600
|
const sessionIds = await time("sessionIds", () => collectSessionIds({
|
|
27549
27601
|
auditEvents,
|
|
27550
27602
|
callDebuggerReports,
|
|
27551
27603
|
events,
|
|
27604
|
+
incidentBundles,
|
|
27552
27605
|
operationsRecords: baseOperationsRecords,
|
|
27553
27606
|
sessionIds: options.sessionIds,
|
|
27554
27607
|
sessionSnapshots
|
|
@@ -27575,10 +27628,14 @@ var buildVoiceObservabilityExport = async (options = {}) => {
|
|
|
27575
27628
|
const operationArtifacts = await time("operationArtifacts", () => operationsRecords.map((record) => createOperationArtifact(record, options.links?.operationsRecord?.(record.sessionId))));
|
|
27576
27629
|
const sessionSnapshotArtifacts = await time("sessionSnapshotArtifacts", () => sessionSnapshots.map((snapshot) => createSessionSnapshotArtifact(snapshot, options.links?.sessionSnapshot?.(snapshot.sessionId))));
|
|
27577
27630
|
const callDebuggerArtifacts = await time("callDebuggerArtifacts", () => callDebuggerReports.map((report) => createCallDebuggerArtifact(report, options.links?.callDebugger?.(report.sessionId))));
|
|
27631
|
+
const incidentBundleArtifacts = await time("incidentBundleArtifacts", () => incidentBundles.map(createIncidentBundleArtifact));
|
|
27632
|
+
const incidentRecoveryOutcomeArtifacts = await time("incidentRecoveryOutcomeArtifacts", () => incidentRecoveryOutcomeReports.map(createIncidentRecoveryOutcomeArtifact));
|
|
27578
27633
|
const artifacts = await time("artifacts", async () => addArtifactDownloadHrefs(await verifyArtifacts([
|
|
27579
27634
|
...operationArtifacts,
|
|
27580
27635
|
...sessionSnapshotArtifacts,
|
|
27581
27636
|
...callDebuggerArtifacts,
|
|
27637
|
+
...incidentBundleArtifacts,
|
|
27638
|
+
...incidentRecoveryOutcomeArtifacts,
|
|
27582
27639
|
...options.artifacts ?? []
|
|
27583
27640
|
], options.artifactIntegrity), options.links));
|
|
27584
27641
|
const operationHrefBySessionId = new Map(sessionIds.map((sessionId) => [
|
|
@@ -30183,6 +30240,62 @@ var defaultIncidentRecoveryActions = (events, links) => {
|
|
|
30183
30240
|
return actions;
|
|
30184
30241
|
};
|
|
30185
30242
|
var worstStatus3 = (statuses) => statuses.includes("fail") ? "fail" : statuses.includes("warn") ? "warn" : "pass";
|
|
30243
|
+
var statusRank6 = (status) => status === "fail" ? 3 : status === "warn" ? 2 : status === "pass" ? 1 : 0;
|
|
30244
|
+
var isRecord4 = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
30245
|
+
var getIncidentRecoveryBody = (event) => {
|
|
30246
|
+
const payload = isRecord4(event.payload) ? event.payload : {};
|
|
30247
|
+
return isRecord4(payload.body) ? payload.body : {};
|
|
30248
|
+
};
|
|
30249
|
+
var getIncidentRecoveryStatus = (value) => value === "fail" || value === "pass" || value === "warn" ? value : undefined;
|
|
30250
|
+
var getIncidentRecoveryDetail = (event) => {
|
|
30251
|
+
const payload = isRecord4(event.payload) ? event.payload : {};
|
|
30252
|
+
const body = getIncidentRecoveryBody(event);
|
|
30253
|
+
const result = isRecord4(body.result) ? body.result : {};
|
|
30254
|
+
const detail = result.detail ?? payload.error;
|
|
30255
|
+
return typeof detail === "string" ? detail : undefined;
|
|
30256
|
+
};
|
|
30257
|
+
var toIncidentRecoveryOutcomeEntry = (event) => {
|
|
30258
|
+
const body = getIncidentRecoveryBody(event);
|
|
30259
|
+
const beforeStatus = getIncidentRecoveryStatus(body.beforeStatus);
|
|
30260
|
+
const afterStatus = getIncidentRecoveryStatus(body.afterStatus);
|
|
30261
|
+
const beforeRank = statusRank6(beforeStatus);
|
|
30262
|
+
const afterRank = statusRank6(afterStatus);
|
|
30263
|
+
const outcome = event.outcome === "error" ? "failed" : beforeRank > 0 && afterRank > 0 && afterRank < beforeRank ? "improved" : beforeRank > 0 && afterRank > beforeRank ? "regressed" : "unchanged";
|
|
30264
|
+
const payload = isRecord4(event.payload) ? event.payload : {};
|
|
30265
|
+
return {
|
|
30266
|
+
actionId: event.action.replace(/^incident\./, ""),
|
|
30267
|
+
afterStatus,
|
|
30268
|
+
at: event.at,
|
|
30269
|
+
beforeStatus,
|
|
30270
|
+
detail: getIncidentRecoveryDetail(event),
|
|
30271
|
+
eventId: event.id,
|
|
30272
|
+
outcome,
|
|
30273
|
+
status: typeof payload.status === "number" ? payload.status : undefined,
|
|
30274
|
+
traceId: event.traceId
|
|
30275
|
+
};
|
|
30276
|
+
};
|
|
30277
|
+
var buildVoiceIncidentRecoveryOutcomeReport = async (options) => {
|
|
30278
|
+
const events = options.audit ? await options.audit.list({
|
|
30279
|
+
limit: options.limit ?? 50,
|
|
30280
|
+
resourceType: "voice.ops.action",
|
|
30281
|
+
type: "operator.action"
|
|
30282
|
+
}) : [];
|
|
30283
|
+
const entries = events.filter((event) => event.action.startsWith("incident.")).map(toIncidentRecoveryOutcomeEntry).sort((left, right) => right.at - left.at);
|
|
30284
|
+
return {
|
|
30285
|
+
checkedAt: Date.now(),
|
|
30286
|
+
entries,
|
|
30287
|
+
failed: entries.filter((entry) => entry.outcome === "failed").length,
|
|
30288
|
+
improved: entries.filter((entry) => entry.outcome === "improved").length,
|
|
30289
|
+
regressed: entries.filter((entry) => entry.outcome === "regressed").length,
|
|
30290
|
+
total: entries.length,
|
|
30291
|
+
unchanged: entries.filter((entry) => entry.outcome === "unchanged").length
|
|
30292
|
+
};
|
|
30293
|
+
};
|
|
30294
|
+
var renderVoiceIncidentRecoveryOutcomeHTML = (report, options = {}) => {
|
|
30295
|
+
const title = options.title ?? "AbsoluteJS Voice Incident Recovery Outcomes";
|
|
30296
|
+
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("");
|
|
30297
|
+
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>`;
|
|
30298
|
+
};
|
|
30186
30299
|
var pushOperationalStatusEvents = (events, report, links) => {
|
|
30187
30300
|
if (!report) {
|
|
30188
30301
|
return;
|
|
@@ -30429,6 +30542,8 @@ var createVoiceIncidentTimelineRoutes = (options) => {
|
|
|
30429
30542
|
const htmlPath = options.htmlPath === undefined ? "/voice/incident-timeline" : options.htmlPath;
|
|
30430
30543
|
const markdownPath = options.markdownPath === undefined ? "/voice/incident-timeline.md" : options.markdownPath;
|
|
30431
30544
|
const actionPath = options.actionPath === undefined ? "/api/voice/incident-timeline/actions" : options.actionPath;
|
|
30545
|
+
const recoveryOutcomePath = options.recoveryOutcomePath === undefined ? "/api/voice/incident-timeline/recovery-outcomes" : options.recoveryOutcomePath;
|
|
30546
|
+
const recoveryOutcomeHtmlPath = options.recoveryOutcomeHtmlPath === undefined ? "/voice/incident-recovery-outcomes" : options.recoveryOutcomeHtmlPath;
|
|
30432
30547
|
const routes = new Elysia46({
|
|
30433
30548
|
name: options.name ?? "absolutejs-voice-incident-timeline"
|
|
30434
30549
|
}).get(path, async () => {
|
|
@@ -30552,6 +30667,34 @@ var createVoiceIncidentTimelineRoutes = (options) => {
|
|
|
30552
30667
|
});
|
|
30553
30668
|
});
|
|
30554
30669
|
}
|
|
30670
|
+
if (recoveryOutcomePath !== false) {
|
|
30671
|
+
routes.get(recoveryOutcomePath, async () => {
|
|
30672
|
+
const report = await buildVoiceIncidentRecoveryOutcomeReport({
|
|
30673
|
+
audit: options.audit
|
|
30674
|
+
});
|
|
30675
|
+
return new Response(JSON.stringify(report), {
|
|
30676
|
+
headers: {
|
|
30677
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
30678
|
+
...options.headers
|
|
30679
|
+
}
|
|
30680
|
+
});
|
|
30681
|
+
});
|
|
30682
|
+
}
|
|
30683
|
+
if (recoveryOutcomeHtmlPath !== false) {
|
|
30684
|
+
routes.get(recoveryOutcomeHtmlPath, async () => {
|
|
30685
|
+
const report = await buildVoiceIncidentRecoveryOutcomeReport({
|
|
30686
|
+
audit: options.audit
|
|
30687
|
+
});
|
|
30688
|
+
return new Response(renderVoiceIncidentRecoveryOutcomeHTML(report, {
|
|
30689
|
+
title: `${options.title ?? "AbsoluteJS Voice Incident Timeline"} Recovery Outcomes`
|
|
30690
|
+
}), {
|
|
30691
|
+
headers: {
|
|
30692
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
30693
|
+
...options.headers
|
|
30694
|
+
}
|
|
30695
|
+
});
|
|
30696
|
+
});
|
|
30697
|
+
}
|
|
30555
30698
|
return routes;
|
|
30556
30699
|
};
|
|
30557
30700
|
// src/dataControl.ts
|
|
@@ -36062,7 +36205,7 @@ var defaultRequirement = {
|
|
|
36062
36205
|
requireFallback: false,
|
|
36063
36206
|
requireTimeoutBudget: false
|
|
36064
36207
|
};
|
|
36065
|
-
var
|
|
36208
|
+
var statusRank7 = {
|
|
36066
36209
|
pass: 0,
|
|
36067
36210
|
warn: 1,
|
|
36068
36211
|
fail: 2
|
|
@@ -36078,7 +36221,7 @@ var surfaceProviderNames = (surface) => uniqueSorted8([
|
|
|
36078
36221
|
...isProviderList(surface.fallback) ? surface.fallback : [],
|
|
36079
36222
|
...isProviderList(surface.allowProviders) ? surface.allowProviders : []
|
|
36080
36223
|
]);
|
|
36081
|
-
var surfaceStatus = (issues) => issues.reduce((status, issue) =>
|
|
36224
|
+
var surfaceStatus = (issues) => issues.reduce((status, issue) => statusRank7[issue.status] > statusRank7[status] ? issue.status : status, "pass");
|
|
36082
36225
|
var resolvedRequirement = (surface, options) => ({
|
|
36083
36226
|
...defaultRequirement,
|
|
36084
36227
|
...options.defaultRequirement ?? {},
|
|
@@ -37222,12 +37365,12 @@ var recommendVoiceProviderStack = (input) => {
|
|
|
37222
37365
|
};
|
|
37223
37366
|
};
|
|
37224
37367
|
var rollupContractStatus = (checks) => checks.some((check) => check.status === "fail") ? "fail" : checks.some((check) => check.status === "warn") ? "warn" : "pass";
|
|
37225
|
-
var
|
|
37368
|
+
var statusRank8 = {
|
|
37226
37369
|
pass: 0,
|
|
37227
37370
|
warn: 1,
|
|
37228
37371
|
fail: 2
|
|
37229
37372
|
};
|
|
37230
|
-
var statusExceeds2 = (actual, max2) =>
|
|
37373
|
+
var statusExceeds2 = (actual, max2) => statusRank8[actual] > statusRank8[max2];
|
|
37231
37374
|
var buildVoiceProviderContractMatrix = (input) => {
|
|
37232
37375
|
const rows = input.contracts.map((contract) => {
|
|
37233
37376
|
const configured = contract.configured !== false;
|
|
@@ -37817,6 +37960,7 @@ var buildSummary = (record) => ({
|
|
|
37817
37960
|
turns: record.summary.turnCount
|
|
37818
37961
|
});
|
|
37819
37962
|
var renderIncidentMarkdown = (input) => {
|
|
37963
|
+
const recoveryOutcomes = input.recoveryOutcomes;
|
|
37820
37964
|
const lines = [
|
|
37821
37965
|
`# ${input.title ?? `Voice Incident ${input.summary.sessionId}`}`,
|
|
37822
37966
|
"",
|
|
@@ -37848,6 +37992,18 @@ var renderIncidentMarkdown = (input) => {
|
|
|
37848
37992
|
"",
|
|
37849
37993
|
...input.record.tools.length ? input.record.tools.map((tool) => `- ${tool.toolName ?? "tool"} ${tool.status ?? ""} ${tool.elapsedMs === undefined ? "" : `${tool.elapsedMs}ms`} ${tool.error ?? ""}`.trim()) : ["- none"],
|
|
37850
37994
|
"",
|
|
37995
|
+
"## Recovery Outcomes",
|
|
37996
|
+
"",
|
|
37997
|
+
...recoveryOutcomes ? [
|
|
37998
|
+
`- Improved: ${recoveryOutcomes.improved}`,
|
|
37999
|
+
`- Unchanged: ${recoveryOutcomes.unchanged}`,
|
|
38000
|
+
`- Regressed: ${recoveryOutcomes.regressed}`,
|
|
38001
|
+
`- Failed: ${recoveryOutcomes.failed}`,
|
|
38002
|
+
`- Total actions: ${recoveryOutcomes.total}`,
|
|
38003
|
+
"",
|
|
38004
|
+
...recoveryOutcomes.entries.length ? recoveryOutcomes.entries.map((entry) => `- ${entry.outcome}: ${entry.actionId} ${entry.beforeStatus ?? "unknown"} -> ${entry.afterStatus ?? "unknown"}${entry.detail ? ` - ${entry.detail}` : ""}`) : ["- no recovery actions recorded"]
|
|
38005
|
+
] : ["- no recovery outcome report attached"],
|
|
38006
|
+
"",
|
|
37851
38007
|
renderVoiceOperationsRecordGuardrailMarkdown(input.record),
|
|
37852
38008
|
"",
|
|
37853
38009
|
"## Trace Evidence",
|
|
@@ -37897,6 +38053,7 @@ var buildVoiceIncidentBundle = async (options) => {
|
|
|
37897
38053
|
traceEvents: redactedTraceEvents
|
|
37898
38054
|
});
|
|
37899
38055
|
const summary = buildSummary(redactedRecord);
|
|
38056
|
+
const recoveryOutcomes = options.recoveryOutcomes ? redactRecordValue(options.recoveryOutcomes, redactedTraceEvents, options.redact) : undefined;
|
|
37900
38057
|
const traceMarkdown = renderVoiceTraceMarkdown(record.traceEvents, {
|
|
37901
38058
|
evaluation: options.evaluation,
|
|
37902
38059
|
redact: options.redact,
|
|
@@ -37909,6 +38066,7 @@ var buildVoiceIncidentBundle = async (options) => {
|
|
|
37909
38066
|
const markdown = renderIncidentMarkdown({
|
|
37910
38067
|
auditMarkdown,
|
|
37911
38068
|
record: redactedRecord,
|
|
38069
|
+
recoveryOutcomes,
|
|
37912
38070
|
summary,
|
|
37913
38071
|
title: options.title,
|
|
37914
38072
|
traceMarkdown
|
|
@@ -37919,6 +38077,7 @@ var buildVoiceIncidentBundle = async (options) => {
|
|
|
37919
38077
|
formatVersion: 1,
|
|
37920
38078
|
markdown,
|
|
37921
38079
|
record: redactedRecord,
|
|
38080
|
+
recoveryOutcomes,
|
|
37922
38081
|
redacted: Boolean(options.redact),
|
|
37923
38082
|
sessionId: options.sessionId,
|
|
37924
38083
|
summary,
|
|
@@ -41402,6 +41561,7 @@ export {
|
|
|
41402
41561
|
renderVoiceLatencySLOMarkdown,
|
|
41403
41562
|
renderVoiceIncidentTimelineMarkdown,
|
|
41404
41563
|
renderVoiceIncidentTimelineHTML,
|
|
41564
|
+
renderVoiceIncidentRecoveryOutcomeHTML,
|
|
41405
41565
|
renderVoiceHandoffHealthHTML,
|
|
41406
41566
|
renderVoiceGuardrailMarkdown,
|
|
41407
41567
|
renderVoiceFailureReplayMarkdown,
|
|
@@ -41903,6 +42063,7 @@ export {
|
|
|
41903
42063
|
buildVoiceLiveOpsControlState,
|
|
41904
42064
|
buildVoiceLatencySLOGate,
|
|
41905
42065
|
buildVoiceIncidentTimelineReport,
|
|
42066
|
+
buildVoiceIncidentRecoveryOutcomeReport,
|
|
41906
42067
|
buildVoiceIncidentBundle,
|
|
41907
42068
|
buildVoiceIOProviderRouterTraceEvent,
|
|
41908
42069
|
buildVoiceGuardrailReport,
|
|
@@ -4,6 +4,8 @@ import { Database } from 'bun:sqlite';
|
|
|
4
4
|
import { type VoiceAuditSinkDeliveryQueueSummary, type VoiceAuditSinkDeliveryRecord, type VoiceAuditSinkDeliveryStore } from './auditSinks';
|
|
5
5
|
import type { VoiceAuditEventStore, VoiceAuditEventType } from './audit';
|
|
6
6
|
import type { VoiceCallDebuggerReport } from './callDebugger';
|
|
7
|
+
import type { VoiceIncidentBundle } from './incidentBundle';
|
|
8
|
+
import type { VoiceIncidentRecoveryOutcomeReport } from './incidentTimeline';
|
|
7
9
|
import { type VoiceOperationsRecord } from './operationsRecord';
|
|
8
10
|
import type { VoiceSessionSnapshot } from './sessionSnapshot';
|
|
9
11
|
import { type VoiceTraceSinkDeliveryQueueSummary } from './queue';
|
|
@@ -43,7 +45,7 @@ export type VoiceObservabilityExportRecordValidationOptions = {
|
|
|
43
45
|
};
|
|
44
46
|
export declare const validateVoiceObservabilityExportRecord: (input: unknown, options?: VoiceObservabilityExportRecordValidationOptions) => VoiceObservabilityExportValidationResult;
|
|
45
47
|
export declare const assertVoiceObservabilityExportRecord: (input: unknown, options?: VoiceObservabilityExportRecordValidationOptions) => VoiceObservabilityExportValidationResult;
|
|
46
|
-
export type VoiceObservabilityExportArtifactKind = 'call-debugger' | 'incident' | 'markdown' | 'operations-record' | 'proof-pack' | 'readiness' | 'screenshot' | 'session-snapshot' | 'slo' | 'trace' | 'audit' | 'custom';
|
|
48
|
+
export type VoiceObservabilityExportArtifactKind = 'call-debugger' | 'incident' | 'incident-recovery-outcomes' | 'markdown' | 'operations-record' | 'proof-pack' | 'readiness' | 'screenshot' | 'session-snapshot' | 'slo' | 'trace' | 'audit' | 'custom';
|
|
47
49
|
export type VoiceObservabilityExportArtifactChecksum = {
|
|
48
50
|
algorithm: 'sha256';
|
|
49
51
|
value: string;
|
|
@@ -86,7 +88,7 @@ export type VoiceObservabilityExportEnvelope = {
|
|
|
86
88
|
severity: VoiceObservabilityExportStatus;
|
|
87
89
|
traceId?: string;
|
|
88
90
|
};
|
|
89
|
-
export type VoiceObservabilityExportIssueCode = 'voice.observability.no_evidence' | 'voice.observability.operation_failed' | 'voice.observability.artifact_missing' | 'voice.observability.artifact_stale' | 'voice.observability.audit_delivery_failed' | 'voice.observability.audit_delivery_pending' | 'voice.observability.trace_delivery_failed' | 'voice.observability.trace_delivery_pending';
|
|
91
|
+
export type VoiceObservabilityExportIssueCode = 'voice.observability.no_evidence' | 'voice.observability.operation_failed' | 'voice.observability.artifact_failed' | 'voice.observability.artifact_missing' | 'voice.observability.artifact_stale' | 'voice.observability.audit_delivery_failed' | 'voice.observability.audit_delivery_pending' | 'voice.observability.trace_delivery_failed' | 'voice.observability.trace_delivery_pending';
|
|
90
92
|
export type VoiceObservabilityExportIssue = {
|
|
91
93
|
code: VoiceObservabilityExportIssueCode;
|
|
92
94
|
detail?: string;
|
|
@@ -389,6 +391,8 @@ export type VoiceObservabilityExportOptions = {
|
|
|
389
391
|
sessionSnapshot?: (sessionId: string) => string;
|
|
390
392
|
};
|
|
391
393
|
callDebuggerReports?: VoiceCallDebuggerReport[] | (() => VoiceCallDebuggerReport[] | Promise<VoiceCallDebuggerReport[]>);
|
|
394
|
+
incidentBundles?: VoiceIncidentBundle[] | (() => VoiceIncidentBundle[] | Promise<VoiceIncidentBundle[]>);
|
|
395
|
+
incidentRecoveryOutcomeReports?: VoiceIncidentRecoveryOutcomeReport[] | (() => VoiceIncidentRecoveryOutcomeReport[] | Promise<VoiceIncidentRecoveryOutcomeReport[]>);
|
|
392
396
|
operationsRecords?: VoiceOperationsRecord[];
|
|
393
397
|
onTiming?: (timing: VoiceObservabilityExportTiming) => void;
|
|
394
398
|
redact?: VoiceTraceRedactionConfig;
|