@webex/plugin-meetings 3.10.0 → 3.11.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.
Files changed (283) hide show
  1. package/dist/annotation/annotation.types.js.map +1 -1
  2. package/dist/annotation/constants.js.map +1 -1
  3. package/dist/annotation/index.js +19 -22
  4. package/dist/annotation/index.js.map +1 -1
  5. package/dist/breakouts/breakout.js +6 -6
  6. package/dist/breakouts/breakout.js.map +1 -1
  7. package/dist/breakouts/collection.js.map +1 -1
  8. package/dist/breakouts/edit-lock-error.js +9 -11
  9. package/dist/breakouts/edit-lock-error.js.map +1 -1
  10. package/dist/breakouts/events.js.map +1 -1
  11. package/dist/breakouts/index.js +126 -127
  12. package/dist/breakouts/index.js.map +1 -1
  13. package/dist/breakouts/request.js +6 -8
  14. package/dist/breakouts/request.js.map +1 -1
  15. package/dist/breakouts/utils.js.map +1 -1
  16. package/dist/common/browser-detection.js.map +1 -1
  17. package/dist/common/collection.js +1 -2
  18. package/dist/common/collection.js.map +1 -1
  19. package/dist/common/config.js.map +1 -1
  20. package/dist/common/errors/captcha-error.js +9 -11
  21. package/dist/common/errors/captcha-error.js.map +1 -1
  22. package/dist/common/errors/intent-to-join.js +10 -12
  23. package/dist/common/errors/intent-to-join.js.map +1 -1
  24. package/dist/common/errors/join-forbidden-error.js +10 -12
  25. package/dist/common/errors/join-forbidden-error.js.map +1 -1
  26. package/dist/common/errors/join-meeting.js +10 -12
  27. package/dist/common/errors/join-meeting.js.map +1 -1
  28. package/dist/common/errors/join-webinar-error.js +9 -11
  29. package/dist/common/errors/join-webinar-error.js.map +1 -1
  30. package/dist/common/errors/media.js +9 -11
  31. package/dist/common/errors/media.js.map +1 -1
  32. package/dist/common/errors/multistream-not-supported-error.js +9 -11
  33. package/dist/common/errors/multistream-not-supported-error.js.map +1 -1
  34. package/dist/common/errors/no-meeting-info.js +9 -11
  35. package/dist/common/errors/no-meeting-info.js.map +1 -1
  36. package/dist/common/errors/parameter.js +11 -14
  37. package/dist/common/errors/parameter.js.map +1 -1
  38. package/dist/common/errors/password-error.js +9 -11
  39. package/dist/common/errors/password-error.js.map +1 -1
  40. package/dist/common/errors/permission.js +9 -11
  41. package/dist/common/errors/permission.js.map +1 -1
  42. package/dist/common/errors/reclaim-host-role-errors.js +32 -38
  43. package/dist/common/errors/reclaim-host-role-errors.js.map +1 -1
  44. package/dist/common/errors/reconnection-not-started.js +5 -6
  45. package/dist/common/errors/reconnection-not-started.js.map +1 -1
  46. package/dist/common/errors/reconnection.js +9 -11
  47. package/dist/common/errors/reconnection.js.map +1 -1
  48. package/dist/common/errors/stats.js +9 -11
  49. package/dist/common/errors/stats.js.map +1 -1
  50. package/dist/common/errors/webex-errors.js +38 -27
  51. package/dist/common/errors/webex-errors.js.map +1 -1
  52. package/dist/common/errors/webex-meetings-error.js +9 -12
  53. package/dist/common/errors/webex-meetings-error.js.map +1 -1
  54. package/dist/common/events/events-scope.js +9 -10
  55. package/dist/common/events/events-scope.js.map +1 -1
  56. package/dist/common/events/events.js +9 -10
  57. package/dist/common/events/events.js.map +1 -1
  58. package/dist/common/events/trigger-proxy.js.map +1 -1
  59. package/dist/common/events/util.js.map +1 -1
  60. package/dist/common/logs/logger-config.js.map +1 -1
  61. package/dist/common/logs/logger-proxy.js.map +1 -1
  62. package/dist/common/logs/request.js +17 -17
  63. package/dist/common/logs/request.js.map +1 -1
  64. package/dist/common/queue.js +1 -2
  65. package/dist/common/queue.js.map +1 -1
  66. package/dist/config.js +2 -2
  67. package/dist/config.js.map +1 -1
  68. package/dist/constants.js +13 -8
  69. package/dist/constants.js.map +1 -1
  70. package/dist/controls-options-manager/constants.js.map +1 -1
  71. package/dist/controls-options-manager/enums.js.map +1 -1
  72. package/dist/controls-options-manager/index.js +1 -2
  73. package/dist/controls-options-manager/index.js.map +1 -1
  74. package/dist/controls-options-manager/types.js.map +1 -1
  75. package/dist/controls-options-manager/util.js +1 -2
  76. package/dist/controls-options-manager/util.js.map +1 -1
  77. package/dist/hashTree/constants.js +20 -0
  78. package/dist/hashTree/constants.js.map +1 -0
  79. package/dist/hashTree/hashTree.js +515 -0
  80. package/dist/hashTree/hashTree.js.map +1 -0
  81. package/dist/hashTree/hashTreeParser.js +1250 -0
  82. package/dist/hashTree/hashTreeParser.js.map +1 -0
  83. package/dist/hashTree/types.js +23 -0
  84. package/dist/hashTree/types.js.map +1 -0
  85. package/dist/hashTree/utils.js +59 -0
  86. package/dist/hashTree/utils.js.map +1 -0
  87. package/dist/index.js +8 -2
  88. package/dist/index.js.map +1 -1
  89. package/dist/interceptors/index.js.map +1 -1
  90. package/dist/interceptors/locusRetry.js +6 -8
  91. package/dist/interceptors/locusRetry.js.map +1 -1
  92. package/dist/interceptors/locusRouteToken.js +33 -13
  93. package/dist/interceptors/locusRouteToken.js.map +1 -1
  94. package/dist/interpretation/collection.js.map +1 -1
  95. package/dist/interpretation/index.js +1 -2
  96. package/dist/interpretation/index.js.map +1 -1
  97. package/dist/interpretation/siLanguage.js +1 -1
  98. package/dist/interpretation/siLanguage.js.map +1 -1
  99. package/dist/locus-info/controlsUtils.js +5 -3
  100. package/dist/locus-info/controlsUtils.js.map +1 -1
  101. package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
  102. package/dist/locus-info/fullState.js.map +1 -1
  103. package/dist/locus-info/hostUtils.js.map +1 -1
  104. package/dist/locus-info/index.js +619 -177
  105. package/dist/locus-info/index.js.map +1 -1
  106. package/dist/locus-info/infoUtils.js.map +1 -1
  107. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  108. package/dist/locus-info/parser.js +3 -4
  109. package/dist/locus-info/parser.js.map +1 -1
  110. package/dist/locus-info/selfUtils.js.map +1 -1
  111. package/dist/locus-info/types.js +7 -0
  112. package/dist/locus-info/types.js.map +1 -0
  113. package/dist/media/MediaConnectionAwaiter.js +1 -2
  114. package/dist/media/MediaConnectionAwaiter.js.map +1 -1
  115. package/dist/media/index.js +5 -2
  116. package/dist/media/index.js.map +1 -1
  117. package/dist/media/properties.js +15 -17
  118. package/dist/media/properties.js.map +1 -1
  119. package/dist/media/util.js.map +1 -1
  120. package/dist/meeting/brbState.js +8 -9
  121. package/dist/meeting/brbState.js.map +1 -1
  122. package/dist/meeting/connectionStateHandler.js +10 -13
  123. package/dist/meeting/connectionStateHandler.js.map +1 -1
  124. package/dist/meeting/in-meeting-actions.js.map +1 -1
  125. package/dist/meeting/index.js +1632 -1535
  126. package/dist/meeting/index.js.map +1 -1
  127. package/dist/meeting/locusMediaRequest.js +13 -17
  128. package/dist/meeting/locusMediaRequest.js.map +1 -1
  129. package/dist/meeting/muteState.js +11 -12
  130. package/dist/meeting/muteState.js.map +1 -1
  131. package/dist/meeting/request.js +101 -104
  132. package/dist/meeting/request.js.map +1 -1
  133. package/dist/meeting/request.type.js.map +1 -1
  134. package/dist/meeting/state.js.map +1 -1
  135. package/dist/meeting/type.js.map +1 -1
  136. package/dist/meeting/util.js +24 -23
  137. package/dist/meeting/util.js.map +1 -1
  138. package/dist/meeting/voicea-meeting.js +3 -3
  139. package/dist/meeting/voicea-meeting.js.map +1 -1
  140. package/dist/meeting-info/collection.js +7 -10
  141. package/dist/meeting-info/collection.js.map +1 -1
  142. package/dist/meeting-info/index.js +1 -2
  143. package/dist/meeting-info/index.js.map +1 -1
  144. package/dist/meeting-info/meeting-info-v2.js +135 -146
  145. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  146. package/dist/meeting-info/request.js +1 -2
  147. package/dist/meeting-info/request.js.map +1 -1
  148. package/dist/meeting-info/util.js +36 -37
  149. package/dist/meeting-info/util.js.map +1 -1
  150. package/dist/meeting-info/utilv2.js +30 -31
  151. package/dist/meeting-info/utilv2.js.map +1 -1
  152. package/dist/meetings/collection.js +6 -8
  153. package/dist/meetings/collection.js.map +1 -1
  154. package/dist/meetings/index.js +200 -148
  155. package/dist/meetings/index.js.map +1 -1
  156. package/dist/meetings/meetings.types.js.map +1 -1
  157. package/dist/meetings/request.js +6 -8
  158. package/dist/meetings/request.js.map +1 -1
  159. package/dist/meetings/util.js +36 -30
  160. package/dist/meetings/util.js.map +1 -1
  161. package/dist/member/index.js +1 -2
  162. package/dist/member/index.js.map +1 -1
  163. package/dist/member/types.js +6 -3
  164. package/dist/member/types.js.map +1 -1
  165. package/dist/member/util.js.map +1 -1
  166. package/dist/members/collection.js +1 -2
  167. package/dist/members/collection.js.map +1 -1
  168. package/dist/members/index.js +18 -21
  169. package/dist/members/index.js.map +1 -1
  170. package/dist/members/request.js +8 -11
  171. package/dist/members/request.js.map +1 -1
  172. package/dist/members/types.js.map +1 -1
  173. package/dist/members/util.js.map +1 -1
  174. package/dist/metrics/constants.js +4 -1
  175. package/dist/metrics/constants.js.map +1 -1
  176. package/dist/metrics/index.js +3 -4
  177. package/dist/metrics/index.js.map +1 -1
  178. package/dist/multistream/mediaRequestManager.js +1 -2
  179. package/dist/multistream/mediaRequestManager.js.map +1 -1
  180. package/dist/multistream/receiveSlot.js +34 -45
  181. package/dist/multistream/receiveSlot.js.map +1 -1
  182. package/dist/multistream/receiveSlotManager.js +8 -9
  183. package/dist/multistream/receiveSlotManager.js.map +1 -1
  184. package/dist/multistream/remoteMedia.js +12 -15
  185. package/dist/multistream/remoteMedia.js.map +1 -1
  186. package/dist/multistream/remoteMediaGroup.js +1 -2
  187. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  188. package/dist/multistream/remoteMediaManager.js +122 -123
  189. package/dist/multistream/remoteMediaManager.js.map +1 -1
  190. package/dist/multistream/sendSlotManager.js +29 -30
  191. package/dist/multistream/sendSlotManager.js.map +1 -1
  192. package/dist/personal-meeting-room/index.js +16 -19
  193. package/dist/personal-meeting-room/index.js.map +1 -1
  194. package/dist/personal-meeting-room/request.js +7 -10
  195. package/dist/personal-meeting-room/request.js.map +1 -1
  196. package/dist/personal-meeting-room/util.js.map +1 -1
  197. package/dist/reachability/clusterReachability.js +188 -352
  198. package/dist/reachability/clusterReachability.js.map +1 -1
  199. package/dist/reachability/index.js +206 -206
  200. package/dist/reachability/index.js.map +1 -1
  201. package/dist/reachability/reachability.types.js +14 -1
  202. package/dist/reachability/reachability.types.js.map +1 -1
  203. package/dist/reachability/reachabilityPeerConnection.js +445 -0
  204. package/dist/reachability/reachabilityPeerConnection.js.map +1 -0
  205. package/dist/reachability/request.js.map +1 -1
  206. package/dist/reachability/util.js.map +1 -1
  207. package/dist/reactions/constants.js.map +1 -1
  208. package/dist/reactions/reactions.js.map +1 -1
  209. package/dist/reactions/reactions.type.js.map +1 -1
  210. package/dist/reconnection-manager/index.js +178 -176
  211. package/dist/reconnection-manager/index.js.map +1 -1
  212. package/dist/recording-controller/enums.js.map +1 -1
  213. package/dist/recording-controller/index.js +1 -2
  214. package/dist/recording-controller/index.js.map +1 -1
  215. package/dist/recording-controller/util.js.map +1 -1
  216. package/dist/roap/index.js +12 -15
  217. package/dist/roap/index.js.map +1 -1
  218. package/dist/roap/request.js +24 -26
  219. package/dist/roap/request.js.map +1 -1
  220. package/dist/roap/turnDiscovery.js +75 -76
  221. package/dist/roap/turnDiscovery.js.map +1 -1
  222. package/dist/roap/types.js.map +1 -1
  223. package/dist/transcription/index.js +4 -5
  224. package/dist/transcription/index.js.map +1 -1
  225. package/dist/types/common/errors/webex-errors.d.ts +12 -0
  226. package/dist/types/config.d.ts +1 -0
  227. package/dist/types/constants.d.ts +27 -21
  228. package/dist/types/hashTree/constants.d.ts +8 -0
  229. package/dist/types/hashTree/hashTree.d.ts +129 -0
  230. package/dist/types/hashTree/hashTreeParser.d.ts +250 -0
  231. package/dist/types/hashTree/types.d.ts +33 -0
  232. package/dist/types/hashTree/utils.d.ts +16 -0
  233. package/dist/types/index.d.ts +2 -1
  234. package/dist/types/interceptors/locusRouteToken.d.ts +2 -0
  235. package/dist/types/locus-info/index.d.ts +98 -80
  236. package/dist/types/locus-info/types.d.ts +54 -0
  237. package/dist/types/meeting/index.d.ts +35 -9
  238. package/dist/types/meetings/index.d.ts +9 -2
  239. package/dist/types/metrics/constants.d.ts +3 -0
  240. package/dist/types/reachability/clusterReachability.d.ts +33 -84
  241. package/dist/types/reachability/reachability.types.d.ts +12 -1
  242. package/dist/types/reachability/reachabilityPeerConnection.d.ts +111 -0
  243. package/dist/webinar/collection.js +1 -2
  244. package/dist/webinar/collection.js.map +1 -1
  245. package/dist/webinar/index.js +148 -158
  246. package/dist/webinar/index.js.map +1 -1
  247. package/package.json +24 -23
  248. package/src/common/errors/webex-errors.ts +19 -0
  249. package/src/config.ts +1 -0
  250. package/src/constants.ts +15 -2
  251. package/src/hashTree/constants.ts +9 -0
  252. package/src/hashTree/hashTree.ts +463 -0
  253. package/src/hashTree/hashTreeParser.ts +1143 -0
  254. package/src/hashTree/types.ts +39 -0
  255. package/src/hashTree/utils.ts +53 -0
  256. package/src/index.ts +2 -0
  257. package/src/interceptors/locusRouteToken.ts +22 -5
  258. package/src/locus-info/controlsUtils.ts +6 -0
  259. package/src/locus-info/index.ts +641 -164
  260. package/src/locus-info/types.ts +53 -0
  261. package/src/media/index.ts +6 -0
  262. package/src/meeting/index.ts +137 -28
  263. package/src/meeting/util.ts +1 -0
  264. package/src/meetings/index.ts +119 -59
  265. package/src/meetings/util.ts +10 -9
  266. package/src/metrics/constants.ts +3 -0
  267. package/src/reachability/clusterReachability.ts +159 -330
  268. package/src/reachability/index.ts +6 -1
  269. package/src/reachability/reachability.types.ts +15 -1
  270. package/src/reachability/reachabilityPeerConnection.ts +418 -0
  271. package/test/unit/spec/hashTree/hashTree.ts +655 -0
  272. package/test/unit/spec/hashTree/hashTreeParser.ts +1524 -0
  273. package/test/unit/spec/hashTree/utils.ts +140 -0
  274. package/test/unit/spec/interceptors/locusRouteToken.ts +44 -0
  275. package/test/unit/spec/locus-info/controlsUtils.js +27 -1
  276. package/test/unit/spec/locus-info/index.js +879 -16
  277. package/test/unit/spec/media/index.ts +140 -9
  278. package/test/unit/spec/meeting/index.js +299 -94
  279. package/test/unit/spec/meeting/utils.js +78 -1
  280. package/test/unit/spec/meetings/index.js +263 -29
  281. package/test/unit/spec/meetings/utils.js +51 -1
  282. package/test/unit/spec/reachability/clusterReachability.ts +404 -137
  283. package/test/unit/spec/reachability/index.ts +3 -3
