@webex/contact-center 3.11.0 → 3.12.0-auth-prejoin-fetch.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. package/.sdd/manifest.json +883 -0
  2. package/AGENTS.md +94 -0
  3. package/ai-docs/ARCHITECTURE.md +168 -0
  4. package/ai-docs/CONTRACTS.md +50 -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 +48 -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 +359 -0
  17. package/ai-docs/features/consult-transfer-list-policy/spec/feature-spec.md +362 -0
  18. package/ai-docs/features/generated-spec-conformance-fidelity-remediation/spec/feature-spec.md +117 -0
  19. package/ai-docs/features/residual-warning-coverage-completion/spec/feature-spec.md +203 -0
  20. package/ai-docs/features/validator-code-fidelity-drift-fix/spec/feature-spec.md +315 -0
  21. package/ai-docs/patterns/event-driven-patterns.md +485 -0
  22. package/ai-docs/patterns/testing-patterns.md +480 -0
  23. package/ai-docs/patterns/typescript-patterns.md +365 -0
  24. package/ai-docs/templates/README.md +102 -0
  25. package/ai-docs/templates/documentation/create-agents-md.md +240 -0
  26. package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
  27. package/ai-docs/templates/existing-service/bug-fix.md +254 -0
  28. package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
  29. package/ai-docs/templates/new-method/00-master.md +80 -0
  30. package/ai-docs/templates/new-method/01-requirements.md +232 -0
  31. package/ai-docs/templates/new-method/02-implementation.md +295 -0
  32. package/ai-docs/templates/new-method/03-tests.md +201 -0
  33. package/ai-docs/templates/new-method/04-validation.md +141 -0
  34. package/ai-docs/templates/new-service/00-master.md +109 -0
  35. package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
  36. package/ai-docs/templates/new-service/02-code-generation.md +346 -0
  37. package/ai-docs/templates/new-service/03-integration.md +178 -0
  38. package/ai-docs/templates/new-service/04-test-generation.md +205 -0
  39. package/ai-docs/templates/new-service/05-validation.md +145 -0
  40. package/dist/cc.js +818 -59
  41. package/dist/cc.js.map +1 -1
  42. package/dist/config.js +13 -0
  43. package/dist/config.js.map +1 -1
  44. package/dist/constants.js +31 -3
  45. package/dist/constants.js.map +1 -1
  46. package/dist/index.js +27 -5
  47. package/dist/index.js.map +1 -1
  48. package/dist/metrics/behavioral-events.js +127 -0
  49. package/dist/metrics/behavioral-events.js.map +1 -1
  50. package/dist/metrics/constants.js +34 -3
  51. package/dist/metrics/constants.js.map +1 -1
  52. package/dist/services/AddressBook.js +18 -15
  53. package/dist/services/AddressBook.js.map +1 -1
  54. package/dist/services/AnswerCallOnWebexService.js +174 -0
  55. package/dist/services/AnswerCallOnWebexService.js.map +1 -0
  56. package/dist/services/ApiAiAssistant.js +318 -0
  57. package/dist/services/ApiAiAssistant.js.map +1 -0
  58. package/dist/services/EntryPoint.js +46 -68
  59. package/dist/services/EntryPoint.js.map +1 -1
  60. package/dist/services/Queue.js +27 -22
  61. package/dist/services/Queue.js.map +1 -1
  62. package/dist/services/UserPreference.js +427 -0
  63. package/dist/services/UserPreference.js.map +1 -0
  64. package/dist/services/WebexCrossClientService.js +171 -0
  65. package/dist/services/WebexCrossClientService.js.map +1 -0
  66. package/dist/services/WxAppTelephonyMercurySync.js +93 -0
  67. package/dist/services/WxAppTelephonyMercurySync.js.map +1 -0
  68. package/dist/services/agent/types.js.map +1 -1
  69. package/dist/services/config/Util.js +11 -4
  70. package/dist/services/config/Util.js.map +1 -1
  71. package/dist/services/config/constants.js +45 -8
  72. package/dist/services/config/constants.js.map +1 -1
  73. package/dist/services/config/index.js +41 -2
  74. package/dist/services/config/index.js.map +1 -1
  75. package/dist/services/config/types.js +70 -8
  76. package/dist/services/config/types.js.map +1 -1
  77. package/dist/services/constants.js +27 -1
  78. package/dist/services/constants.js.map +1 -1
  79. package/dist/services/core/Err.js.map +1 -1
  80. package/dist/services/core/Utils.js +122 -25
  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 +119 -30
  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 +22 -6
  88. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  89. package/dist/services/core/websocket/connection-service.js +3 -1
  90. package/dist/services/core/websocket/connection-service.js.map +1 -1
  91. package/dist/services/core/websocket/types.js.map +1 -1
  92. package/dist/services/index.js +6 -0
  93. package/dist/services/index.js.map +1 -1
  94. package/dist/services/task/Task.js +754 -0
  95. package/dist/services/task/Task.js.map +1 -0
  96. package/dist/services/task/TaskFactory.js +49 -0
  97. package/dist/services/task/TaskFactory.js.map +1 -0
  98. package/dist/services/task/TaskManager.js +1073 -447
  99. package/dist/services/task/TaskManager.js.map +1 -1
  100. package/dist/services/task/TaskUtils.js +220 -23
  101. package/dist/services/task/TaskUtils.js.map +1 -1
  102. package/dist/services/task/WebexCallingUtils.js +70 -0
  103. package/dist/services/task/WebexCallingUtils.js.map +1 -0
  104. package/dist/services/task/constants.js +26 -2
  105. package/dist/services/task/constants.js.map +1 -1
  106. package/dist/services/task/contact.js +29 -0
  107. package/dist/services/task/contact.js.map +1 -1
  108. package/dist/services/task/dialer.js +129 -0
  109. package/dist/services/task/dialer.js.map +1 -1
  110. package/dist/services/task/digital/Digital.js +78 -0
  111. package/dist/services/task/digital/Digital.js.map +1 -0
  112. package/dist/services/task/state-machine/TaskStateMachine.js +971 -0
  113. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  114. package/dist/services/task/state-machine/actions.js +572 -0
  115. package/dist/services/task/state-machine/actions.js.map +1 -0
  116. package/dist/services/task/state-machine/constants.js +161 -0
  117. package/dist/services/task/state-machine/constants.js.map +1 -0
  118. package/dist/services/task/state-machine/guards.js +409 -0
  119. package/dist/services/task/state-machine/guards.js.map +1 -0
  120. package/dist/services/task/state-machine/index.js +53 -0
  121. package/dist/services/task/state-machine/index.js.map +1 -0
  122. package/dist/services/task/state-machine/types.js +54 -0
  123. package/dist/services/task/state-machine/types.js.map +1 -0
  124. package/dist/services/task/state-machine/uiControlsComputer.js +703 -0
  125. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  126. package/dist/services/task/taskDataNormalizer.js +99 -0
  127. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  128. package/dist/services/task/types.js +243 -5
  129. package/dist/services/task/types.js.map +1 -1
  130. package/dist/services/task/voice/Voice.js +1380 -0
  131. package/dist/services/task/voice/Voice.js.map +1 -0
  132. package/dist/services/task/voice/WebRTC.js +152 -0
  133. package/dist/services/task/voice/WebRTC.js.map +1 -0
  134. package/dist/services/task/voice/wxAppVoiceMethods.js +201 -0
  135. package/dist/services/task/voice/wxAppVoiceMethods.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 +967 -0
  139. package/dist/types/config.d.ts +79 -0
  140. package/dist/types/constants.d.ts +74 -0
  141. package/dist/types/index.d.ts +201 -0
  142. package/dist/types/logger-proxy.d.ts +71 -0
  143. package/dist/types/metrics/MetricsManager.d.ts +223 -0
  144. package/dist/types/metrics/behavioral-events.d.ts +29 -0
  145. package/dist/types/metrics/constants.d.ts +183 -0
  146. package/dist/types/services/AddressBook.d.ts +75 -0
  147. package/dist/types/services/AnswerCallOnWebexService.d.ts +37 -0
  148. package/dist/types/services/ApiAiAssistant.d.ts +49 -0
  149. package/dist/types/services/EntryPoint.d.ts +69 -0
  150. package/dist/types/services/Queue.d.ts +78 -0
  151. package/dist/types/services/UserPreference.d.ts +118 -0
  152. package/dist/types/services/WebCallingService.d.ts +1 -0
  153. package/dist/types/services/WebexCrossClientService.d.ts +28 -0
  154. package/dist/types/services/WxAppTelephonyMercurySync.d.ts +28 -0
  155. package/dist/types/services/agent/index.d.ts +46 -0
  156. package/dist/types/services/agent/types.d.ts +413 -0
  157. package/dist/types/services/config/Util.d.ts +20 -0
  158. package/dist/types/services/config/constants.d.ts +273 -0
  159. package/dist/types/services/config/index.d.ts +177 -0
  160. package/dist/types/services/config/types.d.ts +1381 -0
  161. package/dist/types/services/constants.d.ts +110 -0
  162. package/dist/types/services/core/Err.d.ts +127 -0
  163. package/dist/types/services/core/GlobalTypes.d.ts +58 -0
  164. package/dist/types/services/core/Utils.d.ts +121 -0
  165. package/dist/types/services/core/WebexRequest.d.ts +23 -0
  166. package/dist/types/services/core/aqm-reqs.d.ts +65 -0
  167. package/dist/types/services/core/constants.d.ts +99 -0
  168. package/dist/types/services/core/types.d.ts +49 -0
  169. package/dist/types/services/core/websocket/WebSocketManager.d.ts +36 -0
  170. package/dist/types/services/core/websocket/connection-service.d.ts +27 -0
  171. package/dist/types/services/core/websocket/keepalive.worker.d.ts +2 -0
  172. package/dist/types/services/core/websocket/types.d.ts +37 -0
  173. package/dist/types/services/index.d.ts +54 -0
  174. package/dist/types/services/task/AutoWrapup.d.ts +40 -0
  175. package/dist/types/services/task/Task.d.ts +175 -0
  176. package/dist/types/services/task/TaskFactory.d.ts +13 -0
  177. package/dist/types/services/task/TaskManager.d.ts +1 -0
  178. package/dist/types/services/task/TaskUtils.d.ts +138 -0
  179. package/dist/types/services/task/WebexCallingUtils.d.ts +11 -0
  180. package/dist/types/services/task/constants.d.ts +94 -0
  181. package/dist/types/services/task/contact.d.ts +73 -0
  182. package/dist/types/services/task/dialer.d.ts +73 -0
  183. package/dist/types/services/task/digital/Digital.d.ts +22 -0
  184. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1398 -0
  185. package/dist/types/services/task/state-machine/actions.d.ts +10 -0
  186. package/dist/types/services/task/state-machine/constants.d.ts +107 -0
  187. package/dist/types/services/task/state-machine/guards.d.ts +103 -0
  188. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  189. package/dist/types/services/task/state-machine/types.d.ts +277 -0
  190. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  191. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  192. package/dist/types/services/task/types.d.ts +1933 -0
  193. package/dist/types/services/task/voice/Voice.d.ts +223 -0
  194. package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
  195. package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +52 -0
  196. package/dist/types/services/wxAppTelephonyUtils.d.ts +5 -0
  197. package/dist/types/types.d.ts +784 -0
  198. package/dist/types/utils/PageCache.d.ts +190 -0
  199. package/dist/types/webex-config.d.ts +53 -0
  200. package/dist/types/webex.d.ts +8 -0
  201. package/dist/types.js +137 -3
  202. package/dist/types.js.map +1 -1
  203. package/dist/utils/PageCache.js +19 -5
  204. package/dist/utils/PageCache.js.map +1 -1
  205. package/dist/webex.js +14 -2
  206. package/dist/webex.js.map +1 -1
  207. package/package.json +16 -12
  208. package/src/cc.ts +983 -60
  209. package/src/config.ts +13 -0
  210. package/src/constants.ts +29 -1
  211. package/src/index.ts +26 -5
  212. package/src/metrics/ai-docs/AGENTS.md +350 -0
  213. package/src/metrics/ai-docs/ARCHITECTURE.md +338 -0
  214. package/src/metrics/ai-docs/metrics-spec.md +860 -0
  215. package/src/metrics/behavioral-events.ts +134 -0
  216. package/src/metrics/constants.ts +39 -3
  217. package/src/services/AddressBook.ts +17 -6
  218. package/src/services/AnswerCallOnWebexService.ts +206 -0
  219. package/src/services/ApiAiAssistant.ts +412 -0
  220. package/src/services/EntryPoint.ts +59 -60
  221. package/src/services/Queue.ts +29 -12
  222. package/src/services/UserPreference.ts +509 -0
  223. package/src/services/WebexCrossClientService.ts +212 -0
  224. package/src/services/WxAppTelephonyMercurySync.ts +115 -0
  225. package/src/services/agent/ai-docs/AGENTS.md +240 -0
  226. package/src/services/agent/ai-docs/ARCHITECTURE.md +304 -0
  227. package/src/services/agent/ai-docs/agent-spec.md +504 -0
  228. package/src/services/agent/types.ts +1 -1
  229. package/src/services/ai-docs/AGENTS.md +386 -0
  230. package/src/services/ai-docs/services-spec.md +497 -0
  231. package/src/services/config/Util.ts +13 -2
  232. package/src/services/config/ai-docs/AGENTS.md +255 -0
  233. package/src/services/config/ai-docs/ARCHITECTURE.md +426 -0
  234. package/src/services/config/ai-docs/config-spec.md +675 -0
  235. package/src/services/config/constants.ts +47 -7
  236. package/src/services/config/index.ts +45 -1
  237. package/src/services/config/types.ts +253 -11
  238. package/src/services/constants.ts +29 -0
  239. package/src/services/core/Err.ts +4 -0
  240. package/src/services/core/Utils.ts +143 -30
  241. package/src/services/core/WebexRequest.ts +3 -1
  242. package/src/services/core/ai-docs/AGENTS.md +381 -0
  243. package/src/services/core/ai-docs/ARCHITECTURE.md +698 -0
  244. package/src/services/core/ai-docs/core-spec.md +787 -0
  245. package/src/services/core/aqm-reqs.ts +125 -32
  246. package/src/services/core/types.ts +2 -0
  247. package/src/services/core/websocket/WebSocketManager.ts +23 -6
  248. package/src/services/core/websocket/connection-service.ts +5 -1
  249. package/src/services/core/websocket/types.ts +1 -1
  250. package/src/services/index.ts +4 -0
  251. package/src/services/task/Task.ts +908 -0
  252. package/src/services/task/TaskFactory.ts +60 -0
  253. package/src/services/task/TaskManager.ts +1291 -513
  254. package/src/services/task/TaskUtils.ts +314 -24
  255. package/src/services/task/WebexCallingUtils.ts +136 -0
  256. package/src/services/task/ai-docs/AGENTS.md +457 -0
  257. package/src/services/task/ai-docs/ARCHITECTURE.md +595 -0
  258. package/src/services/task/ai-docs/task-spec.md +1469 -0
  259. package/src/services/task/constants.ts +26 -0
  260. package/src/services/task/contact.ts +30 -0
  261. package/src/services/task/dialer.ts +136 -1
  262. package/src/services/task/digital/Digital.ts +97 -0
  263. package/src/services/task/state-machine/TaskStateMachine.ts +1313 -0
  264. package/src/services/task/state-machine/actions.ts +741 -0
  265. package/src/services/task/state-machine/ai-docs/AGENTS.md +462 -0
  266. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1146 -0
  267. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2209 -0
  268. package/src/services/task/state-machine/constants.ts +172 -0
  269. package/src/services/task/state-machine/guards.ts +498 -0
  270. package/src/services/task/state-machine/index.ts +28 -0
  271. package/src/services/task/state-machine/types.ts +258 -0
  272. package/src/services/task/state-machine/uiControlsComputer.ts +1135 -0
  273. package/src/services/task/taskDataNormalizer.ts +137 -0
  274. package/src/services/task/types.ts +843 -70
  275. package/src/services/task/voice/Voice.ts +1720 -0
  276. package/src/services/task/voice/WebRTC.ts +191 -0
  277. package/src/services/task/voice/wxAppVoiceMethods.ts +307 -0
  278. package/src/services/wxAppTelephonyUtils.ts +14 -0
  279. package/src/types.ts +238 -11
  280. package/src/utils/AGENTS.md +289 -0
  281. package/src/utils/PageCache.ts +38 -5
  282. package/src/utils/ai-docs/utils-spec.md +391 -0
  283. package/src/webex.js +2 -0
  284. package/test/unit/spec/cc.ts +1856 -122
  285. package/test/unit/spec/logger-proxy.ts +70 -0
  286. package/test/unit/spec/metrics/behavioral-events.ts +18 -0
  287. package/test/unit/spec/services/AddressBook.ts +37 -6
  288. package/test/unit/spec/services/AnswerCallOnWebexService.ts +223 -0
  289. package/test/unit/spec/services/ApiAiAssistant.ts +273 -0
  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 +7 -1
  294. package/test/unit/spec/services/WebexCrossClientService.ts +261 -0
  295. package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +113 -0
  296. package/test/unit/spec/services/config/Util.ts +85 -0
  297. package/test/unit/spec/services/config/index.ts +85 -29
  298. package/test/unit/spec/services/core/Utils.ts +481 -2
  299. package/test/unit/spec/services/core/WebexRequest.ts +3 -1
  300. package/test/unit/spec/services/core/aqm-reqs.ts +113 -1
  301. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +137 -41
  302. package/test/unit/spec/services/core/websocket/connection-service.ts +3 -1
  303. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  304. package/test/unit/spec/services/task/Task.ts +677 -0
  305. package/test/unit/spec/services/task/TaskFactory.ts +99 -0
  306. package/test/unit/spec/services/task/TaskManager.ts +2209 -918
  307. package/test/unit/spec/services/task/TaskUtils.ts +235 -0
  308. package/test/unit/spec/services/task/WebexCallingUtils.ts +153 -0
  309. package/test/unit/spec/services/task/contact.ts +33 -0
  310. package/test/unit/spec/services/task/dialer.ts +372 -96
  311. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  312. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +3433 -0
  313. package/test/unit/spec/services/task/state-machine/guards.ts +839 -0
  314. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  315. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +3101 -0
  316. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  317. package/test/unit/spec/services/task/voice/Voice.ts +1523 -0
  318. package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
  319. package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +459 -0
  320. package/umd/contact-center.min.js +2 -2
  321. package/umd/contact-center.min.js.map +1 -1
  322. package/dist/services/task/index.js +0 -1525
  323. package/dist/services/task/index.js.map +0 -1
  324. package/src/services/task/index.ts +0 -1801
  325. package/test/unit/spec/services/task/index.ts +0 -2184
