@absolutejs/voice 0.0.22-beta.231 → 0.0.22-beta.233

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
@@ -2758,14 +2758,40 @@ Every export manifest and artifact index includes a stable schema contract:
2758
2758
  ```ts
2759
2759
  import {
2760
2760
  assertVoiceObservabilityExportSchema,
2761
+ validateVoiceObservabilityExportRecord,
2761
2762
  voiceObservabilityExportSchemaId,
2762
2763
  voiceObservabilityExportSchemaVersion
2763
2764
  } from '@absolutejs/voice';
2764
2765
 
2765
2766
  assertVoiceObservabilityExportSchema(exportReport);
2767
+ const validation = validateVoiceObservabilityExportRecord(exportReport);
2768
+ if (!validation.ok) {
2769
+ throw new Error(validation.issues.map((issue) => issue.message).join('\n'));
2770
+ }
2766
2771
  console.log(voiceObservabilityExportSchemaId, voiceObservabilityExportSchemaVersion);
2767
2772
  ```
2768
2773
 
2774
+ Use `validateVoiceObservabilityExportRecord(...)` or `assertVoiceObservabilityExportRecord(...)` when reading customer-owned records back from SQLite, Postgres, S3, a webhook collector, a warehouse, or a SIEM. The validator accepts manifests, artifact indexes, delivery reports, delivery receipts, delivery histories, and database payload records, then checks the stable schema id/version plus the minimum shape required for safe ingestion.
2775
+
2776
+ Use `replayVoiceObservabilityExport(...)` when you need to prove an already-delivered evidence bundle is still usable:
2777
+
2778
+ ```ts
2779
+ import { replayVoiceObservabilityExport } from '@absolutejs/voice';
2780
+
2781
+ const replay = await replayVoiceObservabilityExport({
2782
+ kind: 'sqlite',
2783
+ path: '.voice-runtime/observability-exports.sqlite',
2784
+ runId: '2026-04-29T17-20-51.032Z',
2785
+ tableName: 'voice_observability_exports'
2786
+ });
2787
+
2788
+ if (replay.status !== 'pass') {
2789
+ console.error(replay.issues);
2790
+ }
2791
+ ```
2792
+
2793
+ Replay sources support supplied records plus file, S3, SQLite, and Postgres delivery targets. The replay report re-validates the manifest/index/database payload, counts artifacts and delivery destinations, flags failed artifacts or destinations, and gives a readiness-style `pass`, `warn`, or `fail` result for customer-owned evidence pipelines.
2794
+
2769
2795
  ```ts
2770
2796
  import {
2771
2797
  buildVoiceObservabilityExport,
package/dist/index.d.ts CHANGED
@@ -53,7 +53,7 @@ export { createVoiceReadinessProfile, recommendVoiceReadinessProfile } from './r
53
53
  export { buildVoiceProviderContractMatrix, createVoiceProviderContractMatrixHTMLHandler, createVoiceProviderContractMatrixJSONHandler, createVoiceProviderContractMatrixPreset, createVoiceProviderContractMatrixRoutes, evaluateVoiceProviderStackGaps, renderVoiceProviderContractMatrixHTML, recommendVoiceProviderStack } from './providerStackRecommendations';
54
54
  export { buildVoiceOpsConsoleReport, createVoiceOpsConsoleRoutes, renderVoiceOpsConsoleHTML } from './opsConsoleRoutes';
55
55
  export { buildVoiceOperationsRecord, createVoiceOperationsRecordRoutes, renderVoiceOperationsRecordHTML, renderVoiceOperationsRecordIncidentMarkdown } from './operationsRecord';
56
- export { buildVoiceObservabilityArtifactIndex, buildVoiceObservabilityExportDeliveryHistory, buildVoiceObservabilityExport, assertVoiceObservabilityExportSchema, createVoiceObservabilityExportSchema, createVoiceFileObservabilityExportDeliveryReceiptStore, createVoiceMemoryObservabilityExportDeliveryReceiptStore, createVoiceObservabilityExportRoutes, deliverVoiceObservabilityExport, renderVoiceObservabilityExportMarkdown, voiceObservabilityExportSchemaId, voiceObservabilityExportSchemaVersion } from './observabilityExport';
56
+ export { assertVoiceObservabilityExportRecord, buildVoiceObservabilityArtifactIndex, buildVoiceObservabilityExportDeliveryHistory, buildVoiceObservabilityExportReplayReport, buildVoiceObservabilityExport, assertVoiceObservabilityExportSchema, createVoiceObservabilityExportSchema, createVoiceFileObservabilityExportDeliveryReceiptStore, createVoiceMemoryObservabilityExportDeliveryReceiptStore, createVoiceObservabilityExportRoutes, deliverVoiceObservabilityExport, loadVoiceObservabilityExportReplaySource, replayVoiceObservabilityExport, renderVoiceObservabilityExportMarkdown, validateVoiceObservabilityExportRecord, voiceObservabilityExportSchemaId, voiceObservabilityExportSchemaVersion } from './observabilityExport';
57
57
  export { buildVoiceOpsRecoveryReadinessCheck, buildVoiceOpsRecoveryReport, createVoiceOpsRecoveryRoutes, renderVoiceOpsRecoveryHTML, renderVoiceOpsRecoveryMarkdown } from './opsRecovery';
58
58
  export { buildVoiceIncidentBundle, createStoredVoiceIncidentBundleArtifact, createVoiceIncidentBundleRoutes, createVoiceMemoryIncidentBundleStore, pruneVoiceIncidentBundleArtifacts, saveVoiceIncidentBundleArtifact } from './incidentBundle';
59
59
  export { summarizeVoiceOpsStatus } from './opsStatus';
@@ -118,7 +118,7 @@ export type { VoiceProductionReadinessAction, VoiceProductionReadinessAuditOptio
118
118
  export type { VoiceReadinessProfileName, VoiceReadinessProfileOptions, VoiceReadinessProfileRecommendation, VoiceReadinessProfileRecommendationScore, VoiceReadinessProfileRoutesOptions } from './readinessProfiles';
119
119
  export type { VoiceProviderStackChoice, VoiceProviderStackCapabilities, VoiceProviderStackCapabilityGap, VoiceProviderStackCapabilityGapInput, VoiceProviderStackCapabilityGapReport, VoiceProviderContractCheck, VoiceProviderContractCheckStatus, VoiceProviderContractDefinition, VoiceProviderContractMatrixHandlerOptions, VoiceProviderContractMatrixHTMLHandlerOptions, VoiceProviderContractMatrixInput, VoiceProviderContractMatrixPresetOptions, VoiceProviderContractMatrixReport, VoiceProviderContractMatrixRoutesOptions, VoiceProviderContractMatrixRow, VoiceProviderStackInput, VoiceProviderStackKind, VoiceProviderStackRecommendation } from './providerStackRecommendations';
120
120
  export type { VoiceOperationsRecord, VoiceOperationsRecordAgentHandoff, VoiceOperationsRecordAuditSummary, VoiceOperationsRecordIntegrationEventSummary, VoiceOperationsRecordOptions, VoiceOperationsRecordOutcome, VoiceOperationsRecordProviderDecision, VoiceOperationsRecordReviewSummary, VoiceOperationsRecordRoutesOptions, VoiceOperationsRecordStatus, VoiceOperationsRecordTaskSummary, VoiceOperationsRecordTranscriptTurn, VoiceOperationsRecordTool } from './operationsRecord';
121
- export type { VoiceObservabilityExportArtifact, VoiceObservabilityExportArtifactChecksum, VoiceObservabilityExportArtifactFreshness, VoiceObservabilityExportArtifactIndex, VoiceObservabilityExportArtifactIndexItem, VoiceObservabilityExportArtifactKind, VoiceObservabilityExportDeliverySummary, VoiceObservabilityExportDeliveryDestination, VoiceObservabilityExportDeliveryDestinationResult, VoiceObservabilityExportDeliveryHistory, VoiceObservabilityExportDeliveryOptions, VoiceObservabilityExportDeliveryReceipt, VoiceObservabilityExportDeliveryReceiptStore, VoiceObservabilityExportDeliveryReport, VoiceObservabilityExportEnvelope, VoiceObservabilityExportIssue, VoiceObservabilityExportIssueCode, VoiceObservabilityExportOptions, VoiceObservabilityExportRedactionSummary, VoiceObservabilityExportReport, VoiceObservabilityExportRoutesOptions, VoiceObservabilityExportSchema, VoiceObservabilityExportStatus } from './observabilityExport';
121
+ export type { VoiceObservabilityExportArtifact, VoiceObservabilityExportArtifactChecksum, VoiceObservabilityExportArtifactFreshness, VoiceObservabilityExportArtifactIndex, VoiceObservabilityExportArtifactIndexItem, VoiceObservabilityExportArtifactKind, VoiceObservabilityExportDeliverySummary, VoiceObservabilityExportDeliveryDestination, VoiceObservabilityExportDeliveryDestinationResult, VoiceObservabilityExportDeliveryHistory, VoiceObservabilityExportDeliveryOptions, VoiceObservabilityExportDeliveryReceipt, VoiceObservabilityExportDeliveryReceiptStore, VoiceObservabilityExportDeliveryReport, VoiceObservabilityExportEnvelope, VoiceObservabilityExportIssue, VoiceObservabilityExportIssueCode, VoiceObservabilityExportOptions, VoiceObservabilityExportIngestedRecordKind, VoiceObservabilityExportRedactionSummary, VoiceObservabilityExportRecordValidationOptions, VoiceObservabilityExportReplayIssue, VoiceObservabilityExportReplayIssueCode, VoiceObservabilityExportReplayRecords, VoiceObservabilityExportReplayReport, VoiceObservabilityExportReplaySource, VoiceObservabilityExportReport, VoiceObservabilityExportRoutesOptions, VoiceObservabilityExportSchema, VoiceObservabilityExportStatus, VoiceObservabilityExportValidationIssue, VoiceObservabilityExportValidationResult } from './observabilityExport';
122
122
  export type { VoiceOpsRecoveryFailedSession, VoiceOpsRecoveryInterventionSummary, VoiceOpsRecoveryIssue, VoiceOpsRecoveryIssueCode, VoiceOpsRecoveryLinks, VoiceOpsRecoveryProviderSummary, VoiceOpsRecoveryReport, VoiceOpsRecoveryReportOptions, VoiceOpsRecoveryRoutesOptions, VoiceOpsRecoveryStatus } from './opsRecovery';
123
123
  export type { StoredVoiceIncidentBundleArtifact, VoiceIncidentBundle, VoiceIncidentBundleArtifactOptions, VoiceIncidentBundleFormat, VoiceIncidentBundleOptions, VoiceIncidentBundleRetentionOptions, VoiceIncidentBundleRetentionReport, VoiceIncidentBundleRoutesOptions, VoiceIncidentBundleStore, VoiceIncidentBundleStoreFilter, VoiceIncidentBundleSummary } from './incidentBundle';
124
124
  export type { VoiceQualityLink, VoiceQualityMetric, VoiceQualityReport, VoiceQualityRoutesOptions, VoiceQualityStatus, VoiceQualityThresholds } from './qualityRoutes';
package/dist/index.js CHANGED
@@ -22998,6 +22998,265 @@ var assertVoiceObservabilityExportSchema = (input) => {
22998
22998
  throw new Error(`Unsupported voice observability export schema: ${input.schema?.id ?? "missing"}@${input.schema?.version ?? "missing"}`);
22999
22999
  }
23000
23000
  };
23001
+ var isRecord2 = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
23002
+ var isStatus2 = (value) => value === "fail" || value === "pass" || value === "warn";
23003
+ var getRecord = (value, key) => isRecord2(value) && isRecord2(value[key]) ? value[key] : undefined;
23004
+ var getRecordArray = (value, key) => isRecord2(value) && Array.isArray(value[key]) ? value[key] : undefined;
23005
+ var inferVoiceObservabilityExportRecordKind = (record) => {
23006
+ if (isRecord2(record.manifest) && isRecord2(record.artifactIndex)) {
23007
+ return "database-record";
23008
+ }
23009
+ if (Array.isArray(record.receipts)) {
23010
+ return "delivery-history";
23011
+ }
23012
+ if (typeof record.runId === "string" && Array.isArray(record.destinations)) {
23013
+ return "delivery-receipt";
23014
+ }
23015
+ if (Array.isArray(record.destinations) && isRecord2(record.summary) && typeof record.exportStatus === "string") {
23016
+ return "delivery-report";
23017
+ }
23018
+ if (Array.isArray(record.artifacts) && isRecord2(record.summary)) {
23019
+ return Array.isArray(record.envelopes) ? "manifest" : "artifact-index";
23020
+ }
23021
+ return;
23022
+ };
23023
+ var pushValidationIssue = (issues, issue) => {
23024
+ issues.push(issue);
23025
+ };
23026
+ var requireRecordSchema = (issues, record, path) => {
23027
+ const schema = getRecord(record, "schema");
23028
+ if (schema?.id !== voiceObservabilityExportSchemaId || schema?.version !== voiceObservabilityExportSchemaVersion) {
23029
+ pushValidationIssue(issues, {
23030
+ code: "voice.observability.export.unsupported_schema",
23031
+ message: `Unsupported voice observability export schema: ${schema?.id ?? "missing"}@${schema?.version ?? "missing"}`,
23032
+ path: `${path}.schema`
23033
+ });
23034
+ }
23035
+ return schema;
23036
+ };
23037
+ var requireArrayField = (issues, record, key, path) => {
23038
+ if (!Array.isArray(record[key])) {
23039
+ pushValidationIssue(issues, {
23040
+ code: "voice.observability.export.missing_field",
23041
+ message: `${path}.${key} must be an array.`,
23042
+ path: `${path}.${key}`
23043
+ });
23044
+ }
23045
+ };
23046
+ var requireNumberField = (issues, record, key, path) => {
23047
+ if (typeof record[key] !== "number") {
23048
+ pushValidationIssue(issues, {
23049
+ code: "voice.observability.export.missing_field",
23050
+ message: `${path}.${key} must be a number.`,
23051
+ path: `${path}.${key}`
23052
+ });
23053
+ }
23054
+ };
23055
+ var requireStatusField = (issues, record, key, path) => {
23056
+ if (!isStatus2(record[key])) {
23057
+ pushValidationIssue(issues, {
23058
+ code: "voice.observability.export.missing_field",
23059
+ message: `${path}.${key} must be pass, warn, or fail.`,
23060
+ path: `${path}.${key}`
23061
+ });
23062
+ }
23063
+ };
23064
+ var requireDeliveryDestinationStatusField = (issues, record, key, path) => {
23065
+ if (record[key] !== "delivered" && record[key] !== "failed") {
23066
+ pushValidationIssue(issues, {
23067
+ code: "voice.observability.export.missing_field",
23068
+ message: `${path}.${key} must be delivered or failed.`,
23069
+ path: `${path}.${key}`
23070
+ });
23071
+ }
23072
+ };
23073
+ var validateDeliveryDestinations = (issues, destinations, path) => {
23074
+ if (!destinations) {
23075
+ pushValidationIssue(issues, {
23076
+ code: "voice.observability.export.missing_field",
23077
+ message: `${path} must be an array.`,
23078
+ path
23079
+ });
23080
+ return;
23081
+ }
23082
+ destinations.forEach((destination, index) => {
23083
+ const destinationPath = `${path}.${index}`;
23084
+ if (!isRecord2(destination)) {
23085
+ pushValidationIssue(issues, {
23086
+ code: "voice.observability.export.invalid_shape",
23087
+ message: `${destinationPath} must be an object.`,
23088
+ path: destinationPath
23089
+ });
23090
+ return;
23091
+ }
23092
+ requireRecordSchema(issues, destination, destinationPath);
23093
+ requireDeliveryDestinationStatusField(issues, destination, "status", destinationPath);
23094
+ if (typeof destination.destinationKind !== "string") {
23095
+ pushValidationIssue(issues, {
23096
+ code: "voice.observability.export.missing_field",
23097
+ message: `${destinationPath}.destinationKind must be a string.`,
23098
+ path: `${destinationPath}.destinationKind`
23099
+ });
23100
+ }
23101
+ });
23102
+ };
23103
+ var validateVoiceObservabilityExportRecord = (input, options = {}) => {
23104
+ const issues = [];
23105
+ if (!isRecord2(input)) {
23106
+ return {
23107
+ issues: [
23108
+ {
23109
+ code: "voice.observability.export.invalid_shape",
23110
+ message: "Voice observability export record must be an object.",
23111
+ path: "$"
23112
+ }
23113
+ ],
23114
+ ok: false
23115
+ };
23116
+ }
23117
+ const kind = options.kind ?? inferVoiceObservabilityExportRecordKind(input);
23118
+ if (!kind) {
23119
+ return {
23120
+ issues: [
23121
+ {
23122
+ code: "voice.observability.export.invalid_shape",
23123
+ message: "Voice observability export record kind could not be inferred.",
23124
+ path: "$"
23125
+ }
23126
+ ],
23127
+ ok: false
23128
+ };
23129
+ }
23130
+ let schema;
23131
+ if (kind === "manifest") {
23132
+ schema = requireRecordSchema(issues, input, "$");
23133
+ requireArrayField(issues, input, "artifacts", "$");
23134
+ requireArrayField(issues, input, "envelopes", "$");
23135
+ requireArrayField(issues, input, "issues", "$");
23136
+ requireArrayField(issues, input, "operationsRecords", "$");
23137
+ requireArrayField(issues, input, "sessionIds", "$");
23138
+ requireNumberField(issues, input, "checkedAt", "$");
23139
+ requireStatusField(issues, input, "status", "$");
23140
+ if (!isRecord2(input.deliveries)) {
23141
+ pushValidationIssue(issues, {
23142
+ code: "voice.observability.export.missing_field",
23143
+ message: "$.deliveries must be an object.",
23144
+ path: "$.deliveries"
23145
+ });
23146
+ }
23147
+ if (!isRecord2(input.redaction)) {
23148
+ pushValidationIssue(issues, {
23149
+ code: "voice.observability.export.missing_field",
23150
+ message: "$.redaction must be an object.",
23151
+ path: "$.redaction"
23152
+ });
23153
+ }
23154
+ if (!isRecord2(input.summary)) {
23155
+ pushValidationIssue(issues, {
23156
+ code: "voice.observability.export.missing_field",
23157
+ message: "$.summary must be an object.",
23158
+ path: "$.summary"
23159
+ });
23160
+ }
23161
+ } else if (kind === "artifact-index") {
23162
+ schema = requireRecordSchema(issues, input, "$");
23163
+ requireArrayField(issues, input, "artifacts", "$");
23164
+ requireNumberField(issues, input, "checkedAt", "$");
23165
+ requireStatusField(issues, input, "status", "$");
23166
+ if (!isRecord2(input.summary)) {
23167
+ pushValidationIssue(issues, {
23168
+ code: "voice.observability.export.missing_field",
23169
+ message: "$.summary must be an object.",
23170
+ path: "$.summary"
23171
+ });
23172
+ }
23173
+ } else if (kind === "database-record") {
23174
+ schema = requireRecordSchema(issues, input, "$");
23175
+ requireNumberField(issues, input, "checkedAt", "$");
23176
+ requireStatusField(issues, input, "status", "$");
23177
+ requireStatusField(issues, input, "exportStatus", "$");
23178
+ if (!isRecord2(input.manifest)) {
23179
+ pushValidationIssue(issues, {
23180
+ code: "voice.observability.export.missing_field",
23181
+ message: "$.manifest must be an object.",
23182
+ path: "$.manifest"
23183
+ });
23184
+ } else {
23185
+ issues.push(...validateVoiceObservabilityExportRecord(input.manifest, {
23186
+ kind: "manifest"
23187
+ }).issues.map((issue) => ({
23188
+ ...issue,
23189
+ path: `$.manifest${issue.path.slice(1)}`
23190
+ })));
23191
+ }
23192
+ if (!isRecord2(input.artifactIndex)) {
23193
+ pushValidationIssue(issues, {
23194
+ code: "voice.observability.export.missing_field",
23195
+ message: "$.artifactIndex must be an object.",
23196
+ path: "$.artifactIndex"
23197
+ });
23198
+ } else {
23199
+ issues.push(...validateVoiceObservabilityExportRecord(input.artifactIndex, {
23200
+ kind: "artifact-index"
23201
+ }).issues.map((issue) => ({
23202
+ ...issue,
23203
+ path: `$.artifactIndex${issue.path.slice(1)}`
23204
+ })));
23205
+ }
23206
+ } else if (kind === "delivery-report") {
23207
+ requireNumberField(issues, input, "checkedAt", "$");
23208
+ requireStatusField(issues, input, "status", "$");
23209
+ requireStatusField(issues, input, "exportStatus", "$");
23210
+ validateDeliveryDestinations(issues, getRecordArray(input, "destinations"), "$.destinations");
23211
+ } else if (kind === "delivery-receipt") {
23212
+ requireNumberField(issues, input, "checkedAt", "$");
23213
+ requireStatusField(issues, input, "status", "$");
23214
+ requireStatusField(issues, input, "exportStatus", "$");
23215
+ if (typeof input.runId !== "string") {
23216
+ pushValidationIssue(issues, {
23217
+ code: "voice.observability.export.missing_field",
23218
+ message: "$.runId must be a string.",
23219
+ path: "$.runId"
23220
+ });
23221
+ }
23222
+ validateDeliveryDestinations(issues, getRecordArray(input, "destinations"), "$.destinations");
23223
+ } else if (kind === "delivery-history") {
23224
+ requireNumberField(issues, input, "checkedAt", "$");
23225
+ requireStatusField(issues, input, "status", "$");
23226
+ const receipts = getRecordArray(input, "receipts");
23227
+ if (!receipts) {
23228
+ pushValidationIssue(issues, {
23229
+ code: "voice.observability.export.missing_field",
23230
+ message: "$.receipts must be an array.",
23231
+ path: "$.receipts"
23232
+ });
23233
+ } else {
23234
+ receipts.forEach((receipt, index) => {
23235
+ const result = validateVoiceObservabilityExportRecord(receipt, {
23236
+ kind: "delivery-receipt"
23237
+ });
23238
+ issues.push(...result.issues.map((issue) => ({
23239
+ ...issue,
23240
+ path: `$.receipts.${index}${issue.path.slice(1)}`
23241
+ })));
23242
+ });
23243
+ }
23244
+ }
23245
+ return {
23246
+ issues,
23247
+ kind,
23248
+ ok: issues.length === 0,
23249
+ schema
23250
+ };
23251
+ };
23252
+ var assertVoiceObservabilityExportRecord = (input, options) => {
23253
+ const result = validateVoiceObservabilityExportRecord(input, options);
23254
+ if (!result.ok) {
23255
+ const firstIssue = result.issues[0];
23256
+ throw new Error(`Invalid voice observability export record: ${firstIssue?.path ?? "$"} ${firstIssue?.message ?? "unknown validation failure"}`);
23257
+ }
23258
+ return result;
23259
+ };
23001
23260
  var isDeliveryStore = (value) => !Array.isArray(value) && typeof value.list === "function";
23002
23261
  var getString17 = (value) => typeof value === "string" ? value : undefined;
23003
23262
  var getProviderKind = (payload) => getString17(payload.kind) ?? getString17(payload.providerKind);
@@ -23050,6 +23309,10 @@ var writeS3Object = async (input) => {
23050
23309
  type: input.contentType
23051
23310
  });
23052
23311
  };
23312
+ var readS3ObjectText = async (input) => {
23313
+ const file = input.client.file(input.key, input.options);
23314
+ return await file.text();
23315
+ };
23053
23316
  var quoteObservabilityIdentifier = (value) => `"${value.replace(/"/g, '""')}"`;
