@vapi-ai/web 2.3.9 → 2.4.0

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.
@@ -0,0 +1,11 @@
1
+ import type { DailyCall, DailyAdvancedConfig, DailyFactoryOptions } from '@daily-co/daily-js';
2
+ export interface SafeDailyAdvancedConfig extends Omit<DailyAdvancedConfig, 'alwaysIncludeMicInPermissionPrompt'> {
3
+ alwaysIncludeMicInPermissionPrompt?: true;
4
+ }
5
+ export interface SafeDailyFactoryOptions extends Omit<DailyFactoryOptions, 'audioSource'> {
6
+ audioSource?: string | boolean | MediaStreamTrack;
7
+ }
8
+ export declare function createSafeDailyConfig(config?: Pick<DailyAdvancedConfig, 'avoidEval' | 'alwaysIncludeMicInPermissionPrompt'>): SafeDailyAdvancedConfig;
9
+ export declare function safeSetLocalAudio(call: DailyCall | null, enabled: boolean): void;
10
+ export declare function safeSetInputDevicesAsync(call: DailyCall | null, options: Parameters<DailyCall['setInputDevicesAsync']>[0]): Promise<void>;
11
+ export declare function createSafeDailyFactoryOptions(options?: Pick<DailyFactoryOptions, 'audioSource' | 'startAudioOff'>): SafeDailyFactoryOptions;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSafeDailyConfig = createSafeDailyConfig;
4
+ exports.safeSetLocalAudio = safeSetLocalAudio;
5
+ exports.safeSetInputDevicesAsync = safeSetInputDevicesAsync;
6
+ exports.createSafeDailyFactoryOptions = createSafeDailyFactoryOptions;
7
+ function createSafeDailyConfig(config) {
8
+ if (!config)
9
+ return {};
10
+ const { alwaysIncludeMicInPermissionPrompt, ...rest } = config;
11
+ // Force true or remove the property entirely. This can cause Chrome 140+ issues
12
+ if (alwaysIncludeMicInPermissionPrompt === false) {
13
+ console.warn('[Vapi] alwaysIncludeMicInPermissionPrompt:false detected. ' +
14
+ 'This can cause Chrome 140+ issues. Removing the property.');
15
+ return rest;
16
+ }
17
+ return config;
18
+ }
19
+ function safeSetLocalAudio(call, enabled) {
20
+ if (!call) {
21
+ throw new Error('Call object is not available.');
22
+ }
23
+ // Never use forceDiscardTrack. This can cause Chrome 140+ issues
24
+ call.setLocalAudio(enabled);
25
+ }
26
+ async function safeSetInputDevicesAsync(call, options) {
27
+ if (!call) {
28
+ throw new Error('Call object is not available.');
29
+ }
30
+ // Validate audioSource
31
+ if ('audioSource' in options && options.audioSource === false) {
32
+ console.warn('[Vapi] setInputDevicesAsync with audioSource:false detected. ' +
33
+ 'This can cause Chrome 140+ issues. Using default device instead.');
34
+ const { audioSource, ...safeOptions } = options;
35
+ await call.setInputDevicesAsync(safeOptions);
36
+ return;
37
+ }
38
+ await call.setInputDevicesAsync(options);
39
+ }
40
+ function createSafeDailyFactoryOptions(options) {
41
+ if (!options)
42
+ return {};
43
+ // Ensure audioSource is never false
44
+ if (options.audioSource === false) {
45
+ console.warn('[Vapi] audioSource:false detected in factory options. ' +
46
+ 'This can cause Chrome 140+ issues. Defaulting to true.');
47
+ return { ...options, audioSource: true };
48
+ }
49
+ return options;
50
+ }
package/dist/vapi.d.ts CHANGED
@@ -79,12 +79,12 @@ export default class Vapi extends VapiEventEmitter {
79
79
  start(assistant?: CreateAssistantDTO | string, assistantOverrides?: AssistantOverrides, squad?: CreateSquadDTO | string, workflow?: CreateWorkflowDTO | string, workflowOverrides?: WorkflowOverrides): Promise<Call | null>;
80
80
  private onAppMessage;
81
81
  private handleRemoteParticipantsAudioLevel;
82
- stop(): void;
82
+ stop(): Promise<void>;
83
83
  send(message: VapiClientToServerMessage): void;
84
84
  setMuted(mute: boolean): void;
85
85
  isMuted(): boolean;
86
86
  say(message: string, endCallAfterSpoken?: boolean, interruptionsEnabled?: boolean, interruptAssistantEnabled?: boolean): void;
87
- setInputDevicesAsync(options: Parameters<DailyCall['setInputDevicesAsync']>[0]): void;
87
+ setInputDevicesAsync(options: Parameters<DailyCall['setInputDevicesAsync']>[0]): Promise<void>;
88
88
  increaseMicLevel(gain: number): Promise<void>;
89
89
  setOutputDeviceAsync(options: Parameters<DailyCall['setOutputDeviceAsync']>[0]): void;
90
90
  getDailyCallObject(): DailyCall | null;
package/dist/vapi.js CHANGED
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  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
+ const daily_guards_1 = require("./daily-guards");
9
10
  async function startAudioPlayer(player, track) {
10
11
  player.muted = false;
11
12
  player.autoplay = true;
@@ -67,14 +68,16 @@ class Vapi extends VapiEventEmitter {
67
68
  super();
68
69
  client_1.client.baseUrl = apiBaseUrl ?? 'https://api.vapi.ai';
69
70
  client_1.client.setSecurityData(apiToken);
70
- this.dailyCallConfig = dailyCallConfig ?? {};
71
- this.dailyCallObject = dailyCallObject ?? {};
71
+ this.dailyCallConfig = (0, daily_guards_1.createSafeDailyConfig)(dailyCallConfig);
72
+ this.dailyCallObject = (0, daily_guards_1.createSafeDailyFactoryOptions)(dailyCallObject);
72
73
  }
73
- cleanup() {
74
+ async cleanup() {
74
75
  this.started = false;
75
76
  this.hasEmittedCallEndedStatus = false;
76
- this.call?.destroy();
77
- this.call = null;
77
+ if (this.call) {
78
+ await this.call.destroy();
79
+ this.call = null;
80
+ }
78
81
  this.speakingTimeout = null;
79
82
  }
80
83
  isMobileDevice() {
@@ -157,7 +160,7 @@ class Vapi extends VapiEventEmitter {
157
160
  timestamp: new Date().toISOString(),
158
161
  metadata: { action: 'cleanup-existing' }
159
162
  });
160
- this.cleanup();
163
+ await this.cleanup();
161
164
  }
162
165
  const isVideoRecordingEnabled = webCall?.artifactPlan?.videoRecordingEnabled ?? false;
163
166
  const isVideoEnabled = webCall?.assistant?.voice?.provider === 'tavus';
@@ -219,7 +222,7 @@ class Vapi extends VapiEventEmitter {
219
222
  if (isVideoRecordingEnabled) {
220
223
  this.call?.stopRecording();
221
224
  }
222
- this.cleanup();
225
+ this.cleanup().catch(console.error);
223
226
  });
224
227
  this.call.on('error', (error) => {
225
228
  this.emit('error', error);
@@ -455,7 +458,7 @@ class Vapi extends VapiEventEmitter {
455
458
  },
456
459
  })
457
460
  .then(() => {
458
- this.call?.setLocalAudio(true);
461
+ (0, daily_guards_1.safeSetLocalAudio)(this.call, true);
459
462
  });
460
463
  }
