@absolutejs/voice 0.0.22-beta.144 → 0.0.22-beta.146

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,3 +1,4 @@
1
+ import type { S3Client, S3Options } from 'bun';
1
2
  import type { StoredVoiceAuditEvent, VoiceAuditEventStore, VoiceAuditEventType } from './audit';
2
3
  import type { VoiceIdempotencyStore, VoiceRedisTaskLeaseCoordinator } from './queue';
3
4
  import type { VoiceTraceRedactionConfig } from './trace';
@@ -59,6 +60,22 @@ export type VoiceAuditHTTPSinkOptions<TBody extends Record<string, unknown> = Re
59
60
  timeoutMs?: number;
60
61
  url: string;
61
62
  };
63
+ export type VoiceS3AuditSinkFile = {
64
+ write: (data: string, options?: BlobPropertyBag) => Promise<number> | number;
65
+ };
66
+ export type VoiceS3AuditSinkClient = Pick<S3Client, 'file'>;
67
+ export type VoiceAuditS3SinkOptions<TBody extends Record<string, unknown> = Record<string, unknown>> = S3Options & {
68
+ body?: (input: {
69
+ events: StoredVoiceAuditEvent[];
70
+ key: string;
71
+ }) => Promise<TBody> | TBody;
72
+ client?: VoiceS3AuditSinkClient;
73
+ contentType?: string;
74
+ eventTypes?: VoiceAuditEventType[];
75
+ id: string;
76
+ keyPrefix?: string;
77
+ kind?: string;
78
+ };
62
79
  export type VoiceAuditSinkStoreOptions<TEvent extends StoredVoiceAuditEvent = StoredVoiceAuditEvent> = {
63
80
  awaitDelivery?: boolean;
64
81
  deliveryQueue?: VoiceAuditSinkDeliveryStore;
@@ -117,6 +134,7 @@ export declare const createVoiceAuditSinkDeliveryRecord: (input: {
117
134
  id?: string;
118
135
  } & Partial<Omit<VoiceAuditSinkDeliveryRecord, "createdAt" | "events" | "id">>) => VoiceAuditSinkDeliveryRecord;
119
136
  export declare const createVoiceAuditHTTPSink: <TBody extends Record<string, unknown> = Record<string, unknown>>(options: VoiceAuditHTTPSinkOptions<TBody>) => VoiceAuditSink;
137
+ export declare const createVoiceAuditS3Sink: <TBody extends Record<string, unknown> = Record<string, unknown>>(options: VoiceAuditS3SinkOptions<TBody>) => VoiceAuditSink;
120
138
  export declare const deliverVoiceAuditEventsToSinks: (input: {
121
139
  events: StoredVoiceAuditEvent[];
122
140
  redact?: VoiceTraceRedactionConfig;
@@ -9,6 +9,29 @@ export type VoiceDeliverySinkDescriptor = {
9
9
  id: string;
10
10
  kind: VoiceDeliverySinkKind;
11
11
  label: string;
12
+ mode?: string;
13
+ target?: string;
14
+ };
15
+ export type VoiceDeliverySinkDescriptorInput = {
16
+ description?: string;
17
+ href?: string;
18
+ id?: string;
19
+ kind: VoiceDeliverySinkKind;
20
+ label?: string;
21
+ mode?: string;
22
+ target?: string;
23
+ };
24
+ export type VoiceDeliverySinkPairOptions = {
25
+ auditHref?: string;
26
+ auditId?: string;
27
+ auditLabel?: string;
28
+ description?: string;
29
+ kind: VoiceDeliverySinkKind;
30
+ mode?: string;
31
+ target?: string;
32
+ traceHref?: string;
33
+ traceId?: string;
34
+ traceLabel?: string;
12
35
  };
13
36
  export type VoiceDeliverySinkReport = {
14
37
  auditDeliveries?: VoiceTraceDeliverySinkSurface<VoiceAuditSinkDeliveryQueueSummary>;
@@ -41,6 +64,13 @@ export type VoiceDeliverySinkRoutesOptions = {
41
64
  store: VoiceTraceSinkDeliveryStore;
42
65
  };
43
66
  };
67
+ export declare const createVoiceDeliverySinkDescriptor: (input: VoiceDeliverySinkDescriptorInput) => VoiceDeliverySinkDescriptor;
68
+ export declare const createVoiceFileDeliverySink: (input?: Omit<VoiceDeliverySinkDescriptorInput, "kind">) => VoiceDeliverySinkDescriptor;
69
+ export declare const createVoiceWebhookDeliverySink: (input?: Omit<VoiceDeliverySinkDescriptorInput, "kind">) => VoiceDeliverySinkDescriptor;
70
+ export declare const createVoiceS3DeliverySink: (input?: Omit<VoiceDeliverySinkDescriptorInput, "kind">) => VoiceDeliverySinkDescriptor;
71
+ export declare const createVoicePostgresDeliverySink: (input?: Omit<VoiceDeliverySinkDescriptorInput, "kind">) => VoiceDeliverySinkDescriptor;
72
+ export declare const createVoiceSQLiteDeliverySink: (input?: Omit<VoiceDeliverySinkDescriptorInput, "kind">) => VoiceDeliverySinkDescriptor;
73
+ export declare const createVoiceDeliverySinkPair: (options: VoiceDeliverySinkPairOptions) => VoiceDeliverySinkDescriptor[];
44
74
  export declare const buildVoiceDeliverySinkReport: (options: VoiceDeliverySinkRoutesOptions) => Promise<VoiceDeliverySinkReport>;
45
75
  export declare const renderVoiceDeliverySinkHTML: (report: VoiceDeliverySinkReport, options?: {
46
76
  title?: string;
package/dist/index.d.ts CHANGED
@@ -6,17 +6,17 @@ export { createVoiceAssistantHealthHTMLHandler, createVoiceAssistantHealthJSONHa
6
6
  export { createVoiceAuditEvent, createVoiceAuditLogger, createVoiceMemoryAuditEventStore, filterVoiceAuditEvents, recordVoiceAuditEvent, recordVoiceHandoffAuditEvent, recordVoiceOperatorAuditEvent, recordVoiceProviderAuditEvent, recordVoiceRetentionAuditEvent, recordVoiceToolAuditEvent } from './audit';
7
7
  export { buildVoiceAuditTrailReport, createVoiceAuditTrailRoutes, renderVoiceAuditTrailHTML, resolveVoiceAuditTrailFilter, summarizeVoiceAuditTrail } from './auditRoutes';
8
8
  export { buildVoiceAuditExport, exportVoiceAuditTrail, redactVoiceAuditEvent, redactVoiceAuditEvents, renderVoiceAuditHTML, renderVoiceAuditMarkdown } from './auditExport';
9
- export { createVoiceAuditHTTPSink, createVoiceAuditSinkDeliveryId, createVoiceAuditSinkDeliveryRecord, createVoiceAuditSinkDeliveryWorker, createVoiceAuditSinkDeliveryWorkerLoop, createVoiceAuditSinkStore, createVoiceMemoryAuditSinkDeliveryStore, deliverVoiceAuditEventsToSinks, summarizeVoiceAuditSinkDeliveries } from './auditSinks';
9
+ export { createVoiceAuditHTTPSink, createVoiceAuditS3Sink, createVoiceAuditSinkDeliveryId, createVoiceAuditSinkDeliveryRecord, createVoiceAuditSinkDeliveryWorker, createVoiceAuditSinkDeliveryWorkerLoop, createVoiceAuditSinkStore, createVoiceMemoryAuditSinkDeliveryStore, deliverVoiceAuditEventsToSinks, summarizeVoiceAuditSinkDeliveries } from './auditSinks';
10
10
  export { buildVoiceAuditDeliveryReport, createVoiceAuditDeliveryHTMLHandler, createVoiceAuditDeliveryJSONHandler, createVoiceAuditDeliveryRoutes, renderVoiceAuditDeliveryHTML, resolveVoiceAuditDeliveryFilter } from './auditDeliveryRoutes';
11
11
  export { createVoiceBargeInRoutes, renderVoiceBargeInHTML, summarizeVoiceBargeIn } from './bargeInRoutes';
12
12
  export { createVoiceReconnectContractRoutes, renderVoiceReconnectContractHTML, summarizeVoiceReconnectContractSnapshots, runVoiceReconnectContract } from './reconnectContract';
13
13
  export { buildVoiceDiagnosticsMarkdown, createVoiceDiagnosticsRoutes, resolveVoiceDiagnosticsTraceFilter } from './diagnosticsRoutes';
14
14
  export { buildVoiceDemoReadyReport, createVoiceDemoReadyRoutes, renderVoiceDemoReadyHTML } from './demoReadyRoutes';
15
- export { buildVoiceDeliverySinkReport, createVoiceDeliverySinkRoutes, renderVoiceDeliverySinkHTML } from './deliverySinkRoutes';
15
+ export { buildVoiceDeliverySinkReport, createVoiceDeliverySinkDescriptor, createVoiceDeliverySinkPair, createVoiceDeliverySinkRoutes, createVoiceFileDeliverySink, createVoicePostgresDeliverySink, createVoiceS3DeliverySink, createVoiceSQLiteDeliverySink, createVoiceWebhookDeliverySink, renderVoiceDeliverySinkHTML } from './deliverySinkRoutes';
16
16
  export { applyVoiceDataRetentionPolicy, buildVoiceDataRetentionPlan } from './dataControl';
17
17
  export type { VoiceDataRetentionPolicy, VoiceDataRetentionReport, VoiceDataRetentionScope, VoiceDataRetentionScopeReport, VoiceDataRetentionStores } from './dataControl';
18
18
  export type { VoiceDemoReadyReport, VoiceDemoReadyRoutesOptions, VoiceDemoReadySection, VoiceDemoReadyStatus } from './demoReadyRoutes';
19
- export type { VoiceDeliverySinkDescriptor, VoiceDeliverySinkKind, VoiceDeliverySinkReport, VoiceDeliverySinkRoutesOptions, VoiceTraceDeliverySinkSurface } from './deliverySinkRoutes';
19
+ export type { VoiceDeliverySinkDescriptor, VoiceDeliverySinkDescriptorInput, VoiceDeliverySinkKind, VoiceDeliverySinkPairOptions, VoiceDeliverySinkReport, VoiceDeliverySinkRoutesOptions, VoiceTraceDeliverySinkSurface } from './deliverySinkRoutes';
20
20
  export { compareVoiceEvalBaseline, createVoiceFileEvalBaselineStore, createVoiceFileScenarioFixtureStore, createVoiceEvalRoutes, renderVoiceEvalBaselineHTML, renderVoiceEvalHTML, renderVoiceScenarioEvalHTML, renderVoiceScenarioFixtureEvalHTML, runVoiceScenarioEvals, runVoiceScenarioFixtureEvals, runVoiceSessionEvals } from './evalRoutes';
21
21
  export { createVoiceSimulationSuiteRoutes, renderVoiceSimulationSuiteHTML, runVoiceSimulationSuite } from './simulationSuite';
22
22
  export { createVoiceWorkflowContract, createVoiceWorkflowContractHandler, createVoiceWorkflowContractPreset, createVoiceWorkflowScenario, recordVoiceWorkflowContractTrace, validateVoiceWorkflowRouteResult } from './workflowContract';
@@ -47,7 +47,7 @@ export { createVoiceOpsStatusRoutes, renderVoiceOpsStatusHTML } from './opsStatu
47
47
  export { createVoiceQualityRoutes, evaluateVoiceQuality, renderVoiceQualityHTML } from './qualityRoutes';
48
48
  export { createVoiceResilienceRoutes, createVoiceRoutingDecisionSummary, listVoiceRoutingEvents, renderVoiceResilienceHTML, summarizeVoiceRoutingDecision, summarizeVoiceRoutingSessions } from './resilienceRoutes';
49
49
  export { createVoiceSTTProviderRouter, createVoiceTTSProviderRouter } from './providerAdapters';
50
- export { buildVoiceTraceReplay, createVoiceMemoryTraceSinkDeliveryStore, createVoiceTraceHTTPSink, createVoiceMemoryTraceEventStore, createVoiceTraceSinkDeliveryId, createVoiceTraceSinkDeliveryRecord, createVoiceTraceSinkStore, createVoiceTraceEvent, createVoiceTraceEventId, deliverVoiceTraceEventsToSinks, evaluateVoiceTrace, exportVoiceTrace, filterVoiceTraceEvents, pruneVoiceTraceEvents, redactVoiceTraceEvent, redactVoiceTraceEvents, redactVoiceTraceText, renderVoiceTraceHTML, renderVoiceTraceMarkdown, resolveVoiceTraceRedactionOptions, selectVoiceTraceEventsForPrune, summarizeVoiceTrace } from './trace';
50
+ export { buildVoiceTraceReplay, createVoiceMemoryTraceSinkDeliveryStore, createVoiceTraceHTTPSink, createVoiceTraceS3Sink, createVoiceMemoryTraceEventStore, createVoiceTraceSinkDeliveryId, createVoiceTraceSinkDeliveryRecord, createVoiceTraceSinkStore, createVoiceTraceEvent, createVoiceTraceEventId, deliverVoiceTraceEventsToSinks, evaluateVoiceTrace, exportVoiceTrace, filterVoiceTraceEvents, pruneVoiceTraceEvents, redactVoiceTraceEvent, redactVoiceTraceEvents, redactVoiceTraceText, renderVoiceTraceHTML, renderVoiceTraceMarkdown, resolveVoiceTraceRedactionOptions, selectVoiceTraceEventsForPrune, summarizeVoiceTrace } from './trace';
51
51
  export { buildVoiceTraceDeliveryReport, createVoiceTraceDeliveryHTMLHandler, createVoiceTraceDeliveryJSONHandler, createVoiceTraceDeliveryRoutes, renderVoiceTraceDeliveryHTML, resolveVoiceTraceDeliveryFilter } from './traceDeliveryRoutes';
52
52
  export { createVoiceTraceTimelineRoutes, renderVoiceTraceTimelineHTML, renderVoiceTraceTimelineSessionHTML, summarizeVoiceTraceTimeline } from './traceTimeline';
53
53
  export { createVoiceSQLiteAuditEventStore, createVoiceSQLiteAuditSinkDeliveryStore, createVoiceSQLiteCampaignStore, createVoiceSQLiteExternalObjectMapStore, createVoiceSQLiteIntegrationEventStore, createVoiceSQLiteReviewStore, createVoiceSQLiteRuntimeStorage, createVoiceSQLiteSessionStore, createVoiceSQLiteTaskStore, createVoiceSQLiteTelephonyWebhookIdempotencyStore, createVoiceSQLiteTraceSinkDeliveryStore, createVoiceSQLiteTraceEventStore } from './sqliteStore';
@@ -117,10 +117,10 @@ export type { StoredVoiceCallReviewArtifact, VoiceCallReviewArtifact, VoiceCallR
117
117
  export type { StoredVoiceAuditEvent, VoiceAuditActor, VoiceAuditEvent, VoiceAuditEventFilter, VoiceAuditEventStore, VoiceAuditEventType, VoiceAuditLogger, VoiceAuditOutcome, VoiceAuditResource, VoiceHandoffAuditEventInput, VoiceOperatorAuditEventInput, VoiceProviderAuditEventInput, VoiceRetentionAuditEventInput, VoiceToolAuditEventInput } from './audit';
118
118
  export type { VoiceAuditTrailOptions, VoiceAuditTrailReport, VoiceAuditTrailRoutesOptions, VoiceAuditTrailSummary } from './auditRoutes';
119
119
  export type { VoiceAuditExport } from './auditExport';
120
- export type { VoiceAuditHTTPSinkOptions, VoiceAuditSink, VoiceAuditSinkDeliveryQueueStatus, VoiceAuditSinkDeliveryQueueSummary, VoiceAuditSinkDeliveryRecord, VoiceAuditSinkDeliveryResult, VoiceAuditSinkDeliveryStatus, VoiceAuditSinkDeliveryStore, VoiceAuditSinkDeliveryWorkerLoop, VoiceAuditSinkDeliveryWorkerLoopOptions, VoiceAuditSinkDeliveryWorkerOptions, VoiceAuditSinkDeliveryWorkerResult, VoiceAuditSinkFanoutResult, VoiceAuditSinkStoreOptions } from './auditSinks';
120
+ export type { VoiceAuditHTTPSinkOptions, VoiceAuditS3SinkOptions, VoiceS3AuditSinkClient, VoiceS3AuditSinkFile, VoiceAuditSink, VoiceAuditSinkDeliveryQueueStatus, VoiceAuditSinkDeliveryQueueSummary, VoiceAuditSinkDeliveryRecord, VoiceAuditSinkDeliveryResult, VoiceAuditSinkDeliveryStatus, VoiceAuditSinkDeliveryStore, VoiceAuditSinkDeliveryWorkerLoop, VoiceAuditSinkDeliveryWorkerLoopOptions, VoiceAuditSinkDeliveryWorkerOptions, VoiceAuditSinkDeliveryWorkerResult, VoiceAuditSinkFanoutResult, VoiceAuditSinkStoreOptions } from './auditSinks';
121
121
  export type { VoiceAuditDeliveryDrainReport, VoiceAuditDeliveryDrainWorker, VoiceAuditDeliveryFilter, VoiceAuditDeliveryReport, VoiceAuditDeliveryRoutesOptions } from './auditDeliveryRoutes';
122
122
  export type { VoiceFileRuntimeStorage, VoiceFileStoreOptions } from './fileStore';
123
- export type { StoredVoiceTraceEvent, VoiceTraceEvaluation, VoiceTraceEvaluationOptions, VoiceTraceEvent, VoiceTraceEventFilter, VoiceTraceEventStore, VoiceTraceEventType, VoiceTraceIssue, VoiceTraceIssueSeverity, VoiceTraceHTTPSinkOptions, VoiceTracePruneFilter, VoiceTracePruneOptions, VoiceTracePruneResult, VoiceTraceRedactionConfig, VoiceTraceRedactionOptions, VoiceTraceRedactionReplacement, VoiceResolvedTraceRedactionOptions, VoiceTraceSink, VoiceTraceSinkDeliveryQueueStatus, VoiceTraceSinkDeliveryRecord, VoiceTraceSinkDeliveryResult, VoiceTraceSinkDeliveryStatus, VoiceTraceSinkDeliveryStore, VoiceTraceSinkFanoutResult, VoiceTraceSinkStoreOptions, VoiceTraceSummary } from './trace';
123
+ export type { StoredVoiceTraceEvent, VoiceTraceEvaluation, VoiceTraceEvaluationOptions, VoiceTraceEvent, VoiceTraceEventFilter, VoiceTraceEventStore, VoiceTraceEventType, VoiceTraceIssue, VoiceTraceIssueSeverity, VoiceTraceHTTPSinkOptions, VoiceTraceS3SinkOptions, VoiceTracePruneFilter, VoiceTracePruneOptions, VoiceTracePruneResult, VoiceTraceRedactionConfig, VoiceTraceRedactionOptions, VoiceTraceRedactionReplacement, VoiceResolvedTraceRedactionOptions, VoiceTraceSink, VoiceTraceSinkDeliveryQueueStatus, VoiceTraceSinkDeliveryRecord, VoiceTraceSinkDeliveryResult, VoiceTraceSinkDeliveryStatus, VoiceTraceSinkDeliveryStore, VoiceTraceSinkFanoutResult, VoiceTraceSinkStoreOptions, VoiceTraceSummary, VoiceS3TraceSinkClient, VoiceS3TraceSinkFile } from './trace';
124
124
  export type { VoiceTraceDeliveryDrainReport, VoiceTraceDeliveryDrainWorker, VoiceTraceDeliveryFilter, VoiceTraceDeliveryReport, VoiceTraceDeliveryRoutesOptions } from './traceDeliveryRoutes';
125
125
  export type { VoiceTraceTimelineEvent, VoiceTraceTimelineProviderSummary, VoiceTraceTimelineReport, VoiceTraceTimelineRoutesOptions, VoiceTraceTimelineSession } from './traceTimeline';
126
126
  export type { VoicePostgresClient, VoicePostgresRuntimeStorage, VoicePostgresStoreOptions } from './postgresStore';
package/dist/index.js CHANGED
@@ -8534,6 +8534,17 @@ var createVoiceTraceSinkDeliveryError = (input) => {
8534
8534
  }
8535
8535
  return `Attempt ${input.attempt} failed: ${String(input.error)}`;
8536
8536
  };
8537
+ var normalizeVoiceTraceS3KeyPrefix = (prefix) => prefix?.trim().replace(/^\/+|\/+$/g, "") ?? "voice/trace-deliveries";
8538
+ var createVoiceTraceS3ObjectKey = (prefix, events) => {
8539
+ const firstEvent = events[0];
8540
+ const safeSessionId = encodeURIComponent(firstEvent?.sessionId ?? "trace");
8541
+ const safeEventId = encodeURIComponent(firstEvent?.id ?? crypto.randomUUID());
8542
+ return `${prefix}/${safeSessionId}/${Date.now()}-${safeEventId}.json`;
8543
+ };
8544
+ var resolveVoiceS3DeliveredTo = (options, key) => {
8545
+ const bucket = options.bucket;
8546
+ return bucket ? `s3://${bucket}/${key}` : `s3://${key}`;
8547
+ };
8537
8548
  var aggregateVoiceTraceSinkDeliveryStatus = (deliveries) => {
8538
8549
  const statuses = Object.values(deliveries).map((delivery) => delivery.status);
8539
8550
  if (statuses.length === 0 || statuses.every((status) => status === "skipped")) {
@@ -8639,6 +8650,46 @@ var createVoiceTraceHTTPSink = (options) => ({
8639
8650
  id: options.id,
8640
8651
  kind: options.kind ?? "http"
8641
8652
  });
8653
+ var createVoiceTraceS3Sink = (options) => {
8654
+ const client = options.client ?? new Bun.S3Client(options);
8655
+ const keyPrefix = normalizeVoiceTraceS3KeyPrefix(options.keyPrefix);
8656
+ return {
8657
+ deliver: async ({ events }) => {
8658
+ const key = createVoiceTraceS3ObjectKey(keyPrefix, events);
8659
+ const payload = options.body ? await options.body({ events, key }) : {
8660
+ eventCount: events.length,
8661
+ events,
8662
+ key,
8663
+ source: "absolutejs-voice"
8664
+ };
8665
+ try {
8666
+ const file = client.file(key, options);
8667
+ await file.write(JSON.stringify(payload), {
8668
+ type: options.contentType ?? "application/json"
8669
+ });
8670
+ return {
8671
+ attempts: 1,
8672
+ deliveredAt: Date.now(),
8673
+ deliveredTo: resolveVoiceS3DeliveredTo(options, key),
8674
+ eventCount: events.length,
8675
+ responseBody: { key },
8676
+ status: "delivered"
8677
+ };
8678
+ } catch (error) {
8679
+ return {
8680
+ attempts: 1,
8681
+ deliveredTo: resolveVoiceS3DeliveredTo(options, key),
8682
+ error: error instanceof Error ? error.message : String(error),
8683
+ eventCount: events.length,
8684
+ status: "failed"
8685
+ };
8686
+ }
8687
+ },
8688
+ eventTypes: options.eventTypes,
8689
+ id: options.id,
8690
+ kind: options.kind ?? "s3"
8691
+ };
8692
+ };
8642
8693
  var deliverVoiceTraceEventsToSinks = async (input) => {
8643
8694
  const events = input.redact ? redactVoiceTraceEvents(input.events, input.redact) : input.events;
8644
8695
  const sinkDeliveries = {};
@@ -9431,6 +9482,17 @@ var createVoiceAuditSinkDeliveryError = (input) => {
9431
9482
  }
9432
9483
  return `Attempt ${input.attempt} failed: ${String(input.error)}`;
9433
9484
  };
9485
+ var normalizeVoiceAuditS3KeyPrefix = (prefix) => prefix?.trim().replace(/^\/+|\/+$/g, "") ?? "voice/audit-deliveries";
9486
+ var createVoiceAuditS3ObjectKey = (prefix, events) => {
9487
+ const firstEvent = events[0];
9488
+ const safeSessionId = encodeURIComponent(firstEvent?.sessionId ?? "audit");
9489
+ const safeEventId = encodeURIComponent(firstEvent?.id ?? crypto.randomUUID());
9490
+ return `${prefix}/${safeSessionId}/${Date.now()}-${safeEventId}.json`;
9491
+ };
9492
+ var resolveVoiceS3DeliveredTo2 = (options, key) => {
9493
+ const bucket = options.bucket;
9494
+ return bucket ? `s3://${bucket}/${key}` : `s3://${key}`;
9495
+ };
9434
9496
  var aggregateVoiceAuditSinkDeliveryStatus = (deliveries) => {
9435
9497
  const statuses = Object.values(deliveries).map((delivery) => delivery.status);
9436
9498
  if (statuses.length === 0 || statuses.every((status) => status === "skipped")) {
@@ -9559,6 +9621,46 @@ var createVoiceAuditHTTPSink = (options) => ({
9559
9621
  id: options.id,
9560
9622
  kind: options.kind ?? "http"
9561
9623
  });
9624
+ var createVoiceAuditS3Sink = (options) => {
9625
+ const client = options.client ?? new Bun.S3Client(options);
9626
+ const keyPrefix = normalizeVoiceAuditS3KeyPrefix(options.keyPrefix);
9627
+ return {
9628
+ deliver: async ({ events }) => {
9629
+ const key = createVoiceAuditS3ObjectKey(keyPrefix, events);
9630
+ const payload = options.body ? await options.body({ events, key }) : {
9631
+ eventCount: events.length,
9632
+ events,
9633
+ key,
9634
+ source: "absolutejs-voice"
9635
+ };
9636
+ try {
9637
+ const file = client.file(key, options);
9638
+ await file.write(JSON.stringify(payload), {
9639
+ type: options.contentType ?? "application/json"
9640
+ });
9641
+ return {
9642
+ attempts: 1,
9643
+ deliveredAt: Date.now(),
9644
+ deliveredTo: resolveVoiceS3DeliveredTo2(options, key),
9645
+ eventCount: events.length,
9646
+ responseBody: { key },
9647
+ status: "delivered"
9648
+ };
9649
+ } catch (error) {
9650
+ return {
9651
+ attempts: 1,
9652
+ deliveredTo: resolveVoiceS3DeliveredTo2(options, key),
9653
+ error: error instanceof Error ? error.message : String(error),
9654
+ eventCount: events.length,
9655
+ status: "failed"
9656
+ };
9657
+ }
9658
+ },
9659
+ eventTypes: options.eventTypes,
9660
+ id: options.id,
9661
+ kind: options.kind ?? "s3"
9662
+ };
9663
+ };
9562
9664
  var deliverVoiceAuditEventsToSinks = async (input) => {
9563
9665
  const events = input.redact === false ? input.events : redactVoiceAuditEvents(input.events, input.redact ?? true);
9564
9666
  const sinkDeliveries = {};
@@ -11348,6 +11450,56 @@ var rollupDeliverySinkStatus = (report) => {
11348
11450
  ];
11349
11451
  return statuses.includes("fail") ? "fail" : statuses.includes("warn") ? "warn" : "pass";
11350
11452
  };
11453
+ var deliverySinkLabel = (kind) => `${String(kind).replaceAll(/[-_]+/g, " ")} sink`;
11454
+ var createVoiceDeliverySinkDescriptor = (input) => ({
11455
+ description: input.description,
11456
+ href: input.href,
11457
+ id: input.id ?? `${input.kind}-sink`,
11458
+ kind: input.kind,
11459
+ label: input.label ?? deliverySinkLabel(input.kind),
11460
+ mode: input.mode,
11461
+ target: input.target
11462
+ });
11463
+ var createVoiceFileDeliverySink = (input = {}) => createVoiceDeliverySinkDescriptor({
11464
+ ...input,
11465
+ kind: "file"
11466
+ });
11467
+ var createVoiceWebhookDeliverySink = (input = {}) => createVoiceDeliverySinkDescriptor({
11468
+ ...input,
11469
+ kind: "webhook"
11470
+ });
11471
+ var createVoiceS3DeliverySink = (input = {}) => createVoiceDeliverySinkDescriptor({
11472
+ ...input,
11473
+ kind: "s3"
11474
+ });
11475
+ var createVoicePostgresDeliverySink = (input = {}) => createVoiceDeliverySinkDescriptor({
11476
+ ...input,
11477
+ kind: "postgres"
11478
+ });
11479
+ var createVoiceSQLiteDeliverySink = (input = {}) => createVoiceDeliverySinkDescriptor({
11480
+ ...input,
11481
+ kind: "sqlite"
11482
+ });
11483
+ var createVoiceDeliverySinkPair = (options) => [
11484
+ createVoiceDeliverySinkDescriptor({
11485
+ description: options.description,
11486
+ href: options.auditHref,
11487
+ id: options.auditId ?? `${options.kind}-audit-sink`,
11488
+ kind: options.kind,
11489
+ label: options.auditLabel ?? `${deliverySinkLabel(options.kind)} audit`,
11490
+ mode: options.mode,
11491
+ target: options.target
11492
+ }),
11493
+ createVoiceDeliverySinkDescriptor({
11494
+ description: options.description,
11495
+ href: options.traceHref,
11496
+ id: options.traceId ?? `${options.kind}-trace-sink`,
11497
+ kind: options.kind,
11498
+ label: options.traceLabel ?? `${deliverySinkLabel(options.kind)} trace`,
11499
+ mode: options.mode,
11500
+ target: options.target
11501
+ })
11502
+ ];
11351
11503
  var buildVoiceDeliverySinkReport = async (options) => {
11352
11504
  const [auditSummary, traceSummary] = await Promise.all([
11353
11505
  options.auditDeliveries ? Promise.resolve(options.auditDeliveries.store.list()).then((deliveries) => summarizeVoiceAuditSinkDeliveries(deliveries)) : undefined,
@@ -11382,11 +11534,14 @@ var renderSurfaceCard = (surface) => {
11382
11534
  };
11383
11535
  var renderVoiceDeliverySinkHTML = (report, options = {}) => {
11384
11536
  const title = options.title ?? "AbsoluteJS Voice Delivery Sinks";
11385
- const sinks = report.sinks.length ? report.sinks.map((sink) => `<article><span>${escapeHtml14(sink.kind)}</span><strong style="font-size:1.5rem">${escapeHtml14(sink.label)}</strong>${sink.description ? `<p class="muted">${escapeHtml14(sink.description)}</p>` : ""}${sink.href ? `<p><a href="${escapeHtml14(sink.href)}">Open sink</a></p>` : ""}</article>`).join("") : '<article><span>Sink</span><strong style="font-size:1.5rem">Not described</strong><p class="muted">Pass sink descriptors to document your file, webhook, S3, SQLite, or Postgres targets.</p></article>';
11537
+ const sinks = report.sinks.length ? report.sinks.map((sink) => `<article><span>${escapeHtml14(sink.kind)}</span><strong style="font-size:1.5rem">${escapeHtml14(sink.label)}</strong>${sink.description ? `<p class="muted">${escapeHtml14(sink.description)}</p>` : ""}${sink.mode ? `<p class="muted">Mode: ${escapeHtml14(sink.mode)}</p>` : ""}${sink.target ? `<p class="muted">Target: <code>${escapeHtml14(sink.target)}</code></p>` : ""}${sink.href ? `<p><a href="${escapeHtml14(sink.href)}">Open sink</a></p>` : ""}</article>`).join("") : '<article><span>Sink</span><strong style="font-size:1.5rem">Not described</strong><p class="muted">Pass sink descriptors to document your file, webhook, S3, SQLite, or Postgres targets.</p></article>';
11386
11538
  return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml14(title)}</title><style>body{background:#11120d;color:#fbf7e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{max-width:1120px;margin:auto;padding:32px}a{color:#fde68a;text-decoration:none}.hero{background:linear-gradient(135deg,rgba(253,230,138,.2),rgba(34,197,94,.14));border:1px solid #3a3420;border-radius:30px;margin-bottom:18px;padding:28px}.eyebrow{color:#fde68a;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.8rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #575030;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.status.pass{border-color:rgba(34,197,94,.65)}.status.warn{border-color:rgba(245,158,11,.65)}.status.fail{border-color:rgba(239,68,68,.75)}.muted{color:#b8b093}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));margin:18px 0}article,.card{background:#191a13;border:1px solid #33311f;border-radius:22px;padding:18px}article span{color:#b8b093;display:block;font-weight:800}article strong{display:block;font-size:2.4rem;margin-top:8px}pre{background:#0c0d09;border:1px solid #33311f;border-radius:18px;color:#fef3c7;overflow:auto;padding:16px}code{color:#fef3c7}</style></head><body><main><p><a href="/production-readiness">Production readiness</a></p><section class="hero"><p class="eyebrow">Composable sink primitive</p><h1>${escapeHtml14(title)}</h1><p class="muted">Delivery queues prove audit and trace exports without owning your infrastructure. Swap file, webhook, S3, SQLite, or Postgres sinks behind the same readiness surface.</p><p class="status ${escapeHtml14(report.status)}">Overall: ${escapeHtml14(report.status.toUpperCase())}</p><p class="muted">Checked ${escapeHtml14(new Date(report.checkedAt).toLocaleString())}</p></section><section class="grid">${renderSurfaceCard(report.auditDeliveries)}${renderSurfaceCard(report.traceDeliveries)}${sinks}</section><section class="card"><h2>Primitive shape</h2><p class="muted">Mount delivery sink routes beside audit and trace delivery queues. Production readiness can consume the same stores for pass/fail evidence.</p><pre>createVoiceDeliverySinkRoutes({
11387
11539
  auditDeliveries: { store: runtimeStorage.auditDeliveries },
11388
11540
  traceDeliveries: { store: runtimeStorage.traceDeliveries },
11389
- sinks: [{ id: "audit-file", kind: "file", label: "Audit file sink" }]
11541
+ sinks: createVoiceDeliverySinkPair({
11542
+ kind: "file",
11543
+ target: "file://.voice-runtime/voice-demo"
11544
+ })
11390
11545
  })</pre></section></main></body></html>`;
11391
11546
  };
11392
11547
  var createVoiceDeliverySinkRoutes = (options) => {
@@ -23062,6 +23217,7 @@ export {
23062
23217
  createVoiceWebhookHandoffAdapter,
23063
23218
  createVoiceWebhookDeliveryWorkerLoop,
23064
23219
  createVoiceWebhookDeliveryWorker,
23220
+ createVoiceWebhookDeliverySink,
23065
23221
  createVoiceTwilioRedirectHandoffAdapter,
23066
23222
  createVoiceTwilioCampaignDialer,
23067
23223
  createVoiceTurnQualityRoutes,
@@ -23076,6 +23232,7 @@ export {
23076
23232
  createVoiceTraceSinkDeliveryWorker,
23077
23233
  createVoiceTraceSinkDeliveryRecord,
23078
23234
  createVoiceTraceSinkDeliveryId,
23235
+ createVoiceTraceS3Sink,
23079
23236
  createVoiceTraceHTTPSink,
23080
23237
  createVoiceTraceEventId,
23081
23238
  createVoiceTraceEvent,
@@ -23119,10 +23276,12 @@ export {
23119
23276
  createVoiceSQLiteReviewStore,
23120
23277
  createVoiceSQLiteIntegrationEventStore,
23121
23278
  createVoiceSQLiteExternalObjectMapStore,
23279
+ createVoiceSQLiteDeliverySink,
23122
23280
  createVoiceSQLiteCampaignStore,
23123
23281
  createVoiceSQLiteAuditSinkDeliveryStore,
23124
23282
  createVoiceSQLiteAuditEventStore,
23125
23283
  createVoiceS3ReviewStore,
23284
+ createVoiceS3DeliverySink,
23126
23285
  createVoiceRoutingDecisionSummary,
23127
23286
  createVoiceReviewSavedEvent,
23128
23287
  createVoiceResilienceRoutes,
@@ -23148,6 +23307,7 @@ export {
23148
23307
  createVoicePostgresReviewStore,
23149
23308
  createVoicePostgresIntegrationEventStore,
23150
23309
  createVoicePostgresExternalObjectMapStore,
23310
+ createVoicePostgresDeliverySink,
23151
23311
  createVoicePostgresCampaignStore,
23152
23312
  createVoicePostgresAuditSinkDeliveryStore,
23153
23313
  createVoicePostgresAuditEventStore,
@@ -23204,6 +23364,7 @@ export {
23204
23364
  createVoiceFileIntegrationEventStore,
23205
23365
  createVoiceFileExternalObjectMapStore,
23206
23366
  createVoiceFileEvalBaselineStore,
23367
+ createVoiceFileDeliverySink,
23207
23368
  createVoiceFileCampaignStore,
23208
23369
  createVoiceFileAuditSinkDeliveryStore,
23209
23370
  createVoiceFileAuditEventStore,
@@ -23215,6 +23376,8 @@ export {
23215
23376
  createVoiceDiagnosticsRoutes,
23216
23377
  createVoiceDemoReadyRoutes,
23217
23378
  createVoiceDeliverySinkRoutes,
23379
+ createVoiceDeliverySinkPair,
23380
+ createVoiceDeliverySinkDescriptor,
23218
23381
  createVoiceCampaignWorkerLoop,
23219
23382
  createVoiceCampaignWorker,
23220
23383
  createVoiceCampaignTelephonyOutcomeHandler,
@@ -23232,6 +23395,7 @@ export {
23232
23395
  createVoiceAuditSinkDeliveryWorker,
23233
23396
  createVoiceAuditSinkDeliveryRecord,
23234
23397
  createVoiceAuditSinkDeliveryId,
23398
+ createVoiceAuditS3Sink,
23235
23399
  createVoiceAuditLogger,
23236
23400
  createVoiceAuditHTTPSink,
23237
23401
  createVoiceAuditEvent,
package/dist/trace.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { S3Client, S3Options } from 'bun';
1
2
  export type VoiceTraceEventType = 'assistant.guardrail' | 'assistant.memory' | 'assistant.run' | 'agent.handoff' | 'agent.model' | 'agent.result' | 'agent.tool' | 'call.handoff' | 'call.lifecycle' | 'client.barge_in' | 'client.live_latency' | 'client.reconnect' | 'session.error' | 'turn.assistant' | 'turn.committed' | 'turn.cost' | 'turn_latency.stage' | 'turn.transcript' | 'workflow.contract';
2
3
  export type VoiceTraceEvent<TPayload extends Record<string, unknown> = Record<string, unknown>> = {
3
4
  at: number;
@@ -101,6 +102,22 @@ export type VoiceTraceHTTPSinkOptions<TBody extends Record<string, unknown> = Re
101
102
  timeoutMs?: number;
102
103
  url: string;
103
104
  };
105
+ export type VoiceS3TraceSinkFile = {
106
+ write: (data: string, options?: BlobPropertyBag) => Promise<number> | number;
107
+ };
108
+ export type VoiceS3TraceSinkClient = Pick<S3Client, 'file'>;
109
+ export type VoiceTraceS3SinkOptions<TBody extends Record<string, unknown> = Record<string, unknown>> = S3Options & {
110
+ body?: (input: {
111
+ events: StoredVoiceTraceEvent[];
112
+ key: string;
113
+ }) => Promise<TBody> | TBody;
114
+ client?: VoiceS3TraceSinkClient;
115
+ contentType?: string;
116
+ eventTypes?: VoiceTraceEventType[];
117
+ id: string;
118
+ keyPrefix?: string;
119
+ kind?: string;
120
+ };
104
121
  export type VoiceTraceSinkStoreOptions<TEvent extends StoredVoiceTraceEvent = StoredVoiceTraceEvent> = {
105
122
  awaitDelivery?: boolean;
106
123
  deliveryQueue?: VoiceTraceSinkDeliveryStore;
@@ -187,6 +204,7 @@ export declare const filterVoiceTraceEvents: <TEvent extends StoredVoiceTraceEve
187
204
  export declare const selectVoiceTraceEventsForPrune: <TEvent extends StoredVoiceTraceEvent = StoredVoiceTraceEvent>(events: TEvent[], options?: Omit<VoiceTracePruneOptions, "store">) => TEvent[];
188
205
  export declare const pruneVoiceTraceEvents: (options: VoiceTracePruneOptions) => Promise<VoiceTracePruneResult>;
189
206
  export declare const createVoiceTraceHTTPSink: <TBody extends Record<string, unknown> = Record<string, unknown>>(options: VoiceTraceHTTPSinkOptions<TBody>) => VoiceTraceSink;
207
+ export declare const createVoiceTraceS3Sink: <TBody extends Record<string, unknown> = Record<string, unknown>>(options: VoiceTraceS3SinkOptions<TBody>) => VoiceTraceSink;
190
208
  export declare const deliverVoiceTraceEventsToSinks: (input: {
191
209
  events: StoredVoiceTraceEvent[];
192
210
  redact?: VoiceTraceRedactionConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.144",
3
+ "version": "0.0.22-beta.146",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",