@webex/plugin-meetings 3.9.0-webinar5k.1 → 3.10.0-multi-llms.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.
Files changed (147) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +1 -1
  3. package/dist/common/errors/webex-errors.js +21 -1
  4. package/dist/common/errors/webex-errors.js.map +1 -1
  5. package/dist/constants.js +25 -0
  6. package/dist/constants.js.map +1 -1
  7. package/dist/controls-options-manager/index.js +22 -5
  8. package/dist/controls-options-manager/index.js.map +1 -1
  9. package/dist/index.js +9 -1
  10. package/dist/index.js.map +1 -1
  11. package/dist/interceptors/index.js +7 -0
  12. package/dist/interceptors/index.js.map +1 -1
  13. package/dist/interceptors/locusRouteToken.js +116 -0
  14. package/dist/interceptors/locusRouteToken.js.map +1 -0
  15. package/dist/interpretation/index.js +1 -1
  16. package/dist/interpretation/siLanguage.js +1 -1
  17. package/dist/locus-info/controlsUtils.js +11 -2
  18. package/dist/locus-info/controlsUtils.js.map +1 -1
  19. package/dist/locus-info/index.js +76 -322
  20. package/dist/locus-info/index.js.map +1 -1
  21. package/dist/locus-info/parser.js +4 -1
  22. package/dist/locus-info/parser.js.map +1 -1
  23. package/dist/media/index.js +5 -0
  24. package/dist/media/index.js.map +1 -1
  25. package/dist/media/properties.js +53 -5
  26. package/dist/media/properties.js.map +1 -1
  27. package/dist/meeting/in-meeting-actions.js +14 -0
  28. package/dist/meeting/in-meeting-actions.js.map +1 -1
  29. package/dist/meeting/index.js +468 -278
  30. package/dist/meeting/index.js.map +1 -1
  31. package/dist/meeting/request.js +177 -14
  32. package/dist/meeting/request.js.map +1 -1
  33. package/dist/meeting/type.js +7 -0
  34. package/dist/meeting/type.js.map +1 -0
  35. package/dist/meeting/util.js +100 -3
  36. package/dist/meeting/util.js.map +1 -1
  37. package/dist/meeting-info/meeting-info-v2.js +29 -21
  38. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  39. package/dist/meetings/index.js +20 -16
  40. package/dist/meetings/index.js.map +1 -1
  41. package/dist/member/index.js +9 -0
  42. package/dist/member/index.js.map +1 -1
  43. package/dist/member/util.js +10 -0
  44. package/dist/member/util.js.map +1 -1
  45. package/dist/members/index.js +10 -7
  46. package/dist/members/index.js.map +1 -1
  47. package/dist/members/util.js +7 -2
  48. package/dist/members/util.js.map +1 -1
  49. package/dist/metrics/constants.js +2 -1
  50. package/dist/metrics/constants.js.map +1 -1
  51. package/dist/multistream/mediaRequestManager.js +1 -1
  52. package/dist/multistream/mediaRequestManager.js.map +1 -1
  53. package/dist/multistream/remoteMedia.js +34 -5
  54. package/dist/multistream/remoteMedia.js.map +1 -1
  55. package/dist/multistream/remoteMediaGroup.js +42 -2
  56. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  57. package/dist/reachability/index.js +3 -3
  58. package/dist/reachability/index.js.map +1 -1
  59. package/dist/types/common/errors/webex-errors.d.ts +12 -0
  60. package/dist/types/constants.d.ts +23 -0
  61. package/dist/types/controls-options-manager/index.d.ts +9 -1
  62. package/dist/types/index.d.ts +2 -1
  63. package/dist/types/interceptors/index.d.ts +2 -1
  64. package/dist/types/interceptors/locusRouteToken.d.ts +38 -0
  65. package/dist/types/locus-info/index.d.ts +9 -54
  66. package/dist/types/media/properties.d.ts +21 -0
  67. package/dist/types/meeting/in-meeting-actions.d.ts +14 -0
  68. package/dist/types/meeting/index.d.ts +64 -29
  69. package/dist/types/meeting/request.d.ts +42 -0
  70. package/dist/types/meeting/type.d.ts +9 -0
  71. package/dist/types/meeting/util.d.ts +13 -0
  72. package/dist/types/meeting-info/meeting-info-v2.d.ts +6 -3
  73. package/dist/types/meetings/index.d.ts +3 -1
  74. package/dist/types/member/index.d.ts +1 -0
  75. package/dist/types/member/util.d.ts +5 -0
  76. package/dist/types/members/index.d.ts +12 -11
  77. package/dist/types/members/util.d.ts +8 -4
  78. package/dist/types/metrics/constants.d.ts +1 -0
  79. package/dist/types/multistream/remoteMedia.d.ts +20 -1
  80. package/dist/types/multistream/remoteMediaGroup.d.ts +11 -0
  81. package/dist/webinar/index.js +1 -1
  82. package/package.json +26 -28
  83. package/src/common/errors/webex-errors.ts +19 -0
  84. package/src/constants.ts +26 -2
  85. package/src/controls-options-manager/index.ts +26 -5
  86. package/src/index.ts +4 -1
  87. package/src/interceptors/index.ts +2 -1
  88. package/src/interceptors/locusRouteToken.ts +80 -0
  89. package/src/locus-info/controlsUtils.ts +18 -0
  90. package/src/locus-info/index.ts +69 -357
  91. package/src/locus-info/parser.ts +5 -1
  92. package/src/media/index.ts +6 -0
  93. package/src/media/properties.ts +43 -0
  94. package/src/meeting/in-meeting-actions.ts +29 -0
  95. package/src/meeting/index.ts +299 -88
  96. package/src/meeting/request.ts +141 -0
  97. package/src/meeting/type.ts +9 -0
  98. package/src/meeting/util.ts +107 -3
  99. package/src/meeting-info/meeting-info-v2.ts +24 -5
  100. package/src/meetings/index.ts +15 -22
  101. package/src/member/index.ts +10 -0
  102. package/src/member/util.ts +14 -0
  103. package/src/members/index.ts +20 -10
  104. package/src/members/util.ts +20 -3
  105. package/src/metrics/constants.ts +1 -0
  106. package/src/multistream/mediaRequestManager.ts +7 -7
  107. package/src/multistream/remoteMedia.ts +34 -4
  108. package/src/multistream/remoteMediaGroup.ts +37 -2
  109. package/src/reachability/index.ts +3 -3
  110. package/test/unit/spec/common/browser-detection.js +0 -24
  111. package/test/unit/spec/controls-options-manager/index.js +47 -0
  112. package/test/unit/spec/fixture/locus.js +1 -0
  113. package/test/unit/spec/interceptors/locusRouteToken.ts +87 -0
  114. package/test/unit/spec/locus-info/index.js +80 -361
  115. package/test/unit/spec/locus-info/parser.js +3 -2
  116. package/test/unit/spec/media/index.ts +140 -9
  117. package/test/unit/spec/media/properties.ts +137 -0
  118. package/test/unit/spec/meeting/in-meeting-actions.ts +14 -0
  119. package/test/unit/spec/meeting/index.js +679 -82
  120. package/test/unit/spec/meeting/muteState.js +32 -6
  121. package/test/unit/spec/meeting/request.js +21 -0
  122. package/test/unit/spec/meeting/utils.js +170 -17
  123. package/test/unit/spec/meeting-info/meetinginfov2.js +8 -3
  124. package/test/unit/spec/meetings/index.js +12 -7
  125. package/test/unit/spec/member/util.js +24 -0
  126. package/test/unit/spec/members/collection.js +120 -0
  127. package/test/unit/spec/members/index.js +107 -2
  128. package/test/unit/spec/members/request.js +55 -0
  129. package/test/unit/spec/members/utils.js +116 -14
  130. package/test/unit/spec/multistream/mediaRequestManager.ts +19 -6
  131. package/test/unit/spec/multistream/remoteMedia.ts +66 -2
  132. package/test/unit/spec/reachability/index.ts +158 -3
  133. package/test/unit/spec/roap/turnDiscovery.ts +3 -3
  134. package/dist/hashTree/constants.js +0 -23
  135. package/dist/hashTree/constants.js.map +0 -1
  136. package/dist/hashTree/hashTree.js +0 -516
  137. package/dist/hashTree/hashTree.js.map +0 -1
  138. package/dist/hashTree/hashTreeParser.js +0 -521
  139. package/dist/hashTree/hashTreeParser.js.map +0 -1
  140. package/dist/types/hashTree/constants.d.ts +0 -8
  141. package/dist/types/hashTree/hashTree.d.ts +0 -128
  142. package/dist/types/hashTree/hashTreeParser.d.ts +0 -152
  143. package/src/hashTree/constants.ts +0 -12
  144. package/src/hashTree/hashTree.ts +0 -460
  145. package/src/hashTree/hashTreeParser.ts +0 -556
  146. package/test/unit/spec/hashTree/hashTree.ts +0 -394
  147. package/test/unit/spec/hashTree/hashTreeParser.ts +0 -156
