@adminforth/agent 1.43.2 → 1.43.3

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/build.log CHANGED
@@ -58,5 +58,5 @@ custom/speech_recognition_frontend/voiceActivityDetection.ts
58
58
  custom/speech_recognition_frontend/types/
59
59
  custom/speech_recognition_frontend/types/voice-activity-detection.d.ts
60
60
 
61
- sent 1,661,185 bytes received 860 bytes 3,324,090.00 bytes/sec
62
- total size is 1,657,269 speedup is 1.00
61
+ sent 1,664,048 bytes received 860 bytes 3,329,816.00 bytes/sec
62
+ total size is 1,660,186 speedup is 1.00
@@ -14,26 +14,118 @@ type StreamingAudioState = {
14
14
  isDone: boolean;
15
15
  };
16
16
 
17
+ let audioUnlockSourceUrl: string | null = null;
18
+ let audioUnlockInFlight: Promise<void> | null = null;
19
+ let isAudioPlaybackUnlocked = false;
17
20
  let standByAudio: HTMLAudioElement | null = null;
18
21
  let isStandByAudioPlaying = false;
22
+
23
+ function writeAsciiString(view: DataView, offset: number, value: string) {
24
+ for (let index = 0; index < value.length; index += 1) {
25
+ view.setUint8(offset + index, value.charCodeAt(index));
26
+ }
27
+ }
28
+
29
+ function createSilentWavBlob(durationMs = 50) {
30
+ const sampleRate = 8000;
31
+ const bitsPerSample = 16;
32
+ const channelCount = 1;
33
+ const bytesPerSample = bitsPerSample / 8;
34
+ const sampleCount = Math.max(1, Math.round((sampleRate * durationMs) / 1000));
35
+ const pcmDataSize = sampleCount * channelCount * bytesPerSample;
36
+ const buffer = new ArrayBuffer(44 + pcmDataSize);
37
+ const view = new DataView(buffer);
38
+
39
+ writeAsciiString(view, 0, 'RIFF');
40
+ view.setUint32(4, 36 + pcmDataSize, true);
41
+ writeAsciiString(view, 8, 'WAVE');
42
+ writeAsciiString(view, 12, 'fmt ');
43
+ view.setUint32(16, 16, true);
44
+ view.setUint16(20, 1, true);
45
+ view.setUint16(22, channelCount, true);
46
+ view.setUint32(24, sampleRate, true);
47
+ view.setUint32(28, sampleRate * channelCount * bytesPerSample, true);
48
+ view.setUint16(32, channelCount * bytesPerSample, true);
49
+ view.setUint16(34, bitsPerSample, true);
50
+ writeAsciiString(view, 36, 'data');
51
+ view.setUint32(40, pcmDataSize, true);
52
+
53
+ return new Blob([buffer], { type: 'audio/wav' });
54
+ }
55
+
56
+ function getAudioUnlockSourceUrl() {
57
+ if (!audioUnlockSourceUrl) {
58
+ audioUnlockSourceUrl = URL.createObjectURL(createSilentWavBlob());
59
+ }
60
+
61
+ return audioUnlockSourceUrl;
62
+ }
63
+
64
+ async function unlockAudioPlayback() {
65
+ if (isAudioPlaybackUnlocked) {
66
+ return;
67
+ }
68
+
69
+ if (audioUnlockInFlight) {
70
+ await audioUnlockInFlight;
71
+ return;
72
+ }
73
+
74
+ audioUnlockInFlight = (async () => {
75
+ const unlockAudio = new Audio(getAudioUnlockSourceUrl());
76
+ unlockAudio.muted = true;
77
+ unlockAudio.preload = 'auto';
78
+ unlockAudio.setAttribute('playsinline', '');
79
+
80
+ try {
81
+ await unlockAudio.play();
82
+ unlockAudio.pause();
83
+ unlockAudio.currentTime = 0;
84
+ isAudioPlaybackUnlocked = true;
85
+ } catch (error) {
86
+ console.error('Failed to unlock audio playback:', error);
87
+ } finally {
88
+ unlockAudio.removeAttribute('src');
89
+ unlockAudio.load();
90
+ audioUnlockInFlight = null;
91
+ }
92
+ })();
93
+
94
+ await audioUnlockInFlight;
95
+ }
96
+
19
97
  async function playStandByAudio() {
20
98
  if (!standByAudio) {
21
99
  standByAudio = new Audio(`/plugins/AdminForthAgentPlugin/agentAudio/agent-processing.mp3`);
100
+ standByAudio.preload = 'auto';
101
+ standByAudio.setAttribute('playsinline', '');
22
102
  standByAudio.addEventListener('ended', () => {
23
- if (!standByAudio.paused) {
103
+ if (standByAudio?.paused === false) {
24
104
  restartStandByAudio();
25
105
  }
26
106
  });
27
107
  }
108
+
109
+ if (!standByAudio) {
110
+ return;
111
+ }
112
+
28
113
  standByAudio.currentTime = 0;
29
- await standByAudio.play();
30
- isStandByAudioPlaying = true;
114
+
115
+ try {
116
+ await standByAudio.play();
117
+ isStandByAudioPlaying = true;
118
+ } catch (error) {
119
+ isStandByAudioPlaying = false;
120
+ console.error('Failed to play standby audio:', error);
121
+ }
31
122
  }
32
123
 
33
124
  function stopStandByAudio() {
34
125
  if (!standByAudio) {
35
126
  return;
36
127
  }
128
+
37
129
  standByAudio.pause();
38
130
  standByAudio.currentTime = 0;
39
131
  isStandByAudioPlaying = false;
@@ -43,15 +135,15 @@ function restartStandByAudio() {
43
135
  if (standByAudio) {
44
136
  standByAudio.currentTime = 0;
45
137
  }
46
- playStandByAudio();
47
- }
48
138
 
139
+ void playStandByAudio();
140
+ }
49
141
 
50
142
  export const useAgentAudio = defineStore('agentAudio', () => {
51
143
  const agentStore = useAgentStore();
52
- const agentAudioMode = ref<'transcribing' | 'streaming' | 'fetchingAudio' | 'playingAgentResponse' | 'readyToRespond' >('readyToRespond');
144
+ const agentAudioMode = ref<'transcribing' | 'streaming' | 'fetchingAudio' | 'playingAgentResponse' | 'readyToRespond'>('readyToRespond');
53
145
  const isStreamingResponse = ref(false);
54
-
146
+
55
147
  let currentAbortController: AbortController | null = null;
56
148
  let isPlaying = false;
57
149
  let currentAudio: HTMLAudioElement | null = null;
@@ -81,6 +173,7 @@ export const useAgentAudio = defineStore('agentAudio', () => {
81
173
  formData.append('timeZone', Intl.DateTimeFormat().resolvedOptions().timeZone);
82
174
  formData.append('currentPage', JSON.stringify(getCurrentPageContext()));
83
175
  const fullPath = `${import.meta.env.VITE_ADMINFORTH_PUBLIC_PATH || ''}/adminapi/v1/agent/speech-response`;
176
+
84
177
  try {
85
178
  agentAudioMode.value = 'transcribing';
86
179
  const res = await fetch(fullPath, {
@@ -91,22 +184,23 @@ export const useAgentAudio = defineStore('agentAudio', () => {
91
184
  },
92
185
  signal: currentAbortController!.signal,
93
186
  });
187
+
94
188
  isStreamingResponse.value = true;
189
+
95
190
  if (res.ok) {
96
191
  agentAudioMode.value = 'streaming';
97
192
  await readSpeechResponseStream(res);
98
193
  } else {
99
194
  console.error('Failed to transcribe audio:', res.statusText);
100
- adminforth.alert({message: 'Failed to transcribe audio', variant: 'danger'});
195
+ adminforth.alert({ message: 'Failed to transcribe audio', variant: 'danger' });
101
196
  }
102
197
  } catch (error) {
103
- if (error instanceof DOMException && error.name === 'AbortError') {
104
- //
105
- } else {
198
+ if (!(error instanceof DOMException && error.name === 'AbortError')) {
106
199
  console.error('Error sending audio to server:', error);
107
200
  }
108
201
  } finally {
109
202
  isStreamingResponse.value = false;
203
+
110
204
  if (!wasAudioResponseReceived) {
111
205
  setAudioModeReadyToRespond();
112
206
  }
@@ -115,8 +209,9 @@ export const useAgentAudio = defineStore('agentAudio', () => {
115
209
 
116
210
  async function readSpeechResponseStream(res: Response) {
117
211
  const reader = res.body?.getReader();
212
+
118
213
  if (!reader) {
119
- adminforth.alert({message: 'Speech response stream is not available', variant: 'danger'});
214
+ adminforth.alert({ message: 'Speech response stream is not available', variant: 'danger' });
120
215
  return;
121
216
  }
122
217
 
@@ -124,6 +219,7 @@ export const useAgentAudio = defineStore('agentAudio', () => {
124
219
  let buffer = '';
125
220
 
126
221
  agentStore.setCurrentChatStatus('streaming');
222
+
127
223
  try {
128
224
  while (true) {
129
225
  const { value, done } = await reader.read();
@@ -158,6 +254,7 @@ export const useAgentAudio = defineStore('agentAudio', () => {
158
254
  if (currentAbortController?.signal.aborted) {
159
255
  return;
160
256
  }
257
+
161
258
  const data = eventBlock
162
259
  .split('\n')
163
260
  .filter((line) => line.startsWith('data:'))
@@ -171,7 +268,6 @@ export const useAgentAudio = defineStore('agentAudio', () => {
171
268
  const event = JSON.parse(data) as SpeechStreamEvent;
172
269
 
173
270
  if (event.type === 'error') {
174
-
175
271
  return;
176
272
  }
177
273
 
@@ -209,8 +305,9 @@ export const useAgentAudio = defineStore('agentAudio', () => {
209
305
 
210
306
  if (event.type === 'data-tool-call') {
211
307
  if (!isStandByAudioPlaying) {
212
- playStandByAudio();
308
+ void playStandByAudio();
213
309
  }
310
+
214
311
  agentStore.addDataToolCallMessage(event.data);
215
312
  }
216
313
  }
@@ -227,10 +324,16 @@ export const useAgentAudio = defineStore('agentAudio', () => {
227
324
  currentAudio.currentTime = 0;
228
325
  return;
229
326
  }
327
+
230
328
  agentAudioMode.value = 'playingAgentResponse';
231
- await void currentAudio.play().catch((error) => {
329
+
330
+ try {
331
+ await currentAudio.play();
332
+ } catch (error) {
333
+ isPlaying = false;
334
+ setAudioModeReadyToRespond();
232
335
  console.error('Failed to play audio:', error);
233
- });
336
+ }
234
337
  }
235
338
 
236
339
  function initializeAudioStream(mimeType: string) {
@@ -244,6 +347,8 @@ export const useAgentAudio = defineStore('agentAudio', () => {
244
347
  const mediaSource = new MediaSource();
245
348
  currentAudioObjectUrl = URL.createObjectURL(mediaSource);
246
349
  currentAudio = new Audio(currentAudioObjectUrl);
350
+ currentAudio.preload = 'auto';
351
+ currentAudio.setAttribute('playsinline', '');
247
352
  currentAudio.addEventListener('ended', handleAudioEnded, { once: true });
248
353
  currentStreamingAudio = {
249
354
  mimeType,
@@ -299,7 +404,7 @@ export const useAgentAudio = defineStore('agentAudio', () => {
299
404
 
300
405
  if (!currentStreamingAudio.hasStartedPlayback) {
301
406
  currentStreamingAudio.hasStartedPlayback = true;
302
- setIsPlaying(true);
407
+ void setIsPlaying(true);
303
408
  }
304
409
 
305
410
  return;
@@ -356,6 +461,7 @@ export const useAgentAudio = defineStore('agentAudio', () => {
356
461
  bufferedAudioMimeType = 'audio/mpeg';
357
462
  detachStreamingAudio();
358
463
  destroyCurrentAudioElement();
464
+
359
465
  if (!dontResetMode) {
360
466
  setAudioModeReadyToRespond();
361
467
  }
@@ -369,8 +475,10 @@ export const useAgentAudio = defineStore('agentAudio', () => {
369
475
  function playAudioChunks(chunks: ArrayBuffer[], mimeType: string) {
370
476
  currentAudioObjectUrl = URL.createObjectURL(new Blob(chunks, { type: mimeType }));
371
477
  currentAudio = new Audio(currentAudioObjectUrl);
478
+ currentAudio.preload = 'auto';
479
+ currentAudio.setAttribute('playsinline', '');
372
480
  currentAudio.addEventListener('ended', handleAudioEnded, { once: true });
373
- setIsPlaying(true);
481
+ void setIsPlaying(true);
374
482
  }
375
483
 
376
484
  function base64ToArrayBuffer(base64: string) {
@@ -407,8 +515,9 @@ export const useAgentAudio = defineStore('agentAudio', () => {
407
515
  sendAudioToServerAndHandleResponse,
408
516
  stopGenerationAndAudio,
409
517
  stopCurrentAudioPlayback,
518
+ unlockAudioPlayback,
410
519
  playBeep,
411
- agentAudioMode
520
+ agentAudioMode,
521
+ playStandByAudio,
412
522
  };
413
-
414
523
  });
@@ -111,6 +111,7 @@ function toggleChatMode() {
111
111
  }
112
112
 
113
113
  async function onStartRecording() {
114
+ await agentAudio.unlockAudioPlayback();
114
115
  await requestMicAndStartVAD(saidSomething, stopRecording, onAnySound);
115
116
  microphoneButtonMode.value = 'listen';
116
117
  agentAudio.playBeep(1000);
@@ -14,26 +14,118 @@ type StreamingAudioState = {
14
14
  isDone: boolean;
15
15
  };
16
16
 
17
+ let audioUnlockSourceUrl: string | null = null;
18
+ let audioUnlockInFlight: Promise<void> | null = null;
19
+ let isAudioPlaybackUnlocked = false;
17
20
  let standByAudio: HTMLAudioElement | null = null;
18
21
  let isStandByAudioPlaying = false;
22
+
23
+ function writeAsciiString(view: DataView, offset: number, value: string) {
24
+ for (let index = 0; index < value.length; index += 1) {
25
+ view.setUint8(offset + index, value.charCodeAt(index));
26
+ }
27
+ }
28
+
29
+ function createSilentWavBlob(durationMs = 50) {
30
+ const sampleRate = 8000;
31
+ const bitsPerSample = 16;
32
+ const channelCount = 1;
33
+ const bytesPerSample = bitsPerSample / 8;
34
+ const sampleCount = Math.max(1, Math.round((sampleRate * durationMs) / 1000));
35
+ const pcmDataSize = sampleCount * channelCount * bytesPerSample;
36
+ const buffer = new ArrayBuffer(44 + pcmDataSize);
37
+ const view = new DataView(buffer);
38
+
39
+ writeAsciiString(view, 0, 'RIFF');
40
+ view.setUint32(4, 36 + pcmDataSize, true);
41
+ writeAsciiString(view, 8, 'WAVE');
42
+ writeAsciiString(view, 12, 'fmt ');
43
+ view.setUint32(16, 16, true);
44
+ view.setUint16(20, 1, true);
45
+ view.setUint16(22, channelCount, true);
46
+ view.setUint32(24, sampleRate, true);
47
+ view.setUint32(28, sampleRate * channelCount * bytesPerSample, true);
48
+ view.setUint16(32, channelCount * bytesPerSample, true);
49
+ view.setUint16(34, bitsPerSample, true);
50
+ writeAsciiString(view, 36, 'data');
51
+ view.setUint32(40, pcmDataSize, true);
52
+
53
+ return new Blob([buffer], { type: 'audio/wav' });
54
+ }
55
+
56
+ function getAudioUnlockSourceUrl() {
57
+ if (!audioUnlockSourceUrl) {
58
+ audioUnlockSourceUrl = URL.createObjectURL(createSilentWavBlob());
59
+ }
60
+
61
+ return audioUnlockSourceUrl;
62
+ }
63
+
64
+ async function unlockAudioPlayback() {
65
+ if (isAudioPlaybackUnlocked) {
66
+ return;
67
+ }
68
+
69
+ if (audioUnlockInFlight) {
70
+ await audioUnlockInFlight;
71
+ return;
72
+ }
73
+
74
+ audioUnlockInFlight = (async () => {
75
+ const unlockAudio = new Audio(getAudioUnlockSourceUrl());
76
+ unlockAudio.muted = true;
77
+ unlockAudio.preload = 'auto';
78
+ unlockAudio.setAttribute('playsinline', '');
79
+
80
+ try {
81
+ await unlockAudio.play();
82
+ unlockAudio.pause();
83
+ unlockAudio.currentTime = 0;
84
+ isAudioPlaybackUnlocked = true;
85
+ } catch (error) {
86
+ console.error('Failed to unlock audio playback:', error);
87
+ } finally {
88
+ unlockAudio.removeAttribute('src');
89
+ unlockAudio.load();
90
+ audioUnlockInFlight = null;
91
+ }
92
+ })();
93
+
94
+ await audioUnlockInFlight;
95
+ }
96
+
19
97
  async function playStandByAudio() {
20
98
  if (!standByAudio) {
21
99
  standByAudio = new Audio(`/plugins/AdminForthAgentPlugin/agentAudio/agent-processing.mp3`);
100
+ standByAudio.preload = 'auto';
101
+ standByAudio.setAttribute('playsinline', '');
22
102
  standByAudio.addEventListener('ended', () => {
23
- if (!standByAudio.paused) {
103
+ if (standByAudio?.paused === false) {
24
104
  restartStandByAudio();
25
105
  }
26
106
  });
27
107
  }
108
+
109
+ if (!standByAudio) {
110
+ return;
111
+ }
112
+
28
113
  standByAudio.currentTime = 0;
29
- await standByAudio.play();
30
- isStandByAudioPlaying = true;
114
+
115
+ try {
116
+ await standByAudio.play();
117
+ isStandByAudioPlaying = true;
118
+ } catch (error) {
119
+ isStandByAudioPlaying = false;
120
+ console.error('Failed to play standby audio:', error);
121
+ }
31
122
  }
32
123
 
33
124
  function stopStandByAudio() {
34
125
  if (!standByAudio) {
35
126
  return;
36
127
  }
128
+
37
129
  standByAudio.pause();
38
130
  standByAudio.currentTime = 0;
39
131
  isStandByAudioPlaying = false;
@@ -43,15 +135,15 @@ function restartStandByAudio() {
43
135
  if (standByAudio) {
44
136
  standByAudio.currentTime = 0;
45
137
  }
46
- playStandByAudio();
47
- }
48
138
 
139
+ void playStandByAudio();
140
+ }
49
141
 
50
142
  export const useAgentAudio = defineStore('agentAudio', () => {
51
143
  const agentStore = useAgentStore();
52
- const agentAudioMode = ref<'transcribing' | 'streaming' | 'fetchingAudio' | 'playingAgentResponse' | 'readyToRespond' >('readyToRespond');
144
+ const agentAudioMode = ref<'transcribing' | 'streaming' | 'fetchingAudio' | 'playingAgentResponse' | 'readyToRespond'>('readyToRespond');
53
145
  const isStreamingResponse = ref(false);
54
-
146
+
55
147
  let currentAbortController: AbortController | null = null;
56
148
  let isPlaying = false;
57
149
  let currentAudio: HTMLAudioElement | null = null;
@@ -81,6 +173,7 @@ export const useAgentAudio = defineStore('agentAudio', () => {
81
173
  formData.append('timeZone', Intl.DateTimeFormat().resolvedOptions().timeZone);
82
174
  formData.append('currentPage', JSON.stringify(getCurrentPageContext()));
83
175
  const fullPath = `${import.meta.env.VITE_ADMINFORTH_PUBLIC_PATH || ''}/adminapi/v1/agent/speech-response`;
176
+
84
177
  try {
85
178
  agentAudioMode.value = 'transcribing';
86
179
  const res = await fetch(fullPath, {
@@ -91,22 +184,23 @@ export const useAgentAudio = defineStore('agentAudio', () => {
91
184
  },
92
185
  signal: currentAbortController!.signal,
93
186
  });
187
+
94
188
  isStreamingResponse.value = true;
189
+
95
190
  if (res.ok) {
96
191
  agentAudioMode.value = 'streaming';
97
192
  await readSpeechResponseStream(res);
98
193
  } else {
99
194
  console.error('Failed to transcribe audio:', res.statusText);
100
- adminforth.alert({message: 'Failed to transcribe audio', variant: 'danger'});
195
+ adminforth.alert({ message: 'Failed to transcribe audio', variant: 'danger' });
101
196
  }
102
197
  } catch (error) {
103
- if (error instanceof DOMException && error.name === 'AbortError') {
104
- //
105
- } else {
198
+ if (!(error instanceof DOMException && error.name === 'AbortError')) {
106
199
  console.error('Error sending audio to server:', error);
107
200
  }
108
201
  } finally {
109
202
  isStreamingResponse.value = false;
203
+
110
204
  if (!wasAudioResponseReceived) {
111
205
  setAudioModeReadyToRespond();
112
206
  }
@@ -115,8 +209,9 @@ export const useAgentAudio = defineStore('agentAudio', () => {
115
209
 
116
210
  async function readSpeechResponseStream(res: Response) {
117
211
  const reader = res.body?.getReader();
212
+
118
213
  if (!reader) {
119
- adminforth.alert({message: 'Speech response stream is not available', variant: 'danger'});
214
+ adminforth.alert({ message: 'Speech response stream is not available', variant: 'danger' });
120
215
  return;
121
216
  }
122
217
 
@@ -124,6 +219,7 @@ export const useAgentAudio = defineStore('agentAudio', () => {
124
219
  let buffer = '';
125
220
 
126
221
  agentStore.setCurrentChatStatus('streaming');
222
+
127
223
  try {
128
224
  while (true) {
129
225
  const { value, done } = await reader.read();
@@ -158,6 +254,7 @@ export const useAgentAudio = defineStore('agentAudio', () => {
158
254
  if (currentAbortController?.signal.aborted) {
159
255
  return;
160
256
  }
257
+
161
258
  const data = eventBlock
162
259
  .split('\n')
163
260
  .filter((line) => line.startsWith('data:'))
@@ -171,7 +268,6 @@ export const useAgentAudio = defineStore('agentAudio', () => {
171
268
  const event = JSON.parse(data) as SpeechStreamEvent;
172
269
 
173
270
  if (event.type === 'error') {
174
-
175
271
  return;
176
272
  }
177
273
 
@@ -209,8 +305,9 @@ export const useAgentAudio = defineStore('agentAudio', () => {
209
305
 
210
306
  if (event.type === 'data-tool-call') {
211
307
  if (!isStandByAudioPlaying) {
212
- playStandByAudio();
308
+ void playStandByAudio();
213
309
  }
310
+
214
311
  agentStore.addDataToolCallMessage(event.data);
215
312
  }
216
313
  }
@@ -227,10 +324,16 @@ export const useAgentAudio = defineStore('agentAudio', () => {
227
324
  currentAudio.currentTime = 0;
228
325
  return;
229
326
  }
327
+
230
328
  agentAudioMode.value = 'playingAgentResponse';
231
- await void currentAudio.play().catch((error) => {
329
+
330
+ try {
331
+ await currentAudio.play();
332
+ } catch (error) {
333
+ isPlaying = false;
334
+ setAudioModeReadyToRespond();
232
335
  console.error('Failed to play audio:', error);
233
- });
336
+ }
234
337
  }
235
338
 
236
339
  function initializeAudioStream(mimeType: string) {
@@ -244,6 +347,8 @@ export const useAgentAudio = defineStore('agentAudio', () => {
244
347
  const mediaSource = new MediaSource();
245
348
  currentAudioObjectUrl = URL.createObjectURL(mediaSource);
246
349
  currentAudio = new Audio(currentAudioObjectUrl);
350
+ currentAudio.preload = 'auto';
351
+ currentAudio.setAttribute('playsinline', '');
247
352
  currentAudio.addEventListener('ended', handleAudioEnded, { once: true });
248
353
  currentStreamingAudio = {
249
354
  mimeType,
@@ -299,7 +404,7 @@ export const useAgentAudio = defineStore('agentAudio', () => {
299
404
 
300
405
  if (!currentStreamingAudio.hasStartedPlayback) {
301
406
  currentStreamingAudio.hasStartedPlayback = true;
302
- setIsPlaying(true);
407
+ void setIsPlaying(true);
303
408
  }
304
409
 
305
410
  return;
@@ -356,6 +461,7 @@ export const useAgentAudio = defineStore('agentAudio', () => {
356
461
  bufferedAudioMimeType = 'audio/mpeg';
357
462
  detachStreamingAudio();
358
463
  destroyCurrentAudioElement();
464
+
359
465
  if (!dontResetMode) {
360
466
  setAudioModeReadyToRespond();
361
467
  }
@@ -369,8 +475,10 @@ export const useAgentAudio = defineStore('agentAudio', () => {
369
475
  function playAudioChunks(chunks: ArrayBuffer[], mimeType: string) {
370
476
  currentAudioObjectUrl = URL.createObjectURL(new Blob(chunks, { type: mimeType }));
371
477
  currentAudio = new Audio(currentAudioObjectUrl);
478
+ currentAudio.preload = 'auto';
479
+ currentAudio.setAttribute('playsinline', '');
372
480
  currentAudio.addEventListener('ended', handleAudioEnded, { once: true });
373
- setIsPlaying(true);
481
+ void setIsPlaying(true);
374
482
  }
375
483
 
376
484
  function base64ToArrayBuffer(base64: string) {
@@ -407,8 +515,9 @@ export const useAgentAudio = defineStore('agentAudio', () => {
407
515
  sendAudioToServerAndHandleResponse,
408
516
  stopGenerationAndAudio,
409
517
  stopCurrentAudioPlayback,
518
+ unlockAudioPlayback,
410
519
  playBeep,
411
- agentAudioMode
520
+ agentAudioMode,
521
+ playStandByAudio,
412
522
  };
413
-
414
523
  });
@@ -111,6 +111,7 @@ function toggleChatMode() {
111
111
  }
112
112
 
113
113
  async function onStartRecording() {
114
+ await agentAudio.unlockAudioPlayback();
114
115
  await requestMicAndStartVAD(saidSomething, stopRecording, onAnySound);
115
116
  microphoneButtonMode.value = 'listen';
116
117
  agentAudio.playBeep(1000);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminforth/agent",
3
- "version": "1.43.2",
3
+ "version": "1.43.3",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "type": "module",