@webex/contact-center 3.12.0-next.13 → 3.12.0-next.130

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 (312) hide show
  1. package/.sdd/manifest.json +884 -0
  2. package/AGENTS.md +94 -0
  3. package/ai-docs/ARCHITECTURE.md +168 -0
  4. package/ai-docs/CONTRACTS.md +53 -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 +382 -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 +971 -42
  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 +31 -3
  46. package/dist/constants.js.map +1 -1
  47. package/dist/index.js +45 -15
  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 +205 -0
  52. package/dist/metrics/behavioral-events.js.map +1 -1
  53. package/dist/metrics/constants.js +48 -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 +269 -9
  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/WebexCrossClientService.js +222 -0
  68. package/dist/services/WebexCrossClientService.js.map +1 -0
  69. package/dist/services/WxAppTelephonyMercurySync.js +108 -0
  70. package/dist/services/WxAppTelephonyMercurySync.js.map +1 -0
  71. package/dist/services/config/Util.js +11 -4
  72. package/dist/services/config/Util.js.map +1 -1
  73. package/dist/services/config/constants.js +41 -7
  74. package/dist/services/config/constants.js.map +1 -1
  75. package/dist/services/config/index.js +51 -0
  76. package/dist/services/config/index.js.map +1 -1
  77. package/dist/services/config/types.js +57 -12
  78. package/dist/services/config/types.js.map +1 -1
  79. package/dist/services/core/Err.js.map +1 -1
  80. package/dist/services/core/Utils.js +111 -32
  81. package/dist/services/core/Utils.js.map +1 -1
  82. package/dist/services/core/WebexRequest.js +6 -2
  83. package/dist/services/core/WebexRequest.js.map +1 -1
  84. package/dist/services/core/aqm-reqs.js +28 -14
  85. package/dist/services/core/aqm-reqs.js.map +1 -1
  86. package/dist/services/core/types.js.map +1 -1
  87. package/dist/services/core/websocket/WebSocketManager.js +2 -1
  88. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  89. package/dist/services/core/websocket/types.js.map +1 -1
  90. package/dist/services/index.js +1 -1
  91. package/dist/services/index.js.map +1 -1
  92. package/dist/services/task/Task.js +754 -0
  93. package/dist/services/task/Task.js.map +1 -0
  94. package/dist/services/task/TaskFactory.js +49 -0
  95. package/dist/services/task/TaskFactory.js.map +1 -0
  96. package/dist/services/task/TaskManager.js +1172 -521
  97. package/dist/services/task/TaskManager.js.map +1 -1
  98. package/dist/services/task/TaskUtils.js +211 -27
  99. package/dist/services/task/TaskUtils.js.map +1 -1
  100. package/dist/services/task/WebexCallingUtils.js +70 -0
  101. package/dist/services/task/WebexCallingUtils.js.map +1 -0
  102. package/dist/services/task/constants.js +12 -2
  103. package/dist/services/task/constants.js.map +1 -1
  104. package/dist/services/task/contact.js +29 -0
  105. package/dist/services/task/contact.js.map +1 -1
  106. package/dist/services/task/dialer.js +78 -0
  107. package/dist/services/task/dialer.js.map +1 -1
  108. package/dist/services/task/digital/Digital.js +78 -0
  109. package/dist/services/task/digital/Digital.js.map +1 -0
  110. package/dist/services/task/state-machine/TaskStateMachine.js +982 -0
  111. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  112. package/dist/services/task/state-machine/actions.js +572 -0
  113. package/dist/services/task/state-machine/actions.js.map +1 -0
  114. package/dist/services/task/state-machine/constants.js +161 -0
  115. package/dist/services/task/state-machine/constants.js.map +1 -0
  116. package/dist/services/task/state-machine/guards.js +379 -0
  117. package/dist/services/task/state-machine/guards.js.map +1 -0
  118. package/dist/services/task/state-machine/index.js +53 -0
  119. package/dist/services/task/state-machine/index.js.map +1 -0
  120. package/dist/services/task/state-machine/types.js +54 -0
  121. package/dist/services/task/state-machine/types.js.map +1 -0
  122. package/dist/services/task/state-machine/uiControlsComputer.js +703 -0
  123. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  124. package/dist/services/task/taskDataNormalizer.js +99 -0
  125. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  126. package/dist/services/task/types.js +228 -5
  127. package/dist/services/task/types.js.map +1 -1
  128. package/dist/services/task/voice/Voice.js +1485 -0
  129. package/dist/services/task/voice/Voice.js.map +1 -0
  130. package/dist/services/task/voice/WebRTC.js +152 -0
  131. package/dist/services/task/voice/WebRTC.js.map +1 -0
  132. package/dist/services/task/voice/wxAppVoiceMethods.js +354 -0
  133. package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -0
  134. package/dist/services/wxAppDiagnosticLogging.js +122 -0
  135. package/dist/services/wxAppDiagnosticLogging.js.map +1 -0
  136. package/dist/services/wxAppTelephonyUtils.js +19 -0
  137. package/dist/services/wxAppTelephonyUtils.js.map +1 -0
  138. package/dist/types/cc.d.ts +193 -6
  139. package/dist/types/config.d.ts +13 -0
  140. package/dist/types/constants.d.ts +29 -1
  141. package/dist/types/index.d.ts +35 -8
  142. package/dist/types/metrics/MetricsManager.d.ts +6 -0
  143. package/dist/types/metrics/constants.d.ts +43 -1
  144. package/dist/types/services/AddressBook.d.ts +2 -1
  145. package/dist/types/services/AnswerCallOnWebexService.d.ts +38 -0
  146. package/dist/types/services/ApiAiAssistant.d.ts +56 -5
  147. package/dist/types/services/EntryPoint.d.ts +7 -5
  148. package/dist/types/services/Queue.d.ts +5 -3
  149. package/dist/types/services/UserPreference.d.ts +118 -0
  150. package/dist/types/services/WebexCrossClientService.d.ts +32 -0
  151. package/dist/types/services/WxAppTelephonyMercurySync.d.ts +28 -0
  152. package/dist/types/services/config/constants.d.ts +34 -5
  153. package/dist/types/services/config/types.d.ts +197 -12
  154. package/dist/types/services/core/Err.d.ts +6 -0
  155. package/dist/types/services/core/Utils.d.ts +35 -13
  156. package/dist/types/services/core/WebexRequest.d.ts +1 -0
  157. package/dist/types/services/core/types.d.ts +2 -0
  158. package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
  159. package/dist/types/services/core/websocket/types.d.ts +1 -1
  160. package/dist/types/services/index.d.ts +1 -1
  161. package/dist/types/services/task/Task.d.ts +175 -0
  162. package/dist/types/services/task/TaskFactory.d.ts +13 -0
  163. package/dist/types/services/task/TaskUtils.d.ts +48 -2
  164. package/dist/types/services/task/WebexCallingUtils.d.ts +11 -0
  165. package/dist/types/services/task/constants.d.ts +10 -0
  166. package/dist/types/services/task/contact.d.ts +4 -0
  167. package/dist/types/services/task/dialer.d.ts +30 -0
  168. package/dist/types/services/task/digital/Digital.d.ts +22 -0
  169. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1442 -0
  170. package/dist/types/services/task/state-machine/actions.d.ts +10 -0
  171. package/dist/types/services/task/state-machine/constants.d.ts +107 -0
  172. package/dist/types/services/task/state-machine/guards.d.ts +103 -0
  173. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  174. package/dist/types/services/task/state-machine/types.d.ts +277 -0
  175. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  176. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  177. package/dist/types/services/task/types.d.ts +680 -66
  178. package/dist/types/services/task/voice/Voice.d.ts +227 -0
  179. package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
  180. package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +55 -0
  181. package/dist/types/services/wxAppDiagnosticLogging.d.ts +51 -0
  182. package/dist/types/services/wxAppTelephonyUtils.d.ts +5 -0
  183. package/dist/types/types.d.ts +205 -9
  184. package/dist/types/utils/PageCache.d.ts +20 -3
  185. package/dist/types/webex.d.ts +1 -0
  186. package/dist/types.js +162 -4
  187. package/dist/types.js.map +1 -1
  188. package/dist/utils/PageCache.js +19 -5
  189. package/dist/utils/PageCache.js.map +1 -1
  190. package/dist/webex.js +22 -2
  191. package/dist/webex.js.map +1 -1
  192. package/package.json +16 -12
  193. package/src/cc.ts +1289 -112
  194. package/src/config.ts +13 -0
  195. package/src/constants.ts +29 -1
  196. package/src/index.ts +36 -5
  197. package/src/metrics/MetricsManager.ts +16 -0
  198. package/src/metrics/ai-docs/AGENTS.md +350 -0
  199. package/src/metrics/ai-docs/ARCHITECTURE.md +338 -0
  200. package/src/metrics/ai-docs/metrics-spec.md +921 -0
  201. package/src/metrics/behavioral-events.ts +212 -0
  202. package/src/metrics/constants.ts +52 -4
  203. package/src/services/AddressBook.ts +17 -6
  204. package/src/services/AnswerCallOnWebexService.ts +239 -0
  205. package/src/services/ApiAiAssistant.ts +356 -9
  206. package/src/services/EntryPoint.ts +59 -60
  207. package/src/services/Queue.ts +29 -12
  208. package/src/services/UserPreference.ts +509 -0
  209. package/src/services/WebexCrossClientService.ts +293 -0
  210. package/src/services/WxAppTelephonyMercurySync.ts +142 -0
  211. package/src/services/agent/ai-docs/AGENTS.md +240 -0
  212. package/src/services/agent/ai-docs/ARCHITECTURE.md +304 -0
  213. package/src/services/agent/ai-docs/agent-spec.md +505 -0
  214. package/src/services/ai-docs/AGENTS.md +386 -0
  215. package/src/services/ai-docs/services-spec.md +505 -0
  216. package/src/services/config/Util.ts +12 -3
  217. package/src/services/config/ai-docs/AGENTS.md +255 -0
  218. package/src/services/config/ai-docs/ARCHITECTURE.md +426 -0
  219. package/src/services/config/ai-docs/config-spec.md +686 -0
  220. package/src/services/config/constants.ts +46 -6
  221. package/src/services/config/index.ts +65 -0
  222. package/src/services/config/types.ts +197 -13
  223. package/src/services/core/Err.ts +3 -0
  224. package/src/services/core/Utils.ts +127 -37
  225. package/src/services/core/WebexRequest.ts +3 -1
  226. package/src/services/core/ai-docs/AGENTS.md +381 -0
  227. package/src/services/core/ai-docs/ARCHITECTURE.md +698 -0
  228. package/src/services/core/ai-docs/core-spec.md +787 -0
  229. package/src/services/core/aqm-reqs.ts +30 -15
  230. package/src/services/core/types.ts +2 -0
  231. package/src/services/core/websocket/WebSocketManager.ts +2 -0
  232. package/src/services/core/websocket/types.ts +1 -1
  233. package/src/services/index.ts +1 -1
  234. package/src/services/task/Task.ts +908 -0
  235. package/src/services/task/TaskFactory.ts +60 -0
  236. package/src/services/task/TaskManager.ts +1419 -608
  237. package/src/services/task/TaskUtils.ts +303 -25
  238. package/src/services/task/WebexCallingUtils.ts +136 -0
  239. package/src/services/task/ai-docs/AGENTS.md +457 -0
  240. package/src/services/task/ai-docs/ARCHITECTURE.md +595 -0
  241. package/src/services/task/ai-docs/task-spec.md +1474 -0
  242. package/src/services/task/constants.ts +10 -0
  243. package/src/services/task/contact.ts +30 -0
  244. package/src/services/task/dialer.ts +80 -0
  245. package/src/services/task/digital/Digital.ts +97 -0
  246. package/src/services/task/state-machine/TaskStateMachine.ts +1320 -0
  247. package/src/services/task/state-machine/actions.ts +741 -0
  248. package/src/services/task/state-machine/ai-docs/AGENTS.md +460 -0
  249. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1156 -0
  250. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2247 -0
  251. package/src/services/task/state-machine/constants.ts +172 -0
  252. package/src/services/task/state-machine/guards.ts +442 -0
  253. package/src/services/task/state-machine/index.ts +28 -0
  254. package/src/services/task/state-machine/types.ts +258 -0
  255. package/src/services/task/state-machine/uiControlsComputer.ts +1135 -0
  256. package/src/services/task/taskDataNormalizer.ts +137 -0
  257. package/src/services/task/types.ts +819 -70
  258. package/src/services/task/voice/Voice.ts +1851 -0
  259. package/src/services/task/voice/WebRTC.ts +191 -0
  260. package/src/services/task/voice/wxAppVoiceMethods.ts +516 -0
  261. package/src/services/wxAppDiagnosticLogging.ts +174 -0
  262. package/src/services/wxAppTelephonyUtils.ts +14 -0
  263. package/src/types.ts +262 -11
  264. package/src/utils/AGENTS.md +289 -0
  265. package/src/utils/PageCache.ts +38 -5
  266. package/src/utils/ai-docs/utils-spec.md +391 -0
  267. package/src/webex.js +10 -0
  268. package/test/unit/spec/cc.ts +2210 -119
  269. package/test/unit/spec/logger-proxy.ts +70 -0
  270. package/test/unit/spec/metrics/MetricsManager.ts +25 -0
  271. package/test/unit/spec/metrics/behavioral-events.ts +60 -0
  272. package/test/unit/spec/services/AddressBook.ts +37 -6
  273. package/test/unit/spec/services/AnswerCallOnWebexService.ts +264 -0
  274. package/test/unit/spec/services/ApiAiAssistant.ts +311 -21
  275. package/test/unit/spec/services/EntryPoint.ts +87 -40
  276. package/test/unit/spec/services/Queue.ts +123 -12
  277. package/test/unit/spec/services/UserPreference.ts +401 -0
  278. package/test/unit/spec/services/WebCallingService.ts +7 -1
  279. package/test/unit/spec/services/WebexCrossClientService.ts +467 -0
  280. package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +136 -0
  281. package/test/unit/spec/services/config/Util.ts +129 -0
  282. package/test/unit/spec/services/config/index.ts +97 -29
  283. package/test/unit/spec/services/core/Utils.ts +425 -8
  284. package/test/unit/spec/services/core/WebexRequest.ts +3 -1
  285. package/test/unit/spec/services/core/aqm-reqs.ts +113 -1
  286. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +66 -40
  287. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  288. package/test/unit/spec/services/task/Task.ts +745 -0
  289. package/test/unit/spec/services/task/TaskFactory.ts +99 -0
  290. package/test/unit/spec/services/task/TaskManager.ts +2518 -1494
  291. package/test/unit/spec/services/task/TaskUtils.ts +235 -0
  292. package/test/unit/spec/services/task/WebexCallingUtils.ts +153 -0
  293. package/test/unit/spec/services/task/contact.ts +33 -0
  294. package/test/unit/spec/services/task/dialer.ts +190 -0
  295. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  296. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +4060 -0
  297. package/test/unit/spec/services/task/state-machine/guards.ts +839 -0
  298. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  299. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +3101 -0
  300. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  301. package/test/unit/spec/services/task/voice/Voice.ts +2061 -0
  302. package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
  303. package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +603 -0
  304. package/test/unit/spec/services/wxAppDiagnosticLogging.ts +143 -0
  305. package/test/unit/spec/wellnessSampleUtils.ts +288 -0
  306. package/umd/contact-center.min.js +2 -2
  307. package/umd/contact-center.min.js.map +1 -1
  308. package/dist/services/task/index.js +0 -1525
  309. package/dist/services/task/index.js.map +0 -1
  310. package/dist/types/services/task/index.d.ts +0 -650
  311. package/src/services/task/index.ts +0 -1801
  312. package/test/unit/spec/services/task/index.ts +0 -2184