461
464
  });
@@ -536,7 +539,7 @@ class Vapi extends VapiEventEmitter {
536
539
  isMobile: this.isMobileDevice()
537
540
  }
538
541
  });
539
- this.cleanup();
542
+ await this.cleanup();
540
543
  return null;
541
544
  }
542
545
  }
@@ -584,19 +587,19 @@ class Vapi extends VapiEventEmitter {
584
587
  this.speakingTimeout = null;
585
588
  }, 1000);
586
589
  }
587
- stop() {
590
+ async stop() {
588
591
  this.started = false;
589
- this.call?.destroy();
590
- this.call = null;
592
+ if (this.call) {
593
+ await this.call.destroy();
594
+ this.call = null;
595
+ }
596
+ this.speakingTimeout = null;
591
597
  }
592
598
  send(message) {
593
599
  this.call?.sendAppMessage(JSON.stringify(message));
594
600
  }
595
601
  setMuted(mute) {
596
- if (!this.call) {
597
- throw new Error('Call object is not available.');
598
- }
599
- this.call.setLocalAudio(!mute);
602
+ (0, daily_guards_1.safeSetLocalAudio)(this.call, !mute);
600
603
  }
601
604
  isMuted() {
602
605
  if (!this.call) {
@@ -614,7 +617,7 @@ class Vapi extends VapiEventEmitter {
614
617
  });
615
618
  }
616
619
  setInputDevicesAsync(options) {
617
- this.call?.setInputDevicesAsync(options);
620
+ return (0, daily_guards_1.safeSetInputDevicesAsync)(this.call, options);
618
621
  }
619
622
  async increaseMicLevel(gain) {
620
623
  if (!this.call) {
@@ -630,7 +633,7 @@ class Vapi extends VapiEventEmitter {
630
633
  const destination = audioContext.createMediaStreamDestination();
631
634
  gainNode.connect(destination);
632
635
  const [boostedTrack] = destination.stream.getAudioTracks();
633
- await this.call.setInputDevicesAsync({ audioSource: boostedTrack });
636
+ await (0, daily_guards_1.safeSetInputDevicesAsync)(this.call, { audioSource: boostedTrack });
634
637
  }
635
638
  catch (error) {
636
639
  console.error("Error adjusting microphone level:", error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vapi-ai/web",
3
- "version": "2.3.9",
3
+ "version": "2.4.0",
4
4
  "description": "",
5
5
  "main": "dist/vapi.js",
6
6
  "types": "dist/vapi.d.ts",
@@ -30,7 +30,7 @@
30
30
  "node": ">=18.0.0"
31
31
  },
32
32
  "dependencies": {
33
- "@daily-co/daily-js": "^0.80.0",
33
+ "@daily-co/daily-js": "^0.84.0",
34
34
  "events": "^3.3.0"
35
35
  },
36
36
  "devDependencies": {
package/vapi.ts CHANGED
@@ -21,6 +21,12 @@ import {
21
21
  WorkflowOverrides,
22
22
  } from './api';
23
23
  import { client } from './client';
24
+ import {
25
+ createSafeDailyConfig,
26
+ createSafeDailyFactoryOptions,
27
+ safeSetLocalAudio,
28
+ safeSetInputDevicesAsync,
29
+ } from './daily-guards';
24
30
 
25
31
  export interface AddMessageMessage {
26
32
  type: 'add-message';
@@ -206,15 +212,17 @@ export default class Vapi extends VapiEventEmitter {
206
212
  super();
207
213
  client.baseUrl = apiBaseUrl ?? 'https://api.vapi.ai';
208
214
  client.setSecurityData(apiToken);
209
- this.dailyCallConfig = dailyCallConfig ?? {};
210
- this.dailyCallObject = dailyCallObject ?? {};
215
+ this.dailyCallConfig = createSafeDailyConfig(dailyCallConfig);
216
+ this.dailyCallObject = createSafeDailyFactoryOptions(dailyCallObject);
211
217
  }
212
218
 
213
- private cleanup() {
219
+ private async cleanup() {
214
220
  this.started = false;
215
221
  this.hasEmittedCallEndedStatus = false;
216
- this.call?.destroy();
217
- this.call = null;
222
+ if (this.call) {
223
+ await this.call.destroy();
224
+ this.call = null;
225
+ }
218
226
  this.speakingTimeout = null;
219
227
  }
220
228
 
@@ -319,7 +327,7 @@ export default class Vapi extends VapiEventEmitter {
319
327
  timestamp: new Date().toISOString(),
320
328
  metadata: { action: 'cleanup-existing' }
321
329
  });
322
- this.cleanup();
330
+ await this.cleanup();
323
331
  }
324
332
 
325
333
  const isVideoRecordingEnabled =
@@ -389,7 +397,7 @@ export default class Vapi extends VapiEventEmitter {
389
397
  if (isVideoRecordingEnabled) {
390
398
  this.call?.stopRecording();
391
399
  }
392
- this.cleanup();
400
+ this.cleanup().catch(console.error);
393
401
  });
394
402
 
395
403
  this.call.on('error', (error: any) => {
@@ -649,7 +657,7 @@ export default class Vapi extends VapiEventEmitter {
649
657
  },
650
658
  })
651
659
  .then(() => {
652
- this.call?.setLocalAudio(true);
660
+ safeSetLocalAudio(this.call, true);
653
661
  });
654
662
  }
655
663
  });
@@ -737,7 +745,7 @@ export default class Vapi extends VapiEventEmitter {
737
745
  }
738
746
  });
