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

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 (43) 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 +24 -2
  16. package/dist/index.js +3431 -128
  17. package/dist/modelAdapters.d.ts +99 -0
  18. package/dist/opsWebhook.d.ts +126 -0
  19. package/dist/providerAdapters.d.ts +37 -0
  20. package/dist/providerHealth.d.ts +79 -0
  21. package/dist/qualityRoutes.d.ts +76 -0
  22. package/dist/queue.d.ts +52 -0
  23. package/dist/react/index.d.ts +1 -0
  24. package/dist/react/index.js +148 -2
  25. package/dist/react/useVoiceController.d.ts +2 -0
  26. package/dist/react/useVoiceProviderStatus.d.ts +8 -0
  27. package/dist/react/useVoiceStream.d.ts +2 -0
  28. package/dist/resilienceRoutes.d.ts +106 -0
  29. package/dist/sessionReplay.d.ts +175 -0
  30. package/dist/svelte/createVoiceProviderStatus.d.ts +8 -0
  31. package/dist/svelte/index.d.ts +1 -0
  32. package/dist/svelte/index.js +127 -2
  33. package/dist/testing/index.d.ts +2 -0
  34. package/dist/testing/index.js +1468 -7
  35. package/dist/testing/ioProviderSimulator.d.ts +41 -0
  36. package/dist/testing/providerSimulator.d.ts +44 -0
  37. package/dist/trace.d.ts +1 -1
  38. package/dist/types.d.ts +84 -2
  39. package/dist/vue/index.d.ts +1 -0
  40. package/dist/vue/index.js +161 -2
  41. package/dist/vue/useVoiceProviderStatus.d.ts +9 -0
  42. package/dist/vue/useVoiceStream.d.ts +2 -0
  43. package/package.json +1 -1
