@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
package/src/cc.ts CHANGED
@@ -56,6 +56,9 @@ import {
56
56
  import {ConnectionLostDetails} from './services/core/websocket/types';
57
57
  import TaskManager from './services/task/TaskManager';
58
58
  import WebCallingService from './services/WebCallingService';
59
+ import AnswerCallOnWebexService from './services/AnswerCallOnWebexService';
60
+ import WebexCrossClientService from './services/WebexCrossClientService';
61
+ import WxAppTelephonyMercurySync from './services/WxAppTelephonyMercurySync';
59
62
  import {
60
63
  ITask,
61
64
  TASK_EVENTS,
@@ -232,6 +235,34 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
232
235
  */
233
236
  private webCallingService: WebCallingService;
234
237
 
238
+ /**
239
+ * REST telephony for wxApp thick-client answer (WXCC-6026)
240
+ * @private
241
+ */
242
+ private answerCallOnWebexService: AnswerCallOnWebexService;
243
+
244
+ /**
245
+ * usersub cross-client state for Webex App toast suppression
246
+ * @private
247
+ */
248
+ private webexCrossClientService: WebexCrossClientService;
249
+
250
+ /**
251
+ * Mercury telephony mute sync for wxApp thick-client answer
252
+ * @private
253
+ */
254
+ private wxAppTelephonyMercurySync: WxAppTelephonyMercurySync;
255
+
256
+ /** True when wxApp mute sync opened Mercury (Extension/DN-only profiles). */
257
+ private wxAppMercuryConnectedByCc = false;
258
+
259
+ /** True when wxApp mute sync registered the Webex device. */
260
+ private wxAppDeviceRegisteredByCc = false;
261
+
262
+ private static readonly WXAPP_FALSE_PUBLISH_RETRY_DELAY_MS = 30_000;
263
+ private static readonly WXAPP_FALSE_PUBLISH_MAX_RETRIES = 3;
264
+ private wxAppFalsePublishRetryTimer?: ReturnType<typeof setTimeout>;
265
+
235
266
  /**
236
267
  * Core service managers for Contact Center operations
237
268
  * Includes agent, connection, and configuration services
@@ -405,6 +436,9 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
405
436
  });
406
437
 
407
438
  this.webCallingService = new WebCallingService(this.$webex);
439
+ this.answerCallOnWebexService = new AnswerCallOnWebexService(this.$webex);
440
+ this.webexCrossClientService = new WebexCrossClientService(this.$webex);
441
+ this.wxAppTelephonyMercurySync = new WxAppTelephonyMercurySync(this.$webex);
408
442
  this.apiAIAssistant = new ApiAIAssistant(this.$webex);
409
443
  this.metricsManager = MetricsManager.getInstance({webex: this.$webex});
410
444
  this.taskManager = TaskManager.getTaskManager(
@@ -414,6 +448,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
414
448
  this.services.webSocketManager,
415
449
  this.services.rtdWebSocketManager
416
450
  );
451
+ this.taskManager.setAnswerCallOnWebexService(this.answerCallOnWebexService);
417
452
  this.incomingTaskListener();
418
453
 
419
454
  // Initialize API instances
@@ -634,6 +669,10 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
634
669
  this.services.rtdWebSocketManager.off('message', this.handleRTDWebsocketMessage);
635
670
  this.services.connectionService.off('connectionLost', this.handleConnectionLost);
636
671
 
672
+ const {publishError: wxAppPublishError} = await this.teardownWxAppLocalState({
673
+ clearInitFlag: true,
674
+ });
675
+
637
676
  if (
638
677
  this.agentConfig.webRtcEnabled &&
639
678
  this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER)
@@ -665,6 +704,10 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
665
704
  // Clear any cached agent configuration
666
705
  this.agentConfig = null;
667
706
 
707
+ if (wxAppPublishError) {
708
+ throw wxAppPublishError;
709
+ }
710
+
668
711
  LoggerProxy.log('Deregistered successfully', {
669
712
  module: CC_FILE,
670
713
  method: METHODS.DEREGISTER,
@@ -727,6 +770,9 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
727
770
  * ```
728
771
  */
729
772
  public async getBuddyAgents(data: BuddyAgents): Promise<BuddyAgentsResponse> {
773
+ const {action, state, mediaType = 'telephony'} = data;
774
+ const buddyAgentState = state ?? (action === 'Transfer' ? AGENT_STATE_AVAILABLE : undefined);
775
+
730
776
  LoggerProxy.info('Fetching buddy agents', {
731
777
  module: CC_FILE,
732
778
  method: METHODS.GET_BUDDY_AGENTS,
@@ -737,15 +783,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
737
783
  METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_FAILED,
738
784
  ]);
739
785
  const resp = await this.services.agent.buddyAgents({
740
- data: {agentProfileId: this.agentConfig.agentProfileID, ...data},
786
+ data: {
787
+ agentProfileId: this.agentConfig.agentProfileID,
788
+ mediaType,
789
+ ...(buddyAgentState ? {state: buddyAgentState} : {}),
790
+ },
741
791
  });
742
792
 
743
793
  this.metricsManager.trackEvent(
744
794
  METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_SUCCESS,
745
795
  {
746
796
  ...MetricsManager.getCommonTrackingFieldForAQMResponse(resp),
747
- mediaType: data.mediaType,
748
- buddyAgentState: data.state,
797
+ mediaType,
798
+ buddyAgentState,
749
799
  buddyAgentCount: resp.data.agentList.length,
750
800
  },
751
801
  ['operational']
@@ -765,8 +815,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
765
815
  METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_FAILED,
766
816
  {
767
817
  ...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failureResp),
768
- mediaType: data.mediaType,
769
- buddyAgentState: data.state,
818
+ mediaType,
819
+ buddyAgentState,
770
820
  },
771
821
  ['operational']
772
822
  );
@@ -808,6 +858,13 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
808
858
  webRtcEnabled: this.agentConfig.webRtcEnabled,
809
859
  autoWrapup: this.agentConfig.wrapUpData?.wrapUpProps?.autoWrapup ?? false,
810
860
  aiFeature: this.agentConfig.aiFeature,
861
+ consultTransfer: {
862
+ allowConsultToQueue: this.agentConfig.allowConsultToQueue,
863
+ accessQueue: this.agentConfig.accessQueue,
864
+ accessEntryPoint: this.agentConfig.accessEntryPoint,
865
+ accessBuddyTeam: this.agentConfig.accessBuddyTeam,
866
+ },
867
+ enableWxBetterTogether: this.isWxBetterTogetherEnabled(),
811
868
  };
812
869
  this.taskManager.setConfigFlags(configFlags);
813
870
  // TODO: Make profile a singleton to make it available throughout app/sdk so we dont need to inject info everywhere
@@ -1009,6 +1066,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1009
1066
  };
