@webex/contact-center 3.12.0-next.13 → 3.12.0-next.130
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 +884 -0
- package/AGENTS.md +94 -0
- package/ai-docs/ARCHITECTURE.md +168 -0
- package/ai-docs/CONTRACTS.md +53 -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 +971 -42
- package/dist/cc.js.map +1 -1
- package/dist/config.js +13 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +31 -3
- package/dist/constants.js.map +1 -1
- package/dist/index.js +45 -15
- 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 +205 -0
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +48 -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 +269 -9
- 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 +222 -0
- package/dist/services/WebexCrossClientService.js.map +1 -0
- package/dist/services/WxAppTelephonyMercurySync.js +108 -0
- package/dist/services/WxAppTelephonyMercurySync.js.map +1 -0
- package/dist/services/config/Util.js +11 -4
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/constants.js +41 -7
- package/dist/services/config/constants.js.map +1 -1
- package/dist/services/config/index.js +51 -0
- package/dist/services/config/index.js.map +1 -1
- package/dist/services/config/types.js +57 -12
- 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 +754 -0
- package/dist/services/task/Task.js.map +1 -0
- package/dist/services/task/TaskFactory.js +49 -0
- package/dist/services/task/TaskFactory.js.map +1 -0
- package/dist/services/task/TaskManager.js +1172 -521
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +211 -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 +12 -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 +228 -5
- package/dist/services/task/types.js.map +1 -1
- package/dist/services/task/voice/Voice.js +1485 -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/wxAppVoiceMethods.js +354 -0
- package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -0
- package/dist/services/wxAppDiagnosticLogging.js +122 -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 +193 -6
- package/dist/types/config.d.ts +13 -0
- package/dist/types/constants.d.ts +29 -1
- package/dist/types/index.d.ts +35 -8
- package/dist/types/metrics/MetricsManager.d.ts +6 -0
- package/dist/types/metrics/constants.d.ts +43 -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 +56 -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/constants.d.ts +34 -5
- package/dist/types/services/config/types.d.ts +197 -12
- 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 +175 -0
- package/dist/types/services/task/TaskFactory.d.ts +13 -0
- package/dist/types/services/task/TaskUtils.d.ts +48 -2
- package/dist/types/services/task/WebexCallingUtils.d.ts +11 -0
- package/dist/types/services/task/constants.d.ts +10 -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 +22 -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 +680 -66
- package/dist/types/services/task/voice/Voice.d.ts +227 -0
- package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
- package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +55 -0
- package/dist/types/services/wxAppDiagnosticLogging.d.ts +51 -0
- package/dist/types/services/wxAppTelephonyUtils.d.ts +5 -0
- package/dist/types/types.d.ts +205 -9
- package/dist/types/utils/PageCache.d.ts +20 -3
- package/dist/types/webex.d.ts +1 -0
- package/dist/types.js +162 -4
- 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 +1289 -112
- package/src/config.ts +13 -0
- package/src/constants.ts +29 -1
- package/src/index.ts +36 -5
- 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 +921 -0
- package/src/metrics/behavioral-events.ts +212 -0
- package/src/metrics/constants.ts +52 -4
- package/src/services/AddressBook.ts +17 -6
- package/src/services/AnswerCallOnWebexService.ts +239 -0
- package/src/services/ApiAiAssistant.ts +356 -9
- 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 +293 -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 +12 -3
- package/src/services/config/ai-docs/AGENTS.md +255 -0
- package/src/services/config/ai-docs/ARCHITECTURE.md +426 -0
- package/src/services/config/ai-docs/config-spec.md +686 -0
- package/src/services/config/constants.ts +46 -6
- package/src/services/config/index.ts +65 -0
- package/src/services/config/types.ts +197 -13
- 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 +2 -0
- 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 +908 -0
- package/src/services/task/TaskFactory.ts +60 -0
- package/src/services/task/TaskManager.ts +1419 -608
- package/src/services/task/TaskUtils.ts +303 -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 +1474 -0
- package/src/services/task/constants.ts +10 -0
- package/src/services/task/contact.ts +30 -0
- package/src/services/task/dialer.ts +80 -0
- package/src/services/task/digital/Digital.ts +97 -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 +819 -70
- package/src/services/task/voice/Voice.ts +1851 -0
- package/src/services/task/voice/WebRTC.ts +191 -0
- package/src/services/task/voice/wxAppVoiceMethods.ts +516 -0
- package/src/services/wxAppDiagnosticLogging.ts +174 -0
- package/src/services/wxAppTelephonyUtils.ts +14 -0
- package/src/types.ts +262 -11
- 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/spec/cc.ts +2210 -119
- 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 +311 -21
- 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 +467 -0
- package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +136 -0
- package/test/unit/spec/services/config/Util.ts +129 -0
- package/test/unit/spec/services/config/index.ts +97 -29
- package/test/unit/spec/services/core/Utils.ts +425 -8
- package/test/unit/spec/services/core/WebexRequest.ts +3 -1
- 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 +745 -0
- package/test/unit/spec/services/task/TaskFactory.ts +99 -0
- package/test/unit/spec/services/task/TaskManager.ts +2518 -1494
- package/test/unit/spec/services/task/TaskUtils.ts +235 -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 +2061 -0
- package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
- package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +603 -0
- package/test/unit/spec/services/wxAppDiagnosticLogging.ts +143 -0
- package/test/unit/spec/wellnessSampleUtils.ts +288 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
- package/dist/services/task/index.js +0 -1525
- 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 -1801
- package/test/unit/spec/services/task/index.ts +0 -2184
|
@@ -12,11 +12,20 @@ var _constants2 = require("./constants");
|
|
|
12
12
|
var _types2 = require("../config/types");
|
|
13
13
|
var _types3 = require("../../types");
|
|
14
14
|
var _loggerProxy = _interopRequireDefault(require("../../logger-proxy"));
|
|
15
|
-
var _ = _interopRequireDefault(require("."));
|
|
16
|
-
var _MetricsManager = _interopRequireDefault(require("../../metrics/MetricsManager"));
|
|
17
|
-
var _constants3 = require("../../metrics/constants");
|
|
18
15
|
var _TaskUtils = require("./TaskUtils");
|
|
16
|
+
var _TaskFactory = _interopRequireDefault(require("./TaskFactory"));
|
|
17
|
+
var _WebexCallingUtils = require("./WebexCallingUtils");
|
|
18
|
+
var _WebRTC = _interopRequireDefault(require("./voice/WebRTC"));
|
|
19
|
+
var _stateMachine = require("./state-machine");
|
|
20
|
+
var _constants3 = require("./state-machine/constants");
|
|
21
|
+
var _taskDataNormalizer = require("./taskDataNormalizer");
|
|
19
22
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
23
|
+
const CC_EVENT_SET = new Set(Object.values(_types2.CC_EVENTS));
|
|
24
|
+
const MAIN_INTERACTION_CORRELATED_EVENTS = new Set([_types2.CC_EVENTS.AGENT_CONSULT_ENDED, _types2.CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE]);
|
|
25
|
+
const isCcEvent = value => CC_EVENT_SET.has(value);
|
|
26
|
+
const WRAP_UP_STAMP_EVENTS = new Set([_types2.CC_EVENTS.AGENT_WRAPUP, _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCE_ENDED, _types2.CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE, _types2.CC_EVENTS.AGENT_CONFERENCE_TRANSFERRED]);
|
|
27
|
+
const PARTICIPANT_LEAVE_WRAP_STATES = new Set([_stateMachine.TaskState.HELD, _stateMachine.TaskState.RESUME_INITIATING, _stateMachine.TaskState.CONSULTING, _stateMachine.TaskState.CONSULT_INITIATING, _stateMachine.TaskState.CONFERENCING]);
|
|
28
|
+
|
|
20
29
|
/** @internal */
|
|
21
30
|
class TaskManager extends _events.default {
|
|
22
31
|
/**
|
|
@@ -25,46 +34,30 @@ class TaskManager extends _events.default {
|
|
|
25
34
|
* @private
|
|
26
35
|
*/
|
|
27
36
|
|
|
37
|
+
// eslint-disable-next-line no-use-before-define
|
|
38
|
+
|
|
28
39
|
/**
|
|
29
40
|
* @param contact - Routing Contact layer. Talks to AQMReq layer to convert events to promises
|
|
30
41
|
* @param webCallingService - Webrtc Service Layer
|
|
31
42
|
* @param webSocketManager - Websocket Manager to maintain websocket connection and keepalives
|
|
32
43
|
*/
|
|
33
|
-
constructor(apiAIAssistant, contact, webCallingService, webSocketManager) {
|
|
44
|
+
constructor(apiAIAssistant, contact, webCallingService, webSocketManager, rtdWebSocketManager) {
|
|
34
45
|
super();
|
|
35
46
|
this.apiAIAssistant = apiAIAssistant;
|
|
36
47
|
this.contact = contact;
|
|
37
|
-
this.taskCollection = {};
|
|
38
48
|
this.webCallingService = webCallingService;
|
|
39
49
|
this.webSocketManager = webSocketManager;
|
|
40
|
-
this.
|
|
50
|
+
this.rtdWebSocketManager = rtdWebSocketManager;
|
|
51
|
+
this.taskCollection = {};
|
|
52
|
+
this.webRtcEnabled = false;
|
|
41
53
|
this.registerTaskListeners();
|
|
42
54
|
this.registerIncomingCallEvent();
|
|
43
55
|
}
|
|
44
|
-
setWrapupData(wrapupData) {
|
|
45
|
-
this.wrapupData = wrapupData;
|
|
46
|
-
}
|
|
47
|
-
setAgentId(agentId) {
|
|
48
|
-
this.agentId = agentId;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Gets the current agent ID
|
|
53
|
-
* @returns {string} The agent ID set for this task manager instance
|
|
54
|
-
* @public
|
|
55
|
-
*/
|
|
56
|
-
getAgentId() {
|
|
57
|
-
return this.agentId;
|
|
58
|
-
}
|
|
59
|
-
setWebRtcEnabled(webRtcEnabled) {
|
|
60
|
-
this.webRtcEnabled = webRtcEnabled;
|
|
61
|
-
}
|
|
62
56
|
handleRealtimeWebsocketEvent(event) {
|
|
63
57
|
try {
|
|
64
58
|
const payload = JSON.parse(event);
|
|
65
|
-
const eventType = payload?.type || payload?.data?.notifType;
|
|
66
59
|
const interactionId = payload?.data?.data?.conversationId;
|
|
67
|
-
if (!
|
|
60
|
+
if (!interactionId) return;
|
|
68
61
|
const task = this.taskCollection[interactionId];
|
|
69
62
|
if (!task) {
|
|
70
63
|
_loggerProxy.default.info(`Realtime transcription task not found`, {
|
|
@@ -74,7 +67,12 @@ class TaskManager extends _events.default {
|
|
|
74
67
|
});
|
|
75
68
|
return;
|
|
76
69
|
}
|
|
77
|
-
|
|
70
|
+
switch (payload.type) {
|
|
71
|
+
case _types2.CC_EVENTS.REAL_TIME_TRANSCRIPTION:
|
|
72
|
+
case _types2.CC_EVENTS.SUGGESTED_RESPONSE:
|
|
73
|
+
task.emit(payload.type, payload.data);
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
78
76
|
} catch (error) {
|
|
79
77
|
_loggerProxy.default.error('Failed to parse RTD WebSocket message', {
|
|
80
78
|
module: _constants.TASK_MANAGER_FILE,
|
|
@@ -83,6 +81,42 @@ class TaskManager extends _events.default {
|
|
|
83
81
|
});
|
|
84
82
|
}
|
|
85
83
|
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Set config flags for task creation
|
|
87
|
+
*/
|
|
88
|
+
setConfigFlags(configFlags) {
|
|
89
|
+
this.configFlags = configFlags;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Set wrapup configuration data
|
|
94
|
+
*/
|
|
95
|
+
setWrapupData(wrapupData) {
|
|
96
|
+
this.wrapupData = wrapupData;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Set agent ID for task operations
|
|
101
|
+
*/
|
|
102
|
+
setAgentId(agentId) {
|
|
103
|
+
this.agentId = agentId;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Gets the current agent ID
|
|
108
|
+
* @returns {string} The agent ID set for this task manager instance
|
|
109
|
+
* @public
|
|
110
|
+
*/
|
|
111
|
+
getAgentId() {
|
|
112
|
+
return this.agentId;
|
|
113
|
+
}
|
|
114
|
+
setWebRtcEnabled(webRtcEnabled) {
|
|
115
|
+
this.webRtcEnabled = webRtcEnabled;
|
|
116
|
+
}
|
|
117
|
+
setAnswerCallOnWebexService(answerCallOnWebexService) {
|
|
118
|
+
this.answerCallOnWebexService = answerCallOnWebexService;
|
|
119
|
+
}
|
|
86
120
|
handleIncomingWebCall = call => {
|
|
87
121
|
const currentTask = Object.values(this.taskCollection).find(task => task.data.interaction.mediaType === 'telephony' && !(0, _TaskUtils.isCampaignPreviewReservation)(task));
|
|
88
122
|
if (currentTask) {
|
|
@@ -92,7 +126,12 @@ class TaskManager extends _events.default {
|
|
|
92
126
|
method: _constants2.METHODS.HANDLE_INCOMING_WEB_CALL,
|
|
93
127
|
interactionId: currentTask.data.interactionId
|
|
94
128
|
});
|
|
95
|
-
|
|
129
|
+
|
|
130
|
+
// Send TASK_INCOMING to state machine - it will emit on the task object
|
|
131
|
+
const eventPayload = TaskManager.mapEventToTaskStateMachineEvent(_types2.CC_EVENTS.AGENT_CONTACT_RESERVED, currentTask.data);
|
|
132
|
+
if (eventPayload && currentTask) {
|
|
133
|
+
currentTask.sendStateMachineEvent(eventPayload);
|
|
134
|
+
}
|
|
96
135
|
}
|
|
97
136
|
this.call = call;
|
|
98
137
|
};
|
|
@@ -102,470 +141,1035 @@ class TaskManager extends _events.default {
|
|
|
102
141
|
unregisterIncomingCallEvent() {
|
|
103
142
|
this.webCallingService.off(_calling.LINE_EVENTS.INCOMING_CALL, this.handleIncomingWebCall);
|
|
104
143
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* True when this agent left on the current payload.
|
|
147
|
+
* A named other participantId is never self-leave (even if the participants
|
|
148
|
+
* map is partial). Missing-map fallback is EP-DN only: participantId absent
|
|
149
|
+
* and self omitted now, with previous-task evidence when a prior map exists.
|
|
150
|
+
*/
|
|
151
|
+
static didSelfLeaveConference(payload, agentId, task) {
|
|
152
|
+
if (!agentId) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
if (payload.participantId === agentId) {
|
|
156
|
+
return true;
|
|
157
|
+
}
|
|
158
|
+
if (payload.participantId && payload.participantId !== agentId) {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
const participants = payload.interaction?.participants;
|
|
162
|
+
if (!participants || agentId in participants) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
const previousParticipants = task?.data?.interaction?.participants;
|
|
166
|
+
if (previousParticipants && !(agentId in previousParticipants)) {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Consulted non-owner: Voice exitConference / shouldWrapUpForThisAgent stays
|
|
174
|
+
* false unless an explicit wrap-up signal includes this agent.
|
|
175
|
+
*/
|
|
176
|
+
static isConsultedNonOwner(payload, agentId, task) {
|
|
177
|
+
const isConsulted = payload.isConsulted ?? task?.data?.isConsulted;
|
|
178
|
+
if (isConsulted !== true) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
const owner = payload.interaction?.owner ?? task?.data?.interaction?.owner;
|
|
182
|
+
return Boolean(agentId && owner && owner !== agentId);
|
|
183
|
+
}
|
|
184
|
+
static hasNonemptyPendingWrapUp(payload) {
|
|
185
|
+
return Array.isArray(payload.agentsPendingWrapUp) && payload.agentsPendingWrapUp.length > 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Transfer initiator fallback: widgets set transferConferenceRequested on the
|
|
190
|
+
* actor; desktop success payloads may include consultingAgentId. Only used
|
|
191
|
+
* when agentsPendingWrapUp is empty or absent.
|
|
192
|
+
*/
|
|
193
|
+
static isSelfConferenceTransferInitiator(payload, agentId, task) {
|
|
194
|
+
const snapshotContext = task?.stateMachineService?.getSnapshot?.()?.context;
|
|
195
|
+
if (snapshotContext?.transferConferenceRequested === true) {
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
return Boolean(agentId && payload.consultingAgentId === agentId);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Explicit wrap-up signals only (no owner / isConsulted fallback).
|
|
203
|
+
* Used for events that remaining conference agents also receive.
|
|
204
|
+
* nonempty agentsPendingWrapUp is authoritative, then wrapUpRequired / isWrapUp.
|
|
205
|
+
*/
|
|
206
|
+
static wrapUpRequiredFromExplicitSignals(payload, agentId) {
|
|
207
|
+
const pending = payload.agentsPendingWrapUp;
|
|
208
|
+
if (Array.isArray(pending) && pending.length > 0) {
|
|
209
|
+
return Boolean(agentId && pending.includes(agentId));
|
|
210
|
+
}
|
|
211
|
+
if (payload.wrapUpRequired === true) {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
if (!agentId) {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
return payload.interaction?.participants?.[agentId]?.isWrapUp === true;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Stamp wrapUpRequired once at ingress (prepareEventContext) so updateTaskData
|
|
222
|
+
* and the mapper see the same flag. Remaining agents stay false.
|
|
223
|
+
*/
|
|
224
|
+
static stampWrapUpRequiredForEvent(eventType, payload, agentId, task) {
|
|
225
|
+
if (!WRAP_UP_STAMP_EVENTS.has(eventType)) {
|
|
226
|
+
return payload;
|
|
227
|
+
}
|
|
228
|
+
if (eventType === _types2.CC_EVENTS.AGENT_WRAPUP) {
|
|
229
|
+
return {
|
|
230
|
+
...payload,
|
|
231
|
+
wrapUpRequired: true
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
if (eventType === _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCE_ENDED) {
|
|
235
|
+
return {
|
|
236
|
+
...payload,
|
|
237
|
+
wrapUpRequired: TaskManager.wrapUpRequiredFromExplicitSignals(payload, agentId)
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
if (eventType === _types2.CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE) {
|
|
241
|
+
const actorState = task?.stateMachineService?.getSnapshot?.()?.value;
|
|
242
|
+
if (actorState && !PARTICIPANT_LEAVE_WRAP_STATES.has(actorState)) {
|
|
243
|
+
return payload;
|
|
244
|
+
}
|
|
245
|
+
const left = TaskManager.didSelfLeaveConference(payload, agentId, task);
|
|
246
|
+
if (!left) {
|
|
247
|
+
return {
|
|
248
|
+
...payload,
|
|
249
|
+
wrapUpRequired: TaskManager.wrapUpRequiredFromExplicitSignals(payload, agentId)
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
if (TaskManager.hasNonemptyPendingWrapUp(payload)) {
|
|
253
|
+
return {
|
|
254
|
+
...payload,
|
|
255
|
+
wrapUpRequired: Boolean(agentId && payload.agentsPendingWrapUp?.includes(agentId))
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
...payload,
|
|
260
|
+
wrapUpRequired: TaskManager.wrapUpRequiredFromExplicitSignals(payload, agentId) || !TaskManager.isConsultedNonOwner(payload, agentId, task)
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// AGENT_CONFERENCE_TRANSFERRED — pending list wins; initiator only if empty/absent
|
|
265
|
+
if (TaskManager.hasNonemptyPendingWrapUp(payload)) {
|
|
266
|
+
return {
|
|
267
|
+
...payload,
|
|
268
|
+
wrapUpRequired: TaskManager.wrapUpRequiredFromExplicitSignals(payload, agentId)
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
...payload,
|
|
273
|
+
wrapUpRequired: TaskManager.wrapUpRequiredFromExplicitSignals(payload, agentId) || TaskManager.isSelfConferenceTransferInitiator(payload, agentId, task)
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Map WebSocket CC_EVENTS to state machine TaskEvent
|
|
279
|
+
* @param ccEvent - The CC_EVENT type from WebSocket
|
|
280
|
+
* @param payload - The event payload
|
|
281
|
+
* @param agentId - Optional agent ID for state detection (needed for HYDRATE)
|
|
282
|
+
* @param task - Optional task instance for state-aware CONTACT_ENDED mapping
|
|
283
|
+
* @returns TaskEventPayload for state machine or null if no mapping
|
|
284
|
+
*/
|
|
285
|
+
static mapEventToTaskStateMachineEvent(ccEvent, payload, agentId, task, enableWxBetterTogether) {
|
|
286
|
+
const mediaResourceId = payload.mediaResourceId || payload.interaction?.media?.[payload.interactionId]?.mediaResourceId;
|
|
287
|
+
switch (ccEvent) {
|
|
288
|
+
// CC -> TaskEvent mappings (see TaskStateMachine comment for quick reference)
|
|
289
|
+
case _types2.CC_EVENTS.AGENT_CONTACT_RESERVED:
|
|
290
|
+
// AgentContactReserved -> TASK_INCOMING
|
|
291
|
+
if ((0, _TaskUtils.isCampaignPreviewTask)(payload)) {
|
|
292
|
+
return {
|
|
293
|
+
type: _stateMachine.TaskEvent.TASK_INCOMING,
|
|
294
|
+
taskData: payload,
|
|
295
|
+
isCampaignReservationAccept: true
|
|
296
|
+
};
|
|
113
297
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
module: _constants.TASK_MANAGER_FILE,
|
|
265
|
-
method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
|
|
266
|
-
interactionId: payload.data.interactionId
|
|
267
|
-
});
|
|
268
|
-
task = this.updateTaskData(task, {
|
|
269
|
-
...payload.data,
|
|
270
|
-
wrapUpRequired: payload.data.agentsPendingWrapUp?.includes(this.agentId) || false
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
// Handle cleanup based on whether task should be deleted
|
|
274
|
-
this.handleTaskCleanup(task);
|
|
275
|
-
task?.emit(_types.TASK_EVENTS.TASK_END, task);
|
|
276
|
-
}
|
|
277
|
-
break;
|
|
278
|
-
case _types2.CC_EVENTS.CAMPAIGN_CONTACT_UPDATED:
|
|
279
|
-
// CampaignContactUpdated is a non-terminal event (intermediate update during accept).
|
|
280
|
-
// Only update the task data — do NOT remove the task or emit TASK_END.
|
|
281
|
-
// Task cleanup is handled by CONTACT_ENDED or other terminal events.
|
|
282
|
-
if (task) {
|
|
283
|
-
task = this.updateTaskData(task, payload.data);
|
|
284
|
-
}
|
|
285
|
-
break;
|
|
286
|
-
case _types2.CC_EVENTS.CONTACT_MERGED:
|
|
287
|
-
task = this.handleContactMerged(task, payload.data);
|
|
288
|
-
break;
|
|
289
|
-
case _types2.CC_EVENTS.AGENT_CONTACT_HELD:
|
|
290
|
-
// As soon as the main interaction is held, we need to emit TASK_HOLD
|
|
291
|
-
task = this.updateTaskData(task, payload.data);
|
|
292
|
-
task.emit(_types.TASK_EVENTS.TASK_HOLD, task);
|
|
293
|
-
break;
|
|
294
|
-
case _types2.CC_EVENTS.AGENT_CONTACT_UNHELD:
|
|
295
|
-
// As soon as the main interaction is unheld, we need to emit TASK_RESUME
|
|
296
|
-
task = this.updateTaskData(task, payload.data);
|
|
297
|
-
task.emit(_types.TASK_EVENTS.TASK_RESUME, task);
|
|
298
|
-
break;
|
|
299
|
-
case _types2.CC_EVENTS.AGENT_VTEAM_TRANSFERRED:
|
|
300
|
-
task = this.updateTaskData(task, {
|
|
301
|
-
...payload.data,
|
|
302
|
-
wrapUpRequired: true
|
|
303
|
-
});
|
|
304
|
-
task.emit(_types.TASK_EVENTS.TASK_END, task);
|
|
305
|
-
break;
|
|
306
|
-
case _types2.CC_EVENTS.AGENT_CTQ_CANCEL_FAILED:
|
|
307
|
-
task = this.updateTaskData(task, payload.data);
|
|
308
|
-
task.emit(_types.TASK_EVENTS.TASK_CONSULT_QUEUE_FAILED, task);
|
|
309
|
-
break;
|
|
310
|
-
case _types2.CC_EVENTS.AGENT_CONSULT_CREATED:
|
|
311
|
-
// Received when self agent initiates a consult
|
|
312
|
-
task = this.updateTaskData(task, {
|
|
313
|
-
...payload.data,
|
|
314
|
-
isConsulted: false // This ensures that the task consult status is always reset
|
|
315
|
-
});
|
|
316
|
-
task.emit(_types.TASK_EVENTS.TASK_CONSULT_CREATED, task);
|
|
317
|
-
break;
|
|
318
|
-
case _types2.CC_EVENTS.AGENT_OFFER_CONSULT:
|
|
319
|
-
// Received when other agent sends us a consult offer
|
|
320
|
-
task = this.updateTaskData(task, {
|
|
321
|
-
...payload.data,
|
|
322
|
-
isConsulted: true // This ensures that the task is marked as us being requested for a consult
|
|
323
|
-
});
|
|
324
|
-
task.emit(_types.TASK_EVENTS.TASK_OFFER_CONSULT, task);
|
|
325
|
-
|
|
326
|
-
// Handle auto-answer for consult offer
|
|
327
|
-
this.handleAutoAnswer(task);
|
|
328
|
-
break;
|
|
329
|
-
case _types2.CC_EVENTS.AGENT_CONSULTING:
|
|
330
|
-
// Received when agent is in an active consult state
|
|
331
|
-
// TODO: Check if we can use backend consult state instead of isConsulted
|
|
332
|
-
task = this.updateTaskData(task, payload.data);
|
|
333
|
-
if (task.data.isConsulted) {
|
|
334
|
-
// Fire only if you are the agent who received the consult request
|
|
335
|
-
task.emit(_types.TASK_EVENTS.TASK_CONSULT_ACCEPTED, task);
|
|
336
|
-
} else {
|
|
337
|
-
// Fire only if you are the agent who initiated the consult
|
|
338
|
-
task.emit(_types.TASK_EVENTS.TASK_CONSULTING, task);
|
|
339
|
-
}
|
|
340
|
-
break;
|
|
341
|
-
case _types2.CC_EVENTS.AGENT_CONSULT_FAILED:
|
|
342
|
-
// This can only be received by the agent who initiated the consult.
|
|
343
|
-
// We need not emit any event here since this will be result of promise
|
|
344
|
-
task = this.updateTaskData(task, payload.data);
|
|
345
|
-
break;
|
|
346
|
-
case _types2.CC_EVENTS.AGENT_CONSULT_ENDED:
|
|
347
|
-
task = this.updateTaskData(task, payload.data);
|
|
348
|
-
if (task.data.isConsulted) {
|
|
349
|
-
// This will be the end state of the task as soon as we end the consult in case of
|
|
350
|
-
// us being offered a consult
|
|
351
|
-
this.removeTaskFromCollection(task);
|
|
352
|
-
}
|
|
353
|
-
task.emit(_types.TASK_EVENTS.TASK_CONSULT_END, task);
|
|
354
|
-
break;
|
|
355
|
-
case _types2.CC_EVENTS.AGENT_CTQ_CANCELLED:
|
|
356
|
-
// This event is received when the consult using queue is cancelled using API
|
|
357
|
-
task = this.updateTaskData(task, payload.data);
|
|
358
|
-
task.emit(_types.TASK_EVENTS.TASK_CONSULT_QUEUE_CANCELLED, task);
|
|
359
|
-
break;
|
|
360
|
-
case _types2.CC_EVENTS.AGENT_WRAPUP:
|
|
361
|
-
task = this.updateTaskData(task, {
|
|
362
|
-
...payload.data,
|
|
363
|
-
wrapUpRequired: true
|
|
364
|
-
});
|
|
365
|
-
task.emit(_types.TASK_EVENTS.TASK_END, task);
|
|
366
|
-
break;
|
|
367
|
-
case _types2.CC_EVENTS.AGENT_WRAPPEDUP:
|
|
368
|
-
task.cancelAutoWrapupTimer();
|
|
369
|
-
this.removeTaskFromCollection(task);
|
|
370
|
-
task.emit(_types.TASK_EVENTS.TASK_WRAPPEDUP, task);
|
|
371
|
-
break;
|
|
372
|
-
case _types2.CC_EVENTS.CONTACT_RECORDING_PAUSED:
|
|
373
|
-
task = this.updateTaskData(task, payload.data);
|
|
374
|
-
task.emit(_types.TASK_EVENTS.TASK_RECORDING_PAUSED, task);
|
|
375
|
-
break;
|
|
376
|
-
case _types2.CC_EVENTS.CONTACT_RECORDING_PAUSE_FAILED:
|
|
377
|
-
task = this.updateTaskData(task, payload.data);
|
|
378
|
-
task.emit(_types.TASK_EVENTS.TASK_RECORDING_PAUSE_FAILED, task);
|
|
379
|
-
break;
|
|
380
|
-
case _types2.CC_EVENTS.CONTACT_RECORDING_RESUMED:
|
|
381
|
-
task = this.updateTaskData(task, payload.data);
|
|
382
|
-
task.emit(_types.TASK_EVENTS.TASK_RECORDING_RESUMED, task);
|
|
383
|
-
break;
|
|
384
|
-
case _types2.CC_EVENTS.CONTACT_RECORDING_RESUME_FAILED:
|
|
385
|
-
task = this.updateTaskData(task, payload.data);
|
|
386
|
-
task.emit(_types.TASK_EVENTS.TASK_RECORDING_RESUME_FAILED, task);
|
|
387
|
-
break;
|
|
388
|
-
case _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCING:
|
|
389
|
-
// Conference is being established - update task state and emit establishing event
|
|
390
|
-
task = this.updateTaskData(task, payload.data);
|
|
391
|
-
task.emit(_types.TASK_EVENTS.TASK_CONFERENCE_ESTABLISHING, task);
|
|
392
|
-
break;
|
|
393
|
-
case _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCED:
|
|
394
|
-
// Conference started successfully - update task state and emit event
|
|
395
|
-
task = this.updateTaskData(task, payload.data);
|
|
396
|
-
task.emit(_types.TASK_EVENTS.TASK_CONFERENCE_STARTED, task);
|
|
397
|
-
break;
|
|
398
|
-
case _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCE_FAILED:
|
|
399
|
-
// Conference failed - update task state and emit failure event
|
|
400
|
-
task = this.updateTaskData(task, payload.data);
|
|
401
|
-
task.emit(_types.TASK_EVENTS.TASK_CONFERENCE_FAILED, task);
|
|
402
|
-
break;
|
|
403
|
-
case _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCE_ENDED:
|
|
404
|
-
// Conference ended - update task state and emit event
|
|
405
|
-
task = this.updateTaskData(task, payload.data);
|
|
406
|
-
if (!task || (0, _TaskUtils.isPrimary)(task, this.agentId) || (0, _TaskUtils.isParticipantInMainInteraction)(task, this.agentId)) {
|
|
407
|
-
_loggerProxy.default.log('Primary or main interaction participant leaving conference');
|
|
408
|
-
} else {
|
|
409
|
-
this.removeTaskFromCollection(task);
|
|
410
|
-
}
|
|
411
|
-
task.emit(_types.TASK_EVENTS.TASK_CONFERENCE_ENDED, task);
|
|
412
|
-
break;
|
|
413
|
-
case _types2.CC_EVENTS.PARTICIPANT_JOINED_CONFERENCE:
|
|
414
|
-
{
|
|
415
|
-
task = this.updateTaskData(task, {
|
|
416
|
-
...payload.data,
|
|
417
|
-
isConferenceInProgress: (0, _TaskUtils.getIsConferenceInProgress)(payload.data)
|
|
418
|
-
});
|
|
419
|
-
task.emit(_types.TASK_EVENTS.TASK_PARTICIPANT_JOINED, task);
|
|
420
|
-
break;
|
|
421
|
-
}
|
|
422
|
-
case _types2.CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE:
|
|
423
|
-
{
|
|
424
|
-
// Conference ended - update task state and emit event
|
|
425
|
-
|
|
426
|
-
task = this.updateTaskData(task, {
|
|
427
|
-
...payload.data,
|
|
428
|
-
isConferenceInProgress: (0, _TaskUtils.getIsConferenceInProgress)(payload.data)
|
|
429
|
-
});
|
|
430
|
-
if ((0, _TaskUtils.checkParticipantNotInInteraction)(task, this.agentId)) {
|
|
431
|
-
if ((0, _TaskUtils.isParticipantInMainInteraction)(task, this.agentId) || (0, _TaskUtils.isPrimary)(task, this.agentId)) {
|
|
432
|
-
_loggerProxy.default.log('Primary or main interaction participant leaving conference');
|
|
433
|
-
} else {
|
|
434
|
-
this.removeTaskFromCollection(task);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
task.emit(_types.TASK_EVENTS.TASK_PARTICIPANT_LEFT, task);
|
|
438
|
-
break;
|
|
439
|
-
}
|
|
440
|
-
case _types2.CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE_FAILED:
|
|
441
|
-
// Conference exit failed - update task state and emit failure event
|
|
442
|
-
task = this.updateTaskData(task, payload.data);
|
|
443
|
-
task.emit(_types.TASK_EVENTS.TASK_PARTICIPANT_LEFT_FAILED, task);
|
|
444
|
-
break;
|
|
445
|
-
case _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCE_END_FAILED:
|
|
446
|
-
// Conference end failed - update task state with error details and emit failure event
|
|
447
|
-
task = this.updateTaskData(task, payload.data);
|
|
448
|
-
task.emit(_types.TASK_EVENTS.TASK_CONFERENCE_END_FAILED, task);
|
|
449
|
-
break;
|
|
450
|
-
case _types2.CC_EVENTS.AGENT_CONFERENCE_TRANSFERRED:
|
|
451
|
-
// Conference was transferred - update task state and emit transfer success event
|
|
452
|
-
// Note: Backend should provide hasLeft and wrapUpRequired status
|
|
453
|
-
task = this.updateTaskData(task, payload.data);
|
|
454
|
-
task.emit(_types.TASK_EVENTS.TASK_CONFERENCE_TRANSFERRED, task);
|
|
455
|
-
break;
|
|
456
|
-
case _types2.CC_EVENTS.AGENT_CONFERENCE_TRANSFER_FAILED:
|
|
457
|
-
// Conference transfer failed - update task state with error details and emit failure event
|
|
458
|
-
task = this.updateTaskData(task, payload.data);
|
|
459
|
-
task.emit(_types.TASK_EVENTS.TASK_CONFERENCE_TRANSFER_FAILED, task);
|
|
460
|
-
break;
|
|
461
|
-
case _types2.CC_EVENTS.PARTICIPANT_POST_CALL_ACTIVITY:
|
|
462
|
-
// Post-call activity for participant - update task state with activity details
|
|
463
|
-
task = this.updateTaskData(task, payload.data);
|
|
464
|
-
task.emit(_types.TASK_EVENTS.TASK_POST_CALL_ACTIVITY, task);
|
|
465
|
-
break;
|
|
466
|
-
case _types2.CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION:
|
|
467
|
-
{
|
|
468
|
-
// Campaign preview contact offered to agent
|
|
469
|
-
// Create a task in the collection so subsequent events (e.g. AGENT_CONTACT_ASSIGNED
|
|
470
|
-
// after acceptPreviewContact) can find and update it.
|
|
471
|
-
// Emit TASK_CAMPAIGN_PREVIEW_RESERVATION instead of TASK_INCOMING so the call
|
|
472
|
-
// does not ring out to the customer before the agent explicitly accepts the preview contact.
|
|
473
|
-
_loggerProxy.default.log('Campaign preview reservation received', {
|
|
474
|
-
module: _constants.TASK_MANAGER_FILE,
|
|
475
|
-
method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
|
|
476
|
-
interactionId: payload.data.interactionId
|
|
477
|
-
});
|
|
478
|
-
if (!this.taskCollection[payload.data.interactionId]) {
|
|
479
|
-
task = new _.default(this.contact, this.webCallingService, {
|
|
480
|
-
...payload.data,
|
|
481
|
-
wrapUpRequired: false,
|
|
482
|
-
isConferenceInProgress: false,
|
|
483
|
-
isAutoAnswering: false
|
|
484
|
-
}, this.wrapupData, this.agentId);
|
|
485
|
-
this.taskCollection[payload.data.interactionId] = task;
|
|
486
|
-
} else {
|
|
487
|
-
task = this.updateTaskData(task, payload.data);
|
|
298
|
+
return {
|
|
299
|
+
type: _stateMachine.TaskEvent.TASK_INCOMING,
|
|
300
|
+
taskData: payload
|
|
301
|
+
};
|
|
302
|
+
case _types2.CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION:
|
|
303
|
+
// -> TASK_INCOMING (campaign branch via guard)
|
|
304
|
+
return {
|
|
305
|
+
type: _stateMachine.TaskEvent.TASK_INCOMING,
|
|
306
|
+
taskData: payload,
|
|
307
|
+
isCampaignReservationAccept: true
|
|
308
|
+
};
|
|
309
|
+
case _types2.CC_EVENTS.AGENT_OFFER_CONTACT:
|
|
310
|
+
// AgentOfferContact -> TASK_OFFERED
|
|
311
|
+
return {
|
|
312
|
+
type: _stateMachine.TaskEvent.TASK_OFFERED,
|
|
313
|
+
taskData: payload
|
|
314
|
+
};
|
|
315
|
+
case _types2.CC_EVENTS.AGENT_CONTACT:
|
|
316
|
+
// AgentContact -> HYDRATE
|
|
317
|
+
// Include agentId for state detection (e.g., checking isWrapUp in participant data)
|
|
318
|
+
return {
|
|
319
|
+
type: _stateMachine.TaskEvent.HYDRATE,
|
|
320
|
+
taskData: payload,
|
|
321
|
+
agentId
|
|
322
|
+
};
|
|
323
|
+
case _types2.CC_EVENTS.CONTACT_UPDATED:
|
|
324
|
+
if (task && typeof payload.interaction?.owner === 'string' && payload.interaction.owner.trim().length > 0 && payload.interaction.owner !== task.data?.interaction?.owner) {
|
|
325
|
+
return {
|
|
326
|
+
type: _stateMachine.TaskEvent.CONTACT_OWNER_CHANGED,
|
|
327
|
+
taskData: payload
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
return {
|
|
331
|
+
type: _stateMachine.TaskEvent.CONTACT_UPDATED,
|
|
332
|
+
taskData: payload
|
|
333
|
+
};
|
|
334
|
+
case _types2.CC_EVENTS.CONTACT_OWNER_CHANGED:
|
|
335
|
+
return {
|
|
336
|
+
type: _stateMachine.TaskEvent.CONTACT_OWNER_CHANGED,
|
|
337
|
+
taskData: payload
|
|
338
|
+
};
|
|
339
|
+
case _types2.CC_EVENTS.AGENT_OFFER_CONSULT:
|
|
340
|
+
// AgentOfferConsult -> OFFER_CONSULT
|
|
341
|
+
return {
|
|
342
|
+
type: _stateMachine.TaskEvent.OFFER_CONSULT,
|
|
343
|
+
taskData: {
|
|
344
|
+
...payload,
|
|
345
|
+
isConsulted: true
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
case _types2.CC_EVENTS.AGENT_CONTACT_ASSIGNED:
|
|
349
|
+
// AgentContactAssigned -> ASSIGN
|
|
350
|
+
return {
|
|
351
|
+
type: _stateMachine.TaskEvent.ASSIGN,
|
|
352
|
+
taskData: payload
|
|
353
|
+
};
|
|
354
|
+
case _types2.CC_EVENTS.AGENT_CONTACT_HELD:
|
|
355
|
+
return {
|
|
356
|
+
type: _stateMachine.TaskEvent.HOLD_SUCCESS,
|
|
357
|
+
mediaResourceId: mediaResourceId || '',
|
|
358
|
+
taskData: payload
|
|
359
|
+
};
|
|
360
|
+
case _types2.CC_EVENTS.AGENT_CONTACT_UNHELD:
|
|
361
|
+
return {
|
|
362
|
+
type: _stateMachine.TaskEvent.UNHOLD_SUCCESS,
|
|
363
|
+
mediaResourceId: mediaResourceId || '',
|
|
364
|
+
taskData: payload
|
|
365
|
+
};
|
|
366
|
+
case _types2.CC_EVENTS.AGENT_CONSULT_CREATED:
|
|
367
|
+
return {
|
|
368
|
+
type: _stateMachine.TaskEvent.CONSULT_CREATED,
|
|
369
|
+
taskData: {
|
|
370
|
+
...payload,
|
|
371
|
+
isConsulted: false
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
case _types2.CC_EVENTS.AGENT_CONSULTING:
|
|
375
|
+
// AgentConsulting -> CONSULTING_ACTIVE
|
|
376
|
+
// use context to figure out if it's the initiator or receiver using consultInitiator from context
|
|
377
|
+
return {
|
|
378
|
+
type: _stateMachine.TaskEvent.CONSULTING_ACTIVE,
|
|
379
|
+
consultDestinationAgentJoined: true,
|
|
380
|
+
taskData: payload
|
|
381
|
+
};
|
|
382
|
+
case _types2.CC_EVENTS.AGENT_CONSULT_ENDED:
|
|
383
|
+
// AgentConsultEnded -> CONSULT_END
|
|
384
|
+
return {
|
|
385
|
+
type: _stateMachine.TaskEvent.CONSULT_END,
|
|
386
|
+
taskData: payload
|
|
387
|
+
};
|
|
388
|
+
case _types2.CC_EVENTS.AGENT_CONSULT_FAILED:
|
|
389
|
+
case _types2.CC_EVENTS.AGENT_CTQ_FAILED:
|
|
390
|
+
return {
|
|
391
|
+
type: _stateMachine.TaskEvent.CONSULT_FAILED,
|
|
392
|
+
reason: payload.reason,
|
|
393
|
+
taskData: payload
|
|
394
|
+
};
|
|
395
|
+
case _types2.CC_EVENTS.AGENT_CTQ_CANCELLED:
|
|
396
|
+
return {
|
|
397
|
+
type: _stateMachine.TaskEvent.CTQ_CANCEL,
|
|
398
|
+
taskData: payload
|
|
399
|
+
};
|
|
400
|
+
case _types2.CC_EVENTS.AGENT_CTQ_CANCEL_FAILED:
|
|
401
|
+
return {
|
|
402
|
+
type: _stateMachine.TaskEvent.CTQ_CANCEL_FAILED,
|
|
403
|
+
taskData: payload
|
|
404
|
+
};
|
|
405
|
+
case _types2.CC_EVENTS.AGENT_BLIND_TRANSFERRED: // AgentBlindTransferred -> TRANSFER_SUCCESS
|
|
406
|
+
case _types2.CC_EVENTS.AGENT_CONSULT_TRANSFERRED: // AgentConsultTransferred -> TRANSFER_SUCCESS
|
|
407
|
+
case _types2.CC_EVENTS.AGENT_VTEAM_TRANSFERRED:
|
|
408
|
+
// AgentVTeamTransferred -> TRANSFER_SUCCESS
|
|
409
|
+
return {
|
|
410
|
+
type: _stateMachine.TaskEvent.TRANSFER_SUCCESS,
|
|
411
|
+
taskData: payload
|
|
412
|
+
};
|
|
413
|
+
case _types2.CC_EVENTS.AGENT_WRAPUP:
|
|
414
|
+
return {
|
|
415
|
+
type: _stateMachine.TaskEvent.TASK_WRAPUP,
|
|
416
|
+
taskData: {
|
|
417
|
+
...payload,
|
|
418
|
+
wrapUpRequired: true
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
case _types2.CC_EVENTS.AGENT_CONTACT_UNASSIGNED:
|
|
422
|
+
return null;
|
|
423
|
+
// Add WRAPUP if needed
|
|
424
|
+
|
|
425
|
+
case _types2.CC_EVENTS.AGENT_BLIND_TRANSFER_FAILED:
|
|
426
|
+
case _types2.CC_EVENTS.AGENT_VTEAM_TRANSFER_FAILED:
|
|
427
|
+
case _types2.CC_EVENTS.AGENT_CONSULT_TRANSFER_FAILED:
|
|
428
|
+
case _types2.CC_EVENTS.AGENT_CONFERENCE_TRANSFER_FAILED:
|
|
429
|
+
return {
|
|
430
|
+
type: _stateMachine.TaskEvent.TRANSFER_FAILED,
|
|
431
|
+
taskData: payload
|
|
432
|
+
};
|
|
433
|
+
case _types2.CC_EVENTS.CONTACT_ENDED:
|
|
434
|
+
{
|
|
435
|
+
const wrapUpRequired = (0, _TaskUtils.isCampaignPreviewTask)(payload) ? false : payload.agentsPendingWrapUp?.includes(agentId || '') || false;
|
|
436
|
+
|
|
437
|
+
// Post-accept wxApp outdial agent-end delivers ContactEnded instead of
|
|
438
|
+
// AgentOutboundFailed. Normalize to OUTBOUND_FAILED so state machine
|
|
439
|
+
// enters WRAPPING_UP and emits TASK_OUTDIAL_FAILED (AGENT_ENDS).
|
|
440
|
+
// Pre-accept cancelTask decline stays CONTACT_ENDED → TERMINATED (no wrapup).
|
|
441
|
+
if (TaskManager.isAgentTerminatedOutdialWrapup(payload, wrapUpRequired, task, agentId, enableWxBetterTogether)) {
|
|
442
|
+
return {
|
|
443
|
+
type: _stateMachine.TaskEvent.OUTBOUND_FAILED,
|
|
444
|
+
reason: 'AGENT_ENDS',
|
|
445
|
+
taskData: {
|
|
446
|
+
...payload,
|
|
447
|
+
wrapUpRequired: true
|
|
488
448
|
}
|
|
489
|
-
|
|
490
|
-
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
return {
|
|
452
|
+
type: _stateMachine.TaskEvent.CONTACT_ENDED,
|
|
453
|
+
taskData: {
|
|
454
|
+
...payload,
|
|
455
|
+
wrapUpRequired
|
|
491
456
|
}
|
|
492
|
-
|
|
493
|
-
break;
|
|
457
|
+
};
|
|
494
458
|
}
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
459
|
+
case _types2.CC_EVENTS.AGENT_INVITE_FAILED:
|
|
460
|
+
return {
|
|
461
|
+
type: _stateMachine.TaskEvent.INVITE_FAILED,
|
|
462
|
+
reason: payload.reason
|
|
463
|
+
};
|
|
464
|
+
case _types2.CC_EVENTS.AGENT_CONTACT_ASSIGN_FAILED:
|
|
465
|
+
return {
|
|
466
|
+
type: _stateMachine.TaskEvent.ASSIGN_FAILED,
|
|
467
|
+
reason: payload.reason
|
|
468
|
+
};
|
|
469
|
+
case _types2.CC_EVENTS.AGENT_CONTACT_OFFER_RONA:
|
|
470
|
+
return {
|
|
471
|
+
type: _stateMachine.TaskEvent.RONA,
|
|
472
|
+
taskData: payload,
|
|
473
|
+
reason: payload.reason
|
|
474
|
+
};
|
|
475
|
+
case _types2.CC_EVENTS.AGENT_OUTBOUND_FAILED:
|
|
476
|
+
{
|
|
477
|
+
const isAgentTerminatedOutdial = payload.interaction?.outboundType === 'OUTDIAL' && (payload.terminatingParty === 'Agent' || payload.reason === 'AGENT_ENDS');
|
|
478
|
+
const suppressOutdialFailedPopup = isAgentTerminatedOutdial && !TaskManager.isWxAppManagedOutdialTask(agentId, payload, task, enableWxBetterTogether);
|
|
479
|
+
return {
|
|
480
|
+
type: _stateMachine.TaskEvent.OUTBOUND_FAILED,
|
|
481
|
+
taskData: suppressOutdialFailedPopup ? {
|
|
482
|
+
...payload,
|
|
483
|
+
suppressOutdialFailedPopup: true
|
|
484
|
+
} : payload,
|
|
485
|
+
reason: payload.reason
|
|
486
|
+
};
|
|
501
487
|
}
|
|
488
|
+
case _types2.CC_EVENTS.CAMPAIGN_PREVIEW_ACCEPT_FAILED:
|
|
489
|
+
return {
|
|
490
|
+
type: _stateMachine.TaskEvent.CAMPAIGN_PREVIEW_ACCEPT_FAILED,
|
|
491
|
+
taskData: payload
|
|
492
|
+
};
|
|
493
|
+
case _types2.CC_EVENTS.CAMPAIGN_PREVIEW_SKIP_FAILED:
|
|
494
|
+
return {
|
|
495
|
+
type: _stateMachine.TaskEvent.CAMPAIGN_PREVIEW_SKIP_FAILED,
|
|
496
|
+
taskData: payload
|
|
497
|
+
};
|
|
498
|
+
case _types2.CC_EVENTS.CAMPAIGN_PREVIEW_REMOVE_FAILED:
|
|
499
|
+
return {
|
|
500
|
+
type: _stateMachine.TaskEvent.CAMPAIGN_PREVIEW_REMOVE_FAILED,
|
|
501
|
+
taskData: payload
|
|
502
|
+
};
|
|
503
|
+
case _types2.CC_EVENTS.CONTACT_RECORDING_STARTED:
|
|
504
|
+
return {
|
|
505
|
+
type: _stateMachine.TaskEvent.RECORDING_STARTED,
|
|
506
|
+
taskData: payload
|
|
507
|
+
};
|
|
508
|
+
case _types2.CC_EVENTS.CONTACT_RECORDING_PAUSED:
|
|
509
|
+
return {
|
|
510
|
+
type: _stateMachine.TaskEvent.PAUSE_RECORDING,
|
|
511
|
+
taskData: payload
|
|
512
|
+
};
|
|
513
|
+
case _types2.CC_EVENTS.CONTACT_RECORDING_RESUMED:
|
|
514
|
+
return {
|
|
515
|
+
type: _stateMachine.TaskEvent.RESUME_RECORDING,
|
|
516
|
+
taskData: payload
|
|
517
|
+
};
|
|
518
|
+
case _types2.CC_EVENTS.AGENT_WRAPPEDUP:
|
|
519
|
+
return {
|
|
520
|
+
type: _stateMachine.TaskEvent.WRAPUP_COMPLETE,
|
|
521
|
+
taskData: payload
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
// Conference events - these trigger state machine transition to CONFERENCING
|
|
525
|
+
case _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCED:
|
|
526
|
+
case _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCING:
|
|
527
|
+
case _types2.CC_EVENTS.PARTICIPANT_JOINED_CONFERENCE:
|
|
528
|
+
return {
|
|
529
|
+
type: _stateMachine.TaskEvent.CONFERENCE_START,
|
|
530
|
+
taskData: payload
|
|
531
|
+
};
|
|
532
|
+
case _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCE_FAILED:
|
|
533
|
+
return {
|
|
534
|
+
type: _stateMachine.TaskEvent.CONFERENCE_FAILED,
|
|
535
|
+
reason: payload.reason,
|
|
536
|
+
taskData: payload
|
|
537
|
+
};
|
|
538
|
+
case _types2.CC_EVENTS.AGENT_CONSULT_CONFERENCE_ENDED:
|
|
539
|
+
return {
|
|
540
|
+
type: _stateMachine.TaskEvent.CONFERENCE_END,
|
|
541
|
+
taskData: payload
|
|
542
|
+
};
|
|
543
|
+
case _types2.CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE:
|
|
544
|
+
return {
|
|
545
|
+
type: _stateMachine.TaskEvent.PARTICIPANT_LEAVE,
|
|
546
|
+
taskData: payload,
|
|
547
|
+
participantId: payload?.participantId
|
|
548
|
+
};
|
|
549
|
+
case _types2.CC_EVENTS.AGENT_CONFERENCE_TRANSFERRED:
|
|
550
|
+
return {
|
|
551
|
+
type: _stateMachine.TaskEvent.TRANSFER_CONFERENCE_SUCCESS,
|
|
552
|
+
taskData: payload
|
|
553
|
+
};
|
|
554
|
+
default:
|
|
555
|
+
// Not all events need state machine mapping
|
|
556
|
+
return null;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* True when the current agent's task is a wxApp-managed outdial (thick-client telephony).
|
|
562
|
+
*/
|
|
563
|
+
static isWxAppManagedOutdialTask(agentId, payload, task, enableWxBetterTogether) {
|
|
564
|
+
const wxAppEnabled = task?.uiControlConfig?.enableWxBetterTogether ?? enableWxBetterTogether ?? false;
|
|
565
|
+
if (!wxAppEnabled) {
|
|
566
|
+
return false;
|
|
567
|
+
}
|
|
568
|
+
const effectiveAgentId = agentId ?? task?.data?.agentId;
|
|
569
|
+
const participants = payload.interaction?.participants ?? task?.data?.interaction?.participants;
|
|
570
|
+
const details = (0, _WebexCallingUtils.getWebexCallingDeviceDetailsForAgent)(effectiveAgentId, participants);
|
|
571
|
+
return details?.deviceType === 'wxApp';
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* True when ContactEnded represents an agent-terminated wxApp outdial that should
|
|
576
|
+
* enter wrapup with an outdial-failed popup (legacy AgentOutboundFailed parity).
|
|
577
|
+
*/
|
|
578
|
+
static isAgentTerminatedOutdialWrapup(payload, wrapUpRequired, task, agentId, enableWxBetterTogether) {
|
|
579
|
+
const isOutdial = payload.interaction?.outboundType === 'OUTDIAL';
|
|
580
|
+
const agentTerminated = payload.terminatingParty === 'Agent';
|
|
581
|
+
if (!isOutdial || !agentTerminated) {
|
|
582
|
+
return false;
|
|
583
|
+
}
|
|
584
|
+
if (!TaskManager.isWxAppManagedOutdialTask(agentId, payload, task, enableWxBetterTogether)) {
|
|
585
|
+
return false;
|
|
586
|
+
}
|
|
587
|
+
const wxAppAnswerPending = task?.uiControlConfig?.wxAppAnswerPending ?? false;
|
|
588
|
+
|
|
589
|
+
// Pre-accept decline: decline() clears wxAppAnswerPending; backend may still send state wrapUp.
|
|
590
|
+
if (!wxAppAnswerPending && !wrapUpRequired) {
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
const interactionWrapUp = payload.interaction?.state === 'wrapUp';
|
|
594
|
+
return wrapUpRequired || interactionWrapUp || wxAppAnswerPending;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Register WebSocket message listeners for task events
|
|
599
|
+
*
|
|
600
|
+
* Main entry point that orchestrates event processing through a clear pipeline:
|
|
601
|
+
* 1. Parse and validate incoming WebSocket messages
|
|
602
|
+
* 2. Prepare event context with task and state machine mappings
|
|
603
|
+
* 3. Handle task lifecycle (creation, updates, collection management)
|
|
604
|
+
* 4. Send events to state machine (task-level transitions/emissions)
|
|
605
|
+
* 5. Cleanup is triggered via task events emitted by the state machine
|
|
606
|
+
*
|
|
607
|
+
* This architecture separates concerns:
|
|
608
|
+
* - TaskManager: Manages task collection lifecycle and operational concerns
|
|
609
|
+
* - State Machine: Manages individual task state and event emissions
|
|
610
|
+
*/
|
|
611
|
+
registerTaskListeners() {
|
|
612
|
+
this.webSocketManager.on('message', event => {
|
|
613
|
+
// Step 1: Parse and validate the message
|
|
614
|
+
const message = TaskManager.parseWebSocketMessage(event);
|
|
615
|
+
if (!message) return;
|
|
616
|
+
|
|
617
|
+
// Step 2: Prepare event context
|
|
618
|
+
const eventContext = this.prepareEventContext(message);
|
|
619
|
+
if (!eventContext) return;
|
|
620
|
+
const actions = this.handleTaskLifecycleEvent(eventContext);
|
|
621
|
+
const {
|
|
622
|
+
task
|
|
623
|
+
} = actions;
|
|
624
|
+
if (!task) return;
|
|
625
|
+
const {
|
|
626
|
+
payload,
|
|
627
|
+
stateMachineEvent
|
|
628
|
+
} = eventContext;
|
|
629
|
+
|
|
630
|
+
// Always keep task.data updated (even for mapped events) so consumers relying
|
|
631
|
+
// on TaskManager-managed task instances see the latest payload.
|
|
632
|
+
if (payload) {
|
|
633
|
+
this.updateTaskData(task, payload);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// Send event to state machine - this will trigger all TASK_EVENTS emissions
|
|
637
|
+
// including TASK_INCOMING which is now handled via the state machine callbacks
|
|
638
|
+
if (stateMachineEvent) {
|
|
639
|
+
task.sendStateMachineEvent(stateMachineEvent);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// Emit TASK_POST_CALL_ACTIVITY for ParticipantPostCallActivity events so
|
|
643
|
+
// consumers (Widgets) can detect the interaction state change to post_call.
|
|
644
|
+
if (eventContext.eventType === _types2.CC_EVENTS.PARTICIPANT_POST_CALL_ACTIVITY) {
|
|
645
|
+
task.emit(_types.TASK_EVENTS.TASK_POST_CALL_ACTIVITY, task);
|
|
502
646
|
}
|
|
647
|
+
|
|
648
|
+
// Send transcript start/stop events for relevant CC events
|
|
649
|
+
this.requestRealTimeTranscripts(eventContext.eventType, payload.interactionId);
|
|
503
650
|
});
|
|
504
651
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Parse and validate WebSocket message
|
|
655
|
+
* @returns Parsed message or null if invalid/keepalive
|
|
656
|
+
*/
|
|
657
|
+
static parseWebSocketMessage(event) {
|
|
658
|
+
try {
|
|
659
|
+
const payload = JSON.parse(event);
|
|
660
|
+
|
|
661
|
+
// Filter out keepalive messages
|
|
662
|
+
if (payload?.keepalive === 'true' || payload?.keepalive === true) {
|
|
663
|
+
return null;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// Normalize task data if present
|
|
667
|
+
if (payload?.data?.interaction) {
|
|
668
|
+
payload.data = (0, _taskDataNormalizer.normalizeTaskData)(payload.data);
|
|
669
|
+
}
|
|
670
|
+
return payload;
|
|
671
|
+
} catch (error) {
|
|
672
|
+
_loggerProxy.default.error('Failed to parse WebSocket message', {
|
|
511
673
|
module: _constants.TASK_MANAGER_FILE,
|
|
512
|
-
method:
|
|
674
|
+
method: 'parseWebSocketMessage',
|
|
675
|
+
error
|
|
513
676
|
});
|
|
677
|
+
return null;
|
|
514
678
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Prepare context for event processing
|
|
683
|
+
* @returns Event context or null if event type is invalid
|
|
684
|
+
*/
|
|
685
|
+
prepareEventContext(message) {
|
|
686
|
+
const eventType = message.data?.type || message.type;
|
|
687
|
+
if (!eventType || !isCcEvent(eventType)) {
|
|
688
|
+
return null;
|
|
689
|
+
}
|
|
690
|
+
const interactionId = message.data.interactionId;
|
|
691
|
+
const reservationInteractionId = message.data.reservationInteractionId;
|
|
692
|
+
let task = this.taskCollection[interactionId];
|
|
693
|
+
let ownerChangeResolvedByRelatedInteraction = false;
|
|
694
|
+
|
|
695
|
+
// When a campaign preview contact is accepted, the assigned event may arrive
|
|
696
|
+
// with a new interactionId while the task is stored under the original
|
|
697
|
+
// reservationInteractionId. Fall back to that key so the task is found.
|
|
698
|
+
if (!task && reservationInteractionId) {
|
|
699
|
+
task = this.taskCollection[reservationInteractionId];
|
|
700
|
+
if (task) {
|
|
701
|
+
// Re-key the task under the new interaction ID and remove the old entry
|
|
702
|
+
delete this.taskCollection[reservationInteractionId];
|
|
703
|
+
this.taskCollection[interactionId] = task;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
if (!task && eventType === _types2.CC_EVENTS.CONTACT_OWNER_CHANGED) {
|
|
707
|
+
const incomingShowsCurrentAgentPromotion = Boolean(this.agentId && message.data.interaction?.owner === this.agentId && TaskManager.isParticipantActiveOnMainInteraction(message.data, this.agentId));
|
|
708
|
+
task = this.findUniqueTaskByRelatedInteraction(message.data, candidate => {
|
|
709
|
+
const currentAgentId = this.agentId || candidate.data?.agentId;
|
|
710
|
+
return Boolean(currentAgentId && (TaskManager.isParticipantActiveOnMainInteraction(candidate.data, currentAgentId) || incomingShowsCurrentAgentPromotion));
|
|
711
|
+
}, {
|
|
712
|
+
includeCollectionKeys: true,
|
|
713
|
+
includeMainCallMediaKeys: true
|
|
714
|
+
});
|
|
715
|
+
ownerChangeResolvedByRelatedInteraction = Boolean(task);
|
|
716
|
+
}
|
|
717
|
+
let ownerChangeMainInteractionId;
|
|
718
|
+
if (task && eventType === _types2.CC_EVENTS.CONTACT_OWNER_CHANGED) {
|
|
719
|
+
const taskMainInteractionId = task.data?.interaction?.mainInteractionId;
|
|
720
|
+
const payloadMainInteractionId = message.data.interaction?.mainInteractionId;
|
|
721
|
+
const hasConflictingMainInteractionIds = Boolean(taskMainInteractionId && payloadMainInteractionId && taskMainInteractionId !== payloadMainInteractionId);
|
|
722
|
+
// A promoted child task can retain stale embedded IDs. Prefer the
|
|
723
|
+
// authoritative incoming main-call identity before local snapshot IDs.
|
|
724
|
+
const stableMainInteractionId = payloadMainInteractionId || TaskManager.getMainCallMediaId(message.data.interaction) || TaskManager.getMainCallMediaId(task.data?.interaction) || taskMainInteractionId || message.data.interaction?.interactionId || task.data?.interaction?.interactionId || task.data?.interactionId;
|
|
725
|
+
const taskAtStableKey = stableMainInteractionId ? this.taskCollection[stableMainInteractionId] : undefined;
|
|
726
|
+
if (!stableMainInteractionId || hasConflictingMainInteractionIds || taskAtStableKey && taskAtStableKey !== task) {
|
|
727
|
+
_loggerProxy.default.warn('Unable to safely correlate owner change to the main interaction task', {
|
|
728
|
+
module: _constants.TASK_MANAGER_FILE,
|
|
729
|
+
method: 'prepareEventContext',
|
|
730
|
+
interactionId: payloadMainInteractionId || taskMainInteractionId || message.data.interactionId
|
|
731
|
+
});
|
|
732
|
+
task = undefined;
|
|
733
|
+
} else if (ownerChangeResolvedByRelatedInteraction) {
|
|
734
|
+
ownerChangeMainInteractionId = stableMainInteractionId;
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
if (!task && MAIN_INTERACTION_CORRELATED_EVENTS.has(eventType)) {
|
|
738
|
+
task = this.findUniqueTaskByRelatedInteraction(message.data);
|
|
739
|
+
}
|
|
740
|
+
const wasConsultedTask = Boolean(task?.data?.isConsulted);
|
|
741
|
+
const computeWrapUpRequired = () => {
|
|
742
|
+
if (message.data.wrapUpRequired !== undefined) {
|
|
743
|
+
return message.data.wrapUpRequired;
|
|
744
|
+
}
|
|
745
|
+
if (message.data.isConsulted !== undefined) {
|
|
746
|
+
return !message.data.isConsulted;
|
|
747
|
+
}
|
|
748
|
+
return !wasConsultedTask;
|
|
749
|
+
};
|
|
750
|
+
let adjustedPayload = eventType === _types2.CC_EVENTS.AGENT_CONSULT_TRANSFERRED || eventType === _types2.CC_EVENTS.AGENT_BLIND_TRANSFERRED || eventType === _types2.CC_EVENTS.AGENT_VTEAM_TRANSFERRED ? {
|
|
751
|
+
...message.data,
|
|
752
|
+
wrapUpRequired: computeWrapUpRequired()
|
|
753
|
+
} : message.data;
|
|
754
|
+
if (task && ownerChangeMainInteractionId) {
|
|
755
|
+
adjustedPayload = {
|
|
756
|
+
...adjustedPayload,
|
|
757
|
+
interactionId: ownerChangeMainInteractionId
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
if (task && eventType === _types2.CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE) {
|
|
761
|
+
adjustedPayload = TaskManager.preserveConfirmedOwner(task, adjustedPayload);
|
|
762
|
+
}
|
|
763
|
+
if (WRAP_UP_STAMP_EVENTS.has(eventType)) {
|
|
764
|
+
adjustedPayload = TaskManager.stampWrapUpRequiredForEvent(eventType, adjustedPayload, this.agentId, task);
|
|
765
|
+
}
|
|
766
|
+
const stateMachineEvent = TaskManager.mapEventToTaskStateMachineEvent(eventType, adjustedPayload, this.agentId, task, this.configFlags?.enableWxBetterTogether);
|
|
767
|
+
_loggerProxy.default.info(`Handling task event ${eventType}`, {
|
|
768
|
+
module: _constants.TASK_MANAGER_FILE,
|
|
769
|
+
method: 'prepareEventContext',
|
|
770
|
+
interactionId
|
|
771
|
+
});
|
|
772
|
+
return {
|
|
773
|
+
eventType,
|
|
774
|
+
payload: adjustedPayload,
|
|
775
|
+
task,
|
|
776
|
+
stateMachineEvent
|
|
777
|
+
};
|
|
778
|
+
}
|
|
779
|
+
static isParticipantActiveOnMainInteraction(taskData, participantId) {
|
|
780
|
+
const interaction = taskData?.interaction;
|
|
781
|
+
const participant = interaction?.participants?.[participantId];
|
|
782
|
+
if (!participant || participant.hasLeft === true) return false;
|
|
783
|
+
return Object.values(interaction?.media ?? {}).some(media => media?.mType === _constants3.MEDIA_TYPE_MAIN_CALL && media.participants?.includes(participantId));
|
|
784
|
+
}
|
|
785
|
+
static getMainCallMediaId(interaction) {
|
|
786
|
+
return Object.entries(interaction?.media ?? {}).find(([, media]) => media?.mType === _constants3.MEDIA_TYPE_MAIN_CALL)?.[0];
|
|
787
|
+
}
|
|
788
|
+
static getStableMainInteractionId(payload) {
|
|
789
|
+
return payload.interaction?.mainInteractionId || TaskManager.getMainCallMediaId(payload.interaction) || payload.interaction?.interactionId || payload.interactionId;
|
|
790
|
+
}
|
|
791
|
+
canRecoverTaskFromContactOwnerChanged(payload) {
|
|
792
|
+
const interaction = payload.interaction;
|
|
793
|
+
const stableInteractionId = TaskManager.getStableMainInteractionId(payload);
|
|
794
|
+
return Boolean(this.agentId && stableInteractionId && interaction?.mediaType === _types.MEDIA_CHANNEL.TELEPHONY && interaction.isTerminated !== true && interaction.owner === this.agentId && TaskManager.isParticipantActiveOnMainInteraction(payload, this.agentId));
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
/**
|
|
798
|
+
* ParticipantLeftConference may arrive after an owner update while still carrying
|
|
799
|
+
* the departed owner. Keep the confirmed owner only when the new roster proves that
|
|
800
|
+
* owner is active on mainCall and identifies the incoming owner as departed.
|
|
801
|
+
*/
|
|
802
|
+
static preserveConfirmedOwner(task, payload) {
|
|
803
|
+
const confirmedOwner = task.data?.interaction?.owner;
|
|
804
|
+
const incomingOwner = payload.interaction?.owner;
|
|
805
|
+
if (!confirmedOwner || !incomingOwner || confirmedOwner === incomingOwner) {
|
|
806
|
+
return payload;
|
|
807
|
+
}
|
|
808
|
+
const confirmedOwnerIsActive = TaskManager.isParticipantActiveOnMainInteraction(payload, confirmedOwner);
|
|
809
|
+
const incomingOwnerIsExplicitlyDeparted = payload.participantId === incomingOwner || payload.interaction?.participants?.[incomingOwner]?.hasLeft === true;
|
|
810
|
+
if (!confirmedOwnerIsActive || !incomingOwnerIsExplicitlyDeparted) {
|
|
811
|
+
return payload;
|
|
812
|
+
}
|
|
813
|
+
return {
|
|
814
|
+
...payload,
|
|
815
|
+
owner: confirmedOwner,
|
|
816
|
+
interaction: {
|
|
817
|
+
...payload.interaction,
|
|
818
|
+
owner: confirmedOwner
|
|
819
|
+
}
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* Resolve lifecycle events that are emitted for the main interaction while the
|
|
825
|
+
* local task can still be indexed by a child consult interaction. Exact task
|
|
826
|
+
* keys are handled before this fallback. Multiple aliases of the same task are
|
|
827
|
+
* treated as one candidate; genuinely ambiguous matches are intentionally ignored.
|
|
828
|
+
*/
|
|
829
|
+
findUniqueTaskByRelatedInteraction(payload, candidateFilter = () => true, options = {}) {
|
|
830
|
+
const candidates = this.findRelatedTasks(payload, options).filter(candidateFilter);
|
|
831
|
+
if (candidates.length === 1) {
|
|
832
|
+
return candidates[0];
|
|
833
|
+
}
|
|
834
|
+
if (candidates.length > 1) {
|
|
835
|
+
_loggerProxy.default.warn('Unable to correlate task event to a unique main interaction task', {
|
|
521
836
|
module: _constants.TASK_MANAGER_FILE,
|
|
522
|
-
method:
|
|
523
|
-
interactionId:
|
|
837
|
+
method: 'findUniqueTaskByRelatedInteraction',
|
|
838
|
+
interactionId: payload.interaction?.mainInteractionId || payload.interaction?.parentInteractionId || payload.interaction?.callProcessingDetails?.parentInteractionId || payload.interactionId
|
|
524
839
|
});
|
|
525
|
-
return task;
|
|
526
840
|
}
|
|
841
|
+
return undefined;
|
|
842
|
+
}
|
|
843
|
+
findRelatedTasks(payload, options = {}) {
|
|
844
|
+
const {
|
|
845
|
+
includeCollectionKeys = false,
|
|
846
|
+
includeMainCallMediaKeys = false
|
|
847
|
+
} = options;
|
|
848
|
+
const payloadInteractionIds = new Set([payload.interactionId, payload.interaction?.interactionId, payload.interaction?.mainInteractionId, payload.interaction?.parentInteractionId, payload.interaction?.callProcessingDetails?.parentInteractionId, ...(includeMainCallMediaKeys ? [TaskManager.getMainCallMediaId(payload.interaction)] : [])].filter(relatedInteractionId => Boolean(relatedInteractionId)));
|
|
849
|
+
if (payloadInteractionIds.size === 0) return [];
|
|
850
|
+
return [...new Set(Object.entries(this.taskCollection).filter(([taskId, candidate]) => {
|
|
851
|
+
const taskInteraction = candidate?.data?.interaction;
|
|
852
|
+
const candidateInteractionIds = [...(includeCollectionKeys ? [taskId] : []), candidate?.data?.interactionId, taskInteraction?.interactionId, taskInteraction?.mainInteractionId, taskInteraction?.parentInteractionId, taskInteraction?.callProcessingDetails?.parentInteractionId, ...(includeMainCallMediaKeys ? [TaskManager.getMainCallMediaId(taskInteraction)] : [])];
|
|
853
|
+
return candidateInteractionIds.some(relatedInteractionId => Boolean(relatedInteractionId) && payloadInteractionIds.has(relatedInteractionId));
|
|
854
|
+
}).map(([, candidate]) => candidate))];
|
|
527
855
|
}
|
|
528
856
|
|
|
529
857
|
/**
|
|
530
|
-
*
|
|
531
|
-
*
|
|
532
|
-
*
|
|
533
|
-
*
|
|
534
|
-
*
|
|
858
|
+
* Handle task lifecycle events and determine required actions
|
|
859
|
+
*
|
|
860
|
+
* Delegates to specific event handlers based on event type. Each handler
|
|
861
|
+
* is responsible for TaskManager-level concerns:
|
|
862
|
+
* - Task creation and collection management
|
|
863
|
+
* - Metrics tracking
|
|
864
|
+
* - Resource cleanup decisions
|
|
865
|
+
*
|
|
866
|
+
* Note: Task-level state transitions and event emissions are handled by
|
|
867
|
+
* the task state machine via sendStateMachineEvent()
|
|
535
868
|
*/
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
869
|
+
handleTaskLifecycleEvent(context) {
|
|
870
|
+
const {
|
|
871
|
+
eventType
|
|
872
|
+
} = context;
|
|
873
|
+
switch (eventType) {
|
|
874
|
+
case _types2.CC_EVENTS.AGENT_CONTACT_RESERVED:
|
|
875
|
+
return this.handleContactReserved(context);
|
|
876
|
+
case _types2.CC_EVENTS.AGENT_CONTACT:
|
|
877
|
+
return this.handleAgentContact(context);
|
|
878
|
+
case _types2.CC_EVENTS.CONTACT_MERGED:
|
|
879
|
+
return this.handleContactMergedEvent(context);
|
|
880
|
+
case _types2.CC_EVENTS.CONTACT_OWNER_CHANGED:
|
|
881
|
+
return this.handleContactOwnerChanged(context);
|
|
882
|
+
case _types2.CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION:
|
|
883
|
+
return this.handleCampaignPreviewReservation(context);
|
|
884
|
+
case _types2.CC_EVENTS.CAMPAIGN_CONTACT_UPDATED:
|
|
885
|
+
return this.handleCampaignContactUpdated(context);
|
|
886
|
+
default:
|
|
887
|
+
return {
|
|
888
|
+
task: context.task
|
|
889
|
+
};
|
|
540
890
|
}
|
|
541
|
-
|
|
542
|
-
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
/**
|
|
894
|
+
* Recover the promoted agent's active voice task when local task state was lost.
|
|
895
|
+
* Existing related tasks are never replaced: an unresolved or ambiguous relation
|
|
896
|
+
* is safer to ignore than to create a duplicate task.
|
|
897
|
+
*/
|
|
898
|
+
handleContactOwnerChanged(context) {
|
|
899
|
+
if (context.task) {
|
|
900
|
+
return {
|
|
901
|
+
task: context.task
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
const {
|
|
905
|
+
payload
|
|
906
|
+
} = context;
|
|
907
|
+
if (this.findRelatedTasks(payload, {
|
|
908
|
+
includeCollectionKeys: true,
|
|
909
|
+
includeMainCallMediaKeys: true
|
|
910
|
+
}).length > 0 || !this.canRecoverTaskFromContactOwnerChanged(payload)) {
|
|
911
|
+
return {};
|
|
912
|
+
}
|
|
913
|
+
const stableInteractionId = TaskManager.getStableMainInteractionId(payload);
|
|
914
|
+
if (!stableInteractionId || this.taskCollection[stableInteractionId]) {
|
|
915
|
+
return {};
|
|
916
|
+
}
|
|
917
|
+
const normalizedPayload = payload.interactionId === stableInteractionId ? payload : {
|
|
918
|
+
...payload,
|
|
919
|
+
interactionId: stableInteractionId
|
|
920
|
+
};
|
|
921
|
+
const taskData = {
|
|
922
|
+
...normalizedPayload,
|
|
923
|
+
owner: normalizedPayload.interaction.owner,
|
|
924
|
+
wrapUpRequired: normalizedPayload.interaction.participants?.[this.agentId]?.isWrapUp || false,
|
|
925
|
+
isConferenceInProgress: (0, _TaskUtils.getIsConferenceInProgress)(normalizedPayload),
|
|
926
|
+
isConsulted: false,
|
|
927
|
+
isAutoAnswering: false
|
|
928
|
+
};
|
|
929
|
+
const task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId, this.answerCallOnWebexService);
|
|
930
|
+
this.taskCollection[stableInteractionId] = task;
|
|
931
|
+
|
|
932
|
+
// Restore the actor before installing listeners so the internal hydrate does
|
|
933
|
+
// not leak as a second public hydrate or as an incoming-task notification.
|
|
934
|
+
task.sendStateMachineEvent({
|
|
935
|
+
type: _stateMachine.TaskEvent.HYDRATE,
|
|
936
|
+
taskData,
|
|
937
|
+
agentId: this.agentId
|
|
938
|
+
});
|
|
939
|
+
this.setupTaskListeners(task);
|
|
940
|
+
context.payload = taskData;
|
|
941
|
+
context.stateMachineEvent = {
|
|
942
|
+
type: _stateMachine.TaskEvent.CONTACT_OWNER_CHANGED,
|
|
943
|
+
taskData
|
|
944
|
+
};
|
|
945
|
+
return {
|
|
946
|
+
task
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
handleCampaignContactUpdated(context) {
|
|
950
|
+
const {
|
|
951
|
+
payload
|
|
952
|
+
} = context;
|
|
953
|
+
let {
|
|
954
|
+
task
|
|
955
|
+
} = context;
|
|
956
|
+
// CampaignContactUpdated is a non-terminal event (e.g., next contact after skip/remove).
|
|
957
|
+
// Update the task data and emit an event so consumers can react to the updated contact.
|
|
958
|
+
// Do NOT remove the task or emit TASK_END — cleanup is handled by CONTACT_ENDED.
|
|
959
|
+
if (task) {
|
|
960
|
+
// Carry forward campaign preview fields from existing task data since the updated
|
|
961
|
+
// contact payload may not include them, and reconcileData would delete them.
|
|
962
|
+
const existingCpd = task.data?.interaction?.callProcessingDetails;
|
|
963
|
+
const updatedData = {
|
|
964
|
+
...payload
|
|
965
|
+
};
|
|
966
|
+
if (existingCpd) {
|
|
967
|
+
const campaignFields = {
|
|
968
|
+
...(existingCpd.campaignPreviewAutoAction && {
|
|
969
|
+
campaignPreviewAutoAction: existingCpd.campaignPreviewAutoAction
|
|
970
|
+
}),
|
|
971
|
+
...(existingCpd.campaignPreviewOfferTimeout && {
|
|
972
|
+
campaignPreviewOfferTimeout: existingCpd.campaignPreviewOfferTimeout
|
|
973
|
+
}),
|
|
974
|
+
...(existingCpd.campaignPreviewSkipDisabled && {
|
|
975
|
+
campaignPreviewSkipDisabled: existingCpd.campaignPreviewSkipDisabled
|
|
976
|
+
}),
|
|
977
|
+
...(existingCpd.campaignPreviewRemoveDisabled && {
|
|
978
|
+
campaignPreviewRemoveDisabled: existingCpd.campaignPreviewRemoveDisabled
|
|
979
|
+
})
|
|
980
|
+
};
|
|
981
|
+
if (!updatedData.interaction) {
|
|
982
|
+
updatedData.interaction = {};
|
|
983
|
+
}
|
|
984
|
+
updatedData.interaction = {
|
|
985
|
+
...updatedData.interaction,
|
|
986
|
+
callProcessingDetails: {
|
|
987
|
+
...campaignFields,
|
|
988
|
+
...(updatedData.interaction.callProcessingDetails || {})
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
}
|
|
992
|
+
_loggerProxy.default.log('Campaign contact updated - carrying forward preview fields', {
|
|
543
993
|
module: _constants.TASK_MANAGER_FILE,
|
|
544
994
|
method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
|
|
545
|
-
interactionId:
|
|
995
|
+
interactionId: payload.interactionId,
|
|
996
|
+
data: {
|
|
997
|
+
hasCpd: !!updatedData.interaction?.callProcessingDetails,
|
|
998
|
+
autoAction: updatedData.interaction?.callProcessingDetails?.campaignPreviewAutoAction,
|
|
999
|
+
skipDisabled: updatedData.interaction?.callProcessingDetails?.campaignPreviewSkipDisabled,
|
|
1000
|
+
removeDisabled: updatedData.interaction?.callProcessingDetails?.campaignPreviewRemoveDisabled
|
|
1001
|
+
}
|
|
546
1002
|
});
|
|
547
|
-
|
|
548
|
-
task
|
|
1003
|
+
task = this.updateTaskData(task, updatedData);
|
|
1004
|
+
task.emit(_types.TASK_EVENTS.TASK_CAMPAIGN_CONTACT_UPDATED, task);
|
|
1005
|
+
}
|
|
1006
|
+
return {
|
|
1007
|
+
task
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* Creates or updates a task for campaign preview reservation.
|
|
1013
|
+
* TASK_CAMPAIGN_PREVIEW_RESERVATION is emitted by the state machine (campaign TASK_INCOMING branch).
|
|
1014
|
+
*/
|
|
1015
|
+
handleCampaignPreviewReservation(context) {
|
|
1016
|
+
const {
|
|
1017
|
+
payload
|
|
1018
|
+
} = context;
|
|
1019
|
+
let {
|
|
1020
|
+
task
|
|
1021
|
+
} = context;
|
|
1022
|
+
_loggerProxy.default.log('Campaign preview reservation received', {
|
|
1023
|
+
module: _constants.TASK_MANAGER_FILE,
|
|
1024
|
+
method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
|
|
1025
|
+
interactionId: payload.interactionId
|
|
1026
|
+
});
|
|
1027
|
+
if (!task) {
|
|
1028
|
+
task = _TaskFactory.default.createTask(this.contact, this.webCallingService, {
|
|
1029
|
+
...payload,
|
|
1030
|
+
wrapUpRequired: false,
|
|
1031
|
+
isConferenceInProgress: false,
|
|
1032
|
+
isAutoAnswering: false
|
|
1033
|
+
}, this.configFlags, this.wrapupData, this.agentId, this.answerCallOnWebexService);
|
|
1034
|
+
this.setupTaskListeners(task);
|
|
1035
|
+
this.taskCollection[payload.interactionId] = task;
|
|
549
1036
|
} else {
|
|
550
|
-
|
|
551
|
-
|
|
1037
|
+
task = this.updateTaskData(task, payload);
|
|
1038
|
+
}
|
|
1039
|
+
return {
|
|
1040
|
+
task
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
/**
|
|
1045
|
+
* Handle AGENT_CONTACT_RESERVED event
|
|
1046
|
+
* Creates a new task; state machine event is sent during processing
|
|
1047
|
+
*/
|
|
1048
|
+
handleContactReserved(context) {
|
|
1049
|
+
const {
|
|
1050
|
+
payload
|
|
1051
|
+
} = context;
|
|
1052
|
+
const isConsultedTask = payload.isConsulted === true || (0, _TaskUtils.isSecondaryEpDnAgent)(payload.interaction);
|
|
1053
|
+
const shouldAutoAnswer = (0, _TaskUtils.shouldAutoAnswerTask)(payload, this.agentId, this.webCallingService.loginOption, this.webRtcEnabled);
|
|
1054
|
+
const taskData = {
|
|
1055
|
+
...payload,
|
|
1056
|
+
isConsulted: isConsultedTask,
|
|
1057
|
+
isAutoAnswering: shouldAutoAnswer
|
|
1058
|
+
};
|
|
1059
|
+
const task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId, this.answerCallOnWebexService);
|
|
1060
|
+
this.setupTaskListeners(task);
|
|
1061
|
+
this.taskCollection[payload.interactionId] = task;
|
|
1062
|
+
return {
|
|
1063
|
+
task
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
/**
|
|
1068
|
+
* Handle AGENT_CONTACT event
|
|
1069
|
+
* Re-creates task if missing (multi-session scenario)
|
|
1070
|
+
*/
|
|
1071
|
+
handleAgentContact(context) {
|
|
1072
|
+
let {
|
|
1073
|
+
task
|
|
1074
|
+
} = context;
|
|
1075
|
+
const {
|
|
1076
|
+
payload
|
|
1077
|
+
} = context;
|
|
1078
|
+
if (!task) {
|
|
1079
|
+
const isConsultedTask = payload.isConsulted === true || (0, _TaskUtils.isSecondaryEpDnAgent)(payload.interaction);
|
|
1080
|
+
const shouldAutoAnswer = (0, _TaskUtils.shouldAutoAnswerTask)(payload, this.agentId, this.webCallingService.loginOption, this.webRtcEnabled);
|
|
1081
|
+
const taskData = {
|
|
1082
|
+
...payload,
|
|
1083
|
+
isConsulted: isConsultedTask,
|
|
1084
|
+
wrapUpRequired: payload.interaction?.participants?.[this.agentId]?.isWrapUp || false,
|
|
1085
|
+
isConferenceInProgress: (0, _TaskUtils.getIsConferenceInProgress)(payload),
|
|
1086
|
+
isAutoAnswering: shouldAutoAnswer
|
|
1087
|
+
};
|
|
1088
|
+
task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId, this.answerCallOnWebexService);
|
|
1089
|
+
this.setupTaskListeners(task);
|
|
1090
|
+
this.taskCollection[payload.interactionId] = task;
|
|
1091
|
+
}
|
|
1092
|
+
return {
|
|
1093
|
+
task
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
updateTaskData(task, taskData) {
|
|
1097
|
+
if (!task) {
|
|
1098
|
+
throw new Error('Task not found for update');
|
|
1099
|
+
}
|
|
1100
|
+
const snapshot = task.stateMachineService?.getSnapshot?.();
|
|
1101
|
+
const isConsultingFlow = snapshot?.value === 'CONSULTING' || taskData.interaction?.state === 'consulting';
|
|
1102
|
+
const updateTaskData = isConsultingFlow ? {
|
|
1103
|
+
...taskData,
|
|
1104
|
+
destAgentId: taskData.destAgentId ?? snapshot?.context?.consultDestinationAgentId ?? null,
|
|
1105
|
+
destinationType: taskData.destinationType ?? snapshot?.context?.consultDestinationType ?? null
|
|
1106
|
+
} : taskData;
|
|
1107
|
+
task.updateTaskData(updateTaskData);
|
|
1108
|
+
|
|
1109
|
+
// A main-interaction lifecycle event can re-key a child consult task. Remove
|
|
1110
|
+
// any aliases first so consumers never observe duplicate task entries.
|
|
1111
|
+
Object.entries(this.taskCollection).forEach(([taskId, candidate]) => {
|
|
1112
|
+
if (candidate === task && taskId !== taskData.interactionId) {
|
|
1113
|
+
delete this.taskCollection[taskId];
|
|
1114
|
+
}
|
|
1115
|
+
});
|
|
1116
|
+
this.taskCollection[taskData.interactionId] = task;
|
|
1117
|
+
return task;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
/**
|
|
1121
|
+
* Setup listeners for task events that need to be bubbled up to TaskManager
|
|
1122
|
+
* This replaces the previous callback injection pattern
|
|
1123
|
+
*/
|
|
1124
|
+
setupTaskListeners(task) {
|
|
1125
|
+
// Listen for TASK_INCOMING and re-emit so webex.cc can notify consumers
|
|
1126
|
+
task.on(_types.TASK_EVENTS.TASK_INCOMING, t => {
|
|
1127
|
+
_loggerProxy.default.log(`Task incoming event received`, {
|
|
552
1128
|
module: _constants.TASK_MANAGER_FILE,
|
|
553
1129
|
method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
|
|
554
|
-
interactionId:
|
|
1130
|
+
interactionId: t.data?.interactionId
|
|
555
1131
|
});
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
1132
|
+
this.emit(_types.TASK_EVENTS.TASK_INCOMING, t);
|
|
1133
|
+
});
|
|
1134
|
+
task.on(_types.TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, t => {
|
|
1135
|
+
_loggerProxy.default.log(`Campaign preview reservation event received`, {
|
|
1136
|
+
module: _constants.TASK_MANAGER_FILE,
|
|
1137
|
+
method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
|
|
1138
|
+
interactionId: t.data?.interactionId
|
|
1139
|
+
});
|
|
1140
|
+
this.emit(_types.TASK_EVENTS.TASK_CAMPAIGN_PREVIEW_RESERVATION, t);
|
|
1141
|
+
});
|
|
1142
|
+
|
|
1143
|
+
// Listen for TASK_HYDRATE on the task and re-emit on TaskManager
|
|
1144
|
+
task.on(_types.TASK_EVENTS.TASK_HYDRATE, t => {
|
|
1145
|
+
// Task data is already updated by the task itself before emitting
|
|
1146
|
+
this.emit(_types.TASK_EVENTS.TASK_HYDRATE, t);
|
|
1147
|
+
});
|
|
1148
|
+
task.on(_types.TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, t => {
|
|
1149
|
+
this.emit(_types.TASK_EVENTS.TASK_MULTI_LOGIN_HYDRATE, t);
|
|
1150
|
+
});
|
|
1151
|
+
|
|
1152
|
+
// Listen for internal cleanup signal emitted by the state machine
|
|
1153
|
+
task.on(_types.TASK_EVENTS.TASK_CLEANUP, (t, options) => {
|
|
1154
|
+
this.handleTaskCleanup(t);
|
|
1155
|
+
if (options?.removeFromCollection) {
|
|
1156
|
+
const taskIsStillCollected = Object.values(this.taskCollection).some(candidate => candidate === t);
|
|
1157
|
+
if (taskIsStillCollected) {
|
|
1158
|
+
this.removeTaskFromCollection(t);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
});
|
|
565
1162
|
}
|
|
566
1163
|
removeTaskFromCollection(task) {
|
|
1164
|
+
if (typeof task.cancelAutoWrapupTimer === 'function') {
|
|
1165
|
+
task.cancelAutoWrapupTimer();
|
|
1166
|
+
}
|
|
567
1167
|
if (task?.data?.interactionId) {
|
|
568
|
-
|
|
1168
|
+
Object.entries(this.taskCollection).forEach(([taskId, candidate]) => {
|
|
1169
|
+
if (candidate === task) {
|
|
1170
|
+
delete this.taskCollection[taskId];
|
|
1171
|
+
}
|
|
1172
|
+
});
|
|
569
1173
|
_loggerProxy.default.info(`Task removed from collection`, {
|
|
570
1174
|
module: _constants.TASK_MANAGER_FILE,
|
|
571
1175
|
method: _constants2.METHODS.REMOVE_TASK_FROM_COLLECTION,
|
|
@@ -575,62 +1179,62 @@ class TaskManager extends _events.default {
|
|
|
575
1179
|
}
|
|
576
1180
|
|
|
577
1181
|
/**
|
|
578
|
-
* Handles
|
|
579
|
-
*
|
|
580
|
-
*
|
|
581
|
-
*
|
|
582
|
-
* 2. Agent-initiated WebRTC outdial calls
|
|
583
|
-
* 3. Agent-initiated digital outbound (Email/SMS) without previous transfers
|
|
584
|
-
*
|
|
585
|
-
* @param task - The task to auto-answer
|
|
1182
|
+
* Handles CONTACT_MERGED event logic
|
|
1183
|
+
* @param task - The task to process
|
|
1184
|
+
* @param taskData - The task data from the event payload
|
|
1185
|
+
* @returns Updated or newly created task
|
|
586
1186
|
* @private
|
|
587
1187
|
*/
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
1188
|
+
handleContactMergedEvent(context) {
|
|
1189
|
+
const {
|
|
1190
|
+
payload
|
|
1191
|
+
} = context;
|
|
1192
|
+
let task = context.task;
|
|
1193
|
+
if (payload.childInteractionId && this.taskCollection[payload.childInteractionId]) {
|
|
1194
|
+
this.removeTaskFromCollection(this.taskCollection[payload.childInteractionId]);
|
|
591
1195
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
method: 'handleAutoAnswer',
|
|
595
|
-
interactionId: task.data.interactionId
|
|
596
|
-
});
|
|
597
|
-
try {
|
|
598
|
-
await task.accept();
|
|
599
|
-
_loggerProxy.default.info(`Task auto-answered successfully`, {
|
|
1196
|
+
if (task) {
|
|
1197
|
+
_loggerProxy.default.log(`Got CONTACT_MERGED: Task already exists in collection`, {
|
|
600
1198
|
module: _constants.TASK_MANAGER_FILE,
|
|
601
|
-
method:
|
|
602
|
-
interactionId:
|
|
603
|
-
});
|
|
604
|
-
|
|
605
|
-
// Track successful auto-answer
|
|
606
|
-
this.metricsManager.trackEvent(_constants3.METRIC_EVENT_NAMES.TASK_AUTO_ANSWER_SUCCESS, {
|
|
607
|
-
taskId: task.data.interactionId,
|
|
608
|
-
mediaType: task.data.interaction.mediaType,
|
|
609
|
-
isAutoAnswered: true
|
|
610
|
-
}, ['behavioral', 'operational']);
|
|
611
|
-
// Emit task:autoAnswered event for widgets/UI to react
|
|
612
|
-
task.emit(_types.TASK_EVENTS.TASK_AUTO_ANSWERED, task);
|
|
613
|
-
} catch (error) {
|
|
614
|
-
// Reset isAutoAnswering flag on failure
|
|
615
|
-
task.updateTaskData({
|
|
616
|
-
...task.data,
|
|
617
|
-
isAutoAnswering: false
|
|
1199
|
+
method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
|
|
1200
|
+
interactionId: payload.interactionId
|
|
618
1201
|
});
|
|
619
|
-
|
|
1202
|
+
// update the task data
|
|
1203
|
+
this.updateTaskData(task, payload);
|
|
1204
|
+
} else {
|
|
1205
|
+
// Case2 : Task is not present in taskCollection
|
|
1206
|
+
_loggerProxy.default.log(`Got CONTACT_MERGED : Creating new task in taskManager`, {
|
|
620
1207
|
module: _constants.TASK_MANAGER_FILE,
|
|
621
|
-
method:
|
|
622
|
-
interactionId:
|
|
623
|
-
error
|
|
1208
|
+
method: _constants2.METHODS.REGISTER_TASK_LISTENERS,
|
|
1209
|
+
interactionId: payload.interactionId
|
|
624
1210
|
});
|
|
1211
|
+
const taskData = {
|
|
1212
|
+
...payload,
|
|
1213
|
+
wrapUpRequired: payload.interaction?.participants?.[this.agentId]?.isWrapUp || false,
|
|
1214
|
+
isConferenceInProgress: (0, _TaskUtils.getIsConferenceInProgress)(payload),
|
|
1215
|
+
isConsulted: false
|
|
1216
|
+
};
|
|
1217
|
+
task = _TaskFactory.default.createTask(this.contact, this.webCallingService, taskData, this.configFlags, this.wrapupData, this.agentId, this.answerCallOnWebexService);
|
|
1218
|
+
this.taskCollection[payload.interactionId] = task;
|
|
625
1219
|
|
|
626
|
-
//
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
1220
|
+
// Transition the new task out of IDLE immediately so UI controls are
|
|
1221
|
+
// computed before TASK_MERGED is emitted. This handles the race where
|
|
1222
|
+
// AgentContactAssigned arrives before ContactMerged and gets dropped.
|
|
1223
|
+
// Send HYDRATE before setupTaskListeners so the emitTaskHydrate action
|
|
1224
|
+
// doesn't bubble up to the Widget (avoids duplicate listener registration).
|
|
1225
|
+
task.sendStateMachineEvent({
|
|
1226
|
+
type: _stateMachine.TaskEvent.HYDRATE,
|
|
1227
|
+
taskData,
|
|
1228
|
+
agentId: this.agentId
|
|
1229
|
+
});
|
|
1230
|
+
this.setupTaskListeners(task);
|
|
633
1231
|
}
|
|
1232
|
+
if (task) {
|
|
1233
|
+
this.emit(_types.TASK_EVENTS.TASK_MERGED, task);
|
|
1234
|
+
}
|
|
1235
|
+
return {
|
|
1236
|
+
task
|
|
1237
|
+
};
|
|
634
1238
|
}
|
|
635
1239
|
|
|
636
1240
|
/**
|
|
@@ -640,7 +1244,7 @@ class TaskManager extends _events.default {
|
|
|
640
1244
|
*/
|
|
641
1245
|
handleTaskCleanup(task) {
|
|
642
1246
|
// Clean up Desktop/WebRTC calling resources for browser-based telephony tasks
|
|
643
|
-
if (this.webCallingService.loginOption === _types3.LoginOption.BROWSER && task.data.interaction.mediaType ===
|
|
1247
|
+
if (this.webCallingService.loginOption === _types3.LoginOption.BROWSER && task.data.interaction.mediaType === _types.MEDIA_CHANNEL.TELEPHONY && task instanceof _WebRTC.default) {
|
|
644
1248
|
task.unregisterWebCallListeners();
|
|
645
1249
|
this.webCallingService.cleanUpCall();
|
|
646
1250
|
}
|
|
@@ -663,11 +1267,14 @@ class TaskManager extends _events.default {
|
|
|
663
1267
|
*/
|
|
664
1268
|
requestRealTimeTranscripts(eventType, interactionId) {
|
|
665
1269
|
const action = _constants2.TRANSCRIPT_EVENT_MAP[eventType];
|
|
666
|
-
if (!action || !this.apiAIAssistant
|
|
667
|
-
|
|
1270
|
+
if (!action || !this.apiAIAssistant) return;
|
|
1271
|
+
if (this.configFlags?.aiFeature?.realtimeTranscripts?.enable !== true) return;
|
|
1272
|
+
this.apiAIAssistant.sendEvent(this.agentId, interactionId, _types3.AIAssistantEventType.CUSTOM_EVENT, _types3.AIAssistantEventName.GET_TRANSCRIPTS, {
|
|
1273
|
+
action
|
|
1274
|
+
}).catch(error => {
|
|
668
1275
|
_loggerProxy.default.error(`Failed to send transcript ${action} event`, {
|
|
669
1276
|
module: _constants.TASK_MANAGER_FILE,
|
|
670
|
-
method:
|
|
1277
|
+
method: _constants2.METHODS.REQUEST_REAL_TIME_TRANSCRIPTS,
|
|
671
1278
|
interactionId,
|
|
672
1279
|
error
|
|
673
1280
|
});
|
|
@@ -676,18 +1283,62 @@ class TaskManager extends _events.default {
|
|
|
676
1283
|
getTask(taskId) {
|
|
677
1284
|
return this.taskCollection[taskId];
|
|
678
1285
|
}
|
|
1286
|
+
getAllTasks() {
|
|
1287
|
+
return {
|
|
1288
|
+
...this.taskCollection
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
679
1291
|
|
|
680
1292
|
/**
|
|
681
|
-
*
|
|
1293
|
+
* Propagate runtime enableWxBetterTogether changes to config flags and active tasks.
|
|
682
1294
|
*/
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
1295
|
+
applyEnableWxBetterTogether(enabled) {
|
|
1296
|
+
if (this.configFlags) {
|
|
1297
|
+
this.configFlags = {
|
|
1298
|
+
...this.configFlags,
|
|
1299
|
+
enableWxBetterTogether: enabled
|
|
1300
|
+
};
|
|
1301
|
+
}
|
|
1302
|
+
Object.values(this.taskCollection).forEach(task => {
|
|
1303
|
+
task.setEnableWxBetterTogether(enabled);
|
|
1304
|
+
if (enabled) {
|
|
1305
|
+
TaskManager.syncWxAppMuteFromCallDetailsForTask(task);
|
|
1306
|
+
}
|
|
1307
|
+
});
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
/**
|
|
1311
|
+
* Propagate wxApp mute state from Mercury sync to active voice tasks.
|
|
1312
|
+
*/
|
|
1313
|
+
applyWxAppMuteStateFromSync(callId, muted) {
|
|
1314
|
+
Object.values(this.taskCollection).forEach(task => {
|
|
1315
|
+
task.applyWxAppMuteStateFromSync(callId, muted);
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
/**
|
|
1320
|
+
* Re-seeds wxApp mute state from GET telephony/calls/{callId} for one task.
|
|
1321
|
+
*/
|
|
1322
|
+
static syncWxAppMuteFromCallDetailsForTask(task) {
|
|
1323
|
+
const sync = task.syncWxAppMuteFromCallDetails?.bind(task);
|
|
1324
|
+
if (typeof sync === 'function') {
|
|
1325
|
+
sync().catch(() => undefined);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
/**
|
|
1330
|
+
* Re-seeds wxApp mute for all engaged voice tasks (reload / runtime enable backfill).
|
|
1331
|
+
*/
|
|
1332
|
+
syncWxAppMuteFromCallDetailsForAllTasks() {
|
|
1333
|
+
Object.values(this.taskCollection).forEach(task => {
|
|
1334
|
+
TaskManager.syncWxAppMuteFromCallDetailsForTask(task);
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1337
|
+
static getTaskManager(apiAIAssistant, contact, webCallingService, webSocketManager, rtdWebSocketManager) {
|
|
687
1338
|
if (!TaskManager.taskManager) {
|
|
688
|
-
TaskManager.taskManager = new TaskManager(apiAIAssistant, contact, webCallingService, webSocketManager);
|
|
1339
|
+
TaskManager.taskManager = new TaskManager(apiAIAssistant, contact, webCallingService, webSocketManager, rtdWebSocketManager);
|
|
689
1340
|
}
|
|
690
|
-
return
|
|
1341
|
+
return TaskManager.taskManager;
|
|
691
1342
|
}
|
|
692
1343
|
}
|
|
693
1344
|
exports.default = TaskManager;
|