@webex/contact-center 3.12.0-next.15 → 3.12.0-next.151
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.
- package/.sdd/manifest.json +887 -0
- package/AGENTS.md +94 -0
- package/ai-docs/ARCHITECTURE.md +168 -0
- package/ai-docs/CONTRACTS.md +62 -0
- package/ai-docs/GETTING_STARTED.md +168 -0
- package/ai-docs/GLOSSARY.md +43 -0
- package/ai-docs/README.md +138 -0
- package/ai-docs/REVIEW_CHECKLIST.md +41 -0
- package/ai-docs/RULES.md +444 -0
- package/ai-docs/SECURITY.md +52 -0
- package/ai-docs/SERVICE_STATE.md +52 -0
- package/ai-docs/SPEC_INDEX.md +65 -0
- package/ai-docs/adr/0001-spec-source-policy.md +55 -0
- package/ai-docs/adr/README.md +8 -0
- package/ai-docs/adr/_adr-template.md +31 -0
- package/ai-docs/contact-center-spec.md +382 -0
- package/ai-docs/features/consult-transfer-list-policy/spec/feature-spec.md +362 -0
- package/ai-docs/features/contact-owner-change-refactor.md +78 -0
- package/ai-docs/features/generated-spec-conformance-fidelity-remediation/spec/feature-spec.md +117 -0
- package/ai-docs/features/residual-warning-coverage-completion/spec/feature-spec.md +203 -0
- package/ai-docs/features/validator-code-fidelity-drift-fix/spec/feature-spec.md +315 -0
- package/ai-docs/patterns/event-driven-patterns.md +485 -0
- package/ai-docs/patterns/testing-patterns.md +480 -0
- package/ai-docs/patterns/typescript-patterns.md +365 -0
- package/ai-docs/templates/README.md +102 -0
- package/ai-docs/templates/documentation/create-agents-md.md +240 -0
- package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
- package/ai-docs/templates/existing-service/bug-fix.md +254 -0
- package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
- package/ai-docs/templates/new-method/00-master.md +80 -0
- package/ai-docs/templates/new-method/01-requirements.md +232 -0
- package/ai-docs/templates/new-method/02-implementation.md +295 -0
- package/ai-docs/templates/new-method/03-tests.md +201 -0
- package/ai-docs/templates/new-method/04-validation.md +141 -0
- package/ai-docs/templates/new-service/00-master.md +109 -0
- package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
- package/ai-docs/templates/new-service/02-code-generation.md +346 -0
- package/ai-docs/templates/new-service/03-integration.md +178 -0
- package/ai-docs/templates/new-service/04-test-generation.md +205 -0
- package/ai-docs/templates/new-service/05-validation.md +145 -0
- package/dist/cc.js +1010 -47
- package/dist/cc.js.map +1 -1
- package/dist/config.js +13 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +40 -3
- package/dist/constants.js.map +1 -1
- package/dist/index.js +54 -17
- package/dist/index.js.map +1 -1
- package/dist/metrics/MetricsManager.js +16 -0
- package/dist/metrics/MetricsManager.js.map +1 -1
- package/dist/metrics/behavioral-events.js +211 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +58 -4
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/AddressBook.js +18 -15
- package/dist/services/AddressBook.js.map +1 -1
- package/dist/services/AnswerCallOnWebexService.js +202 -0
- package/dist/services/AnswerCallOnWebexService.js.map +1 -0
- package/dist/services/ApiAiAssistant.js +351 -14
- package/dist/services/ApiAiAssistant.js.map +1 -1
- package/dist/services/EntryPoint.js +46 -68
- package/dist/services/EntryPoint.js.map +1 -1
- package/dist/services/Queue.js +27 -22
- package/dist/services/Queue.js.map +1 -1
- package/dist/services/UserPreference.js +427 -0
- package/dist/services/UserPreference.js.map +1 -0
- package/dist/services/WebexCrossClientService.js +225 -0
- package/dist/services/WebexCrossClientService.js.map +1 -0
- package/dist/services/WxAppTelephonyMercurySync.js +111 -0
- package/dist/services/WxAppTelephonyMercurySync.js.map +1 -0
- package/dist/services/config/Util.js +48 -48
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/constants.js +56 -78
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/index.js +100 -226
- package/dist/services/config/index.js.map +1 -1
- package/dist/services/config/types.js +97 -24
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +111 -32
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/WebexRequest.js +6 -2
- package/dist/services/core/WebexRequest.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +28 -14
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/types.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +2 -1
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/core/websocket/types.js.map +1 -1
- package/dist/services/index.js +1 -1
- package/dist/services/index.js.map +1 -1
- package/dist/services/task/Task.js +955 -0
- package/dist/services/task/Task.js.map +1 -0
- package/dist/services/task/TaskFactory.js +50 -0
- package/dist/services/task/TaskFactory.js.map +1 -0
- package/dist/services/task/TaskManager.js +1272 -523
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +221 -27
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/WebexCallingUtils.js +70 -0
- package/dist/services/task/WebexCallingUtils.js.map +1 -0
- package/dist/services/task/constants.js +21 -2
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/contact.js +29 -0
- package/dist/services/task/contact.js.map +1 -1
- package/dist/services/task/dialer.js +78 -0
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/digital/Digital.js +78 -0
- package/dist/services/task/digital/Digital.js.map +1 -0
- package/dist/services/task/state-machine/TaskStateMachine.js +982 -0
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
- package/dist/services/task/state-machine/actions.js +572 -0
- package/dist/services/task/state-machine/actions.js.map +1 -0
- package/dist/services/task/state-machine/constants.js +161 -0
- package/dist/services/task/state-machine/constants.js.map +1 -0
- package/dist/services/task/state-machine/guards.js +379 -0
- package/dist/services/task/state-machine/guards.js.map +1 -0
- package/dist/services/task/state-machine/index.js +53 -0
- package/dist/services/task/state-machine/index.js.map +1 -0
- package/dist/services/task/state-machine/types.js +54 -0
- package/dist/services/task/state-machine/types.js.map +1 -0
- package/dist/services/task/state-machine/uiControlsComputer.js +703 -0
- package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
- package/dist/services/task/taskDataNormalizer.js +99 -0
- package/dist/services/task/taskDataNormalizer.js.map +1 -0
- package/dist/services/task/types.js +259 -5
- package/dist/services/task/types.js.map +1 -1
- package/dist/services/task/voice/Voice.js +1504 -0
- package/dist/services/task/voice/Voice.js.map +1 -0
- package/dist/services/task/voice/WebRTC.js +152 -0
- package/dist/services/task/voice/WebRTC.js.map +1 -0
- package/dist/services/task/voice/wxAppOfferObservability.js +145 -0
- package/dist/services/task/voice/wxAppOfferObservability.js.map +1 -0
- package/dist/services/task/voice/wxAppVoiceMethods.js +395 -0
- package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -0
- package/dist/services/wxAppDiagnosticLogging.js +159 -0
- package/dist/services/wxAppDiagnosticLogging.js.map +1 -0
- package/dist/services/wxAppTelephonyUtils.js +19 -0
- package/dist/services/wxAppTelephonyUtils.js.map +1 -0
- package/dist/types/cc.d.ts +196 -9
- package/dist/types/config.d.ts +13 -0
- package/dist/types/constants.d.ts +38 -1
- package/dist/types/index.d.ts +54 -20
- package/dist/types/metrics/MetricsManager.d.ts +6 -0
- package/dist/types/metrics/constants.d.ts +52 -1
- package/dist/types/services/AddressBook.d.ts +2 -1
- package/dist/types/services/AnswerCallOnWebexService.d.ts +38 -0
- package/dist/types/services/ApiAiAssistant.d.ts +58 -5
- package/dist/types/services/EntryPoint.d.ts +7 -5
- package/dist/types/services/Queue.d.ts +5 -3
- package/dist/types/services/UserPreference.d.ts +118 -0
- package/dist/types/services/WebexCrossClientService.d.ts +32 -0
- package/dist/types/services/WxAppTelephonyMercurySync.d.ts +28 -0
- package/dist/types/services/config/Util.d.ts +4 -8
- package/dist/types/services/config/constants.d.ts +49 -76
- package/dist/types/services/config/index.d.ts +16 -64
- package/dist/types/services/config/types.d.ts +248 -114
- package/dist/types/services/core/Err.d.ts +6 -0
- package/dist/types/services/core/Utils.d.ts +35 -13
- package/dist/types/services/core/WebexRequest.d.ts +1 -0
- package/dist/types/services/core/types.d.ts +2 -0
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
- package/dist/types/services/core/websocket/types.d.ts +1 -1
- package/dist/types/services/index.d.ts +1 -1
- package/dist/types/services/task/Task.d.ts +186 -0
- package/dist/types/services/task/TaskFactory.d.ts +14 -0
- package/dist/types/services/task/TaskUtils.d.ts +54 -2
- package/dist/types/services/task/WebexCallingUtils.d.ts +11 -0
- package/dist/types/services/task/constants.d.ts +19 -0
- package/dist/types/services/task/contact.d.ts +4 -0
- package/dist/types/services/task/dialer.d.ts +30 -0
- package/dist/types/services/task/digital/Digital.d.ts +23 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1442 -0
- package/dist/types/services/task/state-machine/actions.d.ts +10 -0
- package/dist/types/services/task/state-machine/constants.d.ts +107 -0
- package/dist/types/services/task/state-machine/guards.d.ts +103 -0
- package/dist/types/services/task/state-machine/index.d.ts +13 -0
- package/dist/types/services/task/state-machine/types.d.ts +277 -0
- package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
- package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
- package/dist/types/services/task/types.d.ts +790 -66
- package/dist/types/services/task/voice/Voice.d.ts +241 -0
- package/dist/types/services/task/voice/WebRTC.d.ts +54 -0
- package/dist/types/services/task/voice/wxAppOfferObservability.d.ts +39 -0
- package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +68 -0
- package/dist/types/services/wxAppDiagnosticLogging.d.ts +63 -0
- package/dist/types/services/wxAppTelephonyUtils.d.ts +5 -0
- package/dist/types/types.d.ts +213 -11
- package/dist/types/utils/PageCache.d.ts +20 -3
- package/dist/types/webex.d.ts +1 -0
- package/dist/types.js +168 -6
- package/dist/types.js.map +1 -1
- package/dist/utils/PageCache.js +19 -5
- package/dist/utils/PageCache.js.map +1 -1
- package/dist/webex.js +22 -2
- package/dist/webex.js.map +1 -1
- package/package.json +16 -12
- package/src/cc.ts +1338 -121
- package/src/config.ts +13 -0
- package/src/constants.ts +41 -1
- package/src/index.ts +62 -15
- package/src/metrics/MetricsManager.ts +16 -0
- package/src/metrics/ai-docs/AGENTS.md +350 -0
- package/src/metrics/ai-docs/ARCHITECTURE.md +338 -0
- package/src/metrics/ai-docs/metrics-spec.md +971 -0
- package/src/metrics/behavioral-events.ts +218 -0
- package/src/metrics/constants.ts +63 -4
- package/src/services/AddressBook.ts +17 -6
- package/src/services/AnswerCallOnWebexService.ts +239 -0
- package/src/services/ApiAiAssistant.ts +470 -15
- package/src/services/EntryPoint.ts +59 -60
- package/src/services/Queue.ts +29 -12
- package/src/services/UserPreference.ts +509 -0
- package/src/services/WebexCrossClientService.ts +298 -0
- package/src/services/WxAppTelephonyMercurySync.ts +142 -0
- package/src/services/agent/ai-docs/AGENTS.md +240 -0
- package/src/services/agent/ai-docs/ARCHITECTURE.md +304 -0
- package/src/services/agent/ai-docs/agent-spec.md +505 -0
- package/src/services/ai-docs/AGENTS.md +386 -0
- package/src/services/ai-docs/services-spec.md +505 -0
- package/src/services/config/Util.ts +54 -84
- package/src/services/config/ai-docs/AGENTS.md +250 -0
- package/src/services/config/ai-docs/ARCHITECTURE.md +413 -0
- package/src/services/config/ai-docs/config-spec.md +669 -0
- package/src/services/config/constants.ts +63 -85
- package/src/services/config/index.ts +116 -277
- package/src/services/config/types.ts +233 -125
- package/src/services/core/Err.ts +3 -0
- package/src/services/core/Utils.ts +127 -37
- package/src/services/core/WebexRequest.ts +3 -1
- package/src/services/core/ai-docs/AGENTS.md +381 -0
- package/src/services/core/ai-docs/ARCHITECTURE.md +698 -0
- package/src/services/core/ai-docs/core-spec.md +787 -0
- package/src/services/core/aqm-reqs.ts +30 -15
- package/src/services/core/types.ts +33 -1
- package/src/services/core/websocket/WebSocketManager.ts +2 -0
- package/src/services/core/websocket/types.ts +1 -1
- package/src/services/index.ts +1 -1
- package/src/services/task/Task.ts +1211 -0
- package/src/services/task/TaskFactory.ts +82 -0
- package/src/services/task/TaskManager.ts +1584 -643
- package/src/services/task/TaskUtils.ts +316 -25
- package/src/services/task/WebexCallingUtils.ts +136 -0
- package/src/services/task/ai-docs/AGENTS.md +457 -0
- package/src/services/task/ai-docs/ARCHITECTURE.md +595 -0
- package/src/services/task/ai-docs/task-spec.md +1501 -0
- package/src/services/task/constants.ts +20 -0
- package/src/services/task/contact.ts +30 -0
- package/src/services/task/dialer.ts +80 -0
- package/src/services/task/digital/Digital.ts +102 -0
- package/src/services/task/state-machine/TaskStateMachine.ts +1320 -0
- package/src/services/task/state-machine/actions.ts +741 -0
- package/src/services/task/state-machine/ai-docs/AGENTS.md +460 -0
- package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1156 -0
- package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2247 -0
- package/src/services/task/state-machine/constants.ts +172 -0
- package/src/services/task/state-machine/guards.ts +442 -0
- package/src/services/task/state-machine/index.ts +28 -0
- package/src/services/task/state-machine/types.ts +258 -0
- package/src/services/task/state-machine/uiControlsComputer.ts +1135 -0
- package/src/services/task/taskDataNormalizer.ts +137 -0
- package/src/services/task/types.ts +974 -70
- package/src/services/task/voice/Voice.ts +1878 -0
- package/src/services/task/voice/WebRTC.ts +194 -0
- package/src/services/task/voice/wxAppOfferObservability.ts +204 -0
- package/src/services/task/voice/wxAppVoiceMethods.ts +575 -0
- package/src/services/wxAppDiagnosticLogging.ts +286 -0
- package/src/services/wxAppTelephonyUtils.ts +14 -0
- package/src/types.ts +272 -13
- package/src/utils/AGENTS.md +289 -0
- package/src/utils/PageCache.ts +38 -5
- package/src/utils/ai-docs/utils-spec.md +391 -0
- package/src/webex.js +10 -0
- package/test/unit/fixtures/aiSummaryTestUtils.ts +40 -0
- package/test/unit/spec/cc.ts +3705 -150
- package/test/unit/spec/index.ts +885 -0
- package/test/unit/spec/logger-proxy.ts +70 -0
- package/test/unit/spec/metrics/MetricsManager.ts +25 -0
- package/test/unit/spec/metrics/behavioral-events.ts +60 -0
- package/test/unit/spec/services/AddressBook.ts +37 -6
- package/test/unit/spec/services/AnswerCallOnWebexService.ts +264 -0
- package/test/unit/spec/services/ApiAiAssistant.ts +429 -23
- package/test/unit/spec/services/EntryPoint.ts +87 -40
- package/test/unit/spec/services/Queue.ts +123 -12
- package/test/unit/spec/services/UserPreference.ts +401 -0
- package/test/unit/spec/services/WebCallingService.ts +7 -1
- package/test/unit/spec/services/WebexCrossClientService.ts +509 -0
- package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +136 -0
- package/test/unit/spec/services/config/Util.ts +501 -0
- package/test/unit/spec/services/config/index.ts +317 -547
- package/test/unit/spec/services/core/Utils.ts +425 -8
- package/test/unit/spec/services/core/WebexRequest.ts +8 -7
- package/test/unit/spec/services/core/aqm-reqs.ts +113 -1
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +66 -40
- package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
- package/test/unit/spec/services/task/Task.ts +2723 -0
- package/test/unit/spec/services/task/TaskFactory.ts +116 -0
- package/test/unit/spec/services/task/TaskManager.ts +4781 -1585
- package/test/unit/spec/services/task/TaskUtils.ts +307 -0
- package/test/unit/spec/services/task/WebexCallingUtils.ts +153 -0
- package/test/unit/spec/services/task/contact.ts +33 -0
- package/test/unit/spec/services/task/dialer.ts +190 -0
- package/test/unit/spec/services/task/digital/Digital.ts +105 -0
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +4060 -0
- package/test/unit/spec/services/task/state-machine/guards.ts +839 -0
- package/test/unit/spec/services/task/state-machine/types.ts +18 -0
- package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +3101 -0
- package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
- package/test/unit/spec/services/task/voice/Voice.ts +2355 -0
- package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
- package/test/unit/spec/services/task/voice/wxAppOfferObservability.ts +361 -0
- package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +719 -0
- package/test/unit/spec/services/wxAppDiagnosticLogging.ts +179 -0
- package/test/unit/spec/wellnessSampleUtils.ts +288 -0
- package/typedoc.md +1 -1
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
- package/dist/services/task/index.js +0 -1530
- package/dist/services/task/index.js.map +0 -1
- package/dist/types/services/task/index.d.ts +0 -650
- package/src/services/task/index.ts +0 -1806
- package/test/unit/spec/services/task/index.ts +0 -2205
package/dist/cc.js
CHANGED
|
@@ -19,6 +19,10 @@ var _Utils = require("./services/core/Utils");
|
|
|
19
19
|
var _types3 = require("./services/config/types");
|
|
20
20
|
var _TaskManager = _interopRequireDefault(require("./services/task/TaskManager"));
|
|
21
21
|
var _WebCallingService = _interopRequireDefault(require("./services/WebCallingService"));
|
|
22
|
+
var _AnswerCallOnWebexService = _interopRequireDefault(require("./services/AnswerCallOnWebexService"));
|
|
23
|
+
var _WebexCrossClientService = _interopRequireDefault(require("./services/WebexCrossClientService"));
|
|
24
|
+
var _WxAppTelephonyMercurySync = _interopRequireDefault(require("./services/WxAppTelephonyMercurySync"));
|
|
25
|
+
var _wxAppDiagnosticLogging = require("./services/wxAppDiagnosticLogging");
|
|
22
26
|
var _types4 = require("./services/task/types");
|
|
23
27
|
var _MetricsManager = _interopRequireDefault(require("./metrics/MetricsManager"));
|
|
24
28
|
var _constants4 = require("./metrics/constants");
|
|
@@ -26,6 +30,7 @@ var _EntryPoint = require("./services/EntryPoint");
|
|
|
26
30
|
var _AddressBook = require("./services/AddressBook");
|
|
27
31
|
var _Queue = require("./services/Queue");
|
|
28
32
|
var _ApiAiAssistant = require("./services/ApiAiAssistant");
|
|
33
|
+
var _UserPreference = require("./services/UserPreference");
|
|
29
34
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
30
35
|
/**
|
|
31
36
|
* @module CCPlugin
|
|
@@ -118,7 +123,7 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
118
123
|
*
|
|
119
124
|
* // Login with browser-based calling
|
|
120
125
|
* await cc.stationLogin({
|
|
121
|
-
* teamId: profile.teams[0].
|
|
126
|
+
* teamId: profile.teams[0].id,
|
|
122
127
|
* loginOption: 'BROWSER'
|
|
123
128
|
* });
|
|
124
129
|
*
|
|
@@ -184,6 +189,29 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
184
189
|
* @private
|
|
185
190
|
*/
|
|
186
191
|
|
|
192
|
+
/**
|
|
193
|
+
* REST telephony for wxApp thick-client answer (WXCC-6026)
|
|
194
|
+
* @private
|
|
195
|
+
*/
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* usersub cross-client state for Webex App toast suppression
|
|
199
|
+
* @private
|
|
200
|
+
*/
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Mercury telephony mute sync for wxApp thick-client answer
|
|
204
|
+
* @private
|
|
205
|
+
*/
|
|
206
|
+
|
|
207
|
+
/** True when wxApp mute sync opened Mercury (Extension/DN-only profiles). */
|
|
208
|
+
wxAppMercuryConnectedByCc = false;
|
|
209
|
+
|
|
210
|
+
/** True when wxApp mute sync registered the Webex device. */
|
|
211
|
+
wxAppDeviceRegisteredByCc = false;
|
|
212
|
+
static WXAPP_FALSE_PUBLISH_RETRY_DELAY_MS = 30_000;
|
|
213
|
+
static WXAPP_FALSE_PUBLISH_MAX_RETRIES = 3;
|
|
214
|
+
|
|
187
215
|
/**
|
|
188
216
|
* Core service managers for Contact Center operations
|
|
189
217
|
* Includes agent, connection, and configuration services
|
|
@@ -212,6 +240,16 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
212
240
|
* @private
|
|
213
241
|
*/
|
|
214
242
|
|
|
243
|
+
/** Current login/relogin session used for agent-scoped wellness validation. */
|
|
244
|
+
|
|
245
|
+
/** Registered-session cache for the system WellbeingBreak idle code. */
|
|
246
|
+
|
|
247
|
+
/** Registration generation that owns the wellness idle-code cache and in-flight lookups. */
|
|
248
|
+
wellnessRegistrationGeneration = 0;
|
|
249
|
+
updateWellnessSession(agentSessionId) {
|
|
250
|
+
this.currentAgentSessionId = agentSessionId;
|
|
251
|
+
}
|
|
252
|
+
|
|
215
253
|
/**
|
|
216
254
|
* API instance for managing Webex Contact Center entry points
|
|
217
255
|
* Provides functionality to fetch entry points with caching support
|
|
@@ -274,6 +312,36 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
274
312
|
* ```
|
|
275
313
|
*/
|
|
276
314
|
|
|
315
|
+
/**
|
|
316
|
+
* API instance for managing Webex Contact Center user preferences
|
|
317
|
+
* Provides functionality to get, create, update, and delete user preferences
|
|
318
|
+
* @type {UserPreference}
|
|
319
|
+
* @public
|
|
320
|
+
* @example
|
|
321
|
+
* ```typescript
|
|
322
|
+
* const cc = webex.cc;
|
|
323
|
+
* await cc.register();
|
|
324
|
+
* await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' });
|
|
325
|
+
*
|
|
326
|
+
* // Get user preferences for current user
|
|
327
|
+
* const preferences = await cc.userPreference.getUserPreference();
|
|
328
|
+
*
|
|
329
|
+
* // Create new user preferences
|
|
330
|
+
* const newPrefs = await cc.userPreference.createUserPreference({
|
|
331
|
+
* userId: 'user123',
|
|
332
|
+
* preferences: { e911Reminder: true }
|
|
333
|
+
* });
|
|
334
|
+
*
|
|
335
|
+
* // Update user preferences
|
|
336
|
+
* const updatedPrefs = await cc.userPreference.updateUserPreference('user123', {
|
|
337
|
+
* preferences: { e911Reminder: false }
|
|
338
|
+
* });
|
|
339
|
+
*
|
|
340
|
+
* // Delete user preferences
|
|
341
|
+
* await cc.userPreference.deleteUserPreference('user123');
|
|
342
|
+
* ```
|
|
343
|
+
*/
|
|
344
|
+
|
|
277
345
|
/**
|
|
278
346
|
* API instance for AI Assistant operations such as transcript controls.
|
|
279
347
|
* @type {ApiAIAssistant}
|
|
@@ -301,6 +369,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
301
369
|
this.$webex.once(_constants.READY, () => {
|
|
302
370
|
// @ts-ignore
|
|
303
371
|
this.$config = this.config;
|
|
372
|
+
this.validatePluginConfig();
|
|
304
373
|
|
|
305
374
|
/**
|
|
306
375
|
* This is used for handling the async requests by sending webex.request and wait for corresponding websocket event.
|
|
@@ -312,13 +381,20 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
312
381
|
webex: this.$webex,
|
|
313
382
|
connectionConfig: this.getConnectionConfig()
|
|
314
383
|
});
|
|
315
|
-
this.services.webSocketManager.on('message', this.handleWebsocketMessage);
|
|
316
384
|
this.webCallingService = new _WebCallingService.default(this.$webex);
|
|
317
|
-
this.
|
|
385
|
+
this.answerCallOnWebexService = new _AnswerCallOnWebexService.default(this.$webex);
|
|
386
|
+
this.webexCrossClientService = new _WebexCrossClientService.default(this.$webex);
|
|
387
|
+
this.wxAppTelephonyMercurySync = new _WxAppTelephonyMercurySync.default(this.$webex);
|
|
388
|
+
this.apiAIAssistant = new _ApiAiAssistant.ApiAIAssistant(this.$webex, () => ({
|
|
389
|
+
isWellnessBreakEnabled: this.agentConfig?.isWellnessBreakEnabled === true,
|
|
390
|
+
agentId: this.agentConfig?.agentId,
|
|
391
|
+
agentSessionId: this.currentAgentSessionId
|
|
392
|
+
}));
|
|
318
393
|
this.metricsManager = _MetricsManager.default.getInstance({
|
|
319
394
|
webex: this.$webex
|
|
320
395
|
});
|
|
321
|
-
this.taskManager = _TaskManager.default.getTaskManager(this.apiAIAssistant, this.services.contact, this.webCallingService, this.services.webSocketManager);
|
|
396
|
+
this.taskManager = _TaskManager.default.getTaskManager(this.apiAIAssistant, this.services.contact, this.webCallingService, this.services.webSocketManager, this.services.rtdWebSocketManager);
|
|
397
|
+
this.taskManager.setAnswerCallOnWebexService(this.answerCallOnWebexService);
|
|
322
398
|
this.incomingTaskListener();
|
|
323
399
|
|
|
324
400
|
// Initialize API instances
|
|
@@ -326,6 +402,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
326
402
|
this.entryPoint = new _EntryPoint.EntryPoint(this.$webex);
|
|
327
403
|
this.addressBook = new _AddressBook.AddressBook(this.$webex, () => this.agentConfig?.addressBookId);
|
|
328
404
|
this.queue = new _Queue.Queue(this.$webex);
|
|
405
|
+
this.userPreference = new _UserPreference.UserPreference(this.$webex, () => this.agentConfig?.agentId);
|
|
329
406
|
_loggerProxy.default.initialize(this.$webex.logger);
|
|
330
407
|
});
|
|
331
408
|
}
|
|
@@ -350,6 +427,16 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
350
427
|
this.trigger(_types4.TASK_EVENTS.TASK_HYDRATE, task);
|
|
351
428
|
};
|
|
352
429
|
|
|
430
|
+
/**
|
|
431
|
+
* Handles multi-login hydrate events for SDK instances without Mobius registration
|
|
432
|
+
* @private
|
|
433
|
+
* @param {ITask} task The task object associated with the multi-login hydrate
|
|
434
|
+
*/
|
|
435
|
+
handleTaskMultiLoginHydrate = task => {
|
|
436
|
+
// @ts-ignore
|
|
437
|
+
this.trigger(_types4.TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, task);
|
|
438
|
+
};
|
|
439
|
+
|
|
353
440
|
/**
|
|
354
441
|
* Handles task merged events when tasks are combined eg: EPDN merge/transfer
|
|
355
442
|
* @private
|
|
@@ -369,8 +456,83 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
369
456
|
// @ts-ignore
|
|
370
457
|
this.trigger(_types4.TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, task);
|
|
371
458
|
};
|
|
372
|
-
|
|
373
|
-
this.
|
|
459
|
+
trackInvalidWellnessEvent(reason) {
|
|
460
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.AI_ASSISTANT_WELLNESS_EVENT_INVALID, {
|
|
461
|
+
reason
|
|
462
|
+
}, ['operational']);
|
|
463
|
+
}
|
|
464
|
+
normalizeWellnessBreakEvent(payload) {
|
|
465
|
+
if (this.agentConfig?.isWellnessBreakEnabled !== true) {
|
|
466
|
+
this.trackInvalidWellnessEvent('wellness_disabled');
|
|
467
|
+
return null;
|
|
468
|
+
}
|
|
469
|
+
const notificationData = (0, _Utils.isRecord)(payload.data) ? payload.data : null;
|
|
470
|
+
const notificationDetails = notificationData && (0, _Utils.isRecord)(notificationData.notifDetails) ? notificationData.notifDetails : null;
|
|
471
|
+
const sessionData = notificationData && (0, _Utils.isRecord)(notificationData.data) ? notificationData.data : null;
|
|
472
|
+
const actionEvent = notificationDetails?.actionEvent;
|
|
473
|
+
if (payload.type !== _constants.WELLNESS_BREAK_HANDLER || notificationData?.notifType !== _constants.WELLNESS_BREAK_HANDLER || typeof actionEvent !== 'string') {
|
|
474
|
+
this.trackInvalidWellnessEvent('malformed_wellness_event');
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
if (!Object.values(_types.WELLNESS_BREAK_NOTIFICATION_ACTIONS).includes(actionEvent)) {
|
|
478
|
+
this.trackInvalidWellnessEvent('unknown_wellness_action');
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
const agentId = notificationData.agentId;
|
|
482
|
+
const eventOrgId = notificationData.orgId;
|
|
483
|
+
const sessionOrgId = sessionData?.orgId;
|
|
484
|
+
const envelopeOrgId = payload.orgId;
|
|
485
|
+
const agentSessionId = sessionData?.agentSessionId;
|
|
486
|
+
const currentOrgId = this.$webex.credentials.getOrgId();
|
|
487
|
+
if (typeof agentId !== 'string' || typeof eventOrgId !== 'string' || typeof sessionOrgId !== 'string' || typeof envelopeOrgId !== 'string' || typeof agentSessionId !== 'string' || !agentId || !eventOrgId || !sessionOrgId || !envelopeOrgId || !agentSessionId) {
|
|
488
|
+
this.trackInvalidWellnessEvent('missing_wellness_identity');
|
|
489
|
+
return null;
|
|
490
|
+
}
|
|
491
|
+
if (agentId !== this.agentConfig?.agentId || eventOrgId !== currentOrgId || sessionOrgId !== currentOrgId || envelopeOrgId !== currentOrgId) {
|
|
492
|
+
this.trackInvalidWellnessEvent('mismatched_wellness_identity');
|
|
493
|
+
return null;
|
|
494
|
+
}
|
|
495
|
+
const interactionValue = sessionData.interactionId ?? sessionData.InteractionId;
|
|
496
|
+
const actionText = notificationDetails.actionText;
|
|
497
|
+
const trackingId = payload.trackingId;
|
|
498
|
+
return {
|
|
499
|
+
agentId,
|
|
500
|
+
orgId: currentOrgId,
|
|
501
|
+
agentSessionId,
|
|
502
|
+
actionEvent: actionEvent,
|
|
503
|
+
...(typeof actionText === 'string' ? {
|
|
504
|
+
actionText
|
|
505
|
+
} : {}),
|
|
506
|
+
...(typeof interactionValue === 'string' ? {
|
|
507
|
+
interactionId: interactionValue
|
|
508
|
+
} : {}),
|
|
509
|
+
...(typeof trackingId === 'string' ? {
|
|
510
|
+
trackingId
|
|
511
|
+
} : {})
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
handleWellnessBreakWebsocketPayload(payload) {
|
|
515
|
+
if (payload.type !== _constants.WELLNESS_BREAK_HANDLER) {
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
const wellnessEvent = this.normalizeWellnessBreakEvent(payload);
|
|
519
|
+
if (wellnessEvent) {
|
|
520
|
+
// @ts-ignore - WebexPlugin emit is available at runtime but absent from its declaration.
|
|
521
|
+
this.emit(_types3.CC_EVENTS.WELLNESS_BREAK, wellnessEvent);
|
|
522
|
+
}
|
|
523
|
+
return true;
|
|
524
|
+
}
|
|
525
|
+
handleRTDWebsocketMessage = event => {
|
|
526
|
+
let payload;
|
|
527
|
+
try {
|
|
528
|
+
payload = JSON.parse(event);
|
|
529
|
+
} catch {
|
|
530
|
+
// TaskManager retains its existing malformed RTD message handling.
|
|
531
|
+
}
|
|
532
|
+
if ((0, _Utils.isRecord)(payload) && this.handleWellnessBreakWebsocketPayload(payload)) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
this.taskManager.handleRealtimeWebsocketEvent(event);
|
|
374
536
|
};
|
|
375
537
|
|
|
376
538
|
/**
|
|
@@ -381,6 +543,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
381
543
|
incomingTaskListener() {
|
|
382
544
|
this.taskManager.on(_types4.TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
383
545
|
this.taskManager.on(_types4.TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
546
|
+
this.taskManager.on(_types4.TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
|
|
384
547
|
this.taskManager.on(_types4.TASK_EVENTS.TASK_MERGED, this.handleTaskMerged);
|
|
385
548
|
this.taskManager.on(_types4.TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, this.handleCampaignPreviewReservation);
|
|
386
549
|
}
|
|
@@ -413,22 +576,25 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
413
576
|
*
|
|
414
577
|
* console.log('Agent ID:', profile.agentId);
|
|
415
578
|
* console.log('Default DN:', profile.defaultDn);
|
|
416
|
-
* console.log('Teams:', profile.teams.map(t => t.
|
|
579
|
+
* console.log('Teams:', profile.teams.map(t => t.id));
|
|
417
580
|
* console.log('WebRTC Enabled:', profile.webRtcEnabled);
|
|
418
581
|
* console.log('Supported Login Options:', profile.loginVoiceOptions);
|
|
419
582
|
*
|
|
420
583
|
* // Now you can proceed with station login, state changes, etc.
|
|
421
|
-
* await cc.stationLogin({ teamId: profile.teams[0].
|
|
584
|
+
* await cc.stationLogin({ teamId: profile.teams[0].id, loginOption: 'BROWSER' });
|
|
422
585
|
* ```
|
|
423
586
|
*/
|
|
424
587
|
async register() {
|
|
425
|
-
|
|
588
|
+
this.wellnessRegistrationGeneration += 1;
|
|
589
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
590
|
+
_loggerProxy.default.log('Starting CC SDK registration', {
|
|
426
591
|
module: _constants.CC_FILE,
|
|
427
592
|
method: _constants.METHODS.REGISTER
|
|
428
593
|
});
|
|
429
594
|
try {
|
|
430
595
|
this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_SUCCESS, _constants4.METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_FAILED]);
|
|
431
596
|
this.setupEventListeners();
|
|
597
|
+
this.services.webSocketManager.on('message', this.handleWebsocketMessage);
|
|
432
598
|
const resp = await this.connectWebsocket();
|
|
433
599
|
// Ensure 'dn' is always populated from 'defaultDn'
|
|
434
600
|
resp.dn = resp.defaultDn;
|
|
@@ -440,7 +606,14 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
440
606
|
module: _constants.CC_FILE,
|
|
441
607
|
method: _constants.METHODS.REGISTER
|
|
442
608
|
});
|
|
443
|
-
|
|
609
|
+
|
|
610
|
+
// Registration can silently restore a station session without emitting a
|
|
611
|
+
// relogin event. Project the active session for consumers that must
|
|
612
|
+
// recover session-scoped state after a page refresh.
|
|
613
|
+
return resp.isAgentLoggedIn && this.currentAgentSessionId ? {
|
|
614
|
+
...resp,
|
|
615
|
+
agentSessionId: this.currentAgentSessionId
|
|
616
|
+
} : resp;
|
|
444
617
|
} catch (error) {
|
|
445
618
|
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_FAILED, {
|
|
446
619
|
orgId: error.orgId
|
|
@@ -487,15 +660,25 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
487
660
|
*
|
|
488
661
|
*/
|
|
489
662
|
async deregister() {
|
|
663
|
+
this.wellnessRegistrationGeneration += 1;
|
|
664
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
665
|
+
this.updateWellnessSession();
|
|
490
666
|
try {
|
|
491
667
|
this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.WEBSOCKET_DEREGISTER_SUCCESS, _constants4.METRIC_EVENT_NAMES.WEBSOCKET_DEREGISTER_FAIL]);
|
|
668
|
+
this.taskManager.clearAISummaryState();
|
|
492
669
|
this.taskManager.off(_types4.TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
493
670
|
this.taskManager.off(_types4.TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
671
|
+
this.taskManager.off(_types4.TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
|
|
494
672
|
this.taskManager.off(_types4.TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, this.handleCampaignPreviewReservation);
|
|
495
673
|
this.taskManager.unregisterIncomingCallEvent();
|
|
496
674
|
this.services.webSocketManager.off('message', this.handleWebsocketMessage);
|
|
497
675
|
this.services.rtdWebSocketManager.off('message', this.handleRTDWebsocketMessage);
|
|
498
676
|
this.services.connectionService.off('connectionLost', this.handleConnectionLost);
|
|
677
|
+
const {
|
|
678
|
+
publishError: wxAppPublishError
|
|
679
|
+
} = await this.teardownWxAppLocalState({
|
|
680
|
+
clearInitFlag: true
|
|
681
|
+
});
|
|
499
682
|
if (this.agentConfig.webRtcEnabled && this.agentConfig.loginVoiceOptions.includes(_types.LoginOption.BROWSER)) {
|
|
500
683
|
if (this.$webex.internal.mercury.connected) {
|
|
501
684
|
this.$webex.internal.mercury.off('online');
|
|
@@ -512,12 +695,20 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
512
695
|
if (!this.services.webSocketManager.isSocketClosed) {
|
|
513
696
|
this.services.webSocketManager.close(false, 'Unregistering the SDK');
|
|
514
697
|
}
|
|
698
|
+
if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
|
|
699
|
+
this.services.rtdWebSocketManager.close(false, 'Unregistering the SDK');
|
|
700
|
+
}
|
|
515
701
|
if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
|
|
516
702
|
this.services.rtdWebSocketManager.close(false, 'Unregistering the RTD websocket');
|
|
517
703
|
}
|
|
518
704
|
|
|
519
705
|
// Clear any cached agent configuration
|
|
706
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
520
707
|
this.agentConfig = null;
|
|
708
|
+
this.updateWellnessSession();
|
|
709
|
+
if (wxAppPublishError) {
|
|
710
|
+
throw wxAppPublishError;
|
|
711
|
+
}
|
|
521
712
|
_loggerProxy.default.log('Deregistered successfully', {
|
|
522
713
|
module: _constants.CC_FILE,
|
|
523
714
|
method: _constants.METHODS.DEREGISTER
|
|
@@ -571,6 +762,12 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
571
762
|
* ```
|
|
572
763
|
*/
|
|
573
764
|
async getBuddyAgents(data) {
|
|
765
|
+
const {
|
|
766
|
+
action,
|
|
767
|
+
state,
|
|
768
|
+
mediaType = 'telephony'
|
|
769
|
+
} = data;
|
|
770
|
+
const buddyAgentState = state ?? (action === 'Transfer' ? _constants2.AGENT_STATE_AVAILABLE : undefined);
|
|
574
771
|
_loggerProxy.default.info('Fetching buddy agents', {
|
|
575
772
|
module: _constants.CC_FILE,
|
|
576
773
|
method: _constants.METHODS.GET_BUDDY_AGENTS
|
|
@@ -580,13 +777,16 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
580
777
|
const resp = await this.services.agent.buddyAgents({
|
|
581
778
|
data: {
|
|
582
779
|
agentProfileId: this.agentConfig.agentProfileID,
|
|
583
|
-
|
|
780
|
+
mediaType,
|
|
781
|
+
...(buddyAgentState ? {
|
|
782
|
+
state: buddyAgentState
|
|
783
|
+
} : {})
|
|
584
784
|
}
|
|
585
785
|
});
|
|
586
786
|
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_SUCCESS, {
|
|
587
787
|
..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(resp),
|
|
588
|
-
mediaType
|
|
589
|
-
buddyAgentState
|
|
788
|
+
mediaType,
|
|
789
|
+
buddyAgentState,
|
|
590
790
|
buddyAgentCount: resp.data.agentList.length
|
|
591
791
|
}, ['operational']);
|
|
592
792
|
_loggerProxy.default.log(`Successfully retrieved ${resp.data.agentList.length} buddy agents`, {
|
|
@@ -599,8 +799,8 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
599
799
|
const failureResp = error.details;
|
|
600
800
|
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_FAILED, {
|
|
601
801
|
..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(failureResp),
|
|
602
|
-
mediaType
|
|
603
|
-
buddyAgentState
|
|
802
|
+
mediaType,
|
|
803
|
+
buddyAgentState
|
|
604
804
|
}, ['operational']);
|
|
605
805
|
const {
|
|
606
806
|
error: detailedError
|
|
@@ -616,7 +816,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
616
816
|
* @private
|
|
617
817
|
*/
|
|
618
818
|
async connectWebsocket() {
|
|
619
|
-
_loggerProxy.default.
|
|
819
|
+
_loggerProxy.default.log('Connecting to websocket', {
|
|
620
820
|
module: _constants.CC_FILE,
|
|
621
821
|
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
622
822
|
});
|
|
@@ -632,18 +832,42 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
632
832
|
module: _constants.CC_FILE,
|
|
633
833
|
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
634
834
|
});
|
|
635
|
-
|
|
835
|
+
const configFlags = {
|
|
836
|
+
isEndTaskEnabled: this.agentConfig.isEndTaskEnabled,
|
|
837
|
+
isEndConsultEnabled: this.agentConfig.isEndConsultEnabled,
|
|
838
|
+
webRtcEnabled: this.agentConfig.webRtcEnabled,
|
|
839
|
+
autoWrapup: this.agentConfig.wrapUpData?.wrapUpProps?.autoWrapup ?? false,
|
|
840
|
+
aiFeature: this.agentConfig.aiFeature,
|
|
841
|
+
consultTransfer: {
|
|
842
|
+
allowConsultToQueue: this.agentConfig.allowConsultToQueue,
|
|
843
|
+
accessQueue: this.agentConfig.accessQueue,
|
|
844
|
+
accessEntryPoint: this.agentConfig.accessEntryPoint,
|
|
845
|
+
accessBuddyTeam: this.agentConfig.accessBuddyTeam
|
|
846
|
+
},
|
|
847
|
+
enableWxBetterTogether: this.isWxBetterTogetherEnabled(),
|
|
848
|
+
getWxAppUsersubPublished: () => this.webexCrossClientService.isAnswerCallsStateActive()
|
|
849
|
+
};
|
|
850
|
+
this.taskManager.setConfigFlags(configFlags);
|
|
636
851
|
// TODO: Make profile a singleton to make it available throughout app/sdk so we dont need to inject info everywhere
|
|
637
852
|
this.taskManager.setWrapupData(this.agentConfig.wrapUpData);
|
|
638
853
|
this.taskManager.setAgentId(this.agentConfig.agentId);
|
|
854
|
+
this.taskManager.setAgentName(this.agentConfig.agentName);
|
|
639
855
|
this.taskManager.setWebRtcEnabled(this.agentConfig.webRtcEnabled);
|
|
640
856
|
this.apiAIAssistant.setAIFeatureFlags(this.agentConfig.aiFeature);
|
|
641
|
-
|
|
857
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
858
|
+
this.updateWellnessSession();
|
|
859
|
+
|
|
860
|
+
/**
|
|
861
|
+
* RTD websocket currently supports realtime transcripts and suggested responses.
|
|
862
|
+
* Extend this condition when additional AI RTD features are introduced.
|
|
863
|
+
* Wellness break notifications also arrive on this websocket.
|
|
864
|
+
*/
|
|
865
|
+
if (this.agentConfig.aiFeature?.realtimeTranscripts?.enable || this.agentConfig.aiFeature?.suggestedResponses?.enable || this.agentConfig.aiFeature?.generatedSummaries?.wrapUpSummariesEnabled === true || this.agentConfig.aiFeature?.generatedSummaries?.consultTransferSummariesEnabled === true || this.agentConfig.isWellnessBreakEnabled) {
|
|
642
866
|
_loggerProxy.default.info('Connecting to RTD websocket', {
|
|
643
867
|
module: _constants.CC_FILE,
|
|
644
868
|
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
645
869
|
});
|
|
646
|
-
|
|
870
|
+
this.services.rtdWebSocketManager.initWebSocket({
|
|
647
871
|
body: this.getConnectionConfig(),
|
|
648
872
|
resource: _constants3.RTD_SUBSCRIBE_API
|
|
649
873
|
}).then(() => {
|
|
@@ -653,13 +877,13 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
653
877
|
});
|
|
654
878
|
this.services.rtdWebSocketManager.on('message', this.handleRTDWebsocketMessage);
|
|
655
879
|
}).catch(error => {
|
|
656
|
-
_loggerProxy.default.error(`Error
|
|
880
|
+
_loggerProxy.default.error(`Error connecting to RTD websocket ${error}`, {
|
|
657
881
|
module: _constants.CC_FILE,
|
|
658
882
|
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
659
883
|
});
|
|
660
884
|
});
|
|
661
885
|
}
|
|
662
|
-
if (this.agentConfig.webRtcEnabled && this.agentConfig.loginVoiceOptions.includes(_types.LoginOption.BROWSER)) {
|
|
886
|
+
if (this.agentConfig.webRtcEnabled && this.agentConfig.loginVoiceOptions.includes(_types.LoginOption.BROWSER) && !this.isWebRTCRegistrationDisabled()) {
|
|
663
887
|
try {
|
|
664
888
|
await this.$webex.internal.mercury.connect();
|
|
665
889
|
_loggerProxy.default.log('Authentication: webex.internal.mercury.connect successful', {
|
|
@@ -672,12 +896,23 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
672
896
|
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
673
897
|
});
|
|
674
898
|
}
|
|
899
|
+
} else if (this.isWebRTCRegistrationDisabled()) {
|
|
900
|
+
_loggerProxy.default.info('Skipping Mobius registration because disableWebRTCRegistration is enabled', {
|
|
901
|
+
module: _constants.CC_FILE,
|
|
902
|
+
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
903
|
+
});
|
|
675
904
|
}
|
|
676
905
|
if (this.$config && this.$config.allowAutomatedRelogin) {
|
|
677
906
|
await this.silentRelogin();
|
|
907
|
+
} else {
|
|
908
|
+
_loggerProxy.default.log('Skipping silent relogin: allowAutomatedRelogin is disabled', {
|
|
909
|
+
module: _constants.CC_FILE,
|
|
910
|
+
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
911
|
+
});
|
|
678
912
|
}
|
|
679
913
|
return this.agentConfig;
|
|
680
914
|
} catch (error) {
|
|
915
|
+
this.updateWellnessSession();
|
|
681
916
|
_loggerProxy.default.error(`Error during register: ${error}`, {
|
|
682
917
|
module: _constants.CC_FILE,
|
|
683
918
|
method: _constants.METHODS.CONNECT_WEBSOCKET
|
|
@@ -715,30 +950,37 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
715
950
|
* ```
|
|
716
951
|
*/
|
|
717
952
|
async stationLogin(data) {
|
|
718
|
-
|
|
953
|
+
const loggerContext = {
|
|
719
954
|
module: _constants.CC_FILE,
|
|
720
955
|
method: _constants.METHODS.STATION_LOGIN
|
|
721
|
-
}
|
|
956
|
+
};
|
|
722
957
|
try {
|
|
958
|
+
_loggerProxy.default.log(`Starting agent station login | loginOption: ${data?.loginOption} teamId: ${data?.teamId}`, loggerContext);
|
|
723
959
|
this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS, _constants4.METRIC_EVENT_NAMES.STATION_LOGIN_FAILED]);
|
|
724
960
|
const dialPlanEntries = this.agentConfig?.dialPlan?.dialPlanEntity ?? [];
|
|
725
|
-
if (data.loginOption === _types.LoginOption.AGENT_DN
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
961
|
+
if (data.loginOption === _types.LoginOption.AGENT_DN) {
|
|
962
|
+
_loggerProxy.default.log(`Validating dial number | dialPlanEnabled: ${!!this.agentConfig?.dialPlan} | dialPlanEntryCount: ${dialPlanEntries.length}`, loggerContext);
|
|
963
|
+
if (!(0, _Utils.isValidDialNumber)(data.dialNumber, dialPlanEntries)) {
|
|
964
|
+
_loggerProxy.default.log(`Dial number validation failed | dialNumber: ${data.dialNumber} | dialPlanEntryCount: ${dialPlanEntries.length}`, loggerContext);
|
|
965
|
+
const error = new Error('INVALID_DIAL_NUMBER');
|
|
966
|
+
// @ts-ignore - adding custom key to the error object
|
|
967
|
+
error.details = {
|
|
968
|
+
data: {
|
|
969
|
+
reason: 'INVALID_DIAL_NUMBER'
|
|
970
|
+
}
|
|
971
|
+
};
|
|
972
|
+
throw error;
|
|
973
|
+
}
|
|
734
974
|
}
|
|
975
|
+
const deviceId = this.getDeviceId(data.loginOption, data.dialNumber);
|
|
735
976
|
const loginResponse = await this.services.agent.stationLogin({
|
|
736
977
|
data: {
|
|
737
|
-
|
|
978
|
+
// BROWSER DN must be webrtc-{agentId} so VPOP/RTMS do not treat a bare UUID as PSTN.
|
|
979
|
+
dialNumber: data.loginOption === _types.LoginOption.BROWSER ? deviceId : data.dialNumber,
|
|
738
980
|
teamId: data.teamId,
|
|
739
981
|
deviceType: data.loginOption,
|
|
740
982
|
isExtension: data.loginOption === _types.LoginOption.EXTENSION,
|
|
741
|
-
deviceId
|
|
983
|
+
deviceId,
|
|
742
984
|
roles: [_constants3.AGENT],
|
|
743
985
|
teamName: _constants.EMPTY_STRING,
|
|
744
986
|
siteId: _constants.EMPTY_STRING,
|
|
@@ -746,14 +988,20 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
746
988
|
auxCodeId: _constants.EMPTY_STRING
|
|
747
989
|
}
|
|
748
990
|
});
|
|
749
|
-
if (this.agentConfig.webRtcEnabled && data.loginOption === _types.LoginOption.BROWSER) {
|
|
991
|
+
if (this.agentConfig.webRtcEnabled && data.loginOption === _types.LoginOption.BROWSER && !this.isWebRTCRegistrationDisabled()) {
|
|
750
992
|
await this.webCallingService.registerWebCallingLine();
|
|
993
|
+
} else if (this.agentConfig.webRtcEnabled && data.loginOption === _types.LoginOption.BROWSER && this.isWebRTCRegistrationDisabled()) {
|
|
994
|
+
_loggerProxy.default.info('Skipping web calling line registration because disableWebRTCRegistration is enabled', {
|
|
995
|
+
module: _constants.CC_FILE,
|
|
996
|
+
method: _constants.METHODS.STATION_LOGIN
|
|
997
|
+
});
|
|
751
998
|
}
|
|
752
999
|
const resp = await loginResponse;
|
|
753
1000
|
const {
|
|
754
1001
|
channelsMap,
|
|
755
1002
|
...loginData
|
|
756
1003
|
} = resp.data;
|
|
1004
|
+
this.updateWellnessSession(resp.data.agentSessionId);
|
|
757
1005
|
this.agentConfig.currentTeamId = resp.data.teamId;
|
|
758
1006
|
const response = {
|
|
759
1007
|
...loginData,
|
|
@@ -766,6 +1014,13 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
766
1014
|
notifsTrackingId: resp.trackingId
|
|
767
1015
|
};
|
|
768
1016
|
this.webCallingService.setLoginOption(data.loginOption);
|
|
1017
|
+
if (this.agentConfig) {
|
|
1018
|
+
this.agentConfig.deviceType = data.loginOption;
|
|
1019
|
+
if (data.loginOption === _types.LoginOption.AGENT_DN || data.loginOption === _types.LoginOption.EXTENSION) {
|
|
1020
|
+
this.agentConfig.defaultDn = data.dialNumber;
|
|
1021
|
+
this.agentConfig.dn = data.dialNumber;
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
769
1024
|
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS, {
|
|
770
1025
|
..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(resp),
|
|
771
1026
|
loginType: data.loginOption,
|
|
@@ -778,6 +1033,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
778
1033
|
method: _constants.METHODS.STATION_LOGIN,
|
|
779
1034
|
trackingId: resp.trackingId
|
|
780
1035
|
});
|
|
1036
|
+
await this.ensureWxAppPostStationLogin();
|
|
781
1037
|
return response;
|
|
782
1038
|
} catch (error) {
|
|
783
1039
|
const failure = error.details;
|
|
@@ -832,6 +1088,8 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
832
1088
|
if (this.webCallingService) {
|
|
833
1089
|
this.webCallingService.deregisterWebCallingLine();
|
|
834
1090
|
}
|
|
1091
|
+
await this.teardownWxAppLocalState();
|
|
1092
|
+
this.updateWellnessSession();
|
|
835
1093
|
_loggerProxy.default.log(`Agent station logout completed successfully`, {
|
|
836
1094
|
module: _constants.CC_FILE,
|
|
837
1095
|
method: _constants.METHODS.STATION_LOGOUT,
|
|
@@ -925,7 +1183,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
925
1183
|
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.AGENT_STATE_CHANGE_SUCCESS, {
|
|
926
1184
|
..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(agentStatusResponse),
|
|
927
1185
|
requestedState: data.state,
|
|
928
|
-
teamId: this.agentConfig?.teams[0]?.
|
|
1186
|
+
teamId: this.agentConfig?.teams[0]?.id ?? _constants.EMPTY_STRING,
|
|
929
1187
|
status: agentStatusResponse.data?.status,
|
|
930
1188
|
subStatus: agentStatusResponse.data?.subStatus,
|
|
931
1189
|
auxCodeId: data.auxCodeId,
|
|
@@ -952,6 +1210,63 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
952
1210
|
}
|
|
953
1211
|
}
|
|
954
1212
|
|
|
1213
|
+
/**
|
|
1214
|
+
* Returns the system-managed `WellbeingBreak` idle code for the current registration.
|
|
1215
|
+
* The value is fetched outside Desktop Profile filtering and cached until deregistration.
|
|
1216
|
+
* @returns The normalized system idle-code entity
|
|
1217
|
+
* @throws Structured Contact Center error when wellness is disabled or the code is unavailable
|
|
1218
|
+
* @example
|
|
1219
|
+
* const idleCode = await webex.cc.getWellbeingBreakIdleCode();
|
|
1220
|
+
* @public
|
|
1221
|
+
*/
|
|
1222
|
+
async getWellbeingBreakIdleCode() {
|
|
1223
|
+
const method = _constants.METHODS.GET_WELLBEING_BREAK_IDLE_CODE;
|
|
1224
|
+
try {
|
|
1225
|
+
if (!this.agentConfig?.isWellnessBreakEnabled) {
|
|
1226
|
+
const validationError = new Error('WELLNESS_BREAK_NOT_ENABLED');
|
|
1227
|
+
validationError.details = {
|
|
1228
|
+
type: 'SDK_VALIDATION_ERROR',
|
|
1229
|
+
orgId: this.$webex.credentials.getOrgId(),
|
|
1230
|
+
trackingId: _constants.EMPTY_STRING,
|
|
1231
|
+
data: {
|
|
1232
|
+
reason: 'WELLNESS_BREAK_NOT_ENABLED'
|
|
1233
|
+
}
|
|
1234
|
+
};
|
|
1235
|
+
throw validationError;
|
|
1236
|
+
}
|
|
1237
|
+
if (this.wellbeingBreakIdleCode) {
|
|
1238
|
+
return this.wellbeingBreakIdleCode;
|
|
1239
|
+
}
|
|
1240
|
+
this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS, _constants4.METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED]);
|
|
1241
|
+
const registrationGeneration = this.wellnessRegistrationGeneration;
|
|
1242
|
+
const orgId = this.$webex.credentials.getOrgId();
|
|
1243
|
+
const idleCode = await this.services.config.getWellbeingBreakIdleCode(orgId);
|
|
1244
|
+
if (registrationGeneration !== this.wellnessRegistrationGeneration || orgId !== this.$webex.credentials.getOrgId() || !this.agentConfig?.isWellnessBreakEnabled) {
|
|
1245
|
+
const staleRegistrationError = new Error('WELLNESS_BREAK_REGISTRATION_CHANGED');
|
|
1246
|
+
staleRegistrationError.details = {
|
|
1247
|
+
type: 'SDK_VALIDATION_ERROR',
|
|
1248
|
+
orgId,
|
|
1249
|
+
trackingId: _constants.EMPTY_STRING,
|
|
1250
|
+
data: {
|
|
1251
|
+
reason: 'WELLNESS_BREAK_REGISTRATION_CHANGED'
|
|
1252
|
+
}
|
|
1253
|
+
};
|
|
1254
|
+
throw staleRegistrationError;
|
|
1255
|
+
}
|
|
1256
|
+
this.wellbeingBreakIdleCode = idleCode;
|
|
1257
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS, {}, ['operational']);
|
|
1258
|
+
return idleCode;
|
|
1259
|
+
} catch (error) {
|
|
1260
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED, {
|
|
1261
|
+
reason: error instanceof Error ? error.message : _constants.UNKNOWN_ERROR
|
|
1262
|
+
}, ['operational']);
|
|
1263
|
+
const {
|
|
1264
|
+
error: detailedError
|
|
1265
|
+
} = (0, _Utils.getErrorDetails)(error, method, _constants.CC_FILE);
|
|
1266
|
+
throw detailedError;
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
|
|
955
1270
|
/**
|
|
956
1271
|
* Processes incoming websocket messages and emits corresponding events
|
|
957
1272
|
* Handles various event types including agent state changes, login events,
|
|
@@ -985,8 +1300,14 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
985
1300
|
};
|
|
986
1301
|
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WEBSOCKET_EVENT_RECEIVED, metricsPayload, ['operational']);
|
|
987
1302
|
}
|
|
1303
|
+
if (this.handleWellnessBreakWebsocketPayload(eventData)) {
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
988
1306
|
switch (eventData.type) {
|
|
989
1307
|
case _types3.CC_EVENTS.AGENT_MULTI_LOGIN:
|
|
1308
|
+
if (eventData.data?.type === 'AgentMultiLoginCloseSession' && (!eventData.data.agentSessionId || eventData.data.agentSessionId === this.currentAgentSessionId)) {
|
|
1309
|
+
this.updateWellnessSession();
|
|
1310
|
+
}
|
|
990
1311
|
// @ts-ignore
|
|
991
1312
|
this.emit(_types2.AGENT_EVENTS.AGENT_MULTI_LOGIN, eventData.data);
|
|
992
1313
|
break;
|
|
@@ -1007,6 +1328,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1007
1328
|
channelsMap,
|
|
1008
1329
|
...loginData
|
|
1009
1330
|
} = eventData.data;
|
|
1331
|
+
this.updateWellnessSession(loginData.agentSessionId);
|
|
1010
1332
|
const stationLoginData = {
|
|
1011
1333
|
...loginData,
|
|
1012
1334
|
mmProfile: {
|
|
@@ -1028,6 +1350,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1028
1350
|
channelsMap,
|
|
1029
1351
|
...loginData
|
|
1030
1352
|
} = eventData.data;
|
|
1353
|
+
this.updateWellnessSession(loginData.agentSessionId);
|
|
1031
1354
|
const stationReLoginData = {
|
|
1032
1355
|
...loginData,
|
|
1033
1356
|
mmProfile: {
|
|
@@ -1040,8 +1363,8 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1040
1363
|
};
|
|
1041
1364
|
// @ts-ignore
|
|
1042
1365
|
this.emit(_types2.AGENT_EVENTS.AGENT_RELOGIN_SUCCESS, stationReLoginData);
|
|
1366
|
+
break;
|
|
1043
1367
|
}
|
|
1044
|
-
break;
|
|
1045
1368
|
case _types3.CC_EVENTS.AGENT_STATE_CHANGE_SUCCESS:
|
|
1046
1369
|
// @ts-ignore
|
|
1047
1370
|
this.emit(_types2.AGENT_EVENTS.AGENT_STATE_CHANGE_SUCCESS, eventData.data);
|
|
@@ -1055,6 +1378,9 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1055
1378
|
this.emit(_types2.AGENT_EVENTS.AGENT_STATION_LOGIN_FAILED, eventData.data);
|
|
1056
1379
|
break;
|
|
1057
1380
|
case _types3.CC_EVENTS.AGENT_LOGOUT_SUCCESS:
|
|
1381
|
+
if (!eventData.data.agentSessionId || eventData.data.agentSessionId === this.currentAgentSessionId) {
|
|
1382
|
+
this.updateWellnessSession();
|
|
1383
|
+
}
|
|
1058
1384
|
// @ts-ignore
|
|
1059
1385
|
this.emit(_types2.AGENT_EVENTS.AGENT_LOGOUT_SUCCESS, eventData.data);
|
|
1060
1386
|
break;
|
|
@@ -1071,6 +1397,504 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1071
1397
|
}
|
|
1072
1398
|
};
|
|
1073
1399
|
|
|
1400
|
+
/**
|
|
1401
|
+
* Checks whether Mobius/WebRTC registration should be skipped by config
|
|
1402
|
+
* @returns {boolean} True when browser WebRTC registration is disabled
|
|
1403
|
+
* @private
|
|
1404
|
+
*/
|
|
1405
|
+
isWebRTCRegistrationDisabled() {
|
|
1406
|
+
return this.$config?.disableWebRTCRegistration === true;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
/**
|
|
1410
|
+
* Validates contact-center plugin configuration before service initialization
|
|
1411
|
+
* @private
|
|
1412
|
+
*/
|
|
1413
|
+
validatePluginConfig() {
|
|
1414
|
+
if (this.$config?.disableWebRTCRegistration === true && this.$config?.allowMultiLogin === false) {
|
|
1415
|
+
throw new Error('Invalid Contact Center configuration: disableWebRTCRegistration cannot be true when allowMultiLogin is false. Enable allowMultiLogin or allow WebRTC registration so an SDK instance can receive Mobius/WebRTC task events.');
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
/**
|
|
1420
|
+
* Whether wxApp thick-client answer is enabled for this SDK instance.
|
|
1421
|
+
* @public
|
|
1422
|
+
*/
|
|
1423
|
+
isWxBetterTogetherEnabled() {
|
|
1424
|
+
return this.$config?.enableWxBetterTogether === true;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
/**
|
|
1428
|
+
* Current station login option (Extension, Dial Number, or Browser/Desktop).
|
|
1429
|
+
* Prefer webCallingService — updated on every stationLogin before wxApp hooks run.
|
|
1430
|
+
* @private
|
|
1431
|
+
*/
|
|
1432
|
+
getCurrentStationLoginOption() {
|
|
1433
|
+
return this.webCallingService?.loginOption ?? this.agentConfig?.deviceType;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
/**
|
|
1437
|
+
* wxApp thick-client answer is supported for Extension and Dial Number station login only —
|
|
1438
|
+
* not Browser/Desktop (WebRTC) login.
|
|
1439
|
+
* @private
|
|
1440
|
+
*/
|
|
1441
|
+
assertWxAppStationLoginSupportedForEnable() {
|
|
1442
|
+
const loginOption = this.getCurrentStationLoginOption();
|
|
1443
|
+
if (loginOption !== _types.LoginOption.EXTENSION && loginOption !== _types.LoginOption.AGENT_DN) {
|
|
1444
|
+
throw new Error('setManageWebexCallingInWxcc requires EXTENSION or AGENT_DN station login. Complete station login before enabling.');
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
/**
|
|
1449
|
+
* Clears runtime wxApp config after logout/deregister so stale flags do not apply on relogin.
|
|
1450
|
+
* @private
|
|
1451
|
+
*/
|
|
1452
|
+
resetEnableWxBetterTogetherConfig() {
|
|
1453
|
+
if (this.$config) {
|
|
1454
|
+
this.$config.enableWxBetterTogether = false;
|
|
1455
|
+
}
|
|
1456
|
+
this.taskManager.applyEnableWxBetterTogether(false);
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
/**
|
|
1460
|
+
* Runtime enable/disable for wxApp Better Together (toast suppression + Mercury mute sync).
|
|
1461
|
+
*
|
|
1462
|
+
* **Phase 1 (WXCC-6026 / MMT): NOT part of the public host contract.**
|
|
1463
|
+
* Hosts must set `webexConfig.cc.enableWxBetterTogether` before `webex.init()` / `cc.register()`.
|
|
1464
|
+
* To change the flag after the SDK is initialized, re-init the SDK with the updated config.
|
|
1465
|
+
*
|
|
1466
|
+
* **Supported public read API:** {@link isWxBetterTogetherEnabled}.
|
|
1467
|
+
* **Supported telephony API:** unified `task.accept()`, `task.decline()`, `task.toggleMute()`,
|
|
1468
|
+
* `task.transmitDtmf()` — SDK routes wxApp calls internally when the init flag is active.
|
|
1469
|
+
*
|
|
1470
|
+
* **Production lifecycle does not call this method.** Station login, silent relogin, logout,
|
|
1471
|
+
* and deregister use {@link ensureWxAppPostStationLogin} and {@link teardownWxAppLocalState}
|
|
1472
|
+
* (usersub publish + Mercury subscribe/unsubscribe based on the init flag).
|
|
1473
|
+
*
|
|
1474
|
+
* This setter remains as a **private implementation** for Phase 2 runtime toggle and unit tests.
|
|
1475
|
+
* It updates `$config.enableWxBetterTogether`, TaskManager flags, cross-client usersub
|
|
1476
|
+
* (`answer-calls-on-wxcc`), and Mercury mute sync; rolls back config on publish/connect failure.
|
|
1477
|
+
*
|
|
1478
|
+
* @param enabled - `true` to enable wxApp answer + usersub suppression; `false` to disable and tear down.
|
|
1479
|
+
* @throws When enabling without login, on unsupported BROWSER station login, or when usersub/Mercury init fails.
|
|
1480
|
+
* @internal
|
|
1481
|
+
* @see enableWxBetterTogether (CCPluginConfig init flag)
|
|
1482
|
+
* @see ensureWxAppPostStationLogin
|
|
1483
|
+
* @see teardownWxAppLocalState
|
|
1484
|
+
*/
|
|
1485
|
+
async setManageWebexCallingInWxcc(enabled) {
|
|
1486
|
+
if (enabled && !this.$webex.internal.device?.userId) {
|
|
1487
|
+
throw new Error('Cannot publish answer-calls-on-wxcc: user is not logged in');
|
|
1488
|
+
}
|
|
1489
|
+
if (enabled) {
|
|
1490
|
+
this.assertWxAppStationLoginSupportedForEnable();
|
|
1491
|
+
}
|
|
1492
|
+
const previousEnabled = this.isWxBetterTogetherEnabled();
|
|
1493
|
+
if (this.$config) {
|
|
1494
|
+
this.$config.enableWxBetterTogether = enabled;
|
|
1495
|
+
}
|
|
1496
|
+
this.taskManager.applyEnableWxBetterTogether(enabled);
|
|
1497
|
+
let publishedEnable = false;
|
|
1498
|
+
try {
|
|
1499
|
+
if (enabled) {
|
|
1500
|
+
await this.ensureWxAppMercuryAndSubscribe();
|
|
1501
|
+
await this.publishAnswerOnWebexCrossClientState(true);
|
|
1502
|
+
publishedEnable = true;
|
|
1503
|
+
this.taskManager.syncWxAppMuteFromCallDetailsForAllTasks();
|
|
1504
|
+
this.taskManager.refreshWxAppOfferObservabilityForAllTasks();
|
|
1505
|
+
} else {
|
|
1506
|
+
await this.publishAnswerOnWebexCrossClientState(false);
|
|
1507
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1508
|
+
await this.releaseWxAppMercuryResources();
|
|
1509
|
+
}
|
|
1510
|
+
} catch (error) {
|
|
1511
|
+
if (publishedEnable) {
|
|
1512
|
+
await this.revertWxAppCrossClientPublish();
|
|
1513
|
+
} else if (enabled) {
|
|
1514
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1515
|
+
await this.releaseWxAppMercuryResources();
|
|
1516
|
+
}
|
|
1517
|
+
if (this.$config) {
|
|
1518
|
+
this.$config.enableWxBetterTogether = previousEnabled;
|
|
1519
|
+
}
|
|
1520
|
+
this.taskManager.applyEnableWxBetterTogether(previousEnabled);
|
|
1521
|
+
throw error;
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
getWxAppTelephonyTaskType() {
|
|
1525
|
+
const loginOption = this.getCurrentStationLoginOption();
|
|
1526
|
+
if (loginOption === _types.LoginOption.BROWSER) {
|
|
1527
|
+
return 'WebRTC';
|
|
1528
|
+
}
|
|
1529
|
+
if (loginOption === _types.LoginOption.EXTENSION || loginOption === _types.LoginOption.AGENT_DN) {
|
|
1530
|
+
return 'Voice';
|
|
1531
|
+
}
|
|
1532
|
+
return 'unknown';
|
|
1533
|
+
}
|
|
1534
|
+
async ensureWxAppPostStationLogin() {
|
|
1535
|
+
const loginOption = this.getCurrentStationLoginOption();
|
|
1536
|
+
const flagEnabled = this.isWxBetterTogetherEnabled();
|
|
1537
|
+
const telephonyTaskType = this.getWxAppTelephonyTaskType();
|
|
1538
|
+
if (loginOption === _types.LoginOption.BROWSER) {
|
|
1539
|
+
if (flagEnabled) {
|
|
1540
|
+
(0, _wxAppDiagnosticLogging.logWxAppSessionReadiness)({
|
|
1541
|
+
enableWxBetterTogether: true,
|
|
1542
|
+
loginOption,
|
|
1543
|
+
wxAppHooksApplied: false,
|
|
1544
|
+
usersubPublished: false,
|
|
1545
|
+
mercurySubscribed: false,
|
|
1546
|
+
telephonyTaskType,
|
|
1547
|
+
skipReason: 'unsupported_browser_login'
|
|
1548
|
+
});
|
|
1549
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_SKIPPED, {
|
|
1550
|
+
loginOption,
|
|
1551
|
+
enableWxBetterTogether: true,
|
|
1552
|
+
skipReason: 'unsupported_browser_login'
|
|
1553
|
+
}, ['operational', 'behavioral']);
|
|
1554
|
+
}
|
|
1555
|
+
return;
|
|
1556
|
+
}
|
|
1557
|
+
let publishedEnable = false;
|
|
1558
|
+
let wxAppInitStage = 'mercury';
|
|
1559
|
+
try {
|
|
1560
|
+
if (flagEnabled) {
|
|
1561
|
+
this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS, _constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED]);
|
|
1562
|
+
await this.ensureWxAppMercuryAndSubscribe();
|
|
1563
|
+
wxAppInitStage = 'publish';
|
|
1564
|
+
await this.publishAnswerOnWebexCrossClientState(true);
|
|
1565
|
+
publishedEnable = true;
|
|
1566
|
+
this.taskManager.syncWxAppMuteFromCallDetailsForAllTasks();
|
|
1567
|
+
this.taskManager.refreshWxAppOfferObservabilityForAllTasks();
|
|
1568
|
+
const mercurySubscribed = this.wxAppTelephonyMercurySync.isSubscribed();
|
|
1569
|
+
const usersubPublished = this.webexCrossClientService.isAnswerCallsStateActive();
|
|
1570
|
+
(0, _wxAppDiagnosticLogging.logWxAppSessionReadiness)({
|
|
1571
|
+
enableWxBetterTogether: true,
|
|
1572
|
+
loginOption,
|
|
1573
|
+
wxAppHooksApplied: true,
|
|
1574
|
+
usersubPublished,
|
|
1575
|
+
mercurySubscribed,
|
|
1576
|
+
telephonyTaskType
|
|
1577
|
+
});
|
|
1578
|
+
const sessionInitReady = mercurySubscribed && usersubPublished;
|
|
1579
|
+
if (sessionInitReady) {
|
|
1580
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS, {
|
|
1581
|
+
loginOption,
|
|
1582
|
+
enableWxBetterTogether: true,
|
|
1583
|
+
usersubPublished,
|
|
1584
|
+
mercurySubscribed,
|
|
1585
|
+
telephonyTaskType
|
|
1586
|
+
}, ['operational', 'behavioral']);
|
|
1587
|
+
} else {
|
|
1588
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED, {
|
|
1589
|
+
loginOption,
|
|
1590
|
+
enableWxBetterTogether: true,
|
|
1591
|
+
usersubPublished,
|
|
1592
|
+
mercurySubscribed,
|
|
1593
|
+
telephonyTaskType,
|
|
1594
|
+
skipReason: !usersubPublished ? 'usersub_not_published' : 'mercury_not_subscribed'
|
|
1595
|
+
}, ['operational', 'behavioral']);
|
|
1596
|
+
}
|
|
1597
|
+
} else {
|
|
1598
|
+
await this.ensureWxAppDeviceRegistered();
|
|
1599
|
+
await this.publishAnswerOnWebexCrossClientState(false, {
|
|
1600
|
+
force: true
|
|
1601
|
+
});
|
|
1602
|
+
this.webexCrossClientService.teardown();
|
|
1603
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1604
|
+
await this.releaseWxAppMercuryResources();
|
|
1605
|
+
(0, _wxAppDiagnosticLogging.logWxAppSessionReadiness)({
|
|
1606
|
+
enableWxBetterTogether: false,
|
|
1607
|
+
loginOption,
|
|
1608
|
+
wxAppHooksApplied: true,
|
|
1609
|
+
usersubPublished: false,
|
|
1610
|
+
mercurySubscribed: false,
|
|
1611
|
+
telephonyTaskType,
|
|
1612
|
+
skipReason: 'flag_disabled'
|
|
1613
|
+
});
|
|
1614
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_SKIPPED, {
|
|
1615
|
+
loginOption,
|
|
1616
|
+
enableWxBetterTogether: false,
|
|
1617
|
+
skipReason: 'flag_disabled'
|
|
1618
|
+
}, ['operational', 'behavioral']);
|
|
1619
|
+
}
|
|
1620
|
+
} catch (error) {
|
|
1621
|
+
_loggerProxy.default.error(`Failed to initialize wxApp post-station-login: ${error}`, {
|
|
1622
|
+
module: _constants.CC_FILE,
|
|
1623
|
+
method: _constants.METHODS.STATION_LOGIN
|
|
1624
|
+
});
|
|
1625
|
+
if (flagEnabled) {
|
|
1626
|
+
const sessionInitFailureReason = wxAppInitStage === 'mercury' ? 'mercury_subscribe_failed' : 'publish_failed';
|
|
1627
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED, {
|
|
1628
|
+
loginOption,
|
|
1629
|
+
enableWxBetterTogether: true,
|
|
1630
|
+
usersubPublished: this.webexCrossClientService.isAnswerCallsStateActive(),
|
|
1631
|
+
mercurySubscribed: this.wxAppTelephonyMercurySync.isSubscribed(),
|
|
1632
|
+
telephonyTaskType,
|
|
1633
|
+
skipReason: sessionInitFailureReason,
|
|
1634
|
+
error: error instanceof Error ? error.toString() : String(error)
|
|
1635
|
+
}, ['operational', 'behavioral']);
|
|
1636
|
+
(0, _wxAppDiagnosticLogging.logWxAppSessionReadiness)({
|
|
1637
|
+
enableWxBetterTogether: true,
|
|
1638
|
+
loginOption,
|
|
1639
|
+
wxAppHooksApplied: false,
|
|
1640
|
+
usersubPublished: this.webexCrossClientService.isAnswerCallsStateActive(),
|
|
1641
|
+
mercurySubscribed: this.wxAppTelephonyMercurySync.isSubscribed(),
|
|
1642
|
+
telephonyTaskType,
|
|
1643
|
+
skipReason: sessionInitFailureReason
|
|
1644
|
+
});
|
|
1645
|
+
if (publishedEnable) {
|
|
1646
|
+
await this.revertWxAppCrossClientPublish();
|
|
1647
|
+
} else {
|
|
1648
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1649
|
+
await this.releaseWxAppMercuryResources();
|
|
1650
|
+
}
|
|
1651
|
+
this.resetEnableWxBetterTogetherConfig();
|
|
1652
|
+
} else {
|
|
1653
|
+
this.scheduleForcedFalsePublishRetry();
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
clearWxAppFalsePublishRetryTimer() {
|
|
1658
|
+
if (this.wxAppFalsePublishRetryTimer) {
|
|
1659
|
+
clearTimeout(this.wxAppFalsePublishRetryTimer);
|
|
1660
|
+
this.wxAppFalsePublishRetryTimer = undefined;
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
scheduleForcedFalsePublishRetry(retryAttempt = 0) {
|
|
1664
|
+
this.scheduleFalsePublishRetry(retryAttempt, {
|
|
1665
|
+
requireDisabled: true
|
|
1666
|
+
});
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1669
|
+
/**
|
|
1670
|
+
* Retries compensating false publish after rollback failure, regardless of enabled flag.
|
|
1671
|
+
* @private
|
|
1672
|
+
*/
|
|
1673
|
+
scheduleCompensatingFalsePublishRetry(retryAttempt = 0) {
|
|
1674
|
+
this.scheduleFalsePublishRetry(retryAttempt);
|
|
1675
|
+
}
|
|
1676
|
+
scheduleFalsePublishRetry(retryAttempt = 0, options = {}) {
|
|
1677
|
+
if (retryAttempt >= ContactCenter.WXAPP_FALSE_PUBLISH_MAX_RETRIES) {
|
|
1678
|
+
return;
|
|
1679
|
+
}
|
|
1680
|
+
if (options?.requireDisabled && this.isWxBetterTogetherEnabled()) {
|
|
1681
|
+
return;
|
|
1682
|
+
}
|
|
1683
|
+
this.clearWxAppFalsePublishRetryTimer();
|
|
1684
|
+
this.wxAppFalsePublishRetryTimer = setTimeout(async () => {
|
|
1685
|
+
this.wxAppFalsePublishRetryTimer = undefined;
|
|
1686
|
+
if (options?.requireDisabled && this.isWxBetterTogetherEnabled()) {
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1689
|
+
if (!options?.requireDisabled && this.isWxBetterTogetherEnabled() && this.webexCrossClientService.isAnswerCallsStateActive()) {
|
|
1690
|
+
return;
|
|
1691
|
+
}
|
|
1692
|
+
try {
|
|
1693
|
+
await this.ensureWxAppDeviceRegistered();
|
|
1694
|
+
await this.publishAnswerOnWebexCrossClientState(false, {
|
|
1695
|
+
force: true
|
|
1696
|
+
});
|
|
1697
|
+
this.webexCrossClientService.teardown();
|
|
1698
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1699
|
+
await this.releaseWxAppMercuryResources();
|
|
1700
|
+
} catch (retryError) {
|
|
1701
|
+
_loggerProxy.default.error(`Failed to retry forced answer-calls-on-wxcc false publish: ${retryError}`, {
|
|
1702
|
+
module: _constants.CC_FILE,
|
|
1703
|
+
method: _constants.METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC
|
|
1704
|
+
});
|
|
1705
|
+
this.scheduleFalsePublishRetry(retryAttempt + 1, options);
|
|
1706
|
+
}
|
|
1707
|
+
}, ContactCenter.WXAPP_FALSE_PUBLISH_RETRY_DELAY_MS);
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
/**
|
|
1711
|
+
* Best-effort rollback when usersub true was published but wxApp setup did not complete.
|
|
1712
|
+
* @private
|
|
1713
|
+
*/
|
|
1714
|
+
async revertWxAppCrossClientPublish() {
|
|
1715
|
+
try {
|
|
1716
|
+
await this.publishAnswerOnWebexCrossClientState(false, {
|
|
1717
|
+
force: true
|
|
1718
|
+
});
|
|
1719
|
+
} catch (error) {
|
|
1720
|
+
_loggerProxy.default.error(`Failed to publish compensating answer-calls-on-wxcc false during wxApp rollback: ${error}`, {
|
|
1721
|
+
module: _constants.CC_FILE,
|
|
1722
|
+
method: _constants.METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC
|
|
1723
|
+
});
|
|
1724
|
+
this.scheduleCompensatingFalsePublishRetry();
|
|
1725
|
+
}
|
|
1726
|
+
this.webexCrossClientService.teardown();
|
|
1727
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1728
|
+
await this.releaseWxAppMercuryResources();
|
|
1729
|
+
}
|
|
1730
|
+
async ensureWxAppDeviceRegistered() {
|
|
1731
|
+
const device = this.$webex.internal.device;
|
|
1732
|
+
if (device && !device.registered && device.register) {
|
|
1733
|
+
await device.register();
|
|
1734
|
+
this.wxAppDeviceRegisteredByCc = true;
|
|
1735
|
+
_loggerProxy.default.log('WxApp: device registered for cross-client publish', {
|
|
1736
|
+
module: _constants.CC_FILE,
|
|
1737
|
+
method: _constants.METHODS.ENSURE_WXAPP_MERCURY_CONNECTED
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
async ensureWxAppMercuryConnected() {
|
|
1742
|
+
const mercury = this.$webex.internal.mercury;
|
|
1743
|
+
await this.ensureWxAppDeviceRegistered();
|
|
1744
|
+
if (mercury && !mercury.connected) {
|
|
1745
|
+
await mercury.connect();
|
|
1746
|
+
this.wxAppMercuryConnectedByCc = true;
|
|
1747
|
+
_loggerProxy.default.log('WxApp mute sync: mercury connected', {
|
|
1748
|
+
module: _constants.CC_FILE,
|
|
1749
|
+
method: _constants.METHODS.ENSURE_WXAPP_MERCURY_CONNECTED
|
|
1750
|
+
});
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
|
|
1754
|
+
/**
|
|
1755
|
+
* Disconnects Mercury and unregisters the device when wxApp mute sync opened them.
|
|
1756
|
+
* @private
|
|
1757
|
+
*/
|
|
1758
|
+
async releaseWxAppMercuryResources() {
|
|
1759
|
+
const mercury = this.$webex.internal.mercury;
|
|
1760
|
+
const device = this.$webex.internal.device;
|
|
1761
|
+
if (this.wxAppMercuryConnectedByCc && mercury?.connected) {
|
|
1762
|
+
try {
|
|
1763
|
+
await mercury.disconnect();
|
|
1764
|
+
this.wxAppMercuryConnectedByCc = false;
|
|
1765
|
+
_loggerProxy.default.log('WxApp mute sync: mercury disconnected', {
|
|
1766
|
+
module: _constants.CC_FILE,
|
|
1767
|
+
method: _constants.METHODS.ENSURE_WXAPP_MERCURY_CONNECTED
|
|
1768
|
+
});
|
|
1769
|
+
} catch (error) {
|
|
1770
|
+
_loggerProxy.default.error(`Failed to disconnect wxApp Mercury: ${error}`, {
|
|
1771
|
+
module: _constants.CC_FILE,
|
|
1772
|
+
method: _constants.METHODS.ENSURE_WXAPP_MERCURY_CONNECTED
|
|
1773
|
+
});
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
if (this.wxAppDeviceRegisteredByCc && device?.registered && device.unregister) {
|
|
1777
|
+
try {
|
|
1778
|
+
// @ts-ignore
|
|
1779
|
+
await device.unregister();
|
|
1780
|
+
this.wxAppDeviceRegisteredByCc = false;
|
|
1781
|
+
_loggerProxy.default.log('WxApp mute sync: device unregistered', {
|
|
1782
|
+
module: _constants.CC_FILE,
|
|
1783
|
+
method: _constants.METHODS.ENSURE_WXAPP_MERCURY_CONNECTED
|
|
1784
|
+
});
|
|
1785
|
+
} catch (error) {
|
|
1786
|
+
_loggerProxy.default.error(`Failed to unregister wxApp device: ${error}`, {
|
|
1787
|
+
module: _constants.CC_FILE,
|
|
1788
|
+
method: _constants.METHODS.ENSURE_WXAPP_MERCURY_CONNECTED
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
} else if (this.wxAppDeviceRegisteredByCc) {
|
|
1792
|
+
this.wxAppDeviceRegisteredByCc = false;
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
|
|
1796
|
+
/**
|
|
1797
|
+
* Clears wxApp runtime state and feature-owned resources. Publish failures are logged;
|
|
1798
|
+
* optional rethrow for deregister when callers need to surface usersub errors.
|
|
1799
|
+
* @private
|
|
1800
|
+
*/
|
|
1801
|
+
async teardownWxAppLocalState(options) {
|
|
1802
|
+
this.clearWxAppFalsePublishRetryTimer();
|
|
1803
|
+
let publishError;
|
|
1804
|
+
try {
|
|
1805
|
+
const shouldForceFalsePublish = this.isWxBetterTogetherEnabled() || this.webexCrossClientService.isAnswerCallsStateActive();
|
|
1806
|
+
await this.publishAnswerOnWebexCrossClientState(false, {
|
|
1807
|
+
force: shouldForceFalsePublish
|
|
1808
|
+
});
|
|
1809
|
+
} catch (error) {
|
|
1810
|
+
publishError = error;
|
|
1811
|
+
_loggerProxy.default.error(`Failed to publish answer-calls-on-wxcc false during wxApp teardown: ${error}`, {
|
|
1812
|
+
module: _constants.CC_FILE,
|
|
1813
|
+
method: _constants.METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC
|
|
1814
|
+
});
|
|
1815
|
+
}
|
|
1816
|
+
this.webexCrossClientService.teardown();
|
|
1817
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1818
|
+
await this.releaseWxAppMercuryResources();
|
|
1819
|
+
if (options?.clearInitFlag) {
|
|
1820
|
+
this.resetEnableWxBetterTogetherConfig();
|
|
1821
|
+
}
|
|
1822
|
+
if (publishError) {
|
|
1823
|
+
this.scheduleCompensatingFalsePublishRetry();
|
|
1824
|
+
}
|
|
1825
|
+
if (options?.rethrowPublishError && publishError) {
|
|
1826
|
+
throw publishError;
|
|
1827
|
+
}
|
|
1828
|
+
return {
|
|
1829
|
+
publishError: publishError ?? undefined
|
|
1830
|
+
};
|
|
1831
|
+
}
|
|
1832
|
+
async ensureWxAppMercuryAndSubscribe() {
|
|
1833
|
+
if (!this.isWxBetterTogetherEnabled()) {
|
|
1834
|
+
return;
|
|
1835
|
+
}
|
|
1836
|
+
if (this.getCurrentStationLoginOption() === _types.LoginOption.BROWSER) {
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1839
|
+
const agentId = this.agentConfig?.agentId;
|
|
1840
|
+
if (!agentId) {
|
|
1841
|
+
_loggerProxy.default.error('Cannot subscribe wxApp mute sync: agentId unavailable', {
|
|
1842
|
+
module: _constants.CC_FILE,
|
|
1843
|
+
method: _constants.METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY
|
|
1844
|
+
});
|
|
1845
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED, {
|
|
1846
|
+
error: 'agentId unavailable',
|
|
1847
|
+
mercurySubscribed: false
|
|
1848
|
+
}, ['operational', 'behavioral']);
|
|
1849
|
+
return;
|
|
1850
|
+
}
|
|
1851
|
+
try {
|
|
1852
|
+
await this.ensureWxAppMercuryConnected();
|
|
1853
|
+
this.wxAppTelephonyMercurySync.subscribe(agentId, (callId, muted) => {
|
|
1854
|
+
this.taskManager.applyWxAppMuteStateFromSync(callId, muted);
|
|
1855
|
+
});
|
|
1856
|
+
} catch (error) {
|
|
1857
|
+
_loggerProxy.default.error(`Failed to ensure wxApp Mercury mute sync: ${error}`, {
|
|
1858
|
+
module: _constants.CC_FILE,
|
|
1859
|
+
method: _constants.METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY
|
|
1860
|
+
});
|
|
1861
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED, {
|
|
1862
|
+
error: error instanceof Error ? error.toString() : String(error),
|
|
1863
|
+
mercurySubscribed: false
|
|
1864
|
+
}, ['operational', 'behavioral']);
|
|
1865
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1866
|
+
await this.releaseWxAppMercuryResources();
|
|
1867
|
+
throw error;
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
async publishAnswerOnWebexCrossClientState(enable, options) {
|
|
1871
|
+
if (enable && !this.isWxBetterTogetherEnabled()) {
|
|
1872
|
+
return;
|
|
1873
|
+
}
|
|
1874
|
+
if (!enable && !options?.force && !this.webexCrossClientService.isAnswerCallsStateActive()) {
|
|
1875
|
+
return;
|
|
1876
|
+
}
|
|
1877
|
+
const userId = this.$webex.internal.device?.userId;
|
|
1878
|
+
if (!userId) {
|
|
1879
|
+
if (enable && this.isWxBetterTogetherEnabled()) {
|
|
1880
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.WXAPP_USERSUB_PUBLISH_FAILED, {
|
|
1881
|
+
enableWxBetterTogether: true,
|
|
1882
|
+
usersubPublished: this.webexCrossClientService.isAnswerCallsStateActive(),
|
|
1883
|
+
skipReason: 'user_id_unavailable',
|
|
1884
|
+
error: 'User ID is unavailable for cross-client publish'
|
|
1885
|
+
}, ['operational', 'behavioral']);
|
|
1886
|
+
}
|
|
1887
|
+
return;
|
|
1888
|
+
}
|
|
1889
|
+
await this.webexCrossClientService.setManageWebexCallingInWxcc(enable, {
|
|
1890
|
+
userId,
|
|
1891
|
+
trackPublishMetrics: true
|
|
1892
|
+
});
|
|
1893
|
+
if (enable) {
|
|
1894
|
+
this.clearWxAppFalsePublishRetryTimer();
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1074
1898
|
/**
|
|
1075
1899
|
* Initializes event listeners for the Contact Center service
|
|
1076
1900
|
* Sets up handlers for connection state changes and other core events
|
|
@@ -1102,13 +1926,13 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1102
1926
|
async handleConnectionLost(msg) {
|
|
1103
1927
|
if (msg.isConnectionLost) {
|
|
1104
1928
|
// TODO: Emit an event saying connection is lost
|
|
1105
|
-
_loggerProxy.default.
|
|
1929
|
+
_loggerProxy.default.log('event=handleConnectionLost | Connection lost', {
|
|
1106
1930
|
module: _constants.CC_FILE,
|
|
1107
1931
|
method: _constants.METHODS.HANDLE_CONNECTION_LOST
|
|
1108
1932
|
});
|
|
1109
1933
|
} else if (msg.isSocketReconnected) {
|
|
1110
1934
|
// TODO: Emit an event saying connection is re-estabilished
|
|
1111
|
-
_loggerProxy.default.
|
|
1935
|
+
_loggerProxy.default.log('event=handleConnectionReconnect | Connection reconnected attempting to request silent relogin', {
|
|
1112
1936
|
module: _constants.CC_FILE,
|
|
1113
1937
|
method: _constants.METHODS.HANDLE_CONNECTION_LOST
|
|
1114
1938
|
});
|
|
@@ -1123,12 +1947,13 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1123
1947
|
* @private
|
|
1124
1948
|
*/
|
|
1125
1949
|
async silentRelogin() {
|
|
1126
|
-
_loggerProxy.default.
|
|
1950
|
+
_loggerProxy.default.log('Starting silent relogin process', {
|
|
1127
1951
|
module: _constants.CC_FILE,
|
|
1128
1952
|
method: _constants.METHODS.SILENT_RELOGIN
|
|
1129
1953
|
});
|
|
1130
1954
|
try {
|
|
1131
1955
|
const reLoginResponse = await this.services.agent.reload();
|
|
1956
|
+
this.updateWellnessSession(reLoginResponse.data.agentSessionId);
|
|
1132
1957
|
const {
|
|
1133
1958
|
agentId,
|
|
1134
1959
|
lastStateChangeReason,
|
|
@@ -1145,7 +1970,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1145
1970
|
this.agentConfig.currentTeamId = reLoginResponse.data.teamId;
|
|
1146
1971
|
await this.handleDeviceType(deviceType, dn);
|
|
1147
1972
|
if (lastStateChangeReason === 'agent-wss-disconnect') {
|
|
1148
|
-
_loggerProxy.default.
|
|
1973
|
+
_loggerProxy.default.log('event=requestAutoStateChange | Requesting state change to available on socket reconnect', {
|
|
1149
1974
|
module: _constants.CC_FILE,
|
|
1150
1975
|
method: _constants.METHODS.SILENT_RELOGIN
|
|
1151
1976
|
});
|
|
@@ -1169,8 +1994,7 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1169
1994
|
}
|
|
1170
1995
|
this.agentConfig.lastStateAuxCodeId = auxCodeId;
|
|
1171
1996
|
this.agentConfig.isAgentLoggedIn = true;
|
|
1172
|
-
|
|
1173
|
-
this.services.webSocketManager.on('message', this.handleWebsocketMessage);
|
|
1997
|
+
await this.ensureWxAppPostStationLogin();
|
|
1174
1998
|
_loggerProxy.default.log(`Silent relogin process completed successfully with login Option: ${reLoginResponse.data.deviceType} teamId: ${reLoginResponse.data.teamId}`, {
|
|
1175
1999
|
module: _constants.CC_FILE,
|
|
1176
2000
|
method: _constants.METHODS.SILENT_RELOGIN,
|
|
@@ -1205,6 +2029,13 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1205
2029
|
this.agentConfig.deviceType = deviceType;
|
|
1206
2030
|
switch (deviceType) {
|
|
1207
2031
|
case _types.LoginOption.BROWSER:
|
|
2032
|
+
if (this.isWebRTCRegistrationDisabled()) {
|
|
2033
|
+
_loggerProxy.default.info('Skipping web calling line registration because disableWebRTCRegistration is enabled', {
|
|
2034
|
+
module: _constants.CC_FILE,
|
|
2035
|
+
method: _constants.METHODS.HANDLE_DEVICE_TYPE
|
|
2036
|
+
});
|
|
2037
|
+
break;
|
|
2038
|
+
}
|
|
1208
2039
|
try {
|
|
1209
2040
|
await this.webCallingService.registerWebCallingLine();
|
|
1210
2041
|
} catch (error) {
|
|
@@ -1444,6 +2275,138 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1444
2275
|
}
|
|
1445
2276
|
}
|
|
1446
2277
|
|
|
2278
|
+
/**
|
|
2279
|
+
* Skips a campaign preview contact, requesting the next contact from the campaign.
|
|
2280
|
+
*
|
|
2281
|
+
* When a campaign manager reserves a contact for an agent, the agent receives an
|
|
2282
|
+
* `AgentOfferCampaignReservation` event. Instead of accepting, the agent can skip the
|
|
2283
|
+
* preview contact to move to the next contact in the campaign.
|
|
2284
|
+
*
|
|
2285
|
+
* @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
|
|
2286
|
+
* @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
|
|
2287
|
+
* @throws {Error} If the operation fails (network error, etc.)
|
|
2288
|
+
*
|
|
2289
|
+
* @example
|
|
2290
|
+
* ```typescript
|
|
2291
|
+
* webex.cc.on('task:campaignPreviewReservation', async (task) => {
|
|
2292
|
+
* const { interactionId } = task.data;
|
|
2293
|
+
* const campaignId = task.data.interaction.callProcessingDetails.campaignId;
|
|
2294
|
+
*
|
|
2295
|
+
* const result = await webex.cc.skipPreviewContact({ interactionId, campaignId });
|
|
2296
|
+
* });
|
|
2297
|
+
* ```
|
|
2298
|
+
*/
|
|
2299
|
+
async skipPreviewContact(payload) {
|
|
2300
|
+
const task = this.taskManager.getTask(payload.interactionId);
|
|
2301
|
+
if (task?.data?.interaction?.callProcessingDetails?.campaignPreviewSkipDisabled === 'true') {
|
|
2302
|
+
_loggerProxy.default.warn('Skip action is disabled for this campaign preview contact', {
|
|
2303
|
+
module: _constants.CC_FILE,
|
|
2304
|
+
method: _constants.METHODS.SKIP_PREVIEW_CONTACT,
|
|
2305
|
+
interactionId: payload.interactionId
|
|
2306
|
+
});
|
|
2307
|
+
throw new Error('Skip action is disabled for this campaign preview contact');
|
|
2308
|
+
}
|
|
2309
|
+
_loggerProxy.default.info('Skipping campaign preview contact', {
|
|
2310
|
+
module: _constants.CC_FILE,
|
|
2311
|
+
method: _constants.METHODS.SKIP_PREVIEW_CONTACT
|
|
2312
|
+
});
|
|
2313
|
+
try {
|
|
2314
|
+
this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS, _constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED]);
|
|
2315
|
+
const result = await this.services.dialer.skipPreviewContact({
|
|
2316
|
+
data: payload
|
|
2317
|
+
});
|
|
2318
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_SUCCESS, {
|
|
2319
|
+
..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result),
|
|
2320
|
+
interactionId: payload.interactionId,
|
|
2321
|
+
campaignId: payload.campaignId
|
|
2322
|
+
}, ['behavioral', 'business', 'operational']);
|
|
2323
|
+
_loggerProxy.default.log('Campaign preview contact skipped successfully', {
|
|
2324
|
+
module: _constants.CC_FILE,
|
|
2325
|
+
method: _constants.METHODS.SKIP_PREVIEW_CONTACT,
|
|
2326
|
+
trackingId: result.trackingId,
|
|
2327
|
+
interactionId: payload.interactionId
|
|
2328
|
+
});
|
|
2329
|
+
return result;
|
|
2330
|
+
} catch (error) {
|
|
2331
|
+
const failure = error.details;
|
|
2332
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_SKIP_FAILED, {
|
|
2333
|
+
..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(failure),
|
|
2334
|
+
interactionId: payload.interactionId,
|
|
2335
|
+
campaignId: payload.campaignId
|
|
2336
|
+
}, ['behavioral', 'business', 'operational']);
|
|
2337
|
+
const {
|
|
2338
|
+
error: detailedError
|
|
2339
|
+
} = (0, _Utils.getErrorDetails)(error, _constants.METHODS.SKIP_PREVIEW_CONTACT, _constants.CC_FILE);
|
|
2340
|
+
throw detailedError;
|
|
2341
|
+
}
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
/**
|
|
2345
|
+
* Removes a campaign preview contact from the campaign list entirely.
|
|
2346
|
+
*
|
|
2347
|
+
* When a campaign manager reserves a contact for an agent, the agent receives an
|
|
2348
|
+
* `AgentOfferCampaignReservation` event. Instead of accepting or skipping, the agent can
|
|
2349
|
+
* remove the preview contact to permanently take it out of the campaign contact list.
|
|
2350
|
+
*
|
|
2351
|
+
* @param {PreviewContactPayload} payload - The preview contact payload containing interactionId and campaignId (campaign name, not UUID).
|
|
2352
|
+
* @returns {Promise<TaskResponse>} Promise resolving with agent contact on success.
|
|
2353
|
+
* @throws {Error} If the operation fails (network error, etc.)
|
|
2354
|
+
*
|
|
2355
|
+
* @example
|
|
2356
|
+
* ```typescript
|
|
2357
|
+
* webex.cc.on('task:campaignPreviewReservation', async (task) => {
|
|
2358
|
+
* const { interactionId } = task.data;
|
|
2359
|
+
* const campaignId = task.data.interaction.callProcessingDetails.campaignId;
|
|
2360
|
+
*
|
|
2361
|
+
* const result = await webex.cc.removePreviewContact({ interactionId, campaignId });
|
|
2362
|
+
* });
|
|
2363
|
+
* ```
|
|
2364
|
+
*/
|
|
2365
|
+
async removePreviewContact(payload) {
|
|
2366
|
+
const task = this.taskManager.getTask(payload.interactionId);
|
|
2367
|
+
if (task?.data?.interaction?.callProcessingDetails?.campaignPreviewRemoveDisabled === 'true') {
|
|
2368
|
+
_loggerProxy.default.warn('Remove action is disabled for this campaign preview contact', {
|
|
2369
|
+
module: _constants.CC_FILE,
|
|
2370
|
+
method: _constants.METHODS.REMOVE_PREVIEW_CONTACT,
|
|
2371
|
+
interactionId: payload.interactionId
|
|
2372
|
+
});
|
|
2373
|
+
throw new Error('Remove action is disabled for this campaign preview contact');
|
|
2374
|
+
}
|
|
2375
|
+
_loggerProxy.default.info('Removing campaign preview contact', {
|
|
2376
|
+
module: _constants.CC_FILE,
|
|
2377
|
+
method: _constants.METHODS.REMOVE_PREVIEW_CONTACT
|
|
2378
|
+
});
|
|
2379
|
+
try {
|
|
2380
|
+
this.metricsManager.timeEvent([_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS, _constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED]);
|
|
2381
|
+
const result = await this.services.dialer.removePreviewContact({
|
|
2382
|
+
data: payload
|
|
2383
|
+
});
|
|
2384
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_SUCCESS, {
|
|
2385
|
+
..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result),
|
|
2386
|
+
interactionId: payload.interactionId,
|
|
2387
|
+
campaignId: payload.campaignId
|
|
2388
|
+
}, ['behavioral', 'business', 'operational']);
|
|
2389
|
+
_loggerProxy.default.log('Campaign preview contact removed successfully', {
|
|
2390
|
+
module: _constants.CC_FILE,
|
|
2391
|
+
method: _constants.METHODS.REMOVE_PREVIEW_CONTACT,
|
|
2392
|
+
trackingId: result.trackingId,
|
|
2393
|
+
interactionId: payload.interactionId
|
|
2394
|
+
});
|
|
2395
|
+
return result;
|
|
2396
|
+
} catch (error) {
|
|
2397
|
+
const failure = error.details;
|
|
2398
|
+
this.metricsManager.trackEvent(_constants4.METRIC_EVENT_NAMES.CAMPAIGN_PREVIEW_REMOVE_FAILED, {
|
|
2399
|
+
..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(failure),
|
|
2400
|
+
interactionId: payload.interactionId,
|
|
2401
|
+
campaignId: payload.campaignId
|
|
2402
|
+
}, ['behavioral', 'business', 'operational']);
|
|
2403
|
+
const {
|
|
2404
|
+
error: detailedError
|
|
2405
|
+
} = (0, _Utils.getErrorDetails)(error, _constants.METHODS.REMOVE_PREVIEW_CONTACT, _constants.CC_FILE);
|
|
2406
|
+
throw detailedError;
|
|
2407
|
+
}
|
|
2408
|
+
}
|
|
2409
|
+
|
|
1447
2410
|
/**
|
|
1448
2411
|
* Fetches outdial ANI (Automatic Number Identification) entries for an outdial ANI ID.
|
|
1449
2412
|
*
|
|
@@ -1648,8 +2611,8 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1648
2611
|
}
|
|
1649
2612
|
|
|
1650
2613
|
/**
|
|
1651
|
-
* Returns paginated entry points
|
|
1652
|
-
*
|
|
2614
|
+
* Returns paginated entry points using the EntryPoint service defaults.
|
|
2615
|
+
* Existing request parameters can override those defaults.
|
|
1653
2616
|
* @public
|
|
1654
2617
|
*/
|
|
1655
2618
|
async getEntryPoints(params = {}) {
|
|
@@ -1657,8 +2620,8 @@ class ContactCenter extends _webexCore.WebexPlugin {
|
|
|
1657
2620
|
}
|
|
1658
2621
|
|
|
1659
2622
|
/**
|
|
1660
|
-
* Returns paginated contact service queues
|
|
1661
|
-
*
|
|
2623
|
+
* Returns paginated contact service queues using the Queue service defaults.
|
|
2624
|
+
* Existing request parameters can override those defaults.
|
|
1662
2625
|
* @public
|
|
1663
2626
|
*/
|
|
1664
2627
|
async getQueues(params = {}) {
|