@@ -0,0 +1,41 @@
1
+ import type { VoiceIOProviderRouterEvent } from '../providerAdapters';
2
+ export type VoiceIOProviderFailureSimulationMode = 'failure' | 'recovery';
3
+ export type VoiceIOProviderFailureSimulationKind = 'stt' | 'tts';
4
+ export type VoiceIOProviderFailureSimulationOperation = 'open' | 'send';
5
+ export type VoiceIOProviderFailureSimulationResult<TProvider extends string = string> = {
6
+ fallbackProvider?: TProvider;
7
+ mode: VoiceIOProviderFailureSimulationMode;
8
+ provider: TProvider;
9
+ sessionId: string;
10
+ status: 'simulated';
11
+ suppressedUntil?: number;
12
+ };
13
+ export type VoiceIOProviderFailureSimulatorOptions<TProvider extends string = string> = {
14
+ cooldownMs?: number;
15
+ fallback?: readonly TProvider[] | ((provider: TProvider) => readonly TProvider[] | Promise<readonly TProvider[]>);
16
+ failureElapsedMs?: number;
17
+ failureMessage?: (input: {
18
+ kind: VoiceIOProviderFailureSimulationKind;
19
+ operation: VoiceIOProviderFailureSimulationOperation;
20
+ provider: TProvider;
21
+ }) => string;
22
+ kind: VoiceIOProviderFailureSimulationKind;
23
+ latencyBudgets?: Partial<Record<TProvider, number>>;
24
+ now?: () => number;
25
+ onProviderEvent?: (event: VoiceIOProviderRouterEvent<TProvider>, input: {
26
+ mode: VoiceIOProviderFailureSimulationMode;
27
+ provider: TProvider;
28
+ sessionId: string;
29
+ }) => Promise<void> | void;
30
+ operation?: VoiceIOProviderFailureSimulationOperation;
31
+ providers: readonly TProvider[];
32
+ recoveryElapsedMs?: number | Partial<Record<TProvider, number>>;
33
+ sessionId?: (input: {
34
+ mode: VoiceIOProviderFailureSimulationMode;
35
+ now: number;
36
+ provider: TProvider;
37
+ }) => string;
38
+ };
39
+ export declare const createVoiceIOProviderFailureSimulator: <TProvider extends string>(options: VoiceIOProviderFailureSimulatorOptions<TProvider>) => {
40
+ run: (provider: TProvider, mode: VoiceIOProviderFailureSimulationMode) => Promise<VoiceIOProviderFailureSimulationResult<TProvider>>;
41
+ };
@@ -0,0 +1,44 @@
1
+ import type { VoiceAgentModelInput, VoiceAgentModelOutput } from '../agent';
2
+ import type { VoiceSessionRecord } from '../types';
3
+ import type { VoiceProviderRouterEvent, VoiceProviderRouterHealthOptions } from '../modelAdapters';
4
+ export type VoiceProviderFailureSimulationMode = 'failure' | 'recovery';
5
+ export type VoiceProviderFailureSimulationContext<TProvider extends string = string> = {
6
+ query: {
7
+ provider: TProvider;
8
+ recoverProvider?: TProvider;
9
+ simulateFailureProvider?: TProvider;
10
+ };
11
+ };
12
+ export type VoiceProviderFailureSimulationResult<TProvider extends string = string, TResult = unknown> = {
13
+ mode: VoiceProviderFailureSimulationMode;
14
+ provider: TProvider;
15
+ replayHref?: string;
16
+ result: VoiceAgentModelOutput<TResult>;
17
+ sessionId: string;
18
+ status: 'simulated';
19
+ turnId: string;
20
+ };
21
+ type ProviderListResolver<TContext, TSession extends VoiceSessionRecord, TProvider extends string> = readonly TProvider[] | ((input: VoiceAgentModelInput<TContext, TSession>) => readonly TProvider[] | Promise<readonly TProvider[]>);
22
+ export type VoiceProviderFailureSimulatorOptions<TContext extends VoiceProviderFailureSimulationContext<TProvider>, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown, TProvider extends string = string> = {
23
+ allowProviders?: ProviderListResolver<TContext, TSession, TProvider>;
24
+ fallback?: readonly TProvider[] | ((provider: TProvider, input: VoiceAgentModelInput<TContext, TSession>) => readonly TProvider[] | Promise<readonly TProvider[]>);
25
+ isProviderError?: (error: unknown, provider: TProvider) => boolean;
26
+ isRateLimitError?: (error: unknown, provider: TProvider) => boolean;
27
+ onProviderEvent?: (event: VoiceProviderRouterEvent<TProvider>, input: VoiceAgentModelInput<TContext, TSession>) => Promise<void> | void;
28
+ providerHealth?: boolean | VoiceProviderRouterHealthOptions;
29
+ providerLabel?: (provider: TProvider) => string;
30
+ providers: readonly TProvider[];
31
+ replayHref?: false | string | ((input: {
32
+ provider: TProvider;
33
+ sessionId: string;
34
+ turnId: string;
35
+ }) => string);
36
+ response?: (input: {
37
+ mode: VoiceProviderFailureSimulationMode;
38
+ provider: TProvider;
39
+ } & VoiceAgentModelInput<TContext, TSession>) => VoiceAgentModelOutput<TResult> | Promise<VoiceAgentModelOutput<TResult>>;
40
+ };
41
+ export declare const createVoiceProviderFailureSimulator: <TProvider extends string, TResult = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TContext extends VoiceProviderFailureSimulationContext<TProvider> = VoiceProviderFailureSimulationContext<TProvider>>(options: VoiceProviderFailureSimulatorOptions<TContext, TSession, TResult, TProvider>) => {
42
+ run: (provider: TProvider, mode: VoiceProviderFailureSimulationMode) => Promise<VoiceProviderFailureSimulationResult<TProvider, TResult>>;
43
+ };
44
+ export {};
package/dist/trace.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type VoiceTraceEventType = 'assistant.guardrail' | 'assistant.memory' | 'assistant.run' | 'agent.handoff' | 'agent.model' | 'agent.result' | 'agent.tool' | 'call.lifecycle' | 'session.error' | 'turn.assistant' | 'turn.committed' | 'turn.cost' | 'turn.transcript';
1
+ export type VoiceTraceEventType = 'assistant.guardrail' | 'assistant.memory' | 'assistant.run' | 'agent.handoff' | 'agent.model' | 'agent.result' | 'agent.tool' | 'call.handoff' | 'call.lifecycle' | 'session.error' | 'turn.assistant' | 'turn.committed' | 'turn.cost' | 'turn.transcript';
2
2
  export type VoiceTraceEvent<TPayload extends Record<string, unknown> = Record<string, unknown>> = {
3
3
  at: number;
4
4
  id?: string;
package/dist/types.d.ts CHANGED
@@ -269,6 +269,65 @@ export type VoiceCallLifecycleState = {
269
269
  lastEventAt: number;
270
270
  startedAt: number;
271
271
  };
272
+ export type VoiceHandoffAction = 'escalate' | 'no-answer' | 'transfer' | 'voicemail';
273
+ export type VoiceHandoffStatus = 'delivered' | 'failed' | 'skipped';
274
+ export type VoiceHandoffResult = {
275
+ deliveredAt?: number;
276
+ deliveredTo?: string;
277
+ error?: string;
278
+ metadata?: Record<string, unknown>;
279
+ status: VoiceHandoffStatus;
280
+ };
281
+ export type VoiceHandoffDeliveryQueueStatus = VoiceHandoffStatus | 'pending';
282
+ export type StoredVoiceHandoffDelivery<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
283
+ action: VoiceHandoffAction;
284
+ context: TContext;
285
+ createdAt: number;
286
+ deliveredAt?: number;
287
+ deliveries?: Record<string, VoiceHandoffResult & {
288
+ adapterId: string;
289
+ adapterKind?: string;
290
+ }>;
291
+ deliveryAttempts?: number;
292
+ deliveryError?: string;
293
+ deliveryStatus: VoiceHandoffDeliveryQueueStatus;
294
+ id: string;
295
+ metadata?: Record<string, unknown>;
296
+ reason?: string;
297
+ result?: TResult;
298
+ session: TSession;
299
+ sessionId: string;
300
+ target?: string;
301
+ updatedAt: number;
302
+ };
303
+ export type VoiceHandoffDeliveryStore<TDelivery extends StoredVoiceHandoffDelivery = StoredVoiceHandoffDelivery> = {
304
+ get: (id: string) => Promise<TDelivery | undefined> | TDelivery | undefined;
305
+ list: () => Promise<TDelivery[]> | TDelivery[];
306
+ remove: (id: string) => Promise<void> | void;
307
+ set: (id: string, delivery: TDelivery) => Promise<void> | void;
308
+ };
309
+ export type VoiceHandoffInput<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
310
+ action: VoiceHandoffAction;
311
+ api: VoiceSessionHandle<TContext, TSession, TResult>;
312
+ context: TContext;
313
+ metadata?: Record<string, unknown>;
314
+ reason?: string;
315
+ result?: TResult;
316
+ session: TSession;
317
+ target?: string;
318
+ };
319
+ export type VoiceHandoffAdapter<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
320
+ actions?: VoiceHandoffAction[];
321
+ handoff: (input: VoiceHandoffInput<TContext, TSession, TResult>) => Promise<VoiceHandoffResult> | VoiceHandoffResult;
322
+ id: string;
323
+ kind?: string;
324
+ };
325
+ export type VoiceHandoffConfig<TContext = unknown, TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = {
326
+ adapters: VoiceHandoffAdapter<TContext, TSession, TResult>[];
327
+ deliveryQueue?: VoiceHandoffDeliveryStore<StoredVoiceHandoffDelivery<TContext, TSession, TResult>>;
328
+ enqueueOnly?: boolean;
329
+ failMode?: 'record' | 'throw';
330
+ };
272
331
  export type VoiceSessionStore<TSession extends VoiceSessionRecord = VoiceSessionRecord> = SessionStore<TSession, VoiceSessionSummary>;