package/src/cc.ts CHANGED
@@ -25,6 +25,7 @@ import {
25
25
  UploadLogsResponse,
26
26
  UpdateDeviceTypeResponse,
27
27
  GenericError,
28
+ ConfigFlags,
28
29
  } from './types';
29
30
  import {
30
31
  READY,
@@ -39,7 +40,7 @@ import {
39
40
  METHODS,
40
41
  } from './constants';
41
42
  import {AGENT_STATE_AVAILABLE, AGENT_STATE_AVAILABLE_ID} from './services/config/constants';
42
- import {AGENT, WEB_RTC_PREFIX} from './services/constants';
43
+ import {AGENT, RTD_SUBSCRIBE_API, SUBSCRIBE_API, WEB_RTC_PREFIX} from './services/constants';
43
44
  import Services from './services';
44
45
  import WebexRequest from './services/core/WebexRequest';
45
46
  import LoggerProxy from './logger-proxy';
@@ -55,13 +56,24 @@ import {
55
56
  import {ConnectionLostDetails} from './services/core/websocket/types';
56
57
  import TaskManager from './services/task/TaskManager';
57
58
  import WebCallingService from './services/WebCallingService';
58
- import {ITask, TASK_EVENTS, TaskResponse, DialerPayload} from './services/task/types';
59
+ import AnswerCallOnWebexService from './services/AnswerCallOnWebexService';
60
+ import WebexCrossClientService from './services/WebexCrossClientService';
61
+ import WxAppTelephonyMercurySync from './services/WxAppTelephonyMercurySync';
62
+ import {
63
+ ITask,
64
+ TASK_EVENTS,
65
+ TaskResponse,
66
+ DialerPayload,
67
+ PreviewContactPayload,
68
+ } from './services/task/types';
59
69
  import MetricsManager from './metrics/MetricsManager';
60
70
  import {METRIC_EVENT_NAMES} from './metrics/constants';
61
71
  import {Failure} from './services/core/GlobalTypes';
62
- import EntryPoint from './services/EntryPoint';
63
- import AddressBook from './services/AddressBook';
64
- import Queue from './services/Queue';
72
+ import {EntryPoint} from './services/EntryPoint';
73
+ import {AddressBook} from './services/AddressBook';
74
+ import {Queue} from './services/Queue';
75
+ import {ApiAIAssistant} from './services/ApiAiAssistant';
76
+ import {UserPreference} from './services/UserPreference';
65
77
  import type {
66
78
  EntryPointListResponse,
67
79
  EntryPointSearchParams,
@@ -119,6 +131,7 @@ import type {
119
131
  * - `task:established` - Task/call has been connected
120
132
  * - `task:ended` - Task/call has ended
121
133
  * - `task:error` - An error occurred during task handling
134
+ * - `task:campaignPreviewReservation` - Campaign preview contact offered to agent
122
135
  *
123
136
  * @public
124
137
  *
@@ -222,6 +235,34 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
222
235
  */
223
236
  private webCallingService: WebCallingService;
224
237
 
238
+ /**
239
+ * REST telephony for wxApp thick-client answer (WXCC-6026)
240
+ * @private
241
+ */
242
+ private answerCallOnWebexService: AnswerCallOnWebexService;
243
+
244
+ /**
245
+ * usersub cross-client state for Webex App toast suppression
246
+ * @private
247
+ */
248
+ private webexCrossClientService: WebexCrossClientService;
249
+
250
+ /**
251
+ * Mercury telephony mute sync for wxApp thick-client answer
252
+ * @private
253
+ */
254
+ private wxAppTelephonyMercurySync: WxAppTelephonyMercurySync;
255
+
256
+ /** True when wxApp mute sync opened Mercury (Extension/DN-only profiles). */
257
+ private wxAppMercuryConnectedByCc = false;
258
+
259
+ /** True when wxApp mute sync registered the Webex device. */
260
+ private wxAppDeviceRegisteredByCc = false;
261
+
262
+ private static readonly WXAPP_FALSE_PUBLISH_RETRY_DELAY_MS = 30_000;
263
+ private static readonly WXAPP_FALSE_PUBLISH_MAX_RETRIES = 3;
264
+ private wxAppFalsePublishRetryTimer?: ReturnType<typeof setTimeout>;
265
+
225
266
  /**
226
267
  * Core service managers for Contact Center operations
227
268
  * Includes agent, connection, and configuration services
@@ -317,7 +358,45 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
317
358
  * });
318
359
  * ```
319
360
  */
320
- private queue: Queue;
361
+ public queue: Queue;
362
+
363
+ /**
364
+ * API instance for managing Webex Contact Center user preferences
365
+ * Provides functionality to get, create, update, and delete user preferences
366
+ * @type {UserPreference}
367
+ * @public
368
+ * @example
369
+ * ```typescript
370
+ * const cc = webex.cc;
371
+ * await cc.register();
372
+ * await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' });
373
+ *
374
+ * // Get user preferences for current user
375
+ * const preferences = await cc.userPreference.getUserPreference();
376
+ *
377
+ * // Create new user preferences
378
+ * const newPrefs = await cc.userPreference.createUserPreference({
379
+ * userId: 'user123',
380
+ * preferences: { e911Reminder: true }
381
+ * });
382
+ *
383
+ * // Update user preferences
384
+ * const updatedPrefs = await cc.userPreference.updateUserPreference('user123', {
385
+ * preferences: { e911Reminder: false }
386
+ * });
387
+ *
388
+ * // Delete user preferences
389
+ * await cc.userPreference.deleteUserPreference('user123');
390
+ * ```
391
+ */
392
+ public userPreference: UserPreference;
393
+
394
+ /**
395
+ * API instance for AI Assistant operations such as transcript controls.
396
+ * @type {ApiAIAssistant}
397
+ * @public
398
+ */
399
+ public apiAIAssistant: ApiAIAssistant;
321
400
 
322
401
  /**
323
402
  * Logger utility for Contact Center plugin
@@ -342,6 +421,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
342
421
  this.$webex.once(READY, () => {
343
422
  // @ts-ignore
344
423
  this.$config = this.config;
424
+ this.validatePluginConfig();
345
425
 
346
426
  /**
347
427
  * This is used for handling the async requests by sending webex.request and wait for corresponding websocket event.
@@ -354,15 +434,21 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
354
434
  webex: this.$webex,
355
435
  connectionConfig: this.getConnectionConfig(),
356
436
  });
357
- this.services.webSocketManager.on('message', this.handleWebsocketMessage);
358
437
 
359
438
  this.webCallingService = new WebCallingService(this.$webex);
439
+ this.answerCallOnWebexService = new AnswerCallOnWebexService(this.$webex);
440
+ this.webexCrossClientService = new WebexCrossClientService(this.$webex);
441
+ this.wxAppTelephonyMercurySync = new WxAppTelephonyMercurySync(this.$webex);
442
+ this.apiAIAssistant = new ApiAIAssistant(this.$webex);
360
443
  this.metricsManager = MetricsManager.getInstance({webex: this.$webex});
361
444
  this.taskManager = TaskManager.getTaskManager(
445
+ this.apiAIAssistant,
362
446
  this.services.contact,
363
447
  this.webCallingService,
364
- this.services.webSocketManager
448
+ this.services.webSocketManager,
449
+ this.services.rtdWebSocketManager
365
450
  );
451
+ this.taskManager.setAnswerCallOnWebexService(this.answerCallOnWebexService);
366
452
  this.incomingTaskListener();
367
453
 
368
454
  // Initialize API instances
@@ -370,8 +456,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
370
456
  this.entryPoint = new EntryPoint(this.$webex);
371
457
  this.addressBook = new AddressBook(this.$webex, () => this.agentConfig?.addressBookId);
372
458
  this.queue = new Queue(this.$webex);
373
-
374
- // Initialize logger
459
+ this.userPreference = new UserPreference(this.$webex, () => this.agentConfig?.agentId);
375
460
  LoggerProxy.initialize(this.$webex.logger);
376
461
  });
377
462
  }
@@ -396,6 +481,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
396
481
  this.trigger(TASK_EVENTS.TASK_HYDRATE, task);
397
482
  };
398
483
 
484
+ /**
485
+ * Handles multi-login hydrate events for SDK instances without Mobius registration
486
+ * @private
487
+ * @param {ITask} task The task object associated with the multi-login hydrate
488
+ */
489
+ private handleTaskMultiLoginHydrate = (task: ITask) => {
490
+ // @ts-ignore
491
+ this.trigger(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, task);
492
+ };
493
+
399
494
  /**
400
495
  * Handles task merged events when tasks are combined eg: EPDN merge/transfer
401
496
  * @private
@@ -406,6 +501,20 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
406
501
  this.trigger(TASK_EVENTS.TASK_MERGED, task);
407
502
  };
408
503
 
504
+ /**
505
+ * Handles campaign preview reservation events when a contact is offered to the agent
506
+ * @private
507
+ * @param {ITask} task The campaign reservation task
508
+ */
509
+ private handleCampaignPreviewReservation = (task: ITask) => {
510
+ // @ts-ignore
511
+ this.trigger(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, task);
512
+ };
513
+
514
+ private handleRTDWebsocketMessage = (event: string) => {
515
+ this.taskManager.handleRealtimeWebsocketEvent(event);
516
+ };
517
+
409
518
  /**
410
519
  * Sets up event listeners for incoming tasks and task hydration
411
520
  * Subscribes to task events from the task manager
@@ -414,7 +523,12 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
414
523
  private incomingTaskListener() {
415
524
  this.taskManager.on(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
416
525
  this.taskManager.on(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
526
+ this.taskManager.on(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
417
527
  this.taskManager.on(TASK_EVENTS.TASK_MERGED, this.handleTaskMerged);
528
+ this.taskManager.on(
529
+ TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
530
+ this.handleCampaignPreviewReservation
531
+ );
418
532
  }
419
533
 
420
534
  /**
@@ -454,7 +568,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
454
568
  * ```
455
569
  */
456
570
  public async register(): Promise<Profile> {
457
- LoggerProxy.info('Starting CC SDK registration', {
571
+ LoggerProxy.log('Starting CC SDK registration', {
458
572
  module: CC_FILE,
459
573
  method: METHODS.REGISTER,
460
574
  });
@@ -464,6 +578,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
464
578
  METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_FAILED,
465
579
  ]);
466
580
  this.setupEventListeners();
581
+ this.services.webSocketManager.on('message', this.handleWebsocketMessage);
467
582
 
468
583
  const resp = await this.connectWebsocket();
469
584
  // Ensure 'dn' is always populated from 'defaultDn'
@@ -543,11 +658,21 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
543
658
 
544
659
  this.taskManager.off(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
545
660
  this.taskManager.off(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
661
+ this.taskManager.off(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
662
+ this.taskManager.off(
663
+ TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
664
+ this.handleCampaignPreviewReservation
665
+ );
546
666
  this.taskManager.unregisterIncomingCallEvent();
547
667
 
548
668
  this.services.webSocketManager.off('message', this.handleWebsocketMessage);
669
+ this.services.rtdWebSocketManager.off('message', this.handleRTDWebsocketMessage);
549
670
  this.services.connectionService.off('connectionLost', this.handleConnectionLost);
550
671
 
672
+ const {publishError: wxAppPublishError} = await this.teardownWxAppLocalState({
673
+ clearInitFlag: true,
674
+ });
675
+
551
676
  if (
552
677
  this.agentConfig.webRtcEnabled &&
553
678
  this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER)
@@ -568,10 +693,21 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
568
693
  if (!this.services.webSocketManager.isSocketClosed) {
569
694
  this.services.webSocketManager.close(false, 'Unregistering the SDK');
570
695
  }
696
+ if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
697
+ this.services.rtdWebSocketManager.close(false, 'Unregistering the SDK');
698
+ }
699
+
700
+ if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
701
+ this.services.rtdWebSocketManager.close(false, 'Unregistering the RTD websocket');
702
+ }
571
703
 
572
704
  // Clear any cached agent configuration
573
705
  this.agentConfig = null;
574
706
 
707
+ if (wxAppPublishError) {
708
+ throw wxAppPublishError;
709
+ }
710
+
575
711
  LoggerProxy.log('Deregistered successfully', {
576
712
  module: CC_FILE,
577
713
  method: METHODS.DEREGISTER,
@@ -634,6 +770,9 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
634
770
  * ```
635
771
  */
636
772
  public async getBuddyAgents(data: BuddyAgents): Promise<BuddyAgentsResponse> {
773
+ const {action, state, mediaType = 'telephony'} = data;
774
+ const buddyAgentState = state ?? (action === 'Transfer' ? AGENT_STATE_AVAILABLE : undefined);
775
+
637
776
  LoggerProxy.info('Fetching buddy agents', {
638
777
  module: CC_FILE,
639
778
  method: METHODS.GET_BUDDY_AGENTS,
@@ -644,15 +783,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
644
783
  METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_FAILED,
645
784
  ]);
646
785
  const resp = await this.services.agent.buddyAgents({
647
- data: {agentProfileId: this.agentConfig.agentProfileID, ...data},
786
+ data: {
787
+ agentProfileId: this.agentConfig.agentProfileID,
788
+ mediaType,
789
+ ...(buddyAgentState ? {state: buddyAgentState} : {}),
790
+ },
648
791
  });
649
792
 
650
793
  this.metricsManager.trackEvent(
651
794
  METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_SUCCESS,
652
795
  {
653
796
  ...MetricsManager.getCommonTrackingFieldForAQMResponse(resp),
654
- mediaType: data.mediaType,
655
- buddyAgentState: data.state,
797
+ mediaType,
798
+ buddyAgentState,
656
799
  buddyAgentCount: resp.data.agentList.length,
657
800
  },
658
801
  ['operational']
@@ -672,8 +815,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
672
815
  METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_FAILED,
673
816
  {
674
817
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failureResp),
675
- mediaType: data.mediaType,
676
- buddyAgentState: data.state,
818
+ mediaType,
819
+ buddyAgentState,
677
820
  },
678
821
  ['operational']
679
822
  );
@@ -689,7 +832,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
689
832
  * @private
690
833
  */
691
834
  private async connectWebsocket() {
692
- LoggerProxy.info('Connecting to websocket', {
835
+ LoggerProxy.log('Connecting to websocket', {
693
836
  module: CC_FILE,
694
837
  method: METHODS.CONNECT_WEBSOCKET,
695
838
  });
@@ -697,6 +840,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
697
840
  try {
698
841
  const data = (await this.services.webSocketManager.initWebSocket({
699
842
  body: this.getConnectionConfig(),
843
+ resource: SUBSCRIBE_API,
700
844
  })) as WelcomeEvent;
701
845
 
702
846
  const agentId = data.agentId;
@@ -708,14 +852,64 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
708
852
  method: METHODS.CONNECT_WEBSOCKET,
709
853
  });
710
854
 
855
+ const configFlags: ConfigFlags = {
856
+ isEndTaskEnabled: this.agentConfig.isEndTaskEnabled,
857
+ isEndConsultEnabled: this.agentConfig.isEndConsultEnabled,
858
+ webRtcEnabled: this.agentConfig.webRtcEnabled,
859
+ autoWrapup: this.agentConfig.wrapUpData?.wrapUpProps?.autoWrapup ?? false,
860
+ aiFeature: this.agentConfig.aiFeature,
861
+ consultTransfer: {
862
+ allowConsultToQueue: this.agentConfig.allowConsultToQueue,
863
+ accessQueue: this.agentConfig.accessQueue,
864
+ accessEntryPoint: this.agentConfig.accessEntryPoint,
865
+ accessBuddyTeam: this.agentConfig.accessBuddyTeam,
866
+ },
867
+ enableWxBetterTogether: this.isWxBetterTogetherEnabled(),
868
+ };
869
+ this.taskManager.setConfigFlags(configFlags);
711
870
  // TODO: Make profile a singleton to make it available throughout app/sdk so we dont need to inject info everywhere
712
871
  this.taskManager.setWrapupData(this.agentConfig.wrapUpData);
713
872
  this.taskManager.setAgentId(this.agentConfig.agentId);
714
873
  this.taskManager.setWebRtcEnabled(this.agentConfig.webRtcEnabled);
874
+ this.apiAIAssistant.setAIFeatureFlags(this.agentConfig.aiFeature);
875
+
876
+ /**
877
+ * RTD websocket currently supports realtime transcripts and suggested responses.
878
+ * Extend this condition when additional AI RTD features are introduced.
879
+ */
880
+ if (
881
+ this.agentConfig.aiFeature?.realtimeTranscripts?.enable ||
882
+ this.agentConfig.aiFeature?.suggestedResponses?.enable
883
+ ) {
884
+ LoggerProxy.info('Connecting to RTD websocket', {
885
+ module: CC_FILE,
886
+ method: METHODS.CONNECT_WEBSOCKET,
887
+ });
888
+
889
+ this.services.rtdWebSocketManager
890
+ .initWebSocket({
891
+ body: this.getConnectionConfig(),
892
+ resource: RTD_SUBSCRIBE_API,
893
+ })
894
+ .then(() => {
895
+ LoggerProxy.log('RTD websocket connected successfully', {
896
+ module: CC_FILE,
897
+ method: METHODS.CONNECT_WEBSOCKET,
898
+ });
899
+ this.services.rtdWebSocketManager.on('message', this.handleRTDWebsocketMessage);
900
+ })
901
+ .catch((error) => {
902
+ LoggerProxy.error(`Error connecting to RTD websocket ${error}`, {
903
+ module: CC_FILE,
904
+ method: METHODS.CONNECT_WEBSOCKET,
905
+ });
906
+ });
907
+ }
715
908
 
716
909
  if (
717
910
  this.agentConfig.webRtcEnabled &&
718
- this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER)
911
+ this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER) &&
912
+ !this.isWebRTCRegistrationDisabled()
719
913
  ) {
720
914
  try {
721
915
  await this.$webex.internal.mercury.connect();
@@ -729,10 +923,23 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
729
923
  method: METHODS.CONNECT_WEBSOCKET,
730
924
  });
731
925
  }
926
+ } else if (this.isWebRTCRegistrationDisabled()) {
927
+ LoggerProxy.info(
928
+ 'Skipping Mobius registration because disableWebRTCRegistration is enabled',
929
+ {
930
+ module: CC_FILE,
931
+ method: METHODS.CONNECT_WEBSOCKET,
932
+ }
933
+ );
732
934
  }
733
935
 
734
936
  if (this.$config && this.$config.allowAutomatedRelogin) {
735
937
  await this.silentRelogin();
938
+ } else {
939
+ LoggerProxy.log('Skipping silent relogin: allowAutomatedRelogin is disabled', {
940
+ module: CC_FILE,
941
+ method: METHODS.CONNECT_WEBSOCKET,
942
+ });
736
943
  }
737
944
 
738
945
  return this.agentConfig;
@@ -775,22 +982,37 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
775
982
  * ```
776
983
  */
777
984
  public async stationLogin(data: AgentLogin): Promise<StationLoginResponse> {
778
- LoggerProxy.info('Starting agent station login', {
779
- module: CC_FILE,
780
- method: METHODS.STATION_LOGIN,
781
- });
985
+ const loggerContext = {module: CC_FILE, method: METHODS.STATION_LOGIN};
986
+
782
987
  try {
988
+ LoggerProxy.log(
989
+ `Starting agent station login | loginOption: ${data?.loginOption} teamId: ${data?.teamId}`,
990
+ loggerContext
991
+ );
783
992
  this.metricsManager.timeEvent([
784
993
  METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
785
994
  METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
786
995
  ]);
787
996
 
788
- if (data.loginOption === LoginOption.AGENT_DN && !isValidDialNumber(data.dialNumber)) {
789
- const error = new Error('INVALID_DIAL_NUMBER');
790
- // @ts-ignore - adding custom key to the error object
791
- error.details = {data: {reason: 'INVALID_DIAL_NUMBER'}} as Failure;
997
+ const dialPlanEntries = this.agentConfig?.dialPlan?.dialPlanEntity ?? [];
998
+ if (data.loginOption === LoginOption.AGENT_DN) {
999
+ LoggerProxy.log(
1000
+ `Validating dial number | dialPlanEnabled: ${!!this.agentConfig
1001
+ ?.dialPlan} | dialPlanEntryCount: ${dialPlanEntries.length}`,
1002
+ loggerContext
1003
+ );
1004
+
1005
+ if (!isValidDialNumber(data.dialNumber, dialPlanEntries)) {
1006
+ LoggerProxy.log(
1007
+ `Dial number validation failed | dialNumber: ${data.dialNumber} | dialPlanEntryCount: ${dialPlanEntries.length}`,
1008
+ loggerContext
1009
+ );
1010
+ const error = new Error('INVALID_DIAL_NUMBER');
1011
+ // @ts-ignore - adding custom key to the error object
1012
+ error.details = {data: {reason: 'INVALID_DIAL_NUMBER'}} as Failure;
792
1013
 
793
- throw error;
1014
+ throw error;
1015
+ }
794
1016
  }
795
1017
 
796
1018
  const loginResponse = await this.services.agent.stationLogin({
@@ -809,8 +1031,24 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
809
1031
  },
810
1032
  });
811
1033
 
812
- if (this.agentConfig.webRtcEnabled && data.loginOption === LoginOption.BROWSER) {
1034
+ if (
1035
+ this.agentConfig.webRtcEnabled &&
1036
+ data.loginOption === LoginOption.BROWSER &&
1037
+ !this.isWebRTCRegistrationDisabled()
1038
+ ) {
813
1039
  await this.webCallingService.registerWebCallingLine();
1040
+ } else if (
1041
+ this.agentConfig.webRtcEnabled &&
1042
+ data.loginOption === LoginOption.BROWSER &&
1043
+ this.isWebRTCRegistrationDisabled()
1044
+ ) {
1045
+ LoggerProxy.info(
1046
+ 'Skipping web calling line registration because disableWebRTCRegistration is enabled',
1047
+ {
1048
+ module: CC_FILE,
1049
+ method: METHODS.STATION_LOGIN,
1050
+ }
1051
+ );
814
1052
  }
815
1053
 
816
1054
  const resp = await loginResponse;
@@ -828,6 +1066,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
828
1066
  };
829
1067
 
830
1068
  this.webCallingService.setLoginOption(data.loginOption);
1069
+ if (this.agentConfig) {
1070
+ this.agentConfig.deviceType = data.loginOption;
1071
+ if (
1072
+ data.loginOption === LoginOption.AGENT_DN ||
1073
+ data.loginOption === LoginOption.EXTENSION
1074
+ ) {
1075
+ this.agentConfig.defaultDn = data.dialNumber;
1076
+ this.agentConfig.dn = data.dialNumber;
1077
+ }
1078
+ }
831
1079
  this.metricsManager.trackEvent(
832
1080
  METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
833
1081
  {
@@ -849,6 +1097,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
849
1097
  }
850
1098
  );
851
1099
 
1100
+ await this.ensureWxAppPostStationLogin();
1101
+
852
1102
  return response;
853
1103
  } catch (error) {
854
1104
  const failure = error.details as Failure;
@@ -917,6 +1167,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
917
1167
  this.webCallingService.deregisterWebCallingLine();
918
1168
  }
919
1169
 
1170
+ await this.teardownWxAppLocalState();
1171
+
920
1172
  LoggerProxy.log(`Agent station logout completed successfully`, {
921
1173
  module: CC_FILE,
922
1174
  method: METHODS.STATION_LOGOUT,
@@ -1172,6 +1424,454 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1172
1424
  }
1173
1425
  };
1174
1426
 
1427
+ /**
1428
+ * Checks whether Mobius/WebRTC registration should be skipped by config
1429
+ * @returns {boolean} True when browser WebRTC registration is disabled
1430
+ * @private
1431
+ */
1432
+ private isWebRTCRegistrationDisabled(): boolean {
1433
+ return this.$config?.disableWebRTCRegistration === true;
1434
+ }
1435
+
1436
+ /**
1437
+ * Validates contact-center plugin configuration before service initialization
1438
+ * @private
1439
+ */
1440
+ private validatePluginConfig(): void {
1441
+ if (
1442
+ this.$config?.disableWebRTCRegistration === true &&
1443
+ this.$config?.allowMultiLogin === false
1444
+ ) {
1445
+ throw new Error(
1446
+ '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.'
1447
+ );
1448
+ }
1449
+ }
1450
+
1451
+ /**
1452
+ * Whether wxApp thick-client answer is enabled for this SDK instance.
1453
+ * @public
1454
+ */
1455
+ public isWxBetterTogetherEnabled(): boolean {
1456
+ return this.$config?.enableWxBetterTogether === true;
1457
+ }
1458
+
1459
+ /**
1460
+ * Current station login option (Extension, Dial Number, or Browser/Desktop).
1461
+ * Prefer webCallingService — updated on every stationLogin before wxApp hooks run.
1462
+ * @private
1463
+ */
1464
+ private getCurrentStationLoginOption(): LoginOption | undefined {
1465
+ return this.webCallingService?.loginOption ?? this.agentConfig?.deviceType;
1466
+ }
1467
+
1468
+ /**
1469
+ * wxApp thick-client answer is supported for Extension and Dial Number station login only —
1470
+ * not Browser/Desktop (WebRTC) login.
1471
+ * @private
1472
+ */
1473
+ private assertWxAppStationLoginSupportedForEnable(): void {
1474
+ const loginOption = this.getCurrentStationLoginOption();
1475
+ if (loginOption !== LoginOption.EXTENSION && loginOption !== LoginOption.AGENT_DN) {
1476
+ throw new Error(
1477
+ 'setManageWebexCallingInWxcc requires EXTENSION or AGENT_DN station login. Complete station login before enabling.'
1478
+ );
1479
+ }
1480
+ }
1481
+
1482
+ /**
1483
+ * Clears runtime wxApp config after logout/deregister so stale flags do not apply on relogin.
1484
+ * @private
1485
+ */
1486
+ private resetEnableWxBetterTogetherConfig(): void {
1487
+ if (this.$config) {
1488
+ this.$config.enableWxBetterTogether = false;
1489
+ }
1490
+ this.taskManager.applyEnableWxBetterTogether(false);
1491
+ }
1492
+
1493
+ /**
1494
+ * Runtime enable/disable for wxApp Better Together (toast suppression + Mercury mute sync).
1495
+ *
1496
+ * **Phase 1 (WXCC-6026 / MMT): NOT part of the public host contract.**
1497
+ * Hosts must set `webexConfig.cc.enableWxBetterTogether` before `webex.init()` / `cc.register()`.
1498
+ * To change the flag after the SDK is initialized, re-init the SDK with the updated config.
1499
+ *
1500
+ * **Supported public read API:** {@link isWxBetterTogetherEnabled}.
1501
+ * **Supported telephony API:** unified `task.accept()`, `task.decline()`, `task.toggleMute()`,
1502
+ * `task.transmitDtmf()` — SDK routes wxApp calls internally when the init flag is active.
1503
+ *
1504
+ * **Production lifecycle does not call this method.** Station login, silent relogin, logout,
1505
+ * and deregister use {@link ensureWxAppPostStationLogin} and {@link teardownWxAppLocalState}
1506
+ * (usersub publish + Mercury subscribe/unsubscribe based on the init flag).
1507
+ *
1508
+ * This setter remains as a **private implementation** for Phase 2 runtime toggle and unit tests.
1509
+ * It updates `$config.enableWxBetterTogether`, TaskManager flags, cross-client usersub
1510
+ * (`answer-calls-on-wxcc`), and Mercury mute sync; rolls back config on publish/connect failure.
1511
+ *
1512
+ * @param enabled - `true` to enable wxApp answer + usersub suppression; `false` to disable and tear down.
1513
+ * @throws When enabling without login, on unsupported BROWSER station login, or when usersub/Mercury init fails.
1514
+ * @internal
1515
+ * @see enableWxBetterTogether (CCPluginConfig init flag)
1516
+ * @see ensureWxAppPostStationLogin
1517
+ * @see teardownWxAppLocalState
1518
+ */
1519
+ private async setManageWebexCallingInWxcc(enabled: boolean): Promise<void> {
1520
+ if (enabled && !this.$webex.internal.device?.userId) {
1521
+ throw new Error('Cannot publish answer-calls-on-wxcc: user is not logged in');
1522
+ }
1523
+
1524
+ if (enabled) {
1525
+ this.assertWxAppStationLoginSupportedForEnable();
1526
+ }
1527
+
1528
+ const previousEnabled = this.isWxBetterTogetherEnabled();
1529
+
1530
+ if (this.$config) {
1531
+ this.$config.enableWxBetterTogether = enabled;
1532
+ }
1533
+
1534
+ this.taskManager.applyEnableWxBetterTogether(enabled);
1535
+
1536
+ let publishedEnable = false;
1537
+
1538
+ try {
1539
+ if (enabled) {
1540
+ await this.ensureWxAppMercuryAndSubscribe();
1541
+ await this.publishAnswerOnWebexCrossClientState(true);
1542
+ publishedEnable = true;
1543
+ this.taskManager.syncWxAppMuteFromCallDetailsForAllTasks();
1544
+ } else {
1545
+ await this.publishAnswerOnWebexCrossClientState(false);
1546
+ this.wxAppTelephonyMercurySync.unsubscribe();
1547
+ await this.releaseWxAppMercuryResources();
1548
+ }
1549
+ } catch (error) {
1550
+ if (publishedEnable) {
1551
+ await this.revertWxAppCrossClientPublish();
1552
+ } else if (enabled) {
1553
+ this.wxAppTelephonyMercurySync.unsubscribe();
1554
+ await this.releaseWxAppMercuryResources();
1555
+ }
1556
+
1557
+ if (this.$config) {
1558
+ this.$config.enableWxBetterTogether = previousEnabled;
1559
+ }
1560
+ this.taskManager.applyEnableWxBetterTogether(previousEnabled);
1561
+ throw error;
1562
+ }
1563
+ }
1564
+
1565
+ private async ensureWxAppPostStationLogin(): Promise<void> {
1566
+ if (this.getCurrentStationLoginOption() === LoginOption.BROWSER) {
1567
+ return;
1568
+ }
1569
+
1570
+ let publishedEnable = false;
1571
+
1572
+ try {
1573
+ if (this.isWxBetterTogetherEnabled()) {
1574
+ await this.ensureWxAppMercuryAndSubscribe();
1575
+ await this.publishAnswerOnWebexCrossClientState(true);
1576
+ publishedEnable = true;
1577
+ this.taskManager.syncWxAppMuteFromCallDetailsForAllTasks();
1578
+ } else {
1579
+ await this.ensureWxAppDeviceRegistered();
1580
+ await this.publishAnswerOnWebexCrossClientState(false, {force: true});
1581
+ this.webexCrossClientService.teardown();
1582
+ this.wxAppTelephonyMercurySync.unsubscribe();
1583
+ await this.releaseWxAppMercuryResources();
1584
+ }
1585
+ } catch (error) {
1586
+ LoggerProxy.error(`Failed to initialize wxApp post-station-login: ${error}`, {
1587
+ module: CC_FILE,
1588
+ method: METHODS.STATION_LOGIN,
1589
+ });
1590
+
1591
+ if (this.isWxBetterTogetherEnabled()) {
1592
+ if (publishedEnable) {
1593
+ await this.revertWxAppCrossClientPublish();
1594
+ } else {
1595
+ this.wxAppTelephonyMercurySync.unsubscribe();
1596
+ await this.releaseWxAppMercuryResources();
1597
+ }
1598
+ this.resetEnableWxBetterTogetherConfig();
1599
+ } else {
1600
+ this.scheduleForcedFalsePublishRetry();
1601
+ }
1602
+ }
1603
+ }
1604
+
1605
+ private clearWxAppFalsePublishRetryTimer(): void {
1606
+ if (this.wxAppFalsePublishRetryTimer) {
1607
+ clearTimeout(this.wxAppFalsePublishRetryTimer);
1608
+ this.wxAppFalsePublishRetryTimer = undefined;
1609
+ }
1610
+ }
1611
+
1612
+ private scheduleForcedFalsePublishRetry(retryAttempt = 0): void {
1613
+ this.scheduleFalsePublishRetry(retryAttempt, {requireDisabled: true});
1614
+ }
1615
+
1616
+ /**
1617
+ * Retries compensating false publish after rollback failure, regardless of enabled flag.
1618
+ * @private
1619
+ */
1620
+ private scheduleCompensatingFalsePublishRetry(retryAttempt = 0): void {
1621
+ this.scheduleFalsePublishRetry(retryAttempt);
1622
+ }
1623
+
1624
+ private scheduleFalsePublishRetry(
1625
+ retryAttempt = 0,
1626
+ options: {requireDisabled?: boolean} = {}
1627
+ ): void {
1628
+ if (retryAttempt >= ContactCenter.WXAPP_FALSE_PUBLISH_MAX_RETRIES) {
1629
+ return;
1630
+ }
1631
+
1632
+ if (options?.requireDisabled && this.isWxBetterTogetherEnabled()) {
1633
+ return;
1634
+ }
1635
+
1636
+ this.clearWxAppFalsePublishRetryTimer();
1637
+
1638
+ this.wxAppFalsePublishRetryTimer = setTimeout(async () => {
1639
+ this.wxAppFalsePublishRetryTimer = undefined;
1640
+
1641
+ if (options?.requireDisabled && this.isWxBetterTogetherEnabled()) {
1642
+ return;
1643
+ }
1644
+
1645
+ if (
1646
+ !options?.requireDisabled &&
1647
+ this.isWxBetterTogetherEnabled() &&
1648
+ this.webexCrossClientService.isAnswerCallsStateActive()
1649
+ ) {
1650
+ return;
1651
+ }
1652
+
1653
+ try {
1654
+ await this.ensureWxAppDeviceRegistered();
1655
+ await this.publishAnswerOnWebexCrossClientState(false, {force: true});
1656
+ this.webexCrossClientService.teardown();
1657
+ this.wxAppTelephonyMercurySync.unsubscribe();
1658
+ await this.releaseWxAppMercuryResources();
1659
+ } catch (retryError) {
1660
+ LoggerProxy.error(
1661
+ `Failed to retry forced answer-calls-on-wxcc false publish: ${retryError}`,
1662
+ {
1663
+ module: CC_FILE,
1664
+ method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
1665
+ }
1666
+ );
1667
+ this.scheduleFalsePublishRetry(retryAttempt + 1, options);
1668
+ }
1669
+ }, ContactCenter.WXAPP_FALSE_PUBLISH_RETRY_DELAY_MS);
1670
+ }
1671
+
1672
+ /**
1673
+ * Best-effort rollback when usersub true was published but wxApp setup did not complete.
1674
+ * @private
1675
+ */
1676
+ private async revertWxAppCrossClientPublish(): Promise<void> {
1677
+ try {
1678
+ await this.publishAnswerOnWebexCrossClientState(false, {force: true});
1679
+ } catch (error) {
1680
+ LoggerProxy.error(
1681
+ `Failed to publish compensating answer-calls-on-wxcc false during wxApp rollback: ${error}`,
1682
+ {
1683
+ module: CC_FILE,
1684
+ method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
1685
+ }
1686
+ );
1687
+ this.scheduleCompensatingFalsePublishRetry();
1688
+ }
1689
+
1690
+ this.webexCrossClientService.teardown();
1691
+ this.wxAppTelephonyMercurySync.unsubscribe();
1692
+ await this.releaseWxAppMercuryResources();
1693
+ }
1694
+
1695
+ private async ensureWxAppDeviceRegistered(): Promise<void> {
1696
+ const device = this.$webex.internal.device;
1697
+
1698
+ if (device && !device.registered && device.register) {
1699
+ await device.register();
1700
+ this.wxAppDeviceRegisteredByCc = true;
1701
+ LoggerProxy.log('WxApp: device registered for cross-client publish', {
1702
+ module: CC_FILE,
1703
+ method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
1704
+ });
1705
+ }
1706
+ }
1707
+
1708
+ private async ensureWxAppMercuryConnected(): Promise<void> {
1709
+ const mercury = this.$webex.internal.mercury;
1710
+
1711
+ await this.ensureWxAppDeviceRegistered();
1712
+
1713
+ if (mercury && !mercury.connected) {
1714
+ await mercury.connect();
1715
+ this.wxAppMercuryConnectedByCc = true;
1716
+ LoggerProxy.log('WxApp mute sync: mercury connected', {
1717
+ module: CC_FILE,
1718
+ method: 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
+ private async releaseWxAppMercuryResources(): Promise<void> {
1728
+ const mercury = this.$webex.internal.mercury;
1729
+ const device = this.$webex.internal.device;
1730
+
1731
+ if (this.wxAppMercuryConnectedByCc && mercury?.connected) {
1732
+ try {
1733
+ await mercury.disconnect();
1734
+ this.wxAppMercuryConnectedByCc = false;
1735
+ LoggerProxy.log('WxApp mute sync: mercury disconnected', {
1736
+ module: CC_FILE,
1737
+ method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
1738
+ });
1739
+ } catch (error) {
1740
+ LoggerProxy.error(`Failed to disconnect wxApp Mercury: ${error}`, {
1741
+ module: CC_FILE,
1742
+ method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
1743
+ });
1744
+ }
1745
+ }
1746
+
1747
+ if (this.wxAppDeviceRegisteredByCc && device?.registered && device.unregister) {
1748
+ try {
1749
+ // @ts-ignore
1750
+ await device.unregister();
1751
+ this.wxAppDeviceRegisteredByCc = false;
1752
+ LoggerProxy.log('WxApp mute sync: device unregistered', {
1753
+ module: CC_FILE,
1754
+ method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
1755
+ });
1756
+ } catch (error) {
1757
+ LoggerProxy.error(`Failed to unregister wxApp device: ${error}`, {
1758
+ module: CC_FILE,
1759
+ method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
1760
+ });
1761
+ }
1762
+ } else if (this.wxAppDeviceRegisteredByCc) {
1763
+ this.wxAppDeviceRegisteredByCc = false;
1764
+ }
1765
+ }
1766
+
1767
+ /**
1768
+ * Clears wxApp runtime state and feature-owned resources. Publish failures are logged;
1769
+ * optional rethrow for deregister when callers need to surface usersub errors.
1770
+ * @private
1771
+ */
1772
+ private async teardownWxAppLocalState(options?: {
1773
+ rethrowPublishError?: boolean;
1774
+ clearInitFlag?: boolean;
1775
+ }): Promise<{publishError?: unknown}> {
1776
+ this.clearWxAppFalsePublishRetryTimer();
1777
+ let publishError: unknown;
1778
+
1779
+ try {
1780
+ const shouldForceFalsePublish =
1781
+ this.isWxBetterTogetherEnabled() || this.webexCrossClientService.isAnswerCallsStateActive();
1782
+
1783
+ await this.publishAnswerOnWebexCrossClientState(false, {
1784
+ force: shouldForceFalsePublish,
1785
+ });
1786
+ } catch (error) {
1787
+ publishError = error;
1788
+ LoggerProxy.error(
1789
+ `Failed to publish answer-calls-on-wxcc false during wxApp teardown: ${error}`,
1790
+ {
1791
+ module: CC_FILE,
1792
+ method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
1793
+ }
1794
+ );
1795
+ }
1796
+
1797
+ this.webexCrossClientService.teardown();
1798
+ this.wxAppTelephonyMercurySync.unsubscribe();
1799
+ await this.releaseWxAppMercuryResources();
1800
+
1801
+ if (options?.clearInitFlag) {
1802
+ this.resetEnableWxBetterTogetherConfig();
1803
+ }
1804
+
1805
+ if (publishError) {
1806
+ this.scheduleCompensatingFalsePublishRetry();
1807
+ }
1808
+
1809
+ if (options?.rethrowPublishError && publishError) {
1810
+ throw publishError;
1811
+ }
1812
+
1813
+ return {publishError: publishError ?? undefined};
1814
+ }
1815
+
1816
+ private async ensureWxAppMercuryAndSubscribe(): Promise<void> {
1817
+ if (!this.isWxBetterTogetherEnabled()) {
1818
+ return;
1819
+ }
1820
+
1821
+ if (this.getCurrentStationLoginOption() === LoginOption.BROWSER) {
1822
+ return;
1823
+ }
1824
+
1825
+ const agentId = this.agentConfig?.agentId;
1826
+ if (!agentId) {
1827
+ LoggerProxy.error('Cannot subscribe wxApp mute sync: agentId unavailable', {
1828
+ module: CC_FILE,
1829
+ method: METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY,
1830
+ });
1831
+
1832
+ return;
1833
+ }
1834
+
1835
+ try {
1836
+ await this.ensureWxAppMercuryConnected();
1837
+ this.wxAppTelephonyMercurySync.subscribe(agentId, (callId, muted) => {
1838
+ this.taskManager.applyWxAppMuteStateFromSync(callId, muted);
1839
+ });
1840
+ } catch (error) {
1841
+ LoggerProxy.error(`Failed to ensure wxApp Mercury mute sync: ${error}`, {
1842
+ module: CC_FILE,
1843
+ method: METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY,
1844
+ });
1845
+ this.wxAppTelephonyMercurySync.unsubscribe();
1846
+ await this.releaseWxAppMercuryResources();
1847
+ throw error;
1848
+ }
1849
+ }
1850
+
1851
+ private async publishAnswerOnWebexCrossClientState(
1852
+ enable: boolean,
1853
+ options?: {force?: boolean}
1854
+ ): Promise<void> {
1855
+ if (enable && !this.isWxBetterTogetherEnabled()) {
1856
+ return;
1857
+ }
1858
+
1859
+ if (!enable && !options?.force && !this.webexCrossClientService.isAnswerCallsStateActive()) {
1860
+ return;
1861
+ }
1862
+
1863
+ const userId = this.$webex.internal.device?.userId;
1864
+ if (!userId) {
1865
+ return;
1866
+ }
1867
+
1868
+ await this.webexCrossClientService.setManageWebexCallingInWxcc(enable, {userId});
1869
+
1870
+ if (enable) {
1871
+ this.clearWxAppFalsePublishRetryTimer();
1872
+ }
1873
+ }
1874
+
1175
1875
  /**
1176
1876
  * Initializes event listeners for the Contact Center service
1177
1877
  * Sets up handlers for connection state changes and other core events
@@ -1203,13 +1903,13 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1203
1903
  private async handleConnectionLost(msg: ConnectionLostDetails): Promise<void> {
1204
1904
  if (msg.isConnectionLost) {
1205
1905
  // TODO: Emit an event saying connection is lost
1206
- LoggerProxy.info('event=handleConnectionLost | Connection lost', {
1906
+ LoggerProxy.log('event=handleConnectionLost | Connection lost', {
1207
1907
  module: CC_FILE,
1208
1908
  method: METHODS.HANDLE_CONNECTION_LOST,
1209
1909
  });
1210
1910
  } else if (msg.isSocketReconnected) {
1211
1911
  // TODO: Emit an event saying connection is re-estabilished
1212
- LoggerProxy.info(
1912
+ LoggerProxy.log(
1213
1913
  'event=handleConnectionReconnect | Connection reconnected attempting to request silent relogin',
1214
1914
  {module: CC_FILE, method: METHODS.HANDLE_CONNECTION_LOST}
1215
1915
  );
@@ -1224,7 +1924,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1224
1924
  * @private
1225
1925
  */
1226
1926
  private async silentRelogin(): Promise<void> {
1227
- LoggerProxy.info('Starting silent relogin process', {
1927
+ LoggerProxy.log('Starting silent relogin process', {
1228
1928
  module: CC_FILE,
1229
1929
  method: METHODS.SILENT_RELOGIN,
1230
1930
  });
@@ -1246,7 +1946,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1246
1946
  await this.handleDeviceType(deviceType as LoginOption, dn);
1247
1947
 
1248
1948
  if (lastStateChangeReason === 'agent-wss-disconnect') {
1249
- LoggerProxy.info(
1949
+ LoggerProxy.log(
1250
1950
  'event=requestAutoStateChange | Requesting state change to available on socket reconnect',
1251
1951
  {module: CC_FILE, method: METHODS.SILENT_RELOGIN}
1252
1952
  );
@@ -1275,8 +1975,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1275
1975
  }
1276
1976
  this.agentConfig.lastStateAuxCodeId = auxCodeId;
1277
1977
  this.agentConfig.isAgentLoggedIn = true;
1278
- // TODO: https://jira-eng-gpk2.cisco.com/jira/browse/SPARK-626777 Implement the de-register method and close the listener there
1279
- this.services.webSocketManager.on('message', this.handleWebsocketMessage);
1978
+
1979
+ await this.ensureWxAppPostStationLogin();
1280
1980
 
1281
1981
  LoggerProxy.log(
1282
1982
  `Silent relogin process completed successfully with login Option: ${reLoginResponse.data.deviceType} teamId: ${reLoginResponse.data.teamId}`,
@@ -1317,6 +2017,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1317
2017
  this.agentConfig.deviceType = deviceType;
1318
2018
  switch (deviceType) {
1319
2019
  case LoginOption.BROWSER:
2020
+ if (this.isWebRTCRegistrationDisabled()) {
2021
+ LoggerProxy.info(
2022
+ 'Skipping web calling line registration because disableWebRTCRegistration is enabled',
2023
+ {
2024
+ module: CC_FILE,
2025
+ method: METHODS.HANDLE_DEVICE_TYPE,
2026
+ }
2027
+ );
2028
+ break;
2029
+ }
1320
2030
  try {
1321
2031
  await this.webCallingService.registerWebCallingLine();
1322
2032
  } catch (error) {
@@ -1509,6 +2219,239 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1509
2219
  }
1510
2220
  }
1511
2221
 
2222
+ /**
2223
+ * Accepts a campaign preview contact, initiating the outbound call.
2224
+ *
2225
+ * When a campaign manager reserves a contact for an agent, the agent receives an
2226
+ * `AgentOfferCampaignReservation` event. The agent can then accept the preview contact
2227
+ * to initiate the outbound call.
2228
+ *
2229
+ * @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
2230
+ * @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
2231
+ * @throws {Error} If the operation fails (network error, customer unavailable, etc.)
2232
+ *
2233
+ * @example
2234
+ * ```typescript
2235
+ * webex.cc.on('task:campaignPreviewReservation', async (task) => {
2236
+ * const { interactionId } = task.data;
2237
+ * // campaignId is the campaign name (e.g. "MyCampaign"), not a UUID
2238
+ * const campaignId = task.data.interaction.callProcessingDetails.campaignId;
2239
+ *
2240
+ * const result = await webex.cc.acceptPreviewContact({ interactionId, campaignId });
2241
+ * });
2242
+ * ```
2243
+ */
2244
+ public async acceptPreviewContact(payload: PreviewContactPayload): Promise<TaskResponse> {
2245
+ LoggerProxy.info('Accepting campaign preview contact', {
2246
+ module: CC_FILE,
2247
+ method: METHODS.ACCEPT_PREVIEW_CONTACT,
2248
+ });
2249
+ try {
2250
+ this.metricsManager.timeEvent([
2251
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_SUCCESS,
2252
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_FAILED,
2253
+ ]);
2254
+
2255
+ const result = await this.services.dialer.acceptPreviewContact({data: payload});
2256
+
2257
+ this.metricsManager.trackEvent(
2258
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_SUCCESS,
2259
+ {
2260
+ ...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
2261
+ interactionId: payload.interactionId,
2262
+ campaignId: payload.campaignId,
2263
+ },
2264
+ ['behavioral', 'business', 'operational']
2265
+ );
2266
+
2267
+ LoggerProxy.log('Campaign preview contact accepted successfully', {
2268
+ module: CC_FILE,
2269
+ method: METHODS.ACCEPT_PREVIEW_CONTACT,
2270
+ trackingId: result.trackingId,
2271
+ interactionId: payload.interactionId,
2272
+ });
2273
+
2274
+ return result;
2275
+ } catch (error) {
2276
+ const failure = error.details as Failure;
2277
+ this.metricsManager.trackEvent(
2278
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_FAILED,
2279
+ {
2280
+ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failure),
2281
+ interactionId: payload.interactionId,
2282
+ campaignId: payload.campaignId,
2283
+ },
2284
+ ['behavioral', 'business', 'operational']
2285
+ );
2286
+ const {error: detailedError} = getErrorDetails(
2287
+ error,
2288
+ METHODS.ACCEPT_PREVIEW_CONTACT,
2289
+ CC_FILE
2290
+ );
2291
+ throw detailedError;
2292
+ }
2293
+ }
2294
+
2295
+ /**
2296
+ * Skips a campaign preview contact, requesting the next contact from the campaign.
2297
+ *
2298
+ * When a campaign manager reserves a contact for an agent, the agent receives an
2299
+ * `AgentOfferCampaignReservation` event. Instead of accepting, the agent can skip the
2300
+ * preview contact to move to the next contact in the campaign.
2301
+ *
2302
+ * @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
2303
+ * @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
2304
+ * @throws {Error} If the operation fails (network error, etc.)
2305
+ *
2306
+ * @example
2307
+ * ```typescript
2308
+ * webex.cc.on('task:campaignPreviewReservation', async (task) => {
2309
+ * const { interactionId } = task.data;
2310
+ * const campaignId = task.data.interaction.callProcessingDetails.campaignId;
2311
+ *
2312
+ * const result = await webex.cc.skipPreviewContact({ interactionId, campaignId });
2313
+ * });
2314
+ * ```
2315
+ */
2316
+ public async skipPreviewContact(payload: PreviewContactPayload): Promise<TaskResponse> {
2317
+ const task = this.taskManager.getTask(payload.interactionId);
2318
+ if (task?.data?.interaction?.callProcessingDetails?.campaignPreviewSkipDisabled === 'true') {
2319
+ LoggerProxy.warn('Skip action is disabled for this campaign preview contact', {
2320
+ module: CC_FILE,
2321
+ method: METHODS.SKIP_PREVIEW_CONTACT,
2322
+ interactionId: payload.interactionId,
2323
+ });
2324
+ throw new Error('Skip action is disabled for this campaign preview contact');
2325
+ }
2326
+
2327
+ LoggerProxy.info('Skipping campaign preview contact', {
2328
+ module: CC_FILE,
2329
+ method: METHODS.SKIP_PREVIEW_CONTACT,
2330
+ });
2331
+ try {
2332
+ this.metricsManager.timeEvent([
2333
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS,
2334
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED,
2335
+ ]);
2336
+
2337
+ const result = await this.services.dialer.skipPreviewContact({data: payload});
2338
+
2339
+ this.metricsManager.trackEvent(
2340
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS,
2341
+ {
2342
+ ...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
2343
+ interactionId: payload.interactionId,
2344
+ campaignId: payload.campaignId,
2345
+ },
2346
+ ['behavioral', 'business', 'operational']
2347
+ );
2348
+
2349
+ LoggerProxy.log('Campaign preview contact skipped successfully', {
2350
+ module: CC_FILE,
2351
+ method: METHODS.SKIP_PREVIEW_CONTACT,
2352
+ trackingId: result.trackingId,
2353
+ interactionId: payload.interactionId,
2354
+ });
2355
+
2356
+ return result;
2357
+ } catch (error) {
2358
+ const failure = error.details as Failure;
2359
+ this.metricsManager.trackEvent(
2360
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED,
2361
+ {
2362
+ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failure),
2363
+ interactionId: payload.interactionId,
2364
+ campaignId: payload.campaignId,
2365
+ },
2366
+ ['behavioral', 'business', 'operational']
2367
+ );
2368
+ const {error: detailedError} = getErrorDetails(error, METHODS.SKIP_PREVIEW_CONTACT, CC_FILE);
2369
+ throw detailedError;
2370
+ }
2371
+ }
2372
+
2373
+ /**
2374
+ * Removes a campaign preview contact from the campaign list entirely.
2375
+ *
2376
+ * When a campaign manager reserves a contact for an agent, the agent receives an
2377
+ * `AgentOfferCampaignReservation` event. Instead of accepting or skipping, the agent can
2378
+ * remove the preview contact to permanently take it out of the campaign contact list.
2379
+ *
2380
+ * @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
2381
+ * @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
2382
+ * @throws {Error} If the operation fails (network error, etc.)
2383
+ *
2384
+ * @example
2385
+ * ```typescript
2386
+ * webex.cc.on('task:campaignPreviewReservation', async (task) => {
2387
+ * const { interactionId } = task.data;
2388
+ * const campaignId = task.data.interaction.callProcessingDetails.campaignId;
2389
+ *
2390
+ * const result = await webex.cc.removePreviewContact({ interactionId, campaignId });
2391
+ * });
2392
+ * ```
2393
+ */
2394
+ public async removePreviewContact(payload: PreviewContactPayload): Promise<TaskResponse> {
2395
+ const task = this.taskManager.getTask(payload.interactionId);
2396
+ if (task?.data?.interaction?.callProcessingDetails?.campaignPreviewRemoveDisabled === 'true') {
2397
+ LoggerProxy.warn('Remove action is disabled for this campaign preview contact', {
2398
+ module: CC_FILE,
2399
+ method: METHODS.REMOVE_PREVIEW_CONTACT,
2400
+ interactionId: payload.interactionId,
2401
+ });
2402
+ throw new Error('Remove action is disabled for this campaign preview contact');
2403
+ }
2404
+
2405
+ LoggerProxy.info('Removing campaign preview contact', {
2406
+ module: CC_FILE,
2407
+ method: METHODS.REMOVE_PREVIEW_CONTACT,
2408
+ });
2409
+ try {
2410
+ this.metricsManager.timeEvent([
2411
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS,
2412
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED,
2413
+ ]);
2414
+
2415
+ const result = await this.services.dialer.removePreviewContact({data: payload});
2416
+
2417
+ this.metricsManager.trackEvent(
2418
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS,
2419
+ {
2420
+ ...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
2421
+ interactionId: payload.interactionId,
2422
+ campaignId: payload.campaignId,
2423
+ },
2424
+ ['behavioral', 'business', 'operational']
2425
+ );
2426
+
2427
+ LoggerProxy.log('Campaign preview contact removed successfully', {
2428
+ module: CC_FILE,
2429
+ method: METHODS.REMOVE_PREVIEW_CONTACT,
2430
+ trackingId: result.trackingId,
2431
+ interactionId: payload.interactionId,
2432
+ });
2433
+
2434
+ return result;
2435
+ } catch (error) {
2436
+ const failure = error.details as Failure;
2437
+ this.metricsManager.trackEvent(
2438
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED,
2439
+ {
2440
+ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failure),
2441
+ interactionId: payload.interactionId,
2442
+ campaignId: payload.campaignId,
2443
+ },
2444
+ ['behavioral', 'business', 'operational']
2445
+ );
2446
+ const {error: detailedError} = getErrorDetails(
2447
+ error,
2448
+ METHODS.REMOVE_PREVIEW_CONTACT,
2449
+ CC_FILE
2450
+ );
2451
+ throw detailedError;
2452
+ }
2453
+ }
2454
+
1512
2455
  /**
1513
2456
  * Fetches outdial ANI (Automatic Number Identification) entries for an outdial ANI ID.
1514
2457
  *
@@ -1656,6 +2599,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1656
2599
  /**
1657
2600
  * Updates the agent device type and login configuration.
1658
2601
  * Use this method to change how an agent connects to the contact center system (e.g., switching from browser-based calling to a desk phone extension).
2602
+ * Change to any field of the profile is allowed;
1659
2603
  *
1660
2604
  * @param {AgentDeviceUpdate} data Configuration containing:
1661
2605
  * - loginOption: New device type ('BROWSER', 'EXTENSION', 'AGENT_DN')
@@ -1695,29 +2639,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1695
2639
  });
1696
2640
 
1697
2641
  try {
1698
- // Only block if both loginOption AND teamId remain unchanged
1699
- if (
1700
- this.webCallingService?.loginOption === data.loginOption &&
1701
- data.teamId === this.agentConfig.currentTeamId
1702
- ) {
1703
- const message =
1704
- 'Will not proceed with device update as new Device type is same as current device type and teamId is same as current teamId';
1705
- const err = new Error(message) as GenericError;
1706
- err.details = {
1707
- type: 'Identical Device Change Failure',
1708
- orgId: this.$webex.credentials.getOrgId(),
1709
- trackingId,
1710
- data: {
1711
- agentId: this.agentConfig.agentId,
1712
- reasonCode: 'R002',
1713
- reason: message,
1714
- },
1715
- };
1716
- throw err;
1717
- }
1718
-
1719
2642
  await this.stationLogout({
1720
- logoutReason: 'User requested agent device change',
2643
+ logoutReason: 'User requested agent profile update',
1721
2644
  });
1722
2645
 
1723
2646
  const loginPayload: AgentLogin = {
@@ -1773,8 +2696,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1773
2696
  }
1774
2697
 
1775
2698
  /**
1776
- * Returns paginated entry points for the organization.
1777
- * Thin wrapper around internal EntryPoint instance.
2699
+ * Returns paginated entry points using the EntryPoint service defaults.
2700
+ * Existing request parameters can override those defaults.
1778
2701
  * @public
1779
2702
  */
1780
2703
  public async getEntryPoints(
@@ -1784,8 +2707,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1784
2707
  }
1785
2708
 
1786
2709
  /**
1787
- * Returns paginated contact service queues for the organization.
1788
- * Thin wrapper around internal Queue instance.
2710
+ * Returns paginated contact service queues using the Queue service defaults.
2711
+ * Existing request parameters can override those defaults.
1789
2712
  * @public
1790
2713
  */
1791
2714
  public async getQueues(