@webex/contact-center 3.12.0-next.16 → 3.12.0-next.161
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 +385 -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 +888 -49
- package/dist/cc.js.map +1 -1
- package/dist/config.js +13 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +38 -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 +185 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +54 -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 +369 -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/WebCallingService.js +10 -2
- package/dist/services/WebCallingService.js.map +1 -1
- 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 +93 -24
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/constants.js +11 -1
- package/dist/services/constants.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +131 -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 +17 -11
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/constants.js +9 -1
- package/dist/services/core/constants.js.map +1 -1
- package/dist/services/core/types.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +19 -2
- 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 -605
- 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 +19 -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/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 +215 -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 +152 -9
- package/dist/types/config.d.ts +13 -0
- package/dist/types/constants.d.ts +36 -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 +48 -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 +67 -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 +240 -114
- package/dist/types/services/constants.d.ts +9 -0
- package/dist/types/services/core/Err.d.ts +2 -0
- package/dist/types/services/core/Utils.d.ts +43 -13
- package/dist/types/services/core/WebexRequest.d.ts +1 -0
- package/dist/types/services/core/constants.d.ts +7 -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 +17 -0
- package/dist/types/services/task/contact.d.ts +4 -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 +738 -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 +1137 -67
- package/src/config.ts +13 -0
- package/src/constants.ts +39 -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 +190 -0
- package/src/metrics/constants.ts +59 -4
- package/src/services/AddressBook.ts +17 -6
- package/src/services/AnswerCallOnWebexService.ts +239 -0
- package/src/services/ApiAiAssistant.ts +494 -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/WebCallingService.ts +16 -2
- 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 +512 -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 +229 -125
- package/src/services/constants.ts +10 -0
- package/src/services/core/Err.ts +1 -0
- package/src/services/core/Utils.ts +151 -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 +797 -0
- package/src/services/core/aqm-reqs.ts +18 -12
- package/src/services/core/constants.ts +8 -0
- package/src/services/core/types.ts +33 -1
- package/src/services/core/websocket/WebSocketManager.ts +28 -2
- 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 +1585 -724
- 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 +18 -0
- package/src/services/task/contact.ts +30 -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 +918 -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 +3553 -107
- 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 +442 -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 +45 -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 +447 -8
- package/test/unit/spec/services/core/WebexRequest.ts +8 -7
- package/test/unit/spec/services/core/aqm-reqs.ts +142 -1
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +129 -53
- 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 +4734 -1769
- 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/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/src/cc.ts
CHANGED
|
@@ -25,6 +25,10 @@ import {
|
|
|
25
25
|
UploadLogsResponse,
|
|
26
26
|
UpdateDeviceTypeResponse,
|
|
27
27
|
GenericError,
|
|
28
|
+
ConfigFlags,
|
|
29
|
+
WellnessBreakEvent,
|
|
30
|
+
WellnessBreakNotificationAction,
|
|
31
|
+
WELLNESS_BREAK_NOTIFICATION_ACTIONS,
|
|
28
32
|
} from './types';
|
|
29
33
|
import {
|
|
30
34
|
READY,
|
|
@@ -37,6 +41,7 @@ import {
|
|
|
37
41
|
UNKNOWN_ERROR,
|
|
38
42
|
MERCURY_DISCONNECTED_SUCCESS,
|
|
39
43
|
METHODS,
|
|
44
|
+
WELLNESS_BREAK_HANDLER,
|
|
40
45
|
} from './constants';
|
|
41
46
|
import {AGENT_STATE_AVAILABLE, AGENT_STATE_AVAILABLE_ID} from './services/config/constants';
|
|
42
47
|
import {AGENT, RTD_SUBSCRIBE_API, SUBSCRIBE_API, WEB_RTC_PREFIX} from './services/constants';
|
|
@@ -44,17 +49,22 @@ import Services from './services';
|
|
|
44
49
|
import WebexRequest from './services/core/WebexRequest';
|
|
45
50
|
import LoggerProxy from './logger-proxy';
|
|
46
51
|
import {StateChange, Logout, StateChangeSuccess, AGENT_EVENTS} from './services/agent/types';
|
|
47
|
-
import {getErrorDetails, isValidDialNumber} from './services/core/Utils';
|
|
52
|
+
import {getErrorDetails, isRecord, isValidDialNumber} from './services/core/Utils';
|
|
48
53
|
import {
|
|
49
54
|
Profile,
|
|
50
55
|
WelcomeEvent,
|
|
51
56
|
CC_EVENTS,
|
|
52
57
|
OutdialAniEntriesResponse,
|
|
53
58
|
OutdialAniParams,
|
|
59
|
+
Entity,
|
|
54
60
|
} from './services/config/types';
|
|
55
61
|
import {ConnectionLostDetails} from './services/core/websocket/types';
|
|
56
62
|
import TaskManager from './services/task/TaskManager';
|
|
57
63
|
import WebCallingService from './services/WebCallingService';
|
|
64
|
+
import AnswerCallOnWebexService from './services/AnswerCallOnWebexService';
|
|
65
|
+
import WebexCrossClientService from './services/WebexCrossClientService';
|
|
66
|
+
import WxAppTelephonyMercurySync from './services/WxAppTelephonyMercurySync';
|
|
67
|
+
import {logWxAppSessionReadiness} from './services/wxAppDiagnosticLogging';
|
|
58
68
|
import {
|
|
59
69
|
ITask,
|
|
60
70
|
TASK_EVENTS,
|
|
@@ -69,6 +79,7 @@ import {EntryPoint} from './services/EntryPoint';
|
|
|
69
79
|
import {AddressBook} from './services/AddressBook';
|
|
70
80
|
import {Queue} from './services/Queue';
|
|
71
81
|
import {ApiAIAssistant} from './services/ApiAiAssistant';
|
|
82
|
+
import {UserPreference} from './services/UserPreference';
|
|
72
83
|
import type {
|
|
73
84
|
EntryPointListResponse,
|
|
74
85
|
EntryPointSearchParams,
|
|
@@ -76,6 +87,13 @@ import type {
|
|
|
76
87
|
ContactServiceQueueSearchParams,
|
|
77
88
|
} from './types';
|
|
78
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Typed allow-list of server-controlled websocket event names this SDK re-emits verbatim.
|
|
92
|
+
* Guards against emitting an arbitrary/unknown `data.type` supplied by the websocket payload.
|
|
93
|
+
* @ignore
|
|
94
|
+
*/
|
|
95
|
+
const CC_EVENT_ALLOW_LIST = new Set<string>(Object.values(CC_EVENTS));
|
|
96
|
+
|
|
79
97
|
/**
|
|
80
98
|
* The main Contact Center plugin class that enables integration with Webex Contact Center.
|
|
81
99
|
*
|
|
@@ -159,7 +177,7 @@ import type {
|
|
|
159
177
|
*
|
|
160
178
|
* // Login with browser-based calling
|
|
161
179
|
* await cc.stationLogin({
|
|
162
|
-
* teamId: profile.teams[0].
|
|
180
|
+
* teamId: profile.teams[0].id,
|
|
163
181
|
* loginOption: 'BROWSER'
|
|
164
182
|
* });
|
|
165
183
|
*
|
|
@@ -230,6 +248,34 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
230
248
|
*/
|
|
231
249
|
private webCallingService: WebCallingService;
|
|
232
250
|
|
|
251
|
+
/**
|
|
252
|
+
* REST telephony for wxApp thick-client answer (WXCC-6026)
|
|
253
|
+
* @private
|
|
254
|
+
*/
|
|
255
|
+
private answerCallOnWebexService: AnswerCallOnWebexService;
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* usersub cross-client state for Webex App toast suppression
|
|
259
|
+
* @private
|
|
260
|
+
*/
|
|
261
|
+
private webexCrossClientService: WebexCrossClientService;
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Mercury telephony mute sync for wxApp thick-client answer
|
|
265
|
+
* @private
|
|
266
|
+
*/
|
|
267
|
+
private wxAppTelephonyMercurySync: WxAppTelephonyMercurySync;
|
|
268
|
+
|
|
269
|
+
/** True when wxApp mute sync opened Mercury (Extension/DN-only profiles). */
|
|
270
|
+
private wxAppMercuryConnectedByCc = false;
|
|
271
|
+
|
|
272
|
+
/** True when wxApp mute sync registered the Webex device. */
|
|
273
|
+
private wxAppDeviceRegisteredByCc = false;
|
|
274
|
+
|
|
275
|
+
private static readonly WXAPP_FALSE_PUBLISH_RETRY_DELAY_MS = 30_000;
|
|
276
|
+
private static readonly WXAPP_FALSE_PUBLISH_MAX_RETRIES = 3;
|
|
277
|
+
private wxAppFalsePublishRetryTimer?: ReturnType<typeof setTimeout>;
|
|
278
|
+
|
|
233
279
|
/**
|
|
234
280
|
* Core service managers for Contact Center operations
|
|
235
281
|
* Includes agent, connection, and configuration services
|
|
@@ -262,6 +308,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
262
308
|
*/
|
|
263
309
|
private metricsManager: MetricsManager;
|
|
264
310
|
|
|
311
|
+
/** Current login/relogin session used for agent-scoped wellness validation. */
|
|
312
|
+
private currentAgentSessionId?: string;
|
|
313
|
+
|
|
314
|
+
/** Registered-session cache for the system WellbeingBreak idle code. */
|
|
315
|
+
private wellbeingBreakIdleCode?: Entity;
|
|
316
|
+
|
|
317
|
+
/** Registration generation that owns the wellness idle-code cache and in-flight lookups. */
|
|
318
|
+
private wellnessRegistrationGeneration = 0;
|
|
319
|
+
|
|
320
|
+
private updateWellnessSession(agentSessionId?: string): void {
|
|
321
|
+
this.currentAgentSessionId = agentSessionId;
|
|
322
|
+
}
|
|
323
|
+
|
|
265
324
|
/**
|
|
266
325
|
* API instance for managing Webex Contact Center entry points
|
|
267
326
|
* Provides functionality to fetch entry points with caching support
|
|
@@ -325,7 +384,38 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
325
384
|
* });
|
|
326
385
|
* ```
|
|
327
386
|
*/
|
|
328
|
-
|
|
387
|
+
public queue: Queue;
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* API instance for managing Webex Contact Center user preferences
|
|
391
|
+
* Provides functionality to get, create, update, and delete user preferences
|
|
392
|
+
* @type {UserPreference}
|
|
393
|
+
* @public
|
|
394
|
+
* @example
|
|
395
|
+
* ```typescript
|
|
396
|
+
* const cc = webex.cc;
|
|
397
|
+
* await cc.register();
|
|
398
|
+
* await cc.stationLogin({ teamId: 'team123', loginOption: 'BROWSER' });
|
|
399
|
+
*
|
|
400
|
+
* // Get user preferences for current user
|
|
401
|
+
* const preferences = await cc.userPreference.getUserPreference();
|
|
402
|
+
*
|
|
403
|
+
* // Create new user preferences
|
|
404
|
+
* const newPrefs = await cc.userPreference.createUserPreference({
|
|
405
|
+
* userId: 'user123',
|
|
406
|
+
* preferences: { e911Reminder: true }
|
|
407
|
+
* });
|
|
408
|
+
*
|
|
409
|
+
* // Update user preferences
|
|
410
|
+
* const updatedPrefs = await cc.userPreference.updateUserPreference('user123', {
|
|
411
|
+
* preferences: { e911Reminder: false }
|
|
412
|
+
* });
|
|
413
|
+
*
|
|
414
|
+
* // Delete user preferences
|
|
415
|
+
* await cc.userPreference.deleteUserPreference('user123');
|
|
416
|
+
* ```
|
|
417
|
+
*/
|
|
418
|
+
public userPreference: UserPreference;
|
|
329
419
|
|
|
330
420
|
/**
|
|
331
421
|
* API instance for AI Assistant operations such as transcript controls.
|
|
@@ -357,6 +447,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
357
447
|
this.$webex.once(READY, () => {
|
|
358
448
|
// @ts-ignore
|
|
359
449
|
this.$config = this.config;
|
|
450
|
+
this.validatePluginConfig();
|
|
360
451
|
|
|
361
452
|
/**
|
|
362
453
|
* This is used for handling the async requests by sending webex.request and wait for corresponding websocket event.
|
|
@@ -369,17 +460,25 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
369
460
|
webex: this.$webex,
|
|
370
461
|
connectionConfig: this.getConnectionConfig(),
|
|
371
462
|
});
|
|
372
|
-
this.services.webSocketManager.on('message', this.handleWebsocketMessage);
|
|
373
463
|
|
|
374
464
|
this.webCallingService = new WebCallingService(this.$webex);
|
|
375
|
-
this.
|
|
465
|
+
this.answerCallOnWebexService = new AnswerCallOnWebexService(this.$webex);
|
|
466
|
+
this.webexCrossClientService = new WebexCrossClientService(this.$webex);
|
|
467
|
+
this.wxAppTelephonyMercurySync = new WxAppTelephonyMercurySync(this.$webex);
|
|
468
|
+
this.apiAIAssistant = new ApiAIAssistant(this.$webex, () => ({
|
|
469
|
+
isWellnessBreakEnabled: this.agentConfig?.isWellnessBreakEnabled === true,
|
|
470
|
+
agentId: this.agentConfig?.agentId,
|
|
471
|
+
agentSessionId: this.currentAgentSessionId,
|
|
472
|
+
}));
|
|
376
473
|
this.metricsManager = MetricsManager.getInstance({webex: this.$webex});
|
|
377
474
|
this.taskManager = TaskManager.getTaskManager(
|
|
378
475
|
this.apiAIAssistant,
|
|
379
476
|
this.services.contact,
|
|
380
477
|
this.webCallingService,
|
|
381
|
-
this.services.webSocketManager
|
|
478
|
+
this.services.webSocketManager,
|
|
479
|
+
this.services.rtdWebSocketManager
|
|
382
480
|
);
|
|
481
|
+
this.taskManager.setAnswerCallOnWebexService(this.answerCallOnWebexService);
|
|
383
482
|
this.incomingTaskListener();
|
|
384
483
|
|
|
385
484
|
// Initialize API instances
|
|
@@ -387,6 +486,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
387
486
|
this.entryPoint = new EntryPoint(this.$webex);
|
|
388
487
|
this.addressBook = new AddressBook(this.$webex, () => this.agentConfig?.addressBookId);
|
|
389
488
|
this.queue = new Queue(this.$webex);
|
|
489
|
+
this.userPreference = new UserPreference(this.$webex, () => this.agentConfig?.agentId);
|
|
390
490
|
LoggerProxy.initialize(this.$webex.logger);
|
|
391
491
|
});
|
|
392
492
|
}
|
|
@@ -411,6 +511,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
411
511
|
this.trigger(TASK_EVENTS.TASK_HYDRATE, task);
|
|
412
512
|
};
|
|
413
513
|
|
|
514
|
+
/**
|
|
515
|
+
* Handles multi-login hydrate events for SDK instances without Mobius registration
|
|
516
|
+
* @private
|
|
517
|
+
* @param {ITask} task The task object associated with the multi-login hydrate
|
|
518
|
+
*/
|
|
519
|
+
private handleTaskMultiLoginHydrate = (task: ITask) => {
|
|
520
|
+
// @ts-ignore
|
|
521
|
+
this.trigger(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, task);
|
|
522
|
+
};
|
|
523
|
+
|
|
414
524
|
/**
|
|
415
525
|
* Handles task merged events when tasks are combined eg: EPDN merge/transfer
|
|
416
526
|
* @private
|
|
@@ -431,8 +541,127 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
431
541
|
this.trigger(TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, task);
|
|
432
542
|
};
|
|
433
543
|
|
|
434
|
-
private
|
|
435
|
-
this.
|
|
544
|
+
private trackInvalidWellnessEvent(reason: string): void {
|
|
545
|
+
this.metricsManager.trackEvent(
|
|
546
|
+
METRIC_EVENT_NAMES.AI_ASSISTANT_WELLNESS_EVENT_INVALID,
|
|
547
|
+
{reason},
|
|
548
|
+
['operational']
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
private normalizeWellnessBreakEvent(payload: Record<string, unknown>): WellnessBreakEvent | null {
|
|
553
|
+
if (this.agentConfig?.isWellnessBreakEnabled !== true) {
|
|
554
|
+
this.trackInvalidWellnessEvent('wellness_disabled');
|
|
555
|
+
|
|
556
|
+
return null;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const notificationData = isRecord(payload.data) ? payload.data : null;
|
|
560
|
+
const notificationDetails =
|
|
561
|
+
notificationData && isRecord(notificationData.notifDetails)
|
|
562
|
+
? notificationData.notifDetails
|
|
563
|
+
: null;
|
|
564
|
+
const sessionData =
|
|
565
|
+
notificationData && isRecord(notificationData.data) ? notificationData.data : null;
|
|
566
|
+
const actionEvent = notificationDetails?.actionEvent;
|
|
567
|
+
|
|
568
|
+
if (
|
|
569
|
+
payload.type !== WELLNESS_BREAK_HANDLER ||
|
|
570
|
+
notificationData?.notifType !== WELLNESS_BREAK_HANDLER ||
|
|
571
|
+
typeof actionEvent !== 'string'
|
|
572
|
+
) {
|
|
573
|
+
this.trackInvalidWellnessEvent('malformed_wellness_event');
|
|
574
|
+
|
|
575
|
+
return null;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
if (
|
|
579
|
+
!Object.values(WELLNESS_BREAK_NOTIFICATION_ACTIONS).includes(
|
|
580
|
+
actionEvent as WellnessBreakNotificationAction
|
|
581
|
+
)
|
|
582
|
+
) {
|
|
583
|
+
this.trackInvalidWellnessEvent('unknown_wellness_action');
|
|
584
|
+
|
|
585
|
+
return null;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
const agentId = notificationData.agentId;
|
|
589
|
+
const eventOrgId = notificationData.orgId;
|
|
590
|
+
const sessionOrgId = sessionData?.orgId;
|
|
591
|
+
const envelopeOrgId = payload.orgId;
|
|
592
|
+
const agentSessionId = sessionData?.agentSessionId;
|
|
593
|
+
const currentOrgId = this.$webex.credentials.getOrgId();
|
|
594
|
+
|
|
595
|
+
if (
|
|
596
|
+
typeof agentId !== 'string' ||
|
|
597
|
+
typeof eventOrgId !== 'string' ||
|
|
598
|
+
typeof sessionOrgId !== 'string' ||
|
|
599
|
+
typeof envelopeOrgId !== 'string' ||
|
|
600
|
+
typeof agentSessionId !== 'string' ||
|
|
601
|
+
!agentId ||
|
|
602
|
+
!eventOrgId ||
|
|
603
|
+
!sessionOrgId ||
|
|
604
|
+
!envelopeOrgId ||
|
|
605
|
+
!agentSessionId
|
|
606
|
+
) {
|
|
607
|
+
this.trackInvalidWellnessEvent('missing_wellness_identity');
|
|
608
|
+
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
if (
|
|
613
|
+
agentId !== this.agentConfig?.agentId ||
|
|
614
|
+
eventOrgId !== currentOrgId ||
|
|
615
|
+
sessionOrgId !== currentOrgId ||
|
|
616
|
+
envelopeOrgId !== currentOrgId
|
|
617
|
+
) {
|
|
618
|
+
this.trackInvalidWellnessEvent('mismatched_wellness_identity');
|
|
619
|
+
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const interactionValue = sessionData.interactionId ?? sessionData.InteractionId;
|
|
624
|
+
const actionText = notificationDetails.actionText;
|
|
625
|
+
const trackingId = payload.trackingId;
|
|
626
|
+
|
|
627
|
+
return {
|
|
628
|
+
agentId,
|
|
629
|
+
orgId: currentOrgId,
|
|
630
|
+
agentSessionId,
|
|
631
|
+
actionEvent: actionEvent as WellnessBreakNotificationAction,
|
|
632
|
+
...(typeof actionText === 'string' ? {actionText} : {}),
|
|
633
|
+
...(typeof interactionValue === 'string' ? {interactionId: interactionValue} : {}),
|
|
634
|
+
...(typeof trackingId === 'string' ? {trackingId} : {}),
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
private handleWellnessBreakWebsocketPayload(payload: Record<string, unknown>): boolean {
|
|
639
|
+
if (payload.type !== WELLNESS_BREAK_HANDLER) {
|
|
640
|
+
return false;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const wellnessEvent = this.normalizeWellnessBreakEvent(payload);
|
|
644
|
+
if (wellnessEvent) {
|
|
645
|
+
// @ts-ignore - WebexPlugin emit is available at runtime but absent from its declaration.
|
|
646
|
+
this.emit(CC_EVENTS.WELLNESS_BREAK, wellnessEvent);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
return true;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
private handleRTDWebsocketMessage = (event: string) => {
|
|
653
|
+
let payload: unknown;
|
|
654
|
+
try {
|
|
655
|
+
payload = JSON.parse(event);
|
|
656
|
+
} catch {
|
|
657
|
+
// TaskManager retains its existing malformed RTD message handling.
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
if (isRecord(payload) && this.handleWellnessBreakWebsocketPayload(payload)) {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
this.taskManager.handleRealtimeWebsocketEvent(event);
|
|
436
665
|
};
|
|
437
666
|
|
|
438
667
|
/**
|
|
@@ -443,6 +672,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
443
672
|
private incomingTaskListener() {
|
|
444
673
|
this.taskManager.on(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
445
674
|
this.taskManager.on(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
675
|
+
this.taskManager.on(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
|
|
446
676
|
this.taskManager.on(TASK_EVENTS.TASK_MERGED, this.handleTaskMerged);
|
|
447
677
|
this.taskManager.on(
|
|
448
678
|
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
@@ -478,16 +708,18 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
478
708
|
*
|
|
479
709
|
* console.log('Agent ID:', profile.agentId);
|
|
480
710
|
* console.log('Default DN:', profile.defaultDn);
|
|
481
|
-
* console.log('Teams:', profile.teams.map(t => t.
|
|
711
|
+
* console.log('Teams:', profile.teams.map(t => t.id));
|
|
482
712
|
* console.log('WebRTC Enabled:', profile.webRtcEnabled);
|
|
483
713
|
* console.log('Supported Login Options:', profile.loginVoiceOptions);
|
|
484
714
|
*
|
|
485
715
|
* // Now you can proceed with station login, state changes, etc.
|
|
486
|
-
* await cc.stationLogin({ teamId: profile.teams[0].
|
|
716
|
+
* await cc.stationLogin({ teamId: profile.teams[0].id, loginOption: 'BROWSER' });
|
|
487
717
|
* ```
|
|
488
718
|
*/
|
|
489
719
|
public async register(): Promise<Profile> {
|
|
490
|
-
|
|
720
|
+
this.wellnessRegistrationGeneration += 1;
|
|
721
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
722
|
+
LoggerProxy.log('Starting CC SDK registration', {
|
|
491
723
|
module: CC_FILE,
|
|
492
724
|
method: METHODS.REGISTER,
|
|
493
725
|
});
|
|
@@ -497,6 +729,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
497
729
|
METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_FAILED,
|
|
498
730
|
]);
|
|
499
731
|
this.setupEventListeners();
|
|
732
|
+
this.services.webSocketManager.on('message', this.handleWebsocketMessage);
|
|
500
733
|
|
|
501
734
|
const resp = await this.connectWebsocket();
|
|
502
735
|
// Ensure 'dn' is always populated from 'defaultDn'
|
|
@@ -515,7 +748,12 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
515
748
|
method: METHODS.REGISTER,
|
|
516
749
|
});
|
|
517
750
|
|
|
518
|
-
|
|
751
|
+
// Registration can silently restore a station session without emitting a
|
|
752
|
+
// relogin event. Project the active session for consumers that must
|
|
753
|
+
// recover session-scoped state after a page refresh.
|
|
754
|
+
return resp.isAgentLoggedIn && this.currentAgentSessionId
|
|
755
|
+
? {...resp, agentSessionId: this.currentAgentSessionId}
|
|
756
|
+
: resp;
|
|
519
757
|
} catch (error) {
|
|
520
758
|
this.metricsManager.trackEvent(
|
|
521
759
|
METRIC_EVENT_NAMES.WEBSOCKET_REGISTER_FAILED,
|
|
@@ -568,14 +806,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
568
806
|
|
|
569
807
|
*/
|
|
570
808
|
public async deregister(): Promise<void> {
|
|
809
|
+
this.wellnessRegistrationGeneration += 1;
|
|
810
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
811
|
+
this.updateWellnessSession();
|
|
571
812
|
try {
|
|
572
813
|
this.metricsManager.timeEvent([
|
|
573
814
|
METRIC_EVENT_NAMES.WEBSOCKET_DEREGISTER_SUCCESS,
|
|
574
815
|
METRIC_EVENT_NAMES.WEBSOCKET_DEREGISTER_FAIL,
|
|
575
816
|
]);
|
|
576
817
|
|
|
818
|
+
this.taskManager.clearAISummaryState();
|
|
577
819
|
this.taskManager.off(TASK_EVENTS.TASK_INCOMING, this.handleIncomingTask);
|
|
578
820
|
this.taskManager.off(TASK_EVENTS.TASK_HYDRATE, this.handleTaskHydrate);
|
|
821
|
+
this.taskManager.off(TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, this.handleTaskMultiLoginHydrate);
|
|
579
822
|
this.taskManager.off(
|
|
580
823
|
TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION,
|
|
581
824
|
this.handleCampaignPreviewReservation
|
|
@@ -586,6 +829,10 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
586
829
|
this.services.rtdWebSocketManager.off('message', this.handleRTDWebsocketMessage);
|
|
587
830
|
this.services.connectionService.off('connectionLost', this.handleConnectionLost);
|
|
588
831
|
|
|
832
|
+
const {publishError: wxAppPublishError} = await this.teardownWxAppLocalState({
|
|
833
|
+
clearInitFlag: true,
|
|
834
|
+
});
|
|
835
|
+
|
|
589
836
|
if (
|
|
590
837
|
this.agentConfig.webRtcEnabled &&
|
|
591
838
|
this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER)
|
|
@@ -606,13 +853,22 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
606
853
|
if (!this.services.webSocketManager.isSocketClosed) {
|
|
607
854
|
this.services.webSocketManager.close(false, 'Unregistering the SDK');
|
|
608
855
|
}
|
|
856
|
+
if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
|
|
857
|
+
this.services.rtdWebSocketManager.close(false, 'Unregistering the SDK');
|
|
858
|
+
}
|
|
609
859
|
|
|
610
860
|
if (this.services.rtdWebSocketManager && !this.services.rtdWebSocketManager.isSocketClosed) {
|
|
611
861
|
this.services.rtdWebSocketManager.close(false, 'Unregistering the RTD websocket');
|
|
612
862
|
}
|
|
613
863
|
|
|
614
864
|
// Clear any cached agent configuration
|
|
865
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
615
866
|
this.agentConfig = null;
|
|
867
|
+
this.updateWellnessSession();
|
|
868
|
+
|
|
869
|
+
if (wxAppPublishError) {
|
|
870
|
+
throw wxAppPublishError;
|
|
871
|
+
}
|
|
616
872
|
|
|
617
873
|
LoggerProxy.log('Deregistered successfully', {
|
|
618
874
|
module: CC_FILE,
|
|
@@ -676,6 +932,9 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
676
932
|
* ```
|
|
677
933
|
*/
|
|
678
934
|
public async getBuddyAgents(data: BuddyAgents): Promise<BuddyAgentsResponse> {
|
|
935
|
+
const {action, state, mediaType = 'telephony'} = data;
|
|
936
|
+
const buddyAgentState = state ?? (action === 'Transfer' ? AGENT_STATE_AVAILABLE : undefined);
|
|
937
|
+
|
|
679
938
|
LoggerProxy.info('Fetching buddy agents', {
|
|
680
939
|
module: CC_FILE,
|
|
681
940
|
method: METHODS.GET_BUDDY_AGENTS,
|
|
@@ -686,15 +945,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
686
945
|
METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_FAILED,
|
|
687
946
|
]);
|
|
688
947
|
const resp = await this.services.agent.buddyAgents({
|
|
689
|
-
data: {
|
|
948
|
+
data: {
|
|
949
|
+
agentProfileId: this.agentConfig.agentProfileID,
|
|
950
|
+
mediaType,
|
|
951
|
+
...(buddyAgentState ? {state: buddyAgentState} : {}),
|
|
952
|
+
},
|
|
690
953
|
});
|
|
691
954
|
|
|
692
955
|
this.metricsManager.trackEvent(
|
|
693
956
|
METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_SUCCESS,
|
|
694
957
|
{
|
|
695
958
|
...MetricsManager.getCommonTrackingFieldForAQMResponse(resp),
|
|
696
|
-
mediaType
|
|
697
|
-
buddyAgentState
|
|
959
|
+
mediaType,
|
|
960
|
+
buddyAgentState,
|
|
698
961
|
buddyAgentCount: resp.data.agentList.length,
|
|
699
962
|
},
|
|
700
963
|
['operational']
|
|
@@ -714,8 +977,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
714
977
|
METRIC_EVENT_NAMES.FETCH_BUDDY_AGENTS_FAILED,
|
|
715
978
|
{
|
|
716
979
|
...MetricsManager.getCommonTrackingFieldForAQMResponseFailed(failureResp),
|
|
717
|
-
mediaType
|
|
718
|
-
buddyAgentState
|
|
980
|
+
mediaType,
|
|
981
|
+
buddyAgentState,
|
|
719
982
|
},
|
|
720
983
|
['operational']
|
|
721
984
|
);
|
|
@@ -731,7 +994,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
731
994
|
* @private
|
|
732
995
|
*/
|
|
733
996
|
private async connectWebsocket() {
|
|
734
|
-
LoggerProxy.
|
|
997
|
+
LoggerProxy.log('Connecting to websocket', {
|
|
735
998
|
module: CC_FILE,
|
|
736
999
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
737
1000
|
});
|
|
@@ -751,19 +1014,49 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
751
1014
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
752
1015
|
});
|
|
753
1016
|
|
|
1017
|
+
const configFlags: ConfigFlags = {
|
|
1018
|
+
isEndTaskEnabled: this.agentConfig.isEndTaskEnabled,
|
|
1019
|
+
isEndConsultEnabled: this.agentConfig.isEndConsultEnabled,
|
|
1020
|
+
webRtcEnabled: this.agentConfig.webRtcEnabled,
|
|
1021
|
+
autoWrapup: this.agentConfig.wrapUpData?.wrapUpProps?.autoWrapup ?? false,
|
|
1022
|
+
aiFeature: this.agentConfig.aiFeature,
|
|
1023
|
+
consultTransfer: {
|
|
1024
|
+
allowConsultToQueue: this.agentConfig.allowConsultToQueue,
|
|
1025
|
+
accessQueue: this.agentConfig.accessQueue,
|
|
1026
|
+
accessEntryPoint: this.agentConfig.accessEntryPoint,
|
|
1027
|
+
accessBuddyTeam: this.agentConfig.accessBuddyTeam,
|
|
1028
|
+
},
|
|
1029
|
+
enableWxBetterTogether: this.isWxBetterTogetherEnabled(),
|
|
1030
|
+
getWxAppUsersubPublished: () => this.webexCrossClientService.isAnswerCallsStateActive(),
|
|
1031
|
+
};
|
|
1032
|
+
this.taskManager.setConfigFlags(configFlags);
|
|
754
1033
|
// TODO: Make profile a singleton to make it available throughout app/sdk so we dont need to inject info everywhere
|
|
755
1034
|
this.taskManager.setWrapupData(this.agentConfig.wrapUpData);
|
|
756
1035
|
this.taskManager.setAgentId(this.agentConfig.agentId);
|
|
1036
|
+
this.taskManager.setAgentName(this.agentConfig.agentName);
|
|
757
1037
|
this.taskManager.setWebRtcEnabled(this.agentConfig.webRtcEnabled);
|
|
758
1038
|
this.apiAIAssistant.setAIFeatureFlags(this.agentConfig.aiFeature);
|
|
1039
|
+
this.wellbeingBreakIdleCode = undefined;
|
|
1040
|
+
this.updateWellnessSession();
|
|
759
1041
|
|
|
760
|
-
|
|
1042
|
+
/**
|
|
1043
|
+
* RTD websocket currently supports realtime transcripts and suggested responses.
|
|
1044
|
+
* Extend this condition when additional AI RTD features are introduced.
|
|
1045
|
+
* Wellness break notifications also arrive on this websocket.
|
|
1046
|
+
*/
|
|
1047
|
+
if (
|
|
1048
|
+
this.agentConfig.aiFeature?.realtimeTranscripts?.enable ||
|
|
1049
|
+
this.agentConfig.aiFeature?.suggestedResponses?.enable ||
|
|
1050
|
+
this.agentConfig.aiFeature?.generatedSummaries?.wrapUpSummariesEnabled === true ||
|
|
1051
|
+
this.agentConfig.aiFeature?.generatedSummaries?.consultTransferSummariesEnabled === true ||
|
|
1052
|
+
this.agentConfig.isWellnessBreakEnabled
|
|
1053
|
+
) {
|
|
761
1054
|
LoggerProxy.info('Connecting to RTD websocket', {
|
|
762
1055
|
module: CC_FILE,
|
|
763
1056
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
764
1057
|
});
|
|
765
1058
|
|
|
766
|
-
|
|
1059
|
+
this.services.rtdWebSocketManager
|
|
767
1060
|
.initWebSocket({
|
|
768
1061
|
body: this.getConnectionConfig(),
|
|
769
1062
|
resource: RTD_SUBSCRIBE_API,
|
|
@@ -776,7 +1069,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
776
1069
|
this.services.rtdWebSocketManager.on('message', this.handleRTDWebsocketMessage);
|
|
777
1070
|
})
|
|
778
1071
|
.catch((error) => {
|
|
779
|
-
LoggerProxy.error(`Error
|
|
1072
|
+
LoggerProxy.error(`Error connecting to RTD websocket ${error}`, {
|
|
780
1073
|
module: CC_FILE,
|
|
781
1074
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
782
1075
|
});
|
|
@@ -785,7 +1078,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
785
1078
|
|
|
786
1079
|
if (
|
|
787
1080
|
this.agentConfig.webRtcEnabled &&
|
|
788
|
-
this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER)
|
|
1081
|
+
this.agentConfig.loginVoiceOptions.includes(LoginOption.BROWSER) &&
|
|
1082
|
+
!this.isWebRTCRegistrationDisabled()
|
|
789
1083
|
) {
|
|
790
1084
|
try {
|
|
791
1085
|
await this.$webex.internal.mercury.connect();
|
|
@@ -799,14 +1093,28 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
799
1093
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
800
1094
|
});
|
|
801
1095
|
}
|
|
1096
|
+
} else if (this.isWebRTCRegistrationDisabled()) {
|
|
1097
|
+
LoggerProxy.info(
|
|
1098
|
+
'Skipping Mobius registration because disableWebRTCRegistration is enabled',
|
|
1099
|
+
{
|
|
1100
|
+
module: CC_FILE,
|
|
1101
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
1102
|
+
}
|
|
1103
|
+
);
|
|
802
1104
|
}
|
|
803
1105
|
|
|
804
1106
|
if (this.$config && this.$config.allowAutomatedRelogin) {
|
|
805
1107
|
await this.silentRelogin();
|
|
1108
|
+
} else {
|
|
1109
|
+
LoggerProxy.log('Skipping silent relogin: allowAutomatedRelogin is disabled', {
|
|
1110
|
+
module: CC_FILE,
|
|
1111
|
+
method: METHODS.CONNECT_WEBSOCKET,
|
|
1112
|
+
});
|
|
806
1113
|
}
|
|
807
1114
|
|
|
808
1115
|
return this.agentConfig;
|
|
809
1116
|
} catch (error) {
|
|
1117
|
+
this.updateWellnessSession();
|
|
810
1118
|
LoggerProxy.error(`Error during register: ${error}`, {
|
|
811
1119
|
module: CC_FILE,
|
|
812
1120
|
method: METHODS.CONNECT_WEBSOCKET,
|
|
@@ -845,36 +1153,48 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
845
1153
|
* ```
|
|
846
1154
|
*/
|
|
847
1155
|
public async stationLogin(data: AgentLogin): Promise<StationLoginResponse> {
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
method: METHODS.STATION_LOGIN,
|
|
851
|
-
});
|
|
1156
|
+
const loggerContext = {module: CC_FILE, method: METHODS.STATION_LOGIN};
|
|
1157
|
+
|
|
852
1158
|
try {
|
|
1159
|
+
LoggerProxy.log(
|
|
1160
|
+
`Starting agent station login | loginOption: ${data?.loginOption} teamId: ${data?.teamId}`,
|
|
1161
|
+
loggerContext
|
|
1162
|
+
);
|
|
853
1163
|
this.metricsManager.timeEvent([
|
|
854
1164
|
METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
|
|
855
1165
|
METRIC_EVENT_NAMES.STATION_LOGIN_FAILED,
|
|
856
1166
|
]);
|
|
857
1167
|
|
|
858
1168
|
const dialPlanEntries = this.agentConfig?.dialPlan?.dialPlanEntity ?? [];
|
|
859
|
-
if (
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
error.details = {data: {reason: 'INVALID_DIAL_NUMBER'}} as Failure;
|
|
1169
|
+
if (data.loginOption === LoginOption.AGENT_DN) {
|
|
1170
|
+
LoggerProxy.log(
|
|
1171
|
+
`Validating dial number | dialPlanEnabled: ${!!this.agentConfig
|
|
1172
|
+
?.dialPlan} | dialPlanEntryCount: ${dialPlanEntries.length}`,
|
|
1173
|
+
loggerContext
|
|
1174
|
+
);
|
|
866
1175
|
|
|
867
|
-
|
|
1176
|
+
if (!isValidDialNumber(data.dialNumber, dialPlanEntries)) {
|
|
1177
|
+
LoggerProxy.log(
|
|
1178
|
+
`Dial number validation failed | dialNumber: ${data.dialNumber} | dialPlanEntryCount: ${dialPlanEntries.length}`,
|
|
1179
|
+
loggerContext
|
|
1180
|
+
);
|
|
1181
|
+
const error = new Error('INVALID_DIAL_NUMBER');
|
|
1182
|
+
// @ts-ignore - adding custom key to the error object
|
|
1183
|
+
error.details = {data: {reason: 'INVALID_DIAL_NUMBER'}} as Failure;
|
|
1184
|
+
|
|
1185
|
+
throw error;
|
|
1186
|
+
}
|
|
868
1187
|
}
|
|
869
1188
|
|
|
1189
|
+
const deviceId = this.getDeviceId(data.loginOption, data.dialNumber);
|
|
870
1190
|
const loginResponse = await this.services.agent.stationLogin({
|
|
871
1191
|
data: {
|
|
872
|
-
|
|
873
|
-
|
|
1192
|
+
// BROWSER DN must be webrtc-{agentId} so VPOP/RTMS do not treat a bare UUID as PSTN.
|
|
1193
|
+
dialNumber: data.loginOption === LoginOption.BROWSER ? deviceId : data.dialNumber,
|
|
874
1194
|
teamId: data.teamId,
|
|
875
1195
|
deviceType: data.loginOption,
|
|
876
1196
|
isExtension: data.loginOption === LoginOption.EXTENSION,
|
|
877
|
-
deviceId
|
|
1197
|
+
deviceId,
|
|
878
1198
|
roles: [AGENT],
|
|
879
1199
|
teamName: EMPTY_STRING,
|
|
880
1200
|
siteId: EMPTY_STRING,
|
|
@@ -883,12 +1203,29 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
883
1203
|
},
|
|
884
1204
|
});
|
|
885
1205
|
|
|
886
|
-
if (
|
|
1206
|
+
if (
|
|
1207
|
+
this.agentConfig.webRtcEnabled &&
|
|
1208
|
+
data.loginOption === LoginOption.BROWSER &&
|
|
1209
|
+
!this.isWebRTCRegistrationDisabled()
|
|
1210
|
+
) {
|
|
887
1211
|
await this.webCallingService.registerWebCallingLine();
|
|
1212
|
+
} else if (
|
|
1213
|
+
this.agentConfig.webRtcEnabled &&
|
|
1214
|
+
data.loginOption === LoginOption.BROWSER &&
|
|
1215
|
+
this.isWebRTCRegistrationDisabled()
|
|
1216
|
+
) {
|
|
1217
|
+
LoggerProxy.info(
|
|
1218
|
+
'Skipping web calling line registration because disableWebRTCRegistration is enabled',
|
|
1219
|
+
{
|
|
1220
|
+
module: CC_FILE,
|
|
1221
|
+
method: METHODS.STATION_LOGIN,
|
|
1222
|
+
}
|
|
1223
|
+
);
|
|
888
1224
|
}
|
|
889
1225
|
|
|
890
1226
|
const resp = await loginResponse;
|
|
891
1227
|
const {channelsMap, ...loginData} = resp.data;
|
|
1228
|
+
this.updateWellnessSession(resp.data.agentSessionId);
|
|
892
1229
|
this.agentConfig.currentTeamId = resp.data.teamId;
|
|
893
1230
|
const response = {
|
|
894
1231
|
...loginData,
|
|
@@ -902,6 +1239,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
902
1239
|
};
|
|
903
1240
|
|
|
904
1241
|
this.webCallingService.setLoginOption(data.loginOption);
|
|
1242
|
+
if (this.agentConfig) {
|
|
1243
|
+
this.agentConfig.deviceType = data.loginOption;
|
|
1244
|
+
if (
|
|
1245
|
+
data.loginOption === LoginOption.AGENT_DN ||
|
|
1246
|
+
data.loginOption === LoginOption.EXTENSION
|
|
1247
|
+
) {
|
|
1248
|
+
this.agentConfig.defaultDn = data.dialNumber;
|
|
1249
|
+
this.agentConfig.dn = data.dialNumber;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
905
1252
|
this.metricsManager.trackEvent(
|
|
906
1253
|
METRIC_EVENT_NAMES.STATION_LOGIN_SUCCESS,
|
|
907
1254
|
{
|
|
@@ -923,6 +1270,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
923
1270
|
}
|
|
924
1271
|
);
|
|
925
1272
|
|
|
1273
|
+
await this.ensureWxAppPostStationLogin();
|
|
1274
|
+
|
|
926
1275
|
return response;
|
|
927
1276
|
} catch (error) {
|
|
928
1277
|
const failure = error.details as Failure;
|
|
@@ -991,6 +1340,9 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
991
1340
|
this.webCallingService.deregisterWebCallingLine();
|
|
992
1341
|
}
|
|
993
1342
|
|
|
1343
|
+
await this.teardownWxAppLocalState();
|
|
1344
|
+
this.updateWellnessSession();
|
|
1345
|
+
|
|
994
1346
|
LoggerProxy.log(`Agent station logout completed successfully`, {
|
|
995
1347
|
module: CC_FILE,
|
|
996
1348
|
method: METHODS.STATION_LOGOUT,
|
|
@@ -1091,7 +1443,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1091
1443
|
{
|
|
1092
1444
|
...MetricsManager.getCommonTrackingFieldForAQMResponse(agentStatusResponse),
|
|
1093
1445
|
requestedState: data.state,
|
|
1094
|
-
teamId: this.agentConfig?.teams[0]?.
|
|
1446
|
+
teamId: this.agentConfig?.teams[0]?.id ?? EMPTY_STRING,
|
|
1095
1447
|
status: agentStatusResponse.data?.status,
|
|
1096
1448
|
subStatus: agentStatusResponse.data?.subStatus,
|
|
1097
1449
|
auxCodeId: data.auxCodeId,
|
|
@@ -1127,6 +1479,77 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1127
1479
|
}
|
|
1128
1480
|
}
|
|
1129
1481
|
|
|
1482
|
+
/**
|
|
1483
|
+
* Returns the system-managed `WellbeingBreak` idle code for the current registration.
|
|
1484
|
+
* The value is fetched outside Desktop Profile filtering and cached until deregistration.
|
|
1485
|
+
* @returns The normalized system idle-code entity
|
|
1486
|
+
* @throws Structured Contact Center error when wellness is disabled or the code is unavailable
|
|
1487
|
+
* @example
|
|
1488
|
+
* const idleCode = await webex.cc.getWellbeingBreakIdleCode();
|
|
1489
|
+
* @public
|
|
1490
|
+
*/
|
|
1491
|
+
public async getWellbeingBreakIdleCode(): Promise<Entity> {
|
|
1492
|
+
const method = METHODS.GET_WELLBEING_BREAK_IDLE_CODE;
|
|
1493
|
+
try {
|
|
1494
|
+
if (!this.agentConfig?.isWellnessBreakEnabled) {
|
|
1495
|
+
const validationError = new Error('WELLNESS_BREAK_NOT_ENABLED') as GenericError;
|
|
1496
|
+
validationError.details = {
|
|
1497
|
+
type: 'SDK_VALIDATION_ERROR',
|
|
1498
|
+
orgId: this.$webex.credentials.getOrgId(),
|
|
1499
|
+
trackingId: EMPTY_STRING,
|
|
1500
|
+
data: {reason: 'WELLNESS_BREAK_NOT_ENABLED'},
|
|
1501
|
+
};
|
|
1502
|
+
throw validationError;
|
|
1503
|
+
}
|
|
1504
|
+
|
|
1505
|
+
if (this.wellbeingBreakIdleCode) {
|
|
1506
|
+
return this.wellbeingBreakIdleCode;
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
this.metricsManager.timeEvent([
|
|
1510
|
+
METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS,
|
|
1511
|
+
METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED,
|
|
1512
|
+
]);
|
|
1513
|
+
const registrationGeneration = this.wellnessRegistrationGeneration;
|
|
1514
|
+
const orgId = this.$webex.credentials.getOrgId();
|
|
1515
|
+
const idleCode = await this.services.config.getWellbeingBreakIdleCode(orgId);
|
|
1516
|
+
|
|
1517
|
+
if (
|
|
1518
|
+
registrationGeneration !== this.wellnessRegistrationGeneration ||
|
|
1519
|
+
orgId !== this.$webex.credentials.getOrgId() ||
|
|
1520
|
+
!this.agentConfig?.isWellnessBreakEnabled
|
|
1521
|
+
) {
|
|
1522
|
+
const staleRegistrationError = new Error(
|
|
1523
|
+
'WELLNESS_BREAK_REGISTRATION_CHANGED'
|
|
1524
|
+
) as GenericError;
|
|
1525
|
+
staleRegistrationError.details = {
|
|
1526
|
+
type: 'SDK_VALIDATION_ERROR',
|
|
1527
|
+
orgId,
|
|
1528
|
+
trackingId: EMPTY_STRING,
|
|
1529
|
+
data: {reason: 'WELLNESS_BREAK_REGISTRATION_CHANGED'},
|
|
1530
|
+
};
|
|
1531
|
+
throw staleRegistrationError;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
this.wellbeingBreakIdleCode = idleCode;
|
|
1535
|
+
this.metricsManager.trackEvent(
|
|
1536
|
+
METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_SUCCESS,
|
|
1537
|
+
{},
|
|
1538
|
+
['operational']
|
|
1539
|
+
);
|
|
1540
|
+
|
|
1541
|
+
return idleCode;
|
|
1542
|
+
} catch (error) {
|
|
1543
|
+
this.metricsManager.trackEvent(
|
|
1544
|
+
METRIC_EVENT_NAMES.WELLBEING_BREAK_IDLE_CODE_FETCH_FAILED,
|
|
1545
|
+
{reason: error instanceof Error ? error.message : UNKNOWN_ERROR},
|
|
1546
|
+
['operational']
|
|
1547
|
+
);
|
|
1548
|
+
const {error: detailedError} = getErrorDetails(error, method, CC_FILE);
|
|
1549
|
+
throw detailedError;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1130
1553
|
/**
|
|
1131
1554
|
* Processes incoming websocket messages and emits corresponding events
|
|
1132
1555
|
* Handles various event types including agent state changes, login events,
|
|
@@ -1136,8 +1559,14 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1136
1559
|
*/
|
|
1137
1560
|
private handleWebsocketMessage = (event: string) => {
|
|
1138
1561
|
const eventData = JSON.parse(event);
|
|
1139
|
-
// Re-emit all the events related to agent except keep-alives
|
|
1140
|
-
|
|
1562
|
+
// Re-emit all the events related to agent except keep-alives. Only event names present in
|
|
1563
|
+
// the typed CC_EVENTS allow-list are emitted; unknown/server-injected types are ignored.
|
|
1564
|
+
if (
|
|
1565
|
+
!eventData.keepalive &&
|
|
1566
|
+
eventData.data &&
|
|
1567
|
+
eventData.data.type &&
|
|
1568
|
+
CC_EVENT_ALLOW_LIST.has(eventData.data.type)
|
|
1569
|
+
) {
|
|
1141
1570
|
// @ts-ignore
|
|
1142
1571
|
this.emit(eventData.data.type, eventData.data);
|
|
1143
1572
|
}
|
|
@@ -1165,8 +1594,19 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1165
1594
|
]);
|
|
1166
1595
|
}
|
|
1167
1596
|
|
|
1597
|
+
if (this.handleWellnessBreakWebsocketPayload(eventData)) {
|
|
1598
|
+
return;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1168
1601
|
switch (eventData.type) {
|
|
1169
1602
|
case CC_EVENTS.AGENT_MULTI_LOGIN:
|
|
1603
|
+
if (
|
|
1604
|
+
eventData.data?.type === 'AgentMultiLoginCloseSession' &&
|
|
1605
|
+
(!eventData.data.agentSessionId ||
|
|
1606
|
+
eventData.data.agentSessionId === this.currentAgentSessionId)
|
|
1607
|
+
) {
|
|
1608
|
+
this.updateWellnessSession();
|
|
1609
|
+
}
|
|
1170
1610
|
// @ts-ignore
|
|
1171
1611
|
this.emit(AGENT_EVENTS.AGENT_MULTI_LOGIN, eventData.data);
|
|
1172
1612
|
break;
|
|
@@ -1185,6 +1625,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1185
1625
|
switch (eventData.data.type) {
|
|
1186
1626
|
case CC_EVENTS.AGENT_STATION_LOGIN_SUCCESS: {
|
|
1187
1627
|
const {channelsMap, ...loginData} = eventData.data;
|
|
1628
|
+
this.updateWellnessSession(loginData.agentSessionId);
|
|
1188
1629
|
const stationLoginData = {
|
|
1189
1630
|
...loginData,
|
|
1190
1631
|
mmProfile: {
|
|
@@ -1200,23 +1641,23 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1200
1641
|
this.emit(AGENT_EVENTS.AGENT_STATION_LOGIN_SUCCESS, stationLoginData);
|
|
1201
1642
|
break;
|
|
1202
1643
|
}
|
|
1203
|
-
case CC_EVENTS.AGENT_RELOGIN_SUCCESS:
|
|
1204
|
-
{
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
}
|
|
1644
|
+
case CC_EVENTS.AGENT_RELOGIN_SUCCESS: {
|
|
1645
|
+
const {channelsMap, ...loginData} = eventData.data;
|
|
1646
|
+
this.updateWellnessSession(loginData.agentSessionId);
|
|
1647
|
+
const stationReLoginData = {
|
|
1648
|
+
...loginData,
|
|
1649
|
+
mmProfile: {
|
|
1650
|
+
chat: channelsMap.chat?.length,
|
|
1651
|
+
email: channelsMap.email?.length,
|
|
1652
|
+
social: channelsMap.social?.length,
|
|
1653
|
+
telephony: channelsMap.telephony?.length,
|
|
1654
|
+
},
|
|
1655
|
+
notifsTrackingId: eventData.trackingId,
|
|
1656
|
+
};
|
|
1657
|
+
// @ts-ignore
|
|
1658
|
+
this.emit(AGENT_EVENTS.AGENT_RELOGIN_SUCCESS, stationReLoginData);
|
|
1219
1659
|
break;
|
|
1660
|
+
}
|
|
1220
1661
|
case CC_EVENTS.AGENT_STATE_CHANGE_SUCCESS:
|
|
1221
1662
|
// @ts-ignore
|
|
1222
1663
|
this.emit(AGENT_EVENTS.AGENT_STATE_CHANGE_SUCCESS, eventData.data);
|
|
@@ -1230,6 +1671,12 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1230
1671
|
this.emit(AGENT_EVENTS.AGENT_STATION_LOGIN_FAILED, eventData.data);
|
|
1231
1672
|
break;
|
|
1232
1673
|
case CC_EVENTS.AGENT_LOGOUT_SUCCESS:
|
|
1674
|
+
if (
|
|
1675
|
+
!eventData.data.agentSessionId ||
|
|
1676
|
+
eventData.data.agentSessionId === this.currentAgentSessionId
|
|
1677
|
+
) {
|
|
1678
|
+
this.updateWellnessSession();
|
|
1679
|
+
}
|
|
1233
1680
|
// @ts-ignore
|
|
1234
1681
|
this.emit(AGENT_EVENTS.AGENT_LOGOUT_SUCCESS, eventData.data);
|
|
1235
1682
|
break;
|
|
@@ -1246,6 +1693,617 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1246
1693
|
}
|
|
1247
1694
|
};
|
|
1248
1695
|
|
|
1696
|
+
/**
|
|
1697
|
+
* Checks whether Mobius/WebRTC registration should be skipped by config
|
|
1698
|
+
* @returns {boolean} True when browser WebRTC registration is disabled
|
|
1699
|
+
* @private
|
|
1700
|
+
*/
|
|
1701
|
+
private isWebRTCRegistrationDisabled(): boolean {
|
|
1702
|
+
return this.$config?.disableWebRTCRegistration === true;
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
/**
|
|
1706
|
+
* Validates contact-center plugin configuration before service initialization
|
|
1707
|
+
* @private
|
|
1708
|
+
*/
|
|
1709
|
+
private validatePluginConfig(): void {
|
|
1710
|
+
if (
|
|
1711
|
+
this.$config?.disableWebRTCRegistration === true &&
|
|
1712
|
+
this.$config?.allowMultiLogin === false
|
|
1713
|
+
) {
|
|
1714
|
+
throw new Error(
|
|
1715
|
+
'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.'
|
|
1716
|
+
);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
/**
|
|
1721
|
+
* Whether wxApp thick-client answer is enabled for this SDK instance.
|
|
1722
|
+
* @public
|
|
1723
|
+
*/
|
|
1724
|
+
public isWxBetterTogetherEnabled(): boolean {
|
|
1725
|
+
return this.$config?.enableWxBetterTogether === true;
|
|
1726
|
+
}
|
|
1727
|
+
|
|
1728
|
+
/**
|
|
1729
|
+
* Current station login option (Extension, Dial Number, or Browser/Desktop).
|
|
1730
|
+
* Prefer webCallingService — updated on every stationLogin before wxApp hooks run.
|
|
1731
|
+
* @private
|
|
1732
|
+
*/
|
|
1733
|
+
private getCurrentStationLoginOption(): LoginOption | undefined {
|
|
1734
|
+
return this.webCallingService?.loginOption ?? this.agentConfig?.deviceType;
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
/**
|
|
1738
|
+
* wxApp thick-client answer is supported for Extension and Dial Number station login only —
|
|
1739
|
+
* not Browser/Desktop (WebRTC) login.
|
|
1740
|
+
* @private
|
|
1741
|
+
*/
|
|
1742
|
+
private assertWxAppStationLoginSupportedForEnable(): void {
|
|
1743
|
+
const loginOption = this.getCurrentStationLoginOption();
|
|
1744
|
+
if (loginOption !== LoginOption.EXTENSION && loginOption !== LoginOption.AGENT_DN) {
|
|
1745
|
+
throw new Error(
|
|
1746
|
+
'setManageWebexCallingInWxcc requires EXTENSION or AGENT_DN station login. Complete station login before enabling.'
|
|
1747
|
+
);
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
/**
|
|
1752
|
+
* Clears runtime wxApp config after logout/deregister so stale flags do not apply on relogin.
|
|
1753
|
+
* @private
|
|
1754
|
+
*/
|
|
1755
|
+
private resetEnableWxBetterTogetherConfig(): void {
|
|
1756
|
+
if (this.$config) {
|
|
1757
|
+
this.$config.enableWxBetterTogether = false;
|
|
1758
|
+
}
|
|
1759
|
+
this.taskManager.applyEnableWxBetterTogether(false);
|
|
1760
|
+
}
|
|
1761
|
+
|
|
1762
|
+
/**
|
|
1763
|
+
* Runtime enable/disable for wxApp Better Together (toast suppression + Mercury mute sync).
|
|
1764
|
+
*
|
|
1765
|
+
* **Phase 1 (WXCC-6026 / MMT): NOT part of the public host contract.**
|
|
1766
|
+
* Hosts must set `webexConfig.cc.enableWxBetterTogether` before `webex.init()` / `cc.register()`.
|
|
1767
|
+
* To change the flag after the SDK is initialized, re-init the SDK with the updated config.
|
|
1768
|
+
*
|
|
1769
|
+
* **Supported public read API:** {@link isWxBetterTogetherEnabled}.
|
|
1770
|
+
* **Supported telephony API:** unified `task.accept()`, `task.decline()`, `task.toggleMute()`,
|
|
1771
|
+
* `task.transmitDtmf()` — SDK routes wxApp calls internally when the init flag is active.
|
|
1772
|
+
*
|
|
1773
|
+
* **Production lifecycle does not call this method.** Station login, silent relogin, logout,
|
|
1774
|
+
* and deregister use {@link ensureWxAppPostStationLogin} and {@link teardownWxAppLocalState}
|
|
1775
|
+
* (usersub publish + Mercury subscribe/unsubscribe based on the init flag).
|
|
1776
|
+
*
|
|
1777
|
+
* This setter remains as a **private implementation** for Phase 2 runtime toggle and unit tests.
|
|
1778
|
+
* It updates `$config.enableWxBetterTogether`, TaskManager flags, cross-client usersub
|
|
1779
|
+
* (`answer-calls-on-wxcc`), and Mercury mute sync; rolls back config on publish/connect failure.
|
|
1780
|
+
*
|
|
1781
|
+
* @param enabled - `true` to enable wxApp answer + usersub suppression; `false` to disable and tear down.
|
|
1782
|
+
* @throws When enabling without login, on unsupported BROWSER station login, or when usersub/Mercury init fails.
|
|
1783
|
+
* @internal
|
|
1784
|
+
* @see enableWxBetterTogether (CCPluginConfig init flag)
|
|
1785
|
+
* @see ensureWxAppPostStationLogin
|
|
1786
|
+
* @see teardownWxAppLocalState
|
|
1787
|
+
*/
|
|
1788
|
+
private async setManageWebexCallingInWxcc(enabled: boolean): Promise<void> {
|
|
1789
|
+
if (enabled && !this.$webex.internal.device?.userId) {
|
|
1790
|
+
throw new Error('Cannot publish answer-calls-on-wxcc: user is not logged in');
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
if (enabled) {
|
|
1794
|
+
this.assertWxAppStationLoginSupportedForEnable();
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
const previousEnabled = this.isWxBetterTogetherEnabled();
|
|
1798
|
+
|
|
1799
|
+
if (this.$config) {
|
|
1800
|
+
this.$config.enableWxBetterTogether = enabled;
|
|
1801
|
+
}
|
|
1802
|
+
|
|
1803
|
+
this.taskManager.applyEnableWxBetterTogether(enabled);
|
|
1804
|
+
|
|
1805
|
+
let publishedEnable = false;
|
|
1806
|
+
|
|
1807
|
+
try {
|
|
1808
|
+
if (enabled) {
|
|
1809
|
+
await this.ensureWxAppMercuryAndSubscribe();
|
|
1810
|
+
await this.publishAnswerOnWebexCrossClientState(true);
|
|
1811
|
+
publishedEnable = true;
|
|
1812
|
+
this.taskManager.syncWxAppMuteFromCallDetailsForAllTasks();
|
|
1813
|
+
this.taskManager.refreshWxAppOfferObservabilityForAllTasks();
|
|
1814
|
+
} else {
|
|
1815
|
+
await this.publishAnswerOnWebexCrossClientState(false);
|
|
1816
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1817
|
+
await this.releaseWxAppMercuryResources();
|
|
1818
|
+
}
|
|
1819
|
+
} catch (error) {
|
|
1820
|
+
if (publishedEnable) {
|
|
1821
|
+
await this.revertWxAppCrossClientPublish();
|
|
1822
|
+
} else if (enabled) {
|
|
1823
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1824
|
+
await this.releaseWxAppMercuryResources();
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
if (this.$config) {
|
|
1828
|
+
this.$config.enableWxBetterTogether = previousEnabled;
|
|
1829
|
+
}
|
|
1830
|
+
this.taskManager.applyEnableWxBetterTogether(previousEnabled);
|
|
1831
|
+
throw error;
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
private getWxAppTelephonyTaskType(): 'Voice' | 'WebRTC' | 'unknown' {
|
|
1836
|
+
const loginOption = this.getCurrentStationLoginOption();
|
|
1837
|
+
|
|
1838
|
+
if (loginOption === LoginOption.BROWSER) {
|
|
1839
|
+
return 'WebRTC';
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
if (loginOption === LoginOption.EXTENSION || loginOption === LoginOption.AGENT_DN) {
|
|
1843
|
+
return 'Voice';
|
|
1844
|
+
}
|
|
1845
|
+
|
|
1846
|
+
return 'unknown';
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
private async ensureWxAppPostStationLogin(): Promise<void> {
|
|
1850
|
+
const loginOption = this.getCurrentStationLoginOption();
|
|
1851
|
+
const flagEnabled = this.isWxBetterTogetherEnabled();
|
|
1852
|
+
const telephonyTaskType = this.getWxAppTelephonyTaskType();
|
|
1853
|
+
|
|
1854
|
+
if (loginOption === LoginOption.BROWSER) {
|
|
1855
|
+
if (flagEnabled) {
|
|
1856
|
+
logWxAppSessionReadiness({
|
|
1857
|
+
enableWxBetterTogether: true,
|
|
1858
|
+
loginOption,
|
|
1859
|
+
wxAppHooksApplied: false,
|
|
1860
|
+
usersubPublished: false,
|
|
1861
|
+
mercurySubscribed: false,
|
|
1862
|
+
telephonyTaskType,
|
|
1863
|
+
skipReason: 'unsupported_browser_login',
|
|
1864
|
+
});
|
|
1865
|
+
|
|
1866
|
+
this.metricsManager.trackEvent(
|
|
1867
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_SKIPPED,
|
|
1868
|
+
{
|
|
1869
|
+
loginOption,
|
|
1870
|
+
enableWxBetterTogether: true,
|
|
1871
|
+
skipReason: 'unsupported_browser_login',
|
|
1872
|
+
},
|
|
1873
|
+
['operational', 'behavioral']
|
|
1874
|
+
);
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
return;
|
|
1878
|
+
}
|
|
1879
|
+
|
|
1880
|
+
let publishedEnable = false;
|
|
1881
|
+
let wxAppInitStage: 'mercury' | 'publish' = 'mercury';
|
|
1882
|
+
|
|
1883
|
+
try {
|
|
1884
|
+
if (flagEnabled) {
|
|
1885
|
+
this.metricsManager.timeEvent([
|
|
1886
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS,
|
|
1887
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1888
|
+
]);
|
|
1889
|
+
|
|
1890
|
+
await this.ensureWxAppMercuryAndSubscribe();
|
|
1891
|
+
wxAppInitStage = 'publish';
|
|
1892
|
+
await this.publishAnswerOnWebexCrossClientState(true);
|
|
1893
|
+
publishedEnable = true;
|
|
1894
|
+
this.taskManager.syncWxAppMuteFromCallDetailsForAllTasks();
|
|
1895
|
+
this.taskManager.refreshWxAppOfferObservabilityForAllTasks();
|
|
1896
|
+
|
|
1897
|
+
const mercurySubscribed = this.wxAppTelephonyMercurySync.isSubscribed();
|
|
1898
|
+
const usersubPublished = this.webexCrossClientService.isAnswerCallsStateActive();
|
|
1899
|
+
|
|
1900
|
+
logWxAppSessionReadiness({
|
|
1901
|
+
enableWxBetterTogether: true,
|
|
1902
|
+
loginOption,
|
|
1903
|
+
wxAppHooksApplied: true,
|
|
1904
|
+
usersubPublished,
|
|
1905
|
+
mercurySubscribed,
|
|
1906
|
+
telephonyTaskType,
|
|
1907
|
+
});
|
|
1908
|
+
|
|
1909
|
+
const sessionInitReady = mercurySubscribed && usersubPublished;
|
|
1910
|
+
|
|
1911
|
+
if (sessionInitReady) {
|
|
1912
|
+
this.metricsManager.trackEvent(
|
|
1913
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_SUCCESS,
|
|
1914
|
+
{
|
|
1915
|
+
loginOption,
|
|
1916
|
+
enableWxBetterTogether: true,
|
|
1917
|
+
usersubPublished,
|
|
1918
|
+
mercurySubscribed,
|
|
1919
|
+
telephonyTaskType,
|
|
1920
|
+
},
|
|
1921
|
+
['operational', 'behavioral']
|
|
1922
|
+
);
|
|
1923
|
+
} else {
|
|
1924
|
+
this.metricsManager.trackEvent(
|
|
1925
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1926
|
+
{
|
|
1927
|
+
loginOption,
|
|
1928
|
+
enableWxBetterTogether: true,
|
|
1929
|
+
usersubPublished,
|
|
1930
|
+
mercurySubscribed,
|
|
1931
|
+
telephonyTaskType,
|
|
1932
|
+
skipReason: !usersubPublished ? 'usersub_not_published' : 'mercury_not_subscribed',
|
|
1933
|
+
},
|
|
1934
|
+
['operational', 'behavioral']
|
|
1935
|
+
);
|
|
1936
|
+
}
|
|
1937
|
+
} else {
|
|
1938
|
+
await this.ensureWxAppDeviceRegistered();
|
|
1939
|
+
await this.publishAnswerOnWebexCrossClientState(false, {force: true});
|
|
1940
|
+
this.webexCrossClientService.teardown();
|
|
1941
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1942
|
+
await this.releaseWxAppMercuryResources();
|
|
1943
|
+
|
|
1944
|
+
logWxAppSessionReadiness({
|
|
1945
|
+
enableWxBetterTogether: false,
|
|
1946
|
+
loginOption,
|
|
1947
|
+
wxAppHooksApplied: true,
|
|
1948
|
+
usersubPublished: false,
|
|
1949
|
+
mercurySubscribed: false,
|
|
1950
|
+
telephonyTaskType,
|
|
1951
|
+
skipReason: 'flag_disabled',
|
|
1952
|
+
});
|
|
1953
|
+
|
|
1954
|
+
this.metricsManager.trackEvent(
|
|
1955
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_SKIPPED,
|
|
1956
|
+
{loginOption, enableWxBetterTogether: false, skipReason: 'flag_disabled'},
|
|
1957
|
+
['operational', 'behavioral']
|
|
1958
|
+
);
|
|
1959
|
+
}
|
|
1960
|
+
} catch (error) {
|
|
1961
|
+
LoggerProxy.error(`Failed to initialize wxApp post-station-login: ${error}`, {
|
|
1962
|
+
module: CC_FILE,
|
|
1963
|
+
method: METHODS.STATION_LOGIN,
|
|
1964
|
+
});
|
|
1965
|
+
|
|
1966
|
+
if (flagEnabled) {
|
|
1967
|
+
const sessionInitFailureReason =
|
|
1968
|
+
wxAppInitStage === 'mercury' ? 'mercury_subscribe_failed' : 'publish_failed';
|
|
1969
|
+
|
|
1970
|
+
this.metricsManager.trackEvent(
|
|
1971
|
+
METRIC_EVENT_NAMES.WXAPP_SESSION_INIT_FAILED,
|
|
1972
|
+
{
|
|
1973
|
+
loginOption,
|
|
1974
|
+
enableWxBetterTogether: true,
|
|
1975
|
+
usersubPublished: this.webexCrossClientService.isAnswerCallsStateActive(),
|
|
1976
|
+
mercurySubscribed: this.wxAppTelephonyMercurySync.isSubscribed(),
|
|
1977
|
+
telephonyTaskType,
|
|
1978
|
+
skipReason: sessionInitFailureReason,
|
|
1979
|
+
error: error instanceof Error ? error.toString() : String(error),
|
|
1980
|
+
},
|
|
1981
|
+
['operational', 'behavioral']
|
|
1982
|
+
);
|
|
1983
|
+
|
|
1984
|
+
logWxAppSessionReadiness({
|
|
1985
|
+
enableWxBetterTogether: true,
|
|
1986
|
+
loginOption,
|
|
1987
|
+
wxAppHooksApplied: false,
|
|
1988
|
+
usersubPublished: this.webexCrossClientService.isAnswerCallsStateActive(),
|
|
1989
|
+
mercurySubscribed: this.wxAppTelephonyMercurySync.isSubscribed(),
|
|
1990
|
+
telephonyTaskType,
|
|
1991
|
+
skipReason: sessionInitFailureReason,
|
|
1992
|
+
});
|
|
1993
|
+
|
|
1994
|
+
if (publishedEnable) {
|
|
1995
|
+
await this.revertWxAppCrossClientPublish();
|
|
1996
|
+
} else {
|
|
1997
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
1998
|
+
await this.releaseWxAppMercuryResources();
|
|
1999
|
+
}
|
|
2000
|
+
this.resetEnableWxBetterTogetherConfig();
|
|
2001
|
+
} else {
|
|
2002
|
+
this.scheduleForcedFalsePublishRetry();
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
private clearWxAppFalsePublishRetryTimer(): void {
|
|
2008
|
+
if (this.wxAppFalsePublishRetryTimer) {
|
|
2009
|
+
clearTimeout(this.wxAppFalsePublishRetryTimer);
|
|
2010
|
+
this.wxAppFalsePublishRetryTimer = undefined;
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
private scheduleForcedFalsePublishRetry(retryAttempt = 0): void {
|
|
2015
|
+
this.scheduleFalsePublishRetry(retryAttempt, {requireDisabled: true});
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
/**
|
|
2019
|
+
* Retries compensating false publish after rollback failure, regardless of enabled flag.
|
|
2020
|
+
* @private
|
|
2021
|
+
*/
|
|
2022
|
+
private scheduleCompensatingFalsePublishRetry(retryAttempt = 0): void {
|
|
2023
|
+
this.scheduleFalsePublishRetry(retryAttempt);
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
private scheduleFalsePublishRetry(
|
|
2027
|
+
retryAttempt = 0,
|
|
2028
|
+
options: {requireDisabled?: boolean} = {}
|
|
2029
|
+
): void {
|
|
2030
|
+
if (retryAttempt >= ContactCenter.WXAPP_FALSE_PUBLISH_MAX_RETRIES) {
|
|
2031
|
+
return;
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
if (options?.requireDisabled && this.isWxBetterTogetherEnabled()) {
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
this.clearWxAppFalsePublishRetryTimer();
|
|
2039
|
+
|
|
2040
|
+
this.wxAppFalsePublishRetryTimer = setTimeout(async () => {
|
|
2041
|
+
this.wxAppFalsePublishRetryTimer = undefined;
|
|
2042
|
+
|
|
2043
|
+
if (options?.requireDisabled && this.isWxBetterTogetherEnabled()) {
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
if (
|
|
2048
|
+
!options?.requireDisabled &&
|
|
2049
|
+
this.isWxBetterTogetherEnabled() &&
|
|
2050
|
+
this.webexCrossClientService.isAnswerCallsStateActive()
|
|
2051
|
+
) {
|
|
2052
|
+
return;
|
|
2053
|
+
}
|
|
2054
|
+
|
|
2055
|
+
try {
|
|
2056
|
+
await this.ensureWxAppDeviceRegistered();
|
|
2057
|
+
await this.publishAnswerOnWebexCrossClientState(false, {force: true});
|
|
2058
|
+
this.webexCrossClientService.teardown();
|
|
2059
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
2060
|
+
await this.releaseWxAppMercuryResources();
|
|
2061
|
+
} catch (retryError) {
|
|
2062
|
+
LoggerProxy.error(
|
|
2063
|
+
`Failed to retry forced answer-calls-on-wxcc false publish: ${retryError}`,
|
|
2064
|
+
{
|
|
2065
|
+
module: CC_FILE,
|
|
2066
|
+
method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
|
|
2067
|
+
}
|
|
2068
|
+
);
|
|
2069
|
+
this.scheduleFalsePublishRetry(retryAttempt + 1, options);
|
|
2070
|
+
}
|
|
2071
|
+
}, ContactCenter.WXAPP_FALSE_PUBLISH_RETRY_DELAY_MS);
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
/**
|
|
2075
|
+
* Best-effort rollback when usersub true was published but wxApp setup did not complete.
|
|
2076
|
+
* @private
|
|
2077
|
+
*/
|
|
2078
|
+
private async revertWxAppCrossClientPublish(): Promise<void> {
|
|
2079
|
+
try {
|
|
2080
|
+
await this.publishAnswerOnWebexCrossClientState(false, {force: true});
|
|
2081
|
+
} catch (error) {
|
|
2082
|
+
LoggerProxy.error(
|
|
2083
|
+
`Failed to publish compensating answer-calls-on-wxcc false during wxApp rollback: ${error}`,
|
|
2084
|
+
{
|
|
2085
|
+
module: CC_FILE,
|
|
2086
|
+
method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
|
|
2087
|
+
}
|
|
2088
|
+
);
|
|
2089
|
+
this.scheduleCompensatingFalsePublishRetry();
|
|
2090
|
+
}
|
|
2091
|
+
|
|
2092
|
+
this.webexCrossClientService.teardown();
|
|
2093
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
2094
|
+
await this.releaseWxAppMercuryResources();
|
|
2095
|
+
}
|
|
2096
|
+
|
|
2097
|
+
private async ensureWxAppDeviceRegistered(): Promise<void> {
|
|
2098
|
+
const device = this.$webex.internal.device;
|
|
2099
|
+
|
|
2100
|
+
if (device && !device.registered && device.register) {
|
|
2101
|
+
await device.register();
|
|
2102
|
+
this.wxAppDeviceRegisteredByCc = true;
|
|
2103
|
+
LoggerProxy.log('WxApp: device registered for cross-client publish', {
|
|
2104
|
+
module: CC_FILE,
|
|
2105
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2106
|
+
});
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
private async ensureWxAppMercuryConnected(): Promise<void> {
|
|
2111
|
+
const mercury = this.$webex.internal.mercury;
|
|
2112
|
+
|
|
2113
|
+
await this.ensureWxAppDeviceRegistered();
|
|
2114
|
+
|
|
2115
|
+
if (mercury && !mercury.connected) {
|
|
2116
|
+
await mercury.connect();
|
|
2117
|
+
this.wxAppMercuryConnectedByCc = true;
|
|
2118
|
+
LoggerProxy.log('WxApp mute sync: mercury connected', {
|
|
2119
|
+
module: CC_FILE,
|
|
2120
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2121
|
+
});
|
|
2122
|
+
}
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
/**
|
|
2126
|
+
* Disconnects Mercury and unregisters the device when wxApp mute sync opened them.
|
|
2127
|
+
* @private
|
|
2128
|
+
*/
|
|
2129
|
+
private async releaseWxAppMercuryResources(): Promise<void> {
|
|
2130
|
+
const mercury = this.$webex.internal.mercury;
|
|
2131
|
+
const device = this.$webex.internal.device;
|
|
2132
|
+
|
|
2133
|
+
if (this.wxAppMercuryConnectedByCc && mercury?.connected) {
|
|
2134
|
+
try {
|
|
2135
|
+
await mercury.disconnect();
|
|
2136
|
+
this.wxAppMercuryConnectedByCc = false;
|
|
2137
|
+
LoggerProxy.log('WxApp mute sync: mercury disconnected', {
|
|
2138
|
+
module: CC_FILE,
|
|
2139
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2140
|
+
});
|
|
2141
|
+
} catch (error) {
|
|
2142
|
+
LoggerProxy.error(`Failed to disconnect wxApp Mercury: ${error}`, {
|
|
2143
|
+
module: CC_FILE,
|
|
2144
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2145
|
+
});
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
if (this.wxAppDeviceRegisteredByCc && device?.registered && device.unregister) {
|
|
2150
|
+
try {
|
|
2151
|
+
// @ts-ignore
|
|
2152
|
+
await device.unregister();
|
|
2153
|
+
this.wxAppDeviceRegisteredByCc = false;
|
|
2154
|
+
LoggerProxy.log('WxApp mute sync: device unregistered', {
|
|
2155
|
+
module: CC_FILE,
|
|
2156
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2157
|
+
});
|
|
2158
|
+
} catch (error) {
|
|
2159
|
+
LoggerProxy.error(`Failed to unregister wxApp device: ${error}`, {
|
|
2160
|
+
module: CC_FILE,
|
|
2161
|
+
method: METHODS.ENSURE_WXAPP_MERCURY_CONNECTED,
|
|
2162
|
+
});
|
|
2163
|
+
}
|
|
2164
|
+
} else if (this.wxAppDeviceRegisteredByCc) {
|
|
2165
|
+
this.wxAppDeviceRegisteredByCc = false;
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
/**
|
|
2170
|
+
* Clears wxApp runtime state and feature-owned resources. Publish failures are logged;
|
|
2171
|
+
* optional rethrow for deregister when callers need to surface usersub errors.
|
|
2172
|
+
* @private
|
|
2173
|
+
*/
|
|
2174
|
+
private async teardownWxAppLocalState(options?: {
|
|
2175
|
+
rethrowPublishError?: boolean;
|
|
2176
|
+
clearInitFlag?: boolean;
|
|
2177
|
+
}): Promise<{publishError?: unknown}> {
|
|
2178
|
+
this.clearWxAppFalsePublishRetryTimer();
|
|
2179
|
+
let publishError: unknown;
|
|
2180
|
+
|
|
2181
|
+
try {
|
|
2182
|
+
const shouldForceFalsePublish =
|
|
2183
|
+
this.isWxBetterTogetherEnabled() || this.webexCrossClientService.isAnswerCallsStateActive();
|
|
2184
|
+
|
|
2185
|
+
await this.publishAnswerOnWebexCrossClientState(false, {
|
|
2186
|
+
force: shouldForceFalsePublish,
|
|
2187
|
+
});
|
|
2188
|
+
} catch (error) {
|
|
2189
|
+
publishError = error;
|
|
2190
|
+
LoggerProxy.error(
|
|
2191
|
+
`Failed to publish answer-calls-on-wxcc false during wxApp teardown: ${error}`,
|
|
2192
|
+
{
|
|
2193
|
+
module: CC_FILE,
|
|
2194
|
+
method: METHODS.SET_MANAGE_WEBEX_CALLING_IN_WXCC,
|
|
2195
|
+
}
|
|
2196
|
+
);
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
this.webexCrossClientService.teardown();
|
|
2200
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
2201
|
+
await this.releaseWxAppMercuryResources();
|
|
2202
|
+
|
|
2203
|
+
if (options?.clearInitFlag) {
|
|
2204
|
+
this.resetEnableWxBetterTogetherConfig();
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
if (publishError) {
|
|
2208
|
+
this.scheduleCompensatingFalsePublishRetry();
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
if (options?.rethrowPublishError && publishError) {
|
|
2212
|
+
throw publishError;
|
|
2213
|
+
}
|
|
2214
|
+
|
|
2215
|
+
return {publishError: publishError ?? undefined};
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
private async ensureWxAppMercuryAndSubscribe(): Promise<void> {
|
|
2219
|
+
if (!this.isWxBetterTogetherEnabled()) {
|
|
2220
|
+
return;
|
|
2221
|
+
}
|
|
2222
|
+
|
|
2223
|
+
if (this.getCurrentStationLoginOption() === LoginOption.BROWSER) {
|
|
2224
|
+
return;
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
const agentId = this.agentConfig?.agentId;
|
|
2228
|
+
if (!agentId) {
|
|
2229
|
+
LoggerProxy.error('Cannot subscribe wxApp mute sync: agentId unavailable', {
|
|
2230
|
+
module: CC_FILE,
|
|
2231
|
+
method: METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY,
|
|
2232
|
+
});
|
|
2233
|
+
|
|
2234
|
+
this.metricsManager.trackEvent(
|
|
2235
|
+
METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED,
|
|
2236
|
+
{error: 'agentId unavailable', mercurySubscribed: false},
|
|
2237
|
+
['operational', 'behavioral']
|
|
2238
|
+
);
|
|
2239
|
+
|
|
2240
|
+
return;
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
try {
|
|
2244
|
+
await this.ensureWxAppMercuryConnected();
|
|
2245
|
+
this.wxAppTelephonyMercurySync.subscribe(agentId, (callId, muted) => {
|
|
2246
|
+
this.taskManager.applyWxAppMuteStateFromSync(callId, muted);
|
|
2247
|
+
});
|
|
2248
|
+
} catch (error) {
|
|
2249
|
+
LoggerProxy.error(`Failed to ensure wxApp Mercury mute sync: ${error}`, {
|
|
2250
|
+
module: CC_FILE,
|
|
2251
|
+
method: METHODS.SYNC_WXAPP_MUTE_FROM_MERCURY,
|
|
2252
|
+
});
|
|
2253
|
+
this.metricsManager.trackEvent(
|
|
2254
|
+
METRIC_EVENT_NAMES.WXAPP_MERCURY_SUBSCRIBE_FAILED,
|
|
2255
|
+
{
|
|
2256
|
+
error: error instanceof Error ? error.toString() : String(error),
|
|
2257
|
+
mercurySubscribed: false,
|
|
2258
|
+
},
|
|
2259
|
+
['operational', 'behavioral']
|
|
2260
|
+
);
|
|
2261
|
+
this.wxAppTelephonyMercurySync.unsubscribe();
|
|
2262
|
+
await this.releaseWxAppMercuryResources();
|
|
2263
|
+
throw error;
|
|
2264
|
+
}
|
|
2265
|
+
}
|
|
2266
|
+
|
|
2267
|
+
private async publishAnswerOnWebexCrossClientState(
|
|
2268
|
+
enable: boolean,
|
|
2269
|
+
options?: {force?: boolean}
|
|
2270
|
+
): Promise<void> {
|
|
2271
|
+
if (enable && !this.isWxBetterTogetherEnabled()) {
|
|
2272
|
+
return;
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
if (!enable && !options?.force && !this.webexCrossClientService.isAnswerCallsStateActive()) {
|
|
2276
|
+
return;
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
const userId = this.$webex.internal.device?.userId;
|
|
2280
|
+
if (!userId) {
|
|
2281
|
+
if (enable && this.isWxBetterTogetherEnabled()) {
|
|
2282
|
+
this.metricsManager.trackEvent(
|
|
2283
|
+
METRIC_EVENT_NAMES.WXAPP_USERSUB_PUBLISH_FAILED,
|
|
2284
|
+
{
|
|
2285
|
+
enableWxBetterTogether: true,
|
|
2286
|
+
usersubPublished: this.webexCrossClientService.isAnswerCallsStateActive(),
|
|
2287
|
+
skipReason: 'user_id_unavailable',
|
|
2288
|
+
error: 'User ID is unavailable for cross-client publish',
|
|
2289
|
+
},
|
|
2290
|
+
['operational', 'behavioral']
|
|
2291
|
+
);
|
|
2292
|
+
}
|
|
2293
|
+
|
|
2294
|
+
return;
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
await this.webexCrossClientService.setManageWebexCallingInWxcc(enable, {
|
|
2298
|
+
userId,
|
|
2299
|
+
trackPublishMetrics: true,
|
|
2300
|
+
});
|
|
2301
|
+
|
|
2302
|
+
if (enable) {
|
|
2303
|
+
this.clearWxAppFalsePublishRetryTimer();
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
|
|
1249
2307
|
/**
|
|
1250
2308
|
* Initializes event listeners for the Contact Center service
|
|
1251
2309
|
* Sets up handlers for connection state changes and other core events
|
|
@@ -1277,13 +2335,13 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1277
2335
|
private async handleConnectionLost(msg: ConnectionLostDetails): Promise<void> {
|
|
1278
2336
|
if (msg.isConnectionLost) {
|
|
1279
2337
|
// TODO: Emit an event saying connection is lost
|
|
1280
|
-
LoggerProxy.
|
|
2338
|
+
LoggerProxy.log('event=handleConnectionLost | Connection lost', {
|
|
1281
2339
|
module: CC_FILE,
|
|
1282
2340
|
method: METHODS.HANDLE_CONNECTION_LOST,
|
|
1283
2341
|
});
|
|
1284
2342
|
} else if (msg.isSocketReconnected) {
|
|
1285
2343
|
// TODO: Emit an event saying connection is re-estabilished
|
|
1286
|
-
LoggerProxy.
|
|
2344
|
+
LoggerProxy.log(
|
|
1287
2345
|
'event=handleConnectionReconnect | Connection reconnected attempting to request silent relogin',
|
|
1288
2346
|
{module: CC_FILE, method: METHODS.HANDLE_CONNECTION_LOST}
|
|
1289
2347
|
);
|
|
@@ -1298,13 +2356,15 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1298
2356
|
* @private
|
|
1299
2357
|
*/
|
|
1300
2358
|
private async silentRelogin(): Promise<void> {
|
|
1301
|
-
LoggerProxy.
|
|
2359
|
+
LoggerProxy.log('Starting silent relogin process', {
|
|
1302
2360
|
module: CC_FILE,
|
|
1303
2361
|
method: METHODS.SILENT_RELOGIN,
|
|
1304
2362
|
});
|
|
1305
2363
|
|
|
1306
2364
|
try {
|
|
1307
2365
|
const reLoginResponse = await this.services.agent.reload();
|
|
2366
|
+
this.updateWellnessSession(reLoginResponse.data.agentSessionId);
|
|
2367
|
+
|
|
1308
2368
|
const {
|
|
1309
2369
|
agentId,
|
|
1310
2370
|
lastStateChangeReason,
|
|
@@ -1320,7 +2380,7 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1320
2380
|
await this.handleDeviceType(deviceType as LoginOption, dn);
|
|
1321
2381
|
|
|
1322
2382
|
if (lastStateChangeReason === 'agent-wss-disconnect') {
|
|
1323
|
-
LoggerProxy.
|
|
2383
|
+
LoggerProxy.log(
|
|
1324
2384
|
'event=requestAutoStateChange | Requesting state change to available on socket reconnect',
|
|
1325
2385
|
{module: CC_FILE, method: METHODS.SILENT_RELOGIN}
|
|
1326
2386
|
);
|
|
@@ -1349,8 +2409,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1349
2409
|
}
|
|
1350
2410
|
this.agentConfig.lastStateAuxCodeId = auxCodeId;
|
|
1351
2411
|
this.agentConfig.isAgentLoggedIn = true;
|
|
1352
|
-
|
|
1353
|
-
this.
|
|
2412
|
+
|
|
2413
|
+
await this.ensureWxAppPostStationLogin();
|
|
1354
2414
|
|
|
1355
2415
|
LoggerProxy.log(
|
|
1356
2416
|
`Silent relogin process completed successfully with login Option: ${reLoginResponse.data.deviceType} teamId: ${reLoginResponse.data.teamId}`,
|
|
@@ -1391,6 +2451,16 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
1391
2451
|
this.agentConfig.deviceType = deviceType;
|
|
1392
2452
|
switch (deviceType) {
|
|
1393
2453
|
case LoginOption.BROWSER:
|
|
2454
|
+
if (this.isWebRTCRegistrationDisabled()) {
|
|
2455
|
+
LoggerProxy.info(
|
|
2456
|
+
'Skipping web calling line registration because disableWebRTCRegistration is enabled',
|
|
2457
|
+
{
|
|
2458
|
+
module: CC_FILE,
|
|
2459
|
+
method: METHODS.HANDLE_DEVICE_TYPE,
|
|
2460
|
+
}
|
|
2461
|
+
);
|
|
2462
|
+
break;
|
|
2463
|
+
}
|
|
1394
2464
|
try {
|
|
1395
2465
|
await this.webCallingService.registerWebCallingLine();
|
|
1396
2466
|
} catch (error) {
|
|
@@ -2060,8 +3130,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
2060
3130
|
}
|
|
2061
3131
|
|
|
2062
3132
|
/**
|
|
2063
|
-
* Returns paginated entry points
|
|
2064
|
-
*
|
|
3133
|
+
* Returns paginated entry points using the EntryPoint service defaults.
|
|
3134
|
+
* Existing request parameters can override those defaults.
|
|
2065
3135
|
* @public
|
|
2066
3136
|
*/
|
|
2067
3137
|
public async getEntryPoints(
|
|
@@ -2071,8 +3141,8 @@ export default class ContactCenter extends WebexPlugin implements IContactCenter
|
|
|
2071
3141
|
}
|
|
2072
3142
|
|
|
2073
3143
|
/**
|
|
2074
|
-
* Returns paginated contact service queues
|
|
2075
|
-
*
|
|
3144
|
+
* Returns paginated contact service queues using the Queue service defaults.
|
|
3145
|
+
* Existing request parameters can override those defaults.
|
|
2076
3146
|
* @public
|
|
2077
3147
|
*/
|
|
2078
3148
|
public async getQueues(
|