@webex/contact-center 3.11.0 → 3.12.0-llmrefactor.2

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 (267) hide show
  1. package/.sdd/manifest.json +882 -0
  2. package/AGENTS.md +94 -0
  3. package/ai-docs/ARCHITECTURE.md +168 -0
  4. package/ai-docs/CONTRACTS.md +46 -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 +341 -0
  17. package/ai-docs/features/generated-spec-conformance-fidelity-remediation/spec/feature-spec.md +117 -0
  18. package/ai-docs/features/residual-warning-coverage-completion/spec/feature-spec.md +203 -0
  19. package/ai-docs/features/validator-code-fidelity-drift-fix/spec/feature-spec.md +315 -0
  20. package/ai-docs/patterns/event-driven-patterns.md +485 -0
  21. package/ai-docs/patterns/testing-patterns.md +480 -0
  22. package/ai-docs/patterns/typescript-patterns.md +365 -0
  23. package/ai-docs/templates/README.md +102 -0
  24. package/ai-docs/templates/documentation/create-agents-md.md +240 -0
  25. package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
  26. package/ai-docs/templates/existing-service/bug-fix.md +254 -0
  27. package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
  28. package/ai-docs/templates/new-method/00-master.md +80 -0
  29. package/ai-docs/templates/new-method/01-requirements.md +232 -0
  30. package/ai-docs/templates/new-method/02-implementation.md +295 -0
  31. package/ai-docs/templates/new-method/03-tests.md +201 -0
  32. package/ai-docs/templates/new-method/04-validation.md +141 -0
  33. package/ai-docs/templates/new-service/00-master.md +109 -0
  34. package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
  35. package/ai-docs/templates/new-service/02-code-generation.md +346 -0
  36. package/ai-docs/templates/new-service/03-integration.md +178 -0
  37. package/ai-docs/templates/new-service/04-test-generation.md +205 -0
  38. package/ai-docs/templates/new-service/05-validation.md +145 -0
  39. package/dist/cc.js +379 -50
  40. package/dist/cc.js.map +1 -1
  41. package/dist/config.js +6 -0
  42. package/dist/config.js.map +1 -1
  43. package/dist/constants.js +22 -2
  44. package/dist/constants.js.map +1 -1
  45. package/dist/index.js +27 -5
  46. package/dist/index.js.map +1 -1
  47. package/dist/metrics/behavioral-events.js +114 -0
  48. package/dist/metrics/behavioral-events.js.map +1 -1
  49. package/dist/metrics/constants.js +32 -3
  50. package/dist/metrics/constants.js.map +1 -1
  51. package/dist/services/ApiAiAssistant.js +318 -0
  52. package/dist/services/ApiAiAssistant.js.map +1 -0
  53. package/dist/services/UserPreference.js +427 -0
  54. package/dist/services/UserPreference.js.map +1 -0
  55. package/dist/services/agent/types.js.map +1 -1
  56. package/dist/services/config/Util.js +8 -4
  57. package/dist/services/config/Util.js.map +1 -1
  58. package/dist/services/config/constants.js +35 -2
  59. package/dist/services/config/constants.js.map +1 -1
  60. package/dist/services/config/index.js +41 -2
  61. package/dist/services/config/index.js.map +1 -1
  62. package/dist/services/config/types.js +66 -8
  63. package/dist/services/config/types.js.map +1 -1
  64. package/dist/services/constants.js +27 -1
  65. package/dist/services/constants.js.map +1 -1
  66. package/dist/services/core/Err.js.map +1 -1
  67. package/dist/services/core/Utils.js +122 -25
  68. package/dist/services/core/Utils.js.map +1 -1
  69. package/dist/services/core/aqm-reqs.js +92 -17
  70. package/dist/services/core/aqm-reqs.js.map +1 -1
  71. package/dist/services/core/websocket/WebSocketManager.js +22 -6
  72. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  73. package/dist/services/core/websocket/connection-service.js +3 -1
  74. package/dist/services/core/websocket/connection-service.js.map +1 -1
  75. package/dist/services/core/websocket/types.js.map +1 -1
  76. package/dist/services/index.js +6 -0
  77. package/dist/services/index.js.map +1 -1
  78. package/dist/services/task/Task.js +688 -0
  79. package/dist/services/task/Task.js.map +1 -0
  80. package/dist/services/task/TaskFactory.js +45 -0
  81. package/dist/services/task/TaskFactory.js.map +1 -0
  82. package/dist/services/task/TaskManager.js +751 -457
  83. package/dist/services/task/TaskManager.js.map +1 -1
  84. package/dist/services/task/TaskUtils.js +220 -23
  85. package/dist/services/task/TaskUtils.js.map +1 -1
  86. package/dist/services/task/constants.js +23 -2
  87. package/dist/services/task/constants.js.map +1 -1
  88. package/dist/services/task/dialer.js +129 -0
  89. package/dist/services/task/dialer.js.map +1 -1
  90. package/dist/services/task/digital/Digital.js +77 -0
  91. package/dist/services/task/digital/Digital.js.map +1 -0
  92. package/dist/services/task/state-machine/TaskStateMachine.js +873 -0
  93. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  94. package/dist/services/task/state-machine/actions.js +567 -0
  95. package/dist/services/task/state-machine/actions.js.map +1 -0
  96. package/dist/services/task/state-machine/constants.js +161 -0
  97. package/dist/services/task/state-machine/constants.js.map +1 -0
  98. package/dist/services/task/state-machine/guards.js +382 -0
  99. package/dist/services/task/state-machine/guards.js.map +1 -0
  100. package/dist/services/task/state-machine/index.js +53 -0
  101. package/dist/services/task/state-machine/index.js.map +1 -0
  102. package/dist/services/task/state-machine/types.js +54 -0
  103. package/dist/services/task/state-machine/types.js.map +1 -0
  104. package/dist/services/task/state-machine/uiControlsComputer.js +603 -0
  105. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  106. package/dist/services/task/taskDataNormalizer.js +99 -0
  107. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  108. package/dist/services/task/types.js +227 -4
  109. package/dist/services/task/types.js.map +1 -1
  110. package/dist/services/task/voice/Voice.js +1044 -0
  111. package/dist/services/task/voice/Voice.js.map +1 -0
  112. package/dist/services/task/voice/WebRTC.js +149 -0
  113. package/dist/services/task/voice/WebRTC.js.map +1 -0
  114. package/dist/types/cc.d.ts +894 -0
  115. package/dist/types/config.d.ts +72 -0
  116. package/dist/types/constants.d.ts +66 -0
  117. package/dist/types/index.d.ts +199 -0
  118. package/dist/types/logger-proxy.d.ts +71 -0
  119. package/dist/types/metrics/MetricsManager.d.ts +223 -0
  120. package/dist/types/metrics/behavioral-events.d.ts +29 -0
  121. package/dist/types/metrics/constants.d.ts +181 -0
  122. package/dist/types/services/AddressBook.d.ts +74 -0
  123. package/dist/types/services/ApiAiAssistant.d.ts +49 -0
  124. package/dist/types/services/EntryPoint.d.ts +67 -0
  125. package/dist/types/services/Queue.d.ts +76 -0
  126. package/dist/types/services/UserPreference.d.ts +118 -0
  127. package/dist/types/services/WebCallingService.d.ts +1 -0
  128. package/dist/types/services/agent/index.d.ts +46 -0
  129. package/dist/types/services/agent/types.d.ts +413 -0
  130. package/dist/types/services/config/Util.d.ts +20 -0
  131. package/dist/types/services/config/constants.d.ts +270 -0
  132. package/dist/types/services/config/index.d.ts +177 -0
  133. package/dist/types/services/config/types.d.ts +1368 -0
  134. package/dist/types/services/constants.d.ts +110 -0
  135. package/dist/types/services/core/Err.d.ts +125 -0
  136. package/dist/types/services/core/GlobalTypes.d.ts +58 -0
  137. package/dist/types/services/core/Utils.d.ts +121 -0
  138. package/dist/types/services/core/WebexRequest.d.ts +22 -0
  139. package/dist/types/services/core/aqm-reqs.d.ts +65 -0
  140. package/dist/types/services/core/constants.d.ts +99 -0
  141. package/dist/types/services/core/types.d.ts +47 -0
  142. package/dist/types/services/core/websocket/WebSocketManager.d.ts +36 -0
  143. package/dist/types/services/core/websocket/connection-service.d.ts +27 -0
  144. package/dist/types/services/core/websocket/keepalive.worker.d.ts +2 -0
  145. package/dist/types/services/core/websocket/types.d.ts +37 -0
  146. package/dist/types/services/index.d.ts +54 -0
  147. package/dist/types/services/task/AutoWrapup.d.ts +40 -0
  148. package/dist/types/services/task/Task.d.ts +157 -0
  149. package/dist/types/services/task/TaskFactory.d.ts +12 -0
  150. package/dist/types/services/task/TaskManager.d.ts +1 -0
  151. package/dist/types/services/task/TaskUtils.d.ts +138 -0
  152. package/dist/types/services/task/constants.d.ts +91 -0
  153. package/dist/types/services/task/contact.d.ts +69 -0
  154. package/dist/types/services/task/dialer.d.ts +73 -0
  155. package/dist/types/services/task/digital/Digital.d.ts +22 -0
  156. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1194 -0
  157. package/dist/types/services/task/state-machine/actions.d.ts +10 -0
  158. package/dist/types/services/task/state-machine/constants.d.ts +107 -0
  159. package/dist/types/services/task/state-machine/guards.d.ts +102 -0
  160. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  161. package/dist/types/services/task/state-machine/types.d.ts +269 -0
  162. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  163. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  164. package/dist/types/services/task/types.d.ts +1856 -0
  165. package/dist/types/services/task/voice/Voice.d.ts +184 -0
  166. package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
  167. package/dist/types/types.d.ts +778 -0
  168. package/dist/types/utils/PageCache.d.ts +173 -0
  169. package/dist/types/webex-config.d.ts +53 -0
  170. package/dist/types/webex.d.ts +8 -0
  171. package/dist/types.js +130 -1
  172. package/dist/types.js.map +1 -1
  173. package/dist/webex.js +14 -2
  174. package/dist/webex.js.map +1 -1
  175. package/package.json +16 -12
  176. package/src/cc.ts +477 -51
  177. package/src/config.ts +6 -0
  178. package/src/constants.ts +21 -1
  179. package/src/index.ts +24 -5
  180. package/src/metrics/ai-docs/AGENTS.md +350 -0
  181. package/src/metrics/ai-docs/ARCHITECTURE.md +338 -0
  182. package/src/metrics/ai-docs/metrics-spec.md +854 -0
  183. package/src/metrics/behavioral-events.ts +120 -0
  184. package/src/metrics/constants.ts +37 -3
  185. package/src/services/ApiAiAssistant.ts +412 -0
  186. package/src/services/UserPreference.ts +509 -0
  187. package/src/services/agent/ai-docs/AGENTS.md +240 -0
  188. package/src/services/agent/ai-docs/ARCHITECTURE.md +304 -0
  189. package/src/services/agent/ai-docs/agent-spec.md +504 -0
  190. package/src/services/agent/types.ts +1 -1
  191. package/src/services/ai-docs/AGENTS.md +386 -0
  192. package/src/services/ai-docs/services-spec.md +492 -0
  193. package/src/services/config/Util.ts +10 -2
  194. package/src/services/config/ai-docs/AGENTS.md +255 -0
  195. package/src/services/config/ai-docs/ARCHITECTURE.md +426 -0
  196. package/src/services/config/ai-docs/config-spec.md +669 -0
  197. package/src/services/config/constants.ts +37 -1
  198. package/src/services/config/index.ts +45 -1
  199. package/src/services/config/types.ts +241 -11
  200. package/src/services/constants.ts +29 -0
  201. package/src/services/core/Err.ts +3 -0
  202. package/src/services/core/Utils.ts +143 -30
  203. package/src/services/core/ai-docs/AGENTS.md +381 -0
  204. package/src/services/core/ai-docs/ARCHITECTURE.md +698 -0
  205. package/src/services/core/ai-docs/core-spec.md +783 -0
  206. package/src/services/core/aqm-reqs.ts +100 -22
  207. package/src/services/core/websocket/WebSocketManager.ts +23 -6
  208. package/src/services/core/websocket/connection-service.ts +5 -1
  209. package/src/services/core/websocket/types.ts +1 -1
  210. package/src/services/index.ts +4 -0
  211. package/src/services/task/Task.ts +837 -0
  212. package/src/services/task/TaskFactory.ts +55 -0
  213. package/src/services/task/TaskManager.ts +793 -521
  214. package/src/services/task/TaskUtils.ts +314 -24
  215. package/src/services/task/ai-docs/AGENTS.md +457 -0
  216. package/src/services/task/ai-docs/ARCHITECTURE.md +594 -0
  217. package/src/services/task/ai-docs/task-spec.md +1319 -0
  218. package/src/services/task/constants.ts +23 -0
  219. package/src/services/task/dialer.ts +136 -1
  220. package/src/services/task/digital/Digital.ts +95 -0
  221. package/src/services/task/state-machine/TaskStateMachine.ts +1166 -0
  222. package/src/services/task/state-machine/actions.ts +738 -0
  223. package/src/services/task/state-machine/ai-docs/AGENTS.md +458 -0
  224. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1137 -0
  225. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2177 -0
  226. package/src/services/task/state-machine/constants.ts +172 -0
  227. package/src/services/task/state-machine/guards.ts +445 -0
  228. package/src/services/task/state-machine/index.ts +28 -0
  229. package/src/services/task/state-machine/types.ts +243 -0
  230. package/src/services/task/state-machine/uiControlsComputer.ts +961 -0
  231. package/src/services/task/taskDataNormalizer.ts +137 -0
  232. package/src/services/task/types.ts +734 -71
  233. package/src/services/task/voice/Voice.ts +1270 -0
  234. package/src/services/task/voice/WebRTC.ts +187 -0
  235. package/src/types.ts +205 -2
  236. package/src/utils/AGENTS.md +278 -0
  237. package/src/utils/ai-docs/utils-spec.md +381 -0
  238. package/src/webex.js +2 -0
  239. package/test/unit/spec/cc.ts +503 -43
  240. package/test/unit/spec/logger-proxy.ts +70 -0
  241. package/test/unit/spec/services/ApiAiAssistant.ts +273 -0
  242. package/test/unit/spec/services/UserPreference.ts +401 -0
  243. package/test/unit/spec/services/WebCallingService.ts +7 -1
  244. package/test/unit/spec/services/config/index.ts +85 -29
  245. package/test/unit/spec/services/core/Utils.ts +481 -2
  246. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +137 -41
  247. package/test/unit/spec/services/core/websocket/connection-service.ts +3 -1
  248. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  249. package/test/unit/spec/services/task/Task.ts +477 -0
  250. package/test/unit/spec/services/task/TaskFactory.ts +62 -0
  251. package/test/unit/spec/services/task/TaskManager.ts +1001 -1003
  252. package/test/unit/spec/services/task/TaskUtils.ts +235 -0
  253. package/test/unit/spec/services/task/dialer.ts +372 -96
  254. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  255. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +2651 -0
  256. package/test/unit/spec/services/task/state-machine/guards.ts +637 -0
  257. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  258. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +2663 -0
  259. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  260. package/test/unit/spec/services/task/voice/Voice.ts +649 -0
  261. package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
  262. package/umd/contact-center.min.js +2 -2
  263. package/umd/contact-center.min.js.map +1 -1
  264. package/dist/services/task/index.js +0 -1525
  265. package/dist/services/task/index.js.map +0 -1
  266. package/src/services/task/index.ts +0 -1801
  267. 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,21 @@ 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 {
60
+ ITask,
61
+ TASK_EVENTS,
62
+ TaskResponse,
63
+ DialerPayload,
64
+ PreviewContactPayload,
65
+ } from './services/task/types';
59
66
  import MetricsManager from './metrics/MetricsManager';