@@ -0,0 +1,53 @@
1
+ import {HtMeta} from '../hashTree/types';
2
+
3
+ export type LocusFullState = {
4
+ active: boolean;
5
+ count: number;
6
+ lastActive: string;
7
+ locked: boolean;
8
+ sessionId: string;
9
+ seessionIds: string[];
10
+ startTime: number;
11
+ state: string;
12
+ type: string;
13
+ };
14
+
15
+ export type Links = {
16
+ services: Record<'breakout' | 'record', {url: string}>; // there exist also other services, but these are the ones we currently use
17
+ resources: Record<'webcastInstance' | 'visibleDataSets', {url: string}>; // there exist also other resources, but these are the ones we currently use
18
+ };
19
+
20
+ export type LocusDTO = {
21
+ controls?: any;
22
+ fullState?: LocusFullState;
23
+ host?: {
24
+ id: string;
25
+ incomingCallProtocols: any[];
26
+ isExternal: boolean;
27
+ name: string;
28
+ orgId: string;
29
+ };
30
+ htMeta?: HtMeta;
31
+ info?: any;
32
+ jsSdkMeta?: {
33
+ removedParticipantIds: string[]; // list of ids of participants that are removed in the last update
34
+ };
35
+ links?: Links;
36
+ mediaShares?: any[];
37
+ meetings?: any[];
38
+ participants: any[];
39
+ replaces?: any[];
40
+ self?: any;
41
+ sequence?: {
42
+ dirtyParticipants: number;
43
+ entries: number[];
44
+ rangeEnd: number;
45
+ rangeStart: number;
46
+ sequenceHash: number;
47
+ sessionToken: string;
48
+ since: string;
49
+ totalParticipants: number;
50
+ };
51
+ syncUrl?: string;
52
+ url?: string;
53
+ };
@@ -194,6 +194,12 @@ Media.createMediaConnection = (
194
194
  config.stopIceGatheringAfterFirstRelayCandidate = stopIceGatheringAfterFirstRelayCandidate;
195
195
  }
