@absolutejs/voice 0.0.22-beta.4 → 0.0.22-beta.41

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 (44) hide show
  1. package/dist/angular/index.d.ts +1 -0
  2. package/dist/angular/index.js +172 -2
  3. package/dist/angular/voice-provider-status.service.d.ts +12 -0
  4. package/dist/angular/voice-stream.service.d.ts +2 -0
  5. package/dist/assistantHealth.d.ts +81 -0
  6. package/dist/client/actions.d.ts +22 -0
  7. package/dist/client/connection.d.ts +3 -0
  8. package/dist/client/htmxBootstrap.js +44 -2
  9. package/dist/client/index.d.ts +2 -0
  10. package/dist/client/index.js +125 -2
  11. package/dist/client/providerStatus.d.ts +19 -0
  12. package/dist/diagnosticsRoutes.d.ts +44 -0
  13. package/dist/handoff.d.ts +54 -0
  14. package/dist/handoffHealth.d.ts +94 -0
  15. package/dist/index.d.ts +26 -2
  16. package/dist/index.js +3551 -128
  17. package/dist/modelAdapters.d.ts +99 -0
  18. package/dist/opsConsoleRoutes.d.ts +77 -0
  19. package/dist/opsWebhook.d.ts +126 -0
  20. package/dist/providerAdapters.d.ts +37 -0
  21. package/dist/providerHealth.d.ts +79 -0
  22. package/dist/qualityRoutes.d.ts +76 -0
  23. package/dist/queue.d.ts +52 -0
  24. package/dist/react/index.d.ts +1 -0
  25. package/dist/react/index.js +148 -2
  26. package/dist/react/useVoiceController.d.ts +2 -0
  27. package/dist/react/useVoiceProviderStatus.d.ts +8 -0
  28. package/dist/react/useVoiceStream.d.ts +2 -0
  29. package/dist/resilienceRoutes.d.ts +106 -0
  30. package/dist/sessionReplay.d.ts +175 -0
  31. package/dist/svelte/createVoiceProviderStatus.d.ts +8 -0
  32. package/dist/svelte/index.d.ts +1 -0
  33. package/dist/svelte/index.js +127 -2
  34. package/dist/testing/index.d.ts +2 -0
  35. package/dist/testing/index.js +1468 -7
  36. package/dist/testing/ioProviderSimulator.d.ts +41 -0
  37. package/dist/testing/providerSimulator.d.ts +44 -0
  38. package/dist/trace.d.ts +1 -1
  39. package/dist/types.d.ts +84 -2
  40. package/dist/vue/index.d.ts +1 -0
  41. package/dist/vue/index.js +161 -2
  42. package/dist/vue/useVoiceProviderStatus.d.ts +9 -0
  43. package/dist/vue/useVoiceStream.d.ts +2 -0
  44. package/package.json +1 -1
@@ -1,2 +1,3 @@
1
1
  export { VoiceStreamService } from './voice-stream.service';
2
2
  export { VoiceControllerService } from './voice-controller.service';
3
+ export { VoiceProviderStatusService } from './voice-provider-status.service';
@@ -120,6 +120,12 @@ var serverMessageToAction = (message) => {
120
120
  sessionId: message.sessionId,
121
121
  type: "complete"
122
122
  };
123
+ case "call_lifecycle":
124
+ return {
125
+ event: message.event,
126
+ sessionId: message.sessionId,
127
+ type: "call_lifecycle"
128
+ };
123
129
  case "error":