273
332
  export type VoiceLogger = {
274
333
  debug?: (message: string, meta?: Record<string, unknown>) => void;
@@ -567,6 +626,7 @@ export type VoicePluginConfig<TContext = unknown, TSession extends VoiceSessionR
567
626
  audioConditioning?: VoiceAudioConditioningConfig;
568
627
  logger?: VoiceLogger;
569
628
  htmx?: boolean | VoiceHTMXConfig<TSession, NoInfer<TResult>>;
629
+ handoff?: VoiceHandoffConfig<TContext, TSession, TResult>;
570
630
  ops?: VoiceRuntimeOpsConfig<TContext, TSession, TResult>;
571
631
  trace?: VoiceTraceEventStore;
572
632
  } & VoiceRouteConfig<TContext, TSession, TResult>;
@@ -588,6 +648,7 @@ export type CreateVoiceSessionOptions<TContext = unknown, TSession extends Voice
588
648
  sttLifecycle: VoiceSTTLifecycle;
589
649
  turnDetection: VoiceResolvedTurnDetectionConfig;
590
650
  audioConditioning?: VoiceResolvedAudioConditioningConfig;
651
+ handoff?: VoiceHandoffConfig<TContext, TSession, TResult>;
591
652
  route: VoiceNormalizedRouteConfig<TContext, TSession, TResult>;
592
653
  logger?: VoiceLogger;
593
654
  };
@@ -604,10 +665,17 @@ export type VoiceClientCloseMessage = {
604
665
  type: 'close';
605
666
  reason?: string;
606
667
  };