1010
1067
 
1011
1068
  this.webCallingService.setLoginOption(data.loginOption);
1069
+ if (this.agentConfig) {
1070
+ this.agentConfig.deviceType = data.loginOption;
1071
+ if (
1072
+ data.loginOption === LoginOption.AGENT_DN ||
1073
+ data.loginOption === LoginOption.EXTENSION
1074
+ ) {
1075
+ this.agentConfig.defaultDn = data.dialNumber;
1076
+ this.agentConfig.dn = data.dialNumber;
1077
+ }
1078
+ }
1012
1079
  this.metricsManager.trackEvent(
1013
1080
  METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
1014
1081
  {
@@ -1030,6 +1097,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1030
1097
  }
1031
1098
  );
1032
1099
 
1100
+ await this.ensureWxAppPostStationLogin();
1101
+
1033
1102
  return response;
1034
1103
  } catch (error) {
1035
1104
  const failure = error.details as Failure;
@@ -1098,6 +1167,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1098
1167
  this.webCallingService.deregisterWebCallingLine();
1099
1168
  }
1100
1169
 
1170
+ await this.teardownWxAppLocalState();
1171
+
1101
1172
  LoggerProxy.log(`Agent station logout completed successfully`, {
1102
1173
  module: CC_FILE,
1103
1174
  method: METHODS.STATION_LOGOUT,
@@ -1377,6 +1448,430 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1377
1448
  }
1378
1449
  }