60
67
  import {METRIC_EVENT_NAMES} from './metrics/constants';
61
68
  import {Failure} from './services/core/GlobalTypes';
62
- import EntryPoint from './services/EntryPoint';
63
- import AddressBook from './services/AddressBook';
64
- import Queue from './services/Queue';
69
+ import {EntryPoint} from './services/EntryPoint';
70
+ import {AddressBook} from './services/AddressBook';
71
+ import {Queue} from './services/Queue';
72
+ import {ApiAIAssistant} from './services/ApiAiAssistant';
73
+ import {UserPreference} from './services/UserPreference';
65
74
  import type {
66
75
  EntryPointListResponse,
67
76
  EntryPointSearchParams,
@@ -119,6 +128,7 @@ import type {
119
128
  * - `task:established` - Task/call has been connected
120
129
  * - `task:ended` - Task/call has ended
121
130
  * - `task:error` - An error occurred during task handling
131
+ * - `task:campaignPreviewReservation` - Campaign preview contact offered to agent
122
132
  *
123
133
  * @public
124
134
  *
@@ -317,7 +327,45 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
317
327
  * });
318
328
  * ```
319
329
  */
320
- private queue: Queue;
330
+ public queue: Queue;
331
+
332
+ /**
333
+ * API instance for managing Webex Contact Center user preferences
334
+ * Provides functionality to get, create, update, and delete user preferences
335
+ * @type {UserPreference}
336
+ * @public
337
+ * @example
338
+ * ```typescript
339
+ * const cc = webex.cc;
340
+ * await cc.register();
341
+ * await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' });
342
+ *
343
+ * // Get user preferences for current user
344
+ * const preferences = await cc.userPreference.getUserPreference();
345
+ *
346
+ * // Create new user preferences
347
+ * const newPrefs = await cc.userPreference.createUserPreference({
348
+ * userId: 'user123',
349
+ * preferences: { e911Reminder: true }
350
+ * });
351
+ *
352
+ * // Update user preferences
353
+ * const updatedPrefs = await cc.userPreference.updateUserPreference('user123', {
354
+ * preferences: { e911Reminder: false }
355
+ * });
356
+ *
357
+ * // Delete user preferences
358
+ * await cc.userPreference.deleteUserPreference('user123');
359
+ * ```
360
+ */
361
+ public userPreference: UserPreference;
362
+
363
+ /**
364
+ * API instance for AI Assistant operations such as transcript controls.
365
+ * @type {ApiAIAssistant}
366
+ * @public
367
+ */
368
+ public apiAIAssistant: ApiAIAssistant;
321
369
 
322
370
  /**
323
371
  * Logger utility for Contact Center plugin
@@ -342,6 +390,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
342
390
  this.$webex.once(READY, () => {
343
391
  // @ts-ignore
344
392
  this.$config = this.config;
393
+ this.validatePluginConfig();
345
394
 
346
395
  /**
347
396
  * This is used for handling the async requests by sending webex.request and wait for corresponding websocket event.
@@ -354,14 +403,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
354
403
  webex: this.$webex,
355
404
  connectionConfig: this.getConnectionConfig(),
356
405
  });
357
- this.services.webSocketManager.on('message', this.handleWebsocketMessage);
358
406
 
359
407
  this.webCallingService = new WebCallingService(this.$webex);
408
+ this.apiAIAssistant = new ApiAIAssistant(this.$webex);
360
409
  this.metricsManager = MetricsManager.getInstance({webex: this.$webex});
361
410
  this.taskManager = TaskManager.getTaskManager(
411
+ this.apiAIAssistant,
362
412
  this.services.contact,
363
413
  this.webCallingService,
364
- this.services.webSocketManager
414
+ this.services.webSocketManager,
415
+ this.services.rtdWebSocketManager
365
416
  );
366
417
  this.incomingTaskListener();
367
418
 
@@ -370,8 +421,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
370
421
  this.entryPoint = new EntryPoint(this.$webex);
371
422
  this.addressBook = new AddressBook(this.$webex, () => this.agentConfig?.addressBookId);
372
423
  this.queue = new Queue(this.$webex);
373
-
374
- // Initialize logger
424
+ this.userPreference = new UserPreference(this.$webex, () => this.agentConfig?.agentId);
375
425
  LoggerProxy.initialize(this.$webex.logger);
376
426
  });
377
427
  }
@@ -396,6 +446,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
396
446
  this.trigger(TASK_EVENTS.TASK_HYDRATE, task);
397
447
  };
398
448
 
449
+ /**
450
+ * Handles multi-login hydrate events for SDK instances without Mobius registration
451
+ * @private
452
+ * @param {ITask} task The task object associated with the multi-login hydrate
453
+ */
454
+ private handleTaskMultiLoginHydrate = (task: ITask) => {
455
+ // @ts-ignore
456
+ this.trigger(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, task);
457
+ };
458
+
399
459
  /**
400
460
  * Handles task merged events when tasks are combined eg: EPDN merge/transfer
401
461
  * @private
@@ -406,6 +466,20 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
406
466
  this.trigger(TASK_EVENTS.TASK_MERGED, task);
407
467
  };
408
468
 
469
+ /**
470
+ * Handles campaign preview reservation events when a contact is offered to the agent
471
+ * @private
472
+ * @param {ITask} task The campaign reservation task
473
+ */
474
+ private handleCampaignPreviewReservation = (task: ITask) => {
475
+ // @ts-ignore
476
+ this.trigger(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, task);
477
+ };
478
+
479
+ private handleRTDWebsocketMessage = (event: string) => {
480
+ this.taskManager.handleRealtimeWebsocketEvent(event);
481
+ };
482
+
409
483
  /**
410
484
  * Sets up event listeners for incoming tasks and task hydration
411
485
  * Subscribes to task events from the task manager
@@ -414,7 +488,12 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
414
488
  private incomingTaskListener() {
415
489
  this.taskManager.on(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
416
490
  this.taskManager.on(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
491
+ this.taskManager.on(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
417
492
  this.taskManager.on(TASK_EVENTS.TASK_MERGED, this.handleTaskMerged);
493
+ this.taskManager.on(
494
+ TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
495
+ this.handleCampaignPreviewReservation
496
+ );
418
497
  }
419
498
 
420
499
  /**
@@ -454,7 +533,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
454
533
  * ```