package/dist/cc.js CHANGED
@@ -19,6 +19,10 @@ var _Utils = require("./services/core/Utils");
19
19
  var _types3 = require("./services/config/types");
20
20
  var _TaskManager = _interopRequireDefault(require("./services/task/TaskManager"));
21
21
  var _WebCallingService = _interopRequireDefault(require("./services/WebCallingService"));
22
+ var _AnswerCallOnWebexService = _interopRequireDefault(require("./services/AnswerCallOnWebexService"));
23
+ var _WebexCrossClientService = _interopRequireDefault(require("./services/WebexCrossClientService"));
24
+ var _WxAppTelephonyMercurySync = _interopRequireDefault(require("./services/WxAppTelephonyMercurySync"));
25
+ var _wxAppDiagnosticLogging = require("./services/wxAppDiagnosticLogging");
22
26
  var _types4 = require("./services/task/types");
23
27
  var _MetricsManager = _interopRequireDefault(require("./metrics/MetricsManager"));
24
28
  var _constants4 = require("./metrics/constants");
@@ -26,6 +30,7 @@ var _EntryPoint = require("./services/EntryPoint");
26
30
  var _AddressBook = require("./services/AddressBook");
27
31
  var _Queue = require("./services/Queue");
28
32
  var _ApiAiAssistant = require("./services/ApiAiAssistant");
33
+ var _UserPreference = require("./services/UserPreference");
29
34
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
30
35
  /**
31
36
  * @module CCPlugin
@@ -184,6 +189,29 @@ class ContactCenter extends _webexCore.WebexPlugin {
184
189
  * @private
185
190
  */
186
191
 
192
+ /**
193
+ * REST telephony for wxApp thick-client answer (WXCC-6026)
194
+ * @private
195
+ */
196
+
197
+ /**
198
+ * usersub cross-client state for Webex App toast suppression
199
+ * @private
200
+ */
201
+
202
+ /**
203
+ * Mercury telephony mute sync for wxApp thick-client answer
204
+ * @private
205
+ */
206
+
207
+ /** True when wxApp mute sync opened Mercury (Extension/DN-only profiles). */
208
+ wxAppMercuryConnectedByCc = false;
209
+
210
+ /** True when wxApp mute sync registered the Webex device. */
211
+ wxAppDeviceRegisteredByCc = false;
212
+ static WXAPP_FALSE_PUBLISH_RETRY_DELAY_MS = 30_000;
213
+ static WXAPP_FALSE_PUBLISH_MAX_RETRIES = 3;
214
+
187
215
  /**
188
216
  * Core service managers for Contact Center operations
189
217
  * Includes agent, connection, and configuration services
@@ -212,6 +240,16 @@ class ContactCenter extends _webexCore.WebexPlugin {
212
240
  * @private
213
241
  */
214
242
 
243
+ /** Current login/relogin session used for agent-scoped wellness validation. */
244
+
245
+ /** Registered-session cache for the system WellbeingBreak idle code. */
246
+
247
+ /** Registration generation that owns the wellness idle-code cache and in-flight lookups. */
248
+ wellnessRegistrationGeneration = 0;
249
+ updateWellnessSession(agentSessionId) {
250
+ this.currentAgentSessionId = agentSessionId;
251
+ }
252
+
215
253
  /**
216
254
  * API instance for managing Webex Contact Center entry points
217
255
  * Provides functionality to fetch entry points with caching support
@@ -274,6 +312,36 @@ class ContactCenter extends _webexCore.WebexPlugin {
274
312
  * ```
275
313
  */
276
314
 
