@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
@@ -3,6 +3,7 @@
3
3
  */
4
4
 
5
5
  import {
6
+ CONSULT_TRANSFER_DESTINATION_TYPE,
6
7
  InteractionUIControls,
7
8
  TASK_CHANNEL_TYPE,
8
9
  TaskData,
@@ -11,6 +12,10 @@ import {
11
12
  VOICE_VARIANT,
12
13
  } from '../types';
13
14
  import {TaskContext, UIControlConfig} from './types';
15
+ import {
16
+ getWebexCallingDeviceDetailsForAgent,
17
+ isWxAppEngagedForControls,
18
+ } from '../WebexCallingUtils';
14
19
  import {TaskState, MAX_PARTICIPANTS_IN_MULTIPARTY_CONFERENCE, INTERACTION_STATE} from './constants';
15
20
  import {
16
21
  getIsCustomerInCall,
@@ -20,11 +25,41 @@ import {
20
25
  getIsConsultedAgentForControls,
21
26
  getServerHoldStateForControls,
22
27
  } from '../TaskUtils';
28
+ import {INBOUND_DIRECTION, OUTDIAL_DIRECTION} from '../../../constants';
29
+ import {COLLABORATION_ACCESS} from '../../config/constants';
30
+
31
+ type DestinationControls = TaskUIControls['consultTransferDestinations'];
23
32
 
24
33
  const DISABLED = {isVisible: false, isEnabled: false} as const;
25
34
  const VISIBLE_ENABLED = {isVisible: true, isEnabled: true} as const;
26
35
  const VISIBLE_DISABLED = {isVisible: true, isEnabled: false} as const;
27
36
 
37
+ /** wxApp telephony controls live on the main leg only (mute/keypad via wxApp APIs). */
38
+ function withWxAppTelephonyControls(
39
+ controls: InteractionUIControls,
40
+ isWxAppEngaged: boolean,
41
+ currentLeg: TaskUILeg,
42
+ isWrappingUp: boolean
43
+ ): InteractionUIControls {
44
+ if (!isWxAppEngaged || isWrappingUp) {
45
+ return controls;
46
+ }
47
+
48
+ if (currentLeg === 'main') {
49
+ return {
50
+ ...controls,
51
+ mute: VISIBLE_ENABLED,
52
+ keypad: VISIBLE_ENABLED,
53
+ };
54
+ }
55
+
56
+ return {
57
+ ...controls,
58
+ mute: DISABLED,
59
+ keypad: DISABLED,
60
+ };
61
+ }
62
+
28
63
  function getDefaultInteractionUIControls(): InteractionUIControls {
29
64
  return {
30
65
  accept: DISABLED,
@@ -43,18 +78,24 @@ function getDefaultInteractionUIControls(): InteractionUIControls {
43
78
  transferConference: DISABLED,
44
79
  mergeToConference: DISABLED,
45
80
  switch: DISABLED,
81
+ keypad: DISABLED,
46
82
  };
47
83
  }
48
84
 
49
85
  function createTaskUIControls(
50
86
  main: InteractionUIControls,
51
87
  consult: InteractionUIControls,
52
- activeLeg: TaskUILeg
88
+ activeLeg: TaskUILeg,
89
+ consultTransferDestinations: DestinationControls = {
90
+ consult: [],
91
+ transfer: [],
92
+ }
53
93
  ): TaskUIControls {
54
94
  return {
55
95
  main,
56
96
  consult,
57
97
  activeLeg,
98
+ consultTransferDestinations,
58
99
  };
59
100
  }
60
101
 
@@ -66,6 +107,53 @@ export function getDefaultUIControls(): TaskUIControls {
66
107
  );
67
108
  }
68
109
 
110
+ function getConsultTransferDestinations(
111
+ config: UIControlConfig,
112
+ taskData?: TaskData | null
113
+ ): DestinationControls {
114
+ const destinationConfig = config.consultTransferConfig;
115
+ if (!destinationConfig) {
116
+ return {consult: [], transfer: []};
117
+ }
118
+
119
+ const isVoice = config.channelType === TASK_CHANNEL_TYPE.VOICE;
120
+ const interaction = taskData?.interaction;
121
+ const direction = interaction?.contactDirection?.type?.toUpperCase();
122
+ const destinations: DestinationControls = {
123
+ consult: [],
124
+ transfer: [],
125
+ };
126
+
127
+ if (destinationConfig.accessBuddyTeam?.toUpperCase() !== COLLABORATION_ACCESS.NONE) {
128
+ destinations.consult.push(CONSULT_TRANSFER_DESTINATION_TYPE.AGENT);
129
+ destinations.transfer.push(CONSULT_TRANSFER_DESTINATION_TYPE.AGENT);
130
+ }
131
+
132
+ if (destinationConfig.accessQueue?.toUpperCase() !== COLLABORATION_ACCESS.NONE) {
133
+ const canConsultQueue = !isVoice || destinationConfig.allowConsultToQueue;
134
+ const canTransferQueue =
135
+ !isVoice ||
136
+ direction === INBOUND_DIRECTION ||
137
+ (direction === OUTDIAL_DIRECTION &&
138
+ interaction?.callProcessingDetails?.outdialTransferToQueueEnabled === true);
139
+
140
+ if (canConsultQueue) destinations.consult.push(CONSULT_TRANSFER_DESTINATION_TYPE.QUEUE);
141
+ if (canTransferQueue) destinations.transfer.push(CONSULT_TRANSFER_DESTINATION_TYPE.QUEUE);
142
+ }
143
+
144
+ if (isVoice) {
145
+ destinations.consult.push(CONSULT_TRANSFER_DESTINATION_TYPE.DIALNUMBER);
146
+ destinations.transfer.push(CONSULT_TRANSFER_DESTINATION_TYPE.DIALNUMBER);
147
+
148
+ if (destinationConfig.accessEntryPoint?.toUpperCase() !== COLLABORATION_ACCESS.NONE) {
149
+ destinations.consult.push(CONSULT_TRANSFER_DESTINATION_TYPE.ENTRYPOINT);
150
+ destinations.transfer.push(CONSULT_TRANSFER_DESTINATION_TYPE.ENTRYPOINT);
151
+ }
152
+ }
153
+
154
+ return destinations;
155
+ }
156
+
69
157
  /** Consult media must exist on the interaction payload, not only as a stale resource id. */
70
158
  function hasConsultMediaInInteraction(
71
159
  interaction: TaskData['interaction'] | undefined,
@@ -134,6 +222,23 @@ function computeVoiceInteractionUIControls(
134
222
  interaction && mainCallId ? getConferenceParticipantsCount(interaction, mainCallId) : 0;
135
223
  const maxParticipants = participantCount >= MAX_PARTICIPANTS_IN_MULTIPARTY_CONFERENCE;
136
224
  const selfAgentId = config.agentId ?? taskData?.agentId;
225
+ const enableWxBetterTogether = config.enableWxBetterTogether ?? false;
226
+ const wxAppDeviceDetails = enableWxBetterTogether
227
+ ? getWebexCallingDeviceDetailsForAgent(selfAgentId, interaction?.participants)
228
+ : undefined;
229
+ const isWxAppParticipant = enableWxBetterTogether && wxAppDeviceDetails?.deviceType === 'wxApp';
230
+ const isWxAppOffer =
231
+ isWxAppParticipant && Boolean(wxAppDeviceDetails) && state === TaskState.OFFERED;
232
+ const isWxAppInboundOffer = isWxAppOffer && !isOutdial;
233
+ const isWxAppOutdialOffer = isWxAppOffer && isOutdial;
234
+ const wxAppAnswerPending = config.wxAppAnswerPending ?? false;
235
+ const wxAppAcceptInFlight = config.wxAppAcceptInFlight ?? false;
236
+ const isWxAppEngaged = isWxAppEngagedForControls(
237
+ enableWxBetterTogether,
238
+ selfAgentId,
239
+ interaction?.participants,
240
+ state
241
+ );
137
242
  const consultInProgress = getIsConsultInProgressForConferenceControls(
138
243
  interaction,
139
244
  mainCallId,
@@ -376,37 +481,52 @@ function computeVoiceInteractionUIControls(
376
481
  ? VISIBLE_ENABLED
377
482
  : DISABLED;
378
483
 
379
- return {
380
- ...getDefaultInteractionUIControls(),
381
- hold: VISIBLE_ENABLED,
382
- transfer: VISIBLE_ENABLED,
383
- consult: VISIBLE_ENABLED,
384
- recording: recordingControl,
385
- end: VISIBLE_DISABLED,
386
- };
484
+ return withWxAppTelephonyControls(
485
+ {
486
+ ...getDefaultInteractionUIControls(),
487
+ hold: VISIBLE_ENABLED,
488
+ transfer: VISIBLE_ENABLED,
489
+ consult: VISIBLE_ENABLED,
490
+ recording: recordingControl,
491
+ end: VISIBLE_DISABLED,
492
+ },
493
+ isWxAppEngaged,
494
+ currentLeg,
495
+ isWrappingUp
496
+ );
387
497
  }
388
498
 
389
499
  if (isConsultRequestedPhase && !isEpDnPendingConferenceMergeUi) {
390
500
  if (currentLeg === 'main') {
391
- return {
392
- ...getDefaultInteractionUIControls(),
393
- transfer: shouldHideBlindTransferForEpDnPendingConferenceMerge
394
- ? DISABLED
395
- : VISIBLE_DISABLED,
396
- conference: VISIBLE_DISABLED,
397
- end: VISIBLE_DISABLED,
398
- };
501
+ return withWxAppTelephonyControls(
502
+ {
503
+ ...getDefaultInteractionUIControls(),
504
+ transfer: shouldHideBlindTransferForEpDnPendingConferenceMerge
505
+ ? DISABLED
506
+ : VISIBLE_DISABLED,
507
+ conference: VISIBLE_DISABLED,
508
+ end: VISIBLE_DISABLED,
509
+ },
510
+ isWxAppEngaged,
511
+ currentLeg,
512
+ isWrappingUp
513
+ );
399
514
  }
400
515
 
401
516
  if (currentLeg === 'consult') {
402
- return {
403
- ...getDefaultInteractionUIControls(),
404
- endConsult: VISIBLE_ENABLED,
405
- switch: VISIBLE_DISABLED,
406
- transfer: VISIBLE_DISABLED,
407
- transferConference: inConference ? VISIBLE_DISABLED : DISABLED,
408
- mergeToConference: VISIBLE_DISABLED,
409
- };
517
+ return withWxAppTelephonyControls(
518
+ {
519
+ ...getDefaultInteractionUIControls(),
520
+ endConsult: VISIBLE_ENABLED,
521
+ switch: VISIBLE_DISABLED,
522
+ transfer: VISIBLE_DISABLED,
523
+ transferConference: inConference ? VISIBLE_DISABLED : DISABLED,
524
+ mergeToConference: VISIBLE_DISABLED,
525
+ },
526
+ isWxAppEngaged,
527
+ currentLeg,
528
+ isWrappingUp
529
+ );
410
530
  }
411
531
  }
412
532
 
@@ -415,11 +535,24 @@ function computeVoiceInteractionUIControls(
415
535
  // Desktop/WebRTC + inbound: accept enabled (agent manually accepts)
416
536
  // Desktop/WebRTC + outdial: accept disabled (auto-answer handles it; Widgets show "Accept" disabled)
417
537
  // Extension mode (non-WebRTC): accept disabled (Widgets show "Ringing...")
418
- accept:
419
- state === TaskState.OFFERED && !interaction?.isTerminated
420
- ? {isVisible: true, isEnabled: isWebrtc && !isOutdial}
421
- : DISABLED,
538
+ accept: (() => {
539
+ if (isWxAppInboundOffer) {
540
+ return wxAppAcceptInFlight || wxAppAnswerPending ? VISIBLE_DISABLED : VISIBLE_ENABLED;
541
+ }
542
+ if (isWxAppOutdialOffer) {
543
+ return wxAppAcceptInFlight || wxAppAnswerPending ? VISIBLE_DISABLED : VISIBLE_ENABLED;
544
+ }
545
+ if (state === TaskState.OFFERED && !interaction?.isTerminated) {
546
+ return {isVisible: true, isEnabled: isWebrtc && !isOutdial};
547
+ }
548
+
549
+ return DISABLED;
550
+ })(),
422
551
  decline: (() => {
552
+ if (wxAppAcceptInFlight) return VISIBLE_DISABLED;
553
+ if (wxAppAnswerPending && isWxAppInboundOffer) return VISIBLE_DISABLED;
554
+ if (isWxAppInboundOffer) return VISIBLE_ENABLED;
555
+ if (isWxAppOutdialOffer) return VISIBLE_ENABLED;
423
556
  if (!isWebrtc || state !== TaskState.OFFERED || interaction?.isTerminated) return DISABLED;
424
557
 
425
558
  return isOutdial ? VISIBLE_DISABLED : VISIBLE_ENABLED;
@@ -447,8 +580,11 @@ function computeVoiceInteractionUIControls(
447
580
  return canHold ? VISIBLE_ENABLED : VISIBLE_DISABLED;
448
581
  })(),
449
582
 
450
- // Mute: WebRTC only, active calls; hidden entirely during wrapup
583
+ // Mute: WebRTC or wxApp engaged calls; hidden entirely during wrapup
451
584
  mute: (() => {
585
+ if (isWxAppEngaged && !isWrappingUp) {
586
+ return currentLeg === 'main' ? VISIBLE_ENABLED : DISABLED;
587
+ }
452
588
  if (!isWebrtc) return DISABLED;
453
589
  if (isWrappingUp) return DISABLED;
454
590
  if (isEpDnPostCallCustomerLeft && inConference) return VISIBLE_DISABLED;
@@ -712,6 +848,14 @@ function computeVoiceInteractionUIControls(
712
848
 
713
849
  return DISABLED;
714
850
  })(),
851
+
852
+ keypad: (() => {
853
+ if (isWxAppEngaged && !isWrappingUp) {
854
+ return currentLeg === 'main' ? VISIBLE_ENABLED : DISABLED;
855
+ }
856
+
857
+ return DISABLED;
858
+ })(),
715
859
  };
716
860
  }
717
861
 
@@ -743,6 +887,7 @@ function computeDigitalInteractionUIControls(
743
887
  transferConference: DISABLED,
744
888
  mergeToConference: DISABLED,
745
889
  switch: DISABLED,
890
+ keypad: DISABLED,
746
891
  };
747
892
  }
748
893
 
@@ -896,6 +1041,11 @@ export function computeUIControls(
896
1041
  return getDefaultUIControls();
897
1042
  }
898
1043
 
1044
+ const consultTransferDestinations = getConsultTransferDestinations(
1045
+ context.uiControlConfig,
1046
+ fallbackTaskData ?? context.taskData
1047
+ );
1048
+
899
1049
  switch (context.uiControlConfig.channelType) {
900
1050
  case TASK_CHANNEL_TYPE.VOICE: {
901
1051
  const {hasConsultLeg, activeLeg, mainState, consultState} = getVoiceLegState(
@@ -922,13 +1072,19 @@ export function computeUIControls(
922
1072
  )
923
1073
  : getDefaultInteractionUIControls();
924
1074
 
925
- return createTaskUIControls(mainControls, consultControls, activeLeg);
1075
+ return createTaskUIControls(
1076
+ mainControls,
1077
+ consultControls,
1078
+ activeLeg,
1079
+ consultTransferDestinations
1080
+ );
926
1081
  }
927
1082
  case TASK_CHANNEL_TYPE.DIGITAL:
928
1083
  return createTaskUIControls(
929
1084
  computeDigitalInteractionUIControls(currentState, context, fallbackTaskData),
930
1085
  getDefaultInteractionUIControls(),
931
- 'main'
1086
+ 'main',
1087
+ consultTransferDestinations
932
1088
  );
933
1089
  default:
934
1090
  return getDefaultUIControls();
@@ -947,6 +1103,16 @@ function haveInteractionUIControlsChanged(
947
1103
  });
948
1104
  }
949
1105
 
1106
+ function haveDestinationListChanged(
1107
+ previous: DestinationControls['consult'],
1108
+ next: DestinationControls['consult']
1109
+ ): boolean {
1110
+ return (
1111
+ previous.length !== next.length ||
1112
+ previous.some((destination, index) => destination !== next[index])
1113
+ );
1114
+ }
1115
+
950
1116
  export function haveUIControlsChanged(
951
1117
  previous: TaskUIControls | undefined,
952
1118
  next: TaskUIControls
@@ -955,6 +1121,14 @@ export function haveUIControlsChanged(
955
1121
 
956
1122
  return (
957
1123
  previous.activeLeg !== next.activeLeg ||
1124
+ haveDestinationListChanged(
1125
+ previous.consultTransferDestinations.consult,
1126
+ next.consultTransferDestinations.consult
1127
+ ) ||
1128
+ haveDestinationListChanged(
1129
+ previous.consultTransferDestinations.transfer,
1130
+ next.consultTransferDestinations.transfer
1131
+ ) ||
958
1132
  haveInteractionUIControlsChanged(previous.main, next.main) ||
959
1133
  haveInteractionUIControlsChanged(previous.consult, next.consult)
960
1134
  );
@@ -3,6 +3,7 @@ import type {AnyActorRef} from 'xstate';
3
3
  import {TaskEventPayload} from './state-machine';
4
4
  import {Msg} from '../core/GlobalTypes';
5
5
  import AutoWrapup from './AutoWrapup';
6
+ import type {CollaborationAccess} from '../config/types';
6
7
 
7
8
  /**
8
9
  * Unique identifier for a task in the contact center system
@@ -33,7 +34,7 @@ export const DESTINATION_TYPE = {
33
34
  DIALNUMBER: 'dialNumber',
34
35
  /** Route task to a specific agent */
35
36
  AGENT: 'agent',
36
- /** Route task to an entry point (supported only for consult operations) */
37
+ /** Route task to an entry point */
37
38
  ENTRYPOINT: 'entryPoint',
38
39
  };
39
40
 
@@ -67,6 +68,17 @@ export const CONSULT_TRANSFER_DESTINATION_TYPE = {
67
68
  */
68
69
  export type ConsultTransferDestinationType = Enum<typeof CONSULT_TRANSFER_DESTINATION_TYPE>;
69
70
 
71
+ /**
72
+ * Agent-profile policy used by the task state machine to derive consult/transfer destinations.
73
+ * @internal
74
+ */
75
+ export type ConsultTransferDestinationConfig = {
76
+ allowConsultToQueue: boolean;
77
+ accessQueue?: CollaborationAccess;
78
+ accessEntryPoint?: CollaborationAccess;
79
+ accessBuddyTeam?: CollaborationAccess;
80
+ };
81
+
70
82
  /**
71
83
  * Defines all supported media channel types for customer interactions
72
84
  * These represent the different ways customers can communicate with agents
@@ -234,6 +246,11 @@ export enum TASK_EVENTS {
234
246
  */
235
247
  TASK_UI_CONTROLS_UPDATED = 'task:ui-controls-updated',
236
248
 
249
+ /**
250
+ * Triggered when wxApp mute state changes from Webex App (Mercury sync or call details).
251
+ */
252
+ TASK_WXAPP_MUTE_STATE_UPDATED = 'task:wxapp-mute-state-updated',
253
+
237
254
  /**
238
255
  * Triggered when a consultation request is accepted
239
256
  * @example
@@ -1134,6 +1151,8 @@ export type TaskData = {
1134
1151
  reservedAgentChannelId?: string;
1135
1152
  /** Indicates if wrap-up is required for this task */
1136
1153
  wrapUpRequired?: boolean;
1154
+ /** SDK-internal: skip task:outdialFailed when non-wxApp agent-terminated outdial still needs wrapup */
1155
+ suppressOutdialFailedPopup?: boolean;
1137
1156
 
1138
1157
  /**
1139
1158
  * Current consultation status derived from state machine
@@ -1215,6 +1234,19 @@ export type InteractionUIControls = {
1215
1234
  mergeToConference: TaskUIControlState;
1216
1235
  wrapup: TaskUIControlState;
1217
1236
  switch: TaskUIControlState;
1237
+ keypad?: TaskUIControlState;
1238
+ };
1239
+
1240
+ /** Options for {@link ITask.toggleMute} — wxApp engaged calls use `muted` for target state. */
1241
+ export type TaskToggleMuteOptions = {
1242
+ muted?: boolean;
1243
+ lineOwnerId?: string;
1244
+ };
1245
+
1246
+ /** Options for {@link ITask.transmitDtmf} — wxApp engaged in-call DTMF. */
1247
+ export type TaskTransmitDtmfOptions = {
1248
+ dtmf: string;
1249
+ lineOwnerId?: string;
1218
1250
  };
1219
1251
 
1220
1252
  export type TaskUILeg = 'main' | 'consult';
@@ -1228,6 +1260,14 @@ export type TaskUIControls = {
1228
1260
  main: InteractionUIControls;
1229
1261
  consult: InteractionUIControls;
1230
1262
  activeLeg: TaskUILeg;
1263
+ /**
1264
+ * Ordered destination categories available for each action.
1265
+ * The first category is the default for consumers that render a destination picker.
1266
+ */
1267
+ consultTransferDestinations: {
1268
+ consult: ConsultTransferDestinationType[];
1269
+ transfer: ConsultTransferDestinationType[];
1270
+ };
1231
1271
  };
1232
1272
 
1233
1273
  /**
@@ -1629,6 +1669,15 @@ export type ParticipantBooleanKey =
1629
1669
  */
1630
1670
  export type TaskResponse = AgentContact | Error | void;
1631
1671
 
1672
+ /**
1673
+ * Request payload for removing a supported participant from an active conference.
1674
+ * @public
1675
+ */
1676
+ export type DropConferenceParticipantPayload = {
1677
+ /** Participant identifier supplied by the current task roster. */
1678
+ participantId: string;
1679
+ };
1680
+
1632
1681
  /**
1633
1682
  * Payload shape used by consult conference helper utilities.
1634
1683
  */
@@ -1687,6 +1736,29 @@ export interface ITask extends IEventEmitter {
1687
1736
  */
1688
1737
  readonly uiControls: TaskUIControls;
1689
1738
 
1739
+ /**
1740
+ * Update wxApp thick-client answer flag at runtime.
1741
+ * Voice tasks override to refresh uiControls; other channel types no-op.
1742
+ * @internal
1743
+ */
1744
+ setEnableWxBetterTogether(enabled: boolean): void;
1745
+
1746
+ /**
1747
+ * Apply wxApp mute state from Mercury sync or call-details fetch.
1748
+ * @internal
1749
+ */
1750
+ applyWxAppMuteStateFromSync(incomingCallId: string, muted: boolean): void;
1751
+
1752
+ /**
1753
+ * Current wxApp mute state for engaged telephony (Voice tasks only).
1754
+ */
1755
+ getWxAppMuted?(): boolean;
1756
+
1757
+ /**
1758
+ * Re-seed wxApp mute from GET telephony/calls/{callId}. Resolves to muted state when known.
1759
+ */
1760
+ syncWxAppMuteFromCallDetails?(): Promise<boolean | undefined>;
1761
+
1690
1762
  /**
1691
1763
  * State machine instance for managing task state transitions and derived properties.
1692
1764
  * The state machine handles:
@@ -1875,6 +1947,18 @@ export interface ITask extends IEventEmitter {
1875
1947
  */
1876
1948
  consultConference(): Promise<TaskResponse>;
1877
1949
 
1950
+ /**
1951
+ * Removes a supported participant from the active conference.
1952
+ * Resolves after the correlated ParticipantLeftConference routing event.
1953
+ * @param payload - Participant identifier to remove
1954
+ * @returns Promise<TaskResponse>
1955
+ * @example
1956
+ * ```typescript
1957
+ * await task.dropConferenceParticipant({participantId: 'participant-id'});
1958
+ * ```
1959
+ */
1960
+ dropConferenceParticipant(payload: DropConferenceParticipantPayload): Promise<TaskResponse>;
1961
+
1878
1962
  /**
1879
1963
  * Exits from an ongoing conference.
1880
1964
  * @returns Promise<TaskResponse>
@@ -1909,14 +1993,29 @@ export interface ITask extends IEventEmitter {
1909
1993
  switchCall(): Promise<TaskResponse>;
1910
1994
 
1911
1995
  /**
1912
- * Toggles mute/unmute for the local audio stream during a WebRTC task.
1996
+ * Toggles mute/unmute for the active call.
1997
+ * WebRTC tasks toggle the local stream; wxApp engaged tasks route to telephony mute REST when
1998
+ * `enableWxBetterTogether` is active.
1999
+ * @param options - Optional target mute state (`muted`) for wxApp; ignored for WebRTC toggle.
1913
2000
  * @returns Promise<void>
1914
2001
  * @example
1915
2002
  * ```typescript
1916
2003
  * await task.toggleMute();
2004
+ * await task.toggleMute({ muted: true });
2005
+ * ```
2006
+ */
2007
+ toggleMute(options?: TaskToggleMuteOptions): Promise<void>;
2008
+
2009
+ /**
2010
+ * Sends in-call DTMF tones for wxApp engaged telephony tasks when `enableWxBetterTogether` is active.
2011
+ * @param options - DTMF digit(s) and optional shared-line owner id.
2012
+ * @returns Promise<void>
2013
+ * @example
2014
+ * ```typescript
2015
+ * await task.transmitDtmf({ dtmf: '5' });
1917
2016
  * ```
1918
2017
  */
1919
- toggleMute(): Promise<void>;
2018
+ transmitDtmf(options: TaskTransmitDtmfOptions): Promise<void>;
1920
2019
  }
1921
2020
 
1922
2021
  /**
@@ -1952,6 +2051,12 @@ export interface IVoice extends ITask {
1952
2051
  * ```
1953
2052
  */
1954
2053
  holdResume(): Promise<TaskResponse>;
2054
+ /** @see wxAppVoiceMethods — WXCC-6026 */
2055
+ isWebexAppCallingOffer(): boolean;
2056
+ /** @see wxAppVoiceMethods — WXCC-6026 */
2057
+ getCallingDeviceDetails(): import('./WebexCallingUtils').WebexCallingDeviceDetails | undefined;
2058
+ /** @see wxAppVoiceMethods — WXCC-6026 */
2059
+ getWebexCallingCallId(): string | null;
1955
2060
  }
1956
2061
 
1957
2062
  /**
@@ -1962,6 +2067,9 @@ export type VoiceUIControlOptions = {
1962
2067
  isEndConsultEnabled?: boolean;
1963
2068
  voiceVariant?: VoiceVariant;
1964
2069
  isRecordingEnabled?: boolean;
2070
+ enableWxBetterTogether?: boolean;
2071
+ consultTransferConfig?: ConsultTransferDestinationConfig;
2072
+ answerCallOnWebexService?: import('../AnswerCallOnWebexService').default;
1965
2073
  };
1966
2074
 
1967
2075
  /**
@@ -1987,7 +2095,7 @@ export interface IWebRTC extends IVoice {
1987
2095
  * task.toggleMute();
1988
2096
  * ```
1989
2097
  */
1990
- toggleMute(): Promise<void>;
2098
+ toggleMute(options?: TaskToggleMuteOptions): Promise<void>;
1991
2099
  /**
1992
2100
  * Decline the incoming task for Browser Login
1993
2101
  *
@@ -2012,6 +2120,8 @@ export type WebSocketPayload = TaskData & {
2012
2120
  type: string;
2013
2121
  mediaResourceId?: string;
2014
2122
  reason?: string;
2123
+ /** ContactEnded: party that ended the contact (e.g. 'Agent', 'Customer') */
2124
+ terminatingParty?: string;
2015
2125
  /**
2016
2126
  * Optional real-time transcript chunk payload.
2017
2127
  * Present on REAL_TIME_TRANSCRIPTION notifications.