@webex/contact-center 3.12.0-next.16 → 3.12.0-next.161

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 (328) hide show
  1. package/.sdd/manifest.json +887 -0
  2. package/AGENTS.md +94 -0
  3. package/ai-docs/ARCHITECTURE.md +168 -0
  4. package/ai-docs/CONTRACTS.md +62 -0
  5. package/ai-docs/GETTING_STARTED.md +168 -0
  6. package/ai-docs/GLOSSARY.md +43 -0
  7. package/ai-docs/README.md +138 -0
  8. package/ai-docs/REVIEW_CHECKLIST.md +41 -0
  9. package/ai-docs/RULES.md +444 -0
  10. package/ai-docs/SECURITY.md +52 -0
  11. package/ai-docs/SERVICE_STATE.md +52 -0
  12. package/ai-docs/SPEC_INDEX.md +65 -0
  13. package/ai-docs/adr/0001-spec-source-policy.md +55 -0
  14. package/ai-docs/adr/README.md +8 -0
  15. package/ai-docs/adr/_adr-template.md +31 -0
  16. package/ai-docs/contact-center-spec.md +385 -0
  17. package/ai-docs/features/consult-transfer-list-policy/spec/feature-spec.md +362 -0
  18. package/ai-docs/features/contact-owner-change-refactor.md +78 -0
  19. package/ai-docs/features/generated-spec-conformance-fidelity-remediation/spec/feature-spec.md +117 -0
  20. package/ai-docs/features/residual-warning-coverage-completion/spec/feature-spec.md +203 -0
  21. package/ai-docs/features/validator-code-fidelity-drift-fix/spec/feature-spec.md +315 -0
  22. package/ai-docs/patterns/event-driven-patterns.md +485 -0
  23. package/ai-docs/patterns/testing-patterns.md +480 -0
  24. package/ai-docs/patterns/typescript-patterns.md +365 -0
  25. package/ai-docs/templates/README.md +102 -0
  26. package/ai-docs/templates/documentation/create-agents-md.md +240 -0
  27. package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
  28. package/ai-docs/templates/existing-service/bug-fix.md +254 -0
  29. package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
  30. package/ai-docs/templates/new-method/00-master.md +80 -0
  31. package/ai-docs/templates/new-method/01-requirements.md +232 -0
  32. package/ai-docs/templates/new-method/02-implementation.md +295 -0
  33. package/ai-docs/templates/new-method/03-tests.md +201 -0
  34. package/ai-docs/templates/new-method/04-validation.md +141 -0
  35. package/ai-docs/templates/new-service/00-master.md +109 -0
  36. package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
  37. package/ai-docs/templates/new-service/02-code-generation.md +346 -0
  38. package/ai-docs/templates/new-service/03-integration.md +178 -0
  39. package/ai-docs/templates/new-service/04-test-generation.md +205 -0
  40. package/ai-docs/templates/new-service/05-validation.md +145 -0
  41. package/dist/cc.js +888 -49
  42. package/dist/cc.js.map +1 -1
  43. package/dist/config.js +13 -0
  44. package/dist/config.js.map +1 -1
  45. package/dist/constants.js +38 -3
  46. package/dist/constants.js.map +1 -1
  47. package/dist/index.js +54 -17
  48. package/dist/index.js.map +1 -1
  49. package/dist/metrics/MetricsManager.js +16 -0
  50. package/dist/metrics/MetricsManager.js.map +1 -1
  51. package/dist/metrics/behavioral-events.js +185 -0
  52. package/dist/metrics/behavioral-events.js.map +1 -1
  53. package/dist/metrics/constants.js +54 -4
  54. package/dist/metrics/constants.js.map +1 -1
  55. package/dist/services/AddressBook.js +18 -15
  56. package/dist/services/AddressBook.js.map +1 -1
  57. package/dist/services/AnswerCallOnWebexService.js +202 -0
  58. package/dist/services/AnswerCallOnWebexService.js.map +1 -0
  59. package/dist/services/ApiAiAssistant.js +369 -14
  60. package/dist/services/ApiAiAssistant.js.map +1 -1
  61. package/dist/services/EntryPoint.js +46 -68
  62. package/dist/services/EntryPoint.js.map +1 -1
  63. package/dist/services/Queue.js +27 -22
  64. package/dist/services/Queue.js.map +1 -1
  65. package/dist/services/UserPreference.js +427 -0
  66. package/dist/services/UserPreference.js.map +1 -0
  67. package/dist/services/WebCallingService.js +10 -2
  68. package/dist/services/WebCallingService.js.map +1 -1
  69. package/dist/services/WebexCrossClientService.js +225 -0
  70. package/dist/services/WebexCrossClientService.js.map +1 -0
  71. package/dist/services/WxAppTelephonyMercurySync.js +111 -0
  72. package/dist/services/WxAppTelephonyMercurySync.js.map +1 -0
  73. package/dist/services/config/Util.js +48 -48
  74. package/dist/services/config/Util.js.map +1 -1
  75. package/dist/services/config/constants.js +56 -78
  76. package/dist/services/config/constants.js.map +1 -1
  77. package/dist/services/config/index.js +100 -226
  78. package/dist/services/config/index.js.map +1 -1
  79. package/dist/services/config/types.js +93 -24
  80. package/dist/services/config/types.js.map +1 -1
  81. package/dist/services/constants.js +11 -1
  82. package/dist/services/constants.js.map +1 -1
  83. package/dist/services/core/Err.js.map +1 -1
  84. package/dist/services/core/Utils.js +131 -32
  85. package/dist/services/core/Utils.js.map +1 -1
  86. package/dist/services/core/WebexRequest.js +6 -2
  87. package/dist/services/core/WebexRequest.js.map +1 -1
  88. package/dist/services/core/aqm-reqs.js +17 -11
  89. package/dist/services/core/aqm-reqs.js.map +1 -1
  90. package/dist/services/core/constants.js +9 -1
  91. package/dist/services/core/constants.js.map +1 -1
  92. package/dist/services/core/types.js.map +1 -1
  93. package/dist/services/core/websocket/WebSocketManager.js +19 -2
  94. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  95. package/dist/services/core/websocket/types.js.map +1 -1
  96. package/dist/services/index.js +1 -1
  97. package/dist/services/index.js.map +1 -1
  98. package/dist/services/task/Task.js +955 -0
  99. package/dist/services/task/Task.js.map +1 -0
  100. package/dist/services/task/TaskFactory.js +50 -0
  101. package/dist/services/task/TaskFactory.js.map +1 -0
  102. package/dist/services/task/TaskManager.js +1272 -605
  103. package/dist/services/task/TaskManager.js.map +1 -1
  104. package/dist/services/task/TaskUtils.js +221 -27
  105. package/dist/services/task/TaskUtils.js.map +1 -1
  106. package/dist/services/task/WebexCallingUtils.js +70 -0
  107. package/dist/services/task/WebexCallingUtils.js.map +1 -0
  108. package/dist/services/task/constants.js +19 -2
  109. package/dist/services/task/constants.js.map +1 -1
  110. package/dist/services/task/contact.js +29 -0
  111. package/dist/services/task/contact.js.map +1 -1
  112. package/dist/services/task/digital/Digital.js +78 -0
  113. package/dist/services/task/digital/Digital.js.map +1 -0
  114. package/dist/services/task/state-machine/TaskStateMachine.js +982 -0
  115. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  116. package/dist/services/task/state-machine/actions.js +572 -0
  117. package/dist/services/task/state-machine/actions.js.map +1 -0
  118. package/dist/services/task/state-machine/constants.js +161 -0
  119. package/dist/services/task/state-machine/constants.js.map +1 -0
  120. package/dist/services/task/state-machine/guards.js +379 -0
  121. package/dist/services/task/state-machine/guards.js.map +1 -0
  122. package/dist/services/task/state-machine/index.js +53 -0
  123. package/dist/services/task/state-machine/index.js.map +1 -0
  124. package/dist/services/task/state-machine/types.js +54 -0
  125. package/dist/services/task/state-machine/types.js.map +1 -0
  126. package/dist/services/task/state-machine/uiControlsComputer.js +703 -0
  127. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  128. package/dist/services/task/taskDataNormalizer.js +99 -0
  129. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  130. package/dist/services/task/types.js +215 -5
  131. package/dist/services/task/types.js.map +1 -1
  132. package/dist/services/task/voice/Voice.js +1504 -0
  133. package/dist/services/task/voice/Voice.js.map +1 -0
  134. package/dist/services/task/voice/WebRTC.js +152 -0
  135. package/dist/services/task/voice/WebRTC.js.map +1 -0
  136. package/dist/services/task/voice/wxAppOfferObservability.js +145 -0
  137. package/dist/services/task/voice/wxAppOfferObservability.js.map +1 -0
  138. package/dist/services/task/voice/wxAppVoiceMethods.js +395 -0
  139. package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -0
  140. package/dist/services/wxAppDiagnosticLogging.js +159 -0
  141. package/dist/services/wxAppDiagnosticLogging.js.map +1 -0
  142. package/dist/services/wxAppTelephonyUtils.js +19 -0
  143. package/dist/services/wxAppTelephonyUtils.js.map +1 -0
  144. package/dist/types/cc.d.ts +152 -9
  145. package/dist/types/config.d.ts +13 -0
  146. package/dist/types/constants.d.ts +36 -1
  147. package/dist/types/index.d.ts +54 -20
  148. package/dist/types/metrics/MetricsManager.d.ts +6 -0
  149. package/dist/types/metrics/constants.d.ts +48 -1
  150. package/dist/types/services/AddressBook.d.ts +2 -1
  151. package/dist/types/services/AnswerCallOnWebexService.d.ts +38 -0
  152. package/dist/types/services/ApiAiAssistant.d.ts +67 -5
  153. package/dist/types/services/EntryPoint.d.ts +7 -5
  154. package/dist/types/services/Queue.d.ts +5 -3
  155. package/dist/types/services/UserPreference.d.ts +118 -0
  156. package/dist/types/services/WebexCrossClientService.d.ts +32 -0
  157. package/dist/types/services/WxAppTelephonyMercurySync.d.ts +28 -0
  158. package/dist/types/services/config/Util.d.ts +4 -8
  159. package/dist/types/services/config/constants.d.ts +49 -76
  160. package/dist/types/services/config/index.d.ts +16 -64
  161. package/dist/types/services/config/types.d.ts +240 -114
  162. package/dist/types/services/constants.d.ts +9 -0
  163. package/dist/types/services/core/Err.d.ts +2 -0
  164. package/dist/types/services/core/Utils.d.ts +43 -13
  165. package/dist/types/services/core/WebexRequest.d.ts +1 -0
  166. package/dist/types/services/core/constants.d.ts +7 -0
  167. package/dist/types/services/core/types.d.ts +2 -0
  168. package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
  169. package/dist/types/services/core/websocket/types.d.ts +1 -1
  170. package/dist/types/services/index.d.ts +1 -1
  171. package/dist/types/services/task/Task.d.ts +186 -0
  172. package/dist/types/services/task/TaskFactory.d.ts +14 -0
  173. package/dist/types/services/task/TaskUtils.d.ts +54 -2
  174. package/dist/types/services/task/WebexCallingUtils.d.ts +11 -0
  175. package/dist/types/services/task/constants.d.ts +17 -0
  176. package/dist/types/services/task/contact.d.ts +4 -0
  177. package/dist/types/services/task/digital/Digital.d.ts +23 -0
  178. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1442 -0
  179. package/dist/types/services/task/state-machine/actions.d.ts +10 -0
  180. package/dist/types/services/task/state-machine/constants.d.ts +107 -0
  181. package/dist/types/services/task/state-machine/guards.d.ts +103 -0
  182. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  183. package/dist/types/services/task/state-machine/types.d.ts +277 -0
  184. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  185. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  186. package/dist/types/services/task/types.d.ts +738 -66
  187. package/dist/types/services/task/voice/Voice.d.ts +241 -0
  188. package/dist/types/services/task/voice/WebRTC.d.ts +54 -0
  189. package/dist/types/services/task/voice/wxAppOfferObservability.d.ts +39 -0
  190. package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +68 -0
  191. package/dist/types/services/wxAppDiagnosticLogging.d.ts +63 -0
  192. package/dist/types/services/wxAppTelephonyUtils.d.ts +5 -0
  193. package/dist/types/types.d.ts +213 -11
  194. package/dist/types/utils/PageCache.d.ts +20 -3
  195. package/dist/types/webex.d.ts +1 -0
  196. package/dist/types.js +168 -6
  197. package/dist/types.js.map +1 -1
  198. package/dist/utils/PageCache.js +19 -5
  199. package/dist/utils/PageCache.js.map +1 -1
  200. package/dist/webex.js +22 -2
  201. package/dist/webex.js.map +1 -1
  202. package/package.json +16 -12
  203. package/src/cc.ts +1137 -67
  204. package/src/config.ts +13 -0
  205. package/src/constants.ts +39 -1
  206. package/src/index.ts +62 -15
  207. package/src/metrics/MetricsManager.ts +16 -0
  208. package/src/metrics/ai-docs/AGENTS.md +350 -0
  209. package/src/metrics/ai-docs/ARCHITECTURE.md +338 -0
  210. package/src/metrics/ai-docs/metrics-spec.md +971 -0
  211. package/src/metrics/behavioral-events.ts +190 -0
  212. package/src/metrics/constants.ts +59 -4
  213. package/src/services/AddressBook.ts +17 -6
  214. package/src/services/AnswerCallOnWebexService.ts +239 -0
  215. package/src/services/ApiAiAssistant.ts +494 -15
  216. package/src/services/EntryPoint.ts +59 -60
  217. package/src/services/Queue.ts +29 -12
  218. package/src/services/UserPreference.ts +509 -0
  219. package/src/services/WebCallingService.ts +16 -2
  220. package/src/services/WebexCrossClientService.ts +298 -0
  221. package/src/services/WxAppTelephonyMercurySync.ts +142 -0
  222. package/src/services/agent/ai-docs/AGENTS.md +240 -0
  223. package/src/services/agent/ai-docs/ARCHITECTURE.md +304 -0
  224. package/src/services/agent/ai-docs/agent-spec.md +505 -0
  225. package/src/services/ai-docs/AGENTS.md +386 -0
  226. package/src/services/ai-docs/services-spec.md +512 -0
  227. package/src/services/config/Util.ts +54 -84
  228. package/src/services/config/ai-docs/AGENTS.md +250 -0
  229. package/src/services/config/ai-docs/ARCHITECTURE.md +413 -0
  230. package/src/services/config/ai-docs/config-spec.md +669 -0
  231. package/src/services/config/constants.ts +63 -85
  232. package/src/services/config/index.ts +116 -277
  233. package/src/services/config/types.ts +229 -125
  234. package/src/services/constants.ts +10 -0
  235. package/src/services/core/Err.ts +1 -0
  236. package/src/services/core/Utils.ts +151 -37
  237. package/src/services/core/WebexRequest.ts +3 -1
  238. package/src/services/core/ai-docs/AGENTS.md +381 -0
  239. package/src/services/core/ai-docs/ARCHITECTURE.md +698 -0
  240. package/src/services/core/ai-docs/core-spec.md +797 -0
  241. package/src/services/core/aqm-reqs.ts +18 -12
  242. package/src/services/core/constants.ts +8 -0
  243. package/src/services/core/types.ts +33 -1
  244. package/src/services/core/websocket/WebSocketManager.ts +28 -2
  245. package/src/services/core/websocket/types.ts +1 -1
  246. package/src/services/index.ts +1 -1
  247. package/src/services/task/Task.ts +1211 -0
  248. package/src/services/task/TaskFactory.ts +82 -0
  249. package/src/services/task/TaskManager.ts +1585 -724
  250. package/src/services/task/TaskUtils.ts +316 -25
  251. package/src/services/task/WebexCallingUtils.ts +136 -0
  252. package/src/services/task/ai-docs/AGENTS.md +457 -0
  253. package/src/services/task/ai-docs/ARCHITECTURE.md +595 -0
  254. package/src/services/task/ai-docs/task-spec.md +1501 -0
  255. package/src/services/task/constants.ts +18 -0
  256. package/src/services/task/contact.ts +30 -0
  257. package/src/services/task/digital/Digital.ts +102 -0
  258. package/src/services/task/state-machine/TaskStateMachine.ts +1320 -0
  259. package/src/services/task/state-machine/actions.ts +741 -0
  260. package/src/services/task/state-machine/ai-docs/AGENTS.md +460 -0
  261. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1156 -0
  262. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2247 -0
  263. package/src/services/task/state-machine/constants.ts +172 -0
  264. package/src/services/task/state-machine/guards.ts +442 -0
  265. package/src/services/task/state-machine/index.ts +28 -0
  266. package/src/services/task/state-machine/types.ts +258 -0
  267. package/src/services/task/state-machine/uiControlsComputer.ts +1135 -0
  268. package/src/services/task/taskDataNormalizer.ts +137 -0
  269. package/src/services/task/types.ts +918 -70
  270. package/src/services/task/voice/Voice.ts +1878 -0
  271. package/src/services/task/voice/WebRTC.ts +194 -0
  272. package/src/services/task/voice/wxAppOfferObservability.ts +204 -0
  273. package/src/services/task/voice/wxAppVoiceMethods.ts +575 -0
  274. package/src/services/wxAppDiagnosticLogging.ts +286 -0
  275. package/src/services/wxAppTelephonyUtils.ts +14 -0
  276. package/src/types.ts +272 -13
  277. package/src/utils/AGENTS.md +289 -0
  278. package/src/utils/PageCache.ts +38 -5
  279. package/src/utils/ai-docs/utils-spec.md +391 -0
  280. package/src/webex.js +10 -0
  281. package/test/unit/fixtures/aiSummaryTestUtils.ts +40 -0
  282. package/test/unit/spec/cc.ts +3553 -107
  283. package/test/unit/spec/index.ts +885 -0
  284. package/test/unit/spec/logger-proxy.ts +70 -0
  285. package/test/unit/spec/metrics/MetricsManager.ts +25 -0
  286. package/test/unit/spec/metrics/behavioral-events.ts +60 -0
  287. package/test/unit/spec/services/AddressBook.ts +37 -6
  288. package/test/unit/spec/services/AnswerCallOnWebexService.ts +264 -0
  289. package/test/unit/spec/services/ApiAiAssistant.ts +442 -23
  290. package/test/unit/spec/services/EntryPoint.ts +87 -40
  291. package/test/unit/spec/services/Queue.ts +123 -12
  292. package/test/unit/spec/services/UserPreference.ts +401 -0
  293. package/test/unit/spec/services/WebCallingService.ts +45 -1
  294. package/test/unit/spec/services/WebexCrossClientService.ts +509 -0
  295. package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +136 -0
  296. package/test/unit/spec/services/config/Util.ts +501 -0
  297. package/test/unit/spec/services/config/index.ts +317 -547
  298. package/test/unit/spec/services/core/Utils.ts +447 -8
  299. package/test/unit/spec/services/core/WebexRequest.ts +8 -7
  300. package/test/unit/spec/services/core/aqm-reqs.ts +142 -1
  301. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +129 -53
  302. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  303. package/test/unit/spec/services/task/Task.ts +2723 -0
  304. package/test/unit/spec/services/task/TaskFactory.ts +116 -0
  305. package/test/unit/spec/services/task/TaskManager.ts +4734 -1769
  306. package/test/unit/spec/services/task/TaskUtils.ts +307 -0
  307. package/test/unit/spec/services/task/WebexCallingUtils.ts +153 -0
  308. package/test/unit/spec/services/task/contact.ts +33 -0
  309. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  310. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +4060 -0
  311. package/test/unit/spec/services/task/state-machine/guards.ts +839 -0
  312. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  313. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +3101 -0
  314. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  315. package/test/unit/spec/services/task/voice/Voice.ts +2355 -0
  316. package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
  317. package/test/unit/spec/services/task/voice/wxAppOfferObservability.ts +361 -0
  318. package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +719 -0
  319. package/test/unit/spec/services/wxAppDiagnosticLogging.ts +179 -0
  320. package/test/unit/spec/wellnessSampleUtils.ts +288 -0
  321. package/typedoc.md +1 -1
  322. package/umd/contact-center.min.js +2 -2
  323. package/umd/contact-center.min.js.map +1 -1
  324. package/dist/services/task/index.js +0 -1530
  325. package/dist/services/task/index.js.map +0 -1
  326. package/dist/types/services/task/index.d.ts +0 -650
  327. package/src/services/task/index.ts +0 -1806
  328. package/test/unit/spec/services/task/index.ts +0 -2205