1379
1450
 
1451
+ /**
1452
+ * Whether wxApp thick-client answer is enabled for this SDK instance.
1453
+ * @public
1454
+ */
1455
+ public isWxBetterTogetherEnabled(): boolean {
1456
+ return this.$config?.enableWxBetterTogether === true;
1457
+ }
1458
+
1459
+ /**
1460
+ * Current station login option (Extension, Dial Number, or Browser/Desktop).
1461
+ * Prefer webCallingService — updated on every stationLogin before wxApp hooks run.
1462
+ * @private
1463
+ */
1464
+ private getCurrentStationLoginOption(): LoginOption | undefined {
1465
+ return this.webCallingService?.loginOption ?? this.agentConfig?.deviceType;
1466
+ }
1467
+
1468
+ /**
1469
+ * wxApp thick-client answer is supported for Extension and Dial Number station login only —
1470
+ * not Browser/Desktop (WebRTC) login.
1471
+ * @private
1472
+ */
1473
+ private assertWxAppStationLoginSupportedForEnable(): void {
1474
+ const loginOption = this.getCurrentStationLoginOption();
1475
+ if (loginOption !== LoginOption.EXTENSION && loginOption !== LoginOption.AGENT_DN) {
1476
+ throw new Error(
1477
+ 'setManageWebexCallingInWxcc requires EXTENSION or AGENT_DN station login. Complete station login before enabling.'
1478
+ );
1479
+ }
1480
+ }
1481
+
1482
+ /**
1483
+ * Clears runtime wxApp config after logout/deregister so stale flags do not apply on relogin.
1484
+ * @private
1485
+ */
1486
+ private resetEnableWxBetterTogetherConfig(): void {
1487
+ if (this.$config) {
1488
+ this.$config.enableWxBetterTogether = false;
1489
+ }
1490
+ this.taskManager.applyEnableWxBetterTogether(false);
1491
+ }
1492
+
1493
+ /**
1494
+ * Runtime enable/disable for wxApp Better Together (toast suppression + Mercury mute sync).
1495
+ *
1496
+ * **Phase 1 (WXCC-6026 / MMT): NOT part of the public host contract.**
1497
+ * Hosts must set `webexConfig.cc.enableWxBetterTogether` before `webex.init()` / `cc.register()`.
1498
+ * To change the flag after the SDK is initialized, re-init the SDK with the updated config.
1499
+ *
1500
+ * **Supported public read API:** {@link isWxBetterTogetherEnabled}.
1501
+ * **Supported telephony API:** unified `task.accept()`, `task.decline()`, `task.toggleMute()`,
1502
+ * `task.transmitDtmf()` — SDK routes wxApp calls internally when the init flag is active.
1503
+ *
1504
+ * **Production lifecycle does not call this method.** Station login, silent relogin, logout,
1505
+ * and deregister use {@link ensureWxAppPostStationLogin} and {@link teardownWxAppLocalState}
1506
+ * (usersub publish + Mercury subscribe/unsubscribe based on the init flag).
1507
+ *
1508
+ * This setter remains as a **private implementation** for Phase 2 runtime toggle and unit tests.
1509
+ * It updates `$config.enableWxBetterTogether`, TaskManager flags, cross-client usersub
1510
+ * (`answer-calls-on-wxcc`), and Mercury mute sync; rolls back config on publish/connect failure.
1511
+ *
1512
+ * @param enabled - `true` to enable wxApp answer + usersub suppression; `false` to disable and tear down.
1513
+ * @throws When enabling without login, on unsupported BROWSER station login, or when usersub/Mercury init fails.
1514
+ * @internal
1515
+ * @see enableWxBetterTogether (CCPluginConfig init flag)
1516
+ * @see ensureWxAppPostStationLogin
1517
+ * @see teardownWxAppLocalState
1518
+ */
1519
+ private async setManageWebexCallingInWxcc(enabled: boolean): Promise<void> {
1520
+ if (enabled && !this.$webex.internal.device?.userId) {
1521
+ throw new Error('Cannot publish answer-calls-on-wxcc: user is not logged in');
1522
+ }
1523
+
1524
+ if (enabled) {
1525
+ this.assertWxAppStationLoginSupportedForEnable();
1526
+ }
1527
+
1528
+ const previousEnabled = this.isWxBetterTogetherEnabled();
1529
+
1530
+ if (this.$config) {
1531
+ this.$config.enableWxBetterTogether = enabled;
1532
+ }
1533
+
1534
+ this.taskManager.applyEnableWxBetterTogether(enabled);
1535
+
1536
+ let publishedEnable = false;
1537
+
1538
+ try {
1539
+ if (enabled) {
1540
+ await this.ensureWxAppMercuryAndSubscribe();
1541
+ await this.publishAnswerOnWebexCrossClientState(true);
1542
+ publishedEnable = true;
1543
+ this.taskManager.syncWxAppMuteFromCallDetailsForAllTasks();
1544
+ } else {
1545
+ await this.publishAnswerOnWebexCrossClientState(false);
1546
+ this.wxAppTelephonyMercurySync.unsubscribe();
1547
+ await this.releaseWxAppMercuryResources();
1548
+ }
1549
+ } catch (error) {
1550
+ if (publishedEnable) {
1551
+ await this.revertWxAppCrossClientPublish();
1552
+ } else if (enabled) {
1553
+ this.wxAppTelephonyMercurySync.unsubscribe();
1554
+ await this.releaseWxAppMercuryResources();
1555
+ }
1556
+
1557
+ if (this.$config) {
1558
+ this.$config.enableWxBetterTogether = previousEnabled;
1559
+ }
1560
+ this.taskManager.applyEnableWxBetterTogether(previousEnabled);
1561
+ throw error;
1562
+ }
1563
+ }
1564
+
1565
+ private async ensureWxAppPostStationLogin(): Promise<void> {
1566
+ if (this.getCurrentStationLoginOption() === LoginOption.BROWSER) {
1567
+ return;
1568
+ }
1569
+
1570
+ let publishedEnable = false;
1571
+
1572
+ try {
1573
+ if (this.isWxBetterTogetherEnabled()) {
1574
+ await this.ensureWxAppMercuryAndSubscribe();
1575
+ await this.publishAnswerOnWebexCrossClientState(true);
1576
+ publishedEnable = true;
1577
+ this.taskManager.syncWxAppMuteFromCallDetailsForAllTasks();
1578
+ } else {
1579
+ await this.ensureWxAppDeviceRegistered();
1580
+ await this.publishAnswerOnWebexCrossClientState(false, {force: true});
1581
+ this.webexCrossClientService.teardown();
1582
+ this.wxAppTelephonyMercurySync.unsubscribe();
1583
+ await this.releaseWxAppMercuryResources();
1584
+ }
1585
+ } catch (error) {
1586
+ LoggerProxy.error(`Failed to initialize wxApp post-station-login: ${error}`, {
1587
+ module: CC_FILE,
1588
+ method: METHODS.STATION_LOGIN,
1589
+ });
1590
+
1591
+ if (this.isWxBetterTogetherEnabled()) {
1592
+ if (publishedEnable) {
1593
+ await this.revertWxAppCrossClientPublish();
1594
+ } else {
1595
+ this.wxAppTelephonyMercurySync.unsubscribe();
1596
+ await this.releaseWxAppMercuryResources();
1597
+ }
1598
+ this.resetEnableWxBetterTogetherConfig();
1599
+ } else {
1600
+ this.scheduleForcedFalsePublishRetry();
1601
+ }
1602
+ }
1603
+ }
1604
+
1605
+ private clearWxAppFalsePublishRetryTimer(): void {
1606
+ if (this.wxAppFalsePublishRetryTimer) {
1607
+ clearTimeout(this.wxAppFalsePublishRetryTimer);
1608
+ this.wxAppFalsePublishRetryTimer = undefined;
1609
+ }
1610
+ }
1611
+
1612
+ private scheduleForcedFalsePublishRetry(retryAttempt = 0): void {
1613
+ this.scheduleFalsePublishRetry(retryAttempt, {requireDisabled: true});
1614
+ }
1615
+
1616
+ /**
1617
+ * Retries compensating false publish after rollback failure, regardless of enabled flag.
1618
+ * @private
1619
+ */
1620
+ private scheduleCompensatingFalsePublishRetry(retryAttempt = 0): void {
1621
+ this.scheduleFalsePublishRetry(retryAttempt);
1622
+ }
1623
+
1624
+ private scheduleFalsePublishRetry(
1625
+ retryAttempt = 0,
1626
+ options: {requireDisabled?: boolean} = {}
1627
+ ): void {
1628
+ if (retryAttempt >= ContactCenter.WXAPP_FALSE_PUBLISH_MAX_RETRIES) {
1629
+ return;
1630
+ }
1631
+
1632
+ if (options?.requireDisabled && this.isWxBetterTogetherEnabled()) {
1633
+ return;
1634
+ }
1635
+
1636
+ this.clearWxAppFalsePublishRetryTimer();
1637
+
1638
+ this.wxAppFalsePublishRetryTimer = setTimeout(async () => {
1639
+ this.wxAppFalsePublishRetryTimer = undefined;
1640
+
1641
+ if (options?.requireDisabled && this.isWxBetterTogetherEnabled()) {
1642
+ return;
1643
+ }
1644
+
1645
+ if (
1646
+ !options?.requireDisabled &&
1647
+ this.isWxBetterTogetherEnabled() &&
1648
+ this.webexCrossClientService.isAnswerCallsStateActive()
1649
+ ) {
1650
+ return;
1651
+ }
1652
+
1653
+ try {
1654
+ await this.ensureWxAppDeviceRegistered();
1655
+ await this.publishAnswerOnWebexCrossClientState(false, {force: true});
1656
+ this.webexCrossClientService.teardown();
1657
+ this.wxAppTelephonyMercurySync.unsubscribe();
1658
+ await this.releaseWxAppMercuryResources();
1659
+ } catch (retryError) {
1660
+ LoggerProxy.error(
1661
+ `Failed to retry forced answer-calls-on-wxcc false publish: ${retryError}`,
1662
+ {
1663
+ module: CC_FILE,
1664
+ method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
1665
+ }
1666
+ );
1667
+ this.scheduleFalsePublishRetry(retryAttempt + 1, options);
1668
+ }
1669
+ }, ContactCenter.WXAPP_FALSE_PUBLISH_RETRY_DELAY_MS);
1670
+ }
1671
+
1672
+ /**
1673
+ * Best-effort rollback when usersub true was published but wxApp setup did not complete.
1674
+ * @private
1675
+ */
1676
+ private async revertWxAppCrossClientPublish(): Promise<void> {
1677
+ try {
1678
+ await this.publishAnswerOnWebexCrossClientState(false, {force: true});
1679
+ } catch (error) {
1680
+ LoggerProxy.error(
1681
+ `Failed to publish compensating answer-calls-on-wxcc false during wxApp rollback: ${error}`,
1682
+ {
1683
+ module: CC_FILE,
1684
+ method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
1685
+ }
1686
+ );
1687
+ this.scheduleCompensatingFalsePublishRetry();
1688
+ }
1689
+
1690
+ this.webexCrossClientService.teardown();
1691
+ this.wxAppTelephonyMercurySync.unsubscribe();
1692
+ await this.releaseWxAppMercuryResources();
1693
+ }
1694
+
1695
+ private async ensureWxAppDeviceRegistered(): Promise<void> {
1696
+ const device = this.$webex.internal.device;
1697
+
1698
+ if (device && !device.registered && device.register) {
1699
+ await device.register();
1700
+ this.wxAppDeviceRegisteredByCc = true;
1701
+ LoggerProxy.log('WxApp: device registered for cross-client publish', {
1702
+ module: CC_FILE,
1703
+ method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
1704
+ });
1705
+ }
1706
+ }
1707
+
1708
+ private async ensureWxAppMercuryConnected(): Promise<void> {
1709
+ const mercury = this.$webex.internal.mercury;
1710
+
1711
+ await this.ensureWxAppDeviceRegistered();
1712
+
1713
+ if (mercury && !mercury.connected) {
1714
+ await mercury.connect();
1715
+ this.wxAppMercuryConnectedByCc = true;
1716
+ LoggerProxy.log('WxApp mute sync: mercury connected', {
1717
+ module: CC_FILE,
1718
+ method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
1719
+ });
1720
+ }
1721
+ }
1722
+
1723
+ /**
1724
+ * Disconnects Mercury and unregisters the device when wxApp mute sync opened them.
1725
+ * @private
1726
+ */
1727
+ private async releaseWxAppMercuryResources(): Promise<void> {
1728
+ const mercury = this.$webex.internal.mercury;
1729
+ const device = this.$webex.internal.device;
1730
+
1731
+ if (this.wxAppMercuryConnectedByCc && mercury?.connected) {
1732
+ try {
1733
+ await mercury.disconnect();
1734
+ this.wxAppMercuryConnectedByCc = false;
1735
+ LoggerProxy.log('WxApp mute sync: mercury disconnected', {
1736
+ module: CC_FILE,
1737
+ method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
1738
+ });
1739
+ } catch (error) {
1740
+ LoggerProxy.error(`Failed to disconnect wxApp Mercury: ${error}`, {
1741
+ module: CC_FILE,
1742
+ method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
1743
+ });
1744
+ }
1745
+ }
1746
+
1747
+ if (this.wxAppDeviceRegisteredByCc && device?.registered && device.unregister) {
1748
+ try {
1749
+ // @ts-ignore
1750
+ await device.unregister();
1751
+ this.wxAppDeviceRegisteredByCc = false;
1752
+ LoggerProxy.log('WxApp mute sync: device unregistered', {
1753
+ module: CC_FILE,
1754
+ method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
1755
+ });
1756
+ } catch (error) {
1757
+ LoggerProxy.error(`Failed to unregister wxApp device: ${error}`, {
1758
+ module: CC_FILE,
1759
+ method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
1760
+ });
1761
+ }
1762
+ } else if (this.wxAppDeviceRegisteredByCc) {
1763
+ this.wxAppDeviceRegisteredByCc = false;
1764
+ }
1765
+ }
1766
+
1767
+ /**
1768
+ * Clears wxApp runtime state and feature-owned resources. Publish failures are logged;
1769
+ * optional rethrow for deregister when callers need to surface usersub errors.
1770
+ * @private
1771
+ */
1772
+ private async teardownWxAppLocalState(options?: {
1773
+ rethrowPublishError?: boolean;
1774
+ clearInitFlag?: boolean;
1775
+ }): Promise<{publishError?: unknown}> {
1776
+ this.clearWxAppFalsePublishRetryTimer();
1777
+ let publishError: unknown;
1778
+
1779
+ try {
1780
+ const shouldForceFalsePublish =
1781
+ this.isWxBetterTogetherEnabled() || this.webexCrossClientService.isAnswerCallsStateActive();
1782
+
1783
+ await this.publishAnswerOnWebexCrossClientState(false, {
1784
+ force: shouldForceFalsePublish,
1785
+ });
1786
+ } catch (error) {
1787
+ publishError = error;
1788
+ LoggerProxy.error(
1789
+ `Failed to publish answer-calls-on-wxcc false during wxApp teardown: ${error}`,
1790
+ {
1791
+ module: CC_FILE,
1792
+ method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
1793
+ }
1794
+ );
1795
+ }
1796
+
1797
+ this.webexCrossClientService.teardown();
1798
+ this.wxAppTelephonyMercurySync.unsubscribe();
1799
+ await this.releaseWxAppMercuryResources();
1800
+
1801
+ if (options?.clearInitFlag) {
1802
+ this.resetEnableWxBetterTogetherConfig();
1803
+ }
1804
+
1805
+ if (publishError) {
1806
+ this.scheduleCompensatingFalsePublishRetry();
1807
+ }
1808
+
1809
+ if (options?.rethrowPublishError && publishError) {
1810
+ throw publishError;
1811
+ }
1812
+
1813
+ return {publishError: publishError ?? undefined};
1814
+ }
1815
+
1816
+ private async ensureWxAppMercuryAndSubscribe(): Promise<void> {
1817
+ if (!this.isWxBetterTogetherEnabled()) {
1818
+ return;
1819
+ }
1820
+
1821
+ if (this.getCurrentStationLoginOption() === LoginOption.BROWSER) {
1822
+ return;
1823
+ }
1824
+
1825
+ const agentId = this.agentConfig?.agentId;
1826
+ if (!agentId) {
1827
+ LoggerProxy.error('Cannot subscribe wxApp mute sync: agentId unavailable', {
1828
+ module: CC_FILE,
1829
+ method: METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY,
1830
+ });
1831
+
1832
+ return;
1833
+ }
1834
+
1835
+ try {
1836
+ await this.ensureWxAppMercuryConnected();
1837
+ this.wxAppTelephonyMercurySync.subscribe(agentId, (callId, muted) => {
1838
+ this.taskManager.applyWxAppMuteStateFromSync(callId, muted);
1839
+ });
1840
+ } catch (error) {
1841
+ LoggerProxy.error(`Failed to ensure wxApp Mercury mute sync: ${error}`, {
1842
+ module: CC_FILE,
1843
+ method: METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY,
1844
+ });
1845
+ this.wxAppTelephonyMercurySync.unsubscribe();
1846
+ await this.releaseWxAppMercuryResources();
1847
+ throw error;
1848
+ }
1849
+ }
1850
+
1851
+ private async publishAnswerOnWebexCrossClientState(
1852
+ enable: boolean,
1853
+ options?: {force?: boolean}
1854
+ ): Promise<void> {
1855
+ if (enable && !this.isWxBetterTogetherEnabled()) {
1856
+ return;
1857
+ }
1858
+
1859
+ if (!enable && !options?.force && !this.webexCrossClientService.isAnswerCallsStateActive()) {
1860
+ return;
1861
+ }
1862
+
1863
+ const userId = this.$webex.internal.device?.userId;
1864
+ if (!userId) {
1865
+ return;
1866
+ }
1867
+
1868
+ await this.webexCrossClientService.setManageWebexCallingInWxcc(enable, {userId});
1869
+
1870
+ if (enable) {
1871
+ this.clearWxAppFalsePublishRetryTimer();
1872
+ }
1873
+ }
1874
+
1380
1875
  /**
1381
1876
  * Initializes event listeners for the Contact Center service
1382
1877
  * Sets up handlers for connection state changes and other core events
@@ -1481,6 +1976,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
1481
1976
  this.agentConfig.lastStateAuxCodeId = auxCodeId;
1482
1977
  this.agentConfig.isAgentLoggedIn = true;
1483
1978
 
1979
+ await this.ensureWxAppPostStationLogin();
1980
+
1484
1981
  LoggerProxy.log(
1485
1982
  `Silent relogin process completed successfully with login Option: ${reLoginResponse.data.deviceType} teamId: ${reLoginResponse.data.teamId}`,
1486
1983
  {
@@ -2199,8 +2696,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
2199
2696
  }
2200
2697
 
2201
2698
  /**
2202
- * Returns paginated entry points for the organization.
2203
- * Thin wrapper around internal EntryPoint instance.
2699
+ * Returns paginated entry points using the EntryPoint service defaults.
2700
+ * Existing request parameters can override those defaults.
2204
2701
  * @public
2205
2702
  */
