@vapi-ai/web 2.5.1 → 2.6.1

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/api.js CHANGED
@@ -72,15 +72,20 @@ class HttpClient {
72
72
  [ContentType.Text]: (input) => input !== null && typeof input !== "string"
73
73
  ? JSON.stringify(input)
74
74
  : input,
75
- [ContentType.FormData]: (input) => Object.keys(input || {}).reduce((formData, key) => {
76
- const property = input[key];
77
- formData.append(key, property instanceof Blob
78
- ? property
79
- : typeof property === "object" && property !== null
80
- ? JSON.stringify(property)
81
- : `${property}`);
82
- return formData;
83
- }, new FormData()),
75
+ [ContentType.FormData]: (input) => {
76
+ if (input instanceof FormData) {
77
+ return input;
78
+ }
79
+ return Object.keys(input || {}).reduce((formData, key) => {
80
+ const property = input[key];
81
+ formData.append(key, property instanceof Blob
82
+ ? property
83
+ : typeof property === "object" && property !== null
84
+ ? JSON.stringify(property)
85
+ : `${property}`);
86
+ return formData;
87
+ }, new FormData());
88
+ },
84
89
  [ContentType.UrlEncoded]: (input) => this.toQueryString(input),
85
90
  };
86
91
  mergeRequestParams(params1, params2) {
@@ -138,12 +143,13 @@ class HttpClient {
138
143
  ? null
139
144
  : payloadFormatter(body),
140
145
  }).then(async (response) => {
141
- const r = response.clone();
146
+ const r = response;
142
147
  r.data = null;
143
148
  r.error = null;
149
+ const responseToParse = responseFormat ? response.clone() : response;
144
150
  const data = !responseFormat
145
151
  ? r
146
- : await response[responseFormat]()
152
+ : await responseToParse[responseFormat]()
147
153
  .then((data) => {
148
154
  if (r.ok) {
149
155
  r.data = data;
@@ -1489,23 +1495,6 @@ class Api extends HttpClient {
1489
1495
  format: "json",
1490
1496
  ...params,
1491
1497
  }),
1492
- /**
1493
- * @description Analyzes assistant configuration and generates contextual structured output recommendations
1494
- *
1495
- * @tags Structured Outputs
1496
- * @name StructuredOutputControllerSuggest
1497
- * @summary Generate AI-Powered Structured Output Suggestions
1498
- * @request POST:/structured-output/suggest
1499
- * @secure
1500
- */
1501
- structuredOutputControllerSuggest: (data, params = {}) => this.request({
1502
- path: `/structured-output/suggest`,
1503
- method: "POST",
1504
- body: data,
1505
- secure: true,
1506
- type: ContentType.Json,
1507
- ...params,
1508
- }),
1509
1498
  };
1510
1499
  testSuite = {
1511
1500
  /**
package/dist/vapi.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ChatCompletionMessageParam } from 'openai/resources';
2
- import { DailyCall, DailyAdvancedConfig, DailyFactoryOptions, DailyParticipant, DailyVideoSendSettings } from '@daily-co/daily-js';
2
+ import { DailyCall, DailyAdvancedConfig, DailyFactoryOptions, DailyEventObjectNoPayload, DailyEventObjectRecordingError, DailyEventObjectRecordingStarted, DailyEventObjectRecordingStopped, DailyParticipant, DailyVideoSendSettings } from '@daily-co/daily-js';
3
3
  import EventEmitter from 'events';
4
4
  import { Call, CreateSquadDTO, CreateAssistantDTO, AssistantOverrides, CreateWorkflowDTO, WorkflowOverrides } from './api';
5
5
  export interface EndCallMessage {
@@ -23,7 +23,7 @@ export interface SayMessage {
23
23
  interruptAssistantEnabled?: boolean;
24
24
  }
25
25
  type VapiClientToServerMessage = AddMessageMessage | ControlMessages | SayMessage | EndCallMessage;
26
- type VapiEventNames = 'call-end' | 'call-start' | 'volume-level' | 'speech-start' | 'speech-end' | 'message' | 'video' | 'error' | 'camera-error' | 'network-quality-change' | 'network-connection' | 'daily-participant-updated' | 'call-start-progress' | 'call-start-success' | 'call-start-failed';
26
+ type VapiEventNames = 'call-end' | 'call-start' | 'volume-level' | 'local-volume-level' | 'local-audio-level-observer-error' | 'speech-start' | 'speech-end' | 'message' | 'video' | 'error' | 'camera-error' | 'network-quality-change' | 'network-connection' | 'daily-participant-updated' | 'call-start-progress' | 'call-start-success' | 'call-start-failed' | 'recording-started' | 'recording-stopped' | 'recording-stats' | 'recording-error' | 'recording-upload-completed';
27
27
  interface CallStartProgressEvent {
28
28
  stage: string;
29
29
  status: 'started' | 'completed' | 'failed';
@@ -48,6 +48,8 @@ type VapiEventListeners = {
48
48
  'call-end': () => void;
49
49
  'call-start': () => void;
50
50
  'volume-level': (volume: number) => void;
51
+ 'local-volume-level': (volume: number) => void;
52
+ 'local-audio-level-observer-error': (error: any) => void;
51
53
  'speech-start': () => void;
52
54
  'speech-end': () => void;
53
55
  video: (track: MediaStreamTrack) => void;
@@ -60,6 +62,11 @@ type VapiEventListeners = {
60
62
  'call-start-progress': (event: CallStartProgressEvent) => void;
61
63
  'call-start-success': (event: CallStartSuccessEvent) => void;
62
64
  'call-start-failed': (event: CallStartFailedEvent) => void;
65
+ 'recording-started': (event: DailyEventObjectRecordingStarted) => void;
66
+ 'recording-stopped': (event: DailyEventObjectRecordingStopped) => void;
67
+ 'recording-stats': (event: DailyEventObjectNoPayload) => void;
68
+ 'recording-error': (event: DailyEventObjectRecordingError) => void;
69
+ 'recording-upload-completed': (event: DailyEventObjectNoPayload) => void;
63
70
  };
64
71
  type StartCallOptions = {
65
72
  /**
@@ -123,6 +130,7 @@ export default class Vapi extends VapiEventEmitter {
123
130
  start(assistant?: CreateAssistantDTO | string, assistantOverrides?: AssistantOverrides, squad?: CreateSquadDTO | string, workflow?: CreateWorkflowDTO | string, workflowOverrides?: WorkflowOverrides, options?: StartCallOptions): Promise<Call | null>;
124
131
  private onAppMessage;
125
132
  private handleRemoteParticipantsAudioLevel;
133
+ private handleLocalAudioLevel;
126
134
  /**
127
135
  * Stops the call by destroying the Daily call object.
128
136
  *
@@ -149,6 +157,36 @@ export default class Vapi extends VapiEventEmitter {
149
157
  increaseMicLevel(gain: number): Promise<void>;
150
158
  setOutputDeviceAsync(options: Parameters<DailyCall['setOutputDeviceAsync']>[0]): void;
151
159
  getDailyCallObject(): DailyCall | null;
160
+ /**
161
+ * Returns the most recent local (microphone) audio level, a number between 0 and 1.
162
+ *
163
+ * Useful for detecting when the local participant's microphone is not picking up any
164
+ * audio (e.g. wrong input device selected, OS-level input volume at 0, or muted
165
+ * hardware) so you can surface a "we can't hear you" style warning in your UI.
166
+ *
167
+ * Returns 0 when there is no active call.
168
+ */
169
+ getLocalAudioLevel(): number;
170
+ /**
171
+ * Starts (or restarts) the local audio level observer. The observer is started
172
+ * automatically when a call begins, but this can be used to change the sampling
173
+ * interval or restart it if it was stopped.
174
+ *
175
+ * While running, the Vapi instance emits 'local-volume-level' events with the local
176
+ * microphone audio level (0 to 1).
177
+ *
178
+ * @param interval How often, in milliseconds, to emit audio level events. Defaults to Daily's default.
179
+ */
180
+ startLocalAudioLevelObserver(interval?: number): Promise<void>;
181
+ /**
182
+ * Stops the local audio level observer. After calling this, 'local-volume-level'
183
+ * events will no longer be emitted until the observer is started again.
184
+ */
185
+ stopLocalAudioLevelObserver(): void;
186
+ /**
187
+ * Returns whether the local audio level observer is currently running.
188
+ */
189
+ isLocalAudioLevelObserverRunning(): boolean;
152
190
  startScreenSharing(displayMediaOptions?: DisplayMediaStreamOptions, screenVideoSendSettings?: DailyVideoSendSettings): void;
153
191
  stopScreenSharing(): void;
154
192
  /**
package/dist/vapi.js CHANGED
@@ -7,6 +7,57 @@ const daily_js_1 = __importDefault(require("@daily-co/daily-js"));
7
7
  const events_1 = __importDefault(require("events"));
8
8
  const client_1 = require("./client");
9
9
  const daily_guards_1 = require("./daily-guards");
10
+ /**
11
+ * Extracts error details into a plain object that serializes properly to JSON.
12
+ * Error objects don't serialize well because their properties (message, stack, name)
13
+ * are not enumerable and get lost when using JSON.stringify().
14
+ */
15
+ function serializeError(error) {
16
+ if (error === null || error === undefined) {
17
+ return { message: 'Unknown error (null or undefined)' };
18
+ }
19
+ if (error instanceof Error) {
20
+ const serialized = {
21
+ message: error.message,
22
+ name: error.name,
23
+ stack: error.stack,
24
+ };
25
+ // Include any additional properties that might be on the error
26
+ const errorAsAny = error;
27
+ if (errorAsAny.code !== undefined) {
28
+ serialized.code = errorAsAny.code;
29
+ }
30
+ if (errorAsAny.cause !== undefined) {
31
+ serialized.cause = String(errorAsAny.cause);
32
+ }
33
+ if (errorAsAny.reason !== undefined) {
34
+ serialized.reason = errorAsAny.reason;
35
+ }
36
+ if (errorAsAny.details !== undefined) {
37
+ serialized.details = errorAsAny.details;
38
+ }
39
+ // Daily.co specific error properties
40
+ if (errorAsAny.errorMsg !== undefined) {
41
+ serialized.errorMsg = errorAsAny.errorMsg;
42
+ }
43
+ if (errorAsAny.error !== undefined && typeof errorAsAny.error === 'string') {
44
+ serialized.errorDetail = errorAsAny.error;
45
+ }
46
+ return serialized;
47
+ }
48
+ if (typeof error === 'string') {
49
+ return { message: error };
50
+ }
51
+ if (typeof error === 'object') {
52
+ // It's already a plain object, but let's ensure message exists
53
+ const errorObj = error;
54
+ return {
55
+ message: errorObj.message || errorObj.error || JSON.stringify(error),
56
+ ...errorObj
57
+ };
58
+ }
59
+ return { message: String(error) };
60
+ }
10
61
  async function startAudioPlayer(player, track) {
11
62
  player.muted = false;
12
63
  player.autoplay = true;
@@ -98,7 +149,7 @@ class Vapi extends VapiEventEmitter {
98
149
  this.emit('error', {
99
150
  type: 'validation-error',
100
151
  stage: 'input-validation',
101
- message: error.message,
152
+ error: serializeError(error),
102
153
  timestamp: new Date().toISOString()
103
154
  });
104
155
  throw error;
@@ -194,17 +245,18 @@ class Vapi extends VapiEventEmitter {
194
245
  }
195
246
  catch (error) {
196
247
  const dailyCallDuration = Date.now() - dailyCallStartTime;
248
+ const serializedError = serializeError(error);
197
249
  this.emit('call-start-progress', {
198
250
  stage: 'daily-call-object-creation',
199
251
  status: 'failed',
200
252
  duration: dailyCallDuration,
201
253
  timestamp: new Date().toISOString(),
202
- metadata: { error: error?.toString() }
254
+ metadata: { error: serializedError.message }
203
255
  });
204
256
  this.emit('error', {
205
257
  type: 'daily-call-object-creation-error',
206
258
  stage: 'daily-call-object-creation',
207
- error,
259
+ error: serializedError,
208
260
  timestamp: new Date().toISOString()
209
261
  });
210
262
  throw error;
@@ -226,13 +278,21 @@ class Vapi extends VapiEventEmitter {
226
278
  this.cleanup().catch(console.error);
227
279
  });
228
280
  this.call.on('error', (error) => {
229
- this.emit('error', error);
281
+ this.emit('error', {
282
+ type: 'daily-error',
283
+ error: serializeError(error),
284
+ timestamp: new Date().toISOString()
285
+ });
230
286
  if (isVideoRecordingEnabled) {
231
287
  this.call?.stopRecording();
232
288
  }
233
289
  });
234
290
  this.call.on('camera-error', (error) => {
235
- this.emit('camera-error', error);
291
+ this.emit('camera-error', {
292
+ type: 'camera-error',
293
+ error: serializeError(error),
294
+ timestamp: new Date().toISOString()
295
+ });
236
296
  });
237
297
  this.call.on('network-quality-change', (event) => {
238
298
  this.emit('network-quality-change', event);
@@ -240,6 +300,26 @@ class Vapi extends VapiEventEmitter {
240
300
  this.call.on('network-connection', (event) => {
241
301
  this.emit('network-connection', event);
242
302
  });
303
+ this.call.on('recording-started', (event) => {
304
+ if (event)
305
+ this.emit('recording-started', event);
306
+ });
307
+ this.call.on('recording-stopped', (event) => {
308
+ if (event)
309
+ this.emit('recording-stopped', event);
310
+ });
311
+ this.call.on('recording-stats', (event) => {
312
+ if (event)
313
+ this.emit('recording-stats', event);
314
+ });
315
+ this.call.on('recording-error', (event) => {
316
+ if (event)
317
+ this.emit('recording-error', event);
318
+ });
319
+ this.call.on('recording-upload-completed', (event) => {
320
+ if (event)
321
+ this.emit('recording-upload-completed', event);
322
+ });
243
323
  this.call.on('track-started', async (e) => {
244
324
  if (!e || !e.participant) {
245
325
  return;
@@ -326,17 +406,18 @@ class Vapi extends VapiEventEmitter {
326
406
  }
327
407
  catch (error) {
328
408
  const joinDuration = Date.now() - joinStartTime;
409
+ const serializedError = serializeError(error);
329
410
  this.emit('call-start-progress', {
330
411
  stage: 'daily-call-join',
331
412
  status: 'failed',
332
413
  duration: joinDuration,
333
414
  timestamp: new Date().toISOString(),
334
- metadata: { error: error?.toString() }
415
+ metadata: { error: serializedError.message }
335
416
  });
336
417
  this.emit('error', {
337
418
  type: 'daily-call-join-error',
338
419
  stage: 'daily-call-join',
339
- error,
420
+ error: serializedError,
340
421
  duration: joinDuration,
341
422
  timestamp: new Date().toISOString()
342
423
  });
@@ -384,17 +465,18 @@ class Vapi extends VapiEventEmitter {
384
465
  }
385
466
  catch (error) {
386
467
  const recordingSetupDuration = Date.now() - recordingStartTime;
468
+ const serializedError = serializeError(error);
387
469
  this.emit('call-start-progress', {
388
470
  stage: 'video-recording-setup',
389
471
  status: 'failed',
390
472
  duration: recordingSetupDuration,
391
473
  timestamp: new Date().toISOString(),
392
- metadata: { error: error?.toString() }
474
+ metadata: { error: serializedError.message }
393
475
  });
394
476
  this.emit('error', {
395
477
  type: 'video-recording-setup-error',
396
478
  stage: 'video-recording-setup',
397
- error,
479
+ error: serializedError,
398
480
  timestamp: new Date().toISOString()
399
481
  });
400
482
  // Don't throw here, video recording is optional
@@ -417,6 +499,7 @@ class Vapi extends VapiEventEmitter {
417
499
  const audioObserverStartTime = Date.now();
418
500
  try {
419
501
  this.call.startRemoteParticipantsAudioLevelObserver(100);
502
+ this.call.startLocalAudioLevelObserver(100);
420
503
  const audioObserverDuration = Date.now() - audioObserverStartTime;
421
504
  this.emit('call-start-progress', {
422
505
  stage: 'audio-observer-setup',
@@ -427,17 +510,18 @@ class Vapi extends VapiEventEmitter {
427
510
  }
428
511
  catch (error) {
429
512
  const audioObserverDuration = Date.now() - audioObserverStartTime;
513
+ const serializedError = serializeError(error);
430
514
  this.emit('call-start-progress', {
431
515
  stage: 'audio-observer-setup',
432
516
  status: 'failed',
433
517
  duration: audioObserverDuration,
434
518
  timestamp: new Date().toISOString(),
435
- metadata: { error: error?.toString() }
519
+ metadata: { error: serializedError.message }
436
520
  });
437
521
  this.emit('error', {
438
522
  type: 'audio-observer-setup-error',
439
523
  stage: 'audio-observer-setup',
440
- error,
524
+ error: serializedError,
441
525
  timestamp: new Date().toISOString()
442
526
  });
443
527
  // Don't throw here, this is non-critical
@@ -446,9 +530,16 @@ class Vapi extends VapiEventEmitter {
446
530
  if (e)
447
531
  this.handleRemoteParticipantsAudioLevel(e);
448
532
  });
533
+ this.call.on('local-audio-level', (e) => {
534
+ if (e)
535
+ this.handleLocalAudioLevel(e);
536
+ });
449
537
  this.call.on('app-message', (e) => this.onAppMessage(e));
450
538
  this.call.on('nonfatal-error', (e) => {
451
539
  // https://docs.daily.co/reference/daily-js/events/meeting-events#type-audio-processor-error
540
+ if (e?.type === 'local-audio-level-observer-error') {
541
+ this.emit('local-audio-level-observer-error', serializeError(e));
542
+ }
452
543
  if (e?.type === 'audio-processor-error') {
453
544
  this.call
454
545
  ?.updateInputSettings({
@@ -488,17 +579,18 @@ class Vapi extends VapiEventEmitter {
488
579
  }
489
580
  catch (error) {
490
581
  const audioProcessingDuration = Date.now() - audioProcessingStartTime;
582
+ const serializedError = serializeError(error);
491
583
  this.emit('call-start-progress', {
492
584
  stage: 'audio-processing-setup',
493
585
  status: 'failed',
494
586
  duration: audioProcessingDuration,
495
587
  timestamp: new Date().toISOString(),
496
- metadata: { error: error?.toString() }
588
+ metadata: { error: serializedError.message }
497
589
  });
498
590
  this.emit('error', {
499
591
  type: 'audio-processing-setup-error',
500
592
  stage: 'audio-processing-setup',
501
- error,
593
+ error: serializedError,
502
594
  timestamp: new Date().toISOString()
503
595
  });
504
596
  // Don't throw here, this is non-critical
@@ -513,11 +605,12 @@ class Vapi extends VapiEventEmitter {
513
605
  }
514
606
  catch (e) {
515
607
  const totalDuration = Date.now() - startTime;
608
+ const serializedError = serializeError(e);
516
609
  this.emit('call-start-failed', {
517
610
  stage: 'unknown',
518
611
  totalDuration,
519
- error: e?.toString() || 'Unknown error occurred',
520
- errorStack: e instanceof Error ? e.stack : 'No stack trace available',
612
+ error: serializedError.message,
613
+ errorStack: serializedError.stack || 'No stack trace available',
521
614
  timestamp: new Date().toISOString(),
522
615
  context: {
523
616
  hasAssistant: !!assistant,
@@ -530,7 +623,7 @@ class Vapi extends VapiEventEmitter {
530
623
  this.emit('error', {
531
624
  type: 'start-method-error',
532
625
  stage: 'unknown',
533
- error: e,
626
+ error: serializedError,
534
627
  totalDuration,
535
628
  timestamp: new Date().toISOString(),
536
629
  context: {
@@ -588,6 +681,9 @@ class Vapi extends VapiEventEmitter {
588
681
  this.speakingTimeout = null;
589
682
  }, 1000);
590
683
  }
684
+ handleLocalAudioLevel(e) {
685
+ this.emit('local-volume-level', e.audioLevel);
686
+ }
591
687
  /**
592
688
  * Stops the call by destroying the Daily call object.
593
689
  *
@@ -668,6 +764,53 @@ class Vapi extends VapiEventEmitter {
668
764
  getDailyCallObject() {
669
765
  return this.call;
670
766
  }
767
+ /**
768
+ * Returns the most recent local (microphone) audio level, a number between 0 and 1.
769
+ *
770
+ * Useful for detecting when the local participant's microphone is not picking up any
771
+ * audio (e.g. wrong input device selected, OS-level input volume at 0, or muted
772
+ * hardware) so you can surface a "we can't hear you" style warning in your UI.
773
+ *
774
+ * Returns 0 when there is no active call.
775
+ */
776
+ getLocalAudioLevel() {
777
+ if (!this.call) {
778
+ return 0;
779
+ }
780
+ return this.call.getLocalAudioLevel();
781
+ }
782
+ /**
783
+ * Starts (or restarts) the local audio level observer. The observer is started
784
+ * automatically when a call begins, but this can be used to change the sampling
785
+ * interval or restart it if it was stopped.
786
+ *
787
+ * While running, the Vapi instance emits 'local-volume-level' events with the local
788
+ * microphone audio level (0 to 1).
789
+ *
790
+ * @param interval How often, in milliseconds, to emit audio level events. Defaults to Daily's default.
791
+ */
792
+ async startLocalAudioLevelObserver(interval) {
793
+ if (!this.call) {
794
+ throw new Error('Call object is not available.');
795
+ }
796
+ await this.call.startLocalAudioLevelObserver(interval);
797
+ }
798
+ /**
799
+ * Stops the local audio level observer. After calling this, 'local-volume-level'
800
+ * events will no longer be emitted until the observer is started again.
801
+ */
802
+ stopLocalAudioLevelObserver() {
803
+ this.call?.stopLocalAudioLevelObserver();
804
+ }
805
+ /**
806
+ * Returns whether the local audio level observer is currently running.
807
+ */
808
+ isLocalAudioLevelObserverRunning() {
809
+ if (!this.call) {
810
+ return false;
811
+ }
812
+ return this.call.isLocalAudioLevelObserverRunning();
813
+ }
671
814
  startScreenSharing(displayMediaOptions, screenVideoSendSettings) {
672
815
  this.call?.startScreenShare({
673
816
  displayMediaOptions,
@@ -762,13 +905,21 @@ class Vapi extends VapiEventEmitter {
762
905
  this.cleanup().catch(console.error);
763
906
  });
764
907
  this.call.on('error', (error) => {
765
- this.emit('error', error);
908
+ this.emit('error', {
909
+ type: 'daily-error',
910
+ error: serializeError(error),
911
+ timestamp: new Date().toISOString()
912
+ });
766
913
  if (isVideoRecordingEnabled) {
767
914
  this.call?.stopRecording();
768
915
  }
769
916
  });
770
917
  this.call.on('camera-error', (error) => {
771
- this.emit('camera-error', error);
918
+ this.emit('camera-error', {
919
+ type: 'camera-error',
920
+ error: serializeError(error),
921
+ timestamp: new Date().toISOString()
922
+ });
772
923
  });
773
924
  this.call.on('network-quality-change', (event) => {
774
925
  this.emit('network-quality-change', event);
@@ -776,6 +927,26 @@ class Vapi extends VapiEventEmitter {
776
927
  this.call.on('network-connection', (event) => {
777
928
  this.emit('network-connection', event);
778
929
  });
930
+ this.call.on('recording-started', (event) => {
931
+ if (event)
932
+ this.emit('recording-started', event);
933
+ });
934
+ this.call.on('recording-stopped', (event) => {
935
+ if (event)
936
+ this.emit('recording-stopped', event);
937
+ });
938
+ this.call.on('recording-stats', (event) => {
939
+ if (event)
940
+ this.emit('recording-stats', event);
941
+ });
942
+ this.call.on('recording-error', (event) => {
943
+ if (event)
944
+ this.emit('recording-error', event);
945
+ });
946
+ this.call.on('recording-upload-completed', (event) => {
947
+ if (event)
948
+ this.emit('recording-upload-completed', event);
949
+ });
779
950
  this.call.on('track-started', async (e) => {
780
951
  if (!e || !e.participant) {
781
952
  return;
@@ -815,9 +986,16 @@ class Vapi extends VapiEventEmitter {
815
986
  if (e)
816
987
  this.handleRemoteParticipantsAudioLevel(e);
817
988
  });
989
+ this.call.on('local-audio-level', (e) => {
990
+ if (e)
991
+ this.handleLocalAudioLevel(e);
992
+ });
818
993
  this.call.on('app-message', (e) => this.onAppMessage(e));
819
994
  this.call.on('nonfatal-error', (e) => {
820
995
  // https://docs.daily.co/reference/daily-js/events/meeting-events#type-audio-processor-error
996
+ if (e?.type === 'local-audio-level-observer-error') {
997
+ this.emit('local-audio-level-observer-error', serializeError(e));
998
+ }
821
999
  if (e?.type === 'audio-processor-error') {
822
1000
  this.call
823
1001
  ?.updateInputSettings({
@@ -920,12 +1098,13 @@ class Vapi extends VapiEventEmitter {
920
1098
  }
921
1099
  catch (error) {
922
1100
  const recordingSetupDuration = Date.now() - recordingStartTime;
1101
+ const serializedError = serializeError(error);
923
1102
  this.emit('call-start-progress', {
924
1103
  stage: 'video-recording-setup',
925
1104
  status: 'failed',
926
1105
  duration: recordingSetupDuration,
927
1106
  timestamp: new Date().toISOString(),
928
- metadata: { error: error?.toString() }
1107
+ metadata: { error: serializedError.message }
929
1108
  });
930
1109
  // Don't throw here, video recording is optional
931
1110
  }
@@ -947,6 +1126,7 @@ class Vapi extends VapiEventEmitter {
947
1126
  const audioObserverStartTime = Date.now();
948
1127
  try {
949
1128
  this.call.startRemoteParticipantsAudioLevelObserver(100);
1129
+ this.call.startLocalAudioLevelObserver(100);
950
1130
  const audioObserverDuration = Date.now() - audioObserverStartTime;
951
1131
  this.emit('call-start-progress', {
952
1132
  stage: 'audio-observer-setup',
@@ -957,12 +1137,13 @@ class Vapi extends VapiEventEmitter {
957
1137
  }
958
1138
  catch (error) {
959
1139
  const audioObserverDuration = Date.now() - audioObserverStartTime;
1140
+ const serializedError = serializeError(error);
960
1141
  this.emit('call-start-progress', {
961
1142
  stage: 'audio-observer-setup',
962
1143
  status: 'failed',
963
1144
  duration: audioObserverDuration,
964
1145
  timestamp: new Date().toISOString(),
965
- metadata: { error: error?.toString() }
1146
+ metadata: { error: serializedError.message }
966
1147
  });
967
1148
  // Don't throw here, this is non-critical
968
1149
  }
@@ -991,12 +1172,13 @@ class Vapi extends VapiEventEmitter {
991
1172
  }
992
1173
  catch (error) {
993
1174
  const audioProcessingDuration = Date.now() - audioProcessingStartTime;
1175
+ const serializedError = serializeError(error);
994
1176
  this.emit('call-start-progress', {
995
1177
  stage: 'audio-processing-setup',
996
1178
  status: 'failed',
997
1179
  duration: audioProcessingDuration,
998
1180
  timestamp: new Date().toISOString(),
999
- metadata: { error: error?.toString() }
1181
+ metadata: { error: serializedError.message }
1000
1182
  });
1001
1183
  // Don't throw here, this is non-critical
1002
1184
  }
@@ -1010,11 +1192,12 @@ class Vapi extends VapiEventEmitter {
1010
1192
  }
1011
1193
  catch (e) {
1012
1194
  const totalDuration = Date.now() - startTime;
1195
+ const serializedError = serializeError(e);
1013
1196
  this.emit('call-start-failed', {
1014
1197
  stage: 'reconnect',
1015
1198
  totalDuration,
1016
- error: e?.toString() || 'Unknown error occurred',
1017
- errorStack: e instanceof Error ? e.stack : 'No stack trace available',
1199
+ error: serializedError.message,
1200
+ errorStack: serializedError.stack || 'No stack trace available',
1018
1201
  timestamp: new Date().toISOString(),
1019
1202
  context: {
1020
1203
  isReconnect: true,
@@ -1027,7 +1210,7 @@ class Vapi extends VapiEventEmitter {
1027
1210
  // Also emit the generic error event for backward compatibility
1028
1211
  this.emit('error', {
1029
1212
  type: 'reconnect-error',
1030
- error: e,
1213
+ error: serializedError,
1031
1214
  totalDuration,
1032
1215
  timestamp: new Date().toISOString(),
1033
1216
  context: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vapi-ai/web",
3
- "version": "2.5.1",
3
+ "version": "2.6.1",
4
4
  "description": "",
5
5
  "main": "dist/vapi.js",
6
6
  "types": "dist/vapi.d.ts",
@@ -19,19 +19,19 @@
19
19
  },
20
20
  "repository": {
21
21
  "type": "git",
22
- "url": "git+https://github.com/VapiAI/web.git"
22
+ "url": "git+https://github.com/VapiAI/client-sdk-web.git"
23
23
  },
24
24
  "author": "vapi",
25
25
  "license": "MIT",
26
26
  "bugs": {
27
- "url": "https://github.com/VapiAI/web/issues"
27
+ "url": "https://github.com/VapiAI/client-sdk-web/issues"
28
28
  },
29
- "homepage": "https://github.com/VapiAI/web#readme",
29
+ "homepage": "https://github.com/VapiAI/client-sdk-web#readme",
30
30
  "engines": {
31
31
  "node": ">=18.0.0"
32
32
  },
33
33
  "dependencies": {
34
- "@daily-co/daily-js": "^0.85.0",
34
+ "@daily-co/daily-js": "^0.87.0",
35
35
  "events": "^3.3.0"
36
36
  },
37
37
  "devDependencies": {