739
747
 
740
- this.cleanup();
748
+ await this.cleanup();
741
749
  return null;
742
750
  }
743
751
  }
@@ -794,10 +802,13 @@ export default class Vapi extends VapiEventEmitter {
794
802
  }, 1000);
795
803
  }
796
804
 
797
- stop(): void {
805
+ async stop(): Promise<void> {
798
806
  this.started = false;
799
- this.call?.destroy();
800
- this.call = null;
807
+ if (this.call) {
808
+ await this.call.destroy();
809
+ this.call = null;
810
+ }
811
+ this.speakingTimeout = null;
801
812
  }
802
813
 
803
814
  send(message: VapiClientToServerMessage): void {
@@ -805,10 +816,7 @@ export default class Vapi extends VapiEventEmitter {
805
816
  }
806
817
 
807
818
  public setMuted(mute: boolean) {
808
- if (!this.call) {
809
- throw new Error('Call object is not available.');
810
- }
811
- this.call.setLocalAudio(!mute);
819
+ safeSetLocalAudio(this.call, !mute);
812
820
  }
813
821
 
814
822
  public isMuted() {
@@ -832,7 +840,7 @@ export default class Vapi extends VapiEventEmitter {
832
840
  public setInputDevicesAsync(
833
841
  options: Parameters<DailyCall['setInputDevicesAsync']>[0],
834
842
  ) {
835
- this.call?.setInputDevicesAsync(options);
843
+ return safeSetInputDevicesAsync(this.call, options);
836
844
  }
837
845
 
838
846
  public async increaseMicLevel(gain: number) {
@@ -855,7 +863,7 @@ export default class Vapi extends VapiEventEmitter {
855
863
  gainNode.connect(destination);
856
864
 
857
865
  const [boostedTrack] = destination.stream.getAudioTracks();
858
- await this.call.setInputDevicesAsync({ audioSource: boostedTrack });
866
+ await safeSetInputDevicesAsync(this.call, { audioSource: boostedTrack });
859
867
  } catch (error) {
860
868
  console.error("Error adjusting microphone level:", error);
861
869
  }