124
130
  return {
125
131
  message: normalizeErrorMessage(message.message),
@@ -163,7 +169,7 @@ var DEFAULT_SCENARIO_QUERY_PARAM = "scenarioId";
163
169
  var noop = () => {};
164
170
  var noopUnsubscribe = () => noop;
165
171
  var NOOP_CONNECTION = {
166
- start: () => {},
172
+ callControl: noop,
167
173
  close: noop,
168
174
  endTurn: noop,
169
175
  getReadyState: () => WS_CLOSED,
@@ -171,6 +177,7 @@ var NOOP_CONNECTION = {
171
177
  getSessionId: () => "",
172
178
  send: noop,
173
179
  sendAudio: noop,
180
+ start: () => {},
174
181
  subscribe: noopUnsubscribe
175
182
  };
176
183
  var createSessionId = () => crypto.randomUUID();
@@ -192,6 +199,7 @@ var isVoiceServerMessage = (value) => {
192
199
  switch (value.type) {
193
200
  case "audio":
194
201
  case "assistant":
202
+ case "call_lifecycle":
195
203
  case "complete":
196
204
  case "error":
197
205
  case "final":
@@ -332,6 +340,12 @@ var createVoiceConnection = (path, options = {}) => {
332
340
  const endTurn = () => {
333
341
  send({ type: "end_turn" });
334
342
  };
343
+ const callControl = (message) => {
344
+ send({
345
+ ...message,
346
+ type: "call_control"
347
+ });
348
+ };
335
349
  const close = () => {
336
350
  clearTimers();
337
351
  if (state.ws) {
@@ -349,7 +363,7 @@ var createVoiceConnection = (path, options = {}) => {
349
363
  };
350
364
  connect();
351
365
  return {
352
- start,
366
+ callControl,
353
367
  close,
354
368
  endTurn,
355
369
  getReadyState: () => state.ws?.readyState ?? WS_CLOSED,
@@ -357,6 +371,7 @@ var createVoiceConnection = (path, options = {}) => {
357
371
  getSessionId: () => state.sessionId,
358
372
  send,
359
373
  sendAudio,
374
+ start,
360
375
  subscribe
361
376
  };
362
377
  };
@@ -365,6 +380,7 @@ var createVoiceConnection = (path, options = {}) => {
365
380
  var createInitialState = () => ({
366
381
  assistantAudio: [],
367
382
  assistantTexts: [],
383
+ call: null,
368
384
  error: null,
369
385
  isConnected: false,
370
386
  scenarioId: null,
@@ -408,6 +424,20 @@ var createVoiceStreamStore = () => {
408
424
  status: "completed"
409
425
  };
410
426
  break;
427
+ case "call_lifecycle":
428
+ state = {
429
+ ...state,
430
+ call: {
431
+ ...state.call,
432
+ disposition: action.event.type === "end" ? action.event.disposition : state.call?.disposition,
433
+ endedAt: action.event.type === "end" ? action.event.at : state.call?.endedAt,
434
+ events: [...state.call?.events ?? [], action.event],
435
+ lastEventAt: action.event.at,
436
+ startedAt: state.call?.startedAt ?? action.event.at
437
+ },
438
+ sessionId: action.sessionId
439
+ };
440
+ break;
411
441
  case "connected":
412
442
  state = {
413
443
  ...state,
@@ -494,6 +524,9 @@ var createVoiceStream = (path, options = {}) => {
494
524
  }
495
525
  });
496
526
  return {
527
+ callControl(message) {
528
+ connection.callControl(message);
529
+ },
497
530
  close() {
498
531
  unsubscribeConnection();
499
532
  connection.close();
@@ -537,6 +570,9 @@ var createVoiceStream = (path, options = {}) => {
537
570
  get assistantAudio() {
538
571
  return store.getSnapshot().assistantAudio;
539
572
  },
573
+ get call() {
574
+ return store.getSnapshot().call;
575
+ },
540
576
  sendAudio(audio) {
541
577
  connection.sendAudio(audio);
542
578
  },
@@ -560,6 +596,7 @@ class VoiceStreamService {
560
596
  const stream = createVoiceStream(path, options);
561
597
  const assistantAudioSignal = signal([]);
562
598
  const assistantTextsSignal = signal([]);
599
+ const callSignal = signal(null);
563
600
  const errorSignal = signal(null);
564
601
  const isConnectedSignal = signal(false);
565
602
  const partialSignal = signal("");
@@ -569,6 +606,7 @@ class VoiceStreamService {
569
606
  const sync = () => {
570
607
  assistantAudioSignal.set([...stream.assistantAudio]);
571
608
  assistantTextsSignal.set([...stream.assistantTexts]);
609
+ callSignal.set(stream.call);
572
610
  errorSignal.set(stream.error);
573
611
  isConnectedSignal.set(stream.isConnected);
574
612
  partialSignal.set(stream.partial);
@@ -581,6 +619,8 @@ class VoiceStreamService {
581
619
  return {
582
620
  assistantAudio: computed(() => assistantAudioSignal()),
583
621
  assistantTexts: computed(() => assistantTextsSignal()),
622
+ call: computed(() => callSignal()),
623
+ callControl: (message) => stream.callControl(message),
584
624
  close: () => {
585
625
  unsubscribe();
586
626
  stream.close();
@@ -1065,6 +1105,7 @@ var resolveVoiceRuntimePreset = (name = "default") => {
1065
1105
  var createInitialState2 = (stream) => ({
1066
1106
  assistantAudio: [...stream.assistantAudio],
1067
1107
  assistantTexts: [...stream.assistantTexts],
1108
+ call: stream.call,
1068
1109
  error: stream.error,
1069
1110
  isConnected: stream.isConnected,
1070
1111
  isRecording: false,
@@ -1094,6 +1135,7 @@ var createVoiceController = (path, options = {}) => {
1094
1135
  ...state,
1095
1136
  assistantAudio: [...stream.assistantAudio],
1096
1137
  assistantTexts: [...stream.assistantTexts],
1138
+ call: stream.call,
1097
1139
  error: stream.error,
1098
1140
  isConnected: stream.isConnected,
1099
1141
  partial: stream.partial,
@@ -1171,6 +1213,7 @@ var createVoiceController = (path, options = {}) => {
1171
1213
  bindHTMX(bindingOptions) {
1172
1214
  return bindVoiceHTMX(stream, bindingOptions);
1173
1215
  },
1216
+ callControl: (message) => stream.callControl(message),
1174
1217
  close,
1175
1218
  endTurn: () => stream.endTurn(),
1176
1219
  get error() {
@@ -1223,6 +1266,9 @@ var createVoiceController = (path, options = {}) => {
1223
1266
  },
1224
1267
  get assistantAudio() {
1225
1268
  return state.assistantAudio;
1269
+ },
1270
+ get call() {
1271
+ return state.call;
1226
1272
  }
1227
1273
  };
1228
1274
  };
@@ -1288,7 +1334,131 @@ VoiceControllerService = __decorateElement(_init, 0, "VoiceControllerService", _
1288
1334
  __runInitializers(_init, 1, VoiceControllerService);
1289
1335
  __decoratorMetadata(_init, VoiceControllerService);
1290
1336
  let _VoiceControllerService = VoiceControllerService;
1337
+ // src/angular/voice-provider-status.service.ts
1338
+ import { computed as computed3, Injectable as Injectable3, signal as signal3 } from "@angular/core";
1339
+
1340
+ // src/client/providerStatus.ts
1341
+ var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
1342
+ const fetchImpl = options.fetch ?? globalThis.fetch;
1343
+ const response = await fetchImpl(path);
1344
+ if (!response.ok) {
1345
+ throw new Error(`Voice provider status failed: HTTP ${response.status}`);
1346
+ }
1347
+ return await response.json();
1348
+ };
1349
+ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {}) => {
1350
+ const listeners = new Set;
1351
+ let closed = false;
1352
+ let timer;
1353
+ let snapshot = {
1354
+ error: null,
1355
+ isLoading: false,
1356
+ providers: []
1357
+ };
1358
+ const emit = () => {
1359
+ for (const listener of listeners) {
1360
+ listener();
1361
+ }
1362
+ };
1363
+ const refresh = async () => {
1364
+ if (closed) {
1365
+ return snapshot.providers;
1366
+ }
1367
+ snapshot = {
1368
+ ...snapshot,
1369
+ error: null,
1370
+ isLoading: true
1371
+ };
1372
+ emit();
1373
+ try {
1374
+ const providers = await fetchVoiceProviderStatus(path, options);
1375
+ snapshot = {
1376
+ error: null,
1377
+ isLoading: false,
1378
+ providers,
1379
+ updatedAt: Date.now()
1380
+ };
1381
+ emit();
1382
+ return providers;
1383
+ } catch (error) {
1384
+ snapshot = {
1385
+ ...snapshot,
1386
+ error: error instanceof Error ? error.message : String(error),
1387
+ isLoading: false
1388
+ };
1389
+ emit();
1390
+ throw error;
1391
+ }
1392
+ };
1393
+ const close = () => {
1394
+ closed = true;
1395
+ if (timer) {
1396
+ clearInterval(timer);
1397
+ timer = undefined;
1398
+ }
1399
+ listeners.clear();
1400
+ };
1401
+ if (options.intervalMs && options.intervalMs > 0) {
1402
+ timer = setInterval(() => {
1403
+ refresh().catch(() => {});
1404
+ }, options.intervalMs);
1405
+ }
1406
+ return {
1407
+ close,
1408
+ getServerSnapshot: () => snapshot,
1409
+ getSnapshot: () => snapshot,
1410
+ refresh,
1411
+ subscribe: (listener) => {
1412
+ listeners.add(listener);
1413
+ return () => {
1414
+ listeners.delete(listener);
1415
+ };
1416
+ }
1417
+ };
1418
+ };
1419
+
1420
+ // src/angular/voice-provider-status.service.ts
1421
+ var _dec = [
1422
+ Injectable3({ providedIn: "root" })
1423
+ ];
1424
+ var _init = __decoratorStart(undefined);
1425
+
1426
+ class VoiceProviderStatusService {
1427
+ connect(path = "/api/provider-status", options = {}) {
1428
+ const store = createVoiceProviderStatusStore(path, options);
1429
+ const errorSignal = signal3(null);
1430
+ const isLoadingSignal = signal3(false);
1431
+ const providersSignal = signal3([]);
1432
+ const updatedAtSignal = signal3(undefined);
1433
+ const sync = () => {
1434
+ const snapshot = store.getSnapshot();
1435
+ errorSignal.set(snapshot.error);
1436
+ isLoadingSignal.set(snapshot.isLoading);
1437
+ providersSignal.set([...snapshot.providers]);
1438
+ updatedAtSignal.set(snapshot.updatedAt);
1439
+ };
1440
+ const unsubscribe = store.subscribe(sync);
1441
+ sync();
1442
+ store.refresh().catch(() => {});
1443
+ return {
1444
+ close: () => {
1445
+ unsubscribe();
1446
+ store.close();
1447
+ },
1448
+ error: computed3(() => errorSignal()),
1449
+ isLoading: computed3(() => isLoadingSignal()),
1450
+ providers: computed3(() => providersSignal()),
1451
+ refresh: store.refresh,
1452
+ updatedAt: computed3(() => updatedAtSignal())
1453
+ };
1454
+ }
1455
+ }
1456
+ VoiceProviderStatusService = __decorateElement(_init, 0, "VoiceProviderStatusService", _dec, VoiceProviderStatusService);
1457
+ __runInitializers(_init, 1, VoiceProviderStatusService);
1458
+ __decoratorMetadata(_init, VoiceProviderStatusService);
1459
+ let _VoiceProviderStatusService = VoiceProviderStatusService;
1291
1460
  export {
1292
1461
  VoiceStreamService,
1462
+ VoiceProviderStatusService,
1293
1463
  VoiceControllerService
1294
1464
  };
@@ -0,0 +1,12 @@
1
+ import { type VoiceProviderStatusClientOptions } from '../client/providerStatus';
2
+ import type { VoiceProviderHealthSummary } from '../providerHealth';
3
+ export declare class VoiceProviderStatusService {
4
+ connect<TProvider extends string = string>(path?: string, options?: VoiceProviderStatusClientOptions): {
5
+ close: () => void;
6
+ error: import("@angular/core").Signal<string | null>;
7
+ isLoading: import("@angular/core").Signal<boolean>;
8
+ providers: import("@angular/core").Signal<VoiceProviderHealthSummary<TProvider>[]>;
9
+ refresh: () => Promise<VoiceProviderHealthSummary<TProvider>[]>;
10
+ updatedAt: import("@angular/core").Signal<number | undefined>;
11
+ };
12
+ }
@@ -8,6 +8,8 @@ export declare class VoiceStreamService {
8
8
  turnId?: string;
9
9
  }[]>;
10
10
  assistantTexts: import("@angular/core").Signal<string[]>;
11
+ call: import("@angular/core").Signal<import("..").VoiceCallLifecycleState | null>;
12
+ callControl: (message: Parameters<(message: Omit<import("..").VoiceClientCallControlMessage, "type">) => void>[0]) => void;
11
13
  close: () => void;
12
14
  endTurn: () => void;
13
15
  error: import("@angular/core").Signal<string | null>;
@@ -0,0 +1,81 @@
1
+ import { Elysia } from 'elysia';
2
+ import { type VoiceAssistantRunsSummary } from './assistant';
3
+ import { type VoiceProviderHealthSummary } from './providerHealth';
4
+ import type { StoredVoiceTraceEvent, VoiceTraceEventStore } from './trace';
5
+ export type VoiceAssistantHealthFailure = {
6
+ at: number;
7
+ assistantId?: string;
8
+ error?: string;
9
+ provider?: string;
10
+ rateLimited?: boolean;
11
+ replayHref?: string;
12
+ sessionId: string;
13
+ status?: string;
14
+ turnId?: string;
15
+ type: StoredVoiceTraceEvent['type'];
16
+ };
17
+ export type VoiceAssistantHealthSummary<TProvider extends string = string> = {
18
+ assistantRuns: VoiceAssistantRunsSummary;
19
+ providerHealth: VoiceProviderHealthSummary<TProvider>[];
20
+ recentFailures: VoiceAssistantHealthFailure[];
21
+ };
22
+ export type VoiceAssistantHealthSummaryOptions<TProvider extends string = string> = {
23
+ events?: StoredVoiceTraceEvent[];
24
+ maxFailures?: number;
25
+ providers?: readonly TProvider[];
26
+ replayHref?: false | string | ((failure: Omit<VoiceAssistantHealthFailure, 'replayHref'>) => string);
27
+ store?: VoiceTraceEventStore;
28
+ };
29
+ export type VoiceAssistantHealthHTMLHandlerOptions<TProvider extends string = string> = VoiceAssistantHealthSummaryOptions<TProvider> & {
30
+ headers?: HeadersInit;
31
+ render?: (summary: VoiceAssistantHealthSummary<TProvider>) => string | Promise<string>;
32
+ };
33
+ export type VoiceAssistantHealthRoutesOptions<TProvider extends string = string> = VoiceAssistantHealthHTMLHandlerOptions<TProvider> & {
34
+ htmlPath?: false | string;
35
+ name?: string;
36
+ path?: string;
37
+ };
38
+ export declare const summarizeVoiceAssistantHealth: <TProvider extends string = string>(options: VoiceAssistantHealthSummaryOptions<TProvider>) => Promise<VoiceAssistantHealthSummary<TProvider>>;
39
+ export declare const renderVoiceAssistantHealthHTML: <TProvider extends string = string>(summary: VoiceAssistantHealthSummary<TProvider>) => string;
40
+ export declare const createVoiceAssistantHealthJSONHandler: <TProvider extends string = string>(options: VoiceAssistantHealthSummaryOptions<TProvider>) => () => Promise<VoiceAssistantHealthSummary<TProvider>>;
41
+ export declare const createVoiceAssistantHealthHTMLHandler: <TProvider extends string = string>(options: VoiceAssistantHealthHTMLHandlerOptions<TProvider>) => () => Promise<Response>;
42
+ export declare const createVoiceAssistantHealthRoutes: <TProvider extends string = string>(options: VoiceAssistantHealthRoutesOptions<TProvider>) => Elysia<"", {
43
+ decorator: {};
44
+ store: {};
45
+ derive: {};
46
+ resolve: {};
47
+ }, {
48
+ typebox: {};
49
+ error: {};
50
+ }, {
51
+ schema: {};
52
+ standaloneSchema: {};
53
+ macro: {};
54
+ macroFn: {};
55
+ parser: {};
56
+ response: {};
57
+ }, {
58
+ [x: string]: {
59
+ get: {
60
+ body: unknown;
61
+ params: {};
62
+ query: unknown;
63
+ headers: unknown;
64
+ response: {
65
+ 200: VoiceAssistantHealthSummary<TProvider>;
66
+ };
67
+ };
68
+ };
69
+ }, {
70
+ derive: {};
71
+ resolve: {};
72
+ schema: {};
73
+ standaloneSchema: {};
74
+ response: {};
75
+ }, {
76
+ derive: {};
77
+ resolve: {};
78
+ schema: {};
79
+ standaloneSchema: {};
80
+ response: {};
81
+ }>;
@@ -7,6 +7,7 @@ export declare const serverMessageToAction: <TResult = unknown>(message: VoiceSe
7
7
  type: "audio";
8
8
  text?: undefined;
9
9
  sessionId?: undefined;
10
+ event?: undefined;
10
11
  message?: undefined;
11
12
  transcript?: undefined;
12
13
  scenarioId?: undefined;
@@ -20,6 +21,7 @@ export declare const serverMessageToAction: <TResult = unknown>(message: VoiceSe
20
21
  receivedAt?: undefined;
21
22
  turnId?: undefined;
22
23
  sessionId?: undefined;
24
+ event?: undefined;
23
25
  message?: undefined;
24
26
  transcript?: undefined;
25
27
  scenarioId?: undefined;
@@ -33,6 +35,21 @@ export declare const serverMessageToAction: <TResult = unknown>(message: VoiceSe
33
35
  receivedAt?: undefined;
34
36
  turnId?: undefined;
35
37
  text?: undefined;
38
+ event?: undefined;
39
+ message?: undefined;
40
+ transcript?: undefined;
41
+ scenarioId?: undefined;
42
+ status?: undefined;
43
+ turn?: undefined;
44
+ } | {
45
+ event: import("..").VoiceCallLifecycleEvent;
46
+ sessionId: string;
47
+ type: "call_lifecycle";
48
+ chunk?: undefined;
49
+ format?: undefined;
50
+ receivedAt?: undefined;
51
+ turnId?: undefined;
52
+ text?: undefined;
36
53
  message?: undefined;
37
54
  transcript?: undefined;
38
55
  scenarioId?: undefined;
@@ -47,6 +64,7 @@ export declare const serverMessageToAction: <TResult = unknown>(message: VoiceSe
47
64
  turnId?: undefined;
48
65
  text?: undefined;
49
66
  sessionId?: undefined;
67
+ event?: undefined;
50
68
  transcript?: undefined;
51
69
  scenarioId?: undefined;
52
70
  status?: undefined;
@@ -60,6 +78,7 @@ export declare const serverMessageToAction: <TResult = unknown>(message: VoiceSe
60
78
  turnId?: undefined;
61
79
  text?: undefined;
62
80
  sessionId?: undefined;
81
+ event?: undefined;
63
82
  message?: undefined;
64
83
  scenarioId?: undefined;
65
84
  status?: undefined;
@@ -73,6 +92,7 @@ export declare const serverMessageToAction: <TResult = unknown>(message: VoiceSe
73
92
  turnId?: undefined;
74
93
  text?: undefined;
75
94
  sessionId?: undefined;
95
+ event?: undefined;
76
96
  message?: undefined;
77
97
  scenarioId?: undefined;
78
98
  status?: undefined;
@@ -87,6 +107,7 @@ export declare const serverMessageToAction: <TResult = unknown>(message: VoiceSe
87
107
  receivedAt?: undefined;
88
108
  turnId?: undefined;
89
109
  text?: undefined;
110
+ event?: undefined;
90
111
  message?: undefined;
91
112
  transcript?: undefined;
92
113
  turn?: undefined;
@@ -99,6 +120,7 @@ export declare const serverMessageToAction: <TResult = unknown>(message: VoiceSe
99
120
  turnId?: undefined;
100
121
  text?: undefined;
101
122
  sessionId?: undefined;
123
+ event?: undefined;
102
124
  message?: undefined;
103
125
  transcript?: undefined;
104
126
  scenarioId?: undefined;
@@ -1,5 +1,8 @@
1
1
  import type { VoiceClientMessage, VoiceConnectionOptions, VoiceServerMessage } from '../types';
2
2
  type VoiceConnectionHandle = {
3
+ callControl: (message: Omit<VoiceClientMessage & {
4
+ type: 'call_control';
5
+ }, 'type'>) => void;
3
6
  start: (input?: {
4
7
  sessionId?: string;
5
8
  scenarioId?: string;
@@ -188,6 +188,12 @@ var serverMessageToAction = (message) => {
188
188
  sessionId: message.sessionId,
189
189
  type: "complete"
190
190
  };
191
+ case "call_lifecycle":
192
+ return {
193
+ event: message.event,
194
+ sessionId: message.sessionId,
195
+ type: "call_lifecycle"
196
+ };
191
197
  case "error":
192
198
  return {
193
199
  message: normalizeErrorMessage(message.message),
@@ -231,7 +237,7 @@ var DEFAULT_SCENARIO_QUERY_PARAM = "scenarioId";
231
237
  var noop = () => {};
232
238
  var noopUnsubscribe = () => noop;
233
239
  var NOOP_CONNECTION = {
234
- start: () => {},
240
+ callControl: noop,
235
241
  close: noop,
236
242
  endTurn: noop,
237
243
  getReadyState: () => WS_CLOSED,
@@ -239,6 +245,7 @@ var NOOP_CONNECTION = {
239
245
  getSessionId: () => "",
240
246
  send: noop,
241
247
  sendAudio: noop,
248
+ start: () => {},
242
249
  subscribe: noopUnsubscribe
243
250
  };
244
251
  var createSessionId = () => crypto.randomUUID();
@@ -260,6 +267,7 @@ var isVoiceServerMessage = (value) => {
260
267
  switch (value.type) {
261
268
  case "audio":
262
269
  case "assistant":
270
+ case "call_lifecycle":
263
271
  case "complete":
264
272
  case "error":
265
273
  case "final":
@@ -400,6 +408,12 @@ var createVoiceConnection = (path, options = {}) => {
400
408
  const endTurn = () => {
401
409
  send({ type: "end_turn" });
402
410
  };
411
+ const callControl = (message) => {
412
+ send({
413
+ ...message,
414
+ type: "call_control"
415
+ });
416
+ };
403
417
  const close = () => {
404
418
  clearTimers();
405
419
  if (state.ws) {
@@ -417,7 +431,7 @@ var createVoiceConnection = (path, options = {}) => {
417
431
  };
418
432
  connect();
419
433
  return {
420
- start,
434
+ callControl,
421
435
  close,
422
436
  endTurn,
423
437
  getReadyState: () => state.ws?.readyState ?? WS_CLOSED,
@@ -425,6 +439,7 @@ var createVoiceConnection = (path, options = {}) => {
425
439
  getSessionId: () => state.sessionId,
426
440
  send,
427
441
  sendAudio,
442
+ start,
428
443
  subscribe
429
444
  };
430
445
  };
@@ -433,6 +448,7 @@ var createVoiceConnection = (path, options = {}) => {
433
448
  var createInitialState = () => ({
434
449
  assistantAudio: [],
435
450
  assistantTexts: [],
451
+ call: null,
436
452
  error: null,
437
453
  isConnected: false,
438
454
  scenarioId: null,
@@ -476,6 +492,20 @@ var createVoiceStreamStore = () => {
476
492
  status: "completed"
477
493
  };
478
494
  break;
495
+ case "call_lifecycle":
496
+ state = {
497
+ ...state,
498
+ call: {
499
+ ...state.call,
500
+ disposition: action.event.type === "end" ? action.event.disposition : state.call?.disposition,
501
+ endedAt: action.event.type === "end" ? action.event.at : state.call?.endedAt,
502
+ events: [...state.call?.events ?? [], action.event],
503
+ lastEventAt: action.event.at,
504
+ startedAt: state.call?.startedAt ?? action.event.at
505
+ },
506
+ sessionId: action.sessionId
507
+ };
508
+ break;
479
509
  case "connected":
480
510
  state = {
481
511
  ...state,
@@ -562,6 +592,9 @@ var createVoiceStream = (path, options = {}) => {
562
592
  }
563
593
  });
564
594
  return {
595
+ callControl(message) {
596
+ connection.callControl(message);
597
+ },
565
598
  close() {
566
599
  unsubscribeConnection();
567
600
  connection.close();
@@ -605,6 +638,9 @@ var createVoiceStream = (path, options = {}) => {
605
638
  get assistantAudio() {
606
639
  return store.getSnapshot().assistantAudio;
607
640
  },
641
+ get call() {
642
+ return store.getSnapshot().call;
643
+ },
608
644
  sendAudio(audio) {
609
645
  connection.sendAudio(audio);
610
646
  },
@@ -900,6 +936,7 @@ var resolveVoiceRuntimePreset = (name = "default") => {
900
936
  var createInitialState2 = (stream) => ({
901
937
  assistantAudio: [...stream.assistantAudio],
902
938
  assistantTexts: [...stream.assistantTexts],
939
+ call: stream.call,
903
940
  error: stream.error,
904
941
  isConnected: stream.isConnected,
905
942
  isRecording: false,
@@ -929,6 +966,7 @@ var createVoiceController = (path, options = {}) => {
929
966
  ...state,
930
967
  assistantAudio: [...stream.assistantAudio],
931
968
  assistantTexts: [...stream.assistantTexts],
969
+ call: stream.call,
932
970
  error: stream.error,
933
971
  isConnected: stream.isConnected,
934
972
  partial: stream.partial,
@@ -1006,6 +1044,7 @@ var createVoiceController = (path, options = {}) => {
1006
1044
  bindHTMX(bindingOptions) {
1007
1045
  return bindVoiceHTMX(stream, bindingOptions);
1008
1046
  },
1047
+ callControl: (message) => stream.callControl(message),
1009
1048
  close,
1010
1049
  endTurn: () => stream.endTurn(),
1011
1050
  get error() {
@@ -1058,6 +1097,9 @@ var createVoiceController = (path, options = {}) => {
1058
1097
  },
1059
1098
  get assistantAudio() {
1060
1099
  return state.assistantAudio;
1100
+ },
1101
+ get call() {
1102
+ return state.call;
1061
1103
  }
1062
1104
  };
1063
1105
  };
@@ -5,3 +5,5 @@ export { createVoiceController } from './controller';
5
5
  export { bindVoiceBargeIn, createVoiceDuplexController } from './duplex';
6
6
  export { bindVoiceHTMX } from './htmx';
7
7
  export { createMicrophoneCapture } from './microphone';
8
+ export { createVoiceProviderStatusStore, fetchVoiceProviderStatus } from './providerStatus';
9
+ export type { VoiceProviderStatusClientOptions, VoiceProviderStatusSnapshot } from './providerStatus';