package/src/cc.ts CHANGED
@@ -25,6 +25,10 @@ import {
25
25
  UploadLogsResponse,
26
26
  UpdateDeviceTypeResponse,
27
27
  GenericError,
28
+ ConfigFlags,
29
+ WellnessBreakEvent,
30
+ WellnessBreakNotificationAction,
31
+ WELLNESS_BREAK_NOTIFICATION_ACTIONS,
28
32
  } from './types';
29
33
  import {
30
34
  READY,
@@ -37,6 +41,7 @@ import {
37
41
  UNKNOWN_ERROR,
38
42
  MERCURY_DISCONNECTED_SUCCESS,
39
43
  METHODS,
44
+ WELLNESS_BREAK_HANDLER,
40
45
  } from './constants';
41
46
  import {AGENT_STATE_AVAILABLE, AGENT_STATE_AVAILABLE_ID} from './services/config/constants';
42
47
  import {AGENT, RTD_SUBSCRIBE_API, SUBSCRIBE_API, WEB_RTC_PREFIX} from './services/constants';
@@ -44,17 +49,22 @@ import Services from './services';
44
49
  import WebexRequest from './services/core/WebexRequest';
45
50
  import LoggerProxy from './logger-proxy';
46
51
  import {StateChange, Logout, StateChangeSuccess, AGENT_EVENTS} from './services/agent/types';
47
- import {getErrorDetails, isValidDialNumber} from './services/core/Utils';
52
+ import {getErrorDetails, isRecord, isValidDialNumber} from './services/core/Utils';
48
53
  import {
49
54
  Profile,
50
55
  WelcomeEvent,
51
56
  CC_EVENTS,
52
57
  OutdialAniEntriesResponse,
53
58
  OutdialAniParams,
59
+ Entity,
54
60
  } from './services/config/types';
55
61
  import {ConnectionLostDetails} from './services/core/websocket/types';
56
62
  import TaskManager from './services/task/TaskManager';
57
63
  import WebCallingService from './services/WebCallingService';
64
+ import AnswerCallOnWebexService from './services/AnswerCallOnWebexService';
65
+ import WebexCrossClientService from './services/WebexCrossClientService';
66
+ import WxAppTelephonyMercurySync from './services/WxAppTelephonyMercurySync';
67
+ import {logWxAppSessionReadiness} from './services/wxAppDiagnosticLogging';
58
68
  import {
59
69
  ITask,
60
70
  TASK_EVENTS,
@@ -69,6 +79,7 @@ import {EntryPoint} from './services/EntryPoint';
69
79
  import {AddressBook} from './services/AddressBook';
70
80
  import {Queue} from './services/Queue';
71
81
  import {ApiAIAssistant} from './services/ApiAiAssistant';
82
+ import {UserPreference} from './services/UserPreference';
72
83
  import type {
73
84
  EntryPointListResponse,
74
85
  EntryPointSearchParams,
@@ -76,6 +87,13 @@ import type {
76
87
  ContactServiceQueueSearchParams,
77
88
  } from './types';
78
89
 
90
+ /**
91
+ * Typed allow-list of server-controlled websocket event names this SDK re-emits verbatim.
92
+ * Guards against emitting an arbitrary/unknown `data.type` supplied by the websocket payload.
93
+ * @ignore
94
+ */
95
+ const CC_EVENT_ALLOW_LIST = new Set<string>(Object.values(CC_EVENTS));
96
+
79
97
  /**
80
98
  * The main Contact Center plugin class that enables integration with Webex Contact Center.
81
99
  *
@@ -159,7 +177,7 @@ import type {
159
177
  *
160
178
  * // Login with browser-based calling
161
179
  * await cc.stationLogin({
162
- * teamId: profile.teams[0].teamId,
180
+ * teamId: profile.teams[0].id,
163
181
  * loginOption: 'BROWSER'
164
182
  * });
165
183
  *
@@ -230,6 +248,34 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
230
248
  */
231
249
  private webCallingService: WebCallingService;
232
250
 
251
+ /**
252
+ * REST telephony for wxApp thick-client answer (WXCC-6026)
253
+ * @private
254
+ */
255
+ private answerCallOnWebexService: AnswerCallOnWebexService;
256
+
257
+ /**
258
+ * usersub cross-client state for Webex App toast suppression
259
+ * @private
260
+ */
261
+ private webexCrossClientService: WebexCrossClientService;
262
+
263
+ /**
264
+ * Mercury telephony mute sync for wxApp thick-client answer
265
+ * @private
266
+ */
267
+ private wxAppTelephonyMercurySync: WxAppTelephonyMercurySync;
268
+
269
+ /** True when wxApp mute sync opened Mercury (Extension/DN-only profiles). */
270
+ private wxAppMercuryConnectedByCc = false;
271
+
272
+ /** True when wxApp mute sync registered the Webex device. */
273
+ private wxAppDeviceRegisteredByCc = false;
274
+
275
+ private static readonly WXAPP_FALSE_PUBLISH_RETRY_DELAY_MS = 30_000;
276
+ private static readonly WXAPP_FALSE_PUBLISH_MAX_RETRIES = 3;
277
+ private wxAppFalsePublishRetryTimer?: ReturnType<typeof setTimeout>;
278
+
233
279
  /**
234
280
  * Core service managers for Contact Center operations
235
281
  * Includes agent, connection, and configuration services
@@ -262,6 +308,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
262
308
  */
263
309
  private metricsManager: MetricsManager;
264
310
 
311
+ /** Current login/relogin session used for agent-scoped wellness validation. */
312
+ private currentAgentSessionId?: string;
313
+
314
+ /** Registered-session cache for the system WellbeingBreak idle code. */
315
+ private wellbeingBreakIdleCode?: Entity;
316
+
317
+ /** Registration generation that owns the wellness idle-code cache and in-flight lookups. */
318
+ private wellnessRegistrationGeneration = 0;
319
+
320
+ private updateWellnessSession(agentSessionId?: string): void {
321
+ this.currentAgentSessionId = agentSessionId;
322
+ }
323
+
265
324
  /**
266
325
  * API instance for managing Webex Contact Center entry points
267
326
  * Provides functionality to fetch entry points with caching support
@@ -325,7 +384,38 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
325
384
  * });
326
385
  * ```
327
386
  */
328
- private queue: Queue;
387
+ public queue: Queue;
388
+
389
+ /**
390
+ * API instance for managing Webex Contact Center user preferences
391
+ * Provides functionality to get, create, update, and delete user preferences
392
+ * @type {UserPreference}
393
+ * @public
394
+ * @example
395
+ * ```typescript
396
+ * const cc = webex.cc;
397
+ * await cc.register();
398
+ * await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' });
399
+ *
400
+ * // Get user preferences for current user
401
+ * const preferences = await cc.userPreference.getUserPreference();
402
+ *
403
+ * // Create new user preferences
404
+ * const newPrefs = await cc.userPreference.createUserPreference({
405
+ * userId: 'user123',
406
+ * preferences: { e911Reminder: true }
407
+ * });
408
+ *
409
+ * // Update user preferences
410
+ * const updatedPrefs = await cc.userPreference.updateUserPreference('user123', {
411
+ * preferences: { e911Reminder: false }
412
+ * });
413
+ *
414
+ * // Delete user preferences
415
+ * await cc.userPreference.deleteUserPreference('user123');
416
+ * ```
417
+ */
418
+ public userPreference: UserPreference;
329
419
 
330
420
  /**
331
421
  * API instance for AI Assistant operations such as transcript controls.
@@ -357,6 +447,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
357
447
  this.$webex.once(READY, () => {
358
448
  // @ts-ignore
359
449
  this.$config = this.config;
450
+ this.validatePluginConfig();
360
451
 
361
452
  /**
362
453
  * This is used for handling the async requests by sending webex.request and wait for corresponding websocket event.
@@ -369,17 +460,25 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
369
460
  webex: this.$webex,
370
461
  connectionConfig: this.getConnectionConfig(),
371
462
  });
372
- this.services.webSocketManager.on('message', this.handleWebsocketMessage);
373
463
 
374
464
  this.webCallingService = new WebCallingService(this.$webex);
375
- this.apiAIAssistant = new ApiAIAssistant(this.$webex);
465
+ this.answerCallOnWebexService = new AnswerCallOnWebexService(this.$webex);
466
+ this.webexCrossClientService = new WebexCrossClientService(this.$webex);
467
+ this.wxAppTelephonyMercurySync = new WxAppTelephonyMercurySync(this.$webex);
468
+ this.apiAIAssistant = new ApiAIAssistant(this.$webex, () => ({
469
+ isWellnessBreakEnabled: this.agentConfig?.isWellnessBreakEnabled === true,
470
+ agentId: this.agentConfig?.agentId,
471
+ agentSessionId: this.currentAgentSessionId,
472
+ }));
376
473
  this.metricsManager = MetricsManager.getInstance({webex: this.$webex});
377
474
  this.taskManager = TaskManager.getTaskManager(
378
475
  this.apiAIAssistant,
379
476
  this.services.contact,
380
477
  this.webCallingService,
381
- this.services.webSocketManager
478
+ this.services.webSocketManager,
479
+ this.services.rtdWebSocketManager
382
480
  );
481
+ this.taskManager.setAnswerCallOnWebexService(this.answerCallOnWebexService);
383
482
  this.incomingTaskListener();
384
483
 
385
484
  // Initialize API instances
@@ -387,6 +486,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
387
486
  this.entryPoint = new EntryPoint(this.$webex);
388
487
  this.addressBook = new AddressBook(this.$webex, () => this.agentConfig?.addressBookId);
389
488
  this.queue = new Queue(this.$webex);
489
+ this.userPreference = new UserPreference(this.$webex, () => this.agentConfig?.agentId);
390
490
  LoggerProxy.initialize(this.$webex.logger);
391
491
  });
392
492
  }
@@ -411,6 +511,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
411
511
  this.trigger(TASK_EVENTS.TASK_HYDRATE, task);
412
512
  };
413
513
 
514
+ /**
515
+ * Handles multi-login hydrate events for SDK instances without Mobius registration
516
+ * @private
517
+ * @param {ITask} task The task object associated with the multi-login hydrate
518
+ */
519
+ private handleTaskMultiLoginHydrate = (task: ITask) => {
520
+ // @ts-ignore
521
+ this.trigger(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, task);
522
+ };
523
+
414
524
  /**
415
525
  * Handles task merged events when tasks are combined eg: EPDN merge/transfer
416
526
  * @private
@@ -431,8 +541,127 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
431
541
  this.trigger(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, task);
432
542
  };
433
543
 
434
- private handleRTDWebsocketMessage = (payload: string) => {
435
- this.taskManager.handleRealtimeWebsocketEvent(payload);
544
+ private trackInvalidWellnessEvent(reason: string): void {
545
+ this.metricsManager.trackEvent(
546
+ METRIC_EVENT_NAMES.AI_ASSISTANT_WELLNESS_EVENT_INVALID,
547
+ {reason},
548
+ ['operational']
549
+ );
550
+ }
551
+
552
+ private normalizeWellnessBreakEvent(payload: Record<string, unknown>): WellnessBreakEvent | null {
553
+ if (this.agentConfig?.isWellnessBreakEnabled !== true) {
554
+ this.trackInvalidWellnessEvent('wellness_disabled');
555
+
556
+ return null;
557
+ }
558
+
559
+ const notificationData = isRecord(payload.data) ? payload.data : null;
560
+ const notificationDetails =
561
+ notificationData && isRecord(notificationData.notifDetails)
562
+ ? notificationData.notifDetails
563
+ : null;
564
+ const sessionData =
565
+ notificationData && isRecord(notificationData.data) ? notificationData.data : null;
566
+ const actionEvent = notificationDetails?.actionEvent;
567
+
568
+ if (
569
+ payload.type !== WELLNESS_BREAK_HANDLER ||
570
+ notificationData?.notifType !== WELLNESS_BREAK_HANDLER ||
571
+ typeof actionEvent !== 'string'
572
+ ) {
573
+ this.trackInvalidWellnessEvent('malformed_wellness_event');
574
+
575
+ return null;
576
+ }
577
+
578
+ if (
579
+ !Object.values(WELLNESS_BREAK_NOTIFICATION_ACTIONS).includes(
580
+ actionEvent as WellnessBreakNotificationAction
581
+ )
582
+ ) {
583
+ this.trackInvalidWellnessEvent('unknown_wellness_action');
584
+
585
+ return null;
586
+ }
587
+
588
+ const agentId = notificationData.agentId;
589
+ const eventOrgId = notificationData.orgId;
590
+ const sessionOrgId = sessionData?.orgId;
591
+ const envelopeOrgId = payload.orgId;
592
+ const agentSessionId = sessionData?.agentSessionId;
593
+ const currentOrgId = this.$webex.credentials.getOrgId();
594
+
595
+ if (
596
+ typeof agentId !== 'string' ||
597
+ typeof eventOrgId !== 'string' ||
598
+ typeof sessionOrgId !== 'string' ||
599
+ typeof envelopeOrgId !== 'string' ||
600
+ typeof agentSessionId !== 'string' ||
601
+ !agentId ||
602
+ !eventOrgId ||
603
+ !sessionOrgId ||
604
+ !envelopeOrgId ||
605
+ !agentSessionId
606
+ ) {
607
+ this.trackInvalidWellnessEvent('missing_wellness_identity');
608
+
609
+ return null;
610
+ }
611
+
612
+ if (
613
+ agentId !== this.agentConfig?.agentId ||
614
+ eventOrgId !== currentOrgId ||
615
+ sessionOrgId !== currentOrgId ||
616
+ envelopeOrgId !== currentOrgId
617
+ ) {
618
+ this.trackInvalidWellnessEvent('mismatched_wellness_identity');
619
+
620
+ return null;
621
+ }
622
+
623
+ const interactionValue = sessionData.interactionId ?? sessionData.InteractionId;
624
+ const actionText = notificationDetails.actionText;
625
+ const trackingId = payload.trackingId;
626
+
627
+ return {
628
+ agentId,
629
+ orgId: currentOrgId,
630
+ agentSessionId,
631
+ actionEvent: actionEvent as WellnessBreakNotificationAction,
632
+ ...(typeof actionText === 'string' ? {actionText} : {}),
633
+ ...(typeof interactionValue === 'string' ? {interactionId: interactionValue} : {}),
634
+ ...(typeof trackingId === 'string' ? {trackingId} : {}),
635
+ };
636
+ }
637
+
638
+ private handleWellnessBreakWebsocketPayload(payload: Record<string, unknown>): boolean {
639
+ if (payload.type !== WELLNESS_BREAK_HANDLER) {
640
+ return false;
641
+ }
642
+
643
+ const wellnessEvent = this.normalizeWellnessBreakEvent(payload);
644
+ if (wellnessEvent) {
645
+ // @ts-ignore - WebexPlugin emit is available at runtime but absent from its declaration.
646
+ this.emit(CC_EVENTS.WELLNESS_BREAK, wellnessEvent);
647
+ }
648
+
649
+ return true;
650
+ }
651
+
652
+ private handleRTDWebsocketMessage = (event: string) => {
653
+ let payload: unknown;
654
+ try {
655
+ payload = JSON.parse(event);
656
+ } catch {
657
+ // TaskManager retains its existing malformed RTD message handling.
658
+ }
659
+
660
+ if (isRecord(payload) && this.handleWellnessBreakWebsocketPayload(payload)) {
661
+ return;
662
+ }
663
+
664
+ this.taskManager.handleRealtimeWebsocketEvent(event);
436
665
  };
437
666
 
438
667
  /**
@@ -443,6 +672,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
443
672
  private incomingTaskListener() {
444
673
  this.taskManager.on(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
445
674
  this.taskManager.on(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
675
+ this.taskManager.on(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
446
676
  this.taskManager.on(TASK_EVENTS.TASK_MERGED, this.handleTaskMerged);
447
677
  this.taskManager.on(
448
678
  TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
@@ -478,16 +708,18 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
478
708
  *
479
709
  * console.log('Agent ID:', profile.agentId);
480
710
  * console.log('Default DN:', profile.defaultDn);
481
- * console.log('Teams:', profile.teams.map(t => t.teamId));
711
+ * console.log('Teams:', profile.teams.map(t => t.id));
482
712
  * console.log('WebRTC Enabled:', profile.webRtcEnabled);
483
713
  * console.log('Supported Login Options:', profile.loginVoiceOptions);
484
714
  *
485
715
  * // Now you can proceed with station login, state changes, etc.
486
- * await cc.stationLogin({ teamId: profile.teams[0].teamId, loginOption: 'BROWSER' });
716
+ * await cc.stationLogin({ teamId: profile.teams[0].id, loginOption: 'BROWSER' });
487
717
  * ```
488
718
  */
489
719
  public async register(): Promise<Profile> {
490
- LoggerProxy.info('Starting CC SDK registration', {
720
+ this.wellnessRegistrationGeneration += 1;
721
+ this.wellbeingBreakIdleCode = undefined;
722
+ LoggerProxy.log('Starting CC SDK registration', {
491
723
  module: CC_FILE,
492
724
  method: METHODS.REGISTER,
493
725
  });
@@ -497,6 +729,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
497
729
  METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_FAILED,
498
730
  ]);
499
731
  this.setupEventListeners();
732
+ this.services.webSocketManager.on('message', this.handleWebsocketMessage);
500
733
 
501
734
  const resp = await this.connectWebsocket();
502
735
  // Ensure 'dn' is always populated from 'defaultDn'
@@ -515,7 +748,12 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
515
748
  method: METHODS.REGISTER,
516
749
  });
517
750
 
518
- return resp;
751
+ // Registration can silently restore a station session without emitting a
752
+ // relogin event. Project the active session for consumers that must
753
+ // recover session-scoped state after a page refresh.
754
+ return resp.isAgentLoggedIn && this.currentAgentSessionId
755
+ ? {...resp, agentSessionId: this.currentAgentSessionId}
756
+ : resp;
519
757
  } catch (error) {
520
758
  this.metricsManager.trackEvent(
521
759
  METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_FAILED,
@@ -568,14 +806,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
568
806
 
569
807
  */
570
808
  public async deregister(): Promise<void> {
809
+ this.wellnessRegistrationGeneration += 1;
810
+ this.wellbeingBreakIdleCode = undefined;
811
+ this.updateWellnessSession();
571
812
  try {
572
813
  this.metricsManager.timeEvent([
573
814
  METRIC_EVENT_NAMES.WEBSOCKET_DEREGISTER_SUCCESS,
574
815
  METRIC_EVENT_NAMES.WEBSOCKET_DEREGISTER_FAIL,
575
816
  ]);
576
817
 
818
+ this.taskManager.clearAISummaryState();
577
819
  this.taskManager.off(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
578
820
  this.taskManager.off(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
821
+ this.taskManager.off(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
579
822
  this.taskManager.off(
580
823
  TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
581
824
  this.handleCampaignPreviewReservation
@@ -586,6 +829,10 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
586
829
  this.services.rtdWebSocketManager.off('message', this.handleRTDWebsocketMessage);
587
830
  this.services.connectionService.off('connectionLost', this.handleConnectionLost);
588
831
 
832
+ const {publishError: wxAppPublishError} = await this.teardownWxAppLocalState({
833
+ clearInitFlag: true,
834
+ });
835
+
589
836
  if (
590
837
  this.agentConfig.webRtcEnabled &&
591
838
  this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER)
@@ -606,13 +853,22 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
606
853
  if (!this.services.webSocketManager.isSocketClosed) {
607
854
  this.services.webSocketManager.close(false, 'Unregistering the SDK');
608
855
  }
856
+ if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
857
+ this.services.rtdWebSocketManager.close(false, 'Unregistering the SDK');
858
+ }
609
859
 
610
860
  if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
611
861
  this.services.rtdWebSocketManager.close(false, 'Unregistering the RTD websocket');
612
862
  }
613
863
 
614
864
  // Clear any cached agent configuration
865
+ this.wellbeingBreakIdleCode = undefined;
615
866
  this.agentConfig = null;
867
+ this.updateWellnessSession();
868
+
869
+ if (wxAppPublishError) {
870
+ throw wxAppPublishError;
871
+ }
616
872
 
617
873
  LoggerProxy.log('Deregistered successfully', {
618
874
  module: CC_FILE,
@@ -676,6 +932,9 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
676
932
  * ```
677
933
  */
678
934
  public async getBuddyAgents(data: BuddyAgents): Promise<BuddyAgentsResponse> {
935
+ const {action, state, mediaType = 'telephony'} = data;
936
+ const buddyAgentState = state ?? (action === 'Transfer' ? AGENT_STATE_AVAILABLE : undefined);
937
+
679
938
  LoggerProxy.info('Fetching buddy agents', {
680
939
  module: CC_FILE,
681
940
  method: METHODS.GET_BUDDY_AGENTS,
@@ -686,15 +945,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
686
945
  METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_FAILED,
687
946
  ]);
688
947
  const resp = await this.services.agent.buddyAgents({
689
- data: {agentProfileId: this.agentConfig.agentProfileID, ...data},
948
+ data: {
949
+ agentProfileId: this.agentConfig.agentProfileID,
950
+ mediaType,
951
+ ...(buddyAgentState ? {state: buddyAgentState} : {}),
952
+ },
690
953
  });
691
954
 
692
955
  this.metricsManager.trackEvent(
693
956
  METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_SUCCESS,
694
957
  {
695
958
  ...MetricsManager.getCommonTrackingFieldForAQMResponse(resp),
696
- mediaType: data.mediaType,
697
- buddyAgentState: data.state,
959
+ mediaType,
960
+ buddyAgentState,
698
961
  buddyAgentCount: resp.data.agentList.length,
699
962
  },
700
963
  ['operational']
@@ -714,8 +977,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
714
977
  METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_FAILED,
715
978
  {
716
979
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failureResp),
717
- mediaType: data.mediaType,
718
- buddyAgentState: data.state,
980
+ mediaType,
981
+ buddyAgentState,
719
982
  },
720
983
  ['operational']
721
984
  );
@@ -731,7 +994,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
731
994
  * @private
732
995
  */
733
996
  private async connectWebsocket() {
734
- LoggerProxy.info('Connecting to websocket', {
997
+ LoggerProxy.log('Connecting to websocket', {
735
998
  module: CC_FILE,
736
999
  method: METHODS.CONNECT_WEBSOCKET,
737
1000
  });
@@ -751,19 +1014,49 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
751
1014
  method: METHODS.CONNECT_WEBSOCKET,
752
1015
  });
753
1016
 
1017
+ const configFlags: ConfigFlags = {
1018
+ isEndTaskEnabled: this.agentConfig.isEndTaskEnabled,
1019
+ isEndConsultEnabled: this.agentConfig.isEndConsultEnabled,
1020
+ webRtcEnabled: this.agentConfig.webRtcEnabled,
1021
+ autoWrapup: this.agentConfig.wrapUpData?.wrapUpProps?.autoWrapup ?? false,
1022
+ aiFeature: this.agentConfig.aiFeature,
1023
+ consultTransfer: {
1024
+ allowConsultToQueue: this.agentConfig.allowConsultToQueue,
1025
+ accessQueue: this.agentConfig.accessQueue,
1026
+ accessEntryPoint: this.agentConfig.accessEntryPoint,
1027
+ accessBuddyTeam: this.agentConfig.accessBuddyTeam,
1028
+ },
1029
+ enableWxBetterTogether: this.isWxBetterTogetherEnabled(),
1030
+ getWxAppUsersubPublished: () => this.webexCrossClientService.isAnswerCallsStateActive(),
1031
+ };
1032
+ this.taskManager.setConfigFlags(configFlags);
754
1033
  // TODO: Make profile a singleton to make it available throughout app/sdk so we dont need to inject info everywhere
755
1034
  this.taskManager.setWrapupData(this.agentConfig.wrapUpData);
756
1035
  this.taskManager.setAgentId(this.agentConfig.agentId);
1036
+ this.taskManager.setAgentName(this.agentConfig.agentName);
757
1037
  this.taskManager.setWebRtcEnabled(this.agentConfig.webRtcEnabled);
758
1038
  this.apiAIAssistant.setAIFeatureFlags(this.agentConfig.aiFeature);
1039
+ this.wellbeingBreakIdleCode = undefined;
1040
+ this.updateWellnessSession();
759
1041
 
760
- if (this.agentConfig.aiFeature?.realtimeTranscripts?.enable) {
1042
+ /**
1043
+ * RTD websocket currently supports realtime transcripts and suggested responses.
1044
+ * Extend this condition when additional AI RTD features are introduced.
1045
+ * Wellness break notifications also arrive on this websocket.
1046
+ */
1047
+ if (
1048
+ this.agentConfig.aiFeature?.realtimeTranscripts?.enable ||
1049
+ this.agentConfig.aiFeature?.suggestedResponses?.enable ||
1050
+ this.agentConfig.aiFeature?.generatedSummaries?.wrapUpSummariesEnabled === true ||
1051
+ this.agentConfig.aiFeature?.generatedSummaries?.consultTransferSummariesEnabled === true ||
1052
+ this.agentConfig.isWellnessBreakEnabled
1053
+ ) {
761
1054
  LoggerProxy.info('Connecting to RTD websocket', {
762
1055
  module: CC_FILE,
763
1056
  method: METHODS.CONNECT_WEBSOCKET,
764
1057
  });
765
1058
 
766
- await this.services.rtdWebSocketManager
1059
+ this.services.rtdWebSocketManager
767
1060
  .initWebSocket({
768
1061
  body: this.getConnectionConfig(),
769
1062
  resource: RTD_SUBSCRIBE_API,
@@ -776,7 +1069,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
776
1069
  this.services.rtdWebSocketManager.on('message', this.handleRTDWebsocketMessage);
777
1070
  })
778
1071
  .catch((error) => {
779
- LoggerProxy.error(`Error during RTD websocket setup: ${error}`, {
1072
+ LoggerProxy.error(`Error connecting to RTD websocket ${error}`, {
780
1073
  module: CC_FILE,
781
1074
  method: METHODS.CONNECT_WEBSOCKET,
782
1075
  });
@@ -785,7 +1078,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
785
1078
 
786
1079
  if (
787
1080
  this.agentConfig.webRtcEnabled &&
788
- this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER)
1081
+ this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER) &&
1082
+ !this.isWebRTCRegistrationDisabled()
789
1083
  ) {
790
1084
  try {
791
1085
  await this.$webex.internal.mercury.connect();
@@ -799,14 +1093,28 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
799
1093
  method: METHODS.CONNECT_WEBSOCKET,
800
1094
  });
801
1095
  }
1096
+ } else if (this.isWebRTCRegistrationDisabled()) {
1097
+ LoggerProxy.info(
1098
+ 'Skipping Mobius registration because disableWebRTCRegistration is enabled',
1099
+ {
1100
+ module: CC_FILE,
1101
+ method: METHODS.CONNECT_WEBSOCKET,
1102
+ }
1103
+ );
802
1104
  }
803
1105
 
804
1106
  if (this.$config && this.$config.allowAutomatedRelogin) {
805
1107
  await this.silentRelogin();
1108
+ } else {
1109
+ LoggerProxy.log('Skipping silent relogin: allowAutomatedRelogin is disabled', {
1110
+ module: CC_FILE,
1111
+ method: METHODS.CONNECT_WEBSOCKET,
1112
+ });
806
1113
  }
807
1114
 
808
1115
  return this.agentConfig;
809
1116
  } catch (error) {
1117
+ this.updateWellnessSession();
810
1118
  LoggerProxy.error(`Error during register: ${error}`, {
811
1119
  module: CC_FILE,
812
1120
  method: METHODS.CONNECT_WEBSOCKET,
@@ -845,36 +1153,48 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
845
1153
  * ```
846
1154
  */
847
1155
  public async stationLogin(data: AgentLogin): Promise<StationLoginResponse> {
848
- LoggerProxy.info('Starting agent station login', {
849
- module: CC_FILE,
850
- method: METHODS.STATION_LOGIN,
851
- });
1156
+ const loggerContext = {module: CC_FILE, method: METHODS.STATION_LOGIN};
1157
+
852
1158
  try {
1159
+ LoggerProxy.log(
1160
+ `Starting agent station login | loginOption: ${data?.loginOption} teamId: ${data?.teamId}`,
1161
+ loggerContext
1162
+ );
853
1163
  this.metricsManager.timeEvent([
854
1164
  METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
855
1165
  METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
856
1166
  ]);
857
1167
 
858
1168
  const dialPlanEntries = this.agentConfig?.dialPlan?.dialPlanEntity ?? [];
859
- if (
860
- data.loginOption === LoginOption.AGENT_DN &&
861
- !isValidDialNumber(data.dialNumber, dialPlanEntries)
862
- ) {
863
- const error = new Error('INVALID_DIAL_NUMBER');
864
- // @ts-ignore - adding custom key to the error object
865
- error.details = {data: {reason: 'INVALID_DIAL_NUMBER'}} as Failure;
1169
+ if (data.loginOption === LoginOption.AGENT_DN) {
1170
+ LoggerProxy.log(
1171
+ `Validating dial number | dialPlanEnabled: ${!!this.agentConfig
1172
+ ?.dialPlan} | dialPlanEntryCount: ${dialPlanEntries.length}`,
1173
+ loggerContext
1174
+ );
866
1175
 
867
- throw error;
1176
+ if (!isValidDialNumber(data.dialNumber, dialPlanEntries)) {
1177
+ LoggerProxy.log(
1178
+ `Dial number validation failed | dialNumber: ${data.dialNumber} | dialPlanEntryCount: ${dialPlanEntries.length}`,
1179
+ loggerContext
1180
+ );
1181
+ const error = new Error('INVALID_DIAL_NUMBER');
1182
+ // @ts-ignore - adding custom key to the error object
1183
+ error.details = {data: {reason: 'INVALID_DIAL_NUMBER'}} as Failure;
1184
+
1185
+ throw error;
1186
+ }
868
1187
  }
869
1188
 
1189
+ const deviceId = this.getDeviceId(data.loginOption, data.dialNumber);
870
1190
  const loginResponse = await this.services.agent.stationLogin({
871
1191
  data: {
872
- dialNumber:
873
- data.loginOption === LoginOption.BROWSER ? this.agentConfig.agentId : data.dialNumber,
1192
+ // BROWSER DN must be webrtc-{agentId} so VPOP/RTMS do not treat a bare UUID as PSTN.
1193
+ dialNumber: data.loginOption === LoginOption.BROWSER ? deviceId : data.dialNumber,
874
1194
  teamId: data.teamId,
875
1195
  deviceType: data.loginOption,
876
1196
  isExtension: data.loginOption === LoginOption.EXTENSION,
877
- deviceId: this.getDeviceId(data.loginOption, data.dialNumber),
1197
+ deviceId,
878
1198
  roles: [AGENT],
879
1199
  teamName: EMPTY_STRING,
880
1200
  siteId: EMPTY_STRING,
@@ -883,12 +1203,29 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
883
1203
  },
884
1204
  });
885
1205
 
886
- if (this.agentConfig.webRtcEnabled && data.loginOption === LoginOption.BROWSER) {
1206
+ if (
1207
+ this.agentConfig.webRtcEnabled &&
1208
+ data.loginOption === LoginOption.BROWSER &&
1209
+ !this.isWebRTCRegistrationDisabled()
1210
+ ) {
887
1211
  await this.webCallingService.registerWebCallingLine();
1212
+ } else if (
1213
+ this.agentConfig.webRtcEnabled &&
1214
+ data.loginOption === LoginOption.BROWSER &&
1215
+ this.isWebRTCRegistrationDisabled()
1216
+ ) {
1217
+ LoggerProxy.info(
1218
+ 'Skipping web calling line registration because disableWebRTCRegistration is enabled',
1219
+ {
1220
+ module: CC_FILE,
1221
+ method: METHODS.STATION_LOGIN,
1222
+ }
1223
+ );
888
1224
  }
889
1225
 
890
1226
  const resp = await loginResponse;
891
1227
  const {channelsMap, ...loginData} = resp.data;
1228
+ this.updateWellnessSession(resp.data.agentSessionId);
892
1229
  this.agentConfig.currentTeamId = resp.data.teamId;
893
1230
  const response = {
894
1231
  ...loginData,
@@ -902,6 +1239,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
902
1239
  };
903
1240
 
904
1241
  this.webCallingService.setLoginOption(data.loginOption);
1242
+ if (this.agentConfig) {
1243
+ this.agentConfig.deviceType = data.loginOption;
1244
+ if (
1245
+ data.loginOption === LoginOption.AGENT_DN ||
1246
+ data.loginOption === LoginOption.EXTENSION
1247
+ ) {
1248
+ this.agentConfig.defaultDn = data.dialNumber;
1249
+ this.agentConfig.dn = data.dialNumber;
1250
+ }
1251
+ }
905
1252
  this.metricsManager.trackEvent(
906
1253
  METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
907
1254
  {
@@ -923,6 +1270,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
923
1270
  }
924
1271
  );
925
1272
 
1273
+ await this.ensureWxAppPostStationLogin();
1274
+
926
1275
  return response;
927
1276
  } catch (error) {
928
1277
  const failure = error.details as Failure;
@@ -991,6 +1340,9 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
991
1340
  this.webCallingService.deregisterWebCallingLine();
992
1341
  }
993
1342
 
1343
+ await this.teardownWxAppLocalState();
1344
+ this.updateWellnessSession();
1345
+
994
1346
  LoggerProxy.log(`Agent station logout completed successfully`, {
995
1347
  module: CC_FILE,
996
1348
  method: METHODS.STATION_LOGOUT,
@@ -1091,7 +1443,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1091
1443
  {
1092
1444
  ...MetricsManager.getCommonTrackingFieldForAQMResponse(agentStatusResponse),
1093
1445
  requestedState: data.state,
1094
- teamId: this.agentConfig?.teams[0]?.teamId ?? EMPTY_STRING,
1446
+ teamId: this.agentConfig?.teams[0]?.id ?? EMPTY_STRING,
1095
1447
  status: agentStatusResponse.data?.status,
1096
1448
  subStatus: agentStatusResponse.data?.subStatus,
1097
1449
  auxCodeId: data.auxCodeId,
@@ -1127,6 +1479,77 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1127
1479
  }
1128
1480
  }
1129
1481
 
1482
+ /**
1483
+ * Returns the system-managed `WellbeingBreak` idle code for the current registration.
1484
+ * The value is fetched outside Desktop Profile filtering and cached until deregistration.
1485
+ * @returns The normalized system idle-code entity
1486
+ * @throws Structured Contact Center error when wellness is disabled or the code is unavailable
1487
+ * @example
1488
+ * const idleCode = await webex.cc.getWellbeingBreakIdleCode();
1489
+ * @public
1490
+ */
1491
+ public async getWellbeingBreakIdleCode(): Promise<Entity> {
1492
+ const method = METHODS.GET_WELLBEING_BREAK_IDLE_CODE;
1493
+ try {
1494
+ if (!this.agentConfig?.isWellnessBreakEnabled) {
1495
+ const validationError = new Error('WELLNESS_BREAK_NOT_ENABLED') as GenericError;
1496
+ validationError.details = {
1497
+ type: 'SDK_VALIDATION_ERROR',
1498
+ orgId: this.$webex.credentials.getOrgId(),
1499
+ trackingId: EMPTY_STRING,
1500
+ data: {reason: 'WELLNESS_BREAK_NOT_ENABLED'},
1501
+ };
1502
+ throw validationError;
1503
+ }
1504
+
1505
+ if (this.wellbeingBreakIdleCode) {
1506
+ return this.wellbeingBreakIdleCode;
1507
+ }
1508
+
1509
+ this.metricsManager.timeEvent([
1510
+ METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS,
1511
+ METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED,
1512
+ ]);
1513
+ const registrationGeneration = this.wellnessRegistrationGeneration;
1514
+ const orgId = this.$webex.credentials.getOrgId();
1515
+ const idleCode = await this.services.config.getWellbeingBreakIdleCode(orgId);
1516
+
1517
+ if (
1518
+ registrationGeneration !== this.wellnessRegistrationGeneration ||
1519
+ orgId !== this.$webex.credentials.getOrgId() ||
1520
+ !this.agentConfig?.isWellnessBreakEnabled
1521
+ ) {
1522
+ const staleRegistrationError = new Error(
1523
+ 'WELLNESS_BREAK_REGISTRATION_CHANGED'
1524
+ ) as GenericError;
1525
+ staleRegistrationError.details = {
1526
+ type: 'SDK_VALIDATION_ERROR',
1527
+ orgId,
1528
+ trackingId: EMPTY_STRING,
1529
+ data: {reason: 'WELLNESS_BREAK_REGISTRATION_CHANGED'},
1530
+ };
1531
+ throw staleRegistrationError;
1532
+ }
1533
+
1534
+ this.wellbeingBreakIdleCode = idleCode;
1535
+ this.metricsManager.trackEvent(
1536
+ METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS,
1537
+ {},
1538
+ ['operational']
1539
+ );
1540
+
1541
+ return idleCode;
1542
+ } catch (error) {
1543
+ this.metricsManager.trackEvent(
1544
+ METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED,
1545
+ {reason: error instanceof Error ? error.message : UNKNOWN_ERROR},
1546
+ ['operational']
1547
+ );
1548
+ const {error: detailedError} = getErrorDetails(error, method, CC_FILE);
1549
+ throw detailedError;
1550
+ }
1551
+ }
1552
+
1130
1553
  /**
1131
1554
  * Processes incoming websocket messages and emits corresponding events
1132
1555
  * Handles various event types including agent state changes, login events,
@@ -1136,8 +1559,14 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1136
1559
  */
1137
1560
  private handleWebsocketMessage = (event: string) => {
1138
1561
  const eventData = JSON.parse(event);
1139
- // Re-emit all the events related to agent except keep-alives
1140
- if (!eventData.keepalive && eventData.data && eventData.data.type) {
1562
+ // Re-emit all the events related to agent except keep-alives. Only event names present in
1563
+ // the typed CC_EVENTS allow-list are emitted; unknown/server-injected types are ignored.
1564
+ if (
1565
+ !eventData.keepalive &&
1566
+ eventData.data &&
1567
+ eventData.data.type &&
1568
+ CC_EVENT_ALLOW_LIST.has(eventData.data.type)
1569
+ ) {
1141
1570
  // @ts-ignore
1142
1571
  this.emit(eventData.data.type, eventData.data);
1143
1572
  }
@@ -1165,8 +1594,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1165
1594
  ]);
1166
1595
  }
1167
1596
 
1597
+ if (this.handleWellnessBreakWebsocketPayload(eventData)) {
1598
+ return;
1599
+ }
1600
+
1168
1601
  switch (eventData.type) {
1169
1602
  case CC_EVENTS.AGENT_MULTI_LOGIN:
1603
+ if (
1604
+ eventData.data?.type === 'AgentMultiLoginCloseSession' &&
1605
+ (!eventData.data.agentSessionId ||
1606
+ eventData.data.agentSessionId === this.currentAgentSessionId)
1607
+ ) {
1608
+ this.updateWellnessSession();
1609
+ }
1170
1610
  // @ts-ignore
1171
1611
  this.emit(AGENT_EVENTS.AGENT_MULTI_LOGIN, eventData.data);
1172
1612
  break;
@@ -1185,6 +1625,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1185
1625
  switch (eventData.data.type) {
1186
1626
  case CC_EVENTS.AGENT_STATION_LOGIN_SUCCESS: {
1187
1627
  const {channelsMap, ...loginData} = eventData.data;
1628
+ this.updateWellnessSession(loginData.agentSessionId);
1188
1629
  const stationLoginData = {
1189
1630
  ...loginData,
1190
1631
  mmProfile: {
@@ -1200,23 +1641,23 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1200
1641
  this.emit(AGENT_EVENTS.AGENT_STATION_LOGIN_SUCCESS, stationLoginData);
1201
1642
  break;
1202
1643
  }
1203
- case CC_EVENTS.AGENT_RELOGIN_SUCCESS:
1204
- {
1205
- const {channelsMap, ...loginData} = eventData.data;
1206
- const stationReLoginData = {
1207
- ...loginData,
1208
- mmProfile: {
1209
- chat: channelsMap.chat?.length,
1210
- email: channelsMap.email?.length,
1211
- social: channelsMap.social?.length,
1212
- telephony: channelsMap.telephony?.length,
1213
- },
1214
- notifsTrackingId: eventData.trackingId,
1215
- };
1216
- // @ts-ignore
1217
- this.emit(AGENT_EVENTS.AGENT_RELOGIN_SUCCESS, stationReLoginData);
1218
- }
1644
+ case CC_EVENTS.AGENT_RELOGIN_SUCCESS: {
1645
+ const {channelsMap, ...loginData} = eventData.data;
1646
+ this.updateWellnessSession(loginData.agentSessionId);
1647
+ const stationReLoginData = {
1648
+ ...loginData,
1649
+ mmProfile: {
1650
+ chat: channelsMap.chat?.length,
1651
+ email: channelsMap.email?.length,
1652
+ social: channelsMap.social?.length,
1653
+ telephony: channelsMap.telephony?.length,
1654
+ },
1655
+ notifsTrackingId: eventData.trackingId,
1656
+ };
1657
+ // @ts-ignore
1658
+ this.emit(AGENT_EVENTS.AGENT_RELOGIN_SUCCESS, stationReLoginData);
1219
1659
  break;
1660
+ }
1220
1661
  case CC_EVENTS.AGENT_STATE_CHANGE_SUCCESS:
1221
1662
  // @ts-ignore
1222
1663
  this.emit(AGENT_EVENTS.AGENT_STATE_CHANGE_SUCCESS, eventData.data);
@@ -1230,6 +1671,12 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1230
1671
  this.emit(AGENT_EVENTS.AGENT_STATION_LOGIN_FAILED, eventData.data);
1231
1672
  break;
1232
1673
  case CC_EVENTS.AGENT_LOGOUT_SUCCESS:
1674
+ if (
1675
+ !eventData.data.agentSessionId ||
1676
+ eventData.data.agentSessionId === this.currentAgentSessionId
1677
+ ) {
1678
+ this.updateWellnessSession();
1679
+ }
1233
1680
  // @ts-ignore
1234
1681
  this.emit(AGENT_EVENTS.AGENT_LOGOUT_SUCCESS, eventData.data);
1235
1682
  break;
@@ -1246,6 +1693,617 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1246
1693
  }
1247
1694
  };
1248
1695
 
1696
+ /**
1697
+ * Checks whether Mobius/WebRTC registration should be skipped by config
1698
+ * @returns {boolean} True when browser WebRTC registration is disabled
1699
+ * @private
1700
+ */
1701
+ private isWebRTCRegistrationDisabled(): boolean {
1702
+ return this.$config?.disableWebRTCRegistration === true;
1703
+ }
1704
+
1705
+ /**
1706
+ * Validates contact-center plugin configuration before service initialization
1707
+ * @private
1708
+ */
1709
+ private validatePluginConfig(): void {
1710
+ if (
1711
+ this.$config?.disableWebRTCRegistration === true &&
1712
+ this.$config?.allowMultiLogin === false
1713
+ ) {
1714
+ throw new Error(
1715
+ 'Invalid Contact Center configuration: disableWebRTCRegistration cannot be true when allowMultiLogin is false. Enable allowMultiLogin or allow WebRTC registration so an SDK instance can receive Mobius/WebRTC task events.'
1716
+ );
1717
+ }
1718
+ }
1719
+
1720
+ /**
1721
+ * Whether wxApp thick-client answer is enabled for this SDK instance.
1722
+ * @public
1723
+ */
1724
+ public isWxBetterTogetherEnabled(): boolean {
1725
+ return this.$config?.enableWxBetterTogether === true;
1726
+ }
1727
+
1728
+ /**
1729
+ * Current station login option (Extension, Dial Number, or Browser/Desktop).
1730
+ * Prefer webCallingService — updated on every stationLogin before wxApp hooks run.
1731
+ * @private
1732
+ */
1733
+ private getCurrentStationLoginOption(): LoginOption | undefined {
1734
+ return this.webCallingService?.loginOption ?? this.agentConfig?.deviceType;
1735
+ }
1736
+
1737
+ /**
1738
+ * wxApp thick-client answer is supported for Extension and Dial Number station login only —
1739
+ * not Browser/Desktop (WebRTC) login.
1740
+ * @private
1741
+ */
1742
+ private assertWxAppStationLoginSupportedForEnable(): void {
1743
+ const loginOption = this.getCurrentStationLoginOption();
1744
+ if (loginOption !== LoginOption.EXTENSION && loginOption !== LoginOption.AGENT_DN) {
1745
+ throw new Error(
1746
+ 'setManageWebexCallingInWxcc requires EXTENSION or AGENT_DN station login. Complete station login before enabling.'
1747
+ );
1748
+ }
1749
+ }
1750
+
1751
+ /**
1752
+ * Clears runtime wxApp config after logout/deregister so stale flags do not apply on relogin.
1753
+ * @private
1754
+ */
1755
+ private resetEnableWxBetterTogetherConfig(): void {
1756
+ if (this.$config) {
1757
+ this.$config.enableWxBetterTogether = false;
1758
+ }
1759
+ this.taskManager.applyEnableWxBetterTogether(false);
1760
+ }
1761
+
1762
+ /**
1763
+ * Runtime enable/disable for wxApp Better Together (toast suppression + Mercury mute sync).
1764
+ *
1765
+ * **Phase 1 (WXCC-6026 / MMT): NOT part of the public host contract.**
1766
+ * Hosts must set `webexConfig.cc.enableWxBetterTogether` before `webex.init()` / `cc.register()`.
1767
+ * To change the flag after the SDK is initialized, re-init the SDK with the updated config.
1768
+ *
1769
+ * **Supported public read API:** {@link isWxBetterTogetherEnabled}.
1770
+ * **Supported telephony API:** unified `task.accept()`, `task.decline()`, `task.toggleMute()`,
1771
+ * `task.transmitDtmf()` — SDK routes wxApp calls internally when the init flag is active.
1772
+ *
1773
+ * **Production lifecycle does not call this method.** Station login, silent relogin, logout,
1774
+ * and deregister use {@link ensureWxAppPostStationLogin} and {@link teardownWxAppLocalState}
1775
+ * (usersub publish + Mercury subscribe/unsubscribe based on the init flag).
1776
+ *
1777
+ * This setter remains as a **private implementation** for Phase 2 runtime toggle and unit tests.
1778
+ * It updates `$config.enableWxBetterTogether`, TaskManager flags, cross-client usersub
1779
+ * (`answer-calls-on-wxcc`), and Mercury mute sync; rolls back config on publish/connect failure.
1780
+ *
1781
+ * @param enabled - `true` to enable wxApp answer + usersub suppression; `false` to disable and tear down.
1782
+ * @throws When enabling without login, on unsupported BROWSER station login, or when usersub/Mercury init fails.
1783
+ * @internal
1784
+ * @see enableWxBetterTogether (CCPluginConfig init flag)
1785
+ * @see ensureWxAppPostStationLogin
1786
+ * @see teardownWxAppLocalState
1787
+ */
1788
+ private async setManageWebexCallingInWxcc(enabled: boolean): Promise<void> {
1789
+ if (enabled && !this.$webex.internal.device?.userId) {
1790
+ throw new Error('Cannot publish answer-calls-on-wxcc: user is not logged in');
1791
+ }
1792
+
1793
+ if (enabled) {
1794
+ this.assertWxAppStationLoginSupportedForEnable();
1795
+ }
1796
+
1797
+ const previousEnabled = this.isWxBetterTogetherEnabled();
1798
+
1799
+ if (this.$config) {
1800
+ this.$config.enableWxBetterTogether = enabled;
1801
+ }
1802
+
1803
+ this.taskManager.applyEnableWxBetterTogether(enabled);
1804
+
1805
+ let publishedEnable = false;
1806
+
1807
+ try {
1808
+ if (enabled) {
1809
+ await this.ensureWxAppMercuryAndSubscribe();
1810
+ await this.publishAnswerOnWebexCrossClientState(true);
1811
+ publishedEnable = true;
1812
+ this.taskManager.syncWxAppMuteFromCallDetailsForAllTasks();
1813
+ this.taskManager.refreshWxAppOfferObservabilityForAllTasks();
1814
+ } else {
1815
+ await this.publishAnswerOnWebexCrossClientState(false);
1816
+ this.wxAppTelephonyMercurySync.unsubscribe();
1817
+ await this.releaseWxAppMercuryResources();
1818
+ }
1819
+ } catch (error) {
1820
+ if (publishedEnable) {
1821
+ await this.revertWxAppCrossClientPublish();
1822
+ } else if (enabled) {
1823
+ this.wxAppTelephonyMercurySync.unsubscribe();
1824
+ await this.releaseWxAppMercuryResources();
1825
+ }
1826
+
1827
+ if (this.$config) {
1828
+ this.$config.enableWxBetterTogether = previousEnabled;
1829
+ }
1830
+ this.taskManager.applyEnableWxBetterTogether(previousEnabled);
1831
+ throw error;
1832
+ }
1833
+ }
1834
+
1835
+ private getWxAppTelephonyTaskType(): 'Voice' | 'WebRTC' | 'unknown' {
1836
+ const loginOption = this.getCurrentStationLoginOption();
1837
+
1838
+ if (loginOption === LoginOption.BROWSER) {
1839
+ return 'WebRTC';
1840
+ }
1841
+
1842
+ if (loginOption === LoginOption.EXTENSION || loginOption === LoginOption.AGENT_DN) {
1843
+ return 'Voice';
1844
+ }
1845
+
1846
+ return 'unknown';
1847
+ }
1848
+
1849
+ private async ensureWxAppPostStationLogin(): Promise<void> {
1850
+ const loginOption = this.getCurrentStationLoginOption();
1851
+ const flagEnabled = this.isWxBetterTogetherEnabled();
1852
+ const telephonyTaskType = this.getWxAppTelephonyTaskType();
1853
+
1854
+ if (loginOption === LoginOption.BROWSER) {
1855
+ if (flagEnabled) {
1856
+ logWxAppSessionReadiness({
1857
+ enableWxBetterTogether: true,
1858
+ loginOption,
1859
+ wxAppHooksApplied: false,
1860
+ usersubPublished: false,
1861
+ mercurySubscribed: false,
1862
+ telephonyTaskType,
1863
+ skipReason: 'unsupported_browser_login',
1864
+ });
1865
+
1866
+ this.metricsManager.trackEvent(
1867
+ METRIC_EVENT_NAMES.WXAPP_SESSION_SKIPPED,
1868
+ {
1869
+ loginOption,
1870
+ enableWxBetterTogether: true,
1871
+ skipReason: 'unsupported_browser_login',
1872
+ },
1873
+ ['operational', 'behavioral']
1874
+ );
1875
+ }
1876
+
1877
+ return;
1878
+ }
1879
+
1880
+ let publishedEnable = false;
1881
+ let wxAppInitStage: 'mercury' | 'publish' = 'mercury';
1882
+
1883
+ try {
1884
+ if (flagEnabled) {
1885
+ this.metricsManager.timeEvent([
1886
+ METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS,
1887
+ METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
1888
+ ]);
1889
+
1890
+ await this.ensureWxAppMercuryAndSubscribe();
1891
+ wxAppInitStage = 'publish';
1892
+ await this.publishAnswerOnWebexCrossClientState(true);
1893
+ publishedEnable = true;
1894
+ this.taskManager.syncWxAppMuteFromCallDetailsForAllTasks();
1895
+ this.taskManager.refreshWxAppOfferObservabilityForAllTasks();
1896
+
1897
+ const mercurySubscribed = this.wxAppTelephonyMercurySync.isSubscribed();
1898
+ const usersubPublished = this.webexCrossClientService.isAnswerCallsStateActive();
1899
+
1900
+ logWxAppSessionReadiness({
1901
+ enableWxBetterTogether: true,
1902
+ loginOption,
1903
+ wxAppHooksApplied: true,
1904
+ usersubPublished,
1905
+ mercurySubscribed,
1906
+ telephonyTaskType,
1907
+ });
1908
+
1909
+ const sessionInitReady = mercurySubscribed && usersubPublished;
1910
+
1911
+ if (sessionInitReady) {
1912
+ this.metricsManager.trackEvent(
1913
+ METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS,
1914
+ {
1915
+ loginOption,
1916
+ enableWxBetterTogether: true,
1917
+ usersubPublished,
1918
+ mercurySubscribed,
1919
+ telephonyTaskType,
1920
+ },
1921
+ ['operational', 'behavioral']
1922
+ );
1923
+ } else {
1924
+ this.metricsManager.trackEvent(
1925
+ METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
1926
+ {
1927
+ loginOption,
1928
+ enableWxBetterTogether: true,
1929
+ usersubPublished,
1930
+ mercurySubscribed,
1931
+ telephonyTaskType,
1932
+ skipReason: !usersubPublished ? 'usersub_not_published' : 'mercury_not_subscribed',
1933
+ },
1934
+ ['operational', 'behavioral']
1935
+ );
1936
+ }
1937
+ } else {
1938
+ await this.ensureWxAppDeviceRegistered();
1939
+ await this.publishAnswerOnWebexCrossClientState(false, {force: true});
1940
+ this.webexCrossClientService.teardown();
1941
+ this.wxAppTelephonyMercurySync.unsubscribe();
1942
+ await this.releaseWxAppMercuryResources();
1943
+
1944
+ logWxAppSessionReadiness({
1945
+ enableWxBetterTogether: false,
1946
+ loginOption,
1947
+ wxAppHooksApplied: true,
1948
+ usersubPublished: false,
1949
+ mercurySubscribed: false,
1950
+ telephonyTaskType,
1951
+ skipReason: 'flag_disabled',
1952
+ });
1953
+
1954
+ this.metricsManager.trackEvent(
1955
+ METRIC_EVENT_NAMES.WXAPP_SESSION_SKIPPED,
1956
+ {loginOption, enableWxBetterTogether: false, skipReason: 'flag_disabled'},
1957
+ ['operational', 'behavioral']
1958
+ );
1959
+ }
1960
+ } catch (error) {
1961
+ LoggerProxy.error(`Failed to initialize wxApp post-station-login: ${error}`, {
1962
+ module: CC_FILE,
1963
+ method: METHODS.STATION_LOGIN,
1964
+ });
1965
+
1966
+ if (flagEnabled) {
1967
+ const sessionInitFailureReason =
1968
+ wxAppInitStage === 'mercury' ? 'mercury_subscribe_failed' : 'publish_failed';
1969
+
1970
+ this.metricsManager.trackEvent(
1971
+ METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
1972
+ {
1973
+ loginOption,
1974
+ enableWxBetterTogether: true,
1975
+ usersubPublished: this.webexCrossClientService.isAnswerCallsStateActive(),
1976
+ mercurySubscribed: this.wxAppTelephonyMercurySync.isSubscribed(),
1977
+ telephonyTaskType,
1978
+ skipReason: sessionInitFailureReason,
1979
+ error: error instanceof Error ? error.toString() : String(error),
1980
+ },
1981
+ ['operational', 'behavioral']
1982
+ );
1983
+
1984
+ logWxAppSessionReadiness({
1985
+ enableWxBetterTogether: true,
1986
+ loginOption,
1987
+ wxAppHooksApplied: false,
1988
+ usersubPublished: this.webexCrossClientService.isAnswerCallsStateActive(),
1989
+ mercurySubscribed: this.wxAppTelephonyMercurySync.isSubscribed(),
1990
+ telephonyTaskType,
1991
+ skipReason: sessionInitFailureReason,
1992
+ });
1993
+
1994
+ if (publishedEnable) {
1995
+ await this.revertWxAppCrossClientPublish();
1996
+ } else {
1997
+ this.wxAppTelephonyMercurySync.unsubscribe();
1998
+ await this.releaseWxAppMercuryResources();
1999
+ }
2000
+ this.resetEnableWxBetterTogetherConfig();
2001
+ } else {
2002
+ this.scheduleForcedFalsePublishRetry();
2003
+ }
2004
+ }
2005
+ }
2006
+
2007
+ private clearWxAppFalsePublishRetryTimer(): void {
2008
+ if (this.wxAppFalsePublishRetryTimer) {
2009
+ clearTimeout(this.wxAppFalsePublishRetryTimer);
2010
+ this.wxAppFalsePublishRetryTimer = undefined;
2011
+ }
2012
+ }
2013
+
2014
+ private scheduleForcedFalsePublishRetry(retryAttempt = 0): void {
2015
+ this.scheduleFalsePublishRetry(retryAttempt, {requireDisabled: true});
2016
+ }
2017
+
2018
+ /**
2019
+ * Retries compensating false publish after rollback failure, regardless of enabled flag.
2020
+ * @private
2021
+ */
2022
+ private scheduleCompensatingFalsePublishRetry(retryAttempt = 0): void {
2023
+ this.scheduleFalsePublishRetry(retryAttempt);
2024
+ }
2025
+
2026
+ private scheduleFalsePublishRetry(
2027
+ retryAttempt = 0,
2028
+ options: {requireDisabled?: boolean} = {}
2029
+ ): void {
2030
+ if (retryAttempt >= ContactCenter.WXAPP_FALSE_PUBLISH_MAX_RETRIES) {
2031
+ return;
2032
+ }
2033
+
2034
+ if (options?.requireDisabled && this.isWxBetterTogetherEnabled()) {
2035
+ return;
2036
+ }
2037
+
2038
+ this.clearWxAppFalsePublishRetryTimer();
2039
+
2040
+ this.wxAppFalsePublishRetryTimer = setTimeout(async () => {
2041
+ this.wxAppFalsePublishRetryTimer = undefined;
2042
+
2043
+ if (options?.requireDisabled && this.isWxBetterTogetherEnabled()) {
2044
+ return;
2045
+ }
2046
+
2047
+ if (
2048
+ !options?.requireDisabled &&
2049
+ this.isWxBetterTogetherEnabled() &&
2050
+ this.webexCrossClientService.isAnswerCallsStateActive()
2051
+ ) {
2052
+ return;
2053
+ }
2054
+
2055
+ try {
2056
+ await this.ensureWxAppDeviceRegistered();
2057
+ await this.publishAnswerOnWebexCrossClientState(false, {force: true});
2058
+ this.webexCrossClientService.teardown();
2059
+ this.wxAppTelephonyMercurySync.unsubscribe();
2060
+ await this.releaseWxAppMercuryResources();
2061
+ } catch (retryError) {
2062
+ LoggerProxy.error(
2063
+ `Failed to retry forced answer-calls-on-wxcc false publish: ${retryError}`,
2064
+ {
2065
+ module: CC_FILE,
2066
+ method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
2067
+ }
2068
+ );
2069
+ this.scheduleFalsePublishRetry(retryAttempt + 1, options);
2070
+ }
2071
+ }, ContactCenter.WXAPP_FALSE_PUBLISH_RETRY_DELAY_MS);
2072
+ }
2073
+
2074
+ /**
2075
+ * Best-effort rollback when usersub true was published but wxApp setup did not complete.
2076
+ * @private
2077
+ */
2078
+ private async revertWxAppCrossClientPublish(): Promise<void> {
2079
+ try {
2080
+ await this.publishAnswerOnWebexCrossClientState(false, {force: true});
2081
+ } catch (error) {
2082
+ LoggerProxy.error(
2083
+ `Failed to publish compensating answer-calls-on-wxcc false during wxApp rollback: ${error}`,
2084
+ {
2085
+ module: CC_FILE,
2086
+ method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
2087
+ }
2088
+ );
2089
+ this.scheduleCompensatingFalsePublishRetry();
2090
+ }
2091
+
2092
+ this.webexCrossClientService.teardown();
2093
+ this.wxAppTelephonyMercurySync.unsubscribe();
2094
+ await this.releaseWxAppMercuryResources();
2095
+ }
2096
+
2097
+ private async ensureWxAppDeviceRegistered(): Promise<void> {
2098
+ const device = this.$webex.internal.device;
2099
+
2100
+ if (device && !device.registered && device.register) {
2101
+ await device.register();
2102
+ this.wxAppDeviceRegisteredByCc = true;
2103
+ LoggerProxy.log('WxApp: device registered for cross-client publish', {
2104
+ module: CC_FILE,
2105
+ method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
2106
+ });
2107
+ }
2108
+ }
2109
+
2110
+ private async ensureWxAppMercuryConnected(): Promise<void> {
2111
+ const mercury = this.$webex.internal.mercury;
2112
+
2113
+ await this.ensureWxAppDeviceRegistered();
2114
+
2115
+ if (mercury && !mercury.connected) {
2116
+ await mercury.connect();
2117
+ this.wxAppMercuryConnectedByCc = true;
2118
+ LoggerProxy.log('WxApp mute sync: mercury connected', {
2119
+ module: CC_FILE,
2120
+ method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
2121
+ });
2122
+ }
2123
+ }
2124
+
2125
+ /**
2126
+ * Disconnects Mercury and unregisters the device when wxApp mute sync opened them.
2127
+ * @private
2128
+ */
2129
+ private async releaseWxAppMercuryResources(): Promise<void> {
2130
+ const mercury = this.$webex.internal.mercury;
2131
+ const device = this.$webex.internal.device;
2132
+
2133
+ if (this.wxAppMercuryConnectedByCc && mercury?.connected) {
2134
+ try {
2135
+ await mercury.disconnect();
2136
+ this.wxAppMercuryConnectedByCc = false;
2137
+ LoggerProxy.log('WxApp mute sync: mercury disconnected', {
2138
+ module: CC_FILE,
2139
+ method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
2140
+ });
2141
+ } catch (error) {
2142
+ LoggerProxy.error(`Failed to disconnect wxApp Mercury: ${error}`, {
2143
+ module: CC_FILE,
2144
+ method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
2145
+ });
2146
+ }
2147
+ }
2148
+
2149
+ if (this.wxAppDeviceRegisteredByCc && device?.registered && device.unregister) {
2150
+ try {
2151
+ // @ts-ignore
2152
+ await device.unregister();
2153
+ this.wxAppDeviceRegisteredByCc = false;
2154
+ LoggerProxy.log('WxApp mute sync: device unregistered', {
2155
+ module: CC_FILE,
2156
+ method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
2157
+ });
2158
+ } catch (error) {
2159
+ LoggerProxy.error(`Failed to unregister wxApp device: ${error}`, {
2160
+ module: CC_FILE,
2161
+ method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
2162
+ });
2163
+ }
2164
+ } else if (this.wxAppDeviceRegisteredByCc) {
2165
+ this.wxAppDeviceRegisteredByCc = false;
2166
+ }
2167
+ }
2168
+
2169
+ /**
2170
+ * Clears wxApp runtime state and feature-owned resources. Publish failures are logged;
2171
+ * optional rethrow for deregister when callers need to surface usersub errors.
2172
+ * @private
2173
+ */
2174
+ private async teardownWxAppLocalState(options?: {
2175
+ rethrowPublishError?: boolean;
2176
+ clearInitFlag?: boolean;
2177
+ }): Promise<{publishError?: unknown}> {
2178
+ this.clearWxAppFalsePublishRetryTimer();
2179
+ let publishError: unknown;
2180
+
2181
+ try {
2182
+ const shouldForceFalsePublish =
2183
+ this.isWxBetterTogetherEnabled() || this.webexCrossClientService.isAnswerCallsStateActive();
2184
+
2185
+ await this.publishAnswerOnWebexCrossClientState(false, {
2186
+ force: shouldForceFalsePublish,
2187
+ });
2188
+ } catch (error) {
2189
+ publishError = error;
2190
+ LoggerProxy.error(
2191
+ `Failed to publish answer-calls-on-wxcc false during wxApp teardown: ${error}`,
2192
+ {
2193
+ module: CC_FILE,
2194
+ method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
2195
+ }
2196
+ );
2197
+ }
2198
+
2199
+ this.webexCrossClientService.teardown();
2200
+ this.wxAppTelephonyMercurySync.unsubscribe();
2201
+ await this.releaseWxAppMercuryResources();
2202
+
2203
+ if (options?.clearInitFlag) {
2204
+ this.resetEnableWxBetterTogetherConfig();
2205
+ }
2206
+
2207
+ if (publishError) {
2208
+ this.scheduleCompensatingFalsePublishRetry();
2209
+ }
2210
+
2211
+ if (options?.rethrowPublishError && publishError) {
2212
+ throw publishError;
2213
+ }
2214
+
2215
+ return {publishError: publishError ?? undefined};
2216
+ }
2217
+
2218
+ private async ensureWxAppMercuryAndSubscribe(): Promise<void> {
2219
+ if (!this.isWxBetterTogetherEnabled()) {
2220
+ return;
2221
+ }
2222
+
2223
+ if (this.getCurrentStationLoginOption() === LoginOption.BROWSER) {
2224
+ return;
2225
+ }
2226
+
2227
+ const agentId = this.agentConfig?.agentId;
2228
+ if (!agentId) {
2229
+ LoggerProxy.error('Cannot subscribe wxApp mute sync: agentId unavailable', {
2230
+ module: CC_FILE,
2231
+ method: METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY,
2232
+ });
2233
+
2234
+ this.metricsManager.trackEvent(
2235
+ METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED,
2236
+ {error: 'agentId unavailable', mercurySubscribed: false},
2237
+ ['operational', 'behavioral']
2238
+ );
2239
+
2240
+ return;
2241
+ }
2242
+
2243
+ try {
2244
+ await this.ensureWxAppMercuryConnected();
2245
+ this.wxAppTelephonyMercurySync.subscribe(agentId, (callId, muted) => {
2246
+ this.taskManager.applyWxAppMuteStateFromSync(callId, muted);
2247
+ });
2248
+ } catch (error) {
2249
+ LoggerProxy.error(`Failed to ensure wxApp Mercury mute sync: ${error}`, {
2250
+ module: CC_FILE,
2251
+ method: METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY,
2252
+ });
2253
+ this.metricsManager.trackEvent(
2254
+ METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED,
2255
+ {
2256
+ error: error instanceof Error ? error.toString() : String(error),
2257
+ mercurySubscribed: false,
2258
+ },
2259
+ ['operational', 'behavioral']
2260
+ );
2261
+ this.wxAppTelephonyMercurySync.unsubscribe();
2262
+ await this.releaseWxAppMercuryResources();
2263
+ throw error;
2264
+ }
2265
+ }
2266
+
2267
+ private async publishAnswerOnWebexCrossClientState(
2268
+ enable: boolean,
2269
+ options?: {force?: boolean}
2270
+ ): Promise<void> {
2271
+ if (enable && !this.isWxBetterTogetherEnabled()) {
2272
+ return;
2273
+ }
2274
+
2275
+ if (!enable && !options?.force && !this.webexCrossClientService.isAnswerCallsStateActive()) {
2276
+ return;
2277
+ }
2278
+
2279
+ const userId = this.$webex.internal.device?.userId;
2280
+ if (!userId) {
2281
+ if (enable && this.isWxBetterTogetherEnabled()) {
2282
+ this.metricsManager.trackEvent(
2283
+ METRIC_EVENT_NAMES.WXAPP_USERSUB_PUBLISH_FAILED,
2284
+ {
2285
+ enableWxBetterTogether: true,
2286
+ usersubPublished: this.webexCrossClientService.isAnswerCallsStateActive(),
2287
+ skipReason: 'user_id_unavailable',
2288
+ error: 'User ID is unavailable for cross-client publish',
2289
+ },
2290
+ ['operational', 'behavioral']
2291
+ );
2292
+ }
2293
+
2294
+ return;
2295
+ }
2296
+
2297
+ await this.webexCrossClientService.setManageWebexCallingInWxcc(enable, {
2298
+ userId,
2299
+ trackPublishMetrics: true,
2300
+ });
2301
+
2302
+ if (enable) {
2303
+ this.clearWxAppFalsePublishRetryTimer();
2304
+ }
2305
+ }
2306
+
1249
2307
  /**
1250
2308
  * Initializes event listeners for the Contact Center service
1251
2309
  * Sets up handlers for connection state changes and other core events
@@ -1277,13 +2335,13 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1277
2335
  private async handleConnectionLost(msg: ConnectionLostDetails): Promise<void> {
1278
2336
  if (msg.isConnectionLost) {
1279
2337
  // TODO: Emit an event saying connection is lost
1280
- LoggerProxy.info('event=handleConnectionLost | Connection lost', {
2338
+ LoggerProxy.log('event=handleConnectionLost | Connection lost', {
1281
2339
  module: CC_FILE,
1282
2340
  method: METHODS.HANDLE_CONNECTION_LOST,
1283
2341
  });
1284
2342
  } else if (msg.isSocketReconnected) {
1285
2343
  // TODO: Emit an event saying connection is re-estabilished
1286
- LoggerProxy.info(
2344
+ LoggerProxy.log(
1287
2345
  'event=handleConnectionReconnect | Connection reconnected attempting to request silent relogin',
1288
2346
  {module: CC_FILE, method: METHODS.HANDLE_CONNECTION_LOST}
1289
2347
  );
@@ -1298,13 +2356,15 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1298
2356
  * @private
1299
2357
  */
1300
2358
  private async silentRelogin(): Promise<void> {
1301
- LoggerProxy.info('Starting silent relogin process', {
2359
+ LoggerProxy.log('Starting silent relogin process', {
1302
2360
  module: CC_FILE,
1303
2361
  method: METHODS.SILENT_RELOGIN,
1304
2362
  });
1305
2363
 
1306
2364
  try {
1307
2365
  const reLoginResponse = await this.services.agent.reload();
2366
+ this.updateWellnessSession(reLoginResponse.data.agentSessionId);
2367
+
1308
2368
  const {
1309
2369
  agentId,
1310
2370
  lastStateChangeReason,
@@ -1320,7 +2380,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1320
2380
  await this.handleDeviceType(deviceType as LoginOption, dn);
1321
2381
 
1322
2382
  if (lastStateChangeReason === 'agent-wss-disconnect') {
1323
- LoggerProxy.info(
2383
+ LoggerProxy.log(
1324
2384
  'event=requestAutoStateChange | Requesting state change to available on socket reconnect',
1325
2385
  {module: CC_FILE, method: METHODS.SILENT_RELOGIN}
1326
2386
  );
@@ -1349,8 +2409,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1349
2409
  }
1350
2410
  this.agentConfig.lastStateAuxCodeId = auxCodeId;
1351
2411
  this.agentConfig.isAgentLoggedIn = true;
1352
- // TODO: https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-626777 Implement the de-register method and close the listener there
1353
- this.services.webSocketManager.on('message', this.handleWebsocketMessage);
2412
+
2413
+ await this.ensureWxAppPostStationLogin();
1354
2414
 
1355
2415
  LoggerProxy.log(
1356
2416
  `Silent relogin process completed successfully with login Option: ${reLoginResponse.data.deviceType} teamId: ${reLoginResponse.data.teamId}`,
@@ -1391,6 +2451,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1391
2451
  this.agentConfig.deviceType = deviceType;
1392
2452
  switch (deviceType) {
1393
2453
  case LoginOption.BROWSER:
2454
+ if (this.isWebRTCRegistrationDisabled()) {
2455
+ LoggerProxy.info(
2456
+ 'Skipping web calling line registration because disableWebRTCRegistration is enabled',
2457
+ {
2458
+ module: CC_FILE,
2459
+ method: METHODS.HANDLE_DEVICE_TYPE,
2460
+ }
2461
+ );
2462
+ break;
2463
+ }
1394
2464
  try {
1395
2465
  await this.webCallingService.registerWebCallingLine();
1396
2466
  } catch (error) {
@@ -2060,8 +3130,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
2060
3130
  }
2061
3131
 
2062
3132
  /**
2063
- * Returns paginated entry points for the organization.
2064
- * Thin wrapper around internal EntryPoint instance.
3133
+ * Returns paginated entry points using the EntryPoint service defaults.
3134
+ * Existing request parameters can override those defaults.
2065
3135
  * @public
2066
3136
  */
2067
3137
  public async getEntryPoints(
@@ -2071,8 +3141,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
2071
3141
  }
2072
3142
 
2073
3143
  /**
2074
- * Returns paginated contact service queues for the organization.
2075
- * Thin wrapper around internal Queue instance.
3144
+ * Returns paginated contact service queues using the Queue service defaults.
3145
+ * Existing request parameters can override those defaults.
2076
3146
  * @public
2077
3147
  */
2078
3148
  public async getQueues(