@absolutejs/voice 0.0.22-beta.126 → 0.0.22-beta.128

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 (47) hide show
  1. package/README.md +255 -26
  2. package/dist/agent.d.ts +5 -0
  3. package/dist/angular/index.d.ts +1 -1
  4. package/dist/angular/index.js +16 -16
  5. package/dist/angular/voice-ops-status.service.d.ts +12 -0
  6. package/dist/audit.d.ts +128 -0
  7. package/dist/auditDeliveryRoutes.d.ts +85 -0
  8. package/dist/auditExport.d.ts +34 -0
  9. package/dist/auditRoutes.d.ts +66 -0
  10. package/dist/auditSinks.d.ts +133 -0
  11. package/dist/client/index.d.ts +2 -2
  12. package/dist/client/index.js +11 -11
  13. package/dist/client/opsStatus.d.ts +19 -0
  14. package/dist/client/opsStatusWidget.d.ts +7 -7
  15. package/dist/dataControl.d.ts +47 -0
  16. package/dist/demoReadyRoutes.d.ts +98 -0
  17. package/dist/fileStore.d.ts +8 -2
  18. package/dist/index.d.ts +27 -6
  19. package/dist/index.js +15193 -12626
  20. package/dist/openaiRealtime.d.ts +27 -0
  21. package/dist/opsStatus.d.ts +65 -0
  22. package/dist/opsStatusRoutes.d.ts +33 -0
  23. package/dist/phoneAgent.d.ts +4 -0
  24. package/dist/phoneAgentProductionSmoke.d.ts +115 -0
  25. package/dist/postgresStore.d.ts +8 -2
  26. package/dist/productionReadiness.d.ts +82 -0
  27. package/dist/react/index.d.ts +1 -1
  28. package/dist/react/index.js +16 -16
  29. package/dist/react/useVoiceOpsStatus.d.ts +8 -0
  30. package/dist/sqliteStore.d.ts +8 -2
  31. package/dist/svelte/createVoiceOpsStatus.d.ts +2 -2
  32. package/dist/svelte/index.d.ts +0 -1
  33. package/dist/svelte/index.js +72 -75
  34. package/dist/telephony/twilio.d.ts +3 -2
  35. package/dist/testing/index.js +74 -21
  36. package/dist/traceDeliveryRoutes.d.ts +86 -0
  37. package/dist/types.d.ts +6 -2
  38. package/dist/vue/index.d.ts +1 -1
  39. package/dist/vue/index.js +15 -15
  40. package/dist/vue/useVoiceOpsStatus.d.ts +9 -0
  41. package/package.json +1 -1
  42. package/dist/angular/voice-app-kit-status.service.d.ts +0 -12
  43. package/dist/appKit.d.ts +0 -100
  44. package/dist/client/appKitStatus.d.ts +0 -19
  45. package/dist/react/useVoiceAppKitStatus.d.ts +0 -8
  46. package/dist/svelte/createVoiceAppKitStatus.d.ts +0 -8
  47. package/dist/vue/useVoiceAppKitStatus.d.ts +0 -9
@@ -69,16 +69,24 @@ var __decorateElement = (array, flags, name, decorators, target, extra) => {
69
69
  return k || __decoratorMetadata(array, target), desc && __defProp(target, name, desc), p ? k ^ 4 ? extra : desc : target;
70
70
  };
71
71
 
