@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.
- package/dist/angular/index.js +90 -2
- package/dist/angular/voice-controller.service.d.ts +1 -0
- package/dist/angular/voice-stream.service.d.ts +1 -0
- package/dist/client/actions.d.ts +29 -0
- package/dist/client/htmxBootstrap.js +84 -2
- package/dist/client/index.js +84 -2
- package/dist/react/index.js +94 -2
- package/dist/react/useVoiceController.d.ts +1 -0
- package/dist/react/useVoiceStream.d.ts +1 -0
- package/dist/svelte/index.js +84 -2
- package/dist/testing/index.js +84 -2
- package/dist/types.d.ts +20 -1
- package/dist/vue/index.js +90 -2
- package/dist/vue/useVoiceController.d.ts +1 -0
- package/dist/vue/useVoiceStream.d.ts +1 -0
- package/package.json +1 -1
package/dist/client/index.js
CHANGED
|
@@ -112,6 +112,7 @@ var isVoiceServerMessage = (value) => {
|
|
|
112
112
|
case "assistant":
|
|
113
113
|
case "call_lifecycle":
|
|
114
114
|
case "complete":
|
|
115
|
+
case "connection":
|
|
115
116
|
case "error":
|
|
116
117
|
case "final":
|
|
117
118
|
case "partial":
|
|
@@ -153,6 +154,9 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
153
154
|
sessionId: options.sessionId ?? createSessionId(),
|
|
154
155
|
ws: null
|
|
155
156
|
};
|
|
157
|
+
const emitConnection = (reconnect) => {
|
|
158
|
+
listeners.forEach((listener) => listener(reconnect));
|
|
159
|
+
};
|
|
156
160
|
const clearTimers = () => {
|
|
157
161
|
if (state.pingInterval) {
|
|
158
162
|
clearInterval(state.pingInterval);
|
|
@@ -175,9 +179,28 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
175
179
|
}
|
|
176
180
|
};
|
|
177
181
|
const scheduleReconnect = () => {
|
|
182
|
+
const nextAttemptAt = Date.now() + RECONNECT_DELAY_MS;
|
|
178
183
|
state.reconnectAttempts += 1;
|
|
184
|
+
emitConnection({
|
|
185
|
+
reconnect: {
|
|
186
|
+
attempts: state.reconnectAttempts,
|
|
187
|
+
lastDisconnectAt: Date.now(),
|
|
188
|
+
maxAttempts: maxReconnectAttempts,
|
|
189
|
+
nextAttemptAt,
|
|
190
|
+
status: "reconnecting"
|
|
191
|
+
},
|
|
192
|
+
type: "connection"
|
|
193
|
+
});
|
|
179
194
|
state.reconnectTimeout = setTimeout(() => {
|
|
180
195
|
if (state.reconnectAttempts > maxReconnectAttempts) {
|
|
196
|
+
emitConnection({
|
|
197
|
+
reconnect: {
|
|
198
|
+
attempts: state.reconnectAttempts,
|
|
199
|
+
maxAttempts: maxReconnectAttempts,
|
|
200
|
+
status: "exhausted"
|
|
201
|
+
},
|
|
202
|
+
type: "connection"
|
|
203
|
+
});
|
|
181
204
|
return;
|
|
182
205
|
}
|
|
183
206
|
connect();
|
|
@@ -187,9 +210,21 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
187
210
|
const ws = new WebSocket(buildWsUrl(path, state.sessionId, state.scenarioId));
|
|
188
211
|
ws.binaryType = "arraybuffer";
|
|
189
212
|
ws.onopen = () => {
|
|
213
|
+
const wasReconnecting = state.reconnectAttempts > 0;
|
|
190
214
|
state.isConnected = true;
|
|
191
|
-
state.reconnectAttempts = 0;
|
|
192
215
|
flushPendingMessages();
|
|
216
|
+
if (wasReconnecting) {
|
|
217
|
+
emitConnection({
|
|
218
|
+
reconnect: {
|
|
219
|
+
attempts: state.reconnectAttempts,
|
|
220
|
+
lastResumedAt: Date.now(),
|
|
221
|
+
maxAttempts: maxReconnectAttempts,
|
|
222
|
+
status: "resumed"
|
|
223
|
+
},
|
|
224
|
+
type: "connection"
|
|
225
|
+
});
|
|
226
|
+
state.reconnectAttempts = 0;
|
|
227
|
+
}
|
|
193
228
|
listeners.forEach((listener) => listener({
|
|
194
229
|
scenarioId: state.scenarioId ?? undefined,
|
|
195
230
|
sessionId: state.sessionId,
|
|
@@ -219,6 +254,16 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
219
254
|
const reconnectable = shouldReconnect && event.code !== WS_NORMAL_CLOSURE && state.reconnectAttempts < maxReconnectAttempts;
|
|
220
255
|
if (reconnectable) {
|
|
221
256
|
scheduleReconnect();
|
|
257
|
+
} else if (shouldReconnect && event.code !== WS_NORMAL_CLOSURE) {
|
|
258
|
+
emitConnection({
|
|
259
|
+
reconnect: {
|
|
260
|
+
attempts: state.reconnectAttempts,
|
|
261
|
+
lastDisconnectAt: Date.now(),
|
|
262
|
+
maxAttempts: maxReconnectAttempts,
|
|
263
|
+
status: "exhausted"
|
|
264
|
+
},
|
|
265
|
+
type: "connection"
|
|
266
|
+
});
|
|
222
267
|
}
|
|
223
268
|
};
|
|
224
269
|
state.ws = ws;
|
|
@@ -681,6 +726,11 @@ var serverMessageToAction = (message) => {
|
|
|
681
726
|
sessionId: message.sessionId,
|
|
682
727
|
type: "complete"
|
|
683
728
|
};
|
|
729
|
+
case "connection":
|
|
730
|
+
return {
|
|
731
|
+
reconnect: message.reconnect,
|
|
732
|
+
type: "connection"
|
|
733
|
+
};
|
|
684
734
|
case "call_lifecycle":
|
|
685
735
|
return {
|
|
686
736
|
event: message.event,
|
|
@@ -731,6 +781,11 @@ var serverMessageToAction = (message) => {
|
|
|
731
781
|
};
|
|
732
782
|
|
|
733
783
|
// src/client/store.ts
|
|
784
|
+
var createInitialReconnectState = () => ({
|
|
785
|
+
attempts: 0,
|
|
786
|
+
maxAttempts: 0,
|
|
787
|
+
status: "idle"
|
|
788
|
+
});
|
|
734
789
|
var createInitialState2 = () => ({
|
|
735
790
|
assistantAudio: [],
|
|
736
791
|
assistantTexts: [],
|
|
@@ -739,6 +794,7 @@ var createInitialState2 = () => ({
|
|
|
739
794
|
isConnected: false,
|
|
740
795
|
scenarioId: null,
|
|
741
796
|
partial: "",
|
|
797
|
+
reconnect: createInitialReconnectState(),
|
|
742
798
|
sessionId: null,
|
|
743
799
|
status: "idle",
|
|
744
800
|
turns: []
|
|
@@ -795,7 +851,19 @@ var createVoiceStreamStore = () => {
|
|
|
795
851
|
case "connected":
|
|
796
852
|
state = {
|
|
797
853
|
...state,
|
|
798
|
-
isConnected: true
|
|
854
|
+
isConnected: true,
|
|
855
|
+
reconnect: state.reconnect.status === "reconnecting" ? {
|
|
856
|
+
...state.reconnect,
|
|
857
|
+
lastResumedAt: Date.now(),
|
|
858
|
+
nextAttemptAt: undefined,
|
|
859
|
+
status: "resumed"
|
|
860
|
+
} : state.reconnect
|
|
861
|
+
};
|
|
862
|
+
break;
|
|
863
|
+
case "connection":
|
|
864
|
+
state = {
|
|
865
|
+
...state,
|
|
866
|
+
reconnect: action.reconnect
|
|
799
867
|
};
|
|
800
868
|
break;
|
|
801
869
|
case "disconnected":
|
|
@@ -831,6 +899,12 @@ var createVoiceStreamStore = () => {
|
|
|
831
899
|
error: null,
|
|
832
900
|
isConnected: action.status === "active",
|
|
833
901
|
partial: action.partial,
|
|
902
|
+
reconnect: state.reconnect.status === "reconnecting" ? {
|
|
903
|
+
...state.reconnect,
|
|
904
|
+
lastResumedAt: Date.now(),
|
|
905
|
+
nextAttemptAt: undefined,
|
|
906
|
+
status: "resumed"
|
|
907
|
+
} : state.reconnect,
|
|
834
908
|
scenarioId: action.scenarioId ?? state.scenarioId,
|
|
835
909
|
sessionId: action.sessionId,
|
|
836
910
|
status: action.status,
|
|
@@ -923,6 +997,9 @@ var createVoiceStream = (path, options = {}) => {
|
|
|
923
997
|
get partial() {
|
|
924
998
|
return store.getSnapshot().partial;
|
|
925
999
|
},
|
|
1000
|
+
get reconnect() {
|
|
1001
|
+
return store.getSnapshot().reconnect;
|
|
1002
|
+
},
|
|
926
1003
|
get sessionId() {
|
|
927
1004
|
return connection.getSessionId();
|
|
928
1005
|
},
|
|
@@ -1419,6 +1496,7 @@ var createInitialState3 = (stream) => ({
|
|
|
1419
1496
|
isConnected: stream.isConnected,
|
|
1420
1497
|
isRecording: false,
|
|
1421
1498
|
partial: stream.partial,
|
|
1499
|
+
reconnect: stream.reconnect,
|
|
1422
1500
|
recordingError: null,
|
|
1423
1501
|
sessionId: stream.sessionId,
|
|
1424
1502
|
scenarioId: stream.scenarioId,
|
|
@@ -1448,6 +1526,7 @@ var createVoiceController = (path, options = {}) => {
|
|
|
1448
1526
|
error: stream.error,
|
|
1449
1527
|
isConnected: stream.isConnected,
|
|
1450
1528
|
partial: stream.partial,
|
|
1529
|
+
reconnect: stream.reconnect,
|
|
1451
1530
|
sessionId: stream.sessionId,
|
|
1452
1531
|
scenarioId: stream.scenarioId,
|
|
1453
1532
|
status: stream.status,
|
|
@@ -1548,6 +1627,9 @@ var createVoiceController = (path, options = {}) => {
|
|
|
1548
1627
|
get recordingError() {
|
|
1549
1628
|
return state.recordingError;
|
|
1550
1629
|
},
|
|
1630
|
+
get reconnect() {
|
|
1631
|
+
return state.reconnect;
|
|
1632
|
+
},
|
|
1551
1633
|
sendAudio: (audio) => stream.sendAudio(audio),
|
|
1552
1634
|
get sessionId() {
|
|
1553
1635
|
return state.sessionId;
|
package/dist/react/index.js
CHANGED
|
@@ -2546,6 +2546,11 @@ var serverMessageToAction = (message) => {
|
|
|
2546
2546
|
sessionId: message.sessionId,
|
|
2547
2547
|
type: "complete"
|
|
2548
2548
|
};
|
|
2549
|
+
case "connection":
|
|
2550
|
+
return {
|
|
2551
|
+
reconnect: message.reconnect,
|
|
2552
|
+
type: "connection"
|
|
2553
|
+
};
|
|
2549
2554
|
case "call_lifecycle":
|
|
2550
2555
|
return {
|
|
2551
2556
|
event: message.event,
|
|
@@ -2638,6 +2643,7 @@ var isVoiceServerMessage = (value) => {
|
|
|
2638
2643
|
case "assistant":
|
|
2639
2644
|
case "call_lifecycle":
|
|
2640
2645
|
case "complete":
|
|
2646
|
+
case "connection":
|
|
2641
2647
|
case "error":
|
|
2642
2648
|
case "final":
|
|
2643
2649
|
case "partial":
|
|
@@ -2679,6 +2685,9 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
2679
2685
|
sessionId: options.sessionId ?? createSessionId(),
|
|
2680
2686
|
ws: null
|
|
2681
2687
|
};
|
|
2688
|
+
const emitConnection = (reconnect) => {
|
|
2689
|
+
listeners.forEach((listener) => listener(reconnect));
|
|
2690
|
+
};
|
|
2682
2691
|
const clearTimers = () => {
|
|
2683
2692
|
if (state.pingInterval) {
|
|
2684
2693
|
clearInterval(state.pingInterval);
|
|
@@ -2701,9 +2710,28 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
2701
2710
|
}
|
|
2702
2711
|
};
|
|
2703
2712
|
const scheduleReconnect = () => {
|
|
2713
|
+
const nextAttemptAt = Date.now() + RECONNECT_DELAY_MS;
|
|
2704
2714
|
state.reconnectAttempts += 1;
|
|
2715
|
+
emitConnection({
|
|
2716
|
+
reconnect: {
|
|
2717
|
+
attempts: state.reconnectAttempts,
|
|
2718
|
+
lastDisconnectAt: Date.now(),
|
|
2719
|
+
maxAttempts: maxReconnectAttempts,
|
|
2720
|
+
nextAttemptAt,
|
|
2721
|
+
status: "reconnecting"
|
|
2722
|
+
},
|
|
2723
|
+
type: "connection"
|
|
2724
|
+
});
|
|
2705
2725
|
state.reconnectTimeout = setTimeout(() => {
|
|
2706
2726
|
if (state.reconnectAttempts > maxReconnectAttempts) {
|
|
2727
|
+
emitConnection({
|
|
2728
|
+
reconnect: {
|
|
2729
|
+
attempts: state.reconnectAttempts,
|
|
2730
|
+
maxAttempts: maxReconnectAttempts,
|
|
2731
|
+
status: "exhausted"
|
|
2732
|
+
},
|
|
2733
|
+
type: "connection"
|
|
2734
|
+
});
|
|
2707
2735
|
return;
|
|
2708
2736
|
}
|
|
2709
2737
|
connect();
|
|
@@ -2713,9 +2741,21 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
2713
2741
|
const ws = new WebSocket(buildWsUrl(path, state.sessionId, state.scenarioId));
|
|
2714
2742
|
ws.binaryType = "arraybuffer";
|
|
2715
2743
|
ws.onopen = () => {
|
|
2744
|
+
const wasReconnecting = state.reconnectAttempts > 0;
|
|
2716
2745
|
state.isConnected = true;
|
|
2717
|
-
state.reconnectAttempts = 0;
|
|
2718
2746
|
flushPendingMessages();
|
|
2747
|
+
if (wasReconnecting) {
|
|
2748
|
+
emitConnection({
|
|
2749
|
+
reconnect: {
|
|
2750
|
+
attempts: state.reconnectAttempts,
|
|
2751
|
+
lastResumedAt: Date.now(),
|
|
2752
|
+
maxAttempts: maxReconnectAttempts,
|
|
2753
|
+
status: "resumed"
|
|
2754
|
+
},
|
|
2755
|
+
type: "connection"
|
|
2756
|
+
});
|
|
2757
|
+
state.reconnectAttempts = 0;
|
|
2758
|
+
}
|
|
2719
2759
|
listeners.forEach((listener) => listener({
|
|
2720
2760
|
scenarioId: state.scenarioId ?? undefined,
|
|
2721
2761
|
sessionId: state.sessionId,
|
|
@@ -2745,6 +2785,16 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
2745
2785
|
const reconnectable = shouldReconnect && event.code !== WS_NORMAL_CLOSURE && state.reconnectAttempts < maxReconnectAttempts;
|
|
2746
2786
|
if (reconnectable) {
|
|
2747
2787
|
scheduleReconnect();
|
|
2788
|
+
} else if (shouldReconnect && event.code !== WS_NORMAL_CLOSURE) {
|
|
2789
|
+
emitConnection({
|
|
2790
|
+
reconnect: {
|
|
2791
|
+
attempts: state.reconnectAttempts,
|
|
2792
|
+
lastDisconnectAt: Date.now(),
|
|
2793
|
+
maxAttempts: maxReconnectAttempts,
|
|
2794
|
+
status: "exhausted"
|
|
2795
|
+
},
|
|
2796
|
+
type: "connection"
|
|
2797
|
+
});
|
|
2748
2798
|
}
|
|
2749
2799
|
};
|
|
2750
2800
|
state.ws = ws;
|
|
@@ -2815,6 +2865,11 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
2815
2865
|
};
|
|
2816
2866
|
|
|
2817
2867
|
// src/client/store.ts
|
|
2868
|
+
var createInitialReconnectState = () => ({
|
|
2869
|
+
attempts: 0,
|
|
2870
|
+
maxAttempts: 0,
|
|
2871
|
+
status: "idle"
|
|
2872
|
+
});
|
|
2818
2873
|
var createInitialState = () => ({
|
|
2819
2874
|
assistantAudio: [],
|
|
2820
2875
|
assistantTexts: [],
|
|
@@ -2823,6 +2878,7 @@ var createInitialState = () => ({
|
|
|
2823
2878
|
isConnected: false,
|
|
2824
2879
|
scenarioId: null,
|
|
2825
2880
|
partial: "",
|
|
2881
|
+
reconnect: createInitialReconnectState(),
|
|
2826
2882
|
sessionId: null,
|
|
2827
2883
|
status: "idle",
|
|
2828
2884
|
turns: []
|
|
@@ -2879,7 +2935,19 @@ var createVoiceStreamStore = () => {
|
|
|
2879
2935
|
case "connected":
|
|
2880
2936
|
state = {
|
|
2881
2937
|
...state,
|
|
2882
|
-
isConnected: true
|
|
2938
|
+
isConnected: true,
|
|
2939
|
+
reconnect: state.reconnect.status === "reconnecting" ? {
|
|
2940
|
+
...state.reconnect,
|
|
2941
|
+
lastResumedAt: Date.now(),
|
|
2942
|
+
nextAttemptAt: undefined,
|
|
2943
|
+
status: "resumed"
|
|
2944
|
+
} : state.reconnect
|
|
2945
|
+
};
|
|
2946
|
+
break;
|
|
2947
|
+
case "connection":
|
|
2948
|
+
state = {
|
|
2949
|
+
...state,
|
|
2950
|
+
reconnect: action.reconnect
|
|
2883
2951
|
};
|
|
2884
2952
|
break;
|
|
2885
2953
|
case "disconnected":
|
|
@@ -2915,6 +2983,12 @@ var createVoiceStreamStore = () => {
|
|
|
2915
2983
|
error: null,
|
|
2916
2984
|
isConnected: action.status === "active",
|
|
2917
2985
|
partial: action.partial,
|
|
2986
|
+
reconnect: state.reconnect.status === "reconnecting" ? {
|
|
2987
|
+
...state.reconnect,
|
|
2988
|
+
lastResumedAt: Date.now(),
|
|
2989
|
+
nextAttemptAt: undefined,
|
|
2990
|
+
status: "resumed"
|
|
2991
|
+
} : state.reconnect,
|
|
2918
2992
|
scenarioId: action.scenarioId ?? state.scenarioId,
|
|
2919
2993
|
sessionId: action.sessionId,
|
|
2920
2994
|
status: action.status,
|
|
@@ -3007,6 +3081,9 @@ var createVoiceStream = (path, options = {}) => {
|
|
|
3007
3081
|
get partial() {
|
|
3008
3082
|
return store.getSnapshot().partial;
|
|
3009
3083
|
},
|
|
3084
|
+
get reconnect() {
|
|
3085
|
+
return store.getSnapshot().reconnect;
|
|
3086
|
+
},
|
|
3010
3087
|
get sessionId() {
|
|
3011
3088
|
return connection.getSessionId();
|
|
3012
3089
|
},
|
|
@@ -3045,6 +3122,11 @@ var EMPTY_SNAPSHOT = {
|
|
|
3045
3122
|
error: null,
|
|
3046
3123
|
isConnected: false,
|
|
3047
3124
|
partial: "",
|
|
3125
|
+
reconnect: {
|
|
3126
|
+
attempts: 0,
|
|
3127
|
+
maxAttempts: 0,
|
|
3128
|
+
status: "idle"
|
|
3129
|
+
},
|
|
3048
3130
|
sessionId: "",
|
|
3049
3131
|
status: "idle",
|
|
3050
3132
|
turns: []
|
|
@@ -3535,6 +3617,7 @@ var createInitialState2 = (stream) => ({
|
|
|
3535
3617
|
isConnected: stream.isConnected,
|
|
3536
3618
|
isRecording: false,
|
|
3537
3619
|
partial: stream.partial,
|
|
3620
|
+
reconnect: stream.reconnect,
|
|
3538
3621
|
recordingError: null,
|
|
3539
3622
|
sessionId: stream.sessionId,
|
|
3540
3623
|
scenarioId: stream.scenarioId,
|
|
@@ -3564,6 +3647,7 @@ var createVoiceController = (path, options = {}) => {
|
|
|
3564
3647
|
error: stream.error,
|
|
3565
3648
|
isConnected: stream.isConnected,
|
|
3566
3649
|
partial: stream.partial,
|
|
3650
|
+
reconnect: stream.reconnect,
|
|
3567
3651
|
sessionId: stream.sessionId,
|
|
3568
3652
|
scenarioId: stream.scenarioId,
|
|
3569
3653
|
status: stream.status,
|
|
@@ -3664,6 +3748,9 @@ var createVoiceController = (path, options = {}) => {
|
|
|
3664
3748
|
get recordingError() {
|
|
3665
3749
|
return state.recordingError;
|
|
3666
3750
|
},
|
|
3751
|
+
get reconnect() {
|
|
3752
|
+
return state.reconnect;
|
|
3753
|
+
},
|
|
3667
3754
|
sendAudio: (audio) => stream.sendAudio(audio),
|
|
3668
3755
|
get sessionId() {
|
|
3669
3756
|
return state.sessionId;
|
|
@@ -3713,6 +3800,11 @@ var EMPTY_SNAPSHOT2 = {
|
|
|
3713
3800
|
isConnected: false,
|
|
3714
3801
|
isRecording: false,
|
|
3715
3802
|
partial: "",
|
|
3803
|
+
reconnect: {
|
|
3804
|
+
attempts: 0,
|
|
3805
|
+
maxAttempts: 0,
|
|
3806
|
+
status: "idle"
|
|
3807
|
+
},
|
|
3716
3808
|
recordingError: null,
|
|
3717
3809
|
sessionId: "",
|
|
3718
3810
|
status: "idle",
|
|
@@ -12,6 +12,7 @@ export declare const useVoiceController: <TResult = unknown>(path: string, optio
|
|
|
12
12
|
sessionId: string | null;
|
|
13
13
|
scenarioId: string | null;
|
|
14
14
|
status: import("..").VoiceSessionStatus | "idle";
|
|
15
|
+
reconnect: import("..").VoiceReconnectClientState;
|
|
15
16
|
partial: string;
|
|
16
17
|
turns: import("..").VoiceTurnRecord<TResult>[];
|
|
17
18
|
assistantTexts: string[];
|
|
@@ -8,6 +8,7 @@ export declare const useVoiceStream: <TResult = unknown>(path: string, options?:
|
|
|
8
8
|
sessionId: string | null;
|
|
9
9
|
scenarioId: string | null;
|
|
10
10
|
status: import("..").VoiceSessionStatus | "idle";
|
|
11
|
+
reconnect: import("..").VoiceReconnectClientState;
|
|
11
12
|
partial: string;
|
|
12
13
|
turns: import("..").VoiceTurnRecord<TResult>[];
|
|
13
14
|
assistantTexts: string[];
|
package/dist/svelte/index.js
CHANGED
|
@@ -844,6 +844,11 @@ var serverMessageToAction = (message) => {
|
|
|
844
844
|
sessionId: message.sessionId,
|
|
845
845
|
type: "complete"
|
|
846
846
|
};
|
|
847
|
+
case "connection":
|
|
848
|
+
return {
|
|
849
|
+
reconnect: message.reconnect,
|
|
850
|
+
type: "connection"
|
|
851
|
+
};
|
|
847
852
|
case "call_lifecycle":
|
|
848
853
|
return {
|
|
849
854
|
event: message.event,
|
|
@@ -936,6 +941,7 @@ var isVoiceServerMessage = (value) => {
|
|
|
936
941
|
case "assistant":
|
|
937
942
|
case "call_lifecycle":
|
|
938
943
|
case "complete":
|
|
944
|
+
case "connection":
|
|
939
945
|
case "error":
|
|
940
946
|
case "final":
|
|
941
947
|
case "partial":
|
|
@@ -977,6 +983,9 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
977
983
|
sessionId: options.sessionId ?? createSessionId(),
|
|
978
984
|
ws: null
|
|
979
985
|
};
|
|
986
|
+
const emitConnection = (reconnect) => {
|
|
987
|
+
listeners.forEach((listener) => listener(reconnect));
|
|
988
|
+
};
|
|
980
989
|
const clearTimers = () => {
|
|
981
990
|
if (state.pingInterval) {
|
|
982
991
|
clearInterval(state.pingInterval);
|
|
@@ -999,9 +1008,28 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
999
1008
|
}
|
|
1000
1009
|
};
|
|
1001
1010
|
const scheduleReconnect = () => {
|
|
1011
|
+
const nextAttemptAt = Date.now() + RECONNECT_DELAY_MS;
|
|
1002
1012
|
state.reconnectAttempts += 1;
|
|
1013
|
+
emitConnection({
|
|
1014
|
+
reconnect: {
|
|
1015
|
+
attempts: state.reconnectAttempts,
|
|
1016
|
+
lastDisconnectAt: Date.now(),
|
|
1017
|
+
maxAttempts: maxReconnectAttempts,
|
|
1018
|
+
nextAttemptAt,
|
|
1019
|
+
status: "reconnecting"
|
|
1020
|
+
},
|
|
1021
|
+
type: "connection"
|
|
1022
|
+
});
|
|
1003
1023
|
state.reconnectTimeout = setTimeout(() => {
|
|
1004
1024
|
if (state.reconnectAttempts > maxReconnectAttempts) {
|
|
1025
|
+
emitConnection({
|
|
1026
|
+
reconnect: {
|
|
1027
|
+
attempts: state.reconnectAttempts,
|
|
1028
|
+
maxAttempts: maxReconnectAttempts,
|
|
1029
|
+
status: "exhausted"
|
|
1030
|
+
},
|
|
1031
|
+
type: "connection"
|
|
1032
|
+
});
|
|
1005
1033
|
return;
|
|
1006
1034
|
}
|
|
1007
1035
|
connect();
|
|
@@ -1011,9 +1039,21 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
1011
1039
|
const ws = new WebSocket(buildWsUrl(path, state.sessionId, state.scenarioId));
|
|
1012
1040
|
ws.binaryType = "arraybuffer";
|
|
1013
1041
|
ws.onopen = () => {
|
|
1042
|
+
const wasReconnecting = state.reconnectAttempts > 0;
|
|
1014
1043
|
state.isConnected = true;
|
|
1015
|
-
state.reconnectAttempts = 0;
|
|
1016
1044
|
flushPendingMessages();
|
|
1045
|
+
if (wasReconnecting) {
|
|
1046
|
+
emitConnection({
|
|
1047
|
+
reconnect: {
|
|
1048
|
+
attempts: state.reconnectAttempts,
|
|
1049
|
+
lastResumedAt: Date.now(),
|
|
1050
|
+
maxAttempts: maxReconnectAttempts,
|
|
1051
|
+
status: "resumed"
|
|
1052
|
+
},
|
|
1053
|
+
type: "connection"
|
|
1054
|
+
});
|
|
1055
|
+
state.reconnectAttempts = 0;
|
|
1056
|
+
}
|
|
1017
1057
|
listeners.forEach((listener) => listener({
|
|
1018
1058
|
scenarioId: state.scenarioId ?? undefined,
|
|
1019
1059
|
sessionId: state.sessionId,
|
|
@@ -1043,6 +1083,16 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
1043
1083
|
const reconnectable = shouldReconnect && event.code !== WS_NORMAL_CLOSURE && state.reconnectAttempts < maxReconnectAttempts;
|
|
1044
1084
|
if (reconnectable) {
|
|
1045
1085
|
scheduleReconnect();
|
|
1086
|
+
} else if (shouldReconnect && event.code !== WS_NORMAL_CLOSURE) {
|
|
1087
|
+
emitConnection({
|
|
1088
|
+
reconnect: {
|
|
1089
|
+
attempts: state.reconnectAttempts,
|
|
1090
|
+
lastDisconnectAt: Date.now(),
|
|
1091
|
+
maxAttempts: maxReconnectAttempts,
|
|
1092
|
+
status: "exhausted"
|
|
1093
|
+
},
|
|
1094
|
+
type: "connection"
|
|
1095
|
+
});
|
|
1046
1096
|
}
|
|
1047
1097
|
};
|
|
1048
1098
|
state.ws = ws;
|
|
@@ -1113,6 +1163,11 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
1113
1163
|
};
|
|
1114
1164
|
|
|
1115
1165
|
// src/client/store.ts
|
|
1166
|
+
var createInitialReconnectState = () => ({
|
|
1167
|
+
attempts: 0,
|
|
1168
|
+
maxAttempts: 0,
|
|
1169
|
+
status: "idle"
|
|
1170
|
+
});
|
|
1116
1171
|
var createInitialState = () => ({
|
|
1117
1172
|
assistantAudio: [],
|
|
1118
1173
|
assistantTexts: [],
|
|
@@ -1121,6 +1176,7 @@ var createInitialState = () => ({
|
|
|
1121
1176
|
isConnected: false,
|
|
1122
1177
|
scenarioId: null,
|
|
1123
1178
|
partial: "",
|
|
1179
|
+
reconnect: createInitialReconnectState(),
|
|
1124
1180
|
sessionId: null,
|
|
1125
1181
|
status: "idle",
|
|
1126
1182
|
turns: []
|
|
@@ -1177,7 +1233,19 @@ var createVoiceStreamStore = () => {
|
|
|
1177
1233
|
case "connected":
|
|
1178
1234
|
state = {
|
|
1179
1235
|
...state,
|
|
1180
|
-
isConnected: true
|
|
1236
|
+
isConnected: true,
|
|
1237
|
+
reconnect: state.reconnect.status === "reconnecting" ? {
|
|
1238
|
+
...state.reconnect,
|
|
1239
|
+
lastResumedAt: Date.now(),
|
|
1240
|
+
nextAttemptAt: undefined,
|
|
1241
|
+
status: "resumed"
|
|
1242
|
+
} : state.reconnect
|
|
1243
|
+
};
|
|
1244
|
+
break;
|
|
1245
|
+
case "connection":
|
|
1246
|
+
state = {
|
|
1247
|
+
...state,
|
|
1248
|
+
reconnect: action.reconnect
|
|
1181
1249
|
};
|
|
1182
1250
|
break;
|
|
1183
1251
|
case "disconnected":
|
|
@@ -1213,6 +1281,12 @@ var createVoiceStreamStore = () => {
|
|
|
1213
1281
|
error: null,
|
|
1214
1282
|
isConnected: action.status === "active",
|
|
1215
1283
|
partial: action.partial,
|
|
1284
|
+
reconnect: state.reconnect.status === "reconnecting" ? {
|
|
1285
|
+
...state.reconnect,
|
|
1286
|
+
lastResumedAt: Date.now(),
|
|
1287
|
+
nextAttemptAt: undefined,
|
|
1288
|
+
status: "resumed"
|
|
1289
|
+
} : state.reconnect,
|
|
1216
1290
|
scenarioId: action.scenarioId ?? state.scenarioId,
|
|
1217
1291
|
sessionId: action.sessionId,
|
|
1218
1292
|
status: action.status,
|
|
@@ -1305,6 +1379,9 @@ var createVoiceStream = (path, options = {}) => {
|
|
|
1305
1379
|
get partial() {
|
|
1306
1380
|
return store.getSnapshot().partial;
|
|
1307
1381
|
},
|
|
1382
|
+
get reconnect() {
|
|
1383
|
+
return store.getSnapshot().reconnect;
|
|
1384
|
+
},
|
|
1308
1385
|
get sessionId() {
|
|
1309
1386
|
return connection.getSessionId();
|
|
1310
1387
|
},
|
|
@@ -2861,6 +2938,7 @@ var createInitialState2 = (stream) => ({
|
|
|
2861
2938
|
isConnected: stream.isConnected,
|
|
2862
2939
|
isRecording: false,
|
|
2863
2940
|
partial: stream.partial,
|
|
2941
|
+
reconnect: stream.reconnect,
|
|
2864
2942
|
recordingError: null,
|
|
2865
2943
|
sessionId: stream.sessionId,
|
|
2866
2944
|
scenarioId: stream.scenarioId,
|
|
@@ -2890,6 +2968,7 @@ var createVoiceController = (path, options = {}) => {
|
|
|
2890
2968
|
error: stream.error,
|
|
2891
2969
|
isConnected: stream.isConnected,
|
|
2892
2970
|
partial: stream.partial,
|
|
2971
|
+
reconnect: stream.reconnect,
|
|
2893
2972
|
sessionId: stream.sessionId,
|
|
2894
2973
|
scenarioId: stream.scenarioId,
|
|
2895
2974
|
status: stream.status,
|
|
@@ -2990,6 +3069,9 @@ var createVoiceController = (path, options = {}) => {
|
|
|
2990
3069
|
get recordingError() {
|
|
2991
3070
|
return state.recordingError;
|
|
2992
3071
|
},
|
|
3072
|
+
get reconnect() {
|
|
3073
|
+
return state.reconnect;
|
|
3074
|
+
},
|
|
2993
3075
|
sendAudio: (audio) => stream.sendAudio(audio),
|
|
2994
3076
|
get sessionId() {
|
|
2995
3077
|
return state.sessionId;
|