@absolutejs/voice 0.0.21 → 0.0.22-beta.1

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.
Files changed (49) hide show
  1. package/README.md +1046 -2
  2. package/dist/agent.d.ts +113 -0
  3. package/dist/angular/index.js +90 -0
  4. package/dist/angular/voice-controller.service.d.ts +6 -0
  5. package/dist/angular/voice-stream.service.d.ts +6 -0
  6. package/dist/client/actions.d.ts +41 -0
  7. package/dist/client/audioPlayer.d.ts +40 -0
  8. package/dist/client/duplex.d.ts +3 -0
  9. package/dist/client/htmxBootstrap.js +84 -0
  10. package/dist/client/index.d.ts +2 -0
  11. package/dist/client/index.js +507 -5
  12. package/dist/correction.d.ts +18 -1
  13. package/dist/fileStore.d.ts +37 -0
  14. package/dist/index.d.ts +32 -1
  15. package/dist/index.js +8379 -1245
  16. package/dist/ops.d.ts +327 -0
  17. package/dist/opsPresets.d.ts +19 -0
  18. package/dist/opsRuntime.d.ts +66 -0
  19. package/dist/opsSinks.d.ts +149 -0
  20. package/dist/outcomeRecipes.d.ts +18 -0
  21. package/dist/postgresStore.d.ts +31 -0
  22. package/dist/queue.d.ts +276 -0
  23. package/dist/react/index.js +86 -0
  24. package/dist/react/useVoiceController.d.ts +6 -0
  25. package/dist/react/useVoiceStream.d.ts +6 -0
  26. package/dist/routing.d.ts +3 -0
  27. package/dist/runtimeOps.d.ts +23 -0
  28. package/dist/s3Store.d.ts +14 -0
  29. package/dist/sqliteStore.d.ts +26 -0
  30. package/dist/svelte/index.js +84 -0
  31. package/dist/telephony/response.d.ts +7 -0
  32. package/dist/telephony/twilio.d.ts +116 -0
  33. package/dist/testing/benchmark.d.ts +59 -4
  34. package/dist/testing/corrected.d.ts +41 -0
  35. package/dist/testing/duplex.d.ts +59 -0
  36. package/dist/testing/fixtures.d.ts +18 -2
  37. package/dist/testing/index.d.ts +5 -0
  38. package/dist/testing/index.js +5094 -284
  39. package/dist/testing/review.d.ts +143 -0
  40. package/dist/testing/sessionBenchmark.d.ts +25 -0
  41. package/dist/testing/stt.d.ts +2 -1
  42. package/dist/testing/telephony.d.ts +70 -0
  43. package/dist/testing/tts.d.ts +73 -0
  44. package/dist/trace.d.ts +236 -0
  45. package/dist/types.d.ts +320 -3
  46. package/dist/vue/index.js +90 -0
  47. package/dist/vue/useVoiceController.d.ts +11 -0
  48. package/dist/vue/useVoiceStream.d.ts +11 -0
  49. package/package.json +115 -1
