@vapi-ai/web 2.5.2 → 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/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
@@ -300,6 +300,26 @@ class Vapi extends VapiEventEmitter {
300
300
  this.call.on('network-connection', (event) => {
301
301
  this.emit('network-connection', event);
302
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
+ });
303
323
  this.call.on('track-started', async (e) => {
304
324
  if (!e || !e.participant) {
305
325
  return;
@@ -479,6 +499,7 @@ class Vapi extends VapiEventEmitter {
479
499
  const audioObserverStartTime = Date.now();
480
500
  try {
481
501
  this.call.startRemoteParticipantsAudioLevelObserver(100);
502
+ this.call.startLocalAudioLevelObserver(100);
482
503
  const audioObserverDuration = Date.now() - audioObserverStartTime;
483
504
  this.emit('call-start-progress', {
484
505
  stage: 'audio-observer-setup',
@@ -509,9 +530,16 @@ class Vapi extends VapiEventEmitter {
509
530
  if (e)
510
531
  this.handleRemoteParticipantsAudioLevel(e);
511
532
  });
533
+ this.call.on('local-audio-level', (e) => {
534
+ if (e)
535
+ this.handleLocalAudioLevel(e);
536
+ });
512
537
  this.call.on('app-message', (e) => this.onAppMessage(e));
513
538
  this.call.on('nonfatal-error', (e) => {
514
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
+ }
515
543
  if (e?.type === 'audio-processor-error') {
516
544
  this.call
517
545
  ?.updateInputSettings({
@@ -653,6 +681,9 @@ class Vapi extends VapiEventEmitter {
653
681
  this.speakingTimeout = null;
654
682
  }, 1000);
655
683
  }
684
+ handleLocalAudioLevel(e) {
685
+ this.emit('local-volume-level', e.audioLevel);
686
+ }
656
687
  /**
657
688
  * Stops the call by destroying the Daily call object.
658
689
  *
@@ -733,6 +764,53 @@ class Vapi extends VapiEventEmitter {
733
764
  getDailyCallObject() {
734
765
  return this.call;
735
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
+ }
736
814
  startScreenSharing(displayMediaOptions, screenVideoSendSettings) {
737
815
  this.call?.startScreenShare({
738
816
  displayMediaOptions,
@@ -849,6 +927,26 @@ class Vapi extends VapiEventEmitter {
849
927
  this.call.on('network-connection', (event) => {
850
928
  this.emit('network-connection', event);
851
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
+ });
852
950
  this.call.on('track-started', async (e) => {
853
951
  if (!e || !e.participant) {
854
952
  return;
@@ -888,9 +986,16 @@ class Vapi extends VapiEventEmitter {
888
986
  if (e)
889
987
  this.handleRemoteParticipantsAudioLevel(e);
890
988
  });
989
+ this.call.on('local-audio-level', (e) => {
990
+ if (e)
991
+ this.handleLocalAudioLevel(e);
992
+ });
891
993
  this.call.on('app-message', (e) => this.onAppMessage(e));
892
994
  this.call.on('nonfatal-error', (e) => {
893
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
+ }
894
999
  if (e?.type === 'audio-processor-error') {
895
1000
  this.call
896
1001
  ?.updateInputSettings({
@@ -1021,6 +1126,7 @@ class Vapi extends VapiEventEmitter {
1021
1126
  const audioObserverStartTime = Date.now();
1022
1127
  try {
1023
1128
  this.call.startRemoteParticipantsAudioLevelObserver(100);
1129
+ this.call.startLocalAudioLevelObserver(100);
1024
1130
  const audioObserverDuration = Date.now() - audioObserverStartTime;
1025
1131
  this.emit('call-start-progress', {
1026
1132
  stage: 'audio-observer-setup',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vapi-ai/web",
3
- "version": "2.5.2",
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": {
package/vapi.ts CHANGED
@@ -5,7 +5,12 @@ import DailyIframe, {
5
5
  DailyAdvancedConfig,
6
6
  DailyFactoryOptions,
7
7
  DailyEventObjectAppMessage,
8
+ DailyEventObjectLocalAudioLevel,
9
+ DailyEventObjectNoPayload,
8
10
  DailyEventObjectParticipant,
11
+ DailyEventObjectRecordingError,
12
+ DailyEventObjectRecordingStarted,
13
+ DailyEventObjectRecordingStopped,
9
14
  DailyEventObjectRemoteParticipantsAudioLevel,
10
15
  DailyParticipant,
11
16
  DailyVideoSendSettings,
@@ -62,6 +67,8 @@ type VapiEventNames =
62
67
  | 'call-end'
63
68
  | 'call-start'
64
69
  | 'volume-level'
70
+ | 'local-volume-level'
71
+ | 'local-audio-level-observer-error'
65
72
  | 'speech-start'
66
73
  | 'speech-end'
67
74
  | 'message'
@@ -73,7 +80,12 @@ type VapiEventNames =
73
80
  | 'daily-participant-updated'
74
81
  | 'call-start-progress'
75
82
  | 'call-start-success'
76
- | 'call-start-failed';
83
+ | 'call-start-failed'
84
+ | 'recording-started'
85
+ | 'recording-stopped'
86
+ | 'recording-stats'
87
+ | 'recording-error'
88
+ | 'recording-upload-completed';
77
89
 
78
90
  interface CallStartProgressEvent {
79
91
  stage: string;
@@ -169,6 +181,8 @@ type VapiEventListeners = {
169
181
  'call-end': () => void;
170
182
  'call-start': () => void;
171
183
  'volume-level': (volume: number) => void;
184
+ 'local-volume-level': (volume: number) => void;
185
+ 'local-audio-level-observer-error': (error: any) => void;
172
186
  'speech-start': () => void;
173
187
  'speech-end': () => void;
174
188
  video: (track: MediaStreamTrack) => void;
@@ -181,6 +195,11 @@ type VapiEventListeners = {
181
195
  'call-start-progress': (event: CallStartProgressEvent) => void;
182
196
  'call-start-success': (event: CallStartSuccessEvent) => void;
183
197
  'call-start-failed': (event: CallStartFailedEvent) => void;
198
+ 'recording-started': (event: DailyEventObjectRecordingStarted) => void;
199
+ 'recording-stopped': (event: DailyEventObjectRecordingStopped) => void;
200
+ 'recording-stats': (event: DailyEventObjectNoPayload) => void;
201
+ 'recording-error': (event: DailyEventObjectRecordingError) => void;
202
+ 'recording-upload-completed': (event: DailyEventObjectNoPayload) => void;
184
203
  };
185
204
 
186
205
  type StartCallOptions = {
@@ -539,6 +558,26 @@ export default class Vapi extends VapiEventEmitter {
539
558
  this.emit('network-connection', event);
540
559
  });
541
560
 
561
+ this.call.on('recording-started', (event) => {
562
+ if (event) this.emit('recording-started', event);
563
+ });
564
+
565
+ this.call.on('recording-stopped', (event) => {
566
+ if (event) this.emit('recording-stopped', event);
567
+ });
568
+
569
+ this.call.on('recording-stats', (event) => {
570
+ if (event) this.emit('recording-stats', event);
571
+ });
572
+
573
+ this.call.on('recording-error', (event) => {
574
+ if (event) this.emit('recording-error', event);
575
+ });
576
+
577
+ this.call.on('recording-upload-completed', (event) => {
578
+ if (event) this.emit('recording-upload-completed', event);
579
+ });
580
+
542
581
  this.call.on('track-started', async (e) => {
543
582
  if (!e || !e.participant) {
544
583
  return;
@@ -736,6 +775,7 @@ export default class Vapi extends VapiEventEmitter {
736
775
 
737
776
  try {
738
777
  this.call.startRemoteParticipantsAudioLevelObserver(100);
778
+ this.call.startLocalAudioLevelObserver(100);
739
779
  const audioObserverDuration = Date.now() - audioObserverStartTime;
740
780
  this.emit('call-start-progress', {
741
781
  stage: 'audio-observer-setup',
@@ -766,10 +806,17 @@ export default class Vapi extends VapiEventEmitter {
766
806
  if (e) this.handleRemoteParticipantsAudioLevel(e);
767
807
  });
768
808
 
809
+ this.call.on('local-audio-level', (e) => {
810
+ if (e) this.handleLocalAudioLevel(e);
811
+ });
812
+
769
813
  this.call.on('app-message', (e) => this.onAppMessage(e));
770
814
 
771
815
  this.call.on('nonfatal-error', (e) => {
772
816
  // https://docs.daily.co/reference/daily-js/events/meeting-events#type-audio-processor-error
817
+ if (e?.type === 'local-audio-level-observer-error') {
818
+ this.emit('local-audio-level-observer-error', serializeError(e));
819
+ }
773
820
  if (e?.type === 'audio-processor-error') {
774
821
  this.call
775
822
  ?.updateInputSettings({
@@ -927,6 +974,10 @@ export default class Vapi extends VapiEventEmitter {
927
974
  }, 1000);
928
975
  }
929
976
 
977
+ private handleLocalAudioLevel(e: DailyEventObjectLocalAudioLevel) {
978
+ this.emit('local-volume-level', e.audioLevel);
979
+ }
980
+
930
981
  /**
931
982
  * Stops the call by destroying the Daily call object.
932
983
  *
@@ -1027,6 +1078,57 @@ export default class Vapi extends VapiEventEmitter {
1027
1078
  return this.call;
1028
1079
  }
1029
1080
 
1081
+ /**
1082
+ * Returns the most recent local (microphone) audio level, a number between 0 and 1.
1083
+ *
1084
+ * Useful for detecting when the local participant's microphone is not picking up any
1085
+ * audio (e.g. wrong input device selected, OS-level input volume at 0, or muted
1086
+ * hardware) so you can surface a "we can't hear you" style warning in your UI.
1087
+ *
1088
+ * Returns 0 when there is no active call.
1089
+ */
1090
+ public getLocalAudioLevel(): number {
1091
+ if (!this.call) {
1092
+ return 0;
1093
+ }
1094
+ return this.call.getLocalAudioLevel();
1095
+ }
1096
+
1097
+ /**
1098
+ * Starts (or restarts) the local audio level observer. The observer is started
1099
+ * automatically when a call begins, but this can be used to change the sampling
1100
+ * interval or restart it if it was stopped.
1101
+ *
1102
+ * While running, the Vapi instance emits 'local-volume-level' events with the local
1103
+ * microphone audio level (0 to 1).
1104
+ *
1105
+ * @param interval How often, in milliseconds, to emit audio level events. Defaults to Daily's default.
1106
+ */
1107
+ public async startLocalAudioLevelObserver(interval?: number): Promise<void> {
1108
+ if (!this.call) {
1109
+ throw new Error('Call object is not available.');
1110
+ }
1111
+ await this.call.startLocalAudioLevelObserver(interval);
1112
+ }
1113
+
1114
+ /**
1115
+ * Stops the local audio level observer. After calling this, 'local-volume-level'
1116
+ * events will no longer be emitted until the observer is started again.
1117
+ */
1118
+ public stopLocalAudioLevelObserver(): void {
1119
+ this.call?.stopLocalAudioLevelObserver();
1120
+ }
1121
+
1122
+ /**
1123
+ * Returns whether the local audio level observer is currently running.
1124
+ */
1125
+ public isLocalAudioLevelObserverRunning(): boolean {
1126
+ if (!this.call) {
1127
+ return false;
1128
+ }
1129
+ return this.call.isLocalAudioLevelObserverRunning();
1130
+ }
1131
+
1030
1132
  public startScreenSharing(displayMediaOptions?: DisplayMediaStreamOptions, screenVideoSendSettings?: DailyVideoSendSettings) {
1031
1133
  this.call?.startScreenShare({
1032
1134
  displayMediaOptions,
@@ -1161,6 +1263,26 @@ export default class Vapi extends VapiEventEmitter {
1161
1263
  this.emit('network-connection', event);
1162
1264
  });
1163
1265
 
1266
+ this.call.on('recording-started', (event) => {
1267
+ if (event) this.emit('recording-started', event);
1268
+ });
1269
+
1270
+ this.call.on('recording-stopped', (event) => {
1271
+ if (event) this.emit('recording-stopped', event);
1272
+ });
1273
+
1274
+ this.call.on('recording-stats', (event) => {
1275
+ if (event) this.emit('recording-stats', event);
1276
+ });
1277
+
1278
+ this.call.on('recording-error', (event) => {
1279
+ if (event) this.emit('recording-error', event);
1280
+ });
1281
+
1282
+ this.call.on('recording-upload-completed', (event) => {
1283
+ if (event) this.emit('recording-upload-completed', event);
1284
+ });
1285
+
1164
1286
  this.call.on('track-started', async (e) => {
1165
1287
  if (!e || !e.participant) {
1166
1288
  return;
@@ -1208,10 +1330,17 @@ export default class Vapi extends VapiEventEmitter {
1208
1330
  if (e) this.handleRemoteParticipantsAudioLevel(e);
1209
1331
  });
1210
1332
 
1333
+ this.call.on('local-audio-level', (e) => {
1334
+ if (e) this.handleLocalAudioLevel(e);
1335
+ });
1336
+
1211
1337
  this.call.on('app-message', (e) => this.onAppMessage(e));
1212
1338
 
1213
1339
  this.call.on('nonfatal-error', (e) => {
1214
1340
  // https://docs.daily.co/reference/daily-js/events/meeting-events#type-audio-processor-error
1341
+ if (e?.type === 'local-audio-level-observer-error') {
1342
+ this.emit('local-audio-level-observer-error', serializeError(e));
1343
+ }
1215
1344
  if (e?.type === 'audio-processor-error') {
1216
1345
  this.call
1217
1346
  ?.updateInputSettings({
@@ -1353,6 +1482,7 @@ export default class Vapi extends VapiEventEmitter {
1353
1482
 
1354
1483
  try {
1355
1484
  this.call.startRemoteParticipantsAudioLevelObserver(100);
1485
+ this.call.startLocalAudioLevelObserver(100);
1356
1486
  const audioObserverDuration = Date.now() - audioObserverStartTime;
1357
1487
  this.emit('call-start-progress', {
1358
1488
  stage: 'audio-observer-setup',