@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/vue/index.js
CHANGED
|
@@ -10705,8 +10705,11 @@ var serverMessageToAction = (message) => {
|
|
|
10705
10705
|
case "error":
|
|
10706
10706
|
return {
|
|
10707
10707
|
message: normalizeErrorMessage(message.message),
|
|
10708
|
+
recoverable: message.recoverable,
|
|
10708
10709
|
type: "error"
|
|
10709
10710
|
};
|
|
10711
|
+
case "call_control_ack":
|
|
10712
|
+
return null;
|
|
10710
10713
|
case "final":
|
|
10711
10714
|
return {
|
|
10712
10715
|
transcript: message.transcript,
|
|
@@ -10834,14 +10837,17 @@ var WS_FORBIDDEN = 4403;
|
|
|
10834
10837
|
var DEFAULT_MAX_RECONNECT_ATTEMPTS = 15;
|
|
10835
10838
|
var DEFAULT_PING_INTERVAL = 30000;
|
|
10836
10839
|
var DEFAULT_RECONNECT_RESET_AFTER_MS = 30000;
|
|
10840
|
+
var DEFAULT_CALL_CONTROL_ACK_TIMEOUT_MS = 5000;
|
|
10841
|
+
var MAX_PENDING_CONTROL_MESSAGES = 100;
|
|
10837
10842
|
var RECONNECT_BASE_DELAY_MS = 500;
|
|
10838
10843
|
var DEFAULT_RECONNECT_MAX_DELAY_MS = 8000;
|
|
10839
10844
|
var computeVoiceReconnectDelayMs = (attempt, baseMs, maxDelayMs) => Math.min(maxDelayMs, baseMs * 2 ** (Math.max(1, attempt) - 1));
|
|
10840
10845
|
var DEFAULT_SCENARIO_QUERY_PARAM = "scenarioId";
|
|
10841
10846
|
var noop = () => {};
|
|
10847
|
+
var noopAsync = () => Promise.resolve();
|
|
10842
10848
|
var noopUnsubscribe = () => noop;
|
|
10843
10849
|
var NOOP_CONNECTION = {
|
|
10844
|
-
callControl:
|
|
10850
|
+
callControl: noopAsync,
|
|
10845
10851
|
close: noop,
|
|
10846
10852
|
disconnect: noop,
|
|
10847
10853
|
endTurn: noop,
|
|
@@ -10879,6 +10885,7 @@ var isVoiceServerMessage = (value) => {
|
|
|
10879
10885
|
case "audio":
|
|
10880
10886
|
case "assistant":
|
|
10881
10887
|
case "call_lifecycle":
|
|
10888
|
+
case "call_control_ack":
|
|
10882
10889
|
case "complete":
|
|
10883
10890
|
case "connection":
|
|
10884
10891
|
case "error":
|
|
@@ -10909,6 +10916,7 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
10909
10916
|
return NOOP_CONNECTION;
|
|
10910
10917
|
}
|
|
10911
10918
|
const listeners = new Set;
|
|
10919
|
+
const pendingCallControls = new Map;
|
|
10912
10920
|
const shouldReconnect = options.reconnect !== false;
|
|
10913
10921
|
const maxReconnectAttempts = options.maxReconnectAttempts ?? DEFAULT_MAX_RECONNECT_ATTEMPTS;
|
|
10914
10922
|
const reconnectMaxDelayMs = options.reconnectMaxDelayMs ?? DEFAULT_RECONNECT_MAX_DELAY_MS;
|
|
@@ -10929,6 +10937,21 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
10929
10937
|
const emitConnection = (reconnect) => {
|
|
10930
10938
|
listeners.forEach((listener) => listener(reconnect));
|
|
10931
10939
|
};
|
|
10940
|
+
const emitTerminalFailure = (message) => {
|
|
10941
|
+
listeners.forEach((listener) => listener({
|
|
10942
|
+
message,
|
|
10943
|
+
recoverable: false,
|
|
10944
|
+
type: "error"
|
|
10945
|
+
}));
|
|
10946
|
+
emitConnection({
|
|
10947
|
+
reconnect: {
|
|
10948
|
+
attempts: state.reconnectAttempts,
|
|
10949
|
+
maxAttempts: maxReconnectAttempts,
|
|
10950
|
+
status: "exhausted"
|
|
10951
|
+
},
|
|
10952
|
+
type: "connection"
|
|
10953
|
+
});
|
|
10954
|
+
};
|
|
10932
10955
|
const clearTimers = () => {
|
|
10933
10956
|
if (state.pingInterval) {
|
|
10934
10957
|
clearInterval(state.pingInterval);
|
|
@@ -10954,6 +10977,13 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
10954
10977
|
}
|
|
10955
10978
|
}
|
|
10956
10979
|
};
|
|
10980
|
+
const rejectPendingCallControls = (message) => {
|
|
10981
|
+
for (const pending of pendingCallControls.values()) {
|
|
10982
|
+
clearTimeout(pending.timeout);
|
|
10983
|
+
pending.reject(new Error(message));
|
|
10984
|
+
}
|
|
10985
|
+
pendingCallControls.clear();
|
|
10986
|
+
};
|
|
10957
10987
|
const scheduleReconnect = () => {
|
|
10958
10988
|
state.reconnectAttempts += 1;
|
|
10959
10989
|
const delayMs = computeReconnectDelayMs(state.reconnectAttempts);
|
|
@@ -10968,16 +10998,24 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
10968
10998
|
},
|
|
10969
10999
|
type: "connection"
|
|
10970
11000
|
});
|
|
10971
|
-
state.reconnectTimeout = setTimeout(() => {
|
|
11001
|
+
state.reconnectTimeout = setTimeout(async () => {
|
|
10972
11002
|
if (state.reconnectAttempts > maxReconnectAttempts) {
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
|
|
10976
|
-
|
|
10977
|
-
|
|
10978
|
-
|
|
10979
|
-
|
|
11003
|
+
emitTerminalFailure("Voice connection could not be restored.");
|
|
11004
|
+
return;
|
|
11005
|
+
}
|
|
11006
|
+
try {
|
|
11007
|
+
await options.prepareReconnect?.({
|
|
11008
|
+
attempt: state.reconnectAttempts,
|
|
11009
|
+
path,
|
|
11010
|
+
scenarioId: state.scenarioId,
|
|
11011
|
+
sessionId: state.sessionId
|
|
10980
11012
|
});
|
|
11013
|
+
} catch {
|
|
11014
|
+
if (state.reconnectAttempts < maxReconnectAttempts) {
|
|
11015
|
+
scheduleReconnect();
|
|
11016
|
+
} else {
|
|
11017
|
+
emitTerminalFailure("Voice authorization could not be renewed for reconnect.");
|
|
11018
|
+
}
|
|
10981
11019
|
return;
|
|
10982
11020
|
}
|
|
10983
11021
|
connect();
|
|
@@ -11034,6 +11072,18 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
11034
11072
|
state.sessionId = parsed.sessionId;
|
|
11035
11073
|
state.scenarioId = parsed.scenarioId ?? state.scenarioId;
|
|
11036
11074
|
}
|
|
11075
|
+
if (parsed.type === "call_control_ack") {
|
|
11076
|
+
const pending = pendingCallControls.get(parsed.requestId);
|
|
11077
|
+
if (pending) {
|
|
11078
|
+
clearTimeout(pending.timeout);
|
|
11079
|
+
pendingCallControls.delete(parsed.requestId);
|
|
11080
|
+
if (parsed.ok) {
|
|
11081
|
+
pending.resolve();
|
|
11082
|
+
} else {
|
|
11083
|
+
pending.reject(new Error(parsed.message ?? `${parsed.action} was not applied`));
|
|
11084
|
+
}
|
|
11085
|
+
}
|
|
11086
|
+
}
|
|
11037
11087
|
listeners.forEach((listener) => listener(parsed));
|
|
11038
11088
|
};
|
|
11039
11089
|
ws.onclose = (event) => {
|
|
@@ -11043,18 +11093,30 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
11043
11093
|
state.isConnected = false;
|
|
11044
11094
|
clearTimers();
|
|
11045
11095
|
const reconnectable = shouldReconnect && event.code !== WS_NORMAL_CLOSURE && event.code !== WS_UNAUTHORIZED && event.code !== WS_FORBIDDEN && state.reconnectAttempts < maxReconnectAttempts;
|
|
11096
|
+
const authorizationFailure = event.code === WS_UNAUTHORIZED || event.code === WS_FORBIDDEN;
|
|
11097
|
+
if (authorizationFailure) {
|
|
11098
|
+
listeners.forEach((listener) => listener({
|
|
11099
|
+
message: event.code === WS_UNAUTHORIZED ? "Voice authorization expired or was rejected." : "Voice authorization was forbidden.",
|
|
11100
|
+
recoverable: false,
|
|
11101
|
+
type: "error"
|
|
11102
|
+
}));
|
|
11103
|
+
}
|
|
11046
11104
|
if (reconnectable) {
|
|
11047
11105
|
scheduleReconnect();
|
|
11048
11106
|
} else if (shouldReconnect && event.code !== WS_NORMAL_CLOSURE) {
|
|
11049
|
-
|
|
11050
|
-
|
|
11051
|
-
|
|
11052
|
-
|
|
11053
|
-
|
|
11054
|
-
|
|
11055
|
-
|
|
11056
|
-
|
|
11057
|
-
|
|
11107
|
+
if (authorizationFailure) {
|
|
11108
|
+
emitConnection({
|
|
11109
|
+
reconnect: {
|
|
11110
|
+
attempts: state.reconnectAttempts,
|
|
11111
|
+
lastDisconnectAt: Date.now(),
|
|
11112
|
+
maxAttempts: maxReconnectAttempts,
|
|
11113
|
+
status: "exhausted"
|
|
11114
|
+
},
|
|
11115
|
+
type: "connection"
|
|
11116
|
+
});
|
|
11117
|
+
} else {
|
|
11118
|
+
emitTerminalFailure("Voice connection could not be restored.");
|
|
11119
|
+
}
|
|
11058
11120
|
}
|
|
11059
11121
|
};
|
|
11060
11122
|
state.ws = ws;
|
|
@@ -11064,6 +11126,12 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
11064
11126
|
state.ws.send(value);
|
|
11065
11127
|
return;
|
|
11066
11128
|
}
|
|
11129
|
+
if (typeof value !== "string") {
|
|
11130
|
+
return;
|
|
11131
|
+
}
|
|
11132
|
+
if (state.pendingMessages.length >= MAX_PENDING_CONTROL_MESSAGES) {
|
|
11133
|
+
state.pendingMessages.shift();
|
|
11134
|
+
}
|
|
11067
11135
|
state.pendingMessages.push(value);
|
|
11068
11136
|
};
|
|
11069
11137
|
const send = (message) => {
|
|
@@ -11089,13 +11157,23 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
11089
11157
|
send({ type: "end_turn" });
|
|
11090
11158
|
};
|
|
11091
11159
|
const callControl = (message) => {
|
|
11160
|
+
const requestId = crypto.randomUUID();
|
|
11161
|
+
const { promise, reject, resolve } = Promise.withResolvers();
|
|
11162
|
+
const timeout = setTimeout(() => {
|
|
11163
|
+
pendingCallControls.delete(requestId);
|
|
11164
|
+
reject(new Error(`${message.action} acknowledgement timed out`));
|
|
11165
|
+
}, DEFAULT_CALL_CONTROL_ACK_TIMEOUT_MS);
|
|
11166
|
+
pendingCallControls.set(requestId, { reject, resolve, timeout });
|
|
11092
11167
|
send({
|
|
11093
11168
|
...message,
|
|
11169
|
+
requestId,
|
|
11094
11170
|
type: "call_control"
|
|
11095
11171
|
});
|
|
11172
|
+
return promise;
|
|
11096
11173
|
};
|
|
11097
11174
|
const close = (reason = "client-close") => {
|
|
11098
11175
|
clearTimers();
|
|
11176
|
+
rejectPendingCallControls("Voice connection closed");
|
|
11099
11177
|
if (state.ws) {
|
|
11100
11178
|
const ws = state.ws;
|
|
11101
11179
|
state.ws = null;
|
|
@@ -11109,6 +11187,7 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
11109
11187
|
};
|
|
11110
11188
|
const disconnect = () => {
|
|
11111
11189
|
clearTimers();
|
|
11190
|
+
rejectPendingCallControls("Voice connection disconnected");
|
|
11112
11191
|
if (state.ws) {
|
|
11113
11192
|
const ws = state.ws;
|
|
11114
11193
|
state.ws = null;
|
|
@@ -11271,7 +11350,8 @@ var createVoiceStreamStore = () => {
|
|
|
11271
11350
|
case "error":
|
|
11272
11351
|
state = {
|
|
11273
11352
|
...state,
|
|
11274
|
-
error: action.message
|
|
11353
|
+
error: action.message,
|
|
11354
|
+
...action.recoverable === false ? { isConnected: false, status: "failed" } : {}
|
|
11275
11355
|
};
|
|
11276
11356
|
break;
|
|
11277
11357
|
case "final":
|
|
@@ -11411,7 +11491,7 @@ var createVoiceStream = (path, options = {}) => {
|
|
|
11411
11491
|
return store.getSnapshot().call;
|
|
11412
11492
|
},
|
|
11413
11493
|
callControl(message) {
|
|
11414
|
-
connection.callControl(message);
|
|
11494
|
+
return connection.callControl(message);
|
|
11415
11495
|
},
|
|
11416
11496
|
close(reason) {
|
|
11417
11497
|
unsubscribeConnection();
|
|
@@ -22,7 +22,7 @@ export declare function useVoiceStream<TResult = unknown>(path: string, options?
|
|
|
22
22
|
sessionMetadata: import("vue").ShallowRef<Record<string, unknown> | null, Record<string, unknown> | null>;
|
|
23
23
|
status: import("vue").Ref<import("..").VoiceSessionStatus | "idle", import("..").VoiceSessionStatus | "idle">;
|
|
24
24
|
turns: import("vue").ShallowRef<VoiceTurnRecord<TResult>[], VoiceTurnRecord<TResult>[]>;
|
|
25
|
-
callControl: (message: Parameters<(message: Omit<import("..").VoiceClientCallControlMessage, "type">) => void
|
|
25
|
+
callControl: (message: Parameters<(message: Omit<import("..").VoiceClientCallControlMessage, "requestId" | "type">) => Promise<void>>[0]) => Promise<void>;
|
|
26
26
|
close: () => void;
|
|
27
27
|
endTurn: () => void;
|
|
28
28
|
sendAudio: (audio: Uint8Array | ArrayBuffer) => void;
|