72
- // src/client/appKitStatus.ts
73
- var fetchVoiceAppKitStatus = async (path = "/app-kit/status", options = {}) => {
72
+ // src/client/campaignDialerProof.ts
73
+ var fetchVoiceCampaignDialerProofStatus = async (path = "/api/voice/campaigns/dialer-proof", options = {}) => {
74
74
  const fetchImpl = options.fetch ?? globalThis.fetch;
75
75
  const response = await fetchImpl(path);
76
76
  if (!response.ok) {
77
- throw new Error(`Voice app kit status failed: HTTP ${response.status}`);
77
+ throw new Error(`Voice campaign dialer proof status failed: HTTP ${response.status}`);
78
78
  }
79
79
  return await response.json();
80
80
  };
81
- var createVoiceAppKitStatusStore = (path = "/app-kit/status", options = {}) => {
81
+ var runVoiceCampaignDialerProofAction = async (path = "/api/voice/campaigns/dialer-proof", options = {}) => {
82
+ const fetchImpl = options.fetch ?? globalThis.fetch;
83
+ const response = await fetchImpl(path, { method: "POST" });
84
+ if (!response.ok) {
85
+ throw new Error(`Voice campaign dialer proof failed: HTTP ${response.status}`);
86
+ }
87
+ return await response.json();
88
+ };
89
+ var createVoiceCampaignDialerProofStore = (path = "/api/voice/campaigns/dialer-proof", options = {}) => {
82
90
  const listeners = new Set;
83
91
  let closed = false;
84
92
  let timer;
@@ -93,20 +101,50 @@ var createVoiceAppKitStatusStore = (path = "/app-kit/status", options = {}) => {
93
101
  };
94
102
  const refresh = async () => {
95
103
  if (closed) {
96
- return snapshot.report;
104
+ return snapshot.status;
97
105
  }
98
- snapshot = {
99
- ...snapshot,
100
- error: null,
101
- isLoading: true
102
- };
106
+ snapshot = { ...snapshot, error: null, isLoading: true };
107
+ emit();
108
+ try {
109
+ const status = await fetchVoiceCampaignDialerProofStatus(path, options);
110
+ snapshot = {
111
+ ...snapshot,
112
+ error: null,
113
+ isLoading: false,
114
+ status,
115
+ updatedAt: Date.now()
116
+ };
117
+ emit();
118
+ return status;
119
+ } catch (error) {
120
+ snapshot = {
121
+ ...snapshot,
122
+ error: error instanceof Error ? error.message : String(error),
123
+ isLoading: false
124
+ };
125
+ emit();
126
+ throw error;
127
+ }
128
+ };
129
+ const runProof = async () => {
130
+ const runPath = options.runPath ?? snapshot.status?.runPath ?? path;
131
+ snapshot = { ...snapshot, error: null, isLoading: true };
103
132
  emit();
104
133
  try {
105
- const report = await fetchVoiceAppKitStatus(path, options);
134
+ const report = await runVoiceCampaignDialerProofAction(runPath, options);
106
135
  snapshot = {
136
+ ...snapshot,
107
137
  error: null,
108
138
  isLoading: false,
109
139
  report,
140
+ status: {
141
+ generatedAt: Date.now(),
142
+ mode: report.mode,
143
+ ok: report.ok,
144
+ providers: report.providers.map((provider) => provider.provider),
145
+ runPath,
146
+ safe: true
147
+ },
110
148
  updatedAt: Date.now()
111
149
  };
112
150
  emit();
@@ -129,7 +167,7 @@ var createVoiceAppKitStatusStore = (path = "/app-kit/status", options = {}) => {
129
167
  }
130
168
  listeners.clear();
131
169
  };
132
- if (typeof window !== "undefined" && options.intervalMs && options.intervalMs > 0) {
170
+ if (options.intervalMs && options.intervalMs > 0) {
133
171
  timer = setInterval(() => {
134
172
  refresh().catch(() => {});
135
173
  }, options.intervalMs);
@@ -139,6 +177,7 @@ var createVoiceAppKitStatusStore = (path = "/app-kit/status", options = {}) => {
139
177
  getServerSnapshot: () => snapshot,
140
178
  getSnapshot: () => snapshot,
141
179
  refresh,
180
+ runProof,
142
181
  subscribe: (listener) => {
143
182
  listeners.add(listener);
144
183
  return () => {
@@ -148,26 +187,18 @@ var createVoiceAppKitStatusStore = (path = "/app-kit/status", options = {}) => {
148
187
  };
149
188
  };
150
189
 
151
- // src/svelte/createVoiceAppKitStatus.ts
152
- var createVoiceAppKitStatus = (path = "/app-kit/status", options = {}) => createVoiceAppKitStatusStore(path, options);
153
- // src/client/campaignDialerProof.ts
154
- var fetchVoiceCampaignDialerProofStatus = async (path = "/api/voice/campaigns/dialer-proof", options = {}) => {
190
+ // src/svelte/createVoiceCampaignDialerProof.ts
191
+ var createVoiceCampaignDialerProof = (path = "/api/voice/campaigns/dialer-proof", options = {}) => createVoiceCampaignDialerProofStore(path, options);
192
+ // src/client/opsStatus.ts
193
+ var fetchVoiceOpsStatus = async (path = "/api/voice/ops-status", options = {}) => {
155
194
  const fetchImpl = options.fetch ?? globalThis.fetch;
156
195
  const response = await fetchImpl(path);
157
196
  if (!response.ok) {
158
- throw new Error(`Voice campaign dialer proof status failed: HTTP ${response.status}`);
197
+ throw new Error(`Voice ops status failed: HTTP ${response.status}`);
159
198
  }
160
199
  return await response.json();
161
200
  };
162
- var runVoiceCampaignDialerProofAction = async (path = "/api/voice/campaigns/dialer-proof", options = {}) => {
163
- const fetchImpl = options.fetch ?? globalThis.fetch;
164
- const response = await fetchImpl(path, { method: "POST" });
165
- if (!response.ok) {
166
- throw new Error(`Voice campaign dialer proof failed: HTTP ${response.status}`);
167
- }
168
- return await response.json();
169
- };
170
- var createVoiceCampaignDialerProofStore = (path = "/api/voice/campaigns/dialer-proof", options = {}) => {
201
+ var createVoiceOpsStatusStore = (path = "/api/voice/ops-status", options = {}) => {
171
202
  const listeners = new Set;
172
203
  let closed = false;
173
204
  let timer;
@@ -182,50 +213,20 @@ var createVoiceCampaignDialerProofStore = (path = "/api/voice/campaigns/dialer-p
182
213
  };
183
214
  const refresh = async () => {
184
215
  if (closed) {
185
- return snapshot.status;
186
- }
187
- snapshot = { ...snapshot, error: null, isLoading: true };
188
- emit();
189
- try {
190
- const status = await fetchVoiceCampaignDialerProofStatus(path, options);
191
- snapshot = {
192
- ...snapshot,
193
- error: null,
194
- isLoading: false,
195
- status,
196
- updatedAt: Date.now()
197
- };
198
- emit();
199
- return status;
200
- } catch (error) {
201
- snapshot = {
202
- ...snapshot,
203
- error: error instanceof Error ? error.message : String(error),
204
- isLoading: false
205
- };
206
- emit();
207
- throw error;
216
+ return snapshot.report;
208
217
  }
209
- };
210
- const runProof = async () => {
211
- const runPath = options.runPath ?? snapshot.status?.runPath ?? path;
212
- snapshot = { ...snapshot, error: null, isLoading: true };
218
+ snapshot = {
219
+ ...snapshot,
220
+ error: null,
221
+ isLoading: true
222
+ };
213
223
  emit();
214
224
  try {
215
- const report = await runVoiceCampaignDialerProofAction(runPath, options);
225
+ const report = await fetchVoiceOpsStatus(path, options);
216
226
  snapshot = {
217
- ...snapshot,
218
227
  error: null,
219
228
  isLoading: false,
220
229
  report,
221
- status: {
222
- generatedAt: Date.now(),
223
- mode: report.mode,
224
- ok: report.ok,
225
- providers: report.providers.map((provider) => provider.provider),
226
- runPath,
227
- safe: true
228
- },
229
230
  updatedAt: Date.now()
230
231
  };
231
232
  emit();
@@ -248,7 +249,7 @@ var createVoiceCampaignDialerProofStore = (path = "/api/voice/campaigns/dialer-p
248
249
  }
249
250
  listeners.clear();
250
251
  };
251
- if (options.intervalMs && options.intervalMs > 0) {
252
+ if (typeof window !== "undefined" && options.intervalMs && options.intervalMs > 0) {
252
253
  timer = setInterval(() => {
253
254
  refresh().catch(() => {});
254
255
  }, options.intervalMs);
@@ -258,7 +259,6 @@ var createVoiceCampaignDialerProofStore = (path = "/api/voice/campaigns/dialer-p
258
259
  getServerSnapshot: () => snapshot,
259
260
  getSnapshot: () => snapshot,
260
261
  refresh,
261
- runProof,
262
262
  subscribe: (listener) => {
263
263
  listeners.add(listener);
264
264
  return () => {
@@ -268,11 +268,9 @@ var createVoiceCampaignDialerProofStore = (path = "/api/voice/campaigns/dialer-p
268
268
  };
269
269
  };
270
270
 
271
- // src/svelte/createVoiceCampaignDialerProof.ts
272
- var createVoiceCampaignDialerProof = (path = "/api/voice/campaigns/dialer-proof", options = {}) => createVoiceCampaignDialerProofStore(path, options);
273
271
  // src/client/opsStatusWidget.ts
274
272
  var DEFAULT_TITLE = "Voice Ops Status";
275
- var DEFAULT_DESCRIPTION = "Certified workflow, provider, and handoff readiness from the AbsoluteJS voice app kit.";
273
+ var DEFAULT_DESCRIPTION = "Certified workflow, provider, and handoff readiness from your AbsoluteJS voice app.";
276
274
  var SURFACE_LABELS = {
277
275
  handoffs: "Handoffs",
278
276
  providers: "Providers",
@@ -357,8 +355,8 @@ var renderVoiceOpsStatusHTML = (snapshot, options = {}) => {
357
355
  </section>`;
358
356
  };
359
357
  var getVoiceOpsStatusCSS = () => `.absolute-voice-ops-status{border:1px solid #d8d2c4;border-radius:20px;background:#fffaf0;color:#16130d;padding:18px;box-shadow:0 18px 40px rgba(47,37,18,.12);font-family:inherit}.absolute-voice-ops-status--fail,.absolute-voice-ops-status--error{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-ops-status__header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-ops-status__eyebrow{color:#73664f;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-ops-status__label{font-size:28px;line-height:1}.absolute-voice-ops-status__description{color:#514733;margin:12px 0 0}.absolute-voice-ops-status__summary,.absolute-voice-ops-status__links{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}.absolute-voice-ops-status__summary span,.absolute-voice-ops-status__links a{border:1px solid #e6ddca;border-radius:999px;color:inherit;padding:6px 10px;text-decoration:none}.absolute-voice-ops-status__surfaces{display:grid;gap:8px;list-style:none;margin:16px 0 0;padding:0}.absolute-voice-ops-status__surface{align-items:center;background:#fff;border:1px solid #eee4d2;border-radius:14px;display:flex;gap:12px;justify-content:space-between;padding:10px 12px}.absolute-voice-ops-status__surface--fail{border-color:#f2a7a7}.absolute-voice-ops-status__surface span{color:#655944}.absolute-voice-ops-status__error{color:#9f1239;font-weight:700}`;
360
- var mountVoiceOpsStatus = (element, path = "/app-kit/status", options = {}) => {
361
- const store = createVoiceAppKitStatusStore(path, options);
358
+ var mountVoiceOpsStatus = (element, path = "/api/voice/ops-status", options = {}) => {
359
+ const store = createVoiceOpsStatusStore(path, options);
362
360
  const render = () => {
363
361
  element.innerHTML = renderVoiceOpsStatusHTML(store.getSnapshot(), options);
364
362
  };
@@ -381,7 +379,7 @@ var defineVoiceOpsStatusElement = (tagName = "absolute-voice-ops-status") => {
381
379
  mounted;
382
380
  connectedCallback() {
383
381
  const intervalMs = Number(this.getAttribute("interval-ms") ?? 5000);
384
- this.mounted = mountVoiceOpsStatus(this, this.getAttribute("path") ?? "/app-kit/status", {
382
+ this.mounted = mountVoiceOpsStatus(this, this.getAttribute("path") ?? "/api/voice/ops-status", {
385
383
  description: this.getAttribute("description") ?? undefined,
386
384
  includeLinks: this.getAttribute("include-links") !== "false",
387
385
  intervalMs: Number.isFinite(intervalMs) ? intervalMs : 5000,
@@ -396,8 +394,8 @@ var defineVoiceOpsStatusElement = (tagName = "absolute-voice-ops-status") => {
396
394
  };
397
395
 
398
396
  // src/svelte/createVoiceOpsStatus.ts
399
- var createVoiceOpsStatus = (path = "/app-kit/status", options = {}) => {
400
- const store = createVoiceAppKitStatusStore(path, options);
397
+ var createVoiceOpsStatus = (path = "/api/voice/ops-status", options = {}) => {
398
+ const store = createVoiceOpsStatusStore(path, options);
401
399
  return {
402
400
  close: store.close,
403
401
  getHTML: () => renderVoiceOpsStatusHTML(store.getSnapshot(), options),
@@ -3017,6 +3015,5 @@ export {
3017
3015
  createVoiceProviderCapabilities,
3018
3016
  createVoiceOpsStatus,
3019
3017
  createVoiceController,
3020
- createVoiceCampaignDialerProof,
3021
- createVoiceAppKitStatus
3018
+ createVoiceCampaignDialerProof
3022
3019
  };
@@ -2,7 +2,7 @@ import { Elysia } from 'elysia';
2
2
  import type { VoiceTelephonySetupStatus, VoiceTelephonySmokeCheck, VoiceTelephonySmokeReport } from './contract';
3
3
  import { type VoiceTelephonyOutcomePolicy, type VoiceTelephonyWebhookRoutesOptions } from '../telephonyOutcome';
4
4
  import { type VoiceCallReviewArtifact, type VoiceCallReviewConfig } from '../testing/review';
5
- import type { AudioFormat, VoiceLogger, VoicePluginConfig, VoiceSessionRecord, VoiceServerMessage } from '../types';
5
+ import type { AudioFormat, STTAdapter, VoiceLogger, VoicePluginConfig, VoiceSessionRecord, VoiceServerMessage } from '../types';
6
6
  type TwilioMediaPayload = {
7
7
  chunk?: string;
8
8
  payload: string;
@@ -78,7 +78,7 @@ export type TwilioMediaStreamSocket = {
78
78
  close: (code?: number, reason?: string) => void | Promise<void>;
79
79
  send: (data: string) => void | Promise<void>;
80
80
  };
81
- export type TwilioMediaStreamBridgeOptions<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = Omit<VoicePluginConfig<TContext, TSession, TResult>, 'htmx' | 'path'> & {
81
+ export type TwilioMediaStreamBridgeOptions<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = Omit<VoicePluginConfig<TContext, TSession, TResult>, 'htmx' | 'path' | 'stt'> & {
82
82
  clearOnInboundMedia?: boolean;
83
83
  context: TContext;
84
84
  logger?: VoiceLogger;
@@ -97,6 +97,7 @@ export type TwilioMediaStreamBridgeOptions<TContext = unknown, TSession extends
97
97
  };
98
98
  scenarioId?: string;
99
99
  sessionId?: string;
100
+ stt: STTAdapter;
100
101
  };
101
102
  export type TwilioMediaStreamBridge = {
102
103
  close: (reason?: string) => Promise<void>;
@@ -5033,6 +5033,12 @@ var DEFAULT_FORMAT = {
5033
5033
  encoding: "pcm_s16le",
5034
5034
  sampleRateHz: 16000
5035
5035
  };
5036
+ var DEFAULT_REALTIME_FORMAT = {
5037
+ channels: 1,
5038
+ container: "raw",
5039
+ encoding: "pcm_s16le",
5040
+ sampleRateHz: 24000
5041
+ };
5036
5042
  var toError = (value) => value instanceof Error ? value : new Error(String(value));
5037
5043
  var createEmptyCurrentTurn = () => ({
5038
5044
  finalText: "",
@@ -5413,6 +5419,23 @@ var createVoiceSession = (options) => {
5413
5419
  });
5414
5420
  }
5415
5421
  };
5422
+ const sendAssistantAudio = async (chunk, input) => {
5423
+ const normalizedChunk = chunk instanceof Uint8Array ? new Uint8Array(chunk) : chunk instanceof ArrayBuffer ? new Uint8Array(chunk.slice(0)) : new Uint8Array(chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength));
5424
+ await send({
5425
+ chunkBase64: encodeBase64(normalizedChunk),
5426
+ format: input.format,
5427
+ receivedAt: input.receivedAt,
5428
+ turnId: activeTTSTurnId,
5429
+ type: "audio"
5430
+ });
5431
+ if (activeTTSTurnId) {
5432
+ await appendTurnLatencyStage({
5433
+ at: input.receivedAt,
5434
+ stage: "assistant_audio_received",
5435
+ turnId: activeTTSTurnId
5436
+ });
5437
+ }
5438
+ };
5416
5439
  const scheduleTurnCommit = (delayMs, reason, reset = true) => {
5417
5440
  if (!reset && silenceTimer) {
5418
5441
  return;
@@ -6114,8 +6137,12 @@ var createVoiceSession = (options) => {
6114
6137
  if (sttSession) {
6115
6138
  return sttSession;
6116
6139
  }
6117
- const openedSession = await options.stt.open({
6118
- format: DEFAULT_FORMAT,
6140
+ const inputAdapter = options.realtime ?? options.stt;
6141
+ if (!inputAdapter) {
6142
+ throw new Error("Voice session requires either an stt or realtime adapter.");
6143
+ }
6144
+ const openedSession = await inputAdapter.open({
6145
+ format: options.realtime ? options.realtimeInputFormat ?? DEFAULT_REALTIME_FORMAT : DEFAULT_FORMAT,
6119
6146
  languageStrategy: options.languageStrategy,
6120
6147
  lexicon,
6121
6148
  phraseHints,
@@ -6150,6 +6177,16 @@ var createVoiceSession = (options) => {
6150
6177
  openedSession.on("close", (event) => {
6151
6178
  runAdapterEvent("adapter.close", () => handleClose(event));
6152
6179
  });
6180
+ if (options.realtime) {
6181
+ openedSession.on("audio", ({ chunk, format, receivedAt }) => {
6182
+ runAdapterEvent("adapter.audio", async () => {
6183
+ await sendAssistantAudio(chunk, {
6184
+ format,
6185
+ receivedAt
6186
+ });
6187
+ });
6188
+ });
6189
+ }
6153
6190
  return openedSession;
6154
6191
  };
6155
6192
  const ensureTTSSession = async () => {
@@ -6174,21 +6211,10 @@ var createVoiceSession = (options) => {
6174
6211
  if (ttsSession !== openedSession) {
6175
6212
  return;
6176
6213
  }
6177
- const normalizedChunk = chunk instanceof Uint8Array ? new Uint8Array(chunk) : chunk instanceof ArrayBuffer ? new Uint8Array(chunk.slice(0)) : new Uint8Array(chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength));
6178
- await send({
6179
- chunkBase64: encodeBase64(normalizedChunk),
6214
+ await sendAssistantAudio(chunk, {
6180
6215
  format,
6181
- receivedAt,
6182
- turnId: activeTTSTurnId,
6183
- type: "audio"
6216
+ receivedAt
6184
6217
  });
6185
- if (activeTTSTurnId) {
6186
- await appendTurnLatencyStage({
6187
- at: receivedAt,
6188
- stage: "assistant_audio_received",
6189
- turnId: activeTTSTurnId
6190
- });
6191
- }
6192
6218
  });
6193
6219
  });
6194
6220
  openedSession.on("error", (event) => {
@@ -6267,7 +6293,8 @@ var createVoiceSession = (options) => {
6267
6293
  await appendTrace({
6268
6294
  payload: {
6269
6295
  text: output.assistantText,
6270
- ttsConfigured: Boolean(options.tts)
6296
+ ttsConfigured: Boolean(options.tts),
6297
+ realtimeConfigured: Boolean(options.realtime)
6271
6298
  },
6272
6299
  session,
6273
6300
  turnId: turn.id,
@@ -6299,9 +6326,35 @@ var createVoiceSession = (options) => {
6299
6326
  turnId: turn.id,
6300
6327
  type: "turn.assistant"
6301
6328
  });
6329
+ } else if (options.realtime) {
6330
+ const activeRealtimeSession = await ensureAdapter();
6331
+ const realtimeStartedAt = Date.now();
6332
+ activeTTSTurnId = turn.id;
6333
+ await appendTurnLatencyStage({
6334
+ at: realtimeStartedAt,
6335
+ session,
6336
+ stage: "tts_send_started",
6337
+ turnId: turn.id
6338
+ });
6339
+ await activeRealtimeSession.send(output.assistantText);
6340
+ await appendTurnLatencyStage({
6341
+ session,
6342
+ stage: "tts_send_completed",
6343
+ turnId: turn.id
6344
+ });
6345
+ await appendTrace({
6346
+ payload: {
6347
+ elapsedMs: Date.now() - realtimeStartedAt,
6348
+ mode: "realtime",
6349
+ status: "sent"
6350
+ },
6351
+ session,
6352
+ turnId: turn.id,
6353
+ type: "turn.assistant"
6354
+ });
6302
6355
  }
6303
6356
  } catch (error) {
6304
- logger.warn("voice tts send failed", {
6357
+ logger.warn("voice assistant audio send failed", {
6305
6358
  error: toError(error).message,
6306
6359
  sessionId: options.id,
6307
6360
  turnId: turn.id
@@ -6309,7 +6362,7 @@ var createVoiceSession = (options) => {
6309
6362
  await appendTrace({
6310
6363
  payload: {
6311
6364
  error: toError(error).message,
6312
- status: "tts-send-failed"
6365
+ status: options.realtime ? "realtime-send-failed" : "tts-send-failed"
6313
6366
  },
6314
6367
  session,
6315
6368
  turnId: turn.id,
@@ -6514,7 +6567,7 @@ var createVoiceSession = (options) => {
6514
6567
  turn,
6515
6568
  type: "turn"
6516
6569
  });
6517
- if (options.sttLifecycle === "turn-scoped") {
6570
+ if (options.stt && options.sttLifecycle === "turn-scoped") {
6518
6571
  await closeAdapter("turn-commit");
6519
6572
  }
6520
6573
  await completeTurn(updatedSession, turn);
@@ -9600,7 +9653,7 @@ var runVoiceTelephonyBenchmark = async (scenarios = getDefaultVoiceTelephonyBenc
9600
9653
  };
9601
9654
  };
9602
9655
  // src/testing/tts.ts
9603
- var DEFAULT_REALTIME_FORMAT = {
9656
+ var DEFAULT_REALTIME_FORMAT2 = {
9604
9657
  channels: 1,
9605
9658
  container: "raw",
9606
9659
  encoding: "pcm_s16le",
@@ -9659,7 +9712,7 @@ var runTTSAdapterFixture = async (adapter, fixture, options = {}) => {
9659
9712
  let audioDurationMs = 0;
9660
9713
  let audioChunkCount = 0;
9661
9714
  const session = adapter.kind === "realtime" ? await adapter.open({
9662
- format: options.realtimeFormat ?? DEFAULT_REALTIME_FORMAT,
9715
+ format: options.realtimeFormat ?? DEFAULT_REALTIME_FORMAT2,
9663
9716
  sessionId: `tts-benchmark:${fixture.id}`,
9664
9717
  ...openOptions ?? {}
9665
9718
  }) : await adapter.open({
@@ -0,0 +1,86 @@
1
+ import { Elysia } from 'elysia';
2
+ import { type VoiceTraceSinkDeliveryQueueSummary, type VoiceTraceSinkDeliveryWorkerResult } from './queue';
3
+ import type { VoiceTraceSinkDeliveryQueueStatus, VoiceTraceSinkDeliveryRecord, VoiceTraceSinkDeliveryStore } from './trace';
4
+ export type VoiceTraceDeliveryReport = {
5
+ checkedAt: number;
6
+ deliveries: VoiceTraceSinkDeliveryRecord[];
7
+ filter: VoiceTraceDeliveryFilter;
8
+ summary: VoiceTraceSinkDeliveryQueueSummary;
9
+ };
10
+ export type VoiceTraceDeliveryDrainWorker = {
11
+ drain: () => Promise<VoiceTraceSinkDeliveryWorkerResult> | VoiceTraceSinkDeliveryWorkerResult;
12
+ };
13
+ export type VoiceTraceDeliveryDrainReport = VoiceTraceSinkDeliveryWorkerResult & {
14
+ drainedAt: number;
15
+ };
16
+ export type VoiceTraceDeliveryFilter = {
17
+ limit?: number;
18
+ q?: string;
19
+ status?: VoiceTraceSinkDeliveryQueueStatus | 'all';
20
+ };
21
+ export type VoiceTraceDeliveryRoutesOptions = {
22
+ deadLetters?: VoiceTraceSinkDeliveryStore;
23
+ filter?: VoiceTraceDeliveryFilter;
24
+ headers?: HeadersInit;
25
+ htmlPath?: false | string;
26
+ limit?: number;
27
+ name?: string;
28
+ path?: string;
29
+ render?: (report: VoiceTraceDeliveryReport) => string | Promise<string>;
30
+ store: VoiceTraceSinkDeliveryStore;
31
+ title?: string;
32
+ worker?: VoiceTraceDeliveryDrainWorker;
33
+ workerPath?: false | string;
34
+ };
35
+ export declare const resolveVoiceTraceDeliveryFilter: (query?: Record<string, unknown>, base?: VoiceTraceDeliveryFilter) => VoiceTraceDeliveryFilter;
36
+ export declare const buildVoiceTraceDeliveryReport: (options: VoiceTraceDeliveryRoutesOptions, filter?: VoiceTraceDeliveryFilter) => Promise<VoiceTraceDeliveryReport>;
37
+ export declare const renderVoiceTraceDeliveryHTML: (report: VoiceTraceDeliveryReport, options?: {
38
+ title?: string;
39
+ workerPath?: false | string;
40
+ }) => string;
41
+ export declare const createVoiceTraceDeliveryJSONHandler: (options: VoiceTraceDeliveryRoutesOptions) => ({ query }: {
42
+ query?: Record<string, string | undefined>;
43
+ }) => Promise<VoiceTraceDeliveryReport>;
44
+ export declare const createVoiceTraceDeliveryHTMLHandler: (options: VoiceTraceDeliveryRoutesOptions) => ({ query }: {
45
+ query?: Record<string, string | undefined>;
46
+ }) => Promise<Response>;
47
+ export declare const createVoiceTraceDeliveryRoutes: (options: VoiceTraceDeliveryRoutesOptions) => Elysia<"", {
48
+ decorator: {};
49
+ store: {};
50
+ derive: {};
51
+ resolve: {};
52
+ }, {
53
+ typebox: {};
54
+ error: {};
55
+ }, {
56
+ schema: {};
57
+ standaloneSchema: {};
58
+ macro: {};
59
+ macroFn: {};
60
+ parser: {};
61
+ response: {};
62
+ }, {
63
+ [x: string]: {
64
+ get: {
65
+ body: unknown;
66
+ params: {};
67
+ query: unknown;
68
+ headers: unknown;
69
+ response: {
70
+ 200: VoiceTraceDeliveryReport;
71
+ };
72
+ };
73
+ };
74
+ }, {
75
+ derive: {};
76
+ resolve: {};
77
+ schema: {};
78
+ standaloneSchema: {};
79
+ response: {};
80
+ }, {
81
+ derive: {};
82
+ resolve: {};
83
+ schema: {};
84
+ standaloneSchema: {};
85
+ response: {};
86
+ }>;
package/dist/types.d.ts CHANGED
@@ -616,9 +616,11 @@ export type VoicePluginConfig<TContext = unknown, TSession extends VoiceSessionR
616
616
  lexicon?: VoiceLexiconEntry[] | VoiceLexiconResolver<TContext>;
617
617
  phraseHints?: VoicePhraseHint[] | VoicePhraseHintResolver<TContext>;
618
618
  preset?: VoiceRuntimePreset;
619
- stt: STTAdapter;
619
+ stt?: STTAdapter;
620
620
  sttFallback?: VoiceSTTFallbackConfig;
621
621
  sttLifecycle?: VoiceSTTLifecycle;
622
+ realtime?: RealtimeAdapter;
623
+ realtimeInputFormat?: AudioFormat;
622
624
  tts?: TTSAdapter;
623
625
  session: VoiceSessionStore<NoInfer<TSession>>;
624
626
  reconnect?: VoiceReconnectConfig;
@@ -635,7 +637,9 @@ export type CreateVoiceSessionOptions<TContext = unknown, TSession extends Voice
635
637
  id: string;
636
638
  context: TContext;
637
639
  socket: VoiceSocket;
638
- stt: STTAdapter;
640
+ stt?: STTAdapter;
641
+ realtime?: RealtimeAdapter;
642
+ realtimeInputFormat?: AudioFormat;
639
643
  tts?: TTSAdapter;
640
644
  languageStrategy?: VoiceLanguageStrategy;
641
645
  lexicon?: VoiceLexiconEntry[];
@@ -5,7 +5,7 @@ export { VoiceProviderStatus } from './VoiceProviderStatus';
5
5
  export { VoiceRoutingStatus } from './VoiceRoutingStatus';
6
6
  export { VoiceTurnLatency } from './VoiceTurnLatency';
7
7
  export { VoiceTurnQuality } from './VoiceTurnQuality';
8
- export { useVoiceAppKitStatus } from './useVoiceAppKitStatus';
8
+ export { useVoiceOpsStatus } from './useVoiceOpsStatus';
9
9
  export { useVoiceCampaignDialerProof } from './useVoiceCampaignDialerProof';
10
10
  export { useVoiceStream } from './useVoiceStream';
11
11
  export { useVoiceController } from './useVoiceController';