@@ -0,0 +1,276 @@
1
+ import type { RedisClient } from 'bun';
2
+ import type { VoiceIntegrationSink } from './opsSinks';
3
+ import type { VoiceTraceRedactionConfig, VoiceTraceSink, VoiceTraceSinkDeliveryRecord, VoiceTraceSinkDeliveryStore, VoiceTraceSinkDeliveryQueueStatus } from './trace';
4
+ import type { VoiceOpsTaskPriority, StoredVoiceOpsTask, StoredVoiceIntegrationEvent, VoiceIntegrationDeliveryStatus, VoiceIntegrationEventStore, VoiceIntegrationWebhookConfig, VoiceOpsTaskKind, VoiceOpsTaskStatus, VoiceOpsTaskStore } from './ops';
5
+ export type VoiceOpsTaskLease = {
6
+ expiresAt: number;
7
+ taskId: string;
8
+ workerId: string;
9
+ };
10
+ export type VoiceRedisTaskLeaseClient = Pick<RedisClient, 'get' | 'send' | 'set'>;
11
+ export type VoiceRedisTaskLeaseCoordinatorOptions = {
12
+ client?: VoiceRedisTaskLeaseClient;
13
+ keyPrefix?: string;
14
+ url?: string;
15
+ };
16
+ export type VoiceRedisTaskLeaseCoordinator = {
17
+ claim: (input: {
18
+ leaseMs: number;
19
+ taskId: string;
20
+ workerId: string;
21
+ }) => Promise<boolean>;
22
+ get: (taskId: string) => Promise<VoiceOpsTaskLease | null>;
23
+ release: (input: {
24
+ taskId: string;
25
+ workerId: string;
26
+ }) => Promise<boolean>;
27
+ renew: (input: {
28
+ leaseMs: number;
29
+ taskId: string;
30
+ workerId: string;
31
+ }) => Promise<boolean>;
32
+ };
33
+ export type VoiceIdempotencyStore = {
34
+ has: (key: string) => Promise<boolean> | boolean;
35
+ remove: (key: string) => Promise<void> | void;
36
+ set: (key: string, input?: {
37
+ ttlSeconds?: number;
38
+ }) => Promise<void> | void;
39
+ };
40
+ export type VoiceRedisIdempotencyClient = Pick<RedisClient, 'del' | 'exists' | 'set'>;
41
+ export type VoiceRedisIdempotencyStoreOptions = {
42
+ client?: VoiceRedisIdempotencyClient;
43
+ keyPrefix?: string;
44
+ ttlSeconds?: number;
45
+ url?: string;
46
+ };
47
+ export type VoiceWebhookDeliveryWorkerOptions<TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent> = {
48
+ deadLetters?: VoiceIntegrationEventStore<TEvent>;
49
+ events: VoiceIntegrationEventStore<TEvent>;
50
+ idempotency?: VoiceIdempotencyStore;
51
+ idempotencyTtlSeconds?: number;
52
+ leaseMs?: number;
53
+ leases: VoiceRedisTaskLeaseCoordinator;
54
+ maxFailures?: number;
55
+ onDeadLetter?: (event: TEvent) => Promise<void> | void;
56
+ statuses?: VoiceIntegrationDeliveryStatus[];
57
+ webhook: VoiceIntegrationWebhookConfig;
58
+ workerId: string;
59
+ };
60
+ export type VoiceWebhookDeliveryWorkerResult = {
61
+ alreadyProcessed: number;
62
+ attempted: number;
63
+ deadLettered: number;
64
+ delivered: number;
65
+ failed: number;
66
+ skipped: number;
67
+ };
68
+ export type VoiceIntegrationEventQueueSummary = {
69
+ byType: Array<[StoredVoiceIntegrationEvent['type'], number]>;
70
+ deadLettered: number;
71
+ delivered: number;
72
+ failed: number;
73
+ pending: number;
74
+ retryEligible: number;
75
+ skipped: number;
76
+ total: number;
77
+ };
78
+ export type VoiceWebhookDeliveryWorkerLoopOptions<TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent> = {
79
+ onError?: (error: unknown) => Promise<void> | void;
80
+ pollIntervalMs?: number;
81
+ worker: ReturnType<typeof createVoiceWebhookDeliveryWorker<TEvent>>;
82
+ };
83
+ export type VoiceWebhookDeliveryWorkerLoop = {
84
+ isRunning: () => boolean;
85
+ start: () => void;
86
+ stop: () => void;
87
+ tick: () => Promise<VoiceWebhookDeliveryWorkerResult>;
88
+ };
89
+ export type VoiceIntegrationSinkWorkerOptions<TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent> = {
90
+ deadLetters?: VoiceIntegrationEventStore<TEvent>;
91
+ events: VoiceIntegrationEventStore<TEvent>;
92
+ idempotency?: VoiceIdempotencyStore;
93
+ idempotencyTtlSeconds?: number;
94
+ leaseMs?: number;
95
+ leases: VoiceRedisTaskLeaseCoordinator;
96
+ maxFailures?: number;
97
+ onDeadLetter?: (event: TEvent) => Promise<void> | void;
98
+ sinks: VoiceIntegrationSink[];
99
+ workerId: string;
100
+ };
101
+ export type VoiceIntegrationSinkWorkerResult = {
102
+ alreadyProcessed: number;
103
+ attempted: number;
104
+ deadLettered: number;
105
+ delivered: number;
106
+ failed: number;
107
+ skipped: number;
108
+ };
109
+ export type VoiceIntegrationSinkWorkerLoopOptions<TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent> = {
110
+ onError?: (error: unknown) => Promise<void> | void;
111
+ pollIntervalMs?: number;
112
+ worker: ReturnType<typeof createVoiceIntegrationSinkWorker<TEvent>>;
113
+ };
114
+ export type VoiceIntegrationSinkWorkerLoop = {
115
+ isRunning: () => boolean;
116
+ start: () => void;
117
+ stop: () => void;
118
+ tick: () => Promise<VoiceIntegrationSinkWorkerResult>;
119
+ };
120
+ export type VoiceTraceSinkDeliveryWorkerOptions<TDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord> = {
121
+ deadLetters?: VoiceTraceSinkDeliveryStore<TDelivery>;
122
+ deliveries: VoiceTraceSinkDeliveryStore<TDelivery>;
123
+ idempotency?: VoiceIdempotencyStore;
124
+ idempotencyTtlSeconds?: number;
125
+ leaseMs?: number;
126
+ leases: VoiceRedisTaskLeaseCoordinator;
127
+ maxFailures?: number;
128
+ onDeadLetter?: (delivery: TDelivery) => Promise<void> | void;
129
+ redact?: VoiceTraceRedactionConfig;
130
+ sinks: VoiceTraceSink[];
131
+ statuses?: VoiceTraceSinkDeliveryQueueStatus[];
132
+ workerId: string;
133
+ };
134
+ export type VoiceTraceSinkDeliveryWorkerResult = {
135
+ alreadyProcessed: number;
136
+ attempted: number;
137
+ deadLettered: number;
138
+ delivered: number;
139
+ failed: number;
140
+ skipped: number;
141
+ };
142
+ export type VoiceTraceSinkDeliveryWorkerLoopOptions<TDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord> = {
143
+ onError?: (error: unknown) => Promise<void> | void;
144
+ pollIntervalMs?: number;
145
+ worker: ReturnType<typeof createVoiceTraceSinkDeliveryWorker<TDelivery>>;
146
+ };
147
+ export type VoiceTraceSinkDeliveryWorkerLoop = {
148
+ isRunning: () => boolean;
149
+ start: () => void;
150
+ stop: () => void;
151
+ tick: () => Promise<VoiceTraceSinkDeliveryWorkerResult>;
152
+ };
153
+ export type VoiceTraceSinkDeliveryQueueSummary = {
154
+ deadLettered: number;
155
+ delivered: number;
156
+ failed: number;
157
+ pending: number;
158
+ retryEligible: number;
159
+ skipped: number;
160
+ total: number;
161
+ };
162
+ export type VoiceOpsTaskWorkerOptions<TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask> = {
163
+ leaseMs?: number;
164
+ leases: VoiceRedisTaskLeaseCoordinator;
165
+ tasks: VoiceOpsTaskStore<TTask>;
166
+ workerId: string;
167
+ };
168
+ export type VoiceOpsTaskClaimFilters = {
169
+ assignee?: string;
170
+ excludeTaskIds?: string[];
171
+ kinds?: VoiceOpsTaskKind[];
172
+ };
173
+ export type VoiceOpsTaskWorker<TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask> = {
174
+ assign: (taskId: string, assignee: string, input?: {
175
+ actor?: string;
176
+ at?: number;
177
+ }) => Promise<TTask>;
178
+ claimNext: (filters?: VoiceOpsTaskClaimFilters) => Promise<TTask | null>;
179
+ complete: (taskId: string, input?: {
180
+ actor?: string;
181
+ at?: number;
182
+ detail?: string;
183
+ }) => Promise<TTask>;
184
+ heartbeat: (taskId: string, input?: {
185
+ actor?: string;
186
+ at?: number;
187
+ detail?: string;
188
+ leaseMs?: number;
189
+ }) => Promise<TTask>;
190
+ requeue: (taskId: string, input?: {
191
+ actor?: string;
192
+ at?: number;
193
+ detail?: string;
194
+ }) => Promise<TTask>;
195
+ release: (taskId: string) => Promise<boolean>;
196
+ };
197
+ export type VoiceOpsTaskQueueSummary = {
198
+ byAssignee: Array<[string, number]>;
199
+ byClaimedBy: Array<[string, number]>;
200
+ claimed: number;
201
+ deadLettered: number;
202
+ byKind: Array<[VoiceOpsTaskKind, number]>;
203
+ byPriority: Array<[VoiceOpsTaskPriority, number]>;
204
+ byStatus: Array<[VoiceOpsTaskStatus, number]>;
205
+ failed: number;
206
+ inProgress: number;
207
+ open: number;
208
+ overdue: number;
209
+ retryEligible: number;
210
+ total: number;
211
+ unclaimed: number;
212
+ };
213
+ export type VoiceOpsTaskProcessorWorkerOptions<TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask> = {
214
+ deadLetters?: VoiceOpsTaskStore<TTask>;
215
+ filters?: VoiceOpsTaskClaimFilters;
216
+ leaseMs?: number;
217
+ maxFailures?: number;
218
+ onDeadLetter?: (task: TTask) => Promise<void> | void;
219
+ onError?: (error: unknown, task: TTask) => Promise<void> | void;
220
+ process: (task: TTask) => Promise<void | 'complete' | 'requeue' | {
221
+ action?: 'complete' | 'requeue';
222
+ detail?: string;
223
+ }> | void | 'complete' | 'requeue' | {
224
+ action?: 'complete' | 'requeue';
225
+ detail?: string;
226
+ };
227
+ tasks: VoiceOpsTaskStore<TTask>;
228
+ worker: VoiceOpsTaskWorker<TTask>;
229
+ };
230
+ export type VoiceOpsTaskProcessorWorkerResult = {
231
+ attempted: number;
232
+ completed: number;
233
+ deadLettered: number;
234
+ failed: number;
235
+ idle: number;
236
+ requeued: number;
237
+ };
238
+ export type VoiceOpsTaskProcessorWorkerLoopOptions<TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask> = {
239
+ onError?: (error: unknown) => Promise<void> | void;
240
+ pollIntervalMs?: number;
241
+ worker: ReturnType<typeof createVoiceOpsTaskProcessorWorker<TTask>>;
242
+ };
243
+ export type VoiceOpsTaskProcessorWorkerLoop = {
244
+ isRunning: () => boolean;
245
+ start: () => void;
246
+ stop: () => void;
247
+ tick: () => Promise<VoiceOpsTaskProcessorWorkerResult>;
248
+ };
249
+ export declare const summarizeVoiceIntegrationEvents: <TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent>(events: TEvent[], input?: {
250
+ deadLetters?: VoiceIntegrationEventStore<TEvent>;
251
+ }) => Promise<VoiceIntegrationEventQueueSummary> | VoiceIntegrationEventQueueSummary;
252
+ export declare const summarizeVoiceTraceSinkDeliveries: <TDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord>(deliveries: TDelivery[], input?: {
253
+ deadLetters?: VoiceTraceSinkDeliveryStore<TDelivery>;
254
+ }) => Promise<VoiceTraceSinkDeliveryQueueSummary> | VoiceTraceSinkDeliveryQueueSummary;
255
+ export declare const summarizeVoiceOpsTaskQueue: <TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask>(tasks: TTask[], input?: {
256
+ deadLetters?: VoiceOpsTaskStore<TTask>;
257
+ }) => Promise<VoiceOpsTaskQueueSummary> | VoiceOpsTaskQueueSummary;
258
+ export declare const createVoiceRedisTaskLeaseCoordinator: (options?: VoiceRedisTaskLeaseCoordinatorOptions) => VoiceRedisTaskLeaseCoordinator;
259
+ export declare const createVoiceRedisIdempotencyStore: (options?: VoiceRedisIdempotencyStoreOptions) => VoiceIdempotencyStore;
260
+ export declare const createVoiceWebhookDeliveryWorker: <TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent>(options: VoiceWebhookDeliveryWorkerOptions<TEvent>) => {
261
+ drain: () => Promise<VoiceWebhookDeliveryWorkerResult>;
262
+ };
263
+ export declare const createVoiceWebhookDeliveryWorkerLoop: <TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent>(options: VoiceWebhookDeliveryWorkerLoopOptions<TEvent>) => VoiceWebhookDeliveryWorkerLoop;
264
+ export declare const createVoiceIntegrationSinkWorker: <TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent>(options: VoiceIntegrationSinkWorkerOptions<TEvent>) => {
265
+ drain: () => Promise<VoiceIntegrationSinkWorkerResult>;
266
+ };
267
+ export declare const createVoiceIntegrationSinkWorkerLoop: <TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent>(options: VoiceIntegrationSinkWorkerLoopOptions<TEvent>) => VoiceIntegrationSinkWorkerLoop;
268
+ export declare const createVoiceTraceSinkDeliveryWorker: <TDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord>(options: VoiceTraceSinkDeliveryWorkerOptions<TDelivery>) => {
269
+ drain: () => Promise<VoiceTraceSinkDeliveryWorkerResult>;
270
+ };
271
+ export declare const createVoiceTraceSinkDeliveryWorkerLoop: <TDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord>(options: VoiceTraceSinkDeliveryWorkerLoopOptions<TDelivery>) => VoiceTraceSinkDeliveryWorkerLoop;
272
+ export declare const createVoiceOpsTaskWorker: <TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask>(options: VoiceOpsTaskWorkerOptions<TTask>) => VoiceOpsTaskWorker<TTask>;
273
+ export declare const createVoiceOpsTaskProcessorWorker: <TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask>(options: VoiceOpsTaskProcessorWorkerOptions<TTask>) => {
274
+ drain: () => Promise<VoiceOpsTaskProcessorWorkerResult>;
275
+ };
276
+ export declare const createVoiceOpsTaskProcessorWorkerLoop: <TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask>(options: VoiceOpsTaskProcessorWorkerLoopOptions<TTask>) => VoiceOpsTaskProcessorWorkerLoop;
@@ -102,6 +102,14 @@ var normalizeErrorMessage = (value) => {
102
102
  };