315
+ /**
316
+ * API instance for managing Webex Contact Center user preferences
317
+ * Provides functionality to get, create, update, and delete user preferences
318
+ * @type {UserPreference}
319
+ * @public
320
+ * @example
321
+ * ```typescript
322
+ * const cc = webex.cc;
323
+ * await cc.register();
324
+ * await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' });
325
+ *
326
+ * // Get user preferences for current user
327
+ * const preferences = await cc.userPreference.getUserPreference();
328
+ *
329
+ * // Create new user preferences
330
+ * const newPrefs = await cc.userPreference.createUserPreference({
331
+ * userId: 'user123',
332
+ * preferences: { e911Reminder: true }
333
+ * });
334
+ *
335
+ * // Update user preferences
336
+ * const updatedPrefs = await cc.userPreference.updateUserPreference('user123', {
337
+ * preferences: { e911Reminder: false }
338
+ * });
339
+ *
340
+ * // Delete user preferences
341
+ * await cc.userPreference.deleteUserPreference('user123');
342
+ * ```
343
+ */
344
+
277
345
  /**
278
346
  * API instance for AI Assistant operations such as transcript controls.
279
347
  * @type {ApiAIAssistant}
@@ -301,6 +369,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
301
369
  this.$webex.once(_constants.READY, () => {
302
370
  // @ts-ignore
303
371
  this.$config = this.config;
372
+ this.validatePluginConfig();
304
373
 
305
374
  /**
306
375
  * This is used for handling the async requests by sending webex.request and wait for corresponding websocket event.
@@ -312,13 +381,20 @@ class ContactCenter extends _webexCore.WebexPlugin {
312
381
  webex: this.$webex,
313
382
  connectionConfig: this.getConnectionConfig()
314
383
  });
315
- this.services.webSocketManager.on('message', this.handleWebsocketMessage);
316
384
  this.webCallingService = new _WebCallingService.default(this.$webex);
317
- this.apiAIAssistant = new _ApiAiAssistant.ApiAIAssistant(this.$webex);
385
+ this.answerCallOnWebexService = new _AnswerCallOnWebexService.default(this.$webex);
386
+ this.webexCrossClientService = new _WebexCrossClientService.default(this.$webex);
387
+ this.wxAppTelephonyMercurySync = new _WxAppTelephonyMercurySync.default(this.$webex);
388
+ this.apiAIAssistant = new _ApiAiAssistant.ApiAIAssistant(this.$webex, () => ({
389
+ isWellnessBreakEnabled: this.agentConfig?.isWellnessBreakEnabled === true,
390
+ agentId: this.agentConfig?.agentId,
391
+ agentSessionId: this.currentAgentSessionId
392
+ }));
318
393
  this.metricsManager = _MetricsManager.default.getInstance({
319
394
  webex: this.$webex
320
395
  });
321
- this.taskManager = _TaskManager.default.getTaskManager(this.apiAIAssistant, this.services.contact, this.webCallingService, this.services.webSocketManager);
396
+ this.taskManager = _TaskManager.default.getTaskManager(this.apiAIAssistant, this.services.contact, this.webCallingService, this.services.webSocketManager, this.services.rtdWebSocketManager);
397
+ this.taskManager.setAnswerCallOnWebexService(this.answerCallOnWebexService);
322
398
  this.incomingTaskListener();
323
399
 
324
400
  // Initialize API instances
@@ -326,6 +402,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
326
402
  this.entryPoint = new _EntryPoint.EntryPoint(this.$webex);
327
403
  this.addressBook = new _AddressBook.AddressBook(this.$webex, () => this.agentConfig?.addressBookId);
328
404
  this.queue = new _Queue.Queue(this.$webex);
405
+ this.userPreference = new _UserPreference.UserPreference(this.$webex, () => this.agentConfig?.agentId);
329
406
  _loggerProxy.default.initialize(this.$webex.logger);
330
407
  });
331
408
  }
@@ -350,6 +427,16 @@ class ContactCenter extends _webexCore.WebexPlugin {
350
427
  this.trigger(_types4.TASK_EVENTS.TASK_HYDRATE, task);
351
428
  };
352
429
 
430
+ /**
431
+ * Handles multi-login hydrate events for SDK instances without Mobius registration
432
+ * @private
433
+ * @param {ITask} task The task object associated with the multi-login hydrate
434
+ */
435
+ handleTaskMultiLoginHydrate = task => {
436
+ // @ts-ignore
437
+ this.trigger(_types4.TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, task);
438
+ };
439
+
353
440
  /**
354
441
  * Handles task merged events when tasks are combined eg: EPDN merge/transfer
355
442
  * @private
@@ -369,8 +456,74 @@ class ContactCenter extends _webexCore.WebexPlugin {
369
456
  // @ts-ignore
370
457
  this.trigger(_types4.TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, task);
371
458
  };
372
- handleRTDWebsocketMessage = payload => {
373
- this.taskManager.handleRealtimeWebsocketEvent(payload);
459
+ trackInvalidWellnessEvent(reason) {
460
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.AI_ASSISTANT_WELLNESS_EVENT_INVALID, {
461
+ reason
462
+ }, ['operational']);
463
+ }
464
+ normalizeWellnessBreakEvent(payload) {
465
+ if (this.agentConfig?.isWellnessBreakEnabled !== true) {
466
+ this.trackInvalidWellnessEvent('wellness_disabled');
467
+ return null;
468
+ }
469
+ const notificationData = (0, _Utils.isRecord)(payload.data) ? payload.data : null;
470
+ const notificationDetails = notificationData && (0, _Utils.isRecord)(notificationData.notifDetails) ? notificationData.notifDetails : null;
471
+ const sessionData = notificationData && (0, _Utils.isRecord)(notificationData.data) ? notificationData.data : null;
472
+ const actionEvent = notificationDetails?.actionEvent;
473
+ if (payload.type !== _constants.WELLNESS_BREAK_HANDLER || notificationData?.notifType !== _constants.WELLNESS_BREAK_HANDLER || typeof actionEvent !== 'string') {
474
+ this.trackInvalidWellnessEvent('malformed_wellness_event');
475
+ return null;
476
+ }
477
+ if (!Object.values(_types.WELLNESS_BREAK_NOTIFICATION_ACTIONS).includes(actionEvent)) {
478
+ this.trackInvalidWellnessEvent('unknown_wellness_action');
479
+ return null;
480
+ }
481
+ const agentId = notificationData.agentId;
482
+ const eventOrgId = notificationData.orgId;
483
+ const sessionOrgId = sessionData?.orgId;
484
+ const envelopeOrgId = payload.orgId;
485
+ const agentSessionId = sessionData?.agentSessionId;
486
+ const currentOrgId = this.$webex.credentials.getOrgId();
487
+ if (typeof agentId !== 'string' || typeof eventOrgId !== 'string' || typeof sessionOrgId !== 'string' || typeof envelopeOrgId !== 'string' || typeof agentSessionId !== 'string' || !agentId || !eventOrgId || !sessionOrgId || !envelopeOrgId || !agentSessionId) {
488
+ this.trackInvalidWellnessEvent('missing_wellness_identity');
489
+ return null;
490
+ }
491
+ if (agentId !== this.agentConfig?.agentId || eventOrgId !== currentOrgId || sessionOrgId !== currentOrgId || envelopeOrgId !== currentOrgId) {
492
+ this.trackInvalidWellnessEvent('mismatched_wellness_identity');
493
+ return null;
494
+ }
495
+ const interactionValue = sessionData.interactionId ?? sessionData.InteractionId;
496
+ const actionText = notificationDetails.actionText;
497
+ const trackingId = payload.trackingId;
498
+ return {
499
+ agentId,
500
+ orgId: currentOrgId,
501
+ agentSessionId,
502
+ actionEvent: actionEvent,
503
+ ...(typeof actionText === 'string' ? {
504
+ actionText
505
+ } : {}),
506
+ ...(typeof interactionValue === 'string' ? {
507
+ interactionId: interactionValue
508
+ } : {}),
509
+ ...(typeof trackingId === 'string' ? {
510
+ trackingId
511
+ } : {})
512
+ };
513
+ }
514
+ handleWellnessBreakWebsocketPayload(payload) {
515
+ if (payload.type !== _constants.WELLNESS_BREAK_HANDLER) {
516
+ return false;
517
+ }
518
+ const wellnessEvent = this.normalizeWellnessBreakEvent(payload);
519
+ if (wellnessEvent) {
520
+ // @ts-ignore - WebexPlugin emit is available at runtime but absent from its declaration.
521
+ this.emit(_types3.CC_EVENTS.WELLNESS_BREAK, wellnessEvent);
522
+ }
523
+ return true;
524
+ }
525
+ handleRTDWebsocketMessage = event => {
526
+ this.taskManager.handleRealtimeWebsocketEvent(event);
374
527
  };
375
528
 
376
529
  /**
@@ -381,6 +534,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
381
534
  incomingTaskListener() {
382
535
  this.taskManager.on(_types4.TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
383
536
  this.taskManager.on(_types4.TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
537
+ this.taskManager.on(_types4.TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
384
538
  this.taskManager.on(_types4.TASK_EVENTS.TASK_MERGED, this.handleTaskMerged);
385
539
  this.taskManager.on(_types4.TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, this.handleCampaignPreviewReservation);
386
540
  }
@@ -422,13 +576,16 @@ class ContactCenter extends _webexCore.WebexPlugin {
422
576
  * ```
423
577
  */
