@absolutejs/voice 0.0.22-beta.436 → 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.
@@ -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'> & {
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 [sessionSnapshots, callDebuggerReports] = await time("supportArtifacts", () => Promise.all([
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) => [
@@ -37903,6 +37960,7 @@ var buildSummary = (record) => ({
37903
37960
  turns: record.summary.turnCount
37904
37961
  });
37905
37962
  var renderIncidentMarkdown = (input) => {
37963
+ const recoveryOutcomes = input.recoveryOutcomes;
37906
37964
  const lines = [
37907
37965
  `# ${input.title ?? `Voice Incident ${input.summary.sessionId}`}`,
37908
37966
  "",
@@ -37934,6 +37992,18 @@ var renderIncidentMarkdown = (input) => {
37934
37992
  "",
37935
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"],
37936
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
+ "",
37937
38007
  renderVoiceOperationsRecordGuardrailMarkdown(input.record),
37938
38008
  "",
37939
38009
  "## Trace Evidence",
@@ -37983,6 +38053,7 @@ var buildVoiceIncidentBundle = async (options) => {
37983
38053
  traceEvents: redactedTraceEvents
37984
38054
  });
37985
38055
  const summary = buildSummary(redactedRecord);
38056
+ const recoveryOutcomes = options.recoveryOutcomes ? redactRecordValue(options.recoveryOutcomes, redactedTraceEvents, options.redact) : undefined;
37986
38057
  const traceMarkdown = renderVoiceTraceMarkdown(record.traceEvents, {
37987
38058
  evaluation: options.evaluation,
37988
38059
  redact: options.redact,
@@ -37995,6 +38066,7 @@ var buildVoiceIncidentBundle = async (options) => {
37995
38066
  const markdown = renderIncidentMarkdown({
37996
38067
  auditMarkdown,
37997
38068
  record: redactedRecord,
38069
+ recoveryOutcomes,
37998
38070
  summary,
37999
38071
  title: options.title,
38000
38072
  traceMarkdown
@@ -38005,6 +38077,7 @@ var buildVoiceIncidentBundle = async (options) => {
38005
38077
  formatVersion: 1,
38006
38078
  markdown,
38007
38079
  record: redactedRecord,
38080
+ recoveryOutcomes,
38008
38081
  redacted: Boolean(options.redact),
38009
38082
  sessionId: options.sessionId,
38010
38083
  summary,
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.436",
3
+ "version": "0.0.22-beta.437",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",