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

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 (187) hide show
  1. package/.sdd/manifest.json +5 -4
  2. package/ai-docs/ARCHITECTURE.md +1 -1
  3. package/ai-docs/CONTRACTS.md +5 -1
  4. package/ai-docs/SECURITY.md +1 -1
  5. package/ai-docs/contact-center-spec.md +22 -4
  6. package/ai-docs/features/consult-transfer-list-policy/spec/feature-spec.md +362 -0
  7. package/dist/cc.js +440 -10
  8. package/dist/cc.js.map +1 -1
  9. package/dist/config.js +7 -0
  10. package/dist/config.js.map +1 -1
  11. package/dist/constants.js +10 -2
  12. package/dist/constants.js.map +1 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/metrics/behavioral-events.js +13 -0
  15. package/dist/metrics/behavioral-events.js.map +1 -1
  16. package/dist/metrics/constants.js +2 -0
  17. package/dist/metrics/constants.js.map +1 -1
  18. package/dist/services/AddressBook.js +18 -15
  19. package/dist/services/AddressBook.js.map +1 -1
  20. package/dist/services/AnswerCallOnWebexService.js +174 -0
  21. package/dist/services/AnswerCallOnWebexService.js.map +1 -0
  22. package/dist/services/EntryPoint.js +46 -68
  23. package/dist/services/EntryPoint.js.map +1 -1
  24. package/dist/services/Queue.js +27 -22
  25. package/dist/services/Queue.js.map +1 -1
  26. package/dist/services/WebexCrossClientService.js +171 -0
  27. package/dist/services/WebexCrossClientService.js.map +1 -0
  28. package/dist/services/WxAppTelephonyMercurySync.js +93 -0
  29. package/dist/services/WxAppTelephonyMercurySync.js.map +1 -0
  30. package/dist/services/config/Util.js +3 -0
  31. package/dist/services/config/Util.js.map +1 -1
  32. package/dist/services/config/constants.js +10 -6
  33. package/dist/services/config/constants.js.map +1 -1
  34. package/dist/services/config/types.js +4 -0
  35. package/dist/services/config/types.js.map +1 -1
  36. package/dist/services/core/Err.js.map +1 -1
  37. package/dist/services/core/WebexRequest.js +6 -2
  38. package/dist/services/core/WebexRequest.js.map +1 -1
  39. package/dist/services/core/aqm-reqs.js +28 -14
  40. package/dist/services/core/aqm-reqs.js.map +1 -1
  41. package/dist/services/core/types.js.map +1 -1
  42. package/dist/services/task/Task.js +73 -7
  43. package/dist/services/task/Task.js.map +1 -1
  44. package/dist/services/task/TaskFactory.js +8 -4
  45. package/dist/services/task/TaskFactory.js.map +1 -1
  46. package/dist/services/task/TaskManager.js +176 -20
  47. package/dist/services/task/TaskManager.js.map +1 -1
  48. package/dist/services/task/WebexCallingUtils.js +70 -0
  49. package/dist/services/task/WebexCallingUtils.js.map +1 -0
  50. package/dist/services/task/constants.js +4 -1
  51. package/dist/services/task/constants.js.map +1 -1
  52. package/dist/services/task/contact.js +29 -0
  53. package/dist/services/task/contact.js.map +1 -1
  54. package/dist/services/task/digital/Digital.js +3 -2
  55. package/dist/services/task/digital/Digital.js.map +1 -1
  56. package/dist/services/task/state-machine/TaskStateMachine.js +135 -37
  57. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
  58. package/dist/services/task/state-machine/actions.js +6 -1
  59. package/dist/services/task/state-machine/actions.js.map +1 -1
  60. package/dist/services/task/state-machine/guards.js +38 -11
  61. package/dist/services/task/state-machine/guards.js.map +1 -1
  62. package/dist/services/task/state-machine/types.js.map +1 -1
  63. package/dist/services/task/state-machine/uiControlsComputer.js +118 -18
  64. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -1
  65. package/dist/services/task/types.js +16 -1
  66. package/dist/services/task/types.js.map +1 -1
  67. package/dist/services/task/voice/Voice.js +350 -14
  68. package/dist/services/task/voice/Voice.js.map +1 -1
  69. package/dist/services/task/voice/WebRTC.js +4 -1
  70. package/dist/services/task/voice/WebRTC.js.map +1 -1
  71. package/dist/services/task/voice/wxAppVoiceMethods.js +201 -0
  72. package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -0
  73. package/dist/services/wxAppTelephonyUtils.js +19 -0
  74. package/dist/services/wxAppTelephonyUtils.js.map +1 -0
  75. package/dist/types/cc.d.ts +77 -4
  76. package/dist/types/config.d.ts +7 -0
  77. package/dist/types/constants.d.ts +8 -0
  78. package/dist/types/index.d.ts +3 -1
  79. package/dist/types/metrics/constants.d.ts +2 -0
  80. package/dist/types/services/AddressBook.d.ts +2 -1
  81. package/dist/types/services/AnswerCallOnWebexService.d.ts +37 -0
  82. package/dist/types/services/EntryPoint.d.ts +7 -5
  83. package/dist/types/services/Queue.d.ts +5 -3
  84. package/dist/types/services/WebexCrossClientService.d.ts +28 -0
  85. package/dist/types/services/WxAppTelephonyMercurySync.d.ts +28 -0
  86. package/dist/types/services/config/constants.d.ts +8 -5
  87. package/dist/types/services/config/types.d.ts +17 -4
  88. package/dist/types/services/core/Err.d.ts +2 -0
  89. package/dist/types/services/core/WebexRequest.d.ts +1 -0
  90. package/dist/types/services/core/types.d.ts +2 -0
  91. package/dist/types/services/task/Task.d.ts +20 -2
  92. package/dist/types/services/task/TaskFactory.d.ts +2 -1
  93. package/dist/types/services/task/WebexCallingUtils.d.ts +11 -0
  94. package/dist/types/services/task/constants.d.ts +3 -0
  95. package/dist/types/services/task/contact.d.ts +4 -0
  96. package/dist/types/services/task/digital/Digital.d.ts +2 -2
  97. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +260 -56
  98. package/dist/types/services/task/state-machine/guards.d.ts +6 -5
  99. package/dist/types/services/task/state-machine/types.d.ts +9 -1
  100. package/dist/types/services/task/types.d.ts +81 -4
  101. package/dist/types/services/task/voice/Voice.d.ts +48 -9
  102. package/dist/types/services/task/voice/WebRTC.d.ts +2 -2
  103. package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +52 -0
  104. package/dist/types/services/wxAppTelephonyUtils.d.ts +5 -0
  105. package/dist/types/types.d.ts +14 -8
  106. package/dist/types/utils/PageCache.d.ts +20 -3
  107. package/dist/types.js +7 -2
  108. package/dist/types.js.map +1 -1
  109. package/dist/utils/PageCache.js +19 -5
  110. package/dist/utils/PageCache.js.map +1 -1
  111. package/dist/webex.js +1 -1
  112. package/package.json +1 -1
  113. package/src/cc.ts +506 -9
  114. package/src/config.ts +7 -0
  115. package/src/constants.ts +8 -0
  116. package/src/index.ts +2 -0
  117. package/src/metrics/ai-docs/metrics-spec.md +9 -3
  118. package/src/metrics/behavioral-events.ts +14 -0
  119. package/src/metrics/constants.ts +2 -0
  120. package/src/services/AddressBook.ts +17 -6
  121. package/src/services/AnswerCallOnWebexService.ts +206 -0
  122. package/src/services/EntryPoint.ts +59 -60
  123. package/src/services/Queue.ts +29 -12
  124. package/src/services/WebexCrossClientService.ts +212 -0
  125. package/src/services/WxAppTelephonyMercurySync.ts +115 -0
  126. package/src/services/ai-docs/AGENTS.md +10 -10
  127. package/src/services/ai-docs/services-spec.md +6 -1
  128. package/src/services/config/Util.ts +3 -0
  129. package/src/services/config/ai-docs/AGENTS.md +1 -1
  130. package/src/services/config/ai-docs/ARCHITECTURE.md +2 -2
  131. package/src/services/config/ai-docs/config-spec.md +6 -0
  132. package/src/services/config/constants.ts +10 -6
  133. package/src/services/config/types.ts +16 -4
  134. package/src/services/core/Err.ts +1 -0
  135. package/src/services/core/WebexRequest.ts +3 -1
  136. package/src/services/core/ai-docs/core-spec.md +5 -1
  137. package/src/services/core/aqm-reqs.ts +30 -15
  138. package/src/services/core/types.ts +2 -0
  139. package/src/services/task/Task.ts +79 -8
  140. package/src/services/task/TaskFactory.ts +8 -3
  141. package/src/services/task/TaskManager.ts +254 -15
  142. package/src/services/task/WebexCallingUtils.ts +136 -0
  143. package/src/services/task/ai-docs/ARCHITECTURE.md +1 -0
  144. package/src/services/task/ai-docs/task-spec.md +117 -2
  145. package/src/services/task/constants.ts +3 -0
  146. package/src/services/task/contact.ts +30 -0
  147. package/src/services/task/digital/Digital.ts +4 -2
  148. package/src/services/task/state-machine/TaskStateMachine.ts +210 -63
  149. package/src/services/task/state-machine/actions.ts +5 -2
  150. package/src/services/task/state-machine/ai-docs/AGENTS.md +8 -4
  151. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +17 -8
  152. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +46 -22
  153. package/src/services/task/state-machine/guards.ts +64 -11
  154. package/src/services/task/state-machine/types.ts +16 -1
  155. package/src/services/task/state-machine/uiControlsComputer.ts +206 -32
  156. package/src/services/task/types.ts +114 -4
  157. package/src/services/task/voice/Voice.ts +462 -12
  158. package/src/services/task/voice/WebRTC.ts +5 -1
  159. package/src/services/task/voice/wxAppVoiceMethods.ts +307 -0
  160. package/src/services/wxAppTelephonyUtils.ts +14 -0
  161. package/src/types.ts +33 -9
  162. package/src/utils/AGENTS.md +21 -10
  163. package/src/utils/PageCache.ts +38 -5
  164. package/src/utils/ai-docs/utils-spec.md +21 -11
  165. package/test/unit/spec/cc.ts +1274 -0
  166. package/test/unit/spec/metrics/behavioral-events.ts +18 -0
  167. package/test/unit/spec/services/AddressBook.ts +37 -6
  168. package/test/unit/spec/services/AnswerCallOnWebexService.ts +223 -0
  169. package/test/unit/spec/services/EntryPoint.ts +87 -40
  170. package/test/unit/spec/services/Queue.ts +123 -12
  171. package/test/unit/spec/services/WebexCrossClientService.ts +261 -0
  172. package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +113 -0
  173. package/test/unit/spec/services/config/Util.ts +85 -0
  174. package/test/unit/spec/services/core/WebexRequest.ts +3 -1
  175. package/test/unit/spec/services/core/aqm-reqs.ts +113 -1
  176. package/test/unit/spec/services/task/Task.ts +200 -0
  177. package/test/unit/spec/services/task/TaskFactory.ts +39 -2
  178. package/test/unit/spec/services/task/TaskManager.ts +653 -1
  179. package/test/unit/spec/services/task/WebexCallingUtils.ts +153 -0
  180. package/test/unit/spec/services/task/contact.ts +33 -0
  181. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +854 -72
  182. package/test/unit/spec/services/task/state-machine/guards.ts +210 -8
  183. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +445 -7
  184. package/test/unit/spec/services/task/voice/Voice.ts +874 -0
  185. package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +459 -0
  186. package/umd/contact-center.min.js +2 -2
  187. package/umd/contact-center.min.js.map +1 -1