@@ -28,6 +28,9 @@ import {
28
28
  StatsAnalyzerEventNames,
29
29
  NetworkQualityEventNames,
30
30
  NetworkQualityMonitor,
31
+ StatsMonitor,
32
+ StatsMonitorEventNames,
33
+ InboundAudioIssueSubTypes,
31
34
  } from '@webex/internal-media-core';
32
35
 
33
36
  import {
@@ -55,6 +58,7 @@ import {
55
58
  NoMediaEstablishedYetError,
56
59
  UserNotJoinedError,
57
60
  AddMediaFailed,
61
+ SdpResponseTimeoutError,
58
62
  } from '../common/errors/webex-errors';
59
63
 
60
64
  import LoggerProxy from '../common/logs/logger-proxy';
@@ -66,7 +70,7 @@ import Media, {type BundlePolicy} from '../media';
66
70
  import MediaProperties from '../media/properties';
67
71
  import MeetingStateMachine from './state';
68
72
  import {createMuteState} from './muteState';
69
- import LocusInfo, {LocusDTO, LocusLLMEvent} from '../locus-info';
73
+ import LocusInfo from '../locus-info';
70
74
  import Metrics from '../metrics';
71
75
  import ReconnectionManager from '../reconnection-manager';
72
76
  import ReconnectionNotStartedError from '../common/errors/reconnection-not-started';
@@ -166,7 +170,7 @@ import MultistreamNotSupportedError from '../common/errors/multistream-not-suppo
166
170
  import JoinForbiddenError from '../common/errors/join-forbidden-error';
167
171
  import {ReachabilityMetrics} from '../reachability/reachability.types';
168
172
  import {SetStageOptions, SetStageVideoLayout, UnsetStageVideoLayout} from './request.type';
169
- import {DataSet} from '../hashTree/hashTreeParser';
173
+ import {Invitee} from './type';
170
174
 
171
175
  // default callback so we don't call an undefined function, but in practice it should never be used
172
176
  const DEFAULT_ICE_PHASE_CALLBACK = () => 'JOIN_MEETING_FINAL';
@@ -251,6 +255,7 @@ export type CallStateForMetrics = {
251
255
  loginType?: string;
252
256
  userNameInput?: string;
253
257
  emailInput?: string;
258
+ pstnCorrelationId?: string;
254
259
  };
255
260
 
256
261
  export const MEDIA_UPDATE_TYPE = {
@@ -268,6 +273,7 @@ export enum ScreenShareFloorStatus {
268
273
  type FetchMeetingInfoParams = {
269
274
  password?: string;
270
275
  registrationId?: string;
276
+ classificationId?: string;
271
277
  captchaCode?: string;
272
278
  extraParams?: Record<string, any>;
273
279
  sendCAevents?: boolean;
@@ -632,6 +638,7 @@ export default class Meeting extends StatelessWebexPlugin {
632
638
  shareStatus: string;
633
639
  screenShareFloorState: ScreenShareFloorStatus;
634
640
  statsAnalyzer: StatsAnalyzer;
641
+ statsMonitor: StatsMonitor;
635
642
  transcription: Transcription;
636
643
  updateMediaConnections: (mediaConnections: any[]) => void;
637
644
  userDisplayHints: any;
@@ -744,10 +751,11 @@ export default class Meeting extends StatelessWebexPlugin {
744
751
  /**
745
752
  * @param {Object} attrs
746
753
  * @param {Object} options
754
+ * @param {Function} callback - if provided, it will be called with the newly created meeting object as soon as the meeting.id is set
747
755
  * @constructor
748
756
  * @memberof Meeting
749
757
  */
750
- constructor(attrs: any, options: object) {
758
+ constructor(attrs: any, options: object, callback: (meeting: Meeting) => void) {
751
759
  super({}, options);
752
760
  /**
753
761
  * @instance
@@ -773,6 +781,11 @@ export default class Meeting extends StatelessWebexPlugin {
773
781
  * @memberof Meeting
774
782
  */
775
783
  this.id = uuid.v4();
784
+
785
+ if (callback) {
786
+ callback(this);
787
+ }
788
+
776
789
  /**
777
790
  * Call state used for metrics
778
791
  * @instance
@@ -1279,6 +1292,13 @@ export default class Meeting extends StatelessWebexPlugin {
1279
1292
  * @memberof Meeting
1280
1293
  */
1281
1294
  this.networkQualityMonitor = null;
1295
+ /**
1296
+ * @instance
1297
+ * @type {StatsMonitor}
1298
+ * @private
1299
+ * @memberof Meeting
1300
+ */
1301
+ this.statsMonitor = null;
1282
1302
  /**
1283
1303
  * Indicates network status of the webrtc media connection
1284
1304
  * @instance
@@ -1677,6 +1697,22 @@ export default class Meeting extends StatelessWebexPlugin {
1677
1697
  this.callStateForMetrics.correlationId = correlationId;
1678
1698
  }
1679
1699
 
1700
+ /**
1701
+ * Getter - Returns callStateForMetrics.pstnCorrelationId
1702
+ * @returns {string | undefined}
1703
+ */
1704
+ get pstnCorrelationId(): string | undefined {
1705
+ return this.callStateForMetrics.pstnCorrelationId;
1706
+ }
1707
+
1708
+ /**
1709
+ * Setter - sets callStateForMetrics.pstnCorrelationId
1710
+ * @param {string | undefined} correlationId
1711
+ */
1712
+ set pstnCorrelationId(correlationId: string | undefined) {
1713
+ this.callStateForMetrics.pstnCorrelationId = correlationId;
1714
+ }
1715
+
1680
1716
  /**
1681
1717
  * Getter - Returns callStateForMetrics.userNameInput
1682
1718
  * @returns {string}
@@ -1879,6 +1915,7 @@ export default class Meeting extends StatelessWebexPlugin {
1879
1915
  extraParams = {},
1880
1916
  sendCAevents = false,
1881
1917
  registrationId = null,
1918
+ classificationId = null,
1882
1919
  }): Promise<void> {
1883
1920
  try {
1884
1921
  const captchaInfo = captchaCode
@@ -1895,7 +1932,9 @@ export default class Meeting extends StatelessWebexPlugin {
1895
1932
  this.locusId,
1896
1933
  extraParams,
1897
1934
  {meetingId: this.id, sendCAevents},
1898
- registrationId
1935
+ registrationId,
1936
+ null,
1937
+ classificationId
1899
1938
  );
1900
1939
 
1901
1940
  this.parseMeetingInfo(info?.body, this.destination, info?.errors);
@@ -2939,6 +2978,18 @@ export default class Meeting extends StatelessWebexPlugin {
2939
2978
  );
2940
2979
  });
2941
2980
 
2981
+ this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_AUTO_END_MEETING_WARNING_CHANGED, ({state}) => {
2982
+ Trigger.trigger(
2983
+ this,
2984
+ {
2985
+ file: 'meeting/index',
2986
+ function: 'setupLocusControlsListener',
2987
+ },
2988
+ EVENT_TRIGGERS.MEETING_CONTROLS_AUTO_END_MEETING_WARNING_UPDATED,
2989
+ {state}
2990
+ );
2991
+ });
2992
+
2942
2993
  this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_ANNOTATION_CHANGED, ({state}) => {
2943
2994
  Trigger.trigger(
2944
2995
  this,
@@ -3059,12 +3110,16 @@ export default class Meeting extends StatelessWebexPlugin {
3059
3110
  // There is no concept of local/remote share for whiteboard
3060
3111
  // It does not matter who requested to share the whiteboard, everyone gets the same view
3061
3112
  else if (whiteboardShare.disposition === FLOOR_ACTION.GRANTED) {
3062
- // WHITEBOARD - sharing whiteboard
3063
- // Webinar attendee should receive whiteboard as remote share
3064
- newShareStatus =
3065
- this.locusInfo?.info?.isWebinar && this.webinar?.selfIsAttendee
3066
- ? SHARE_STATUS.REMOTE_SHARE_ACTIVE
3067
- : SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
3113
+ if (this.locusInfo?.info?.isWebinar && this.webinar?.selfIsAttendee) {
3114
+ // WHITEBOARD - sharing whiteboard
3115
+ // Webinar attendee should receive whiteboard as remote share
3116
+ newShareStatus = SHARE_STATUS.REMOTE_SHARE_ACTIVE;
3117
+ } else if (this.guest) {
3118
+ // If user is a guest to a meeting, they should receive whiteboard as remote share
3119
+ newShareStatus = SHARE_STATUS.REMOTE_SHARE_ACTIVE;
3120
+ } else {
3121
+ newShareStatus = SHARE_STATUS.WHITEBOARD_SHARE_ACTIVE;
3122
+ }
3068
3123
  }
3069
3124
  // or if content share is either released or null and whiteboard share is either released or null, no one is sharing
3070
3125
  else if (
@@ -3122,6 +3177,23 @@ export default class Meeting extends StatelessWebexPlugin {
3122
3177
  },
3123
3178
  EVENT_TRIGGERS.MEETING_STOPPED_SHARING_WHITEBOARD
3124
3179
  );
3180
+ // @ts-ignore
3181
+ this.webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp({
3182
+ key: 'internal.client.share.stopped',
3183
+ });
3184
+ // @ts-ignore
3185
+ this.webex.internal.newMetrics.submitClientEvent({
3186
+ name: 'client.share.stopped',
3187
+ payload: {
3188
+ mediaType: 'whiteboard',
3189
+ shareDuration:
3190
+ // @ts-ignore
3191
+ this.webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration(),
3192
+ },
3193
+ options: {
3194
+ meetingId: this.id,
3195
+ },
3196
+ });
3125
3197
  break;
3126
3198
 
3127
3199
  case SHARE_STATUS.NO_SHARE:
@@ -3140,6 +3212,14 @@ export default class Meeting extends StatelessWebexPlugin {
3140
3212
  this.shareCAEventSentStatus.receiveStart = false;
3141
3213
  this.shareCAEventSentStatus.receiveStop = false;
3142
3214
 
3215
+ let finalBeneficiaryId = contentShare.beneficiaryId;
3216
+ // In case of attendee in webinar, the whiteboard is shared by other participants
3217
+ if (this.locusInfo?.info?.isWebinar && this.webinar?.selfIsAttendee) {
3218
+ if (!finalBeneficiaryId && whiteboardShare.beneficiaryId) {
3219
+ finalBeneficiaryId = whiteboardShare.beneficiaryId;
3220
+ }
3221
+ }
3222
+
3143
3223
  Trigger.trigger(
3144
3224
  this,
3145
3225
  {
@@ -3148,7 +3228,7 @@ export default class Meeting extends StatelessWebexPlugin {
3148
3228
  },
3149
3229
  EVENT_TRIGGERS.MEETING_STARTED_SHARING_REMOTE,
3150
3230
  {
3151
- memberId: contentShare.beneficiaryId,
3231
+ memberId: finalBeneficiaryId,
3152
3232
  url: contentShare.url,
3153
3233
  shareInstanceId: this.remoteShareInstanceId,
3154
3234
  annotationInfo: contentShare.annotation,
@@ -3290,27 +3370,31 @@ export default class Meeting extends StatelessWebexPlugin {
3290
3370
  * @memberof Meeting
3291
3371
  */
3292
3372
  private setUpLocusUrlListener() {
3293
- this.locusInfo.on(EVENTS.LOCUS_INFO_UPDATE_URL, (payload) => {
3294
- this.members.locusUrlUpdate(payload);
3295
- this.breakouts.locusUrlUpdate(payload);
3296
- this.simultaneousInterpretation.locusUrlUpdate(payload);
3297
- this.annotation.locusUrlUpdate(payload);
3298
- this.locusUrl = payload;
3299
- this.locusId = this.locusUrl?.split('/').pop();
3300
- this.recordingController.setLocusUrl(this.locusUrl);
3301
- this.controlsOptionsManager.setLocusUrl(this.locusUrl);
3302
- this.webinar.locusUrlUpdate(payload);
3373
+ this.locusInfo.on(
3374
+ EVENTS.LOCUS_INFO_UPDATE_URL,
3375
+ (payload: {url: string; isMainLocus?: boolean}) => {
3376
+ const {url, isMainLocus} = payload;
3377
+ this.members.locusUrlUpdate(url);
3378
+ this.breakouts.locusUrlUpdate(url);
3379
+ this.simultaneousInterpretation.locusUrlUpdate(url);
3380
+ this.annotation.locusUrlUpdate(url);
3381
+ this.locusUrl = url;
3382
+ this.locusId = this.locusUrl?.split('/').pop();
3383
+ this.recordingController.setLocusUrl(this.locusUrl);
3384
+ this.controlsOptionsManager.setLocusUrl(this.locusUrl, !!isMainLocus);
3385
+ this.webinar.locusUrlUpdate(url);
3303
3386
 
3304
- Trigger.trigger(
3305
- this,
3306
- {
3307
- file: 'meeting/index',
3308
- function: 'setUpLocusSelfListener',
3309
- },
3310
- EVENT_TRIGGERS.MEETING_LOCUS_URL_UPDATE,
3311
- {locusUrl: payload}
3312
- );
3313
- });
3387
+ Trigger.trigger(
3388
+ this,
3389
+ {
3390
+ file: 'meeting/index',
3391
+ function: 'setUpLocusSelfListener',
3392
+ },
3393
+ EVENT_TRIGGERS.MEETING_LOCUS_URL_UPDATE,
3394
+ {locusUrl: url}
3395
+ );
3396
+ }
3397
+ );
3314
3398
  }
3315
3399
 
3316
3400
  /**
@@ -3830,49 +3914,43 @@ export default class Meeting extends StatelessWebexPlugin {
3830
3914
 
3831
3915
  /**
3832
3916
  * Invite a guest to the call that isn't normally part of this call
3833
- * @param {Object} invitee
3917
+ * @param {Invitee} invitee
3834
3918
  * @param {String} invitee.emailAddress
3835
3919
  * @param {String} invitee.email
3836
3920
  * @param {String} invitee.phoneNumber
3837
3921
  * @param {Boolean} [alertIfActive]
3922
+ * @param {Boolean} [invitee.skipEmailValidation]
3923
+ * @param {Boolean} [invitee.isInternalNumber]
3838
3924
  * @returns {Promise} see #members.addMember
3839
3925
  * @public
3840
3926
  * @memberof Meeting
3841
3927
  */
3842
- public invite(
3843
- invitee: {
3844
- emailAddress: string;
3845
- email: string;
3846
- phoneNumber: string;
3847
- roles: Array<string>;
3848
- },
3849
- alertIfActive = true
3850
- ) {
3928
+ public invite(invitee: Invitee, alertIfActive = true) {
3851
3929
  return this.members.addMember(invitee, alertIfActive);
3852
3930
  }
3853
3931
 
3854
3932
  /**
3855
3933
  * Cancel an outgoing phone call invitation made during a meeting
3856
- * @param {Object} invitee
3934
+ * @param {Invitee} invitee
3857
3935
  * @param {String} invitee.phoneNumber
3858
3936
  * @returns {Promise} see #members.cancelPhoneInvite
3859
3937
  * @public
3860
3938
  * @memberof Meeting
3861
3939
  */
3862
- public cancelPhoneInvite(invitee: {phoneNumber: string}) {
3940
+ public cancelPhoneInvite(invitee: Invitee) {
3863
3941
  return this.members.cancelPhoneInvite(invitee);
3864
3942
  }
3865
3943
 
3866
3944
  /**
3867
3945
  * Cancel an SIP/phone call invitation made during a meeting
3868
- * @param {Object} invitee
3946
+ * @param {Invitee} invitee
3869
3947
  * @param {String} invitee.memberId
3870
3948
  * @param {Boolean} [invitee.isInternalNumber] - When cancel phone invitation, if the number is internal
3871
3949
  * @returns {Promise} see #members.cancelInviteByMemberId
3872
3950
  * @public
3873
3951
  * @memberof Meeting
3874
3952
  */
3875
- public cancelInviteByMemberId(invitee: {memberId: string; isInternalNumber?: boolean}) {
3953
+ public cancelInviteByMemberId(invitee: Invitee) {
3876
3954
  return this.members.cancelInviteByMemberId(invitee);
3877
3955
  }
3878
3956
 
@@ -4159,6 +4237,7 @@ export default class Meeting extends StatelessWebexPlugin {
4159
4237
  this.userDisplayHints,
4160
4238
  this.selfUserPolicies
4161
4239
  ),
4240
+ showAutoEndMeetingWarning: MeetingUtil.showAutoEndMeetingWarning(this.userDisplayHints),
4162
4241
  canRaiseHand: MeetingUtil.canUserRaiseHand(this.userDisplayHints),
4163
4242
  canLowerAllHands: MeetingUtil.canUserLowerAllHands(this.userDisplayHints),
4164
4243
  canLowerSomeoneElsesHand: MeetingUtil.canUserLowerSomeoneElsesHand(this.userDisplayHints),
@@ -4171,8 +4250,16 @@ export default class Meeting extends StatelessWebexPlugin {
4171
4250
  isClosedCaptionActive: MeetingUtil.isClosedCaptionActive(this.userDisplayHints),
4172
4251
  canStartManualCaption: MeetingUtil.canStartManualCaption(this.userDisplayHints),
4173
4252
  canStopManualCaption: MeetingUtil.canStopManualCaption(this.userDisplayHints),
4253
+ isLocalRecordingStarted: MeetingUtil.isLocalRecordingStarted(this.userDisplayHints),
4254
+ isLocalRecordingStopped: MeetingUtil.isLocalRecordingStopped(this.userDisplayHints),
4255
+ isLocalRecordingPaused: MeetingUtil.isLocalRecordingPaused(this.userDisplayHints),
4256
+ isLocalStreamingStarted: MeetingUtil.isLocalStreamingStarted(this.userDisplayHints),
4257
+ isLocalStreamingStopped: MeetingUtil.isLocalStreamingStopped(this.userDisplayHints),
4174
4258
  isManualCaptionActive: MeetingUtil.isManualCaptionActive(this.userDisplayHints),
4175
4259
  isSaveTranscriptsEnabled: MeetingUtil.isSaveTranscriptsEnabled(this.userDisplayHints),
4260
+ isSpokenLanguageAutoDetectionEnabled: MeetingUtil.isSpokenLanguageAutoDetectionEnabled(
4261
+ this.userDisplayHints
4262
+ ),
4176
4263
  isWebexAssistantActive: MeetingUtil.isWebexAssistantActive(this.userDisplayHints),
4177
4264
  canViewCaptionPanel: MeetingUtil.canViewCaptionPanel(this.userDisplayHints),
4178
4265
  isRealTimeTranslationEnabled: MeetingUtil.isRealTimeTranslationEnabled(
@@ -4479,13 +4566,11 @@ export default class Meeting extends StatelessWebexPlugin {
4479
4566
  setLocus(
4480
4567
  locus:
4481
4568
  | {
4482
- locus: LocusDTO;
4483
4569
  mediaConnections: Array<any>;
4484
4570
  locusUrl: string;
4485
4571
  locusId: string;
4486
4572
  mediaId: string;
4487
4573
  host: object;
4488
- dataSets: DataSet[];
4489
4574
  }
4490
4575
  | any
4491
4576
  ) {
@@ -4499,7 +4584,7 @@ export default class Meeting extends StatelessWebexPlugin {
4499
4584
  this.selfId = locus.selfId;
4500
4585
  this.mediaId = locus.mediaId;
4501
4586
  this.hostId = mtgLocus.host ? mtgLocus.host.id : this.hostId;
4502
- this.locusInfo.initialSetup(mtgLocus, locus.dataSets);
4587
+ this.locusInfo.initialSetup(mtgLocus);
4503
4588
  }
4504
4589
 
4505
4590
  /**
@@ -5612,21 +5697,6 @@ export default class Meeting extends StatelessWebexPlugin {
5612
5697
  }
5613
5698
  }
5614
5699
 
5615
- /** Handles Locus LLM events
5616
- *
5617
- * @param {LocusLLMEvent} event - The Locus LLM event to process
5618
- * @returns {void}
5619
- */
5620
- private processLocusLLMEvent = (event: LocusLLMEvent): void => {
5621
- if (event.data.eventType === 'locus.state_message') {
5622
- this.locusInfo.parse(this, event.data);
5623
- } else {
5624
- LoggerProxy.logger.warn(
5625
- `Meeting:index#processLocusLLMEvent --> Unknown event type: ${event.data.eventType}`
5626
- );
5627
- }
5628
- };
5629
-
5630
5700
  /**
5631
5701
  * Callback called when a relay event is received from meeting LLM Connection
5632
5702
  * @param {RelayEvent} e Event object coming from LLM Connection
@@ -5945,15 +6015,6 @@ export default class Meeting extends StatelessWebexPlugin {
5945
6015
  this.meetingFiniteStateMachine.fail(error);
5946
6016
  LoggerProxy.logger.error('Meeting:index#join --> Failed', error);
5947
6017
 
5948
- // @ts-ignore
5949
- this.webex.internal.newMetrics.submitClientEvent({
5950
- name: 'client.locus.join.response',
5951
- payload: {
5952
- identifiers: {meetingLookupUrl: this.meetingInfo?.meetingLookupUrl},
5953
- },
5954
- options: {meetingId: this.id, rawError: error},
5955
- });
5956
-
5957
6018
  // TODO: change this to error codes and pre defined dictionary
5958
6019
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.JOIN_FAILURE, {
5959
6020
  correlation_id: this.correlationId,
@@ -6047,8 +6108,6 @@ export default class Meeting extends StatelessWebexPlugin {
6047
6108
  );
6048
6109
  // @ts-ignore - Fix type
6049
6110
  this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
6050
- // @ts-ignore - Fix type
6051
- this.webex.internal.llm.off('event:locus.state_message', this.processLocusLLMEvent);
6052
6111
  }
6053
6112
 
6054
6113
  if (!isJoined) {
@@ -6063,10 +6122,6 @@ export default class Meeting extends StatelessWebexPlugin {
6063
6122
  this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
6064
6123
  // @ts-ignore - Fix type
6065
6124
  this.webex.internal.llm.on('event:relay.event', this.processRelayEvent);
6066
- // @ts-ignore - Fix type
6067
- this.webex.internal.llm.off('event:locus.state_message', this.processLocusLLMEvent);
6068
- // @ts-ignore - Fix type
6069
- this.webex.internal.llm.on('event:locus.state_message', this.processLocusLLMEvent);
6070
6125
  LoggerProxy.logger.info(
6071
6126
  'Meeting:index#updateLLMConnection --> enabled to receive relay events!'
6072
6127
  );
@@ -6109,8 +6164,9 @@ export default class Meeting extends StatelessWebexPlugin {
6109
6164
  */
6110
6165
  private dialInPstn() {
6111
6166
  if (this.isPhoneProvisioned(this.dialInDeviceStatus)) return Promise.resolve(); // prevent multiple dial in devices from being provisioned
6167
+ this.pstnCorrelationId = uuid.v4();
6112
6168
 
6113
- const {correlationId, locusUrl} = this;
6169
+ const {pstnCorrelationId, locusUrl} = this;
6114
6170
 
6115
6171
  if (!this.dialInUrl) this.dialInUrl = `dialin:///${uuid.v4()}`;
6116
6172
 
@@ -6118,7 +6174,7 @@ export default class Meeting extends StatelessWebexPlugin {
6118
6174
  this.meetingRequest
6119
6175
  // @ts-ignore
6120
6176
  .dialIn({
6121
- correlationId,
6177
+ correlationId: pstnCorrelationId,
6122
6178
  dialInUrl: this.dialInUrl,
6123
6179
  locusUrl,
6124
6180
  clientUrl: this.deviceUrl,
@@ -6127,12 +6183,17 @@ export default class Meeting extends StatelessWebexPlugin {
6127
6183
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_DIAL_IN_FAILURE, {
6128
6184
  correlation_id: this.correlationId,
6129
6185
  dial_in_url: this.dialInUrl,
6186
+ dial_in_correlation_id: pstnCorrelationId,
6130
6187
  locus_id: locusUrl.split('/').pop(),
6131
6188
  client_url: this.deviceUrl,
6132
6189
  reason: error.error?.message,
6133
6190
  stack: error.stack,
6134
6191
  });
6135
6192
 
6193
+ if (this.pstnCorrelationId === pstnCorrelationId) {
6194
+ this.pstnCorrelationId = undefined;
6195
+ }
6196
+
6136
6197
  return Promise.reject(error);
6137
6198
  })
6138
6199
  );
@@ -6147,8 +6208,9 @@ export default class Meeting extends StatelessWebexPlugin {
6147
6208
  */
6148
6209
  private dialOutPstn(phoneNumber: string) {
6149
6210
  if (this.isPhoneProvisioned(this.dialOutDeviceStatus)) return Promise.resolve(); // prevent multiple dial out devices from being provisioned
6211
+ this.pstnCorrelationId = uuid.v4();
6150
6212
 
6151
- const {correlationId, locusUrl} = this;
6213
+ const {locusUrl, pstnCorrelationId} = this;
6152
6214
 
6153
6215
  if (!this.dialOutUrl) this.dialOutUrl = `dialout:///${uuid.v4()}`;
6154
6216
 
@@ -6156,7 +6218,7 @@ export default class Meeting extends StatelessWebexPlugin {
6156
6218
  this.meetingRequest
6157
6219
  // @ts-ignore
6158
6220
  .dialOut({
6159
- correlationId,
6221
+ correlationId: pstnCorrelationId,
6160
6222
  dialOutUrl: this.dialOutUrl,
6161
6223
  phoneNumber,
6162
6224
  locusUrl,
@@ -6166,12 +6228,17 @@ export default class Meeting extends StatelessWebexPlugin {
6166
6228
  Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.ADD_DIAL_OUT_FAILURE, {
6167
6229
  correlation_id: this.correlationId,
6168
6230
  dial_out_url: this.dialOutUrl,
6231
+ dial_out_correlation_id: pstnCorrelationId,
6169
6232
  locus_id: locusUrl.split('/').pop(),
6170
6233
  client_url: this.deviceUrl,
6171
6234
  reason: error.error?.message,
6172
6235
  stack: error.stack,
6173
6236
  });
6174
6237
 
6238
+ if (this.pstnCorrelationId === pstnCorrelationId) {
6239
+ this.pstnCorrelationId = undefined;
6240
+ }
6241
+
6175
6242
  return Promise.reject(error);
6176
6243
  })
6177
6244
  );
@@ -6185,6 +6252,8 @@ export default class Meeting extends StatelessWebexPlugin {
6185
6252
  * @returns {Promise}
6186
6253
  */
6187
6254
  public disconnectPhoneAudio() {
6255
+ const correlationToClear = this.pstnCorrelationId;
6256
+
6188
6257
  return Promise.all([
6189
6258
  this.isPhoneProvisioned(this.dialInDeviceStatus)
6190
6259
  ? MeetingUtil.disconnectPhoneAudio(this, this.dialInUrl)
@@ -6192,7 +6261,11 @@ export default class Meeting extends StatelessWebexPlugin {
6192
6261
  this.isPhoneProvisioned(this.dialOutDeviceStatus)
6193
6262
  ? MeetingUtil.disconnectPhoneAudio(this, this.dialOutUrl)
6194
6263
  : Promise.resolve(),
6195
- ]);
6264
+ ]).then(() => {
6265
+ if (this.pstnCorrelationId === correlationToClear) {
6266
+ this.pstnCorrelationId = undefined;
6267
+ }
6268
+ });
6196
6269
  }
6197
6270
 
6198
6271
  /**
@@ -6769,6 +6842,10 @@ export default class Meeting extends StatelessWebexPlugin {
6769
6842
  // @ts-ignore
6770
6843
  this.webex.internal.newMetrics.submitClientEvent({
6771
6844
  name: 'client.ice.start',
6845
+ payload: {
6846
+ // @ts-ignore
6847
+ labels: MeetingUtil.getCaEventLabelsForIpVersion(this.webex),
6848
+ },
6772
6849
  options: {
6773
6850
  meetingId: this.id,
6774
6851
  },
@@ -6938,10 +7015,10 @@ export default class Meeting extends StatelessWebexPlugin {
6938
7015
  }
6939
7016
  }
6940
7017
 
6941
- // Count members that are in the meeting.
7018
+ // Count members that are in the meeting or in the lobby.
6942
7019
  const {members} = this.getMembers().membersCollection;
6943
7020
  event.data.intervalMetadata.meetingUserCount = Object.values(members).filter(
6944
- (member: Member) => member.isInMeeting
7021
+ (member: Member) => member.isInMeeting || member.isInLobby
6945
7022
  ).length;
6946
7023
 
6947
7024
  // @ts-ignore
@@ -7300,10 +7377,12 @@ export default class Meeting extends StatelessWebexPlugin {
7300
7377
  if (this.config.stats.enableStatsAnalyzer) {
7301
7378
  // @ts-ignore - config coming from registerPlugin
7302
7379
  this.networkQualityMonitor = new NetworkQualityMonitor(this.config.stats);
7380
+ this.statsMonitor = new StatsMonitor();
7303
7381
  this.statsAnalyzer = new StatsAnalyzer({
7304
7382
  // @ts-ignore - config coming from registerPlugin
7305
7383
  config: this.config.stats,
7306
7384
  networkQualityMonitor: this.networkQualityMonitor,
7385
+ statsMonitor: this.statsMonitor,
7307
7386
  isMultistream: this.isMultistream,
7308
7387
  });
7309
7388
  this.shareCAEventSentStatus = {
@@ -7317,6 +7396,33 @@ export default class Meeting extends StatelessWebexPlugin {
7317
7396
  NetworkQualityEventNames.NETWORK_QUALITY,
7318
7397
  this.sendNetworkQualityEvent.bind(this)
7319
7398
  );
7399
+
7400
+ this.statsMonitor.on(StatsMonitorEventNames.INBOUND_AUDIO_ISSUE, (data) => {
7401
+ // Before forwarding any inbound audio issues to the app, make sure that we have at least one other
7402
+ // participant in the meeting with unmuted audio.
7403
+ // We don't check this.mediaProperties.mediaDirection here, because that's already handled in statsAnalyzer,
7404
+ // so we won't get this event if we are not setup to receive any audio
7405
+ const atLeastOneUnmutedOtherMember = Object.values(
7406
+ this.members.membersCollection.getAll()
7407
+ ).find((member) => {
7408
+ return !member.isSelf && !member.isPairedWithSelf && !member.isAudioMuted;
7409
+ });
7410
+
7411
+ if (atLeastOneUnmutedOtherMember) {
7412
+ this.mediaProperties.sendMediaIssueMetric(
7413
+ 'inbound_audio',
7414
+ data.issueSubType,
7415
+ this.correlationId
7416
+ );
7417
+
7418
+ Trigger.trigger(
7419
+ this,
7420
+ {file: 'meeting/index', function: 'createStatsAnalyzer'},
7421
+ EVENT_TRIGGERS.MEDIA_INBOUND_AUDIO_ISSUE_DETECTED,
7422
+ data
7423
+ );
7424
+ }
7425
+ });
7320
7426
  }
7321
7427
  }
7322
7428
 
@@ -7347,7 +7453,7 @@ export default class Meeting extends StatelessWebexPlugin {
7347
7453
  } seconds`
7348
7454
  );
7349
7455
 
7350
- const error = new Error('Timed out waiting for REMOTE SDP ANSWER');
7456
+ const error = new SdpResponseTimeoutError();
7351
7457
 
7352
7458
  // @ts-ignore
7353
7459
  this.webex.internal.newMetrics.submitClientEvent({
@@ -7615,6 +7721,10 @@ export default class Meeting extends StatelessWebexPlugin {
7615
7721
  }
7616
7722
 
7617
7723
  this.statsAnalyzer = null;
7724
+ this.networkQualityMonitor?.removeAllListeners();
7725
+ this.networkQualityMonitor = null;
7726
+ this.statsMonitor?.removeAllListeners();
7727
+ this.statsMonitor = null;
7618
7728
 
7619
7729
  // when media fails, we want to upload a webrtc dump to see whats going on
7620
7730
  // this function is async, but returns once the stats have been gathered
@@ -7638,6 +7748,10 @@ export default class Meeting extends StatelessWebexPlugin {
7638
7748
  await this.statsAnalyzer.stopAnalyzer();
7639
7749
  }
7640
7750
  this.statsAnalyzer = null;
7751
+ this.networkQualityMonitor?.removeAllListeners();
7752
+ this.networkQualityMonitor = null;
7753
+ this.statsMonitor?.removeAllListeners();
7754
+ this.statsMonitor = null;
7641
7755
 
7642
7756
  this.isMultistream = false;
7643
7757
 
@@ -7809,6 +7923,9 @@ export default class Meeting extends StatelessWebexPlugin {
7809
7923
 
7810
7924
  this.allowMediaInLobby = options?.allowMediaInLobby;
7811
7925
 
7926
+ // @ts-ignore
7927
+ const ipver = MeetingUtil.getIpVersion(this.webex); // used just for metrics
7928
+
7812
7929
  // If the user is unjoined or guest waiting in lobby dont allow the user to addMedia
7813
7930
  // @ts-ignore - isUserUnadmitted coming from SelfUtil
7814
7931
  if (this.isUserUnadmitted && !this.wirelessShare && !this.allowMediaInLobby) {
@@ -7907,6 +8024,7 @@ export default class Meeting extends StatelessWebexPlugin {
7907
8024
  locus_id: this.locusUrl.split('/').pop(),
7908
8025
  connectionType,
7909
8026
  ipVersion,
8027
+ ipver,
7910
8028
  selectedCandidatePairChanges,
7911
8029
  numTransports,
7912
8030
  isMultistream: this.isMultistream,
@@ -7975,6 +8093,7 @@ export default class Meeting extends StatelessWebexPlugin {
7975
8093
  ...reachabilityMetrics,
7976
8094
  ...iceCandidateErrors,
7977
8095
  iceCandidatesCount: this.iceCandidatesCount,
8096
+ ipver,
7978
8097
  });
7979
8098
 
7980
8099
  await this.cleanUpOnAddMediaFailure();
@@ -8414,6 +8533,10 @@ export default class Meeting extends StatelessWebexPlugin {
8414
8533
  }
8415
8534
 
8416
8535
  if (whiteboard) {
8536
+ // @ts-ignore
8537
+ this.webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp({
8538
+ key: 'internal.client.share.initiated',
8539
+ });
8417
8540
  // @ts-ignore
8418
8541
  this.webex.internal.newMetrics.submitClientEvent({
8419
8542
  name: 'client.share.initiated',
@@ -8473,11 +8596,17 @@ export default class Meeting extends StatelessWebexPlugin {
8473
8596
  const whiteboard = this.locusInfo.mediaShares.find((element) => element.name === 'whiteboard');
8474
8597
 
8475
8598
  if (whiteboard) {
8599
+ // @ts-ignore
8600
+ this.webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp({
8601
+ key: 'internal.client.share.stopped',
8602
+ });
8476
8603
  // @ts-ignore
8477
8604
  this.webex.internal.newMetrics.submitClientEvent({
8478
8605
  name: 'client.share.stopped',
8479
8606
  payload: {
8480
8607
  mediaType: 'whiteboard',
8608
+ // @ts-ignore
8609
+ shareDuration: this.webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration(),
8481
8610
  },
8482
8611
  options: {
8483
8612
  meetingId: this.id,
@@ -8635,12 +8764,18 @@ export default class Meeting extends StatelessWebexPlugin {
8635
8764
  }
8636
8765
  this.screenShareFloorState = ScreenShareFloorStatus.RELEASED;
8637
8766
  if (content) {
8767
+ // @ts-ignore
8768
+ this.webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp({
8769
+ key: 'internal.client.share.stopped',
8770
+ });
8638
8771
  // @ts-ignore
8639
8772
  this.webex.internal.newMetrics.submitClientEvent({
8640
8773
  name: 'client.share.stopped',
8641
8774
  payload: {
8642
8775
  mediaType: 'share',
8643
8776
  shareInstanceId: this.localShareInstanceId,
8777
+ // @ts-ignore
8778
+ shareDuration: this.webex.internal.newMetrics.callDiagnosticLatencies.getShareDuration(),
8644
8779
  },
8645
8780
  options: {meetingId: this.id},
8646
8781
  });
@@ -9238,8 +9373,6 @@ export default class Meeting extends StatelessWebexPlugin {
9238
9373
 
9239
9374
  // @ts-ignore - fix types
9240
9375
  this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
9241
- // @ts-ignore - Fix type
9242
- this.webex.internal.llm.off('event:locus.state_message', this.processLocusLLMEvent);
9243
9376
  };
9244
9377
 
9245
9378
  /**
@@ -9337,6 +9470,36 @@ export default class Meeting extends StatelessWebexPlugin {
9337
9470
  return Promise.reject(new Error('Error sending reaction, service url not found.'));
9338
9471
  }
9339
9472
 
9473
+ /**
9474
+ * Extend the current meeting duration.
9475
+ *
9476
+ * @param {number} extensionMinutes - how many minutes to extend
9477
+ * @returns {Promise}
9478
+ * @public
9479
+ * @memberof Meeting
9480
+ */
9481
+ public extendMeeting({
9482
+ meetingPolicyUrl,
9483
+ meetingInstanceId,
9484
+ participantId,
9485
+ extensionMinutes = 30,
9486
+ }) {
9487
+ if (!meetingInstanceId || !participantId) {
9488
+ return Promise.reject(new Error('Missing meetingInstanceId or participantId'));
9489
+ }
9490
+
9491
+ if (!meetingPolicyUrl) {
9492
+ return Promise.reject(new Error('Missing meetingPolicyUrl'));
9493
+ }
9494
+
9495
+ return this.meetingRequest.extendMeeting({
9496
+ meetingInstanceId,
9497
+ participantId,
9498
+ extensionMinutes,
9499
+ meetingPolicyUrl,
9500
+ });
9501
+ }
9502
+
9340
9503
  /**
9341
9504
  * Method to enable or disable reactions inside the meeting.
9342
9505
  *
@@ -9619,6 +9782,11 @@ export default class Meeting extends StatelessWebexPlugin {
9619
9782
  this.shareCAEventSentStatus.transmitStart = false;
9620
9783
  this.shareCAEventSentStatus.transmitStop = false;
9621
9784
 
9785
+ // @ts-ignore
9786
+ this.webex.internal.newMetrics.callDiagnosticLatencies.saveTimestamp({
9787
+ key: 'internal.client.share.initiated',
9788
+ });
9789
+
9622
9790
  // @ts-ignore
9623
9791
  this.webex.internal.newMetrics.submitClientEvent({
9624
9792
  name: 'client.share.initiated',
@@ -9861,4 +10029,47 @@ export default class Meeting extends StatelessWebexPlugin {
9861
10029
 
9862
10030
  return this.meetingRequest.synchronizeStage(this.locusUrl, videoLayout);
9863
10031
  }
10032
+
10033
+ /**
10034
+ * Notifies the host with the given meeting UUID and display names.
10035
+ *
10036
+ * @param {string} meetingUuid - The UUID of the meeting.
10037
+ * @param {string[]} displayName - An array of display names to notify the host with.
10038
+ * @returns {Promise<any>} The result of the notifyHost request.
10039
+ */
10040
+ notifyHost(meetingUuid: string, displayName: string[]) {
10041
+ return this.meetingRequest.notifyHost(
10042
+ this.meetingInfo.siteFullUrl,
10043
+ this.locusId,
10044
+ meetingUuid,
10045
+ displayName
10046
+ );
10047
+ }
10048
+
10049
+ /**
10050
+ * Call out a SIP participant to a meeting
10051
+ * @param {string} address - The SIP address or phone number
10052
+ * @param {string} displayName - The display name for the participant
10053
+ * @param {string} [correlationId] - Optional correlation ID
10054
+ * @returns {Promise} Promise that resolves when the call-out is initiated
10055
+ */
10056
+ sipCallOut(address: string, displayName: string) {
10057
+ return this.meetingRequest.sipCallOut(
10058
+ this.meetingInfo.meetingId,
10059
+ this.meetingInfo.meetingId,
10060
+ address,
10061
+ displayName
10062
+ );
10063
+ }
10064
+
10065
+ /**
10066
+ * Cancel an ongoing SIP call-out
10067
+ * @param {string} participantId - The participant ID to cancel
10068
+ * @returns {Promise} Promise that resolves when the call-out is cancelled
10069
+ * @public
10070
+ * @memberof Meetings
10071
+ */
10072
+ cancelSipCallOut(participantId: string) {
10073
+ return this.meetingRequest.cancelSipCallOut(participantId);
10074
+ }
9864
10075
  }