196
196
 
197
+ if (BrowserInfo.isEdge() || BrowserInfo.isChrome()) {
198
+ // we need this for getting inbound audio metadata
199
+ // but the audioLevel that we use is only available on Chromium based browsers
200
+ config.enableInboundAudioLevelMonitoring = true;
201
+ }
202
+
197
203
  return new MultistreamRoapMediaConnection(
198
204
  config,
199
205
  meetingId,
@@ -1,5 +1,5 @@
1
1
  import uuid from 'uuid';
2
- import {cloneDeep, isEqual, isEmpty} from 'lodash';
2
+ import {cloneDeep, isEqual, isEmpty, merge} from 'lodash';
3
3
  import jwtDecode from 'jwt-decode';
4
4
  // @ts-ignore - Fix this
5
5
  import {StatelessWebexPlugin} from '@webex/webex-core';
@@ -30,6 +30,7 @@ import {
30
30
  NetworkQualityMonitor,
31
31
  StatsMonitor,
32
32
  StatsMonitorEventNames,
33
+ InboundAudioIssueSubTypes,
33
34
  } from '@webex/internal-media-core';
34
35
 
35
36
  import {
@@ -49,6 +50,11 @@ import {
49
50
  type MeetingTranscriptPayload,
50
51
  } from '@webex/internal-plugin-voicea';
51
52
 
53
+ import {
54
+ getBrowserMediaErrorCode,
55
+ isBrowserMediaError,
56
+ isBrowserMediaErrorName,
57
+ } from '@webex/internal-plugin-metrics/src/call-diagnostic/call-diagnostic-metrics.util';
52
58
  import {processNewCaptions} from './voicea-meeting';
53
59
 
54
60
  import {
@@ -57,6 +63,7 @@ import {
57
63
  NoMediaEstablishedYetError,
58
64
  UserNotJoinedError,
59
65
  AddMediaFailed,
66
+ SdpResponseTimeoutError,
60
67
  } from '../common/errors/webex-errors';
61
68
 
62
69
  import LoggerProxy from '../common/logs/logger-proxy';
@@ -68,7 +75,7 @@ import Media, {type BundlePolicy} from '../media';
68
75
  import MediaProperties from '../media/properties';
69
76
  import MeetingStateMachine from './state';
70
77
  import {createMuteState} from './muteState';
71
- import LocusInfo from '../locus-info';
78
+ import LocusInfo, {LocusLLMEvent} from '../locus-info';
72
79
  import Metrics from '../metrics';
73
80
  import ReconnectionManager from '../reconnection-manager';
74
81
  import ReconnectionNotStartedError from '../common/errors/reconnection-not-started';
@@ -124,6 +131,8 @@ import {
124
131
  JOIN_BEFORE_HOST,
125
132
  REGISTRATION_ID_STATUS,
126
133
  STAGE_MANAGER_TYPE,
134
+ LOCUSEVENT,
135
+ LOCUS_LLM_EVENT,
127
136
  } from '../constants';
128
137
  import BEHAVIORAL_METRICS from '../metrics/constants';
129
138
  import ParameterError from '../common/errors/parameter';
@@ -169,10 +178,14 @@ import JoinForbiddenError from '../common/errors/join-forbidden-error';
169
178
  import {ReachabilityMetrics} from '../reachability/reachability.types';
170
179
  import {SetStageOptions, SetStageVideoLayout, UnsetStageVideoLayout} from './request.type';
171
180
  import {Invitee} from './type';
181
+ import {DataSet} from '../hashTree/hashTreeParser';
182
+ import {LocusDTO} from '../locus-info/types';
172
183
 
173
184
  // default callback so we don't call an undefined function, but in practice it should never be used
174
185
  const DEFAULT_ICE_PHASE_CALLBACK = () => 'JOIN_MEETING_FINAL';
175
186
 
187
+ const LLM_HEALTHCHECK_TIMER_MS = 3 * 60 * 1000;
188
+
176
189
  const logRequest = (request: any, {logText = ''}) => {
177
190
  LoggerProxy.logger.info(`${logText} - sending request`);
178
191
 
@@ -745,6 +758,7 @@ export default class Meeting extends StatelessWebexPlugin {
745
758
  private uploadLogsTimer?: ReturnType<typeof setTimeout>;
746
759
  private logUploadIntervalIndex: number;
747
760
  private mediaServerIp: string;
761
+ private llmHealthCheckTimer?: ReturnType<typeof setTimeout>;
748
762
 
749
763
  /**
750
764
  * @param {Object} attrs
@@ -2837,6 +2851,13 @@ export default class Meeting extends StatelessWebexPlugin {
2837
2851
  );
2838
2852
  });
2839
2853
 
2854
+ this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_MEETING_HESIOD_LLM_ID_UPDATED, ({hesiodLlmId}) => {
2855
+ if (hesiodLlmId) {
2856
+ // @ts-ignore
2857
+ this.webex.internal.voicea.onCaptionServiceIdUpdate(hesiodLlmId);
2858
+ }
2859
+ });
2860
+
2840
2861
  this.locusInfo.on(LOCUSINFO.EVENTS.CONTROLS_MEETING_BREAKOUT_UPDATED, ({breakout}) => {
2841
2862
  this.breakouts.updateBreakout(breakout);
2842
2863
  Trigger.trigger(
@@ -4549,40 +4570,45 @@ export default class Meeting extends StatelessWebexPlugin {
4549
4570
  }
4550
4571
 
4551
4572
  /**
4552
- * Set the locus info the class instance
4553
- * @param {Object} locus
4554
- * @param {Array} locus.mediaConnections
4555
- * @param {String} locus.locusUrl
4556
- * @param {String} locus.locusId
4557
- * @param {String} locus.mediaId
4558
- * @param {Object} locus.host
4573
+ * Set the locus info the class instance. Should be called with the parsed locus
4574
+ * we got in the join response.
4575
+ *
4576
+ * @param {Object} data
4577
+ * @param {Array} data.mediaConnections
4578
+ * @param {String} data.locusUrl
4579
+ * @param {String} data.locusId
4580
+ * @param {String} data.mediaId
4581
+ * @param {Object} data.host
4559
4582
  * @todo change name to genertic parser
4560
4583
  * @returns {undefined}
4561
4584
  * @private
4562
4585
  * @memberof Meeting
4563
4586
  */
4564
- setLocus(
4565
- locus:
4566
- | {
4567
- mediaConnections: Array<any>;
4568
- locusUrl: string;
4569
- locusId: string;
4570
- mediaId: string;
4571
- host: object;
4572
- }
4573
- | any
4574
- ) {
4575
- const mtgLocus: any = locus.locus || locus;
4587
+ setLocus(data: {
4588
+ locus: LocusDTO;
4589
+ mediaConnections: Array<any>;
4590
+ locusUrl: string;
4591
+ locusId: string;
4592
+ mediaId: string;
4593
+ host: object;
4594
+ selfId: string;
4595
+ dataSets: DataSet[];
4596
+ }) {
4597
+ const mtgLocus: any = data.locus;
4576
4598
 
4577
4599
  // LocusInfo object saves the locus object
4578
4600
  // this.locus = mtgLocus;
4579
- this.mediaConnections = locus.mediaConnections;
4580
- this.locusUrl = locus.locusUrl || locus.url;
4581
- this.locusId = locus.locusId;
4582
- this.selfId = locus.selfId;
4583
- this.mediaId = locus.mediaId;
4601
+ this.mediaConnections = data.mediaConnections;
4602
+ this.locusUrl = data.locusUrl;
4603
+ this.locusId = data.locusId;
4604
+ this.selfId = data.selfId;
4605
+ this.mediaId = data.mediaId;
4584
4606
  this.hostId = mtgLocus.host ? mtgLocus.host.id : this.hostId;
4585
- this.locusInfo.initialSetup(mtgLocus);
4607
+ this.locusInfo.initialSetup({
4608
+ trigger: 'join-response',
4609
+ locus: mtgLocus,
4610
+ dataSets: data.dataSets,
4611
+ });
4586
4612
  }
4587
4613
 
4588
4614
  /**
@@ -5429,6 +5455,20 @@ export default class Meeting extends StatelessWebexPlugin {
5429
5455
  shouldRetry = false;
5430
5456
  }
5431
5457
 
5458
+ if (CallDiagnosticUtils.isBrowserMediaError(error)) {
5459
+ shouldRetry = false;
5460
+ // eslint-disable-next-line no-ex-assign
5461
+ error = merge({
5462
+ error: {
5463
+ body: {
5464
+ errorCode: CallDiagnosticUtils.getBrowserMediaErrorCode(error),
5465
+ message: error?.message,
5466
+ name: error?.name,
5467
+ },
5468
+ },
5469
+ });
5470
+ }
5471
+
5432
5472
  // we only want to call leave if join was successful and this was a retry or we won't be doing any more retries
5433
5473
  if (joined && (isRetry || !shouldRetry)) {
5434
5474
  try {
@@ -5695,6 +5735,21 @@ export default class Meeting extends StatelessWebexPlugin {
5695
5735
  }
5696
5736
  }
5697
5737
 
5738
+ /** Handles Locus LLM events
5739
+ *
5740
+ * @param {LocusLLMEvent} event - The Locus LLM event to process
5741
+ * @returns {void}
5742
+ */
5743
+ private processLocusLLMEvent = (event: LocusLLMEvent): void => {
5744
+ if (event.data.eventType === LOCUSEVENT.HASH_TREE_DATA_UPDATED) {
5745
+ this.locusInfo.parse(this, event.data);
5746
+ } else {
5747
+ LoggerProxy.logger.warn(
5748
+ `Meeting:index#processLocusLLMEvent --> Unknown event type: ${event.data.eventType}`
5749
+ );
5750
+ }
5751
+ };
5752
+
5698
5753
  /**
5699
5754
  * Callback called when a relay event is received from meeting LLM Connection
5700
5755
  * @param {RelayEvent} e Event object coming from LLM Connection
@@ -6066,6 +6121,46 @@ export default class Meeting extends StatelessWebexPlugin {
6066
6121
  });
6067
6122
  }
6068
6123
 
6124
+ /** starts a timer that after a few minutes checks if
6125
+ * the LLM connection is connected, if not it sends a metric
6126
+ * @private
6127
+ * @returns {void}
6128
+ */
6129
+ private startLLMHealthCheckTimer() {
6130
+ // first cancel any existing timer
6131
+ this.clearLLMHealthCheckTimer();
6132
+
6133
+ this.llmHealthCheckTimer = setTimeout(() => {
6134
+ // @ts-ignore
6135
+ const isConnected = this.webex.internal.llm.isConnected();
6136
+
6137
+ if (!isConnected) {
6138
+ // @ts-ignore
6139
+ const {hasEverConnected} = this.webex.internal.llm;
6140
+
6141
+ // only send metric if not connected - to avoid too many metrics
6142
+ Metrics.sendBehavioralMetric(BEHAVIORAL_METRICS.LLM_HEALTHCHECK_FAILURE, {
6143
+ correlation_id: this.correlationId,
6144
+ hasEverConnected,
6145
+ });
6146
+ }
6147
+
6148
+ this.llmHealthCheckTimer = undefined;
6149
+ }, LLM_HEALTHCHECK_TIMER_MS);
6150
+ }
6151
+
6152
+ /**
6153
+ * Clears the LLM health check timer
6154
+ * @private
6155
+ * @returns {void}
6156
+ */
6157
+ private clearLLMHealthCheckTimer() {
6158
+ if (this.llmHealthCheckTimer) {
6159
+ clearTimeout(this.llmHealthCheckTimer);
6160
+ this.llmHealthCheckTimer = undefined;
6161
+ }
6162
+ }
6163
+
6069
6164
  /**
6070
6165
  * Connects to low latency mercury and reconnects if the address has changed
6071
6166
  * It will also disconnect if called when the meeting has ended
@@ -6106,6 +6201,10 @@ export default class Meeting extends StatelessWebexPlugin {
6106
6201
  );
6107
6202
  // @ts-ignore - Fix type
6108
6203
  this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
6204
+ // @ts-ignore - Fix type
6205
+ this.webex.internal.llm.off(LOCUS_LLM_EVENT, this.processLocusLLMEvent);
6206
+
6207
+ this.clearLLMHealthCheckTimer();
6109
6208
  }
6110
6209
 
6111
6210
  if (!isJoined) {
@@ -6120,10 +6219,16 @@ export default class Meeting extends StatelessWebexPlugin {
6120
6219
  this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
6121
6220
  // @ts-ignore - Fix type
6122
6221
  this.webex.internal.llm.on('event:relay.event', this.processRelayEvent);
6222
+ // @ts-ignore - Fix type
6223
+ this.webex.internal.llm.off(LOCUS_LLM_EVENT, this.processLocusLLMEvent);
6224
+ // @ts-ignore - Fix type
6225
+ this.webex.internal.llm.on(LOCUS_LLM_EVENT, this.processLocusLLMEvent);
6123
6226
  LoggerProxy.logger.info(
6124
6227
  'Meeting:index#updateLLMConnection --> enabled to receive relay events!'
6125
6228
  );
6126
6229
 
6230
+ this.startLLMHealthCheckTimer();
6231
+
6127
6232
  return Promise.resolve(registerAndConnectResult);
6128
6233
  });
6129
6234
  }
@@ -7451,7 +7556,7 @@ export default class Meeting extends StatelessWebexPlugin {
7451
7556
  } seconds`
7452
7557
  );
7453
7558
 
7454
- const error = new Error('Timed out waiting for REMOTE SDP ANSWER');
7559
+ const error = new SdpResponseTimeoutError();
7455
7560
 
7456
7561
  // @ts-ignore
7457
7562
  this.webex.internal.newMetrics.submitClientEvent({
@@ -9371,6 +9476,10 @@ export default class Meeting extends StatelessWebexPlugin {
9371
9476
 
9372
9477
  // @ts-ignore - fix types
9373
9478
  this.webex.internal.llm.off('event:relay.event', this.processRelayEvent);
9479
+ // @ts-ignore - Fix type
9480
+ this.webex.internal.llm.off(LOCUS_LLM_EVENT, this.processLocusLLMEvent);
9481
+
9482
+ this.clearLLMHealthCheckTimer();
9374
9483
  };
9375
9484
 
9376
9485
  /**
@@ -31,6 +31,7 @@ const MeetingUtil = {
31
31
 
32
32
  // First todo: add check for existance
33
33
  parsed.locus = response.body.locus;
34
+ parsed.dataSets = response.body.dataSets;
34
35
  parsed.mediaConnections = response.body.mediaConnections;
35
36
  parsed.locusUrl = parsed.locus.url;
36
37
  parsed.locusId = parsed.locus.url.split('/').pop();
@@ -1,5 +1,5 @@
1
1
  /* eslint no-shadow: ["error", { "allow": ["eventType"] }] */
2
- import {cloneDeep, clone} from 'lodash';
2
+ import {cloneDeep, clone, set} from 'lodash';
3
3
  import '@webex/internal-plugin-mercury';
4
4
  import '@webex/internal-plugin-conversation';
5
5
  import '@webex/internal-plugin-metrics';
@@ -66,6 +66,9 @@ import JoinWebinarError from '../common/errors/join-webinar-error';
66
66
  import {SpaceIDDeprecatedError} from '../common/errors/webex-errors';
67
67
  import NoMeetingInfoError from '../common/errors/no-meeting-info';
68
68
  import JoinForbiddenError from '../common/errors/join-forbidden-error';
69
+ import {HashTreeMessage} from '../hashTree/hashTreeParser';
70
+ import {HashTreeObject} from '../hashTree/types';
71
+ import {isSelf} from '../hashTree/utils';
69
72
 
70
73
  let mediaLogger;
71
74
 
@@ -94,6 +97,18 @@ class MediaLogger {
94
97
  LoggerProxy.logger.debug(...args);
95
98
  }
96
99
  }
100
+
101
+ export type LocusEvent = {
102
+ eventType: LOCUSEVENT;
103
+
104
+ // fields populated for "classic" locus events (eventType = 'locus.difference' and others, see LOCUSEVENT)
105
+ locusUrl?: string;
106
+ locus?: any;
107
+
108
+ // fields populated for "hash tree" locus events (eventType = 'locus.state_message' - see LOCUSEVENT.HASH_TREE_DATA_UPDATED)
109
+ stateElementsMessage?: HashTreeMessage;
110
+ };
111
+
97
112
  /**
98
113
  * Meetings Ready Event
99
114
  * Emitted when the meetings instance on webex is ready
@@ -406,29 +421,42 @@ export default class Meetings extends WebexPlugin {
406
421
  * @private
407
422
  * @memberof Meetings
408
423
  */
409
- getCorrespondingMeetingByLocus(data) {
410
- // getting meeting by correlationId. This will happen for the new event
411
- // Either the locus
412
- // TODO : Add check for the callBack Address
424
+ getCorrespondingMeetingByLocus(data: LocusEvent) {
425
+ const locusUrl =
426
+ data.stateElementsMessage?.locusUrl || // hash tree event
427
+ data.locusUrl; // classic event
428
+
429
+ // first try to find by locusUrl - that's the simplest and quickest way
430
+ const existingMeeting = this.meetingCollection.getByKey(MEETING_KEY.LOCUS_URL, locusUrl);
431
+
432
+ if (existingMeeting) {
433
+ return existingMeeting;
434
+ }
435
+
436
+ // if that didn't work, fallback to other fields like correlationId, sipUri, etc
437
+
438
+ // If the event is a hash tree event, we need to extract "self" object from it
439
+ // We don't care about the version, just need to find the meeting this event is for,
440
+ // so any hash tree object of type "self" will do
441
+ const hashTreeEventSelf = data.stateElementsMessage?.locusStateElements?.find(
442
+ (obj: HashTreeObject) => isSelf(obj)
443
+ )?.data;
444
+
445
+ const self = hashTreeEventSelf || data.locus?.self;
446
+
413
447
  return (
414
- this.meetingCollection.getByKey(MEETING_KEY.LOCUS_URL, data.locusUrl) ||
415
448
  // @ts-ignore
416
449
  this.meetingCollection.getByKey(
417
450
  MEETING_KEY.CORRELATION_ID,
418
451
  // @ts-ignore
419
- MeetingsUtil.checkForCorrelationId(this.webex.internal.device.url, data.locus)
452
+ MeetingsUtil.getCorrelationIdForDevice(this.webex.internal.device.url, self)
420
453
  ) ||
421
- this.meetingCollection.getByKey(
422
- MEETING_KEY.SIP_URI,
423
- data.locus.self &&
424
- data.locus.self.callbackInfo &&
425
- data.locus.self.callbackInfo.callbackAddress
426
- ) ||
427
- (data.locus.info?.isUnifiedSpaceMeeting
454
+ this.meetingCollection.getByKey(MEETING_KEY.SIP_URI, self?.callbackInfo?.callbackAddress) ||
455
+ (data.locus?.info?.isUnifiedSpaceMeeting
428
456
  ? undefined
429
457
  : this.meetingCollection.getByKey(
430
458
  MEETING_KEY.CONVERSATION_URL,
431
- data.locus.conversationUrl
459
+ data.locus?.conversationUrl
432
460
  )) ||
433
461
  this.meetingCollection.getByKey(MEETING_KEY.MEETINGNUMBER, data.locus?.info?.webExMeetingId)
434
462
  );
@@ -445,30 +473,33 @@ export default class Meetings extends WebexPlugin {
445
473
  * @private
446
474
  * @memberof Meetings
447
475
  */
448
- private handleLocusEvent(data: {locusUrl: string; locus: any}, useRandomDelayForInfo = false) {
476
+ private handleLocusEvent(data: LocusEvent, useRandomDelayForInfo = false) {
449
477
  let meeting = this.getCorrespondingMeetingByLocus(data);
450
478
 
451
479
  // Special case when locus has got replaced, This only happend once if a replace locus exists
452
480
  // https://sqbu-github.cisco.com/WebExSquared/locus/wiki/Locus-changing-mid-call
453
481
 
454
- if (!meeting && data.locus?.replaces?.length > 0) {
455
- // Always the last element in the replace is the active one
456
- meeting = this.meetingCollection.getByKey(
457
- MEETING_KEY.LOCUS_URL,
458
- data.locus.replaces[data.locus.replaces.length - 1].locusUrl
459
- );
460
- }
482
+ if (data.eventType !== LOCUSEVENT.HASH_TREE_DATA_UPDATED) {
483
+ if (!meeting && data.locus?.replaces?.length > 0) {
484
+ // Always the last element in the replace is the active one
485
+ meeting = this.meetingCollection.getByKey(
486
+ MEETING_KEY.LOCUS_URL,
487
+ data.locus.replaces[data.locus.replaces.length - 1].locusUrl
488
+ );
489
+ }
461
490
 
462
- if (meeting && !MeetingsUtil.isBreakoutLocusDTO(data.locus)) {
463
- meeting.locusInfo.updateMainSessionLocusCache(data.locus);
464
- }
465
- if (!this.isNeedHandleLocusDTO(meeting, data.locus)) {
466
- LoggerProxy.logger.log(
467
- `Meetings:index#handleLocusEvent --> doesn't need to process locus event`
468
- );
491
+ if (meeting && !MeetingsUtil.isBreakoutLocusDTO(data.locus)) {
492
+ meeting.locusInfo.updateMainSessionLocusCache(data.locus);
493
+ }
494
+ if (!this.isNeedHandleLocusDTO(meeting, data.locus)) {
495
+ LoggerProxy.logger.log(
496
+ `Meetings:index#handleLocusEvent --> doesn't need to process locus event`
497
+ );
469
498
 
470
- return;
499
+ return;
500
+ }
471
501
  }
502
+
472
503
  if (!meeting) {
473
504
  // TODO: create meeting when we get a meeting object
474
505
  // const checkForEnded = (locus) => {
@@ -489,42 +520,65 @@ export default class Meetings extends WebexPlugin {
489
520
  // };
490
521
  // rather then locus object change to locus url
491
522
 
492
- if (
493
- data.locus &&
494
- data.locus.fullState &&
495
- data.locus.fullState.state === LOCUS.STATE.INACTIVE
496
- ) {
497
- // just ignore the event as its already ended and not active
498
- LoggerProxy.logger.warn(
499
- 'Meetings:index#handleLocusEvent --> Locus event received for meeting, after it was ended.'
500
- );
523
+ if (data.eventType !== LOCUSEVENT.HASH_TREE_DATA_UPDATED) {
524
+ if (
525
+ data.locus &&
526
+ data.locus.fullState &&
527
+ data.locus.fullState.state === LOCUS.STATE.INACTIVE
528
+ ) {
529
+ // just ignore the event as its already ended and not active
530
+ LoggerProxy.logger.warn(
531
+ 'Meetings:index#handleLocusEvent --> Locus event received for meeting, after it was ended.'
532
+ );
501
533
 
502
- return;
503
- }
534
+ return;
535
+ }
504
536
 
505
- // When its wireless share or guest and user leaves the meeting we dont have to keep the meeting object
506
- // Any future events will be neglected
537
+ // When its wireless share or guest and user leaves the meeting we dont have to keep the meeting object
538
+ // Any future events will be neglected
539
+
540
+ if (
541
+ data.locus &&
542
+ data.locus.self &&
543
+ data.locus.self.state === _LEFT_ &&
544
+ data.locus.self.removed === true
545
+ ) {
546
+ // just ignore the event as its already ended and not active
547
+ LoggerProxy.logger.warn(
548
+ 'Meetings:index#handleLocusEvent --> Locus event received for meeting, after it was ended.'
549
+ );
507
550
 
508
- if (
509
- data.locus &&
510
- data.locus.self &&
511
- data.locus.self.state === _LEFT_ &&
512
- data.locus.self.removed === true
513
- ) {
514
- // just ignore the event as its already ended and not active
515
- LoggerProxy.logger.warn(
516
- 'Meetings:index#handleLocusEvent --> Locus event received for meeting, after it was ended.'
517
- );
551
+ return;
552
+ }
553
+ }
518
554
 
519
- return;
555
+ if (data.eventType === LOCUSEVENT.HASH_TREE_DATA_UPDATED) {
556
+ // in hash tree messages we don't ge the locus object, but the meeting constructor needs at least locus.url
557
+ set(data, 'locus.url', data.stateElementsMessage.locusUrl);
520
558
  }
521
559
 
522
560
  this.create(data.locus, DESTINATION_TYPE.LOCUS_ID, useRandomDelayForInfo)
523
- .then((newMeeting) => {
561
+ .then(async (newMeeting) => {
524
562
  meeting = newMeeting;
525
563
 
526
- // It's a new meeting so initialize the locus data
527
- meeting.locusInfo.initialSetup(data.locus);
564
+ try {
565
+ // It's a new meeting so initialize the locus data
566
+ await meeting.locusInfo.initialSetup({
567
+ trigger:
568
+ data.eventType === LOCUSEVENT.SDK_LOCUS_FROM_SYNC_MEETINGS
569
+ ? 'get-loci-response'
570
+ : 'locus-message',
571
+ locus: data.locus,
572
+ hashTreeMessage: data.stateElementsMessage,
573
+ });
574
+ } catch (error) {
575
+ LoggerProxy.logger.warn(
576
+ `Meetings:index#handleLocusEvent --> Error initializing locus data: ${error.message}`
577
+ );
578
+ // @ts-ignore
579
+ this.destroy(meeting, MEETING_REMOVED_REASON.LOCUS_DTO_SYNC_FAILED);
580
+ }
581
+
528
582
  this.checkHandleBreakoutLocus(data.locus);
529
583
  })
530
584
  .catch((e) => {
@@ -1739,6 +1793,7 @@ export default class Meetings extends WebexPlugin {
1739
1793
  lociToUpdate.forEach((locus) => {
1740
1794
  activeLocusUrl.push(locus.url);
1741
1795
  this.handleLocusEvent({
1796
+ eventType: LOCUSEVENT.SDK_LOCUS_FROM_SYNC_MEETINGS,
1742
1797
  locus,
1743
1798
  locusUrl: locus.url,
1744
1799
  });
@@ -1786,6 +1841,7 @@ export default class Meetings extends WebexPlugin {
1786
1841
  (mainLocus) => mainLocus.controls?.breakout?.url === breakoutLocus.controls?.breakout?.url
1787
1842
  );
1788
1843
  const existCorrespondingMeeting = this.getCorrespondingMeetingByLocus({
1844
+ eventType: LOCUSEVENT.SDK_NO_EVENT,
1789
1845
  locus: breakoutLocus,
1790
1846
  locusUrl: breakoutLocus.url,
1791
1847
  });
@@ -1831,7 +1887,11 @@ export default class Meetings extends WebexPlugin {
1831
1887
  }
1832
1888
 
1833
1889
  const associateBreakoutLocus = this.breakoutLocusForHandleLater[existIndex];
1834
- this.handleLocusEvent({locus: associateBreakoutLocus, locusUrl: associateBreakoutLocus.url});
1890
+ this.handleLocusEvent({
1891
+ eventType: LOCUSEVENT.SDK_NO_EVENT,
1892
+ locus: associateBreakoutLocus,
1893
+ locusUrl: associateBreakoutLocus.url,
1894
+ });
1835
1895
  this.breakoutLocusForHandleLater.splice(existIndex, 1);
1836
1896
  }
1837
1897
 
@@ -117,15 +117,16 @@ MeetingsUtil.getMediaServerIp = (sdp) => {
117
117
  return mediaServerIp;
118
118
  };
119
119
 
120
- MeetingsUtil.checkForCorrelationId = (deviceUrl, locus) => {
121
- let devices = [];
122
-
123
- if (locus) {
124
- if (locus && locus.self && locus.self.devices) {
125
- devices = locus.self.devices;
126
- }
127
-
128
- const foundDevice = devices.find((device) => device.url === deviceUrl);
120
+ /**
121
+ * Finds correlationId of a device from locus self devices array
122
+ * that matches the given deviceUrl
123
+ * @param {string} deviceUrl
124
+ * @param {object} locusSelf
125
+ * @returns {string|false} correlationId or false if not found
126
+ */
127
+ MeetingsUtil.getCorrelationIdForDevice = (deviceUrl: string, locusSelf: any) => {
128
+ if (locusSelf?.devices) {
129
+ const foundDevice = locusSelf?.devices.find((device) => device.url === deviceUrl);
129
130
 
130
131
  if (foundDevice && foundDevice.correlationId) {
131
132
  return foundDevice.correlationId;
@@ -21,6 +21,7 @@ const BEHAVIORAL_METRICS = {
21
21
  GET_DISPLAY_MEDIA_FAILURE: 'js_sdk_get_display_media_failures',
22
22
  JOIN_WITH_MEDIA_FAILURE: 'js_sdk_join_with_media_failures',
23
23
  LLM_CONNECTION_AFTER_JOIN_FAILURE: 'js_sdk_llm_connection_after_join_failure',
24
+ LLM_HEALTHCHECK_FAILURE: 'js_sdk_llm_healthcheck_failure',
24
25
  RECEIVE_TRANSCRIPTION_AFTER_JOIN_FAILURE: 'js_sdk_receive_transcription_after_join_failure',
25
26
 
26
27
  DISCONNECT_DUE_TO_INACTIVITY: 'js_sdk_disconnect_due_to_inactivity',
@@ -87,6 +88,8 @@ const BEHAVIORAL_METRICS = {
87
88
  VERIFY_REGISTRATION_ID_ERROR: 'js_sdk_verify_registrationId_error',
88
89
  JOIN_FORBIDDEN_ERROR: 'js_sdk_join_forbidden_error',
89
90
  MEDIA_ISSUE_DETECTED: 'js_sdk_media_issue_detected',
91
+ LOCUS_CLASSIC_VS_HASH_TREE_MISMATCH: 'js_sdk_locus_classic_vs_hash_tree_mismatch',
92
+ LOCUS_HASH_TREE_UNSUPPORTED_OPERATION: 'js_sdk_locus_hash_tree_unsupported_operation',
90
93
  };
91
94
 
92
95
  export {BEHAVIORAL_METRICS as default};