@absolutejs/voice 0.0.22-beta.129 → 0.0.22-beta.130

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.
@@ -409,6 +409,11 @@ var serverMessageToAction = (message) => {
409
409
  sessionId: message.sessionId,
410
410
  type: "complete"
411
411
  };
412
+ case "connection":
413
+ return {
414
+ reconnect: message.reconnect,
415
+ type: "connection"
416
+ };
412
417
  case "call_lifecycle":
413
418
  return {
414
419
  event: message.event,
@@ -501,6 +506,7 @@ var isVoiceServerMessage = (value) => {
501
506
  case "assistant":
502
507
  case "call_lifecycle":
503
508
  case "complete":
509
+ case "connection":
504
510
  case "error":
505
511
  case "final":
506
512
  case "partial":
@@ -542,6 +548,9 @@ var createVoiceConnection = (path, options = {}) => {
542
548
  sessionId: options.sessionId ?? createSessionId(),
543
549
  ws: null
544
550
  };
551
+ const emitConnection = (reconnect) => {
552
+ listeners.forEach((listener) => listener(reconnect));
553
+ };
545
554
  const clearTimers = () => {
546
555
  if (state.pingInterval) {
547
556
  clearInterval(state.pingInterval);
@@ -564,9 +573,28 @@ var createVoiceConnection = (path, options = {}) => {
564
573
  }
565
574
  };
566
575
  const scheduleReconnect = () => {
576
+ const nextAttemptAt = Date.now() + RECONNECT_DELAY_MS;
567
577
  state.reconnectAttempts += 1;
578
+ emitConnection({
579
+ reconnect: {
580
+ attempts: state.reconnectAttempts,
581
+ lastDisconnectAt: Date.now(),
582
+ maxAttempts: maxReconnectAttempts,
583
+ nextAttemptAt,
584
+ status: "reconnecting"
585
+ },
586
+ type: "connection"
587
+ });
568
588
  state.reconnectTimeout = setTimeout(() => {
569
589
  if (state.reconnectAttempts > maxReconnectAttempts) {
590
+ emitConnection({
591
+ reconnect: {
592
+ attempts: state.reconnectAttempts,
593
+ maxAttempts: maxReconnectAttempts,
594
+ status: "exhausted"
595
+ },
596
+ type: "connection"
597
+ });
570
598
  return;
571
599
  }
572
600
  connect();
@@ -576,9 +604,21 @@ var createVoiceConnection = (path, options = {}) => {
576
604
  const ws = new WebSocket(buildWsUrl(path, state.sessionId, state.scenarioId));
577
605
  ws.binaryType = "arraybuffer";
578
606
  ws.onopen = () => {
607
+ const wasReconnecting = state.reconnectAttempts > 0;
579
608
  state.isConnected = true;
580
- state.reconnectAttempts = 0;
581
609
  flushPendingMessages();
610
+ if (wasReconnecting) {
611
+ emitConnection({
612
+ reconnect: {
613
+ attempts: state.reconnectAttempts,
614
+ lastResumedAt: Date.now(),
615
+ maxAttempts: maxReconnectAttempts,
616
+ status: "resumed"
617
+ },
618
+ type: "connection"
619
+ });
620
+ state.reconnectAttempts = 0;
621
+ }
582
622
  listeners.forEach((listener) => listener({
583
623
  scenarioId: state.scenarioId ?? undefined,
584
624
  sessionId: state.sessionId,
@@ -608,6 +648,16 @@ var createVoiceConnection = (path, options = {}) => {
608
648
  const reconnectable = shouldReconnect && event.code !== WS_NORMAL_CLOSURE && state.reconnectAttempts < maxReconnectAttempts;
609
649
  if (reconnectable) {
610
650
  scheduleReconnect();
651
+ } else if (shouldReconnect && event.code !== WS_NORMAL_CLOSURE) {
652
+ emitConnection({
653
+ reconnect: {
654
+ attempts: state.reconnectAttempts,
655
+ lastDisconnectAt: Date.now(),
656
+ maxAttempts: maxReconnectAttempts,
657
+ status: "exhausted"
658
+ },
659
+ type: "connection"
660
+ });
611
661
  }
612
662
  };
613
663
  state.ws = ws;
@@ -678,6 +728,11 @@ var createVoiceConnection = (path, options = {}) => {
678
728
  };
679
729
 
680
730
  // src/client/store.ts
731
+ var createInitialReconnectState = () => ({
732
+ attempts: 0,
733
+ maxAttempts: 0,
734
+ status: "idle"
735
+ });
681
736
  var createInitialState = () => ({
682
737
  assistantAudio: [],
683
738
  assistantTexts: [],
@@ -686,6 +741,7 @@ var createInitialState = () => ({
686
741
  isConnected: false,
687
742
  scenarioId: null,
688
743
  partial: "",
744
+ reconnect: createInitialReconnectState(),
689
745
  sessionId: null,
690
746
  status: "idle",
691
747
  turns: []
@@ -742,7 +798,19 @@ var createVoiceStreamStore = () => {
742
798
  case "connected":
743
799
  state = {
744
800
  ...state,
745
- isConnected: true
801
+ isConnected: true,
802
+ reconnect: state.reconnect.status === "reconnecting" ? {
803
+ ...state.reconnect,
804
+ lastResumedAt: Date.now(),
805
+ nextAttemptAt: undefined,
806
+ status: "resumed"
807
+ } : state.reconnect
808
+ };
809
+ break;
810
+ case "connection":
811
+ state = {
812
+ ...state,
813
+ reconnect: action.reconnect
746
814
  };
747
815
  break;
748
816
  case "disconnected":
@@ -778,6 +846,12 @@ var createVoiceStreamStore = () => {
778
846
  error: null,
779
847
  isConnected: action.status === "active",
780
848
  partial: action.partial,
849
+ reconnect: state.reconnect.status === "reconnecting" ? {
850
+ ...state.reconnect,
851
+ lastResumedAt: Date.now(),
852
+ nextAttemptAt: undefined,
853
+ status: "resumed"
854
+ } : state.reconnect,
781
855
  scenarioId: action.scenarioId ?? state.scenarioId,
782
856
  sessionId: action.sessionId,
783
857
  status: action.status,
@@ -870,6 +944,9 @@ var createVoiceStream = (path, options = {}) => {
870
944
  get partial() {
871
945
  return store.getSnapshot().partial;
872
946
  },
947
+ get reconnect() {
948
+ return store.getSnapshot().reconnect;
949
+ },
873
950
  get sessionId() {
874
951
  return connection.getSessionId();
875
952
  },
@@ -915,6 +992,7 @@ class VoiceStreamService {
915
992
  const errorSignal = signal3(null);
916
993
  const isConnectedSignal = signal3(false);
917
994
  const partialSignal = signal3("");
995
+ const reconnectSignal = signal3(stream.reconnect);
918
996
  const sessionIdSignal = signal3(stream.sessionId);
919
997
  const statusSignal = signal3(stream.status);
920
998
  const turnsSignal = signal3([]);
@@ -925,6 +1003,7 @@ class VoiceStreamService {
925
1003
  errorSignal.set(stream.error);
926
1004
  isConnectedSignal.set(stream.isConnected);
927
1005
  partialSignal.set(stream.partial);
1006
+ reconnectSignal.set(stream.reconnect);
928
1007
  sessionIdSignal.set(stream.sessionId);
929
1008
  statusSignal.set(stream.status);
930
1009
  turnsSignal.set([...stream.turns]);
@@ -944,6 +1023,7 @@ class VoiceStreamService {
944
1023
  error: computed3(() => errorSignal()),
945
1024
  isConnected: computed3(() => isConnectedSignal()),
946
1025
  partial: computed3(() => partialSignal()),
1026
+ reconnect: computed3(() => reconnectSignal()),
947
1027
  sendAudio: (audio) => stream.sendAudio(audio),
948
1028
  sessionId: computed3(() => sessionIdSignal()),
949
1029
  status: computed3(() => statusSignal()),
@@ -1425,6 +1505,7 @@ var createInitialState2 = (stream) => ({
1425
1505
  isConnected: stream.isConnected,
1426
1506
  isRecording: false,
1427
1507
  partial: stream.partial,
1508
+ reconnect: stream.reconnect,
1428
1509
  recordingError: null,
1429
1510
  sessionId: stream.sessionId,
1430
1511
  scenarioId: stream.scenarioId,
@@ -1454,6 +1535,7 @@ var createVoiceController = (path, options = {}) => {
1454
1535
  error: stream.error,
1455
1536
  isConnected: stream.isConnected,
1456
1537
  partial: stream.partial,
1538
+ reconnect: stream.reconnect,
1457
1539
  sessionId: stream.sessionId,
1458
1540
  scenarioId: stream.scenarioId,
1459
1541
  status: stream.status,
@@ -1554,6 +1636,9 @@ var createVoiceController = (path, options = {}) => {
1554
1636
  get recordingError() {
1555
1637
  return state.recordingError;
1556
1638
  },
1639
+ get reconnect() {
1640
+ return state.reconnect;
1641
+ },
1557
1642
  sendAudio: (audio) => stream.sendAudio(audio),
1558
1643
  get sessionId() {
1559
1644
  return state.sessionId;
@@ -1609,6 +1694,7 @@ class VoiceControllerService {
1609
1694
  const isConnectedSignal = signal4(false);
1610
1695
  const isRecordingSignal = signal4(false);
1611
1696
  const partialSignal = signal4("");
1697
+ const reconnectSignal = signal4(controller.reconnect);
1612
1698
  const recordingErrorSignal = signal4(null);
1613
1699
  const sessionIdSignal = signal4(controller.sessionId);
1614
1700
  const statusSignal = signal4(controller.status);
@@ -1620,6 +1706,7 @@ class VoiceControllerService {
1620
1706
  isConnectedSignal.set(controller.isConnected);
1621
1707
  isRecordingSignal.set(controller.isRecording);
1622
1708
  partialSignal.set(controller.partial);
1709
+ reconnectSignal.set(controller.reconnect);
1623
1710
  recordingErrorSignal.set(controller.recordingError);
1624
1711
  sessionIdSignal.set(controller.sessionId);
1625
1712
  statusSignal.set(controller.status);
@@ -1640,6 +1727,7 @@ class VoiceControllerService {
1640
1727
  isConnected: computed4(() => isConnectedSignal()),
1641
1728
  isRecording: computed4(() => isRecordingSignal()),
1642
1729
  partial: computed4(() => partialSignal()),
1730
+ reconnect: computed4(() => reconnectSignal()),
1643
1731
  recordingError: computed4(() => recordingErrorSignal()),
1644
1732
  sendAudio: (audio) => controller.sendAudio(audio),
1645
1733
  sessionId: computed4(() => sessionIdSignal()),
@@ -15,6 +15,7 @@ export declare class VoiceControllerService {
15
15
  isConnected: import("@angular/core").Signal<boolean>;
16
16
  isRecording: import("@angular/core").Signal<boolean>;
17
17
  partial: import("@angular/core").Signal<string>;
18
+ reconnect: import("@angular/core").Signal<import("..").VoiceReconnectClientState>;
18
19
  recordingError: import("@angular/core").Signal<string | null>;
19
20
  sendAudio: (audio: Uint8Array | ArrayBuffer) => void;
20
21
  sessionId: import("@angular/core").Signal<string | null>;
@@ -15,6 +15,7 @@ export declare class VoiceStreamService {
15
15
  error: import("@angular/core").Signal<string | null>;
16
16
  isConnected: import("@angular/core").Signal<boolean>;
17
17
  partial: import("@angular/core").Signal<string>;
18
+ reconnect: import("@angular/core").Signal<import("..").VoiceReconnectClientState>;
18
19
  sendAudio: (audio: Uint8Array | ArrayBuffer) => void;
19
20
  sessionId: import("@angular/core").Signal<string | null>;
20
21
  status: import("@angular/core").Signal<import("..").VoiceSessionStatus | "idle">;
@@ -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
+ reconnect?: undefined;
10
11
  event?: undefined;
11
12
  message?: undefined;
12
13
  transcript?: undefined;
@@ -25,6 +26,7 @@ export declare const serverMessageToAction: <TResult = unknown>(message: VoiceSe
25
26
  receivedAt?: undefined;
26
27
  turnId?: undefined;
27
28
  sessionId?: undefined;
29
+ reconnect?: undefined;
28
30
  event?: undefined;
29
31
  message?: undefined;
30
32
  transcript?: undefined;
@@ -43,6 +45,26 @@ export declare const serverMessageToAction: <TResult = unknown>(message: VoiceSe
43
45
  receivedAt?: undefined;
44
46
  turnId?: undefined;
45
47
  text?: undefined;
48
+ reconnect?: undefined;
49
+ event?: undefined;
50
+ message?: undefined;
51
+ transcript?: undefined;
52
+ assistantTexts?: undefined;
53
+ call?: undefined;
54
+ partial?: undefined;
55
+ scenarioId?: undefined;
56
+ status?: undefined;
57
+ turns?: undefined;
58
+ turn?: undefined;
59
+ } | {
60
+ reconnect: import("..").VoiceReconnectClientState;
61
+ type: "connection";
62
+ chunk?: undefined;
63
+ format?: undefined;
64
+ receivedAt?: undefined;
65
+ turnId?: undefined;
66
+ text?: undefined;
67
+ sessionId?: undefined;
46
68
  event?: undefined;
47
69
  message?: undefined;
48
70
  transcript?: undefined;
@@ -62,6 +84,7 @@ export declare const serverMessageToAction: <TResult = unknown>(message: VoiceSe
62
84
  receivedAt?: undefined;
63
85
  turnId?: undefined;
64
86
  text?: undefined;
87
+ reconnect?: undefined;
65
88
  message?: undefined;
66
89
  transcript?: undefined;
67
90
  assistantTexts?: undefined;
@@ -80,6 +103,7 @@ export declare const serverMessageToAction: <TResult = unknown>(message: VoiceSe
80
103
  turnId?: undefined;
81
104
  text?: undefined;
82
105
  sessionId?: undefined;
106
+ reconnect?: undefined;
83
107
  event?: undefined;
84
108
  transcript?: undefined;
85
109
  assistantTexts?: undefined;
@@ -98,6 +122,7 @@ export declare const serverMessageToAction: <TResult = unknown>(message: VoiceSe
98
122
  turnId?: undefined;
99
123
  text?: undefined;
100
124
  sessionId?: undefined;
125
+ reconnect?: undefined;
101
126
  event?: undefined;
102
127
  message?: undefined;
103
128
  assistantTexts?: undefined;
@@ -116,6 +141,7 @@ export declare const serverMessageToAction: <TResult = unknown>(message: VoiceSe
116
141
  turnId?: undefined;
117
142
  text?: undefined;
118
143
  sessionId?: undefined;
144
+ reconnect?: undefined;
119
145
  event?: undefined;
120
146
  message?: undefined;
121
147
  assistantTexts?: undefined;
@@ -139,6 +165,7 @@ export declare const serverMessageToAction: <TResult = unknown>(message: VoiceSe
139
165
  receivedAt?: undefined;
140
166
  turnId?: undefined;
141
167
  text?: undefined;
168
+ reconnect?: undefined;
142
169
  event?: undefined;
143
170
  message?: undefined;
144
171
  transcript?: undefined;
@@ -153,6 +180,7 @@ export declare const serverMessageToAction: <TResult = unknown>(message: VoiceSe
153
180
  receivedAt?: undefined;
154
181
  turnId?: undefined;
155
182
  text?: undefined;
183
+ reconnect?: undefined;
156
184
  event?: undefined;
157
185
  message?: undefined;
158
186
  transcript?: undefined;
@@ -170,6 +198,7 @@ export declare const serverMessageToAction: <TResult = unknown>(message: VoiceSe
170
198
  turnId?: undefined;
171
199
  text?: undefined;
172
200
  sessionId?: undefined;
201
+ reconnect?: undefined;
173
202
  event?: undefined;
174
203
  message?: undefined;
175
204
  transcript?: undefined;
@@ -188,6 +188,11 @@ var serverMessageToAction = (message) => {
188
188
  sessionId: message.sessionId,
189
189
  type: "complete"
190
190
  };
191
+ case "connection":
192
+ return {
193
+ reconnect: message.reconnect,
194
+ type: "connection"
195
+ };
191
196
  case "call_lifecycle":
192
197
  return {
193
198
  event: message.event,
@@ -280,6 +285,7 @@ var isVoiceServerMessage = (value) => {
280
285
  case "assistant":
281
286
  case "call_lifecycle":
282
287
  case "complete":
288
+ case "connection":
283
289
  case "error":
284
290
  case "final":
285
291
  case "partial":
@@ -321,6 +327,9 @@ var createVoiceConnection = (path, options = {}) => {
321
327
  sessionId: options.sessionId ?? createSessionId(),
322
328
  ws: null
323
329
  };
330
+ const emitConnection = (reconnect) => {
331
+ listeners.forEach((listener) => listener(reconnect));
332
+ };
324
333
  const clearTimers = () => {
325
334
  if (state.pingInterval) {
326
335
  clearInterval(state.pingInterval);
@@ -343,9 +352,28 @@ var createVoiceConnection = (path, options = {}) => {
343
352
  }
344
353
  };
345
354
  const scheduleReconnect = () => {
355
+ const nextAttemptAt = Date.now() + RECONNECT_DELAY_MS;
346
356
  state.reconnectAttempts += 1;
357
+ emitConnection({
358
+ reconnect: {
359
+ attempts: state.reconnectAttempts,
360
+ lastDisconnectAt: Date.now(),
361
+ maxAttempts: maxReconnectAttempts,
362
+ nextAttemptAt,
363
+ status: "reconnecting"
364
+ },
365
+ type: "connection"
366
+ });
347
367
  state.reconnectTimeout = setTimeout(() => {
348
368
  if (state.reconnectAttempts > maxReconnectAttempts) {
369
+ emitConnection({
370
+ reconnect: {
371
+ attempts: state.reconnectAttempts,
372
+ maxAttempts: maxReconnectAttempts,
373
+ status: "exhausted"
374
+ },
375
+ type: "connection"
376
+ });
349
377
  return;
350
378
  }
351
379
  connect();
@@ -355,9 +383,21 @@ var createVoiceConnection = (path, options = {}) => {
355
383
  const ws = new WebSocket(buildWsUrl(path, state.sessionId, state.scenarioId));
356
384
  ws.binaryType = "arraybuffer";
357
385
  ws.onopen = () => {
386
+ const wasReconnecting = state.reconnectAttempts > 0;
358
387
  state.isConnected = true;
359
- state.reconnectAttempts = 0;
360
388
  flushPendingMessages();
389
+ if (wasReconnecting) {
390
+ emitConnection({
391
+ reconnect: {
392
+ attempts: state.reconnectAttempts,
393
+ lastResumedAt: Date.now(),
394
+ maxAttempts: maxReconnectAttempts,
395
+ status: "resumed"
396
+ },
397
+ type: "connection"
398
+ });
399
+ state.reconnectAttempts = 0;
400
+ }
361
401
  listeners.forEach((listener) => listener({
362
402
  scenarioId: state.scenarioId ?? undefined,
363
403
  sessionId: state.sessionId,
@@ -387,6 +427,16 @@ var createVoiceConnection = (path, options = {}) => {
387
427
  const reconnectable = shouldReconnect && event.code !== WS_NORMAL_CLOSURE && state.reconnectAttempts < maxReconnectAttempts;
388
428
  if (reconnectable) {
389
429
  scheduleReconnect();
430
+ } else if (shouldReconnect && event.code !== WS_NORMAL_CLOSURE) {
431
+ emitConnection({
432
+ reconnect: {
433
+ attempts: state.reconnectAttempts,
434
+ lastDisconnectAt: Date.now(),
435
+ maxAttempts: maxReconnectAttempts,
436
+ status: "exhausted"
437
+ },
438
+ type: "connection"
439
+ });
390
440
  }
391
441
  };
392
442
  state.ws = ws;
@@ -457,6 +507,11 @@ var createVoiceConnection = (path, options = {}) => {
457
507
  };
458
508
 
459
509
  // src/client/store.ts
510
+ var createInitialReconnectState = () => ({
511
+ attempts: 0,
512
+ maxAttempts: 0,
513
+ status: "idle"
514
+ });
460
515
  var createInitialState = () => ({
461
516
  assistantAudio: [],
462
517
  assistantTexts: [],
@@ -465,6 +520,7 @@ var createInitialState = () => ({
465
520
  isConnected: false,
466
521
  scenarioId: null,
467
522
  partial: "",
523
+ reconnect: createInitialReconnectState(),
468
524
  sessionId: null,
469
525
  status: "idle",
470
526
  turns: []
@@ -521,7 +577,19 @@ var createVoiceStreamStore = () => {
521
577
  case "connected":
522
578
  state = {
523
579
  ...state,
524
- isConnected: true
580
+ isConnected: true,
581
+ reconnect: state.reconnect.status === "reconnecting" ? {
582
+ ...state.reconnect,
583
+ lastResumedAt: Date.now(),
584
+ nextAttemptAt: undefined,
585
+ status: "resumed"
586
+ } : state.reconnect
587
+ };
588
+ break;
589
+ case "connection":
590
+ state = {
591
+ ...state,
592
+ reconnect: action.reconnect
525
593
  };
526
594
  break;
527
595
  case "disconnected":
@@ -557,6 +625,12 @@ var createVoiceStreamStore = () => {
557
625
  error: null,
558
626
  isConnected: action.status === "active",
559
627
  partial: action.partial,
628
+ reconnect: state.reconnect.status === "reconnecting" ? {
629
+ ...state.reconnect,
630
+ lastResumedAt: Date.now(),
631
+ nextAttemptAt: undefined,
632
+ status: "resumed"
633
+ } : state.reconnect,
560
634
  scenarioId: action.scenarioId ?? state.scenarioId,
561
635
  sessionId: action.sessionId,
562
636
  status: action.status,
@@ -649,6 +723,9 @@ var createVoiceStream = (path, options = {}) => {
649
723
  get partial() {
650
724
  return store.getSnapshot().partial;
651
725
  },
726
+ get reconnect() {
727
+ return store.getSnapshot().reconnect;
728
+ },
652
729
  get sessionId() {
653
730
  return connection.getSessionId();
654
731
  },
@@ -967,6 +1044,7 @@ var createInitialState2 = (stream) => ({
967
1044
  isConnected: stream.isConnected,
968
1045
  isRecording: false,
969
1046
  partial: stream.partial,
1047
+ reconnect: stream.reconnect,
970
1048
  recordingError: null,
971
1049
  sessionId: stream.sessionId,
972
1050
  scenarioId: stream.scenarioId,
@@ -996,6 +1074,7 @@ var createVoiceController = (path, options = {}) => {
996
1074
  error: stream.error,
997
1075
  isConnected: stream.isConnected,
998
1076
  partial: stream.partial,
1077
+ reconnect: stream.reconnect,
999
1078
  sessionId: stream.sessionId,
1000
1079
  scenarioId: stream.scenarioId,
1001
1080
  status: stream.status,
@@ -1096,6 +1175,9 @@ var createVoiceController = (path, options = {}) => {
1096
1175
  get recordingError() {
1097
1176
  return state.recordingError;
1098
1177
  },
1178
+ get reconnect() {
1179
+ return state.reconnect;
1180
+ },
1099
1181
  sendAudio: (audio) => stream.sendAudio(audio),
1100
1182
  get sessionId() {
1101
1183
  return state.sessionId;