424
578
  async register() {
425
- _loggerProxy.default.info('Starting CC SDK registration', {
579
+ this.wellnessRegistrationGeneration += 1;
580
+ this.wellbeingBreakIdleCode = undefined;
581
+ _loggerProxy.default.log('Starting CC SDK registration', {
426
582
  module: _constants.CC_FILE,
427
583
  method: _constants.METHODS.REGISTER
428
584
  });
429
585
  try {
430
586
  this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_SUCCESS, _constants4.METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_FAILED]);
431
587
  this.setupEventListeners();
588
+ this.services.webSocketManager.on('message', this.handleWebsocketMessage);
432
589
  const resp = await this.connectWebsocket();
433
590
  // Ensure 'dn' is always populated from 'defaultDn'
434
591
  resp.dn = resp.defaultDn;
@@ -487,15 +644,24 @@ class ContactCenter extends _webexCore.WebexPlugin {
487
644
  *
488
645
  */
489
646
  async deregister() {
647
+ this.wellnessRegistrationGeneration += 1;
648
+ this.wellbeingBreakIdleCode = undefined;
649
+ this.updateWellnessSession();
490
650
  try {
491
651
  this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.WEBSOCKET_DEREGISTER_SUCCESS, _constants4.METRIC_EVENT_NAMES.WEBSOCKET_DEREGISTER_FAIL]);
492
652
  this.taskManager.off(_types4.TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
493
653
  this.taskManager.off(_types4.TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
654
+ this.taskManager.off(_types4.TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
494
655
  this.taskManager.off(_types4.TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, this.handleCampaignPreviewReservation);
495
656
  this.taskManager.unregisterIncomingCallEvent();
496
657
  this.services.webSocketManager.off('message', this.handleWebsocketMessage);
497
658
  this.services.rtdWebSocketManager.off('message', this.handleRTDWebsocketMessage);
498
659
  this.services.connectionService.off('connectionLost', this.handleConnectionLost);
660
+ const {
661
+ publishError: wxAppPublishError
662
+ } = await this.teardownWxAppLocalState({
663
+ clearInitFlag: true
664
+ });
499
665
  if (this.agentConfig.webRtcEnabled && this.agentConfig.loginVoiceOptions.includes(_types.LoginOption.BROWSER)) {
500
666
  if (this.$webex.internal.mercury.connected) {
501
667
  this.$webex.internal.mercury.off('online');
@@ -512,12 +678,20 @@ class ContactCenter extends _webexCore.WebexPlugin {
512
678
  if (!this.services.webSocketManager.isSocketClosed) {
513
679
  this.services.webSocketManager.close(false, 'Unregistering the SDK');
514
680
  }
681
+ if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
682
+ this.services.rtdWebSocketManager.close(false, 'Unregistering the SDK');
683
+ }
515
684
  if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
516
685
  this.services.rtdWebSocketManager.close(false, 'Unregistering the RTD websocket');
517
686
  }
518
687
 
519
688
  // Clear any cached agent configuration
689
+ this.wellbeingBreakIdleCode = undefined;
520
690
  this.agentConfig = null;
691
+ this.updateWellnessSession();
692
+ if (wxAppPublishError) {
693
+ throw wxAppPublishError;
694
+ }
521
695
  _loggerProxy.default.log('Deregistered successfully', {
522
696
  module: _constants.CC_FILE,
523
697
  method: _constants.METHODS.DEREGISTER
@@ -571,6 +745,12 @@ class ContactCenter extends _webexCore.WebexPlugin {
571
745
  * ```
572
746
  */
573
747
  async getBuddyAgents(data) {
748
+ const {
749
+ action,
750
+ state,
751
+ mediaType = 'telephony'
752
+ } = data;
753
+ const buddyAgentState = state ?? (action === 'Transfer' ? _constants2.AGENT_STATE_AVAILABLE : undefined);
574
754
  _loggerProxy.default.info('Fetching buddy agents', {
575
755
  module: _constants.CC_FILE,
576
756
  method: _constants.METHODS.GET_BUDDY_AGENTS
@@ -580,13 +760,16 @@ class ContactCenter extends _webexCore.WebexPlugin {
580
760
  const resp = await this.services.agent.buddyAgents({
581
761
  data: {
582
762
  agentProfileId: this.agentConfig.agentProfileID,
583
- ...data
763
+ mediaType,
764
+ ...(buddyAgentState ? {
765
+ state: buddyAgentState
766
+ } : {})
584
767
  }
585
768
  });
586
769
  this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_SUCCESS, {
587
770
  ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(resp),
588
- mediaType: data.mediaType,
589
- buddyAgentState: data.state,
771
+ mediaType,
772
+ buddyAgentState,
590
773
  buddyAgentCount: resp.data.agentList.length
591
774
  }, ['operational']);
592
775
  _loggerProxy.default.log(`Successfully retrieved ${resp.data.agentList.length} buddy agents`, {
@@ -599,8 +782,8 @@ class ContactCenter extends _webexCore.WebexPlugin {
599
782
  const failureResp = error.details;
600
783
  this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_FAILED, {
601
784
  ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(failureResp),
602
- mediaType: data.mediaType,
603
- buddyAgentState: data.state
785
+ mediaType,
786
+ buddyAgentState
604
787
  }, ['operational']);
605
788
  const {
606
789
  error: detailedError
@@ -616,7 +799,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
616
799
  * @private
617
800
  */
618
801
  async connectWebsocket() {
619
- _loggerProxy.default.info('Connecting to websocket', {
802
+ _loggerProxy.default.log('Connecting to websocket', {
620
803
  module: _constants.CC_FILE,
621
804
  method: _constants.METHODS.CONNECT_WEBSOCKET
622
805
  });
@@ -632,18 +815,39 @@ class ContactCenter extends _webexCore.WebexPlugin {
632
815
  module: _constants.CC_FILE,
633
816
  method: _constants.METHODS.CONNECT_WEBSOCKET
634
817
  });
635
-
818
+ const configFlags = {
819
+ isEndTaskEnabled: this.agentConfig.isEndTaskEnabled,
820
+ isEndConsultEnabled: this.agentConfig.isEndConsultEnabled,
821
+ webRtcEnabled: this.agentConfig.webRtcEnabled,
822
+ autoWrapup: this.agentConfig.wrapUpData?.wrapUpProps?.autoWrapup ?? false,
823
+ aiFeature: this.agentConfig.aiFeature,
824
+ consultTransfer: {
825
+ allowConsultToQueue: this.agentConfig.allowConsultToQueue,
826
+ accessQueue: this.agentConfig.accessQueue,
827
+ accessEntryPoint: this.agentConfig.accessEntryPoint,
828
+ accessBuddyTeam: this.agentConfig.accessBuddyTeam
829
+ },
830
+ enableWxBetterTogether: this.isWxBetterTogetherEnabled()
831
+ };
832
+ this.taskManager.setConfigFlags(configFlags);
636
833
  // TODO: Make profile a singleton to make it available throughout app/sdk so we dont need to inject info everywhere
637
834
  this.taskManager.setWrapupData(this.agentConfig.wrapUpData);
638
835
  this.taskManager.setAgentId(this.agentConfig.agentId);
639
836
  this.taskManager.setWebRtcEnabled(this.agentConfig.webRtcEnabled);
640
837
  this.apiAIAssistant.setAIFeatureFlags(this.agentConfig.aiFeature);
641
- if (this.agentConfig.aiFeature?.realtimeTranscripts?.enable) {
838
+ this.wellbeingBreakIdleCode = undefined;
839
+ this.updateWellnessSession();
840
+
841
+ /**
842
+ * RTD websocket currently supports realtime transcripts and suggested responses.
843
+ * Extend this condition when additional AI RTD features are introduced.
844
+ */
845
+ if (this.agentConfig.aiFeature?.realtimeTranscripts?.enable || this.agentConfig.aiFeature?.suggestedResponses?.enable) {
642
846
  _loggerProxy.default.info('Connecting to RTD websocket', {
643
847
  module: _constants.CC_FILE,
644
848
  method: _constants.METHODS.CONNECT_WEBSOCKET
645
849
  });
646
- await this.services.rtdWebSocketManager.initWebSocket({
850
+ this.services.rtdWebSocketManager.initWebSocket({
647
851
  body: this.getConnectionConfig(),
648
852
  resource: _constants3.RTD_SUBSCRIBE_API
649
853
  }).then(() => {
@@ -653,13 +857,13 @@ class ContactCenter extends _webexCore.WebexPlugin {
653
857
  });
654
858
  this.services.rtdWebSocketManager.on('message', this.handleRTDWebsocketMessage);
655
859
  }).catch(error => {
656
- _loggerProxy.default.error(`Error during RTD websocket setup: ${error}`, {
860
+ _loggerProxy.default.error(`Error connecting to RTD websocket ${error}`, {
657
861
  module: _constants.CC_FILE,
658
862
  method: _constants.METHODS.CONNECT_WEBSOCKET
659
863
  });
660
864
  });
661
865
  }
662
- if (this.agentConfig.webRtcEnabled && this.agentConfig.loginVoiceOptions.includes(_types.LoginOption.BROWSER)) {
866
+ if (this.agentConfig.webRtcEnabled && this.agentConfig.loginVoiceOptions.includes(_types.LoginOption.BROWSER) && !this.isWebRTCRegistrationDisabled()) {
663
867
  try {
664
868
  await this.$webex.internal.mercury.connect();
665
869
  _loggerProxy.default.log('Authentication: webex.internal.mercury.connect successful', {
@@ -672,12 +876,23 @@ class ContactCenter extends _webexCore.WebexPlugin {
672
876
  method: _constants.METHODS.CONNECT_WEBSOCKET
673
877
  });
674
878
  }
879
+ } else if (this.isWebRTCRegistrationDisabled()) {
880
+ _loggerProxy.default.info('Skipping Mobius registration because disableWebRTCRegistration is enabled', {
881
+ module: _constants.CC_FILE,
882
+ method: _constants.METHODS.CONNECT_WEBSOCKET
883
+ });
675
884
  }
676
885
  if (this.$config && this.$config.allowAutomatedRelogin) {
677
886
  await this.silentRelogin();
887
+ } else {
888
+ _loggerProxy.default.log('Skipping silent relogin: allowAutomatedRelogin is disabled', {
889
+ module: _constants.CC_FILE,
890
+ method: _constants.METHODS.CONNECT_WEBSOCKET
891
+ });
678
892
  }
679
893
  return this.agentConfig;
680
894
  } catch (error) {
895
+ this.updateWellnessSession();
681
896
  _loggerProxy.default.error(`Error during register: ${error}`, {
682
897
  module: _constants.CC_FILE,
683
898
  method: _constants.METHODS.CONNECT_WEBSOCKET
@@ -715,30 +930,37 @@ class ContactCenter extends _webexCore.WebexPlugin {
715
930
  * ```
716
931
  */
717
932
  async stationLogin(data) {
718
- _loggerProxy.default.info('Starting agent station login', {
933
+ const loggerContext = {
719
934
  module: _constants.CC_FILE,
720
935
  method: _constants.METHODS.STATION_LOGIN
721
- });
936
+ };
722
937
  try {
938
+ _loggerProxy.default.log(`Starting agent station login | loginOption: ${data?.loginOption} teamId: ${data?.teamId}`, loggerContext);
723
939
  this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS, _constants4.METRIC_EVENT_NAMES.STATION_LOGIN_FAILED]);
724
940
  const dialPlanEntries = this.agentConfig?.dialPlan?.dialPlanEntity ?? [];
725
- if (data.loginOption === _types.LoginOption.AGENT_DN && !(0, _Utils.isValidDialNumber)(data.dialNumber, dialPlanEntries)) {
726
- const error = new Error('INVALID_DIAL_NUMBER');
727
- // @ts-ignore - adding custom key to the error object
728
- error.details = {
729
- data: {
730
- reason: 'INVALID_DIAL_NUMBER'
731
- }
732
- };
733
- throw error;
941
+ if (data.loginOption === _types.LoginOption.AGENT_DN) {
942
+ _loggerProxy.default.log(`Validating dial number | dialPlanEnabled: ${!!this.agentConfig?.dialPlan} | dialPlanEntryCount: ${dialPlanEntries.length}`, loggerContext);
943
+ if (!(0, _Utils.isValidDialNumber)(data.dialNumber, dialPlanEntries)) {
944
+ _loggerProxy.default.log(`Dial number validation failed | dialNumber: ${data.dialNumber} | dialPlanEntryCount: ${dialPlanEntries.length}`, loggerContext);
945
+ const error = new Error('INVALID_DIAL_NUMBER');
946
+ // @ts-ignore - adding custom key to the error object
947
+ error.details = {
948
+ data: {
949
+ reason: 'INVALID_DIAL_NUMBER'
950
+ }
951
+ };
952
+ throw error;
953
+ }
734
954
  }
955
+ const deviceId = this.getDeviceId(data.loginOption, data.dialNumber);
735
956
  const loginResponse = await this.services.agent.stationLogin({
736
957
  data: {
737
- dialNumber: data.loginOption === _types.LoginOption.BROWSER ? this.agentConfig.agentId : data.dialNumber,
958
+ // BROWSER DN must be webrtc-{agentId} so VPOP/RTMS do not treat a bare UUID as PSTN.
959
+ dialNumber: data.loginOption === _types.LoginOption.BROWSER ? deviceId : data.dialNumber,
738
960
  teamId: data.teamId,
739
961
  deviceType: data.loginOption,
740
962
  isExtension: data.loginOption === _types.LoginOption.EXTENSION,
741
- deviceId: this.getDeviceId(data.loginOption, data.dialNumber),
963
+ deviceId,
742
964
  roles: [_constants3.AGENT],
743
965
  teamName: _constants.EMPTY_STRING,
744
966
  siteId: _constants.EMPTY_STRING,
@@ -746,14 +968,20 @@ class ContactCenter extends _webexCore.WebexPlugin {
746
968
  auxCodeId: _constants.EMPTY_STRING
747
969
  }
748
970
  });
749
- if (this.agentConfig.webRtcEnabled && data.loginOption === _types.LoginOption.BROWSER) {
971
+ if (this.agentConfig.webRtcEnabled && data.loginOption === _types.LoginOption.BROWSER && !this.isWebRTCRegistrationDisabled()) {
750
972
  await this.webCallingService.registerWebCallingLine();
973
+ } else if (this.agentConfig.webRtcEnabled && data.loginOption === _types.LoginOption.BROWSER && this.isWebRTCRegistrationDisabled()) {
974
+ _loggerProxy.default.info('Skipping web calling line registration because disableWebRTCRegistration is enabled', {
975
+ module: _constants.CC_FILE,
976
+ method: _constants.METHODS.STATION_LOGIN
977
+ });
751
978
  }
752
979
  const resp = await loginResponse;
753
980
  const {
754
981
  channelsMap,
755
982
  ...loginData
756
983
  } = resp.data;
984
+ this.updateWellnessSession(resp.data.agentSessionId);
757
985
  this.agentConfig.currentTeamId = resp.data.teamId;
758
986
  const response = {
759
987
  ...loginData,
@@ -766,6 +994,13 @@ class ContactCenter extends _webexCore.WebexPlugin {
766
994
  notifsTrackingId: resp.trackingId
767
995
  };
768
996
  this.webCallingService.setLoginOption(data.loginOption);
997
+ if (this.agentConfig) {
998
+ this.agentConfig.deviceType = data.loginOption;
999
+ if (data.loginOption === _types.LoginOption.AGENT_DN || data.loginOption === _types.LoginOption.EXTENSION) {
1000
+ this.agentConfig.defaultDn = data.dialNumber;
1001
+ this.agentConfig.dn = data.dialNumber;
1002
+ }
1003
+ }
769
1004
  this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS, {
770
1005
  ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(resp),
771
1006
  loginType: data.loginOption,
@@ -778,6 +1013,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
778
1013
  method: _constants.METHODS.STATION_LOGIN,
779
1014
  trackingId: resp.trackingId
780
1015
  });
1016
+ await this.ensureWxAppPostStationLogin();
781
1017
  return response;
782
1018
  } catch (error) {
783
1019
  const failure = error.details;
@@ -832,6 +1068,8 @@ class ContactCenter extends _webexCore.WebexPlugin {
832
1068
  if (this.webCallingService) {
833
1069
  this.webCallingService.deregisterWebCallingLine();
834
1070
  }
1071
+ await this.teardownWxAppLocalState();
1072
+ this.updateWellnessSession();
835
1073
  _loggerProxy.default.log(`Agent station logout completed successfully`, {
836
1074
  module: _constants.CC_FILE,
837
1075
  method: _constants.METHODS.STATION_LOGOUT,
@@ -952,6 +1190,63 @@ class ContactCenter extends _webexCore.WebexPlugin {
952
1190
  }
953
1191
  }
954
1192
 
1193
+ /**
1194
+ * Returns the system-managed `WellbeingBreak` idle code for the current registration.
1195
+ * The value is fetched outside Desktop Profile filtering and cached until deregistration.
1196
+ * @returns The normalized system idle-code entity
1197
+ * @throws Structured Contact Center error when wellness is disabled or the code is unavailable
1198
+ * @example
1199
+ * const idleCode = await webex.cc.getWellbeingBreakIdleCode();
1200
+ * @public
1201
+ */
1202
+ async getWellbeingBreakIdleCode() {
1203
+ const method = _constants.METHODS.GET_WELLBEING_BREAK_IDLE_CODE;
1204
+ try {
1205
+ if (!this.agentConfig?.isWellnessBreakEnabled) {
1206
+ const validationError = new Error('WELLNESS_BREAK_NOT_ENABLED');
1207
+ validationError.details = {
1208
+ type: 'SDK_VALIDATION_ERROR',
1209
+ orgId: this.$webex.credentials.getOrgId(),
1210
+ trackingId: _constants.EMPTY_STRING,
1211
+ data: {
1212
+ reason: 'WELLNESS_BREAK_NOT_ENABLED'
1213
+ }
1214
+ };
1215
+ throw validationError;
1216
+ }
1217
+ if (this.wellbeingBreakIdleCode) {
1218
+ return this.wellbeingBreakIdleCode;
1219
+ }
1220
+ this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS, _constants4.METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED]);
1221
+ const registrationGeneration = this.wellnessRegistrationGeneration;
1222
+ const orgId = this.$webex.credentials.getOrgId();
1223
+ const idleCode = await this.services.config.getWellbeingBreakIdleCode(orgId);
1224
+ if (registrationGeneration !== this.wellnessRegistrationGeneration || orgId !== this.$webex.credentials.getOrgId() || !this.agentConfig?.isWellnessBreakEnabled) {
1225
+ const staleRegistrationError = new Error('WELLNESS_BREAK_REGISTRATION_CHANGED');
1226
+ staleRegistrationError.details = {
1227
+ type: 'SDK_VALIDATION_ERROR',
1228
+ orgId,
1229
+ trackingId: _constants.EMPTY_STRING,
1230
+ data: {
1231
+ reason: 'WELLNESS_BREAK_REGISTRATION_CHANGED'
1232
+ }
1233
+ };
1234
+ throw staleRegistrationError;
1235
+ }
1236
+ this.wellbeingBreakIdleCode = idleCode;
1237
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS, {}, ['operational']);
1238
+ return idleCode;
1239
+ } catch (error) {
1240
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED, {
1241
+ reason: error instanceof Error ? error.message : _constants.UNKNOWN_ERROR
1242
+ }, ['operational']);
1243
+ const {
1244
+ error: detailedError
1245
+ } = (0, _Utils.getErrorDetails)(error, method, _constants.CC_FILE);
1246
+ throw detailedError;
1247
+ }
1248
+ }
1249
+
955
1250
  /**
956
1251
  * Processes incoming websocket messages and emits corresponding events
957
1252
  * Handles various event types including agent state changes, login events,
@@ -985,8 +1280,14 @@ class ContactCenter extends _webexCore.WebexPlugin {
985
1280
  };
986
1281
  this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WEBSOCKET_EVENT_RECEIVED, metricsPayload, ['operational']);
987
1282
  }
1283
+ if (this.handleWellnessBreakWebsocketPayload(eventData)) {
1284
+ return;
1285
+ }
988
1286
  switch (eventData.type) {
989
1287
  case _types3.CC_EVENTS.AGENT_MULTI_LOGIN:
1288
+ if (eventData.data?.type === 'AgentMultiLoginCloseSession' && (!eventData.data.agentSessionId || eventData.data.agentSessionId === this.currentAgentSessionId)) {
1289
+ this.updateWellnessSession();
1290
+ }
990
1291
  // @ts-ignore
991
1292
  this.emit(_types2.AGENT_EVENTS.AGENT_MULTI_LOGIN, eventData.data);
992
1293
  break;
@@ -1007,6 +1308,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
1007
1308
  channelsMap,
1008
1309
  ...loginData
1009
1310
  } = eventData.data;
1311
+ this.updateWellnessSession(loginData.agentSessionId);
1010
1312
  const stationLoginData = {
1011
1313
  ...loginData,
1012
1314
  mmProfile: {
@@ -1028,6 +1330,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
1028
1330
  channelsMap,
1029
1331
  ...loginData
1030
1332
  } = eventData.data;
1333
+ this.updateWellnessSession(loginData.agentSessionId);
1031
1334
  const stationReLoginData = {
1032
1335
  ...loginData,
1033
1336
  mmProfile: {
@@ -1040,8 +1343,8 @@ class ContactCenter extends _webexCore.WebexPlugin {
1040
1343
  };
1041
1344
  // @ts-ignore
1042
1345
  this.emit(_types2.AGENT_EVENTS.AGENT_RELOGIN_SUCCESS, stationReLoginData);
1346
+ break;
1043
1347
  }
1044
- break;
1045
1348
  case _types3.CC_EVENTS.AGENT_STATE_CHANGE_SUCCESS:
1046
1349
  // @ts-ignore
1047
1350
  this.emit(_types2.AGENT_EVENTS.AGENT_STATE_CHANGE_SUCCESS, eventData.data);
@@ -1055,6 +1358,9 @@ class ContactCenter extends _webexCore.WebexPlugin {
1055
1358
  this.emit(_types2.AGENT_EVENTS.AGENT_STATION_LOGIN_FAILED, eventData.data);
1056
1359
  break;
1057
1360
  case _types3.CC_EVENTS.AGENT_LOGOUT_SUCCESS:
1361
+ if (!eventData.data.agentSessionId || eventData.data.agentSessionId === this.currentAgentSessionId) {
1362
+ this.updateWellnessSession();
1363
+ }
1058
1364
  // @ts-ignore
1059
1365
  this.emit(_types2.AGENT_EVENTS.AGENT_LOGOUT_SUCCESS, eventData.data);
1060
1366
  break;
@@ -1071,6 +1377,490 @@ class ContactCenter extends _webexCore.WebexPlugin {
1071
1377
  }
1072
1378
  };
1073
1379
 
1380
+ /**
1381
+ * Checks whether Mobius/WebRTC registration should be skipped by config
1382
+ * @returns {boolean} True when browser WebRTC registration is disabled
1383
+ * @private
1384
+ */
1385
+ isWebRTCRegistrationDisabled() {
1386
+ return this.$config?.disableWebRTCRegistration === true;
1387
+ }
1388
+
1389
+ /**
1390
+ * Validates contact-center plugin configuration before service initialization
1391
+ * @private
1392
+ */
1393
+ validatePluginConfig() {
1394
+ if (this.$config?.disableWebRTCRegistration === true && this.$config?.allowMultiLogin === false) {
1395
+ throw new Error('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.');
1396
+ }
1397
+ }
1398
+
1399
+ /**
1400
+ * Whether wxApp thick-client answer is enabled for this SDK instance.
1401
+ * @public
1402
+ */
1403
+ isWxBetterTogetherEnabled() {
1404
+ return this.$config?.enableWxBetterTogether === true;
1405
+ }
1406
+
1407
+ /**
1408
+ * Current station login option (Extension, Dial Number, or Browser/Desktop).
1409
+ * Prefer webCallingService — updated on every stationLogin before wxApp hooks run.
1410
+ * @private
1411
+ */
1412
+ getCurrentStationLoginOption() {
1413
+ return this.webCallingService?.loginOption ?? this.agentConfig?.deviceType;
1414
+ }
1415
+
1416
+ /**
1417
+ * wxApp thick-client answer is supported for Extension and Dial Number station login only —
1418
+ * not Browser/Desktop (WebRTC) login.
1419
+ * @private
1420
+ */
1421
+ assertWxAppStationLoginSupportedForEnable() {
1422
+ const loginOption = this.getCurrentStationLoginOption();
1423
+ if (loginOption !== _types.LoginOption.EXTENSION && loginOption !== _types.LoginOption.AGENT_DN) {
1424
+ throw new Error('setManageWebexCallingInWxcc requires EXTENSION or AGENT_DN station login. Complete station login before enabling.');
1425
+ }
1426
+ }
1427
+
1428
+ /**
1429
+ * Clears runtime wxApp config after logout/deregister so stale flags do not apply on relogin.
1430
+ * @private
1431
+ */
1432
+ resetEnableWxBetterTogetherConfig() {
1433
+ if (this.$config) {
1434
+ this.$config.enableWxBetterTogether = false;
1435
+ }
1436
+ this.taskManager.applyEnableWxBetterTogether(false);
1437
+ }
1438
+
1439
+ /**
1440
+ * Runtime enable/disable for wxApp Better Together (toast suppression + Mercury mute sync).
1441
+ *
1442
+ * **Phase 1 (WXCC-6026 / MMT): NOT part of the public host contract.**
1443
+ * Hosts must set `webexConfig.cc.enableWxBetterTogether` before `webex.init()` / `cc.register()`.
1444
+ * To change the flag after the SDK is initialized, re-init the SDK with the updated config.
1445
+ *
1446
+ * **Supported public read API:** {@link isWxBetterTogetherEnabled}.
1447
+ * **Supported telephony API:** unified `task.accept()`, `task.decline()`, `task.toggleMute()`,
1448
+ * `task.transmitDtmf()` — SDK routes wxApp calls internally when the init flag is active.
1449
+ *
1450
+ * **Production lifecycle does not call this method.** Station login, silent relogin, logout,
1451
+ * and deregister use {@link ensureWxAppPostStationLogin} and {@link teardownWxAppLocalState}
1452
+ * (usersub publish + Mercury subscribe/unsubscribe based on the init flag).
1453
+ *
1454
+ * This setter remains as a **private implementation** for Phase 2 runtime toggle and unit tests.
1455
+ * It updates `$config.enableWxBetterTogether`, TaskManager flags, cross-client usersub
1456
+ * (`answer-calls-on-wxcc`), and Mercury mute sync; rolls back config on publish/connect failure.
1457
+ *
1458
+ * @param enabled - `true` to enable wxApp answer + usersub suppression; `false` to disable and tear down.
1459
+ * @throws When enabling without login, on unsupported BROWSER station login, or when usersub/Mercury init fails.
1460
+ * @internal
1461
+ * @see enableWxBetterTogether (CCPluginConfig init flag)
1462
+ * @see ensureWxAppPostStationLogin
1463
+ * @see teardownWxAppLocalState
1464
+ */
1465
+ async setManageWebexCallingInWxcc(enabled) {
1466
+ if (enabled && !this.$webex.internal.device?.userId) {
1467
+ throw new Error('Cannot publish answer-calls-on-wxcc: user is not logged in');
1468
+ }
1469
+ if (enabled) {
1470
+ this.assertWxAppStationLoginSupportedForEnable();
1471
+ }
1472
+ const previousEnabled = this.isWxBetterTogetherEnabled();
1473
+ if (this.$config) {
1474
+ this.$config.enableWxBetterTogether = enabled;
1475
+ }
1476
+ this.taskManager.applyEnableWxBetterTogether(enabled);
1477
+ let publishedEnable = false;
1478
+ try {
1479
+ if (enabled) {
1480
+ await this.ensureWxAppMercuryAndSubscribe();
1481
+ await this.publishAnswerOnWebexCrossClientState(true);
1482
+ publishedEnable = true;
1483
+ this.taskManager.syncWxAppMuteFromCallDetailsForAllTasks();
1484
+ } else {
1485
+ await this.publishAnswerOnWebexCrossClientState(false);
1486
+ this.wxAppTelephonyMercurySync.unsubscribe();
1487
+ await this.releaseWxAppMercuryResources();
1488
+ }
1489
+ } catch (error) {
1490
+ if (publishedEnable) {
1491
+ await this.revertWxAppCrossClientPublish();
1492
+ } else if (enabled) {
1493
+ this.wxAppTelephonyMercurySync.unsubscribe();
1494
+ await this.releaseWxAppMercuryResources();
1495
+ }
1496
+ if (this.$config) {
1497
+ this.$config.enableWxBetterTogether = previousEnabled;
1498
+ }
1499
+ this.taskManager.applyEnableWxBetterTogether(previousEnabled);
1500
+ throw error;
1501
+ }
1502
+ }
1503
+ getWxAppTelephonyTaskType() {
1504
+ const loginOption = this.getCurrentStationLoginOption();
1505
+ if (loginOption === _types.LoginOption.BROWSER) {
1506
+ return 'WebRTC';
1507
+ }
1508
+ if (loginOption === _types.LoginOption.EXTENSION || loginOption === _types.LoginOption.AGENT_DN) {
1509
+ return 'Voice';
1510
+ }
1511
+ return 'unknown';
1512
+ }
1513
+ async ensureWxAppPostStationLogin() {
1514
+ const loginOption = this.getCurrentStationLoginOption();
1515
+ const flagEnabled = this.isWxBetterTogetherEnabled();
1516
+ const telephonyTaskType = this.getWxAppTelephonyTaskType();
1517
+ if (loginOption === _types.LoginOption.BROWSER) {
1518
+ if (flagEnabled) {
1519
+ (0, _wxAppDiagnosticLogging.logWxAppSessionReadiness)({
1520
+ enableWxBetterTogether: true,
1521
+ loginOption,
1522
+ wxAppHooksApplied: false,
1523
+ usersubPublished: false,
1524
+ mercurySubscribed: false,
1525
+ telephonyTaskType,
1526
+ skipReason: 'unsupported_browser_login'
1527
+ });
1528
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_SKIPPED, {
1529
+ loginOption,
1530
+ enableWxBetterTogether: true,
1531
+ skipReason: 'unsupported_browser_login'
1532
+ }, ['operational', 'behavioral']);
1533
+ }
1534
+ return;
1535
+ }
1536
+ let publishedEnable = false;
1537
+ let wxAppInitStage = 'mercury';
1538
+ try {
1539
+ if (flagEnabled) {
1540
+ this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS, _constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED]);
1541
+ await this.ensureWxAppMercuryAndSubscribe();
1542
+ wxAppInitStage = 'publish';
1543
+ await this.publishAnswerOnWebexCrossClientState(true);
1544
+ publishedEnable = true;
1545
+ this.taskManager.syncWxAppMuteFromCallDetailsForAllTasks();
1546
+ const mercurySubscribed = this.wxAppTelephonyMercurySync.isSubscribed();
1547
+ const usersubPublished = this.webexCrossClientService.isAnswerCallsStateActive();
1548
+ (0, _wxAppDiagnosticLogging.logWxAppSessionReadiness)({
1549
+ enableWxBetterTogether: true,
1550
+ loginOption,
1551
+ wxAppHooksApplied: true,
1552
+ usersubPublished,
1553
+ mercurySubscribed,
1554
+ telephonyTaskType
1555
+ });
1556
+ const sessionInitReady = mercurySubscribed && usersubPublished;
1557
+ if (sessionInitReady) {
1558
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS, {
1559
+ loginOption,
1560
+ enableWxBetterTogether: true
1561
+ }, ['operational', 'behavioral']);
1562
+ } else {
1563
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED, {
1564
+ loginOption,
1565
+ enableWxBetterTogether: true,
1566
+ skipReason: !usersubPublished ? 'usersub_not_published' : 'mercury_not_subscribed'
1567
+ }, ['operational', 'behavioral']);
1568
+ }
1569
+ } else {
1570
+ await this.ensureWxAppDeviceRegistered();
1571
+ await this.publishAnswerOnWebexCrossClientState(false, {
1572
+ force: true
1573
+ });
1574
+ this.webexCrossClientService.teardown();
1575
+ this.wxAppTelephonyMercurySync.unsubscribe();
1576
+ await this.releaseWxAppMercuryResources();
1577
+ (0, _wxAppDiagnosticLogging.logWxAppSessionReadiness)({
1578
+ enableWxBetterTogether: false,
1579
+ loginOption,
1580
+ wxAppHooksApplied: true,
1581
+ usersubPublished: false,
1582
+ mercurySubscribed: false,
1583
+ telephonyTaskType,
1584
+ skipReason: 'flag_disabled'
1585
+ });
1586
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_SKIPPED, {
1587
+ loginOption,
1588
+ enableWxBetterTogether: false,
1589
+ skipReason: 'flag_disabled'
1590
+ }, ['operational', 'behavioral']);
1591
+ }
1592
+ } catch (error) {
1593
+ _loggerProxy.default.error(`Failed to initialize wxApp post-station-login: ${error}`, {
1594
+ module: _constants.CC_FILE,
1595
+ method: _constants.METHODS.STATION_LOGIN
1596
+ });
1597
+ if (flagEnabled) {
1598
+ const sessionInitFailureReason = wxAppInitStage === 'mercury' ? 'mercury_subscribe_failed' : 'publish_failed';
1599
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED, {
1600
+ loginOption,
1601
+ enableWxBetterTogether: true,
1602
+ skipReason: sessionInitFailureReason,
1603
+ error: error instanceof Error ? error.toString() : String(error)
1604
+ }, ['operational', 'behavioral']);
1605
+ (0, _wxAppDiagnosticLogging.logWxAppSessionReadiness)({
1606
+ enableWxBetterTogether: true,
1607
+ loginOption,
1608
+ wxAppHooksApplied: false,
1609
+ usersubPublished: publishedEnable && this.webexCrossClientService.isAnswerCallsStateActive(),
1610
+ mercurySubscribed: this.wxAppTelephonyMercurySync.isSubscribed(),
1611
+ telephonyTaskType,
1612
+ skipReason: sessionInitFailureReason
1613
+ });
1614
+ if (publishedEnable) {
1615
+ await this.revertWxAppCrossClientPublish();
1616
+ } else {
1617
+ this.wxAppTelephonyMercurySync.unsubscribe();
1618
+ await this.releaseWxAppMercuryResources();
1619
+ }
1620
+ this.resetEnableWxBetterTogetherConfig();
1621
+ } else {
1622
+ this.scheduleForcedFalsePublishRetry();
1623
+ }
1624
+ }
1625
+ }
1626
+ clearWxAppFalsePublishRetryTimer() {
1627
+ if (this.wxAppFalsePublishRetryTimer) {
1628
+ clearTimeout(this.wxAppFalsePublishRetryTimer);
1629
+ this.wxAppFalsePublishRetryTimer = undefined;
1630
+ }
1631
+ }
1632
+ scheduleForcedFalsePublishRetry(retryAttempt = 0) {
1633
+ this.scheduleFalsePublishRetry(retryAttempt, {
1634
+ requireDisabled: true
1635
+ });
1636
+ }
1637
+
1638
+ /**
1639
+ * Retries compensating false publish after rollback failure, regardless of enabled flag.
1640
+ * @private
1641
+ */
1642
+ scheduleCompensatingFalsePublishRetry(retryAttempt = 0) {
1643
+ this.scheduleFalsePublishRetry(retryAttempt);
1644
+ }
1645
+ scheduleFalsePublishRetry(retryAttempt = 0, options = {}) {
1646
+ if (retryAttempt >= ContactCenter.WXAPP_FALSE_PUBLISH_MAX_RETRIES) {
1647
+ return;
1648
+ }
1649
+ if (options?.requireDisabled && this.isWxBetterTogetherEnabled()) {
1650
+ return;
1651
+ }
1652
+ this.clearWxAppFalsePublishRetryTimer();
1653
+ this.wxAppFalsePublishRetryTimer = setTimeout(async () => {
1654
+ this.wxAppFalsePublishRetryTimer = undefined;
1655
+ if (options?.requireDisabled && this.isWxBetterTogetherEnabled()) {
1656
+ return;
1657
+ }
1658
+ if (!options?.requireDisabled && this.isWxBetterTogetherEnabled() && this.webexCrossClientService.isAnswerCallsStateActive()) {
1659
+ return;
1660
+ }
1661
+ try {
1662
+ await this.ensureWxAppDeviceRegistered();
1663
+ await this.publishAnswerOnWebexCrossClientState(false, {
1664
+ force: true
1665
+ });
1666
+ this.webexCrossClientService.teardown();
1667
+ this.wxAppTelephonyMercurySync.unsubscribe();
1668
+ await this.releaseWxAppMercuryResources();
1669
+ } catch (retryError) {
1670
+ _loggerProxy.default.error(`Failed to retry forced answer-calls-on-wxcc false publish: ${retryError}`, {
1671
+ module: _constants.CC_FILE,
1672
+ method: _constants.METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC
1673
+ });
1674
+ this.scheduleFalsePublishRetry(retryAttempt + 1, options);
1675
+ }
1676
+ }, ContactCenter.WXAPP_FALSE_PUBLISH_RETRY_DELAY_MS);
1677
+ }
1678
+
1679
+ /**
1680
+ * Best-effort rollback when usersub true was published but wxApp setup did not complete.
1681
+ * @private
1682
+ */
1683
+ async revertWxAppCrossClientPublish() {
1684
+ try {
1685
+ await this.publishAnswerOnWebexCrossClientState(false, {
1686
+ force: true
1687
+ });
1688
+ } catch (error) {
1689
+ _loggerProxy.default.error(`Failed to publish compensating answer-calls-on-wxcc false during wxApp rollback: ${error}`, {
1690
+ module: _constants.CC_FILE,
1691
+ method: _constants.METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC
1692
+ });
1693
+ this.scheduleCompensatingFalsePublishRetry();
1694
+ }
1695
+ this.webexCrossClientService.teardown();
1696
+ this.wxAppTelephonyMercurySync.unsubscribe();
1697
+ await this.releaseWxAppMercuryResources();
1698
+ }
1699
+ async ensureWxAppDeviceRegistered() {
1700
+ const device = this.$webex.internal.device;
1701
+ if (device && !device.registered && device.register) {
1702
+ await device.register();
1703
+ this.wxAppDeviceRegisteredByCc = true;
1704
+ _loggerProxy.default.log('WxApp: device registered for cross-client publish', {
1705
+ module: _constants.CC_FILE,
1706
+ method: _constants.METHODS.ENSURE_WXAPP_MERCURY_CONNECTED
1707
+ });
1708
+ }
1709
+ }
1710
+ async ensureWxAppMercuryConnected() {
1711
+ const mercury = this.$webex.internal.mercury;
1712
+ await this.ensureWxAppDeviceRegistered();
1713
+ if (mercury && !mercury.connected) {
1714
+ await mercury.connect();
1715
+ this.wxAppMercuryConnectedByCc = true;
1716
+ _loggerProxy.default.log('WxApp mute sync: mercury connected', {
1717
+ module: _constants.CC_FILE,
1718
+ method: _constants.METHODS.ENSURE_WXAPP_MERCURY_CONNECTED
1719
+ });
1720
+ }
1721
+ }
1722
+
1723
+ /**
1724
+ * Disconnects Mercury and unregisters the device when wxApp mute sync opened them.
1725
+ * @private
1726
+ */
1727
+ async releaseWxAppMercuryResources() {
1728
+ const mercury = this.$webex.internal.mercury;
1729
+ const device = this.$webex.internal.device;
1730
+ if (this.wxAppMercuryConnectedByCc && mercury?.connected) {
1731
+ try {
1732
+ await mercury.disconnect();
1733
+ this.wxAppMercuryConnectedByCc = false;
1734
+ _loggerProxy.default.log('WxApp mute sync: mercury disconnected', {
1735
+ module: _constants.CC_FILE,
1736
+ method: _constants.METHODS.ENSURE_WXAPP_MERCURY_CONNECTED
1737
+ });
1738
+ } catch (error) {
1739
+ _loggerProxy.default.error(`Failed to disconnect wxApp Mercury: ${error}`, {
1740
+ module: _constants.CC_FILE,
1741
+ method: _constants.METHODS.ENSURE_WXAPP_MERCURY_CONNECTED
1742
+ });
1743
+ }
1744
+ }
1745
+ if (this.wxAppDeviceRegisteredByCc && device?.registered && device.unregister) {
1746
+ try {
1747
+ // @ts-ignore
1748
+ await device.unregister();
1749
+ this.wxAppDeviceRegisteredByCc = false;
1750
+ _loggerProxy.default.log('WxApp mute sync: device unregistered', {
1751
+ module: _constants.CC_FILE,
1752
+ method: _constants.METHODS.ENSURE_WXAPP_MERCURY_CONNECTED
1753
+ });
1754
+ } catch (error) {
1755
+ _loggerProxy.default.error(`Failed to unregister wxApp device: ${error}`, {
1756
+ module: _constants.CC_FILE,
1757
+ method: _constants.METHODS.ENSURE_WXAPP_MERCURY_CONNECTED
1758
+ });
1759
+ }
1760
+ } else if (this.wxAppDeviceRegisteredByCc) {
1761
+ this.wxAppDeviceRegisteredByCc = false;
1762
+ }
1763
+ }
1764
+
1765
+ /**
1766
+ * Clears wxApp runtime state and feature-owned resources. Publish failures are logged;
1767
+ * optional rethrow for deregister when callers need to surface usersub errors.
1768
+ * @private
1769
+ */
1770
+ async teardownWxAppLocalState(options) {
1771
+ this.clearWxAppFalsePublishRetryTimer();
1772
+ let publishError;
1773
+ try {
1774
+ const shouldForceFalsePublish = this.isWxBetterTogetherEnabled() || this.webexCrossClientService.isAnswerCallsStateActive();
1775
+ await this.publishAnswerOnWebexCrossClientState(false, {
1776
+ force: shouldForceFalsePublish
1777
+ });
1778
+ } catch (error) {
1779
+ publishError = error;
1780
+ _loggerProxy.default.error(`Failed to publish answer-calls-on-wxcc false during wxApp teardown: ${error}`, {
1781
+ module: _constants.CC_FILE,
1782
+ method: _constants.METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC
1783
+ });
1784
+ }
1785
+ this.webexCrossClientService.teardown();
1786
+ this.wxAppTelephonyMercurySync.unsubscribe();
1787
+ await this.releaseWxAppMercuryResources();
1788
+ if (options?.clearInitFlag) {
1789
+ this.resetEnableWxBetterTogetherConfig();
1790
+ }
1791
+ if (publishError) {
1792
+ this.scheduleCompensatingFalsePublishRetry();
1793
+ }
1794
+ if (options?.rethrowPublishError && publishError) {
1795
+ throw publishError;
1796
+ }
1797
+ return {
1798
+ publishError: publishError ?? undefined
1799
+ };
1800
+ }
1801
+ async ensureWxAppMercuryAndSubscribe() {
1802
+ if (!this.isWxBetterTogetherEnabled()) {
1803
+ return;
1804
+ }
1805
+ if (this.getCurrentStationLoginOption() === _types.LoginOption.BROWSER) {
1806
+ return;
1807
+ }
1808
+ const agentId = this.agentConfig?.agentId;
1809
+ if (!agentId) {
1810
+ _loggerProxy.default.error('Cannot subscribe wxApp mute sync: agentId unavailable', {
1811
+ module: _constants.CC_FILE,
1812
+ method: _constants.METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY
1813
+ });
1814
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED, {
1815
+ error: 'agentId unavailable'
1816
+ }, ['operational', 'behavioral']);
1817
+ return;
1818
+ }
1819
+ try {
1820
+ await this.ensureWxAppMercuryConnected();
1821
+ this.wxAppTelephonyMercurySync.subscribe(agentId, (callId, muted) => {
1822
+ this.taskManager.applyWxAppMuteStateFromSync(callId, muted);
1823
+ });
1824
+ } catch (error) {
1825
+ _loggerProxy.default.error(`Failed to ensure wxApp Mercury mute sync: ${error}`, {
1826
+ module: _constants.CC_FILE,
1827
+ method: _constants.METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY
1828
+ });
1829
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED, {
1830
+ error: error instanceof Error ? error.toString() : String(error)
1831
+ }, ['operational', 'behavioral']);
1832
+ this.wxAppTelephonyMercurySync.unsubscribe();
1833
+ await this.releaseWxAppMercuryResources();
1834
+ throw error;
1835
+ }
1836
+ }
1837
+ async publishAnswerOnWebexCrossClientState(enable, options) {
1838
+ if (enable && !this.isWxBetterTogetherEnabled()) {
1839
+ return;
1840
+ }
1841
+ if (!enable && !options?.force && !this.webexCrossClientService.isAnswerCallsStateActive()) {
1842
+ return;
1843
+ }
1844
+ const userId = this.$webex.internal.device?.userId;
1845
+ if (!userId) {
1846
+ if (enable && this.isWxBetterTogetherEnabled()) {
1847
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_USERSUB_PUBLISH_FAILED, {
1848
+ enableWxBetterTogether: true,
1849
+ skipReason: 'user_id_unavailable',
1850
+ error: 'User ID is unavailable for cross-client publish'
1851
+ }, ['operational', 'behavioral']);
1852
+ }
1853
+ return;
1854
+ }
1855
+ await this.webexCrossClientService.setManageWebexCallingInWxcc(enable, {
1856
+ userId,
1857
+ trackPublishMetrics: true
1858
+ });
1859
+ if (enable) {
1860
+ this.clearWxAppFalsePublishRetryTimer();
1861
+ }
1862
+ }
1863
+
1074
1864
  /**
1075
1865
  * Initializes event listeners for the Contact Center service
1076
1866
  * Sets up handlers for connection state changes and other core events
@@ -1102,13 +1892,13 @@ class ContactCenter extends _webexCore.WebexPlugin {
1102
1892
  async handleConnectionLost(msg) {
1103
1893
  if (msg.isConnectionLost) {
1104
1894
  // TODO: Emit an event saying connection is lost
1105
- _loggerProxy.default.info('event=handleConnectionLost | Connection lost', {
1895
+ _loggerProxy.default.log('event=handleConnectionLost | Connection lost', {
1106
1896
  module: _constants.CC_FILE,
1107
1897
  method: _constants.METHODS.HANDLE_CONNECTION_LOST
1108
1898
  });
1109
1899
  } else if (msg.isSocketReconnected) {
1110
1900
  // TODO: Emit an event saying connection is re-estabilished
1111
- _loggerProxy.default.info('event=handleConnectionReconnect | Connection reconnected attempting to request silent relogin', {
1901
+ _loggerProxy.default.log('event=handleConnectionReconnect | Connection reconnected attempting to request silent relogin', {
1112
1902
  module: _constants.CC_FILE,
1113
1903
  method: _constants.METHODS.HANDLE_CONNECTION_LOST
1114
1904
  });
@@ -1123,12 +1913,13 @@ class ContactCenter extends _webexCore.WebexPlugin {
1123
1913
  * @private
1124
1914
  */
1125
1915
  async silentRelogin() {
1126
- _loggerProxy.default.info('Starting silent relogin process', {
1916
+ _loggerProxy.default.log('Starting silent relogin process', {
1127
1917
  module: _constants.CC_FILE,
1128
1918
  method: _constants.METHODS.SILENT_RELOGIN
1129
1919
  });
1130
1920
  try {
1131
1921
  const reLoginResponse = await this.services.agent.reload();
1922
+ this.updateWellnessSession(reLoginResponse.data.agentSessionId);
1132
1923
  const {
1133
1924
  agentId,
1134
1925
  lastStateChangeReason,
@@ -1145,7 +1936,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
1145
1936
  this.agentConfig.currentTeamId = reLoginResponse.data.teamId;
1146
1937
  await this.handleDeviceType(deviceType, dn);
1147
1938
  if (lastStateChangeReason === 'agent-wss-disconnect') {
1148
- _loggerProxy.default.info('event=requestAutoStateChange | Requesting state change to available on socket reconnect', {
1939
+ _loggerProxy.default.log('event=requestAutoStateChange | Requesting state change to available on socket reconnect', {
1149
1940
  module: _constants.CC_FILE,
1150
1941
  method: _constants.METHODS.SILENT_RELOGIN
1151
1942
  });
@@ -1169,8 +1960,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
1169
1960
  }
1170
1961
  this.agentConfig.lastStateAuxCodeId = auxCodeId;
1171
1962
  this.agentConfig.isAgentLoggedIn = true;
1172
- // TODO: https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-626777 Implement the de-register method and close the listener there
1173
- this.services.webSocketManager.on('message', this.handleWebsocketMessage);
1963
+ await this.ensureWxAppPostStationLogin();
1174
1964
  _loggerProxy.default.log(`Silent relogin process completed successfully with login Option: ${reLoginResponse.data.deviceType} teamId: ${reLoginResponse.data.teamId}`, {
1175
1965
  module: _constants.CC_FILE,
1176
1966
  method: _constants.METHODS.SILENT_RELOGIN,
@@ -1205,6 +1995,13 @@ class ContactCenter extends _webexCore.WebexPlugin {
1205
1995
  this.agentConfig.deviceType = deviceType;
1206
1996
  switch (deviceType) {
1207
1997
  case _types.LoginOption.BROWSER:
1998
+ if (this.isWebRTCRegistrationDisabled()) {
1999
+ _loggerProxy.default.info('Skipping web calling line registration because disableWebRTCRegistration is enabled', {
2000
+ module: _constants.CC_FILE,
2001
+ method: _constants.METHODS.HANDLE_DEVICE_TYPE
2002
+ });
2003
+ break;
2004
+ }
1208
2005
  try {
1209
2006
  await this.webCallingService.registerWebCallingLine();
1210
2007
  } catch (error) {
@@ -1444,6 +2241,138 @@ class ContactCenter extends _webexCore.WebexPlugin {
1444
2241
  }
1445
2242
  }
1446
2243
 
2244
+ /**
2245
+ * Skips a campaign preview contact, requesting the next contact from the campaign.
2246
+ *
2247
+ * When a campaign manager reserves a contact for an agent, the agent receives an
2248
+ * `AgentOfferCampaignReservation` event. Instead of accepting, the agent can skip the
2249
+ * preview contact to move to the next contact in the campaign.
2250
+ *
2251
+ * @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
2252
+ * @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
2253
+ * @throws {Error} If the operation fails (network error, etc.)
2254
+ *
2255
+ * @example
2256
+ * ```typescript
2257
+ * webex.cc.on('task:campaignPreviewReservation', async (task) => {
2258
+ * const { interactionId } = task.data;
2259
+ * const campaignId = task.data.interaction.callProcessingDetails.campaignId;
2260
+ *
2261
+ * const result = await webex.cc.skipPreviewContact({ interactionId, campaignId });
2262
+ * });
2263
+ * ```
2264
+ */
2265
+ async skipPreviewContact(payload) {
2266
+ const task = this.taskManager.getTask(payload.interactionId);
2267
+ if (task?.data?.interaction?.callProcessingDetails?.campaignPreviewSkipDisabled === 'true') {
2268
+ _loggerProxy.default.warn('Skip action is disabled for this campaign preview contact', {
2269
+ module: _constants.CC_FILE,
2270
+ method: _constants.METHODS.SKIP_PREVIEW_CONTACT,
2271
+ interactionId: payload.interactionId
2272
+ });
2273
+ throw new Error('Skip action is disabled for this campaign preview contact');
2274
+ }
2275
+ _loggerProxy.default.info('Skipping campaign preview contact', {
2276
+ module: _constants.CC_FILE,
2277
+ method: _constants.METHODS.SKIP_PREVIEW_CONTACT
2278
+ });
2279
+ try {
2280
+ this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS, _constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED]);
2281
+ const result = await this.services.dialer.skipPreviewContact({
2282
+ data: payload
2283
+ });
2284
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS, {
2285
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result),
2286
+ interactionId: payload.interactionId,
2287
+ campaignId: payload.campaignId
2288
+ }, ['behavioral', 'business', 'operational']);
2289
+ _loggerProxy.default.log('Campaign preview contact skipped successfully', {
2290
+ module: _constants.CC_FILE,
2291
+ method: _constants.METHODS.SKIP_PREVIEW_CONTACT,
2292
+ trackingId: result.trackingId,
2293
+ interactionId: payload.interactionId
2294
+ });
2295
+ return result;
2296
+ } catch (error) {
2297
+ const failure = error.details;
2298
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED, {
2299
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(failure),
2300
+ interactionId: payload.interactionId,
2301
+ campaignId: payload.campaignId
2302
+ }, ['behavioral', 'business', 'operational']);
2303
+ const {
2304
+ error: detailedError
2305
+ } = (0, _Utils.getErrorDetails)(error, _constants.METHODS.SKIP_PREVIEW_CONTACT, _constants.CC_FILE);
2306
+ throw detailedError;
2307
+ }
2308
+ }
2309
+
2310
+ /**
2311
+ * Removes a campaign preview contact from the campaign list entirely.
2312
+ *
2313
+ * When a campaign manager reserves a contact for an agent, the agent receives an
2314
+ * `AgentOfferCampaignReservation` event. Instead of accepting or skipping, the agent can
2315
+ * remove the preview contact to permanently take it out of the campaign contact list.
2316
+ *
2317
+ * @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
2318
+ * @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
2319
+ * @throws {Error} If the operation fails (network error, etc.)
2320
+ *
2321
+ * @example
2322
+ * ```typescript
2323
+ * webex.cc.on('task:campaignPreviewReservation', async (task) => {
2324
+ * const { interactionId } = task.data;
2325
+ * const campaignId = task.data.interaction.callProcessingDetails.campaignId;
2326
+ *
2327
+ * const result = await webex.cc.removePreviewContact({ interactionId, campaignId });
2328
+ * });
2329
+ * ```
2330
+ */
2331
+ async removePreviewContact(payload) {
2332
+ const task = this.taskManager.getTask(payload.interactionId);
2333
+ if (task?.data?.interaction?.callProcessingDetails?.campaignPreviewRemoveDisabled === 'true') {
2334
+ _loggerProxy.default.warn('Remove action is disabled for this campaign preview contact', {
2335
+ module: _constants.CC_FILE,
2336
+ method: _constants.METHODS.REMOVE_PREVIEW_CONTACT,
2337
+ interactionId: payload.interactionId
2338
+ });
2339
+ throw new Error('Remove action is disabled for this campaign preview contact');
2340
+ }
2341
+ _loggerProxy.default.info('Removing campaign preview contact', {
2342
+ module: _constants.CC_FILE,
2343
+ method: _constants.METHODS.REMOVE_PREVIEW_CONTACT
2344
+ });
2345
+ try {
2346
+ this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS, _constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED]);
2347
+ const result = await this.services.dialer.removePreviewContact({
2348
+ data: payload
2349
+ });
2350
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS, {
2351
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result),
2352
+ interactionId: payload.interactionId,
2353
+ campaignId: payload.campaignId
2354
+ }, ['behavioral', 'business', 'operational']);
2355
+ _loggerProxy.default.log('Campaign preview contact removed successfully', {
2356
+ module: _constants.CC_FILE,
2357
+ method: _constants.METHODS.REMOVE_PREVIEW_CONTACT,
2358
+ trackingId: result.trackingId,
2359
+ interactionId: payload.interactionId
2360
+ });
2361
+ return result;
2362
+ } catch (error) {
2363
+ const failure = error.details;
2364
+ this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED, {
2365
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(failure),
2366
+ interactionId: payload.interactionId,
2367
+ campaignId: payload.campaignId
2368
+ }, ['behavioral', 'business', 'operational']);
2369
+ const {
2370
+ error: detailedError
2371
+ } = (0, _Utils.getErrorDetails)(error, _constants.METHODS.REMOVE_PREVIEW_CONTACT, _constants.CC_FILE);
2372
+ throw detailedError;
2373
+ }
2374
+ }
2375
+
1447
2376
  /**
1448
2377
  * Fetches outdial ANI (Automatic Number Identification) entries for an outdial ANI ID.
1449
2378
  *
@@ -1648,8 +2577,8 @@ class ContactCenter extends _webexCore.WebexPlugin {
1648
2577
  }
1649
2578
 
1650
2579
  /**
1651
- * Returns paginated entry points for the organization.
1652
- * Thin wrapper around internal EntryPoint instance.
2580
+ * Returns paginated entry points using the EntryPoint service defaults.
2581
+ * Existing request parameters can override those defaults.
1653
2582
  * @public
1654
2583
  */
1655
2584
  async getEntryPoints(params = {}) {
@@ -1657,8 +2586,8 @@ class ContactCenter extends _webexCore.WebexPlugin {
1657
2586
  }
1658
2587
 
1659
2588
  /**
1660
- * Returns paginated contact service queues for the organization.
1661
- * Thin wrapper around internal Queue instance.
2589
+ * Returns paginated contact service queues using the Queue service defaults.
2590
+ * Existing request parameters can override those defaults.
1662
2591
  * @public
1663
2592
  */
1664
2593
  async getQueues(params = {}) {