@@ -14,11 +14,13 @@ var _types3 = require("../../types");
14
14
  var _loggerProxy = _interopRequireDefault(require("../../logger-proxy"));
15
15
  var _TaskUtils = require("./TaskUtils");
16
16
  var _TaskFactory = _interopRequireDefault(require("./TaskFactory"));
17
+ var _WebexCallingUtils = require("./WebexCallingUtils");
17
18
  var _WebRTC = _interopRequireDefault(require("./voice/WebRTC"));
18
19
  var _stateMachine = require("./state-machine");
19
20
  var _taskDataNormalizer = require("./taskDataNormalizer");
20
21
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
21
22
  const CC_EVENT_SET = new Set(Object.values(_types2.CC_EVENTS));
23
+ const MAIN_INTERACTION_CORRELATED_EVENTS = new Set([_types2.CC_EVENTS.AGENT_CONSULT_ENDED, _types2.CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE]);
22
24
  const isCcEvent = value => CC_EVENT_SET.has(value);
23
25
 
24
26
  /** @internal */
@@ -109,6 +111,9 @@ class TaskManager extends _events.default {
109
111
  setWebRtcEnabled(webRtcEnabled) {
110
112
  this.webRtcEnabled = webRtcEnabled;
111
113
  }
114
+ setAnswerCallOnWebexService(answerCallOnWebexService) {
115
+ this.answerCallOnWebexService = answerCallOnWebexService;
116
+ }
112
117
  handleIncomingWebCall = call => {
113
118
  const currentTask = Object.values(this.taskCollection).find(task => task.data.interaction.mediaType === 'telephony' && !(0, _TaskUtils.isCampaignPreviewReservation)(task));
114
119
  if (currentTask) {
@@ -139,9 +144,10 @@ class TaskManager extends _events.default {
139
144
  * @param ccEvent - The CC_EVENT type from WebSocket
140
145
  * @param payload - The event payload
141
146
  * @param agentId - Optional agent ID for state detection (needed for HYDRATE)
147
+ * @param task - Optional task instance for state-aware CONTACT_ENDED mapping
142
148
  * @returns TaskEventPayload for state machine or null if no mapping
143
149
  */
144
- static mapEventToTaskStateMachineEvent(ccEvent, payload, agentId) {
150
+ static mapEventToTaskStateMachineEvent(ccEvent, payload, agentId, task, enableWxBetterTogether) {
145
151
  const mediaResourceId = payload.mediaResourceId || payload.interaction?.media?.[payload.interactionId]?.mediaResourceId;
146
152
  switch (ccEvent) {
147
153
  // CC -> TaskEvent mappings (see TaskStateMachine comment for quick reference)
@@ -284,13 +290,31 @@ class TaskManager extends _events.default {
284
290
  taskData: payload
285
291
  };
286
292
  case _types2.CC_EVENTS.CONTACT_ENDED:
287
- return {
288
- type: _stateMachine.TaskEvent.CONTACT_ENDED,
289
- taskData: {
290
- ...payload,
291
- wrapUpRequired: (0, _TaskUtils.isCampaignPreviewTask)(payload) ? false : payload.agentsPendingWrapUp?.includes(agentId || '') || false
293
+ {
294
+ const wrapUpRequired = (0, _TaskUtils.isCampaignPreviewTask)(payload) ? false : payload.agentsPendingWrapUp?.includes(agentId || '') || false;
295
+
296
+ // Post-accept wxApp outdial agent-end delivers ContactEnded instead of
297
+ // AgentOutboundFailed. Normalize to OUTBOUND_FAILED so state machine
298
+ // enters WRAPPING_UP and emits TASK_OUTDIAL_FAILED (AGENT_ENDS).
299
+ // Pre-accept cancelTask decline stays CONTACT_ENDED → TERMINATED (no wrapup).
300
+ if (TaskManager.isAgentTerminatedOutdialWrapup(payload, wrapUpRequired, task, agentId, enableWxBetterTogether)) {
301
+ return {
302
+ type: _stateMachine.TaskEvent.OUTBOUND_FAILED,
303
+ reason: 'AGENT_ENDS',
304
+ taskData: {
305
+ ...payload,
306
+ wrapUpRequired: true
307
+ }
308
+ };
292
309
  }
293
- };
310
+ return {
311
+ type: _stateMachine.TaskEvent.CONTACT_ENDED,
312
+ taskData: {
313
+ ...payload,
314
+ wrapUpRequired
315
+ }
316
+ };
317
+ }
294
318
  case _types2.CC_EVENTS.AGENT_INVITE_FAILED:
295
319
  return {
296
320
  type: _stateMachine.TaskEvent.INVITE_FAILED,
@@ -308,11 +332,18 @@ class TaskManager extends _events.default {
308
332
  reason: payload.reason
309
333
  };
310
334
  case _types2.CC_EVENTS.AGENT_OUTBOUND_FAILED:
311
- return {
312
- type: _stateMachine.TaskEvent.OUTBOUND_FAILED,
313
- taskData: payload,
314
- reason: payload.reason
315
- };
335
+ {
336
+ const isAgentTerminatedOutdial = payload.interaction?.outboundType === 'OUTDIAL' && (payload.terminatingParty === 'Agent' || payload.reason === 'AGENT_ENDS');
337
+ const suppressOutdialFailedPopup = isAgentTerminatedOutdial && !TaskManager.isWxAppManagedOutdialTask(agentId, payload, task, enableWxBetterTogether);
338
+ return {
339
+ type: _stateMachine.TaskEvent.OUTBOUND_FAILED,
340
+ taskData: suppressOutdialFailedPopup ? {
341
+ ...payload,
342
+ suppressOutdialFailedPopup: true
343
+ } : payload,
344
+ reason: payload.reason
345
+ };
346
+ }
316
347
  case _types2.CC_EVENTS.CAMPAIGN_PREVIEW_ACCEPT_FAILED:
317
348
  return {
318
349
  type: _stateMachine.TaskEvent.CAMPAIGN_PREVIEW_ACCEPT_FAILED,
@@ -385,6 +416,43 @@ class TaskManager extends _events.default {
385
416
  }
386
417
  }
387
418
 
419
+ /**
420
+ * True when the current agent's task is a wxApp-managed outdial (thick-client telephony).
421
+ */
422
+ static isWxAppManagedOutdialTask(agentId, payload, task, enableWxBetterTogether) {
423
+ const wxAppEnabled = task?.uiControlConfig?.enableWxBetterTogether ?? enableWxBetterTogether ?? false;
424
+ if (!wxAppEnabled) {
425
+ return false;
426
+ }
427
+ const effectiveAgentId = agentId ?? task?.data?.agentId;
428
+ const participants = payload.interaction?.participants ?? task?.data?.interaction?.participants;
429
+ const details = (0, _WebexCallingUtils.getWebexCallingDeviceDetailsForAgent)(effectiveAgentId, participants);
430
+ return details?.deviceType === 'wxApp';
431
+ }
432
+
433
+ /**
434
+ * True when ContactEnded represents an agent-terminated wxApp outdial that should
435
+ * enter wrapup with an outdial-failed popup (legacy AgentOutboundFailed parity).
436
+ */
437
+ static isAgentTerminatedOutdialWrapup(payload, wrapUpRequired, task, agentId, enableWxBetterTogether) {
438
+ const isOutdial = payload.interaction?.outboundType === 'OUTDIAL';
439
+ const agentTerminated = payload.terminatingParty === 'Agent';
440
+ if (!isOutdial || !agentTerminated) {
441
+ return false;
442
+ }
443
+ if (!TaskManager.isWxAppManagedOutdialTask(agentId, payload, task, enableWxBetterTogether)) {
444
+ return false;
445
+ }
446
+ const wxAppAnswerPending = task?.uiControlConfig?.wxAppAnswerPending ?? false;
447
+
448
+ // Pre-accept decline: decline() clears wxAppAnswerPending; backend may still send state wrapUp.
449
+ if (!wxAppAnswerPending && !wrapUpRequired) {
450
+ return false;
451
+ }
452
+ const interactionWrapUp = payload.interaction?.state === 'wrapUp';
453
+ return wrapUpRequired || interactionWrapUp || wxAppAnswerPending;
454
+ }
455
+
388
456
  /**
389
457
  * Register WebSocket message listeners for task events
390
458
  *
@@ -493,6 +561,9 @@ class TaskManager extends _events.default {
493
561
  this.taskCollection[interactionId] = task;
494
562
  }
495
563
  }
564
+ if (!task && MAIN_INTERACTION_CORRELATED_EVENTS.has(eventType)) {
565
+ task = this.findUniqueTaskByRelatedInteraction(message.data);
566
+ }
496
567
  const wasConsultedTask = Boolean(task?.data?.isConsulted);
497
568
  const computeWrapUpRequired = () => {
498
569
  if (message.data.wrapUpRequired !== undefined) {
@@ -507,7 +578,7 @@ class TaskManager extends _events.default {
507
578
  ...message.data,
508
579
  wrapUpRequired: computeWrapUpRequired()
509
580
  } : message.data;
510
- const stateMachineEvent = TaskManager.mapEventToTaskStateMachineEvent(eventType, adjustedPayload, this.agentId);
581
+ const stateMachineEvent = TaskManager.mapEventToTaskStateMachineEvent(eventType, adjustedPayload, this.agentId, task, this.configFlags?.enableWxBetterTogether);
511
582
  _loggerProxy.default.info(`Handling task event ${eventType}`, {
512
583
  module: _constants.TASK_MANAGER_FILE,
513
584
  method: 'prepareEventContext',
@@ -521,6 +592,33 @@ class TaskManager extends _events.default {
521
592
  };
522
593
  }
523
594
 
595
+ /**
596
+ * Resolve lifecycle events that are emitted for the main interaction while the
597
+ * local task can still be indexed by a child consult interaction. Exact task
598
+ * keys are handled before this fallback. Multiple aliases of the same task are
599
+ * treated as one candidate; genuinely ambiguous matches are intentionally ignored.
600
+ */
601
+ findUniqueTaskByRelatedInteraction(payload) {
602
+ const payloadInteractionIds = new Set([payload.interactionId, payload.interaction?.interactionId, payload.interaction?.mainInteractionId, payload.interaction?.parentInteractionId, payload.interaction?.callProcessingDetails?.parentInteractionId].filter(interactionId => Boolean(interactionId)));
603
+ if (payloadInteractionIds.size === 0) return undefined;
604
+ const candidates = [...new Set(Object.values(this.taskCollection).filter(candidate => {
605
+ const taskInteraction = candidate?.data?.interaction;
606
+ const candidateInteractionIds = [candidate?.data?.interactionId, taskInteraction?.interactionId, taskInteraction?.mainInteractionId, taskInteraction?.parentInteractionId, taskInteraction?.callProcessingDetails?.parentInteractionId];
607
+ return candidateInteractionIds.some(interactionId => Boolean(interactionId) && payloadInteractionIds.has(interactionId));
608
+ }))];
609
+ if (candidates.length === 1) {
610
+ return candidates[0];
611
+ }
612
+ if (candidates.length > 1) {
613
+ _loggerProxy.default.warn('Unable to correlate task event to a unique main interaction task', {
614
+ module: _constants.TASK_MANAGER_FILE,
615
+ method: 'findUniqueTaskByRelatedInteraction',
616
+ interactionId: payload.interaction?.mainInteractionId || payload.interaction?.parentInteractionId || payload.interaction?.callProcessingDetails?.parentInteractionId || payload.interactionId
617
+ });
618
+ }
619
+ return undefined;
620
+ }
621
+
524
622
  /**
525
623
  * Handle task lifecycle events and determine required actions
526
624
  *
@@ -638,7 +736,7 @@ class TaskManager extends _events.default {
638
736
  wrapUpRequired: false,
639
737
  isConferenceInProgress: false,
640
738
  isAutoAnswering: false
641
- }, this.configFlags, this.wrapupData, this.agentId);
739
+ }, this.configFlags, this.wrapupData, this.agentId, this.answerCallOnWebexService);
642
740
  this.setupTaskListeners(task);
643
741
  this.taskCollection[payload.interactionId] = task;
644
742
  } else {
@@ -664,7 +762,7 @@ class TaskManager extends _events.default {
664
762
  isConsulted: isConsultedTask,
665
763
  isAutoAnswering: shouldAutoAnswer
666
764
  };
667
- const task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId);
765
+ const task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId, this.answerCallOnWebexService);
668
766
  this.setupTaskListeners(task);
669
767
  this.taskCollection[payload.interactionId] = task;
670
768
  return {
@@ -693,7 +791,7 @@ class TaskManager extends _events.default {
693
791
  isConferenceInProgress: (0, _TaskUtils.getIsConferenceInProgress)(payload),
694
792
  isAutoAnswering: shouldAutoAnswer
695
793
  };
696
- task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId);
794
+ task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId, this.answerCallOnWebexService);
697
795
  this.setupTaskListeners(task);
698
796
  this.taskCollection[payload.interactionId] = task;
699
797
  }
@@ -713,6 +811,14 @@ class TaskManager extends _events.default {
713
811
  destinationType: taskData.destinationType ?? snapshot?.context?.consultDestinationType ?? null
714
812
  } : taskData;
715
813
  task.updateTaskData(updateTaskData);
814
+
815
+ // A main-interaction lifecycle event can re-key a child consult task. Remove
816
+ // any aliases first so consumers never observe duplicate task entries.
817
+ Object.entries(this.taskCollection).forEach(([taskId, candidate]) => {
818
+ if (candidate === task && taskId !== taskData.interactionId) {
819
+ delete this.taskCollection[taskId];
820
+ }
821
+ });
716
822
  this.taskCollection[taskData.interactionId] = task;
717
823
  return task;
718
824
  }
@@ -753,8 +859,8 @@ class TaskManager extends _events.default {
753
859
  task.on(_types.TASK_EVENTS.TASK_CLEANUP, (t, options) => {
754
860
  this.handleTaskCleanup(t);
755
861
  if (options?.removeFromCollection) {
756
- const interactionId = t?.data?.interactionId;
757
- if (interactionId && this.taskCollection[interactionId]) {
862
+ const taskIsStillCollected = Object.values(this.taskCollection).some(candidate => candidate === t);
863
+ if (taskIsStillCollected) {
758
864
  this.removeTaskFromCollection(t);
759
865
  }
760
866
  }
@@ -765,7 +871,11 @@ class TaskManager extends _events.default {
765
871
  task.cancelAutoWrapupTimer();
766
872
  }
767
873
  if (task?.data?.interactionId) {
768
- delete this.taskCollection[task.data.interactionId];
874
+ Object.entries(this.taskCollection).forEach(([taskId, candidate]) => {
875
+ if (candidate === task) {
876
+ delete this.taskCollection[taskId];
877
+ }
878
+ });
769
879
  _loggerProxy.default.info(`Task removed from collection`, {
770
880
  module: _constants.TASK_MANAGER_FILE,
771
881
  method: _constants2.METHODS.REMOVE_TASK_FROM_COLLECTION,
@@ -810,7 +920,7 @@ class TaskManager extends _events.default {
810
920
  isConferenceInProgress: (0, _TaskUtils.getIsConferenceInProgress)(payload),
811
921
  isConsulted: false
812
922
  };
813
- task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId);
923
+ task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId, this.answerCallOnWebexService);
814
924
  this.taskCollection[payload.interactionId] = task;
815
925
 
816
926
  // Transition the new task out of IDLE immediately so UI controls are
@@ -884,6 +994,52 @@ class TaskManager extends _events.default {
884
994
  ...this.taskCollection
885
995
  };
886
996
  }
997
+
998
+ /**
999
+ * Propagate runtime enableWxBetterTogether changes to config flags and active tasks.
1000
+ */
1001
+ applyEnableWxBetterTogether(enabled) {
1002
+ if (this.configFlags) {
1003
+ this.configFlags = {
1004
+ ...this.configFlags,
1005
+ enableWxBetterTogether: enabled
1006
+ };
1007
+ }
1008
+ Object.values(this.taskCollection).forEach(task => {
1009
+ task.setEnableWxBetterTogether(enabled);
1010
+ if (enabled) {
1011
+ TaskManager.syncWxAppMuteFromCallDetailsForTask(task);
1012
+ }
1013
+ });
1014
+ }
1015
+
1016
+ /**
1017
+ * Propagate wxApp mute state from Mercury sync to active voice tasks.
1018
+ */
1019
+ applyWxAppMuteStateFromSync(callId, muted) {
1020
+ Object.values(this.taskCollection).forEach(task => {
1021
+ task.applyWxAppMuteStateFromSync(callId, muted);
1022
+ });
1023
+ }
1024
+
1025
+ /**
1026
+ * Re-seeds wxApp mute state from GET telephony/calls/{callId} for one task.
1027
+ */
1028
+ static syncWxAppMuteFromCallDetailsForTask(task) {
1029
+ const sync = task.syncWxAppMuteFromCallDetails?.bind(task);
1030
+ if (typeof sync === 'function') {
1031
+ sync().catch(() => undefined);
1032
+ }
1033
+ }
1034
+
1035
+ /**
1036
+ * Re-seeds wxApp mute for all engaged voice tasks (reload / runtime enable backfill).
1037
+ */
1038
+ syncWxAppMuteFromCallDetailsForAllTasks() {
1039
+ Object.values(this.taskCollection).forEach(task => {
1040
+ TaskManager.syncWxAppMuteFromCallDetailsForTask(task);
1041
+ });
1042
+ }
887
1043
  static getTaskManager(apiAIAssistant, contact, webCallingService, webSocketManager, rtdWebSocketManager) {
888
1044
  if (!TaskManager.taskManager) {
889
1045
  TaskManager.taskManager = new TaskManager(apiAIAssistant, contact, webCallingService, webSocketManager, rtdWebSocketManager);