@absolutejs/voice 0.0.22-beta.653 → 0.0.22-beta.655
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 +100 -20
- package/dist/angular/voice-stream.service.d.ts +1 -1
- package/dist/client/actions.d.ts +12 -0
- package/dist/client/connection.d.ts +1 -1
- package/dist/client/htmxBootstrap.js +100 -20
- package/dist/client/index.js +100 -20
- package/dist/core/types.d.ts +21 -3
- package/dist/embed/index.js +100 -20
- package/dist/embed/voice-widget.js +8 -8
- package/dist/index.js +55 -40
- package/dist/react/index.js +100 -20
- package/dist/react/useVoiceController.d.ts +1 -1
- package/dist/react/useVoiceStream.d.ts +1 -1
- package/dist/svelte/index.js +100 -20
- package/dist/testing/index.js +100 -20
- package/dist/vue/index.js +100 -20
- package/dist/vue/useVoiceStream.d.ts +1 -1
- package/package.json +1 -1
package/dist/svelte/index.js
CHANGED
|
@@ -637,8 +637,11 @@ var serverMessageToAction = (message) => {
|
|
|
637
637
|
case "error":
|
|
638
638
|
return {
|
|
639
639
|
message: normalizeErrorMessage(message.message),
|
|
640
|
+
recoverable: message.recoverable,
|
|
640
641
|
type: "error"
|
|
641
642
|
};
|
|
643
|
+
case "call_control_ack":
|
|
644
|
+
return null;
|
|
642
645
|
case "final":
|
|
643
646
|
return {
|
|
644
647
|
transcript: message.transcript,
|
|
@@ -766,14 +769,17 @@ var WS_FORBIDDEN = 4403;
|
|
|
766
769
|
var DEFAULT_MAX_RECONNECT_ATTEMPTS = 15;
|
|
767
770
|
var DEFAULT_PING_INTERVAL = 30000;
|
|
768
771
|
var DEFAULT_RECONNECT_RESET_AFTER_MS = 30000;
|
|
772
|
+
var DEFAULT_CALL_CONTROL_ACK_TIMEOUT_MS = 5000;
|
|
773
|
+
var MAX_PENDING_CONTROL_MESSAGES = 100;
|
|
769
774
|
var RECONNECT_BASE_DELAY_MS = 500;
|
|
770
775
|
var DEFAULT_RECONNECT_MAX_DELAY_MS = 8000;
|
|
771
776
|
var computeVoiceReconnectDelayMs = (attempt, baseMs, maxDelayMs) => Math.min(maxDelayMs, baseMs * 2 ** (Math.max(1, attempt) - 1));
|
|
772
777
|
var DEFAULT_SCENARIO_QUERY_PARAM = "scenarioId";
|
|
773
778
|
var noop = () => {};
|
|
779
|
+
var noopAsync = () => Promise.resolve();
|
|
774
780
|
var noopUnsubscribe = () => noop;
|
|
775
781
|
var NOOP_CONNECTION = {
|
|
776
|
-
callControl:
|
|
782
|
+
callControl: noopAsync,
|
|
777
783
|
close: noop,
|
|
778
784
|
disconnect: noop,
|
|
779
785
|
endTurn: noop,
|
|
@@ -811,6 +817,7 @@ var isVoiceServerMessage = (value) => {
|
|
|
811
817
|
case "audio":
|
|
812
818
|
case "assistant":
|
|
813
819
|
case "call_lifecycle":
|
|
820
|
+
case "call_control_ack":
|
|
814
821
|
case "complete":
|
|
815
822
|
case "connection":
|
|
816
823
|
case "error":
|
|
@@ -841,6 +848,7 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
841
848
|
return NOOP_CONNECTION;
|
|
842
849
|
}
|
|
843
850
|
const listeners = new Set;
|
|
851
|
+
const pendingCallControls = new Map;
|
|
844
852
|
const shouldReconnect = options.reconnect !== false;
|
|
845
853
|
const maxReconnectAttempts = options.maxReconnectAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS;
|
|
846
854
|
const reconnectMaxDelayMs = options.reconnectMaxDelayMs ?? DEFAULT_RECONNECT_MAX_DELAY_MS;
|
|
@@ -861,6 +869,21 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
861
869
|
const emitConnection = (reconnect) => {
|
|
862
870
|
listeners.forEach((listener) => listener(reconnect));
|
|
863
871
|
};
|
|
872
|
+
const emitTerminalFailure = (message) => {
|
|
873
|
+
listeners.forEach((listener) => listener({
|
|
874
|
+
message,
|
|
875
|
+
recoverable: false,
|
|
876
|
+
type: "error"
|
|
877
|
+
}));
|
|
878
|
+
emitConnection({
|
|
879
|
+
reconnect: {
|
|
880
|
+
attempts: state.reconnectAttempts,
|
|
881
|
+
maxAttempts: maxReconnectAttempts,
|
|
882
|
+
status: "exhausted"
|
|
883
|
+
},
|
|
884
|
+
type: "connection"
|
|
885
|
+
});
|
|
886
|
+
};
|
|
864
887
|
const clearTimers = () => {
|
|
865
888
|
if (state.pingInterval) {
|
|
866
889
|
clearInterval(state.pingInterval);
|
|
@@ -886,6 +909,13 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
886
909
|
}
|
|
887
910
|
}
|
|
888
911
|
};
|
|
912
|
+
const rejectPendingCallControls = (message) => {
|
|
913
|
+
for (const pending of pendingCallControls.values()) {
|
|
914
|
+
clearTimeout(pending.timeout);
|
|
915
|
+
pending.reject(new Error(message));
|
|
916
|
+
}
|
|
917
|
+
pendingCallControls.clear();
|
|
918
|
+
};
|
|
889
919
|
const scheduleReconnect = () => {
|
|
890
920
|
state.reconnectAttempts += 1;
|
|
891
921
|
const delayMs = computeReconnectDelayMs(state.reconnectAttempts);
|
|
@@ -900,16 +930,24 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
900
930
|
},
|
|
901
931
|
type: "connection"
|
|
902
932
|
});
|
|
903
|
-
state.reconnectTimeout = setTimeout(() => {
|
|
933
|
+
state.reconnectTimeout = setTimeout(async () => {
|
|
904
934
|
if (state.reconnectAttempts > maxReconnectAttempts) {
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
935
|
+
emitTerminalFailure("Voice connection could not be restored.");
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
try {
|
|
939
|
+
await options.prepareReconnect?.({
|
|
940
|
+
attempt: state.reconnectAttempts,
|
|
941
|
+
path,
|
|
942
|
+
scenarioId: state.scenarioId,
|
|
943
|
+
sessionId: state.sessionId
|
|
912
944
|
});
|
|
945
|
+
} catch {
|
|
946
|
+
if (state.reconnectAttempts < maxReconnectAttempts) {
|
|
947
|
+
scheduleReconnect();
|
|
948
|
+
} else {
|
|
949
|
+
emitTerminalFailure("Voice authorization could not be renewed for reconnect.");
|
|
950
|
+
}
|
|
913
951
|
return;
|
|
914
952
|
}
|
|
915
953
|
connect();
|
|
@@ -966,6 +1004,18 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
966
1004
|
state.sessionId = parsed.sessionId;
|
|
967
1005
|
state.scenarioId = parsed.scenarioId ?? state.scenarioId;
|
|
968
1006
|
}
|
|
1007
|
+
if (parsed.type === "call_control_ack") {
|
|
1008
|
+
const pending = pendingCallControls.get(parsed.requestId);
|
|
1009
|
+
if (pending) {
|
|
1010
|
+
clearTimeout(pending.timeout);
|
|
1011
|
+
pendingCallControls.delete(parsed.requestId);
|
|
1012
|
+
if (parsed.ok) {
|
|
1013
|
+
pending.resolve();
|
|
1014
|
+
} else {
|
|
1015
|
+
pending.reject(new Error(parsed.message ?? `${parsed.action} was not applied`));
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
969
1019
|
listeners.forEach((listener) => listener(parsed));
|
|
970
1020
|
};
|
|
971
1021
|
ws.onclose = (event) => {
|
|
@@ -975,18 +1025,30 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
975
1025
|
state.isConnected = false;
|
|
976
1026
|
clearTimers();
|
|
977
1027
|
const reconnectable = shouldReconnect && event.code !== WS_NORMAL_CLOSURE && event.code !== WS_UNAUTHORIZED && event.code !== WS_FORBIDDEN && state.reconnectAttempts < maxReconnectAttempts;
|
|
1028
|
+
const authorizationFailure = event.code === WS_UNAUTHORIZED || event.code === WS_FORBIDDEN;
|
|
1029
|
+
if (authorizationFailure) {
|
|
1030
|
+
listeners.forEach((listener) => listener({
|
|
1031
|
+
message: event.code === WS_UNAUTHORIZED ? "Voice authorization expired or was rejected." : "Voice authorization was forbidden.",
|
|
1032
|
+
recoverable: false,
|
|
1033
|
+
type: "error"
|
|
1034
|
+
}));
|
|
1035
|
+
}
|
|
978
1036
|
if (reconnectable) {
|
|
979
1037
|
scheduleReconnect();
|
|
980
1038
|
} else if (shouldReconnect && event.code !== WS_NORMAL_CLOSURE) {
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
1039
|
+
if (authorizationFailure) {
|
|
1040
|
+
emitConnection({
|
|
1041
|
+
reconnect: {
|
|
1042
|
+
attempts: state.reconnectAttempts,
|
|
1043
|
+
lastDisconnectAt: Date.now(),
|
|
1044
|
+
maxAttempts: maxReconnectAttempts,
|
|
1045
|
+
status: "exhausted"
|
|
1046
|
+
},
|
|
1047
|
+
type: "connection"
|
|
1048
|
+
});
|
|
1049
|
+
} else {
|
|
1050
|
+
emitTerminalFailure("Voice connection could not be restored.");
|
|
1051
|
+
}
|
|
990
1052
|
}
|
|
991
1053
|
};
|
|
992
1054
|
state.ws = ws;
|
|
@@ -996,6 +1058,12 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
996
1058
|
state.ws.send(value);
|
|
997
1059
|
return;
|
|
998
1060
|
}
|
|
1061
|
+
if (typeof value !== "string") {
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
if (state.pendingMessages.length >= MAX_PENDING_CONTROL_MESSAGES) {
|
|
1065
|
+
state.pendingMessages.shift();
|
|
1066
|
+
}
|
|
999
1067
|
state.pendingMessages.push(value);
|
|
1000
1068
|
};
|
|
1001
1069
|
const send = (message) => {
|
|
@@ -1021,13 +1089,23 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
1021
1089
|
send({ type: "end_turn" });
|
|
1022
1090
|
};
|
|
1023
1091
|
const callControl = (message) => {
|
|
1092
|
+
const requestId = crypto.randomUUID();
|
|
1093
|
+
const { promise, reject, resolve } = Promise.withResolvers();
|
|
1094
|
+
const timeout = setTimeout(() => {
|
|
1095
|
+
pendingCallControls.delete(requestId);
|
|
1096
|
+
reject(new Error(`${message.action} acknowledgement timed out`));
|
|
1097
|
+
}, DEFAULT_CALL_CONTROL_ACK_TIMEOUT_MS);
|
|
1098
|
+
pendingCallControls.set(requestId, { reject, resolve, timeout });
|
|
1024
1099
|
send({
|
|
1025
1100
|
...message,
|
|
1101
|
+
requestId,
|
|
1026
1102
|
type: "call_control"
|
|
1027
1103
|
});
|
|
1104
|
+
return promise;
|
|
1028
1105
|
};
|
|
1029
1106
|
const close = (reason = "client-close") => {
|
|
1030
1107
|
clearTimers();
|
|
1108
|
+
rejectPendingCallControls("Voice connection closed");
|
|
1031
1109
|
if (state.ws) {
|
|
1032
1110
|
const ws = state.ws;
|
|
1033
1111
|
state.ws = null;
|
|
@@ -1041,6 +1119,7 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
1041
1119
|
};
|
|
1042
1120
|
const disconnect = () => {
|
|
1043
1121
|
clearTimers();
|
|
1122
|
+
rejectPendingCallControls("Voice connection disconnected");
|
|
1044
1123
|
if (state.ws) {
|
|
1045
1124
|
const ws = state.ws;
|
|
1046
1125
|
state.ws = null;
|
|
@@ -1203,7 +1282,8 @@ var createVoiceStreamStore = () => {
|
|
|
1203
1282
|
case "error":
|
|
1204
1283
|
state = {
|
|
1205
1284
|
...state,
|
|
1206
|
-
error: action.message
|
|
1285
|
+
error: action.message,
|
|
1286
|
+
...action.recoverable === false ? { isConnected: false, status: "failed" } : {}
|
|
1207
1287
|
};
|
|
1208
1288
|
break;
|
|
1209
1289
|
case "final":
|
|
@@ -1343,7 +1423,7 @@ var createVoiceStream = (path, options = {}) => {
|
|
|
1343
1423
|
return store.getSnapshot().call;
|
|
1344
1424
|
},
|
|
1345
1425
|
callControl(message) {
|
|
1346
|
-
connection.callControl(message);
|
|
1426
|
+
return connection.callControl(message);
|
|
1347
1427
|
},
|
|
1348
1428
|
close(reason) {
|
|
1349
1429
|
unsubscribeConnection();
|
package/dist/testing/index.js
CHANGED
|
@@ -2858,8 +2858,11 @@ var serverMessageToAction = (message) => {
|
|
|
2858
2858
|
case "error":
|
|
2859
2859
|
return {
|
|
2860
2860
|
message: normalizeErrorMessage(message.message),
|
|
2861
|
+
recoverable: message.recoverable,
|
|
2861
2862
|
type: "error"
|
|
2862
2863
|
};
|
|
2864
|
+
case "call_control_ack":
|
|
2865
|
+
return null;
|
|
2863
2866
|
case "final":
|
|
2864
2867
|
return {
|
|
2865
2868
|
transcript: message.transcript,
|
|
@@ -2987,14 +2990,17 @@ var WS_FORBIDDEN = 4403;
|
|
|
2987
2990
|
var DEFAULT_MAX_RECONNECT_ATTEMPTS = 15;
|
|
2988
2991
|
var DEFAULT_PING_INTERVAL = 30000;
|
|
2989
2992
|
var DEFAULT_RECONNECT_RESET_AFTER_MS = 30000;
|
|
2993
|
+
var DEFAULT_CALL_CONTROL_ACK_TIMEOUT_MS = 5000;
|
|
2994
|
+
var MAX_PENDING_CONTROL_MESSAGES = 100;
|
|
2990
2995
|
var RECONNECT_BASE_DELAY_MS = 500;
|
|
2991
2996
|
var DEFAULT_RECONNECT_MAX_DELAY_MS = 8000;
|
|
2992
2997
|
var computeVoiceReconnectDelayMs = (attempt, baseMs, maxDelayMs) => Math.min(maxDelayMs, baseMs * 2 ** (Math.max(1, attempt) - 1));
|
|
2993
2998
|
var DEFAULT_SCENARIO_QUERY_PARAM = "scenarioId";
|
|
2994
2999
|
var noop = () => {};
|
|
3000
|
+
var noopAsync = () => Promise.resolve();
|
|
2995
3001
|
var noopUnsubscribe = () => noop;
|
|
2996
3002
|
var NOOP_CONNECTION = {
|
|
2997
|
-
callControl:
|
|
3003
|
+
callControl: noopAsync,
|
|
2998
3004
|
close: noop,
|
|
2999
3005
|
disconnect: noop,
|
|
3000
3006
|
endTurn: noop,
|
|
@@ -3032,6 +3038,7 @@ var isVoiceServerMessage = (value) => {
|
|
|
3032
3038
|
case "audio":
|
|
3033
3039
|
case "assistant":
|
|
3034
3040
|
case "call_lifecycle":
|
|
3041
|
+
case "call_control_ack":
|
|
3035
3042
|
case "complete":
|
|
3036
3043
|
case "connection":
|
|
3037
3044
|
case "error":
|
|
@@ -3062,6 +3069,7 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
3062
3069
|
return NOOP_CONNECTION;
|
|
3063
3070
|
}
|
|
3064
3071
|
const listeners = new Set;
|
|
3072
|
+
const pendingCallControls = new Map;
|
|
3065
3073
|
const shouldReconnect = options.reconnect !== false;
|
|
3066
3074
|
const maxReconnectAttempts = options.maxReconnectAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS;
|
|
3067
3075
|
const reconnectMaxDelayMs = options.reconnectMaxDelayMs ?? DEFAULT_RECONNECT_MAX_DELAY_MS;
|
|
@@ -3082,6 +3090,21 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
3082
3090
|
const emitConnection = (reconnect) => {
|
|
3083
3091
|
listeners.forEach((listener) => listener(reconnect));
|
|
3084
3092
|
};
|
|
3093
|
+
const emitTerminalFailure = (message) => {
|
|
3094
|
+
listeners.forEach((listener) => listener({
|
|
3095
|
+
message,
|
|
3096
|
+
recoverable: false,
|
|
3097
|
+
type: "error"
|
|
3098
|
+
}));
|
|
3099
|
+
emitConnection({
|
|
3100
|
+
reconnect: {
|
|
3101
|
+
attempts: state.reconnectAttempts,
|
|
3102
|
+
maxAttempts: maxReconnectAttempts,
|
|
3103
|
+
status: "exhausted"
|
|
3104
|
+
},
|
|
3105
|
+
type: "connection"
|
|
3106
|
+
});
|
|
3107
|
+
};
|
|
3085
3108
|
const clearTimers = () => {
|
|
3086
3109
|
if (state.pingInterval) {
|
|
3087
3110
|
clearInterval(state.pingInterval);
|
|
@@ -3107,6 +3130,13 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
3107
3130
|
}
|
|
3108
3131
|
}
|
|
3109
3132
|
};
|
|
3133
|
+
const rejectPendingCallControls = (message) => {
|
|
3134
|
+
for (const pending of pendingCallControls.values()) {
|
|
3135
|
+
clearTimeout(pending.timeout);
|
|
3136
|
+
pending.reject(new Error(message));
|
|
3137
|
+
}
|
|
3138
|
+
pendingCallControls.clear();
|
|
3139
|
+
};
|
|
3110
3140
|
const scheduleReconnect = () => {
|
|
3111
3141
|
state.reconnectAttempts += 1;
|
|
3112
3142
|
const delayMs = computeReconnectDelayMs(state.reconnectAttempts);
|
|
@@ -3121,16 +3151,24 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
3121
3151
|
},
|
|
3122
3152
|
type: "connection"
|
|
3123
3153
|
});
|
|
3124
|
-
state.reconnectTimeout = setTimeout(() => {
|
|
3154
|
+
state.reconnectTimeout = setTimeout(async () => {
|
|
3125
3155
|
if (state.reconnectAttempts > maxReconnectAttempts) {
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3156
|
+
emitTerminalFailure("Voice connection could not be restored.");
|
|
3157
|
+
return;
|
|
3158
|
+
}
|
|
3159
|
+
try {
|
|
3160
|
+
await options.prepareReconnect?.({
|
|
3161
|
+
attempt: state.reconnectAttempts,
|
|
3162
|
+
path,
|
|
3163
|
+
scenarioId: state.scenarioId,
|
|
3164
|
+
sessionId: state.sessionId
|
|
3133
3165
|
});
|
|
3166
|
+
} catch {
|
|
3167
|
+
if (state.reconnectAttempts < maxReconnectAttempts) {
|
|
3168
|
+
scheduleReconnect();
|
|
3169
|
+
} else {
|
|
3170
|
+
emitTerminalFailure("Voice authorization could not be renewed for reconnect.");
|
|
3171
|
+
}
|
|
3134
3172
|
return;
|
|
3135
3173
|
}
|
|
3136
3174
|
connect();
|
|
@@ -3187,6 +3225,18 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
3187
3225
|
state.sessionId = parsed.sessionId;
|
|
3188
3226
|
state.scenarioId = parsed.scenarioId ?? state.scenarioId;
|
|
3189
3227
|
}
|
|
3228
|
+
if (parsed.type === "call_control_ack") {
|
|
3229
|
+
const pending = pendingCallControls.get(parsed.requestId);
|
|
3230
|
+
if (pending) {
|
|
3231
|
+
clearTimeout(pending.timeout);
|
|
3232
|
+
pendingCallControls.delete(parsed.requestId);
|
|
3233
|
+
if (parsed.ok) {
|
|
3234
|
+
pending.resolve();
|
|
3235
|
+
} else {
|
|
3236
|
+
pending.reject(new Error(parsed.message ?? `${parsed.action} was not applied`));
|
|
3237
|
+
}
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3190
3240
|
listeners.forEach((listener) => listener(parsed));
|
|
3191
3241
|
};
|
|
3192
3242
|
ws.onclose = (event) => {
|
|
@@ -3196,18 +3246,30 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
3196
3246
|
state.isConnected = false;
|
|
3197
3247
|
clearTimers();
|
|
3198
3248
|
const reconnectable = shouldReconnect && event.code !== WS_NORMAL_CLOSURE && event.code !== WS_UNAUTHORIZED && event.code !== WS_FORBIDDEN && state.reconnectAttempts < maxReconnectAttempts;
|
|
3249
|
+
const authorizationFailure = event.code === WS_UNAUTHORIZED || event.code === WS_FORBIDDEN;
|
|
3250
|
+
if (authorizationFailure) {
|
|
3251
|
+
listeners.forEach((listener) => listener({
|
|
3252
|
+
message: event.code === WS_UNAUTHORIZED ? "Voice authorization expired or was rejected." : "Voice authorization was forbidden.",
|
|
3253
|
+
recoverable: false,
|
|
3254
|
+
type: "error"
|
|
3255
|
+
}));
|
|
3256
|
+
}
|
|
3199
3257
|
if (reconnectable) {
|
|
3200
3258
|
scheduleReconnect();
|
|
3201
3259
|
} else if (shouldReconnect && event.code !== WS_NORMAL_CLOSURE) {
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3205
|
-
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
|
|
3210
|
-
|
|
3260
|
+
if (authorizationFailure) {
|
|
3261
|
+
emitConnection({
|
|
3262
|
+
reconnect: {
|
|
3263
|
+
attempts: state.reconnectAttempts,
|
|
3264
|
+
lastDisconnectAt: Date.now(),
|
|
3265
|
+
maxAttempts: maxReconnectAttempts,
|
|
3266
|
+
status: "exhausted"
|
|
3267
|
+
},
|
|
3268
|
+
type: "connection"
|
|
3269
|
+
});
|
|
3270
|
+
} else {
|
|
3271
|
+
emitTerminalFailure("Voice connection could not be restored.");
|
|
3272
|
+
}
|
|
3211
3273
|
}
|
|
3212
3274
|
};
|
|
3213
3275
|
state.ws = ws;
|
|
@@ -3217,6 +3279,12 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
3217
3279
|
state.ws.send(value);
|
|
3218
3280
|
return;
|
|
3219
3281
|
}
|
|
3282
|
+
if (typeof value !== "string") {
|
|
3283
|
+
return;
|
|
3284
|
+
}
|
|
3285
|
+
if (state.pendingMessages.length >= MAX_PENDING_CONTROL_MESSAGES) {
|
|
3286
|
+
state.pendingMessages.shift();
|
|
3287
|
+
}
|
|
3220
3288
|
state.pendingMessages.push(value);
|
|
3221
3289
|
};
|
|
3222
3290
|
const send = (message) => {
|
|
@@ -3242,13 +3310,23 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
3242
3310
|
send({ type: "end_turn" });
|
|
3243
3311
|
};
|
|
3244
3312
|
const callControl = (message) => {
|
|
3313
|
+
const requestId = crypto.randomUUID();
|
|
3314
|
+
const { promise, reject, resolve } = Promise.withResolvers();
|
|
3315
|
+
const timeout = setTimeout(() => {
|
|
3316
|
+
pendingCallControls.delete(requestId);
|
|
3317
|
+
reject(new Error(`${message.action} acknowledgement timed out`));
|
|
3318
|
+
}, DEFAULT_CALL_CONTROL_ACK_TIMEOUT_MS);
|
|
3319
|
+
pendingCallControls.set(requestId, { reject, resolve, timeout });
|
|
3245
3320
|
send({
|
|
3246
3321
|
...message,
|
|
3322
|
+
requestId,
|
|
3247
3323
|
type: "call_control"
|
|
3248
3324
|
});
|
|
3325
|
+
return promise;
|
|
3249
3326
|
};
|
|
3250
3327
|
const close = (reason = "client-close") => {
|
|
3251
3328
|
clearTimers();
|
|
3329
|
+
rejectPendingCallControls("Voice connection closed");
|
|
3252
3330
|
if (state.ws) {
|
|
3253
3331
|
const ws = state.ws;
|
|
3254
3332
|
state.ws = null;
|
|
@@ -3262,6 +3340,7 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
3262
3340
|
};
|
|
3263
3341
|
const disconnect = () => {
|
|
3264
3342
|
clearTimers();
|
|
3343
|
+
rejectPendingCallControls("Voice connection disconnected");
|
|
3265
3344
|
if (state.ws) {
|
|
3266
3345
|
const ws = state.ws;
|
|
3267
3346
|
state.ws = null;
|
|
@@ -3424,7 +3503,8 @@ var createVoiceStreamStore = () => {
|
|
|
3424
3503
|
case "error":
|
|
3425
3504
|
state = {
|
|
3426
3505
|
...state,
|
|
3427
|
-
error: action.message
|
|
3506
|
+
error: action.message,
|
|
3507
|
+
...action.recoverable === false ? { isConnected: false, status: "failed" } : {}
|
|
3428
3508
|
};
|
|
3429
3509
|
break;
|
|
3430
3510
|
case "final":
|
|
@@ -3564,7 +3644,7 @@ var createVoiceStream = (path, options = {}) => {
|
|
|
3564
3644
|
return store.getSnapshot().call;
|
|
3565
3645
|
},
|
|
3566
3646
|
callControl(message) {
|
|
3567
|
-
connection.callControl(message);
|
|
3647
|
+
return connection.callControl(message);
|
|
3568
3648
|
},
|
|
3569
3649
|
close(reason) {
|
|
3570
3650
|
unsubscribeConnection();
|