23054
23317
  var normalizeObservabilityIdentifier = (value) => value?.trim().replace(/[^a-zA-Z0-9_]+/g, "_").replace(/^_+|_+$/g, "") || "voice_observability_exports";
23055
23318
  var buildObservabilityExportDatabaseRecord = (input) => ({
@@ -23062,6 +23325,190 @@ var buildObservabilityExportDatabaseRecord = (input) => ({
23062
23325
  schema: input.report.schema,
23063
23326
  status: input.report.status
23064
23327
  });
23328
+ var parseObservabilityExportJson = (value) => typeof value === "string" ? JSON.parse(value) : value;
23329
+ var collectReplayDeliveryDestinations = (value) => {
23330
+ if (!isRecord2(value)) {
23331
+ return [];
23332
+ }
23333
+ if (Array.isArray(value.destinations)) {
23334
+ return value.destinations.filter((destination) => isRecord2(destination));
23335
+ }
23336
+ if (Array.isArray(value.receipts)) {
23337
+ return value.receipts.flatMap((receipt) => collectReplayDeliveryDestinations(receipt));
23338
+ }
23339
+ return [];
23340
+ };
23341
+ var replayIssueSeverity = (status) => status === "fail" ? "fail" : "warn";
23342
+ var buildVoiceObservabilityExportReplayReport = (records) => {
23343
+ const manifest = records.manifest ?? (isRecord2(records.databaseRecord) ? records.databaseRecord.manifest : undefined);
23344
+ const artifactIndex = records.artifactIndex ?? (isRecord2(records.databaseRecord) ? records.databaseRecord.artifactIndex : undefined);
23345
+ const validations = {
23346
+ artifactIndex: validateVoiceObservabilityExportRecord(artifactIndex, {
23347
+ kind: "artifact-index"
23348
+ }),
23349
+ databaseRecord: records.databaseRecord ? validateVoiceObservabilityExportRecord(records.databaseRecord, {
23350
+ kind: "database-record"
23351
+ }) : undefined,
23352
+ deliveryHistory: records.deliveryHistory ? validateVoiceObservabilityExportRecord(records.deliveryHistory, {
23353
+ kind: "delivery-history"
23354
+ }) : undefined,
23355
+ deliveryReceipt: records.deliveryReceipt ? validateVoiceObservabilityExportRecord(records.deliveryReceipt, {
23356
+ kind: "delivery-receipt"
23357
+ }) : undefined,
23358
+ deliveryReport: records.deliveryReport ? validateVoiceObservabilityExportRecord(records.deliveryReport, {
23359
+ kind: "delivery-report"
23360
+ }) : undefined,
23361
+ manifest: validateVoiceObservabilityExportRecord(manifest, {
23362
+ kind: "manifest"
23363
+ })
23364
+ };
23365
+ const validationIssues = Object.entries(validations).flatMap(([kind, result]) => result?.issues.map((issue) => ({
23366
+ kind,
23367
+ issue
23368
+ })) ?? []);
23369
+ const manifestRecord = isRecord2(manifest) ? manifest : undefined;
23370
+ const artifactIndexRecord = isRecord2(artifactIndex) ? artifactIndex : undefined;
23371
+ const artifacts = [
23372
+ ...Array.isArray(manifestRecord?.artifacts) ? manifestRecord.artifacts : [],
23373
+ ...Array.isArray(artifactIndexRecord?.artifacts) ? artifactIndexRecord.artifacts : []
23374
+ ].filter((artifact) => isRecord2(artifact));
23375
+ const failedArtifacts = artifacts.filter((artifact) => artifact.status === "fail");
23376
+ const deliveryDestinations = [
23377
+ ...collectReplayDeliveryDestinations(records.deliveryReport),
23378
+ ...collectReplayDeliveryDestinations(records.deliveryReceipt),
23379
+ ...collectReplayDeliveryDestinations(records.deliveryHistory)
23380
+ ];
23381
+ const failedDeliveryDestinations = deliveryDestinations.filter((destination) => destination.status === "failed");
23382
+ const issues = [
23383
+ ...!records.manifest && !isRecord2(records.databaseRecord) ? [
23384
+ {
23385
+ code: "voice.observability.export_replay.missing_record",
23386
+ label: "Export manifest",
23387
+ severity: "fail",
23388
+ value: "manifest"
23389
+ }
23390
+ ] : [],
23391
+ ...!records.artifactIndex && !isRecord2(records.databaseRecord) ? [
23392
+ {
23393
+ code: "voice.observability.export_replay.missing_record",
23394
+ label: "Artifact index",
23395
+ severity: "fail",
23396
+ value: "artifact-index"
23397
+ }
23398
+ ] : [],
23399
+ ...validationIssues.map(({ kind, issue }) => ({
23400
+ code: "voice.observability.export_replay.validation_failed",
23401
+ detail: issue.message,
23402
+ label: `Invalid ${kind}`,
23403
+ severity: "fail",
23404
+ value: issue.path
23405
+ })),
23406
+ ...manifestRecord && (manifestRecord.status === "fail" || manifestRecord.status === "warn") ? [
23407
+ {
23408
+ code: "voice.observability.export_replay.export_failed",
23409
+ label: "Export manifest status",
23410
+ severity: replayIssueSeverity(manifestRecord.status),
23411
+ value: manifestRecord.status
23412
+ }
23413
+ ] : [],
23414
+ ...failedArtifacts.map((artifact) => ({
23415
+ code: "voice.observability.export_replay.artifact_failed",
23416
+ label: "Export artifact status",
23417
+ severity: "fail",
23418
+ value: artifact.id
23419
+ })),
23420
+ ...failedDeliveryDestinations.map((destination) => ({
23421
+ code: "voice.observability.export_replay.delivery_failed",
23422
+ label: "Export delivery destination",
23423
+ severity: "fail",
23424
+ value: destination.destinationId
23425
+ }))
23426
+ ];
23427
+ return {
23428
+ checkedAt: Date.now(),
23429
+ issues,
23430
+ records: validations,
23431
+ status: issues.some((issue) => issue.severity === "fail") ? "fail" : issues.some((issue) => issue.severity === "warn") ? "warn" : "pass",
23432
+ summary: {
23433
+ artifacts: new Set(artifacts.map((artifact) => artifact.id)).size,
23434
+ deliveryDestinations: deliveryDestinations.length,
23435
+ failedArtifacts: failedArtifacts.length,
23436
+ failedDeliveryDestinations: failedDeliveryDestinations.length,
23437
+ validationIssues: validationIssues.length
23438
+ }
23439
+ };
23440
+ };
23441
+ var loadVoiceObservabilityExportReplaySource = async (source) => {
23442
+ if (source.kind === "records") {
23443
+ return source;
23444
+ }
23445
+ if (source.kind === "file") {
23446
+ const root = join3(source.directory, source.runId);
23447
+ const receiptPath = source.receiptDirectory ? join3(source.receiptDirectory, `${encodeURIComponent(deliveryReceiptId(source.runId))}.json`) : undefined;
23448
+ const deliveryReceipt = receiptPath ? await Bun.file(receiptPath).text().then(JSON.parse).catch(() => {
23449
+ return;
23450
+ }) : undefined;
23451
+ return {
23452
+ artifactIndex: JSON.parse(await Bun.file(join3(root, "artifact-index.json")).text()),
23453
+ deliveryReceipt,
23454
+ manifest: JSON.parse(await Bun.file(join3(root, "manifest.json")).text())
23455
+ };
23456
+ }
23457
+ if (source.kind === "s3") {
23458
+ const client = source.client ?? new Bun.S3Client(source);
23459
+ const s3Options = source;
23460
+ const rootKey = joinS3Key(normalizeExportS3KeyPrefix(source.keyPrefix), source.runId);
23461
+ return {
23462
+ artifactIndex: JSON.parse(await readS3ObjectText({
23463
+ client,
23464
+ key: joinS3Key(rootKey, "artifact-index.json"),
23465
+ options: s3Options
23466
+ })),
23467
+ manifest: JSON.parse(await readS3ObjectText({
23468
+ client,
23469
+ key: joinS3Key(rootKey, "manifest.json"),
23470
+ options: s3Options
23471
+ }))
23472
+ };
23473
+ }
23474
+ if (source.kind === "sqlite") {
23475
+ if (!source.database && !source.path) {
23476
+ throw new Error("SQLite observability export replay requires source.database or source.path.");
23477
+ }
23478
+ const database = source.database ?? new Database(source.path, { create: false });
23479
+ const table2 = quoteObservabilityIdentifier(normalizeObservabilityIdentifier(source.tableName));
23480
+ const row2 = database.query(`SELECT manifest_json, artifact_index_json, payload_json FROM ${table2} WHERE run_id = $runId`).get({ $runId: source.runId });
23481
+ if (!row2) {
23482
+ throw new Error(`No observability export found for run ${source.runId}.`);
23483
+ }
23484
+ return {
23485
+ artifactIndex: parseObservabilityExportJson(row2.artifact_index_json),
23486
+ databaseRecord: parseObservabilityExportJson(row2.payload_json),
23487
+ manifest: parseObservabilityExportJson(row2.manifest_json)
23488
+ };
23489
+ }
23490
+ const sql = source.sql ?? (source.connectionString ? (() => {
23491
+ const client = new Bun.SQL(source.connectionString);
23492
+ return { unsafe: client.unsafe.bind(client) };
23493
+ })() : undefined);
23494
+ if (!sql) {
23495
+ throw new Error("Postgres observability export replay requires source.sql or source.connectionString.");
23496
+ }
23497
+ const schema = normalizeObservabilityIdentifier(source.schemaName ?? "public");
23498
+ const table = normalizeObservabilityIdentifier(source.tableName);
23499
+ const qualifiedTable = `${quoteObservabilityIdentifier(schema)}.${quoteObservabilityIdentifier(table)}`;
23500
+ const rows = await sql.unsafe(`SELECT manifest_json, artifact_index_json, payload FROM ${qualifiedTable} WHERE run_id = $1`, [source.runId]);
23501
+ const row = rows[0];
23502
+ if (!row) {
23503
+ throw new Error(`No observability export found for run ${source.runId}.`);
23504
+ }
23505
+ return {
23506
+ artifactIndex: parseObservabilityExportJson(row.artifact_index_json),
23507
+ databaseRecord: parseObservabilityExportJson(row.payload),
23508
+ manifest: parseObservabilityExportJson(row.manifest_json)
23509
+ };
23510
+ };
23511
+ var replayVoiceObservabilityExport = async (source) => buildVoiceObservabilityExportReplayReport(await loadVoiceObservabilityExportReplaySource(source));
23065
23512
  var deliverObservabilityExportToSQLite = async (input) => {
23066
23513
  if (!input.destination.database && !input.destination.path) {
23067
23514
  throw new Error("SQLite observability export delivery requires destination.database or destination.path.");
@@ -28463,6 +28910,7 @@ export {
28463
28910
  verifyVoicePlivoWebhookSignature,
28464
28911
  verifyVoiceOpsWebhookSignature,
28465
28912
  validateVoiceWorkflowRouteResult,
28913
+ validateVoiceObservabilityExportRecord,
28466
28914
  transcodeTwilioInboundPayloadToPCM16,
28467
28915
  transcodePCMToTwilioOutboundPayload,
28468
28916
  summarizeVoiceTurnQuality,
@@ -28531,6 +28979,7 @@ export {
28531
28979
  resolveTurnDetectionConfig,
28532
28980
  resolveAudioConditioningConfig,
28533
28981
  requeueVoiceOpsTask,
28982
+ replayVoiceObservabilityExport,
28534
28983
  reopenVoiceOpsTask,
28535
28984
  renderVoiceTurnQualityHTML,
28536
28985
  renderVoiceTurnLatencyHTML,
@@ -28605,6 +29054,7 @@ export {
28605
29054
  parseVoiceTelephonyWebhookEvent,
28606
29055
  matchesVoiceOpsTaskAssignmentRule,
28607
29056
  markVoiceOpsTaskSLABreached,
29057
+ loadVoiceObservabilityExportReplaySource,
28608
29058
  listVoiceRoutingEvents,
28609
29059
  listVoiceOpsTasks,
28610
29060
  isVoiceOpsTaskOverdue,
@@ -28903,6 +29353,7 @@ export {
28903
29353
  buildVoiceOpsConsoleReport,
28904
29354
  buildVoiceOpsActionHistoryReport,
28905
29355
  buildVoiceOperationsRecord,
29356
+ buildVoiceObservabilityExportReplayReport,
28906
29357
  buildVoiceObservabilityExportDeliveryHistory,
28907
29358
  buildVoiceObservabilityExport,
28908
29359
  buildVoiceObservabilityArtifactIndex,
@@ -28922,6 +29373,7 @@ export {
28922
29373
  assignVoiceOpsTask,
28923
29374
  assertVoiceProviderRoutingContract,
28924
29375
  assertVoiceObservabilityExportSchema,
29376
+ assertVoiceObservabilityExportRecord,
28925
29377
  assertVoiceLatencySLOGate,
28926
29378
  assertVoiceAgentSquadContract,
28927
29379
  applyVoiceTelephonyOutcome,
@@ -21,6 +21,26 @@ export declare const assertVoiceObservabilityExportSchema: (input: {
21
21
  version?: string;
22
22
  };
23
23
  }) => void;
24
+ export type VoiceObservabilityExportIngestedRecordKind = 'artifact-index' | 'database-record' | 'delivery-history' | 'delivery-receipt' | 'delivery-report' | 'manifest';
25
+ export type VoiceObservabilityExportValidationIssue = {
26
+ code: 'voice.observability.export.invalid_shape' | 'voice.observability.export.missing_field' | 'voice.observability.export.unsupported_schema';
27
+ message: string;
28
+ path: string;
29
+ };
30
+ export type VoiceObservabilityExportValidationResult = {
31
+ issues: VoiceObservabilityExportValidationIssue[];
32
+ kind?: VoiceObservabilityExportIngestedRecordKind;
33
+ ok: boolean;
34
+ schema?: {
35
+ id?: string;
36
+ version?: string;
37
+ };
38
+ };
39
+ export type VoiceObservabilityExportRecordValidationOptions = {
40
+ kind?: VoiceObservabilityExportIngestedRecordKind;
41
+ };
42
+ export declare const validateVoiceObservabilityExportRecord: (input: unknown, options?: VoiceObservabilityExportRecordValidationOptions) => VoiceObservabilityExportValidationResult;
43
+ export declare const assertVoiceObservabilityExportRecord: (input: unknown, options?: VoiceObservabilityExportRecordValidationOptions) => VoiceObservabilityExportValidationResult;
24
44
  export type VoiceObservabilityExportArtifactKind = 'incident' | 'markdown' | 'operations-record' | 'proof-pack' | 'readiness' | 'screenshot' | 'slo' | 'trace' | 'audit' | 'custom';
25
45
  export type VoiceObservabilityExportArtifactChecksum = {
26
46
  algorithm: 'sha256';
@@ -213,6 +233,74 @@ export type VoiceObservabilityExportDeliveryHistory = {
213
233
  totalDestinations: number;
214
234
  };
215
235
  };
236
+ export type VoiceObservabilityExportReplayIssueCode = 'voice.observability.export_replay.artifact_failed' | 'voice.observability.export_replay.delivery_failed' | 'voice.observability.export_replay.export_failed' | 'voice.observability.export_replay.missing_record' | 'voice.observability.export_replay.validation_failed';
237
+ export type VoiceObservabilityExportReplayIssue = {
238
+ code: VoiceObservabilityExportReplayIssueCode;
239
+ detail?: string;
240
+ label: string;
241
+ severity: Exclude<VoiceObservabilityExportStatus, 'pass'>;
242
+ value?: number | string;
243
+ };
244
+ export type VoiceObservabilityExportReplayRecords = {
245
+ artifactIndex?: unknown;
246
+ databaseRecord?: unknown;
247
+ deliveryHistory?: unknown;
248
+ deliveryReceipt?: unknown;
249
+ deliveryReport?: unknown;
250
+ manifest?: unknown;
251
+ };
252
+ export type VoiceObservabilityExportReplayReport = {
253
+ checkedAt: number;
254
+ issues: VoiceObservabilityExportReplayIssue[];
255
+ records: {
256
+ artifactIndex: VoiceObservabilityExportValidationResult;
257
+ databaseRecord?: VoiceObservabilityExportValidationResult;
258
+ deliveryHistory?: VoiceObservabilityExportValidationResult;
259
+ deliveryReceipt?: VoiceObservabilityExportValidationResult;
260
+ deliveryReport?: VoiceObservabilityExportValidationResult;
261
+ manifest: VoiceObservabilityExportValidationResult;
262
+ };
263
+ status: VoiceObservabilityExportStatus;
264
+ summary: {
265
+ artifacts: number;
266
+ deliveryDestinations: number;
267
+ failedArtifacts: number;
268
+ failedDeliveryDestinations: number;
269
+ validationIssues: number;
270
+ };
271
+ };
272
+ export type VoiceObservabilityExportReplaySource = {
273
+ artifactIndex?: unknown;
274
+ databaseRecord?: unknown;
275
+ deliveryHistory?: unknown;
276
+ deliveryReceipt?: unknown;
277
+ deliveryReport?: unknown;
278
+ kind: 'records';
279
+ manifest?: unknown;
280
+ } | {
281
+ directory: string;
282
+ kind: 'file';
283
+ receiptDirectory?: string;
284
+ runId: string;
285
+ } | (S3Options & {
286
+ client?: Pick<S3Client, 'file'>;
287
+ keyPrefix?: string;
288
+ kind: 's3';
289
+ runId: string;
290
+ }) | {
291
+ database?: Database;
292
+ kind: 'sqlite';
293
+ path?: string;
294
+ runId: string;
295
+ tableName?: string;
296
+ } | {
297
+ connectionString?: string;
298
+ kind: 'postgres';
299
+ runId: string;
300
+ schemaName?: string;
301
+ sql?: VoicePostgresClient;
302
+ tableName?: string;
303
+ };
216
304
  export type VoiceObservabilityExportDeliveryOptions = {
217
305
  destinations: VoiceObservabilityExportDeliveryDestination[];
218
306
  report: VoiceObservabilityExportReport;
@@ -256,6 +344,9 @@ export type VoiceObservabilityExportRoutesOptions = VoiceObservabilityExportOpti
256
344
  render?: (report: VoiceObservabilityExportReport) => string | Promise<string>;
257
345
  title?: string;
258
346
  };
347
+ export declare const buildVoiceObservabilityExportReplayReport: (records: VoiceObservabilityExportReplayRecords) => VoiceObservabilityExportReplayReport;
348
+ export declare const loadVoiceObservabilityExportReplaySource: (source: VoiceObservabilityExportReplaySource) => Promise<VoiceObservabilityExportReplayRecords>;
349
+ export declare const replayVoiceObservabilityExport: (source: VoiceObservabilityExportReplaySource) => Promise<VoiceObservabilityExportReplayReport>;
259
350
  export declare const createVoiceMemoryObservabilityExportDeliveryReceiptStore: () => VoiceObservabilityExportDeliveryReceiptStore;
260
351
  export declare const createVoiceFileObservabilityExportDeliveryReceiptStore: (options: {
261
352
  directory: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.231",
3
+ "version": "0.0.22-beta.233",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",