455
534
  */
456
535
  public async register(): Promise<Profile> {
457
- LoggerProxy.info('Starting CC SDK registration', {
536
+ LoggerProxy.log('Starting CC SDK registration', {
458
537
  module: CC_FILE,
459
538
  method: METHODS.REGISTER,
460
539
  });
@@ -464,6 +543,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
464
543
  METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_FAILED,
465
544
  ]);
466
545
  this.setupEventListeners();
546
+ this.services.webSocketManager.on('message', this.handleWebsocketMessage);
467
547
 
468
548
  const resp = await this.connectWebsocket();
469
549
  // Ensure 'dn' is always populated from 'defaultDn'
@@ -543,9 +623,15 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
543
623
 
544
624
  this.taskManager.off(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
545
625
  this.taskManager.off(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
626
+ this.taskManager.off(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
627
+ this.taskManager.off(
628
+ TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
629
+ this.handleCampaignPreviewReservation
630
+ );
546
631
  this.taskManager.unregisterIncomingCallEvent();
547
632
 
548
633
  this.services.webSocketManager.off('message', this.handleWebsocketMessage);
634
+ this.services.rtdWebSocketManager.off('message', this.handleRTDWebsocketMessage);
549
635
  this.services.connectionService.off('connectionLost', this.handleConnectionLost);
550
636
 
551
637
  if (
@@ -568,6 +654,13 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
568
654
  if (!this.services.webSocketManager.isSocketClosed) {
569
655
  this.services.webSocketManager.close(false, 'Unregistering the SDK');
570
656
  }
657
+ if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
658
+ this.services.rtdWebSocketManager.close(false, 'Unregistering the SDK');
659
+ }
660
+
661
+ if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
662
+ this.services.rtdWebSocketManager.close(false, 'Unregistering the RTD websocket');
663
+ }
571
664
 
572
665
  // Clear any cached agent configuration
573
666
  this.agentConfig = null;
@@ -689,7 +782,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
689
782
  * @private
690
783
  */
691
784
  private async connectWebsocket() {
692
- LoggerProxy.info('Connecting to websocket', {
785
+ LoggerProxy.log('Connecting to websocket', {
693
786
  module: CC_FILE,
694
787
  method: METHODS.CONNECT_WEBSOCKET,
695
788
  });
@@ -697,6 +790,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
697
790
  try {
698
791
  const data = (await this.services.webSocketManager.initWebSocket({
699
792
  body: this.getConnectionConfig(),
793
+ resource: SUBSCRIBE_API,
700
794
  })) as WelcomeEvent;
701
795
 
702
796
  const agentId = data.agentId;
@@ -708,14 +802,57 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
708
802
  method: METHODS.CONNECT_WEBSOCKET,
709
803
  });
710
804
 
805
+ const configFlags: ConfigFlags = {
806
+ isEndTaskEnabled: this.agentConfig.isEndTaskEnabled,
807
+ isEndConsultEnabled: this.agentConfig.isEndConsultEnabled,
808
+ webRtcEnabled: this.agentConfig.webRtcEnabled,
809
+ autoWrapup: this.agentConfig.wrapUpData?.wrapUpProps?.autoWrapup ?? false,
810
+ aiFeature: this.agentConfig.aiFeature,
811
+ };
812
+ this.taskManager.setConfigFlags(configFlags);
711
813
  // TODO: Make profile a singleton to make it available throughout app/sdk so we dont need to inject info everywhere
712
814
  this.taskManager.setWrapupData(this.agentConfig.wrapUpData);
713
815
  this.taskManager.setAgentId(this.agentConfig.agentId);
714
816
  this.taskManager.setWebRtcEnabled(this.agentConfig.webRtcEnabled);
817
+ this.apiAIAssistant.setAIFeatureFlags(this.agentConfig.aiFeature);
818
+
819
+ /**
820
+ * RTD websocket currently supports realtime transcripts and suggested responses.
821
+ * Extend this condition when additional AI RTD features are introduced.
822
+ */
823
+ if (
824
+ this.agentConfig.aiFeature?.realtimeTranscripts?.enable ||
825
+ this.agentConfig.aiFeature?.suggestedResponses?.enable
826
+ ) {
827
+ LoggerProxy.info('Connecting to RTD websocket', {
828
+ module: CC_FILE,
829
+ method: METHODS.CONNECT_WEBSOCKET,
830
+ });
831
+
832
+ this.services.rtdWebSocketManager
833
+ .initWebSocket({
834
+ body: this.getConnectionConfig(),
835
+ resource: RTD_SUBSCRIBE_API,
836
+ })
837
+ .then(() => {
838
+ LoggerProxy.log('RTD websocket connected successfully', {
839
+ module: CC_FILE,
840
+ method: METHODS.CONNECT_WEBSOCKET,
841
+ });
842
+ this.services.rtdWebSocketManager.on('message', this.handleRTDWebsocketMessage);
843
+ })
844
+ .catch((error) => {
845
+ LoggerProxy.error(`Error connecting to RTD websocket ${error}`, {
846
+ module: CC_FILE,
847
+ method: METHODS.CONNECT_WEBSOCKET,
848
+ });
849
+ });
850
+ }
715
851
 
716
852
  if (
717
853
  this.agentConfig.webRtcEnabled &&
718
- this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER)
854
+ this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER) &&
855
+ !this.isWebRTCRegistrationDisabled()
719
856
  ) {
720
857
  try {
721
858
  await this.$webex.internal.mercury.connect();
@@ -729,10 +866,23 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
729
866
  method: METHODS.CONNECT_WEBSOCKET,
730
867
  });
731
868
  }
869
+ } else if (this.isWebRTCRegistrationDisabled()) {
870
+ LoggerProxy.info(
871
+ 'Skipping Mobius registration because disableWebRTCRegistration is enabled',
872
+ {
873
+ module: CC_FILE,
874
+ method: METHODS.CONNECT_WEBSOCKET,
875
+ }
876
+ );
732
877
  }
733
878
 
734
879
  if (this.$config && this.$config.allowAutomatedRelogin) {
735
880
  await this.silentRelogin();
881
+ } else {
882
+ LoggerProxy.log('Skipping silent relogin: allowAutomatedRelogin is disabled', {
883
+ module: CC_FILE,
884
+ method: METHODS.CONNECT_WEBSOCKET,
885
+ });
736
886
  }
737
887
 
738
888
  return this.agentConfig;
@@ -775,22 +925,37 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
775
925
  * ```
776
926
  */
777
927
  public async stationLogin(data: AgentLogin): Promise<StationLoginResponse> {
778
- LoggerProxy.info('Starting agent station login', {
779
- module: CC_FILE,
780
- method: METHODS.STATION_LOGIN,
781
- });
928
+ const loggerContext = {module: CC_FILE, method: METHODS.STATION_LOGIN};
929
+
782
930
  try {
931
+ LoggerProxy.log(
932
+ `Starting agent station login | loginOption: ${data?.loginOption} teamId: ${data?.teamId}`,
933
+ loggerContext
934
+ );
783
935
  this.metricsManager.timeEvent([
784
936
  METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
785
937
  METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
786
938
  ]);
787
939
 
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;
940
+ const dialPlanEntries = this.agentConfig?.dialPlan?.dialPlanEntity ?? [];
941
+ if (data.loginOption === LoginOption.AGENT_DN) {
942
+ LoggerProxy.log(
943
+ `Validating dial number | dialPlanEnabled: ${!!this.agentConfig
944
+ ?.dialPlan} | dialPlanEntryCount: ${dialPlanEntries.length}`,
945
+ loggerContext
946
+ );
947
+
948
+ if (!isValidDialNumber(data.dialNumber, dialPlanEntries)) {
949
+ LoggerProxy.log(
950
+ `Dial number validation failed | dialNumber: ${data.dialNumber} | dialPlanEntryCount: ${dialPlanEntries.length}`,
951
+ loggerContext
952
+ );
953
+ const error = new Error('INVALID_DIAL_NUMBER');
954
+ // @ts-ignore - adding custom key to the error object
955
+ error.details = {data: {reason: 'INVALID_DIAL_NUMBER'}} as Failure;
792
956
 
793
- throw error;
957
+ throw error;
958
+ }
794
959
  }
795
960
 
796
961
  const loginResponse = await this.services.agent.stationLogin({
@@ -809,8 +974,24 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
809
974
  },
810
975
  });
811
976
 
812
- if (this.agentConfig.webRtcEnabled && data.loginOption === LoginOption.BROWSER) {
977
+ if (
978
+ this.agentConfig.webRtcEnabled &&
979
+ data.loginOption === LoginOption.BROWSER &&
980
+ !this.isWebRTCRegistrationDisabled()
981
+ ) {
813
982
  await this.webCallingService.registerWebCallingLine();
983
+ } else if (
984
+ this.agentConfig.webRtcEnabled &&
985
+ data.loginOption === LoginOption.BROWSER &&
986
+ this.isWebRTCRegistrationDisabled()
987
+ ) {
988
+ LoggerProxy.info(
989
+ 'Skipping web calling line registration because disableWebRTCRegistration is enabled',
990
+ {
991
+ module: CC_FILE,
992
+ method: METHODS.STATION_LOGIN,
993
+ }
994
+ );
814
995
  }
815
996
 
816
997
  const resp = await loginResponse;
@@ -1172,6 +1353,30 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1172
1353
  }
1173
1354
  };
1174
1355
 
1356
+ /**
1357
+ * Checks whether Mobius/WebRTC registration should be skipped by config
1358
+ * @returns {boolean} True when browser WebRTC registration is disabled
1359
+ * @private
1360
+ */
1361
+ private isWebRTCRegistrationDisabled(): boolean {
1362
+ return this.$config?.disableWebRTCRegistration === true;
1363
+ }
1364
+
1365
+ /**
1366
+ * Validates contact-center plugin configuration before service initialization
1367
+ * @private
1368
+ */
1369
+ private validatePluginConfig(): void {
1370
+ if (
1371
+ this.$config?.disableWebRTCRegistration === true &&
1372
+ this.$config?.allowMultiLogin === false
1373
+ ) {
1374
+ throw new Error(
1375
+ '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.'
1376
+ );
1377
+ }
1378
+ }
1379
+
1175
1380
  /**
1176
1381
  * Initializes event listeners for the Contact Center service
1177
1382
  * Sets up handlers for connection state changes and other core events
@@ -1203,13 +1408,13 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1203
1408
  private async handleConnectionLost(msg: ConnectionLostDetails): Promise<void> {
1204
1409
  if (msg.isConnectionLost) {
1205
1410
  // TODO: Emit an event saying connection is lost
1206
- LoggerProxy.info('event=handleConnectionLost | Connection lost', {
1411
+ LoggerProxy.log('event=handleConnectionLost | Connection lost', {
1207
1412
  module: CC_FILE,
1208
1413
  method: METHODS.HANDLE_CONNECTION_LOST,
1209
1414
  });
1210
1415
  } else if (msg.isSocketReconnected) {
1211
1416
  // TODO: Emit an event saying connection is re-estabilished
1212
- LoggerProxy.info(
1417
+ LoggerProxy.log(
1213
1418
  'event=handleConnectionReconnect | Connection reconnected attempting to request silent relogin',
1214
1419
  {module: CC_FILE, method: METHODS.HANDLE_CONNECTION_LOST}
1215
1420
  );
@@ -1224,7 +1429,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1224
1429
  * @private
1225
1430
  */
1226
1431
  private async silentRelogin(): Promise<void> {
1227
- LoggerProxy.info('Starting silent relogin process', {
1432
+ LoggerProxy.log('Starting silent relogin process', {
1228
1433
  module: CC_FILE,
1229
1434
  method: METHODS.SILENT_RELOGIN,
1230
1435
  });
@@ -1246,7 +1451,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1246
1451
  await this.handleDeviceType(deviceType as LoginOption, dn);
1247
1452
 
1248
1453
  if (lastStateChangeReason === 'agent-wss-disconnect') {
1249
- LoggerProxy.info(
1454
+ LoggerProxy.log(
1250
1455
  'event=requestAutoStateChange | Requesting state change to available on socket reconnect',
1251
1456
  {module: CC_FILE, method: METHODS.SILENT_RELOGIN}
1252
1457
  );
@@ -1275,8 +1480,6 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1275
1480
  }
1276
1481
  this.agentConfig.lastStateAuxCodeId = auxCodeId;
1277
1482
  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);
1280
1483
 
1281
1484
  LoggerProxy.log(
1282
1485
  `Silent relogin process completed successfully with login Option: ${reLoginResponse.data.deviceType} teamId: ${reLoginResponse.data.teamId}`,
@@ -1317,6 +1520,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1317
1520
  this.agentConfig.deviceType = deviceType;
1318
1521
  switch (deviceType) {
1319
1522
  case LoginOption.BROWSER:
1523
+ if (this.isWebRTCRegistrationDisabled()) {
1524
+ LoggerProxy.info(
1525
+ 'Skipping web calling line registration because disableWebRTCRegistration is enabled',
1526
+ {
1527
+ module: CC_FILE,
1528
+ method: METHODS.HANDLE_DEVICE_TYPE,
1529
+ }
1530
+ );
1531
+ break;
1532
+ }
1320
1533
  try {
1321
1534
  await this.webCallingService.registerWebCallingLine();
1322
1535
  } catch (error) {
@@ -1509,6 +1722,239 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1509
1722
  }
1510
1723
  }
1511
1724
 
1725
+ /**
1726
+ * Accepts a campaign preview contact, initiating the outbound call.
1727
+ *
1728
+ * When a campaign manager reserves a contact for an agent, the agent receives an
1729
+ * `AgentOfferCampaignReservation` event. The agent can then accept the preview contact
1730
+ * to initiate the outbound call.
1731
+ *
1732
+ * @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
1733
+ * @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
1734
+ * @throws {Error} If the operation fails (network error, customer unavailable, etc.)
1735
+ *
1736
+ * @example
1737
+ * ```typescript
1738
+ * webex.cc.on('task:campaignPreviewReservation', async (task) => {
1739
+ * const { interactionId } = task.data;
1740
+ * // campaignId is the campaign name (e.g. "MyCampaign"), not a UUID
1741
+ * const campaignId = task.data.interaction.callProcessingDetails.campaignId;
1742
+ *
1743
+ * const result = await webex.cc.acceptPreviewContact({ interactionId, campaignId });
1744
+ * });
1745
+ * ```
1746
+ */
1747
+ public async acceptPreviewContact(payload: PreviewContactPayload): Promise<TaskResponse> {
1748
+ LoggerProxy.info('Accepting campaign preview contact', {
1749
+ module: CC_FILE,
1750
+ method: METHODS.ACCEPT_PREVIEW_CONTACT,
1751
+ });
1752
+ try {
1753
+ this.metricsManager.timeEvent([
1754
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_SUCCESS,
1755
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_FAILED,
1756
+ ]);
1757
+
1758
+ const result = await this.services.dialer.acceptPreviewContact({data: payload});
1759
+
1760
+ this.metricsManager.trackEvent(
1761
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_SUCCESS,
1762
+ {
1763
+ ...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
1764
+ interactionId: payload.interactionId,
1765
+ campaignId: payload.campaignId,
1766
+ },
1767
+ ['behavioral', 'business', 'operational']
1768
+ );
1769
+
1770
+ LoggerProxy.log('Campaign preview contact accepted successfully', {
1771
+ module: CC_FILE,
1772
+ method: METHODS.ACCEPT_PREVIEW_CONTACT,
1773
+ trackingId: result.trackingId,
1774
+ interactionId: payload.interactionId,
1775
+ });
1776
+
1777
+ return result;
1778
+ } catch (error) {
1779
+ const failure = error.details as Failure;
1780
+ this.metricsManager.trackEvent(
1781
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_ACCEPT_FAILED,
1782
+ {
1783
+ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failure),
1784
+ interactionId: payload.interactionId,
1785
+ campaignId: payload.campaignId,
1786
+ },
1787
+ ['behavioral', 'business', 'operational']
1788
+ );
1789
+ const {error: detailedError} = getErrorDetails(
1790
+ error,
1791
+ METHODS.ACCEPT_PREVIEW_CONTACT,
1792
+ CC_FILE
1793
+ );
1794
+ throw detailedError;
1795
+ }
1796
+ }
1797
+
1798
+ /**
1799
+ * Skips a campaign preview contact, requesting the next contact from the campaign.
1800
+ *
1801
+ * When a campaign manager reserves a contact for an agent, the agent receives an
1802
+ * `AgentOfferCampaignReservation` event. Instead of accepting, the agent can skip the
1803
+ * preview contact to move to the next contact in the campaign.
1804
+ *
1805
+ * @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
1806
+ * @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
1807
+ * @throws {Error} If the operation fails (network error, etc.)
1808
+ *
1809
+ * @example
1810
+ * ```typescript
1811
+ * webex.cc.on('task:campaignPreviewReservation', async (task) => {
1812
+ * const { interactionId } = task.data;
1813
+ * const campaignId = task.data.interaction.callProcessingDetails.campaignId;
1814
+ *
1815
+ * const result = await webex.cc.skipPreviewContact({ interactionId, campaignId });
1816
+ * });
1817
+ * ```
1818
+ */
1819
+ public async skipPreviewContact(payload: PreviewContactPayload): Promise<TaskResponse> {
1820
+ const task = this.taskManager.getTask(payload.interactionId);
1821
+ if (task?.data?.interaction?.callProcessingDetails?.campaignPreviewSkipDisabled === 'true') {
1822
+ LoggerProxy.warn('Skip action is disabled for this campaign preview contact', {
1823
+ module: CC_FILE,
1824
+ method: METHODS.SKIP_PREVIEW_CONTACT,
1825
+ interactionId: payload.interactionId,
1826
+ });
1827
+ throw new Error('Skip action is disabled for this campaign preview contact');
1828
+ }
1829
+
1830
+ LoggerProxy.info('Skipping campaign preview contact', {
1831
+ module: CC_FILE,
1832
+ method: METHODS.SKIP_PREVIEW_CONTACT,
1833
+ });
1834
+ try {
1835
+ this.metricsManager.timeEvent([
1836
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS,
1837
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED,
1838
+ ]);
1839
+
1840
+ const result = await this.services.dialer.skipPreviewContact({data: payload});
1841
+
1842
+ this.metricsManager.trackEvent(
1843
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS,
1844
+ {
1845
+ ...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
1846
+ interactionId: payload.interactionId,
1847
+ campaignId: payload.campaignId,
1848
+ },
1849
+ ['behavioral', 'business', 'operational']
1850
+ );
1851
+
1852
+ LoggerProxy.log('Campaign preview contact skipped successfully', {
1853
+ module: CC_FILE,
1854
+ method: METHODS.SKIP_PREVIEW_CONTACT,
1855
+ trackingId: result.trackingId,
1856
+ interactionId: payload.interactionId,
1857
+ });
1858
+
1859
+ return result;
1860
+ } catch (error) {
1861
+ const failure = error.details as Failure;
1862
+ this.metricsManager.trackEvent(
1863
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED,
1864
+ {
1865
+ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failure),
1866
+ interactionId: payload.interactionId,
1867
+ campaignId: payload.campaignId,
1868
+ },
1869
+ ['behavioral', 'business', 'operational']
1870
+ );
1871
+ const {error: detailedError} = getErrorDetails(error, METHODS.SKIP_PREVIEW_CONTACT, CC_FILE);
1872
+ throw detailedError;
1873
+ }
1874
+ }
1875
+
1876
+ /**
1877
+ * Removes a campaign preview contact from the campaign list entirely.
1878
+ *
1879
+ * When a campaign manager reserves a contact for an agent, the agent receives an
1880
+ * `AgentOfferCampaignReservation` event. Instead of accepting or skipping, the agent can
1881
+ * remove the preview contact to permanently take it out of the campaign contact list.
1882
+ *
1883
+ * @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
1884
+ * @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
1885
+ * @throws {Error} If the operation fails (network error, etc.)
1886
+ *
1887
+ * @example
1888
+ * ```typescript
1889
+ * webex.cc.on('task:campaignPreviewReservation', async (task) => {
1890
+ * const { interactionId } = task.data;
1891
+ * const campaignId = task.data.interaction.callProcessingDetails.campaignId;
1892
+ *
1893
+ * const result = await webex.cc.removePreviewContact({ interactionId, campaignId });
1894
+ * });
1895
+ * ```
1896
+ */
1897
+ public async removePreviewContact(payload: PreviewContactPayload): Promise<TaskResponse> {
1898
+ const task = this.taskManager.getTask(payload.interactionId);
1899
+ if (task?.data?.interaction?.callProcessingDetails?.campaignPreviewRemoveDisabled === 'true') {
1900
+ LoggerProxy.warn('Remove action is disabled for this campaign preview contact', {
1901
+ module: CC_FILE,
1902
+ method: METHODS.REMOVE_PREVIEW_CONTACT,
1903
+ interactionId: payload.interactionId,
1904
+ });
1905
+ throw new Error('Remove action is disabled for this campaign preview contact');
1906
+ }
1907
+
1908
+ LoggerProxy.info('Removing campaign preview contact', {
1909
+ module: CC_FILE,
1910
+ method: METHODS.REMOVE_PREVIEW_CONTACT,
1911
+ });
1912
+ try {
1913
+ this.metricsManager.timeEvent([
1914
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS,
1915
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED,
1916
+ ]);
1917
+
1918
+ const result = await this.services.dialer.removePreviewContact({data: payload});
1919
+
1920
+ this.metricsManager.trackEvent(
1921
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS,
1922
+ {
1923
+ ...MetricsManager.getCommonTrackingFieldForAQMResponse(result),
1924
+ interactionId: payload.interactionId,
1925
+ campaignId: payload.campaignId,
1926
+ },
1927
+ ['behavioral', 'business', 'operational']
1928
+ );
1929
+
1930
+ LoggerProxy.log('Campaign preview contact removed successfully', {
1931
+ module: CC_FILE,
1932
+ method: METHODS.REMOVE_PREVIEW_CONTACT,
1933
+ trackingId: result.trackingId,
1934
+ interactionId: payload.interactionId,
1935
+ });
1936
+
1937
+ return result;
1938
+ } catch (error) {
1939
+ const failure = error.details as Failure;
1940
+ this.metricsManager.trackEvent(
1941
+ METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED,
1942
+ {
1943
+ ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failure),
1944
+ interactionId: payload.interactionId,
1945
+ campaignId: payload.campaignId,
1946
+ },
1947
+ ['behavioral', 'business', 'operational']
1948
+ );
1949
+ const {error: detailedError} = getErrorDetails(
1950
+ error,
1951
+ METHODS.REMOVE_PREVIEW_CONTACT,
1952
+ CC_FILE
1953
+ );
1954
+ throw detailedError;
1955
+ }
1956
+ }
1957
+
1512
1958
  /**
1513
1959
  * Fetches outdial ANI (Automatic Number Identification) entries for an outdial ANI ID.
1514
1960
  *
@@ -1656,6 +2102,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1656
2102
  /**
1657
2103
  * Updates the agent device type and login configuration.
1658
2104
  * 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).
2105
+ * Change to any field of the profile is allowed;
1659
2106
  *
1660
2107
  * @param {AgentDeviceUpdate} data Configuration containing:
1661
2108
  * - loginOption: New device type ('BROWSER', 'EXTENSION', 'AGENT_DN')
@@ -1695,29 +2142,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1695
2142
  });
1696
2143
 
1697
2144
  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
2145
  await this.stationLogout({
1720
- logoutReason: 'User requested agent device change',
2146
+ logoutReason: 'User requested agent profile update',
1721
2147
  });
1722
2148
 
1723
2149
  const loginPayload: AgentLogin = {