668
+ export type VoiceClientCallControlMessage = {
669
+ type: 'call_control';
670
+ action: 'complete' | 'escalate' | 'no-answer' | 'transfer' | 'voicemail';
671
+ metadata?: Record<string, unknown>;
672
+ reason?: string;
673
+ target?: string;
674
+ };
607
675
  export type VoiceClientPingMessage = {
608
676
  type: 'ping';
609
677
  };
610
- export type VoiceClientMessage = VoiceClientStartMessage | VoiceClientEndTurnMessage | VoiceClientCloseMessage | VoiceClientPingMessage;
678
+ export type VoiceClientMessage = VoiceClientStartMessage | VoiceClientEndTurnMessage | VoiceClientCloseMessage | VoiceClientCallControlMessage | VoiceClientPingMessage;
611
679
  export type VoiceServerSessionMessage = {
612
680
  type: 'session';
613
681
  sessionId: string;
@@ -642,6 +710,11 @@ export type VoiceServerCompleteMessage = {
642
710
  type: 'complete';
643
711
  sessionId: string;
644
712
  };
713
+ export type VoiceServerCallLifecycleMessage = {
714
+ type: 'call_lifecycle';
715
+ event: VoiceCallLifecycleEvent;
716
+ sessionId: string;
717
+ };
645
718
  export type VoiceServerErrorMessage = {
646
719
  type: 'error';
647
720
  message: string;
@@ -650,7 +723,7 @@ export type VoiceServerErrorMessage = {
650
723
  export type VoiceServerPongMessage = {
651
724
  type: 'pong';
652
725
  };
653
- export type VoiceServerMessage<TResult = unknown> = VoiceServerSessionMessage | VoiceServerPartialMessage | VoiceServerFinalMessage | VoiceServerTurnMessage<TResult> | VoiceServerAssistantMessage | VoiceServerAudioMessage | VoiceServerCompleteMessage | VoiceServerErrorMessage | VoiceServerPongMessage;
726
+ export type VoiceServerMessage<TResult = unknown> = VoiceServerSessionMessage | VoiceServerPartialMessage | VoiceServerFinalMessage | VoiceServerTurnMessage<TResult> | VoiceServerAssistantMessage | VoiceServerAudioMessage | VoiceServerCallLifecycleMessage | VoiceServerCompleteMessage | VoiceServerErrorMessage | VoiceServerPongMessage;
654
727
  export type VoiceConnectionOptions = {
655
728
  protocols?: string[];
656
729
  scenarioId?: string;
@@ -728,6 +801,7 @@ export type VoiceHTMXOptions<TSession extends VoiceSessionRecord = VoiceSessionR
728
801
  };
729
802
  export type VoiceHTMXConfig<TSession extends VoiceSessionRecord = VoiceSessionRecord, TResult = unknown> = VoiceHTMXRenderer<TSession, TResult> | VoiceHTMXOptions<TSession, TResult>;
730
803
  export type VoiceStreamState<TResult = unknown> = {
804
+ call: VoiceCallLifecycleState | null;
731
805
  sessionId: string | null;
732
806
  scenarioId: string | null;
733
807
  status: VoiceSessionStatus | 'idle';
@@ -744,6 +818,8 @@ export type VoiceStreamState<TResult = unknown> = {
744
818
  isConnected: boolean;
745
819
  };
746
820
  export type VoiceStream<TResult = unknown> = {
821
+ call: VoiceCallLifecycleState | null;
822
+ callControl: (message: Omit<VoiceClientCallControlMessage, 'type'>) => void;
747
823
  close: () => void;
748
824
  start: (input?: {
749
825
  scenarioId?: string;
@@ -810,6 +886,8 @@ export type VoiceBargeInBinding = {
810
886
  };
811
887
  export type VoiceController<TResult = unknown> = {
812
888
  bindHTMX: (options: VoiceHTMXBindingOptions) => () => void;
889
+ call: VoiceCallLifecycleState | null;
890
+ callControl: (message: Omit<VoiceClientCallControlMessage, 'type'>) => void;
813
891
  close: () => void;
814
892
  endTurn: () => void;
815
893
  start: (input?: {
@@ -855,6 +933,10 @@ export type VoiceStoreAction<TResult = unknown> = {
855
933
  sessionId: string;
856
934
  scenarioId?: string;
857
935
  status: VoiceSessionStatus;
936
+ } | {
937
+ type: 'call_lifecycle';
938
+ event: VoiceCallLifecycleEvent;
939
+ sessionId: string;
858
940
  } | {
859
941
  type: 'partial';
860
942
  transcript: Transcript;
@@ -1,2 +1,3 @@
1
1
  export { useVoiceStream } from './useVoiceStream';
2
2
  export { useVoiceController } from './useVoiceController';
3
+ export { useVoiceProviderStatus } from './useVoiceProviderStatus';
package/dist/vue/index.js CHANGED
@@ -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
  },
@@ -554,6 +590,7 @@ var useVoiceStream = (path, options = {}) => {
554
590
  const stream = createVoiceStream(path, options);
555
591
  const assistantAudio = shallowRef([]);
556
592
  const assistantTexts = shallowRef([]);
593
+ const call = shallowRef(null);
557
594
  const error = ref(null);
558
595
  const isConnected = ref(false);
559
596
  const partial = ref("");
@@ -563,6 +600,7 @@ var useVoiceStream = (path, options = {}) => {
563
600
  const sync = () => {
564
601
  assistantAudio.value = [...stream.assistantAudio];
565
602
  assistantTexts.value = [...stream.assistantTexts];
603
+ call.value = stream.call;
566
604
  error.value = stream.error;
567
605
  isConnected.value = stream.isConnected;
568
606
  partial.value = stream.partial;
@@ -580,6 +618,8 @@ var useVoiceStream = (path, options = {}) => {
580
618
  return {
581
619
  assistantAudio,
582
620
  assistantTexts,
621
+ call,
622
+ callControl: (message) => stream.callControl(message),
583
623
  close: () => destroy(),
584
624
  endTurn: () => stream.endTurn(),
585
625
  error,
@@ -1056,6 +1096,7 @@ var resolveVoiceRuntimePreset = (name = "default") => {
1056
1096
  var createInitialState2 = (stream) => ({
1057
1097
  assistantAudio: [...stream.assistantAudio],
1058
1098
  assistantTexts: [...stream.assistantTexts],
1099
+ call: stream.call,
1059
1100
  error: stream.error,
1060
1101
  isConnected: stream.isConnected,
1061
1102
  isRecording: false,
@@ -1085,6 +1126,7 @@ var createVoiceController = (path, options = {}) => {
1085
1126
  ...state,
1086
1127
  assistantAudio: [...stream.assistantAudio],
1087
1128
  assistantTexts: [...stream.assistantTexts],
1129
+ call: stream.call,
1088
1130
  error: stream.error,
1089
1131
  isConnected: stream.isConnected,
1090
1132
  partial: stream.partial,
@@ -1162,6 +1204,7 @@ var createVoiceController = (path, options = {}) => {
1162
1204
  bindHTMX(bindingOptions) {
1163
1205
  return bindVoiceHTMX(stream, bindingOptions);
1164
1206
  },
1207
+ callControl: (message) => stream.callControl(message),
1165
1208
  close,
1166
1209
  endTurn: () => stream.endTurn(),
1167
1210
  get error() {
@@ -1214,6 +1257,9 @@ var createVoiceController = (path, options = {}) => {
1214
1257
  },
1215
1258
  get assistantAudio() {
1216
1259
  return state.assistantAudio;
1260
+ },
1261
+ get call() {
1262
+ return state.call;
1217
1263
  }
1218
1264
  };
1219
1265
  };
@@ -1270,7 +1316,120 @@ var useVoiceController = (path, options = {}) => {
1270
1316
  turns
1271
1317
  };
1272
1318
  };
1319
+ // src/vue/useVoiceProviderStatus.ts
1320
+ import { onUnmounted as onUnmounted3, ref as ref3, shallowRef as shallowRef3 } from "vue";
1321
+
1322
+ // src/client/providerStatus.ts
1323
+ var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
1324
+ const fetchImpl = options.fetch ?? globalThis.fetch;
1325
+ const response = await fetchImpl(path);
1326
+ if (!response.ok) {
1327
+ throw new Error(`Voice provider status failed: HTTP ${response.status}`);
1328
+ }
1329
+ return await response.json();
1330
+ };
1331
+ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {}) => {
1332
+ const listeners = new Set;
1333
+ let closed = false;
1334
+ let timer;
1335
+ let snapshot = {
1336
+ error: null,
1337
+ isLoading: false,
1338
+ providers: []
1339
+ };
1340
+ const emit = () => {
1341
+ for (const listener of listeners) {
1342
+ listener();
1343
+ }
1344
+ };
1345
+ const refresh = async () => {
1346
+ if (closed) {
1347
+ return snapshot.providers;
1348
+ }
1349
+ snapshot = {
1350
+ ...snapshot,
1351
+ error: null,
1352
+ isLoading: true
1353
+ };
1354
+ emit();
1355
+ try {
1356
+ const providers = await fetchVoiceProviderStatus(path, options);
1357
+ snapshot = {
1358
+ error: null,
1359
+ isLoading: false,
1360
+ providers,
1361
+ updatedAt: Date.now()
1362
+ };
1363
+ emit();
1364
+ return providers;
1365
+ } catch (error) {
1366
+ snapshot = {
1367
+ ...snapshot,
1368
+ error: error instanceof Error ? error.message : String(error),
1369
+ isLoading: false
1370
+ };
1371
+ emit();
1372
+ throw error;
1373
+ }
1374
+ };
1375
+ const close = () => {
1376
+ closed = true;
1377
+ if (timer) {
1378
+ clearInterval(timer);
1379
+ timer = undefined;
1380
+ }
1381
+ listeners.clear();
1382
+ };
1383
+ if (options.intervalMs && options.intervalMs > 0) {
1384
+ timer = setInterval(() => {
1385
+ refresh().catch(() => {});
1386
+ }, options.intervalMs);
1387
+ }
1388
+ return {
1389
+ close,
1390
+ getServerSnapshot: () => snapshot,
1391
+ getSnapshot: () => snapshot,
1392
+ refresh,
1393
+ subscribe: (listener) => {
1394
+ listeners.add(listener);
1395
+ return () => {
1396
+ listeners.delete(listener);
1397
+ };
1398
+ }
1399
+ };
1400
+ };
1401
+
1402
+ // src/vue/useVoiceProviderStatus.ts
1403
+ var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
1404
+ const store = createVoiceProviderStatusStore(path, options);
1405
+ const error = ref3(null);
1406
+ const isLoading = ref3(false);
1407
+ const providers = shallowRef3([]);
1408
+ const updatedAt = ref3(undefined);
1409
+ const sync = () => {
1410
+ const snapshot = store.getSnapshot();
1411
+ error.value = snapshot.error;
1412
+ isLoading.value = snapshot.isLoading;
1413
+ providers.value = [...snapshot.providers];
1414
+ updatedAt.value = snapshot.updatedAt;
1415
+ };
1416
+ const unsubscribe = store.subscribe(sync);
1417
+ sync();
1418
+ store.refresh().catch(() => {});
1419
+ onUnmounted3(() => {
1420
+ unsubscribe();
1421
+ store.close();
1422
+ });
1423
+ return {
1424
+ error,
1425
+ isLoading,
1426
+ providers,
1427
+ refresh: store.refresh,
1428
+ updatedAt
1429
+ };
1430
+ };
1273
1431
  export {
1274
1432
  useVoiceStream,
1433
+ useVoiceProviderStatus,
1275
1434
  useVoiceController
1276
1435
  };
@@ -0,0 +1,9 @@
1
+ import { type VoiceProviderStatusClientOptions } from '../client/providerStatus';
2
+ import type { VoiceProviderHealthSummary } from '../providerHealth';
3
+ export declare const useVoiceProviderStatus: <TProvider extends string = string>(path?: string, options?: VoiceProviderStatusClientOptions) => {
4
+ error: import("vue").Ref<string | null, string | null>;
5
+ isLoading: import("vue").Ref<boolean, boolean>;
6
+ providers: import("vue").ShallowRef<VoiceProviderHealthSummary<TProvider>[], VoiceProviderHealthSummary<TProvider>[]>;
7
+ refresh: () => Promise<VoiceProviderHealthSummary<TProvider>[]>;
8
+ updatedAt: import("vue").Ref<number | undefined, number | undefined>;
9
+ };
@@ -12,6 +12,8 @@ export declare const useVoiceStream: <TResult = unknown>(path: string, options?:
12
12
  turnId?: string;
13
13
  }[]>;
14
14
  assistantTexts: import("vue").ShallowRef<string[], string[]>;
15
+ call: import("vue").ShallowRef<import("..").VoiceCallLifecycleState | null, import("..").VoiceCallLifecycleState | null>;
16
+ callControl: (message: Parameters<(message: Omit<import("..").VoiceClientCallControlMessage, "type">) => void>[0]) => void;
15
17
  close: () => void;
16
18
  endTurn: () => void;
17
19
  error: import("vue").Ref<string | null, string | null>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.4",
3
+ "version": "0.0.22-beta.40",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",