2206
2703
  public async getEntryPoints(
@@ -2210,8 +2707,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
2210
2707
  }
2211
2708
 
2212
2709
  /**
2213
- * Returns paginated contact service queues for the organization.
2214
- * Thin wrapper around internal Queue instance.
2710
+ * Returns paginated contact service queues using the Queue service defaults.
2711
+ * Existing request parameters can override those defaults.
2215
2712
  * @public
2216
2713
  */
2217
2714
  public async getQueues(
package/src/config.ts CHANGED
@@ -31,6 +31,13 @@ export default {
31
31
  * @default false
32
32
  */
33
33
  disableWebRTCRegistration: false,
34
+ /**
35
+ * Whether wxApp Better Together is enabled at SDK init.
36
+ * Must be set before SDK init. Default false (WXCC-6026).
37
+ * @type {boolean}
38
+ * @default false
39
+ */
40
+ enableWxBetterTogether: false,
34
41
  /**
35
42
  * The type of client making the connection.
36
43
  * @type {string}
package/src/constants.ts CHANGED
@@ -15,6 +15,7 @@ export const WEBEX_REQUEST_FILE = 'WebexRequest';
15
15
  export const TASK_MANAGER_FILE = 'TaskManager';
16
16
  export const TASK_FILE = 'Task';
17
17
  // AGENT OUTDIAL CONSTANTS
18
+ export const INBOUND_DIRECTION = 'INBOUND';
18
19
  export const OUTDIAL_DIRECTION = 'OUTBOUND';
19
20
  export const ATTRIBUTES = {};
20
21
  export const OUTDIAL_MEDIA_TYPE = 'telephony';
@@ -56,9 +57,11 @@ export const METHODS = {
56
57
  TRANSFER_CALL: 'transfer',
57
58
  CONSULT_TRANSFER: 'consultTransfer',
58
59
  CONSULT_CONFERENCE: 'consultConference',
60
+ DROP_CONFERENCE_PARTICIPANT: 'dropConferenceParticipant',
59
61
  EXIT_CONFERENCE: 'exitConference',
60
62
  TRANSFER_CONFERENCE: 'transferConference',
61
63
  TOGGLE_MUTE: 'toggleMute',
64
+ TRANSMIT_DTMF: 'transmitDtmf',
62
65
  COMPLETE_TRANSFER: 'completeTransfer',
63
66
  GET_OUTDIAL_ANI_ENTRIES: 'getOutdialAniEntries',
64
67
  ACCEPT_PREVIEW_CONTACT: 'acceptPreviewContact',
@@ -69,4 +72,9 @@ export const METHODS = {
69
72
  GET_REAL_TIME_ASSISTANCE: 'getRealTimeAssistance',
70
73
  SEND_REAL_TIME_ASSISTANCE_USER_ACTION: 'sendRealTimeAssistanceUserAction',
71
74
  FETCH_HISTORIC_TRANSCRIPTS: 'fetchHistoricTranscripts',
75
+ SET_MANAGE_WEBEX_CALLING_IN_WXCC: 'setManageWebexCallingInWxcc',
76
+ IS_WX_BETTER_TOGETHER_ENABLED: 'isWxBetterTogetherEnabled',
77
+ SYNC_WXAPP_MUTE_FROM_MERCURY: 'syncWxAppMuteFromMercury',
78
+ GET_CALL_DETAILS_ON_WEBEX: 'getCallDetailsOnWebex',
79
+ ENSURE_WXAPP_MERCURY_CONNECTED: 'ensureWxAppMercuryConnected',
72
80
  };
package/src/index.ts CHANGED
@@ -133,6 +133,8 @@ export type {
133
133
  TaskData,
134
134
  /** Task response */
135
135
  TaskResponse,
136
+ /** Conference participant drop request */
137
+ DropConferenceParticipantPayload,
136
138
  ConsultPayload,
137
139
  ConsultEndPayload,
138
140
  ConsultTransferPayLoad,