103
103
  var serverMessageToAction = (message) => {
104
104
  switch (message.type) {
105
+ case "audio":
106
+ return {
107
+ chunk: Uint8Array.from(atob(message.chunkBase64), (char) => char.charCodeAt(0)),
108
+ format: message.format,
109
+ receivedAt: message.receivedAt,
110
+ turnId: message.turnId,
111
+ type: "audio"
112
+ };
105
113
  case "assistant":
106
114
  return {
107
115
  text: message.text,
@@ -182,6 +190,7 @@ var isVoiceServerMessage = (value) => {
182
190
  return false;
183
191
  }
184
192
  switch (value.type) {
193
+ case "audio":
185
194
  case "assistant":
186
195
  case "complete":
187
196
  case "error":
@@ -354,6 +363,7 @@ var createVoiceConnection = (path, options = {}) => {
354
363
 
355
364
  // src/client/store.ts
356
365
  var createInitialState = () => ({
366
+ assistantAudio: [],
357
367
  assistantTexts: [],
358
368
  error: null,
359
369
  isConnected: false,
@@ -371,6 +381,20 @@ var createVoiceStreamStore = () => {
371
381
  };
372
382
  const dispatch = (action) => {
373
383
  switch (action.type) {
384
+ case "audio":
385
+ state = {
386
+ ...state,
387
+ assistantAudio: [
388
+ ...state.assistantAudio,
389
+ {
390
+ chunk: action.chunk,
391
+ format: action.format,
392
+ receivedAt: action.receivedAt,
393
+ turnId: action.turnId
394
+ }
395
+ ]
396
+ };
397
+ break;
374
398
  case "assistant":
375
399
  state = {
376
400
  ...state,
@@ -510,6 +534,9 @@ var createVoiceStream = (path, options = {}) => {
510
534
  get assistantTexts() {
511
535
  return store.getSnapshot().assistantTexts;
512
536
  },
537
+ get assistantAudio() {
538
+ return store.getSnapshot().assistantAudio;
539
+ },
513
540
  sendAudio(audio) {
514
541
  connection.sendAudio(audio);
515
542
  },
@@ -524,6 +551,7 @@ var createVoiceStream = (path, options = {}) => {
524
551
 
525
552
  // src/react/useVoiceStream.tsx
526
553
  var EMPTY_SNAPSHOT = {
554
+ assistantAudio: [],
527
555
  assistantTexts: [],
528
556
  error: null,
529
557
  isConnected: false,
@@ -915,6 +943,58 @@ var PRESET_INPUTS = {
915
943
  transcriptStabilityMs: 1650
916
944
  }
917
945
  },
946
+ "pstn-balanced": {
947
+ audioConditioning: {
948
+ enabled: true,
949
+ maxGain: 2.8,
950
+ noiseGateAttenuation: 0.07,
951
+ noiseGateThreshold: 0.005,
952
+ targetLevel: 0.08
953
+ },
954
+ capture: {
955
+ channelCount: 1,
956
+ sampleRateHz: 16000
957
+ },
958
+ connection: {
959
+ maxReconnectAttempts: 14,
960
+ pingInterval: 45000,
961
+ reconnect: true
962
+ },
963
+ sttLifecycle: "continuous",
964
+ turnDetection: {
965
+ qualityProfile: "noisy-room",
966
+ profile: "long-form",
967
+ silenceMs: 660,
968
+ speechThreshold: 0.012,
969
+ transcriptStabilityMs: 300
970
+ }
971
+ },
972
+ "pstn-fast": {
973
+ audioConditioning: {
974
+ enabled: true,
975
+ maxGain: 2.75,
976
+ noiseGateAttenuation: 0.06,
977
+ noiseGateThreshold: 0.005,
978
+ targetLevel: 0.08
979
+ },
980
+ capture: {
981
+ channelCount: 1,
982
+ sampleRateHz: 16000
983
+ },
984
+ connection: {
985
+ maxReconnectAttempts: 14,
986
+ pingInterval: 45000,
987
+ reconnect: true
988
+ },
989
+ sttLifecycle: "continuous",
990
+ turnDetection: {
991
+ qualityProfile: "noisy-room",
992
+ profile: "long-form",
993
+ silenceMs: 620,
994
+ speechThreshold: 0.012,
995
+ transcriptStabilityMs: 280
996
+ }
997
+ },
918
998
  reliability: {
919
999
  audioConditioning: {
920
1000
  enabled: true,
@@ -958,6 +1038,7 @@ var resolveVoiceRuntimePreset = (name = "default") => {
958
1038
 
959
1039
  // src/client/controller.ts
960
1040
  var createInitialState2 = (stream) => ({
1041
+ assistantAudio: [...stream.assistantAudio],
961
1042
  assistantTexts: [...stream.assistantTexts],
962
1043
  error: stream.error,
963
1044
  isConnected: stream.isConnected,
@@ -986,6 +1067,7 @@ var createVoiceController = (path, options = {}) => {
986
1067
  const sync = () => {
987
1068
  state = {
988
1069
  ...state,
1070
+ assistantAudio: [...stream.assistantAudio],
989
1071
  assistantTexts: [...stream.assistantTexts],
990
1072
  error: stream.error,
991
1073
  isConnected: stream.isConnected,
@@ -1113,12 +1195,16 @@ var createVoiceController = (path, options = {}) => {
1113
1195
  },
1114
1196
  get assistantTexts() {
1115
1197
  return state.assistantTexts;
1198
+ },
1199
+ get assistantAudio() {
1200
+ return state.assistantAudio;
1116
1201
  }
1117
1202
  };
1118
1203
  };
1119
1204
 
1120
1205
  // src/react/useVoiceController.tsx
1121
1206
  var EMPTY_SNAPSHOT2 = {
1207
+ assistantAudio: [],
1122
1208
  assistantTexts: [],
1123
1209
  error: null,
1124
1210
  isConnected: false,
@@ -13,6 +13,12 @@ export declare const useVoiceController: <TResult = unknown>(path: string, optio
13
13
  partial: string;
14
14
  turns: import("..").VoiceTurnRecord<TResult>[];
15
15
  assistantTexts: string[];
16
+ assistantAudio: Array<{
17
+ chunk: Uint8Array;
18
+ format: import("..").AudioFormat;
19
+ receivedAt: number;
20
+ turnId?: string;
21
+ }>;
16
22
  error: string | null;
17
23
  isConnected: boolean;
18
24
  isRecording: boolean;
@@ -9,6 +9,12 @@ export declare const useVoiceStream: <TResult = unknown>(path: string, options?:
9
9
  partial: string;
10
10
  turns: import("..").VoiceTurnRecord<TResult>[];
11
11
  assistantTexts: string[];
12
+ assistantAudio: Array<{
13
+ chunk: Uint8Array;
14
+ format: import("..").AudioFormat;
15
+ receivedAt: number;
16
+ turnId?: string;
17
+ }>;
12
18
  error: string | null;
13
19
  isConnected: boolean;
14
20
  };
@@ -0,0 +1,3 @@
1
+ import type { VoiceSTTRoutingCorrectionMode, VoiceSTTRoutingGoal, VoiceSTTRoutingStrategy, VoiceTurnCorrectionHandler } from './types';
2
+ export declare const resolveVoiceSTTRoutingStrategy: (goal?: VoiceSTTRoutingGoal) => VoiceSTTRoutingStrategy;
3
+ export declare const createVoiceSTTRoutingCorrectionHandler: (mode?: VoiceSTTRoutingCorrectionMode) => VoiceTurnCorrectionHandler | undefined;
@@ -0,0 +1,23 @@
1
+ import type { StoredVoiceCallReviewArtifact, VoiceCallReviewArtifact } from './testing/review';
2
+ import type { VoiceOpsTask } from './ops';
3
+ import type { VoiceCallDisposition, VoiceRuntimeOpsConfig, VoiceSessionHandle, VoiceSessionRecord } from './types';
4
+ export declare const createVoiceCallReviewFromSession: <TSession extends VoiceSessionRecord = VoiceSessionRecord>(input: {
5
+ disposition: VoiceCallDisposition;
6
+ generatedAt?: number;
7
+ reason?: string;
8
+ session: TSession;
9
+ target?: string;
10
+ }) => VoiceCallReviewArtifact;
11
+ export declare const recordVoiceRuntimeOps: <TContext, TSession extends VoiceSessionRecord, TResult>(input: {
12
+ api: VoiceSessionHandle<TContext, TSession, TResult>;
13
+ config?: VoiceRuntimeOpsConfig<TContext, TSession, TResult>;
14
+ context: TContext;
15
+ disposition: VoiceCallDisposition;
16
+ metadata?: Record<string, unknown>;
17
+ reason?: string;
18
+ session: TSession;
19
+ target?: string;
20
+ }) => Promise<{
21
+ review: StoredVoiceCallReviewArtifact | undefined;
22
+ task: VoiceOpsTask | undefined;
23
+ } | undefined>;
@@ -0,0 +1,14 @@
1
+ import type { S3Client, S3Options } from 'bun';
2
+ import type { StoredVoiceCallReviewArtifact, VoiceCallReviewStore } from './testing/review';
3
+ export type VoiceS3ReviewStoreFile = {
4
+ delete: () => Promise<void>;
5
+ exists: () => Promise<boolean>;
6
+ text: () => Promise<string>;
7
+ write: (data: string) => Promise<number>;
8
+ };
9
+ export type VoiceS3ReviewStoreClient = Pick<S3Client, 'file' | 'list'>;
10
+ export type VoiceS3ReviewStoreOptions = S3Options & {
11
+ client?: VoiceS3ReviewStoreClient;
12
+ keyPrefix?: string;
13
+ };
14
+ export declare const createVoiceS3ReviewStore: <TArtifact extends StoredVoiceCallReviewArtifact = StoredVoiceCallReviewArtifact>(options: VoiceS3ReviewStoreOptions) => VoiceCallReviewStore<TArtifact>;
@@ -0,0 +1,26 @@
1
+ import { type StoredVoiceTraceEvent, type VoiceTraceSinkDeliveryRecord, type VoiceTraceSinkDeliveryStore, type VoiceTraceEventStore } from './trace';
2
+ import type { StoredVoiceIntegrationEvent, StoredVoiceExternalObjectMap, StoredVoiceOpsTask, VoiceExternalObjectMapStore, VoiceIntegrationEventStore, VoiceOpsTaskStore } from './ops';
3
+ import type { StoredVoiceCallReviewArtifact, VoiceCallReviewStore } from './testing/review';
4
+ import type { VoiceSessionRecord, VoiceSessionStore } from './types';
5
+ export type VoiceSQLiteStoreOptions = {
6
+ path: string;
7
+ tableName?: string;
8
+ tablePrefix?: string;
9
+ };
10
+ export type VoiceSQLiteRuntimeStorage<TSession extends VoiceSessionRecord = VoiceSessionRecord, TReview extends StoredVoiceCallReviewArtifact = StoredVoiceCallReviewArtifact, TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask, TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent, TMapping extends StoredVoiceExternalObjectMap = StoredVoiceExternalObjectMap, TTrace extends StoredVoiceTraceEvent = StoredVoiceTraceEvent, TTraceDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord> = {
11
+ events: VoiceIntegrationEventStore<TEvent>;
12
+ externalObjects: VoiceExternalObjectMapStore<TMapping>;
13
+ reviews: VoiceCallReviewStore<TReview>;
14
+ session: VoiceSessionStore<TSession>;
15
+ tasks: VoiceOpsTaskStore<TTask>;
16
+ traceDeliveries: VoiceTraceSinkDeliveryStore<TTraceDelivery>;
17
+ traces: VoiceTraceEventStore<TTrace>;
18
+ };
19
+ export declare const createVoiceSQLiteSessionStore: <TSession extends VoiceSessionRecord = VoiceSessionRecord>(options: VoiceSQLiteStoreOptions) => VoiceSessionStore<TSession>;
20
+ export declare const createVoiceSQLiteReviewStore: <TArtifact extends StoredVoiceCallReviewArtifact = StoredVoiceCallReviewArtifact>(options: VoiceSQLiteStoreOptions) => VoiceCallReviewStore<TArtifact>;
21
+ export declare const createVoiceSQLiteTaskStore: <TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask>(options: VoiceSQLiteStoreOptions) => VoiceOpsTaskStore<TTask>;
22
+ export declare const createVoiceSQLiteIntegrationEventStore: <TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent>(options: VoiceSQLiteStoreOptions) => VoiceIntegrationEventStore<TEvent>;
23
+ export declare const createVoiceSQLiteExternalObjectMapStore: <TMapping extends StoredVoiceExternalObjectMap = StoredVoiceExternalObjectMap>(options: VoiceSQLiteStoreOptions) => VoiceExternalObjectMapStore<TMapping>;
24
+ export declare const createVoiceSQLiteTraceEventStore: <TEvent extends StoredVoiceTraceEvent = StoredVoiceTraceEvent>(options: VoiceSQLiteStoreOptions) => VoiceTraceEventStore<TEvent>;
25
+ export declare const createVoiceSQLiteTraceSinkDeliveryStore: <TDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord>(options: VoiceSQLiteStoreOptions) => VoiceTraceSinkDeliveryStore<TDelivery>;
26
+ export declare const createVoiceSQLiteRuntimeStorage: <TSession extends VoiceSessionRecord = VoiceSessionRecord, TReview extends StoredVoiceCallReviewArtifact = StoredVoiceCallReviewArtifact, TTask extends StoredVoiceOpsTask = StoredVoiceOpsTask, TEvent extends StoredVoiceIntegrationEvent = StoredVoiceIntegrationEvent, TMapping extends StoredVoiceExternalObjectMap = StoredVoiceExternalObjectMap, TTrace extends StoredVoiceTraceEvent = StoredVoiceTraceEvent, TTraceDelivery extends VoiceTraceSinkDeliveryRecord = VoiceTraceSinkDeliveryRecord>(options: VoiceSQLiteStoreOptions) => VoiceSQLiteRuntimeStorage<TSession, TReview, TTask, TEvent, TMapping, TTrace, TTraceDelivery>;
@@ -99,6 +99,14 @@ var normalizeErrorMessage = (value) => {
99
99
  };
100
100
  var serverMessageToAction = (message) => {
101
101
  switch (message.type) {
102
+ case "audio":
103
+ return {
104
+ chunk: Uint8Array.from(atob(message.chunkBase64), (char) => char.charCodeAt(0)),
105
+ format: message.format,
106
+ receivedAt: message.receivedAt,
107
+ turnId: message.turnId,
108
+ type: "audio"
109
+ };
102
110
  case "assistant":
103
111
  return {
104
112
  text: message.text,
@@ -179,6 +187,7 @@ var isVoiceServerMessage = (value) => {
179
187
  return false;
180
188
  }
181
189
  switch (value.type) {
190
+ case "audio":
182
191
  case "assistant":
183
192
  case "complete":
184
193
  case "error":
@@ -351,6 +360,7 @@ var createVoiceConnection = (path, options = {}) => {
351
360
 
352
361
  // src/client/store.ts
353
362
  var createInitialState = () => ({
363
+ assistantAudio: [],
354
364
  assistantTexts: [],
355
365
  error: null,
356
366
  isConnected: false,
@@ -368,6 +378,20 @@ var createVoiceStreamStore = () => {
368
378
  };
369
379
  const dispatch = (action) => {
370
380
  switch (action.type) {
381
+ case "audio":
382
+ state = {
383
+ ...state,
384
+ assistantAudio: [
385
+ ...state.assistantAudio,
386
+ {
387
+ chunk: action.chunk,
388
+ format: action.format,
389
+ receivedAt: action.receivedAt,
390
+ turnId: action.turnId
391
+ }
392
+ ]
393
+ };
394
+ break;
371
395
  case "assistant":
372
396
  state = {
373
397
  ...state,
@@ -507,6 +531,9 @@ var createVoiceStream = (path, options = {}) => {
507
531
  get assistantTexts() {
508
532
  return store.getSnapshot().assistantTexts;
509
533
  },
534
+ get assistantAudio() {
535
+ return store.getSnapshot().assistantAudio;
536
+ },
510
537
  sendAudio(audio) {
511
538
  connection.sendAudio(audio);
512
539
  },
@@ -886,6 +913,58 @@ var PRESET_INPUTS = {
886
913
  transcriptStabilityMs: 1650
887
914
  }
888
915
  },
916
+ "pstn-balanced": {
917
+ audioConditioning: {
918
+ enabled: true,
919
+ maxGain: 2.8,
920
+ noiseGateAttenuation: 0.07,
921
+ noiseGateThreshold: 0.005,
922
+ targetLevel: 0.08
923
+ },
924
+ capture: {
925
+ channelCount: 1,
926
+ sampleRateHz: 16000
927
+ },
928
+ connection: {
929
+ maxReconnectAttempts: 14,
930
+ pingInterval: 45000,
931
+ reconnect: true
932
+ },
933
+ sttLifecycle: "continuous",
934
+ turnDetection: {
935
+ qualityProfile: "noisy-room",
936
+ profile: "long-form",
937
+ silenceMs: 660,
938
+ speechThreshold: 0.012,
939
+ transcriptStabilityMs: 300
940
+ }
941
+ },
942
+ "pstn-fast": {
943
+ audioConditioning: {
944
+ enabled: true,
945
+ maxGain: 2.75,
946
+ noiseGateAttenuation: 0.06,
947
+ noiseGateThreshold: 0.005,
948
+ targetLevel: 0.08
949
+ },
950
+ capture: {
951
+ channelCount: 1,
952
+ sampleRateHz: 16000
953
+ },
954
+ connection: {
955
+ maxReconnectAttempts: 14,
956
+ pingInterval: 45000,
957
+ reconnect: true
958
+ },
959
+ sttLifecycle: "continuous",
960
+ turnDetection: {
961
+ qualityProfile: "noisy-room",
962
+ profile: "long-form",
963
+ silenceMs: 620,
964
+ speechThreshold: 0.012,
965
+ transcriptStabilityMs: 280
966
+ }
967
+ },
889
968
  reliability: {
890
969
  audioConditioning: {
891
970
  enabled: true,
@@ -929,6 +1008,7 @@ var resolveVoiceRuntimePreset = (name = "default") => {
929
1008
 
930
1009
  // src/client/controller.ts
931
1010
  var createInitialState2 = (stream) => ({
1011
+ assistantAudio: [...stream.assistantAudio],
932
1012
  assistantTexts: [...stream.assistantTexts],
933
1013
  error: stream.error,
934
1014
  isConnected: stream.isConnected,
@@ -957,6 +1037,7 @@ var createVoiceController = (path, options = {}) => {
957
1037
  const sync = () => {
958
1038
  state = {
959
1039
  ...state,
1040
+ assistantAudio: [...stream.assistantAudio],
960
1041
  assistantTexts: [...stream.assistantTexts],
961
1042
  error: stream.error,
962
1043
  isConnected: stream.isConnected,
@@ -1084,6 +1165,9 @@ var createVoiceController = (path, options = {}) => {
1084
1165
  },
1085
1166
  get assistantTexts() {
1086
1167
  return state.assistantTexts;
1168
+ },
1169
+ get assistantAudio() {
1170
+ return state.assistantAudio;
1087
1171
  }
1088
1172
  };
1089
1173
  };
@@ -0,0 +1,7 @@
1
+ export type TelephonyResponseShapeMode = 'full' | 'lead-clause';
2
+ export type TelephonyResponseShapeOptions = {
3
+ mode?: TelephonyResponseShapeMode;
4
+ maxChars?: number;
5
+ maxWords?: number;
6
+ };
7
+ export declare const shapeTelephonyAssistantText: (text: string, options?: TelephonyResponseShapeOptions) => string;