@absolutejs/voice 0.0.22-beta.216 → 0.0.22-beta.218

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
@@ -2741,6 +2741,51 @@ app.use(
2741
2741
 
2742
2742
  Readiness emits the stable `voice.readiness.ops_recovery` gate code when unresolved recovery issues remain.
2743
2743
 
2744
+ ## Customer-Owned Observability Export
2745
+
2746
+ Use observability exports when a buyer wants the hosted-dashboard evidence graph, but inside their own storage, warehouse, SIEM, incident flow, or release notes. The export manifest links traces, audits, operations records, delivery queues, provider SLOs, readiness reports, screenshots, and proof-pack artifacts without making AbsoluteJS Voice the dashboard.
2747
+
2748
+ ```ts
2749
+ import {
2750
+ buildVoiceObservabilityExport,
2751
+ createVoiceObservabilityExportRoutes
2752
+ } from '@absolutejs/voice';
2753
+
2754
+ app.use(
2755
+ createVoiceObservabilityExportRoutes({
2756
+ artifacts: [
2757
+ {
2758
+ id: 'latest-proof-pack',
2759
+ kind: 'proof-pack',
2760
+ label: 'Latest proof pack',
2761
+ path: '.voice-runtime/proof-pack/latest.md'
2762
+ }
2763
+ ],
2764
+ audit: runtimeStorage.audit,
2765
+ auditDeliveries: runtimeStorage.auditDeliveries,
2766
+ links: {
2767
+ operationsRecord: (sessionId) => `/voice-operations/${sessionId}`
2768
+ },
2769
+ redact: true,
2770
+ store: runtimeStorage.traces,
2771
+ traceDeliveries: runtimeStorage.traceDeliveries
2772
+ })
2773
+ );
2774
+
2775
+ const exportReport = await buildVoiceObservabilityExport({
2776
+ audit: runtimeStorage.audit,
2777
+ auditDeliveries: runtimeStorage.auditDeliveries,
2778
+ links: {
2779
+ operationsRecord: (sessionId) => `/voice-operations/${sessionId}`
2780
+ },
2781
+ redact: true,
2782
+ store: runtimeStorage.traces,
2783
+ traceDeliveries: runtimeStorage.traceDeliveries
2784
+ });
2785
+ ```
2786
+
2787
+ The route helper exposes JSON at `/api/voice/observability-export`, Markdown at `/voice/observability-export.md`, and HTML at `/voice/observability-export`. Failed trace/audit deliveries fail the export report, pending deliveries warn, and every trace/audit envelope includes the linked operations-record URL when one is configured. This is the primitive to use when customers ask how voice evidence leaves the app without going through a hosted vendor dashboard.
2788
+
2744
2789
  ## Production Voice Ops
2745
2790
 
2746
2791
  The recommended production pattern is:
package/dist/index.d.ts CHANGED
@@ -53,6 +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 { buildVoiceObservabilityExport, createVoiceObservabilityExportRoutes, renderVoiceObservabilityExportMarkdown } from './observabilityExport';
56
57
  export { buildVoiceOpsRecoveryReadinessCheck, buildVoiceOpsRecoveryReport, createVoiceOpsRecoveryRoutes, renderVoiceOpsRecoveryHTML, renderVoiceOpsRecoveryMarkdown } from './opsRecovery';
57
58
  export { buildVoiceIncidentBundle, createStoredVoiceIncidentBundleArtifact, createVoiceIncidentBundleRoutes, createVoiceMemoryIncidentBundleStore, pruneVoiceIncidentBundleArtifacts, saveVoiceIncidentBundleArtifact } from './incidentBundle';
58
59
  export { summarizeVoiceOpsStatus } from './opsStatus';
@@ -117,6 +118,7 @@ export type { VoiceProductionReadinessAction, VoiceProductionReadinessAuditOptio
117
118
  export type { VoiceReadinessProfileName, VoiceReadinessProfileOptions, VoiceReadinessProfileRecommendation, VoiceReadinessProfileRecommendationScore, VoiceReadinessProfileRoutesOptions } from './readinessProfiles';
118
119
  export type { VoiceProviderStackChoice, VoiceProviderStackCapabilities, VoiceProviderStackCapabilityGap, VoiceProviderStackCapabilityGapInput, VoiceProviderStackCapabilityGapReport, VoiceProviderContractCheck, VoiceProviderContractCheckStatus, VoiceProviderContractDefinition, VoiceProviderContractMatrixHandlerOptions, VoiceProviderContractMatrixHTMLHandlerOptions, VoiceProviderContractMatrixInput, VoiceProviderContractMatrixPresetOptions, VoiceProviderContractMatrixReport, VoiceProviderContractMatrixRoutesOptions, VoiceProviderContractMatrixRow, VoiceProviderStackInput, VoiceProviderStackKind, VoiceProviderStackRecommendation } from './providerStackRecommendations';
119
120
  export type { VoiceOperationsRecord, VoiceOperationsRecordAgentHandoff, VoiceOperationsRecordAuditSummary, VoiceOperationsRecordIntegrationEventSummary, VoiceOperationsRecordOptions, VoiceOperationsRecordOutcome, VoiceOperationsRecordProviderDecision, VoiceOperationsRecordReviewSummary, VoiceOperationsRecordRoutesOptions, VoiceOperationsRecordStatus, VoiceOperationsRecordTaskSummary, VoiceOperationsRecordTranscriptTurn, VoiceOperationsRecordTool } from './operationsRecord';
121
+ export type { VoiceObservabilityExportArtifact, VoiceObservabilityExportArtifactKind, VoiceObservabilityExportDeliverySummary, VoiceObservabilityExportEnvelope, VoiceObservabilityExportIssue, VoiceObservabilityExportIssueCode, VoiceObservabilityExportOptions, VoiceObservabilityExportRedactionSummary, VoiceObservabilityExportReport, VoiceObservabilityExportRoutesOptions, VoiceObservabilityExportStatus } from './observabilityExport';
120
122
  export type { VoiceOpsRecoveryFailedSession, VoiceOpsRecoveryInterventionSummary, VoiceOpsRecoveryIssue, VoiceOpsRecoveryIssueCode, VoiceOpsRecoveryLinks, VoiceOpsRecoveryProviderSummary, VoiceOpsRecoveryReport, VoiceOpsRecoveryReportOptions, VoiceOpsRecoveryRoutesOptions, VoiceOpsRecoveryStatus } from './opsRecovery';
121
123
  export type { StoredVoiceIncidentBundleArtifact, VoiceIncidentBundle, VoiceIncidentBundleArtifactOptions, VoiceIncidentBundleFormat, VoiceIncidentBundleOptions, VoiceIncidentBundleRetentionOptions, VoiceIncidentBundleRetentionReport, VoiceIncidentBundleRoutesOptions, VoiceIncidentBundleStore, VoiceIncidentBundleStoreFilter, VoiceIncidentBundleSummary } from './incidentBundle';
122
124
  export type { VoiceQualityLink, VoiceQualityMetric, VoiceQualityReport, VoiceQualityRoutesOptions, VoiceQualityStatus, VoiceQualityThresholds } from './qualityRoutes';
package/dist/index.js CHANGED
@@ -21397,6 +21397,7 @@ var listVoiceRoutingEvents = (events) => {
21397
21397
  operation: getString13(event.payload.operation),
21398
21398
  provider,
21399
21399
  routing: getString13(event.payload.routing),
21400
+ scenarioId: event.scenarioId,
21400
21401
  selectedProvider: getString13(event.payload.selectedProvider),
21401
21402
  sessionId: event.sessionId,
21402
21403
  status: providerStatus,
@@ -22067,7 +22068,7 @@ var summarizeSessions = (events) => {
22067
22068
  var buildVoiceProviderSloReport = async (options = {}) => {
22068
22069
  const rawEvents = options.events ?? await options.store?.list() ?? [];
22069
22070
  const now = options.now ?? Date.now();
22070
- const events = normalizeEvents2(rawEvents).filter((event) => typeof options.maxAgeMs !== "number" || now - event.at <= options.maxAgeMs);
22071
+ const events = normalizeEvents2(rawEvents).filter((event) => (typeof options.maxAgeMs !== "number" || now - event.at <= options.maxAgeMs) && (!options.sessionId || event.sessionId === options.sessionId) && (!options.scenarioId || event.scenarioId === options.scenarioId));
22071
22072
  const thresholds = mergeThresholds(options.thresholds);
22072
22073
  const observedKinds = new Set(events.map((event) => event.kind));
22073
22074
  const requiredKinds = new Set(options.requiredKinds ?? [...observedKinds]);
@@ -24900,8 +24901,266 @@ var createVoiceOperationsRecordRoutes = (options) => {
24900
24901
  }
24901
24902
  return routes;
24902
24903
  };
24903
- // src/incidentBundle.ts
24904
+ // src/observabilityExport.ts
24904
24905
  import { Elysia as Elysia42 } from "elysia";
24906
+ var isDeliveryStore = (value) => !Array.isArray(value) && typeof value.list === "function";
24907
+ var getString18 = (value) => typeof value === "string" ? value : undefined;
24908
+ var getProviderKind = (payload) => getString18(payload.kind) ?? getString18(payload.providerKind);
24909
+ var toSeverityFromTrace = (event) => {
24910
+ if (event.type === "session.error" || event.payload.status === "error" || event.payload.providerStatus === "error") {
24911
+ return "fail";
24912
+ }
24913
+ if (event.payload.status === "fallback" || event.payload.providerStatus === "fallback") {
24914
+ return "warn";
24915
+ }
24916
+ return "pass";
24917
+ };
24918
+ var toSeverityFromAudit = (event) => event.outcome === "error" ? "fail" : event.outcome === "skipped" ? "warn" : "pass";
24919
+ var createOperationArtifact = (record, href) => ({
24920
+ generatedAt: record.checkedAt,
24921
+ href,
24922
+ id: `operations-record:${record.sessionId}`,
24923
+ kind: "operations-record",
24924
+ label: `Operations record ${record.sessionId}`,
24925
+ sessionId: record.sessionId,
24926
+ status: record.status === "failed" ? "fail" : record.status === "warning" ? "warn" : "pass"
24927
+ });
24928
+ var unique2 = (values) => [...new Set(values)].sort();
24929
+ var collectSessionIds = (input) => unique2([
24930
+ ...input.sessionIds ?? [],
24931
+ ...input.events.map((event) => event.sessionId),
24932
+ ...input.auditEvents.map((event) => event.sessionId).filter((sessionId) => Boolean(sessionId)),
24933
+ ...input.operationsRecords.map((record) => record.sessionId)
24934
+ ]);
24935
+ var collectIssues = (input) => {
24936
+ const issues = [];
24937
+ const failedOperationsRecords = input.operationsRecords.filter((record) => record.status === "failed").length;
24938
+ if (input.totalEvidence === 0) {
24939
+ issues.push({
24940
+ code: "voice.observability.no_evidence",
24941
+ detail: "No traces, audits, operations records, or artifacts were included in the export manifest.",
24942
+ label: "Observability evidence",
24943
+ severity: "warn",
24944
+ value: 0
24945
+ });
24946
+ }
24947
+ if (failedOperationsRecords > 0) {
24948
+ issues.push({
24949
+ code: "voice.observability.operation_failed",
24950
+ label: "Failed operations records",
24951
+ severity: "fail",
24952
+ value: failedOperationsRecords
24953
+ });
24954
+ }
24955
+ if ((input.auditDeliveries?.failed ?? 0) > 0) {
24956
+ issues.push({
24957
+ code: "voice.observability.audit_delivery_failed",
24958
+ label: "Failed audit exports",
24959
+ severity: "fail",
24960
+ value: input.auditDeliveries?.failed
24961
+ });
24962
+ }
24963
+ if ((input.auditDeliveries?.pending ?? 0) > 0) {
24964
+ issues.push({
24965
+ code: "voice.observability.audit_delivery_pending",
24966
+ label: "Pending audit exports",
24967
+ severity: "warn",
24968
+ value: input.auditDeliveries?.pending
24969
+ });
24970
+ }
24971
+ if ((input.traceDeliveries?.failed ?? 0) > 0) {
24972
+ issues.push({
24973
+ code: "voice.observability.trace_delivery_failed",
24974
+ label: "Failed trace exports",
24975
+ severity: "fail",
24976
+ value: input.traceDeliveries?.failed
24977
+ });
24978
+ }
24979
+ if ((input.traceDeliveries?.pending ?? 0) > 0) {
24980
+ issues.push({
24981
+ code: "voice.observability.trace_delivery_pending",
24982
+ label: "Pending trace exports",
24983
+ severity: "warn",
24984
+ value: input.traceDeliveries?.pending
24985
+ });
24986
+ }
24987
+ return issues;
24988
+ };
24989
+ var buildTraceEnvelope = (event, operationsRecordHref) => ({
24990
+ at: event.at,
24991
+ eventId: event.id,
24992
+ eventType: event.type,
24993
+ kind: "trace",
24994
+ operationsRecordHref,
24995
+ provider: getString18(event.payload.provider),
24996
+ providerKind: getProviderKind(event.payload),
24997
+ scenarioId: event.scenarioId,
24998
+ sessionId: event.sessionId,
24999
+ severity: toSeverityFromTrace(event),
25000
+ traceId: event.traceId
25001
+ });
25002
+ var buildAuditEnvelope = (event, operationsRecordHref) => ({
25003
+ at: event.at,
25004
+ eventId: event.id,
25005
+ eventType: event.type,
25006
+ kind: "audit",
25007
+ operationsRecordHref,
25008
+ provider: getString18(event.payload?.provider),
25009
+ providerKind: getProviderKind(event.payload ?? {}),
25010
+ sessionId: event.sessionId,
25011
+ severity: toSeverityFromAudit(event),
25012
+ traceId: event.traceId
25013
+ });
25014
+ var buildVoiceObservabilityExport = async (options = {}) => {
25015
+ const events = options.events ?? await options.store?.list() ?? [];
25016
+ const auditEvents = options.audit ? await options.audit.list() : [];
25017
+ const baseOperationsRecords = options.operationsRecords ?? [];
25018
+ const sessionIds = collectSessionIds({
25019
+ auditEvents,
25020
+ events,
25021
+ operationsRecords: baseOperationsRecords,
25022
+ sessionIds: options.sessionIds
25023
+ });
25024
+ const shouldBuildOperationsRecords = options.includeOperationsRecords !== false && options.store;
25025
+ const builtOperationsRecords = shouldBuildOperationsRecords ? await Promise.all(sessionIds.map((sessionId) => buildVoiceOperationsRecord({
25026
+ audit: options.audit,
25027
+ redact: options.redact,
25028
+ sessionId,
25029
+ store: options.store
25030
+ }))) : [];
25031
+ const operationsRecords = [
25032
+ ...baseOperationsRecords,
25033
+ ...builtOperationsRecords.filter((record) => !baseOperationsRecords.some((existing) => existing.sessionId === record.sessionId))
25034
+ ];
25035
+ const traceDeliveries = options.traceDeliveries ? isDeliveryStore(options.traceDeliveries) ? await options.traceDeliveries.list() : options.traceDeliveries : undefined;
25036
+ const auditDeliveries = options.auditDeliveries ? isDeliveryStore(options.auditDeliveries) ? await options.auditDeliveries.list() : options.auditDeliveries : undefined;
25037
+ const traceDeliverySummary = traceDeliveries ? await summarizeVoiceTraceSinkDeliveries(traceDeliveries) : undefined;
25038
+ const auditDeliverySummary = auditDeliveries ? await summarizeVoiceAuditSinkDeliveries(auditDeliveries) : undefined;
25039
+ const operationArtifacts = operationsRecords.map((record) => createOperationArtifact(record, options.links?.operationsRecord?.(record.sessionId)));
25040
+ const artifacts = [...operationArtifacts, ...options.artifacts ?? []];
25041
+ const operationHrefBySessionId = new Map(operationsRecords.map((record) => [
25042
+ record.sessionId,
25043
+ options.links?.operationsRecord?.(record.sessionId)
25044
+ ]));
25045
+ const envelopes = [
25046
+ ...events.map((event) => buildTraceEnvelope(event, operationHrefBySessionId.get(event.sessionId))),
25047
+ ...auditEvents.map((event) => buildAuditEnvelope(event, event.sessionId ? operationHrefBySessionId.get(event.sessionId) : undefined))
25048
+ ].sort((left, right) => left.at - right.at);
25049
+ const issues = collectIssues({
25050
+ auditDeliveries: auditDeliverySummary,
25051
+ operationsRecords,
25052
+ totalEvidence: events.length + auditEvents.length + operationsRecords.length + artifacts.length,
25053
+ traceDeliveries: traceDeliverySummary
25054
+ });
25055
+ const status = issues.some((issue) => issue.severity === "fail") ? "fail" : issues.some((issue) => issue.severity === "warn") ? "warn" : "pass";
25056
+ return {
25057
+ artifacts,
25058
+ checkedAt: Date.now(),
25059
+ deliveries: {
25060
+ audit: auditDeliverySummary,
25061
+ trace: traceDeliverySummary
25062
+ },
25063
+ envelopes,
25064
+ issues,
25065
+ operationsRecords,
25066
+ redaction: {
25067
+ enabled: Boolean(options.redact),
25068
+ mode: options.redact ? "redacted" : "none"
25069
+ },
25070
+ sessionIds,
25071
+ status,
25072
+ summary: {
25073
+ auditEvents: auditEvents.length,
25074
+ events: events.length + auditEvents.length,
25075
+ failedOperationsRecords: operationsRecords.filter((record) => record.status === "failed").length,
25076
+ trace: summarizeVoiceTrace(events),
25077
+ traceEvents: events.length
25078
+ }
25079
+ };
25080
+ };
25081
+ var renderVoiceObservabilityExportMarkdown = (report, options = {}) => {
25082
+ const title = options.title ?? "Voice Observability Export";
25083
+ const issues = report.issues.map((issue) => `- ${issue.severity}: ${issue.label}${issue.value !== undefined ? ` (${issue.value})` : ""}${issue.detail ? ` - ${issue.detail}` : ""}`).join(`
25084
+ `) || "No observability export issues.";
25085
+ const artifacts = report.artifacts.map((artifact) => `- ${artifact.label}: ${artifact.kind}${artifact.href ? ` (${artifact.href})` : ""}${artifact.status ? ` - ${artifact.status}` : ""}`).join(`
25086
+ `) || "No artifacts attached.";
25087
+ return `# ${title}
25088
+
25089
+ Generated: ${new Date(report.checkedAt).toISOString()}
25090
+
25091
+ Overall: **${report.status}**
25092
+
25093
+ Redaction: **${report.redaction.mode}**
25094
+
25095
+ Sessions: ${report.sessionIds.length}
25096
+
25097
+ Trace events: ${report.summary.traceEvents}
25098
+
25099
+ Audit events: ${report.summary.auditEvents}
25100
+
25101
+ Operations records: ${report.operationsRecords.length}
25102
+
25103
+ Artifacts: ${report.artifacts.length}
25104
+
25105
+ ## Delivery Summary
25106
+
25107
+ Trace deliveries: ${report.deliveries.trace ? `${report.deliveries.trace.delivered} delivered, ${report.deliveries.trace.pending} pending, ${report.deliveries.trace.failed} failed` : "not configured"}
25108
+
25109
+ Audit deliveries: ${report.deliveries.audit ? `${report.deliveries.audit.delivered} delivered, ${report.deliveries.audit.pending} pending, ${report.deliveries.audit.failed} failed` : "not configured"}
25110
+
25111
+ ## Artifacts
25112
+
25113
+ ${artifacts}
25114
+
25115
+ ## Issues
25116
+
25117
+ ${issues}
25118
+ `;
25119
+ };
25120
+ var createVoiceObservabilityExportRoutes = (options = {}) => {
25121
+ const path = options.path ?? "/api/voice/observability-export";
25122
+ const markdownPath = options.markdownPath ?? "/voice/observability-export.md";
25123
+ const htmlPath = options.htmlPath ?? "/voice/observability-export";
25124
+ const headers = {
25125
+ "cache-control": "no-store",
25126
+ ...options.headers ?? {}
25127
+ };
25128
+ const buildReport = () => buildVoiceObservabilityExport(options);
25129
+ const app = new Elysia42({
25130
+ name: options.name ?? "absolute-voice-observability-export"
25131
+ });
25132
+ app.get(path, async () => Response.json(await buildReport(), { headers }));
25133
+ if (markdownPath !== false) {
25134
+ app.get(markdownPath, async () => {
25135
+ const report = await buildReport();
25136
+ return new Response(renderVoiceObservabilityExportMarkdown(report, {
25137
+ title: options.title
25138
+ }), {
25139
+ headers: {
25140
+ ...headers,
25141
+ "content-type": "text/markdown; charset=utf-8"
25142
+ }
25143
+ });
25144
+ });
25145
+ }
25146
+ if (htmlPath !== false) {
25147
+ app.get(htmlPath, async () => {
25148
+ const report = await buildReport();
25149
+ const markdown = options.render ? await options.render(report) : renderVoiceObservabilityExportMarkdown(report, {
25150
+ title: options.title
25151
+ });
25152
+ return new Response(`<!doctype html><html><head><meta charset="utf-8" /><title>${options.title ?? "Voice Observability Export"}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:auto;max-width:920px;padding:32px;white-space:pre-wrap}</style></head><body>${markdown}</body></html>`, {
25153
+ headers: {
25154
+ ...headers,
25155
+ "content-type": "text/html; charset=utf-8"
25156
+ }
25157
+ });
25158
+ });
25159
+ }
25160
+ return app;
25161
+ };
25162
+ // src/incidentBundle.ts
25163
+ import { Elysia as Elysia43 } from "elysia";
24905
25164
  var filterIncidentBundleArtifacts = (artifacts, filter = {}) => artifacts.filter((artifact) => {
24906
25165
  if (filter.sessionId && artifact.sessionId !== filter.sessionId) {
24907
25166
  return false;
@@ -25100,7 +25359,7 @@ var buildVoiceIncidentBundle = async (options) => {
25100
25359
  var createVoiceIncidentBundleRoutes = (options) => {
25101
25360
  const path = options.path ?? "/api/voice-incidents/:sessionId";
25102
25361
  const markdownPath = options.markdownPath === undefined ? "/voice-incidents/:sessionId/markdown" : options.markdownPath;
25103
- const routes = new Elysia42({
25362
+ const routes = new Elysia43({
25104
25363
  name: options.name ?? "absolutejs-voice-incident-bundle"
25105
25364
  });
25106
25365
  const getSessionId = (params) => params.sessionId ?? "";
@@ -25301,7 +25560,7 @@ var summarizeVoiceOpsStatus = async (options) => {
25301
25560
  };
25302
25561
  };
25303
25562
  // src/opsStatusRoutes.ts
25304
- import { Elysia as Elysia43 } from "elysia";
25563
+ import { Elysia as Elysia44 } from "elysia";
25305
25564
  var escapeHtml43 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
25306
25565
  var renderVoiceOpsStatusHTML = (report, options = {}) => {
25307
25566
  const title = options.title ?? "AbsoluteJS Voice Ops Status";
@@ -25313,7 +25572,7 @@ var renderVoiceOpsStatusHTML = (report, options = {}) => {
25313
25572
  };
25314
25573
  var createVoiceOpsStatusRoutes = (options) => {
25315
25574
  const path = options.path ?? "/api/voice/ops-status";
25316
- const routes = new Elysia43({
25575
+ const routes = new Elysia44({
25317
25576
  name: options.name ?? "absolutejs-voice-ops-status"
25318
25577
  });
25319
25578
  routes.get(path, async () => summarizeVoiceOpsStatus(options));
@@ -25746,9 +26005,9 @@ var createVoiceTTSProviderRouter = (options) => {
25746
26005
  };
25747
26006
  };
25748
26007
  // src/traceDeliveryRoutes.ts
25749
- import { Elysia as Elysia44 } from "elysia";
26008
+ import { Elysia as Elysia45 } from "elysia";
25750
26009
  var escapeHtml44 = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll('"', "&quot;").replaceAll("'", "&#39;");
25751
- var getString18 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
26010
+ var getString19 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
25752
26011
  var getNumber11 = (value) => {
25753
26012
  if (typeof value === "number" && Number.isFinite(value)) {
25754
26013
  return value;
@@ -25760,13 +26019,13 @@ var getNumber11 = (value) => {
25760
26019
  return;
25761
26020
  };
25762
26021
  var parseStatus2 = (value) => {
25763
- const text = getString18(value);
26022
+ const text = getString19(value);
25764
26023
  return text === "pending" || text === "delivered" || text === "failed" || text === "skipped" || text === "all" ? text : undefined;
25765
26024
  };
25766
26025
  var resolveVoiceTraceDeliveryFilter = (query = {}, base = {}) => ({
25767
26026
  ...base,
25768
26027
  limit: getNumber11(query.limit) ?? base.limit,
25769
- q: getString18(query.q) ?? base.q,
26028
+ q: getString19(query.q) ?? base.q,
25770
26029
  status: parseStatus2(query.status) ?? base.status
25771
26030
  });
25772
26031
  var deliverySearchText2 = (delivery) => [
@@ -25855,7 +26114,7 @@ var createVoiceTraceDeliveryRoutes = (options) => {
25855
26114
  const path = options.path ?? "/api/voice-trace-deliveries";
25856
26115
  const htmlPath = options.htmlPath === undefined ? "/traces/deliveries" : options.htmlPath;
25857
26116
  const workerPath = options.workerPath === undefined ? `${path}/drain` : options.workerPath;
25858
- const routes = new Elysia44({
26117
+ const routes = new Elysia45({
25859
26118
  name: options.name ?? "absolutejs-voice-trace-deliveries"
25860
26119
  }).get(path, createVoiceTraceDeliveryJSONHandler(options));
25861
26120
  if (htmlPath !== false) {
@@ -26479,7 +26738,7 @@ var createVoiceMemoryStore = () => {
26479
26738
  return { get, getOrCreate, list, remove, set };
26480
26739
  };
26481
26740
  // src/opsWebhook.ts
26482
- import { Elysia as Elysia45 } from "elysia";
26741
+ import { Elysia as Elysia46 } from "elysia";
26483
26742
  var toHex6 = (bytes) => Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
26484
26743
  var signVoiceOpsWebhookBody = async (input) => {
26485
26744
  const encoder = new TextEncoder;
@@ -26609,7 +26868,7 @@ var verifyVoiceOpsWebhookSignature = async (input) => {
26609
26868
  };
26610
26869
  var createVoiceOpsWebhookReceiverRoutes = (options = {}) => {
26611
26870
  const path = options.path ?? "/api/voice-ops/webhook";
26612
- return new Elysia45().post(path, async ({ body, request, set }) => {
26871
+ return new Elysia46().post(path, async ({ body, request, set }) => {
26613
26872
  const bodyText = typeof body === "string" ? body : JSON.stringify(body);
26614
26873
  if (options.signingSecret) {
26615
26874
  const verification = await verifyVoiceOpsWebhookSignature({
@@ -27560,6 +27819,7 @@ export {
27560
27819
  renderVoiceOpsActionHistoryHTML,
27561
27820
  renderVoiceOperationsRecordIncidentMarkdown,
27562
27821
  renderVoiceOperationsRecordHTML,
27822
+ renderVoiceObservabilityExportMarkdown,
27563
27823
  renderVoiceLiveLatencyHTML,
27564
27824
  renderVoiceLatencySLOMarkdown,
27565
27825
  renderVoiceHandoffHealthHTML,
@@ -27758,6 +28018,7 @@ export {
27758
28018
  createVoiceOpsConsoleRoutes,
27759
28019
  createVoiceOpsActionAuditRoutes,
27760
28020
  createVoiceOperationsRecordRoutes,
28021
+ createVoiceObservabilityExportRoutes,
27761
28022
  createVoiceMemoryTraceSinkDeliveryStore,
27762
28023
  createVoiceMemoryTraceEventStore,
27763
28024
  createVoiceMemoryStore,
@@ -27894,6 +28155,7 @@ export {
27894
28155
  buildVoiceOpsConsoleReport,
27895
28156
  buildVoiceOpsActionHistoryReport,
27896
28157
  buildVoiceOperationsRecord,
28158
+ buildVoiceObservabilityExport,
27897
28159
  buildVoiceLiveOpsControlState,
27898
28160
  buildVoiceLatencySLOGate,
27899
28161
  buildVoiceIncidentBundle,
@@ -0,0 +1,123 @@
1
+ import { Elysia } from 'elysia';
2
+ import { type VoiceAuditSinkDeliveryQueueSummary, type VoiceAuditSinkDeliveryRecord, type VoiceAuditSinkDeliveryStore } from './auditSinks';
3
+ import type { VoiceAuditEventStore, VoiceAuditEventType } from './audit';
4
+ import { type VoiceOperationsRecord } from './operationsRecord';
5
+ import { type VoiceTraceSinkDeliveryQueueSummary } from './queue';
6
+ import { type StoredVoiceTraceEvent, type VoiceTraceEventStore, type VoiceTraceEventType, type VoiceTraceRedactionConfig, type VoiceTraceSinkDeliveryRecord, type VoiceTraceSinkDeliveryStore, type VoiceTraceSummary } from './trace';
7
+ export type VoiceObservabilityExportStatus = 'fail' | 'pass' | 'warn';
8
+ export type VoiceObservabilityExportArtifactKind = 'incident' | 'markdown' | 'operations-record' | 'proof-pack' | 'readiness' | 'screenshot' | 'slo' | 'trace' | 'audit' | 'custom';
9
+ export type VoiceObservabilityExportArtifact = {
10
+ bytes?: number;
11
+ contentType?: string;
12
+ generatedAt?: number | string;
13
+ href?: string;
14
+ id: string;
15
+ kind: VoiceObservabilityExportArtifactKind;
16
+ label: string;
17
+ path?: string;
18
+ sessionId?: string;
19
+ status?: VoiceObservabilityExportStatus;
20
+ };
21
+ export type VoiceObservabilityExportEnvelope = {
22
+ at: number;
23
+ eventId: string;
24
+ eventType: VoiceTraceEventType | VoiceAuditEventType;
25
+ kind: 'audit' | 'trace';
26
+ operationsRecordHref?: string;
27
+ provider?: string;
28
+ providerKind?: string;
29
+ scenarioId?: string;
30
+ sessionId?: string;
31
+ severity: VoiceObservabilityExportStatus;
32
+ traceId?: string;
33
+ };
34
+ export type VoiceObservabilityExportIssueCode = 'voice.observability.no_evidence' | 'voice.observability.operation_failed' | 'voice.observability.audit_delivery_failed' | 'voice.observability.audit_delivery_pending' | 'voice.observability.trace_delivery_failed' | 'voice.observability.trace_delivery_pending';
35
+ export type VoiceObservabilityExportIssue = {
36
+ code: VoiceObservabilityExportIssueCode;
37
+ detail?: string;
38
+ label: string;
39
+ severity: Exclude<VoiceObservabilityExportStatus, 'pass'>;
40
+ value?: number | string;
41
+ };
42
+ export type VoiceObservabilityExportDeliverySummary = {
43
+ audit?: VoiceAuditSinkDeliveryQueueSummary;
44
+ trace?: VoiceTraceSinkDeliveryQueueSummary;
45
+ };
46
+ export type VoiceObservabilityExportRedactionSummary = {
47
+ enabled: boolean;
48
+ mode: 'none' | 'redacted';
49
+ };
50
+ export type VoiceObservabilityExportReport = {
51
+ artifacts: VoiceObservabilityExportArtifact[];
52
+ checkedAt: number;
53
+ deliveries: VoiceObservabilityExportDeliverySummary;
54
+ envelopes: VoiceObservabilityExportEnvelope[];
55
+ issues: VoiceObservabilityExportIssue[];
56
+ operationsRecords: VoiceOperationsRecord[];
57
+ redaction: VoiceObservabilityExportRedactionSummary;
58
+ sessionIds: string[];
59
+ status: VoiceObservabilityExportStatus;
60
+ summary: {
61
+ auditEvents: number;
62
+ events: number;
63
+ failedOperationsRecords: number;
64
+ trace: VoiceTraceSummary;
65
+ traceEvents: number;
66
+ };
67
+ };
68
+ export type VoiceObservabilityExportOptions = {
69
+ artifacts?: VoiceObservabilityExportArtifact[];
70
+ audit?: VoiceAuditEventStore;
71
+ auditDeliveries?: VoiceAuditSinkDeliveryRecord[] | VoiceAuditSinkDeliveryStore;
72
+ events?: StoredVoiceTraceEvent[];
73
+ includeOperationsRecords?: boolean;
74
+ links?: {
75
+ operationsRecord?: (sessionId: string) => string;
76
+ };
77
+ operationsRecords?: VoiceOperationsRecord[];
78
+ redact?: VoiceTraceRedactionConfig;
79
+ sessionIds?: string[];
80
+ store?: VoiceTraceEventStore;
81
+ traceDeliveries?: VoiceTraceSinkDeliveryRecord[] | VoiceTraceSinkDeliveryStore;
82
+ };
83
+ export type VoiceObservabilityExportRoutesOptions = VoiceObservabilityExportOptions & {
84
+ headers?: HeadersInit;
85
+ htmlPath?: false | string;
86
+ markdownPath?: false | string;
87
+ name?: string;
88
+ path?: string;
89
+ render?: (report: VoiceObservabilityExportReport) => string | Promise<string>;
90
+ title?: string;
91
+ };
92
+ export declare const buildVoiceObservabilityExport: (options?: VoiceObservabilityExportOptions) => Promise<VoiceObservabilityExportReport>;
93
+ export declare const renderVoiceObservabilityExportMarkdown: (report: VoiceObservabilityExportReport, options?: {
94
+ title?: string;
95
+ }) => string;
96
+ export declare const createVoiceObservabilityExportRoutes: (options?: VoiceObservabilityExportRoutesOptions) => Elysia<"", {
97
+ decorator: {};
98
+ store: {};
99
+ derive: {};
100
+ resolve: {};
101
+ }, {
102
+ typebox: {};
103
+ error: {};
104
+ }, {
105
+ schema: {};
106
+ standaloneSchema: {};
107
+ macro: {};
108
+ macroFn: {};
109
+ parser: {};
110
+ response: {};
111
+ }, {}, {
112
+ derive: {};
113
+ resolve: {};
114
+ schema: {};
115
+ standaloneSchema: {};
116
+ response: {};
117
+ }, {
118
+ derive: {};
119
+ resolve: {};
120
+ schema: {};
121
+ standaloneSchema: {};
122
+ response: {};
123
+ }>;
@@ -65,6 +65,8 @@ export type VoiceProviderSloReportOptions = {
65
65
  maxAgeMs?: number;
66
66
  now?: number;
67
67
  requiredKinds?: readonly VoiceRoutingEventKind[];
68
+ scenarioId?: string;
69
+ sessionId?: string;
68
70
  store?: VoiceTraceEventStore;
69
71
  thresholds?: VoiceProviderSloThresholdConfig;
70
72
  };
@@ -14,6 +14,7 @@ export type VoiceRoutingEvent = {
14
14
  operation?: string;
15
15
  provider?: string;
16
16
  routing?: string;
17
+ scenarioId?: string;
17
18
  selectedProvider?: string;
18
19
  sessionId: string;
19
20
  status?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.216",
3
+ "version": "0.0.22-beta.218",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",