@webex/contact-center 3.12.0-next.8 → 3.12.0-task-refactor.1
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/AGENTS.md +438 -0
- package/ai-docs/README.md +131 -0
- package/ai-docs/RULES.md +455 -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 +65 -123
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +13 -2
- package/dist/constants.js.map +1 -1
- package/dist/index.js +13 -5
- package/dist/index.js.map +1 -1
- package/dist/metrics/behavioral-events.js +26 -13
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +7 -6
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/ApiAiAssistant.js +0 -3
- package/dist/services/ApiAiAssistant.js.map +1 -1
- package/dist/services/config/Util.js +2 -3
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/types.js +16 -14
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/constants.js +0 -1
- package/dist/services/constants.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +79 -55
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +17 -92
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +5 -25
- 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 -2
- package/dist/services/index.js.map +1 -1
- package/dist/services/task/Task.js +644 -0
- package/dist/services/task/Task.js.map +1 -0
- package/dist/services/task/TaskFactory.js +45 -0
- package/dist/services/task/TaskFactory.js.map +1 -0
- package/dist/services/task/TaskManager.js +556 -532
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +132 -28
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/constants.js +7 -6
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/dialer.js +0 -51
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/digital/Digital.js +77 -0
- package/dist/services/task/digital/Digital.js.map +1 -0
- package/dist/services/task/state-machine/TaskStateMachine.js +634 -0
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
- package/dist/services/task/state-machine/actions.js +366 -0
- package/dist/services/task/state-machine/actions.js.map +1 -0
- package/dist/services/task/state-machine/constants.js +139 -0
- package/dist/services/task/state-machine/constants.js.map +1 -0
- package/dist/services/task/state-machine/guards.js +256 -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 +369 -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 +157 -18
- package/dist/services/task/types.js.map +1 -1
- package/dist/services/task/voice/Voice.js +1031 -0
- package/dist/services/task/voice/Voice.js.map +1 -0
- package/dist/services/task/voice/WebRTC.js +149 -0
- package/dist/services/task/voice/WebRTC.js.map +1 -0
- package/dist/types/cc.d.ts +4 -33
- package/dist/types/constants.d.ts +13 -2
- package/dist/types/index.d.ts +11 -5
- package/dist/types/metrics/constants.d.ts +5 -3
- package/dist/types/services/ApiAiAssistant.d.ts +1 -1
- package/dist/types/services/config/types.d.ts +97 -25
- package/dist/types/services/core/Err.d.ts +0 -2
- package/dist/types/services/core/Utils.d.ts +25 -23
- package/dist/types/services/core/aqm-reqs.d.ts +0 -49
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -1
- package/dist/types/services/core/websocket/connection-service.d.ts +0 -1
- 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 +146 -0
- package/dist/types/services/task/TaskFactory.d.ts +12 -0
- package/dist/types/services/task/TaskUtils.d.ts +39 -8
- package/dist/types/services/task/constants.d.ts +5 -4
- package/dist/types/services/task/dialer.d.ts +0 -15
- package/dist/types/services/task/digital/Digital.d.ts +22 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +906 -0
- package/dist/types/services/task/state-machine/actions.d.ts +8 -0
- package/dist/types/services/task/state-machine/constants.d.ts +91 -0
- package/dist/types/services/task/state-machine/guards.d.ts +78 -0
- package/dist/types/services/task/state-machine/index.d.ts +13 -0
- package/dist/types/services/task/state-machine/types.d.ts +256 -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 +539 -88
- package/dist/types/services/task/voice/Voice.d.ts +183 -0
- package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
- package/dist/types/types.d.ts +68 -0
- package/dist/types/webex.d.ts +1 -0
- package/dist/types.js +70 -0
- package/dist/types.js.map +1 -1
- package/dist/webex.js +14 -2
- package/dist/webex.js.map +1 -1
- package/package.json +14 -11
- package/src/cc.ts +91 -177
- package/src/constants.ts +13 -2
- package/src/index.ts +14 -5
- package/src/metrics/ai-docs/AGENTS.md +348 -0
- package/src/metrics/ai-docs/ARCHITECTURE.md +336 -0
- package/src/metrics/behavioral-events.ts +28 -14
- package/src/metrics/constants.ts +7 -8
- package/src/services/ApiAiAssistant.ts +2 -4
- package/src/services/agent/ai-docs/AGENTS.md +238 -0
- package/src/services/agent/ai-docs/ARCHITECTURE.md +302 -0
- package/src/services/ai-docs/AGENTS.md +384 -0
- package/src/services/config/Util.ts +2 -3
- package/src/services/config/ai-docs/AGENTS.md +253 -0
- package/src/services/config/ai-docs/ARCHITECTURE.md +424 -0
- package/src/services/config/types.ts +108 -20
- package/src/services/constants.ts +0 -1
- package/src/services/core/Err.ts +0 -1
- package/src/services/core/Utils.ts +90 -67
- package/src/services/core/ai-docs/AGENTS.md +379 -0
- package/src/services/core/ai-docs/ARCHITECTURE.md +696 -0
- package/src/services/core/aqm-reqs.ts +22 -100
- package/src/services/core/websocket/WebSocketManager.ts +4 -23
- package/src/services/core/websocket/types.ts +1 -1
- package/src/services/index.ts +1 -2
- package/src/services/task/Task.ts +785 -0
- package/src/services/task/TaskFactory.ts +55 -0
- package/src/services/task/TaskManager.ts +567 -633
- package/src/services/task/TaskUtils.ts +175 -31
- package/src/services/task/ai-docs/AGENTS.md +448 -0
- package/src/services/task/ai-docs/ARCHITECTURE.md +573 -0
- package/src/services/task/constants.ts +5 -4
- package/src/services/task/dialer.ts +1 -56
- package/src/services/task/digital/Digital.ts +95 -0
- package/src/services/task/state-machine/TaskStateMachine.ts +793 -0
- package/src/services/task/state-machine/actions.ts +409 -0
- package/src/services/task/state-machine/ai-docs/AGENTS.md +495 -0
- package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1135 -0
- package/src/services/task/state-machine/constants.ts +150 -0
- package/src/services/task/state-machine/guards.ts +295 -0
- package/src/services/task/state-machine/index.ts +28 -0
- package/src/services/task/state-machine/types.ts +228 -0
- package/src/services/task/state-machine/uiControlsComputer.ts +529 -0
- package/src/services/task/taskDataNormalizer.ts +137 -0
- package/src/services/task/types.ts +641 -95
- package/src/services/task/voice/Voice.ts +1255 -0
- package/src/services/task/voice/WebRTC.ts +187 -0
- package/src/types.ts +88 -5
- package/src/utils/AGENTS.md +276 -0
- package/src/webex.js +2 -0
- package/test/unit/spec/cc.ts +59 -142
- package/test/unit/spec/logger-proxy.ts +70 -0
- package/test/unit/spec/services/ApiAiAssistant.ts +17 -0
- package/test/unit/spec/services/config/index.ts +26 -55
- package/test/unit/spec/services/core/Utils.ts +103 -52
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +48 -112
- package/test/unit/spec/services/core/websocket/connection-service.ts +5 -4
- package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
- package/test/unit/spec/services/task/Task.ts +416 -0
- package/test/unit/spec/services/task/TaskFactory.ts +62 -0
- package/test/unit/spec/services/task/TaskManager.ts +781 -1735
- package/test/unit/spec/services/task/TaskUtils.ts +125 -0
- package/test/unit/spec/services/task/dialer.ts +112 -198
- package/test/unit/spec/services/task/digital/Digital.ts +105 -0
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +473 -0
- package/test/unit/spec/services/task/state-machine/guards.ts +288 -0
- package/test/unit/spec/services/task/state-machine/types.ts +18 -0
- package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +147 -0
- package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
- package/test/unit/spec/services/task/voice/Voice.ts +587 -0
- package/test/unit/spec/services/task/voice/WebRTC.ts +242 -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
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.computeUIControls = computeUIControls;
|
|
7
|
+
exports.getDefaultUIControls = getDefaultUIControls;
|
|
8
|
+
exports.haveUIControlsChanged = haveUIControlsChanged;
|
|
9
|
+
var _types = require("../types");
|
|
10
|
+
var _constants = require("./constants");
|
|
11
|
+
var _TaskUtils = require("../TaskUtils");
|
|
12
|
+
/**
|
|
13
|
+
* UI Controls Computer - Centralized logic for computing UI control states
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const DISABLED = {
|
|
17
|
+
isVisible: false,
|
|
18
|
+
isEnabled: false
|
|
19
|
+
};
|
|
20
|
+
const VISIBLE_ENABLED = {
|
|
21
|
+
isVisible: true,
|
|
22
|
+
isEnabled: true
|
|
23
|
+
};
|
|
24
|
+
const VISIBLE_DISABLED = {
|
|
25
|
+
isVisible: true,
|
|
26
|
+
isEnabled: false
|
|
27
|
+
};
|
|
28
|
+
function getDefaultInteractionUIControls() {
|
|
29
|
+
return {
|
|
30
|
+
accept: DISABLED,
|
|
31
|
+
decline: DISABLED,
|
|
32
|
+
hold: DISABLED,
|
|
33
|
+
mute: DISABLED,
|
|
34
|
+
end: DISABLED,
|
|
35
|
+
transfer: DISABLED,
|
|
36
|
+
consult: DISABLED,
|
|
37
|
+
consultTransfer: DISABLED,
|
|
38
|
+
endConsult: DISABLED,
|
|
39
|
+
recording: DISABLED,
|
|
40
|
+
conference: DISABLED,
|
|
41
|
+
wrapup: DISABLED,
|
|
42
|
+
exitConference: DISABLED,
|
|
43
|
+
transferConference: DISABLED,
|
|
44
|
+
mergeToConference: DISABLED,
|
|
45
|
+
switch: DISABLED
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function createTaskUIControls(main, consult, activeLeg) {
|
|
49
|
+
return {
|
|
50
|
+
main,
|
|
51
|
+
consult,
|
|
52
|
+
activeLeg
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function getDefaultUIControls() {
|
|
56
|
+
return createTaskUIControls(getDefaultInteractionUIControls(), getDefaultInteractionUIControls(), 'main');
|
|
57
|
+
}
|
|
58
|
+
function computeVoiceInteractionUIControls(state, context, config, fallbackTaskData, currentLeg = 'main') {
|
|
59
|
+
// Early exit for idle
|
|
60
|
+
if (state === _constants.TaskState.IDLE) {
|
|
61
|
+
return getDefaultInteractionUIControls();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Essential data
|
|
65
|
+
const taskData = context.taskData ?? fallbackTaskData ?? null;
|
|
66
|
+
const interaction = taskData?.interaction;
|
|
67
|
+
const mainCallId = interaction?.mainInteractionId || taskData?.interactionId;
|
|
68
|
+
const isWebrtc = config.voiceVariant === _types.VOICE_VARIANT.WEBRTC;
|
|
69
|
+
const isOutdial = interaction?.outboundType === 'OUTDIAL';
|
|
70
|
+
const serverHold = (0, _TaskUtils.getServerHoldStateForControls)(context, mainCallId, fallbackTaskData);
|
|
71
|
+
|
|
72
|
+
// Backend-derived checks
|
|
73
|
+
const customerInCall = interaction && mainCallId ? (0, _TaskUtils.getIsCustomerInCall)(interaction, mainCallId) : false;
|
|
74
|
+
// EP-DN/secondary legs can have incomplete media participant lists; fall back to participants map.
|
|
75
|
+
const customerPresent = customerInCall || Boolean(interaction && interaction.participants && Object.values(interaction.participants).some(p => p?.pType === 'Customer' && !p?.hasLeft));
|
|
76
|
+
const participantCount = interaction && mainCallId ? (0, _TaskUtils.getConferenceParticipantsCount)(interaction, mainCallId) : 0;
|
|
77
|
+
const maxParticipants = participantCount >= _constants.MAX_PARTICIPANTS_IN_MULTIPARTY_CONFERENCE;
|
|
78
|
+
const selfAgentId = config.agentId ?? taskData?.agentId;
|
|
79
|
+
const consultInProgress = (0, _TaskUtils.getIsConsultInProgressForConferenceControls)(interaction, mainCallId, selfAgentId);
|
|
80
|
+
const conferenceFromBackend = taskData ? (0, _TaskUtils.getIsConferenceInProgress)(taskData) : false;
|
|
81
|
+
// Note: ownership is used by some controls; keep computations local to those controls
|
|
82
|
+
|
|
83
|
+
// Context flags (set by state machine actions)
|
|
84
|
+
const {
|
|
85
|
+
consultInitiator,
|
|
86
|
+
consultDestinationAgentJoined,
|
|
87
|
+
consultCallHeld,
|
|
88
|
+
consultFromConference
|
|
89
|
+
} = context;
|
|
90
|
+
const {
|
|
91
|
+
recordingControlsAvailable
|
|
92
|
+
} = context;
|
|
93
|
+
const stateImpliesHeld = state === _constants.TaskState.HELD || state === _constants.TaskState.RESUME_INITIATING;
|
|
94
|
+
const stateImpliesConnected = state === _constants.TaskState.CONNECTED || state === _constants.TaskState.HOLD_INITIATING;
|
|
95
|
+
const isHeld = stateImpliesHeld || serverHold === true;
|
|
96
|
+
const isConnected = stateImpliesConnected || !stateImpliesHeld && serverHold === false;
|
|
97
|
+
|
|
98
|
+
// State categories for cleaner logic
|
|
99
|
+
const isConsulting = state === _constants.TaskState.CONSULTING || state === _constants.TaskState.CONSULT_INITIATING || state === _constants.TaskState.CONF_INITIATING;
|
|
100
|
+
const isConferencing = state === _constants.TaskState.CONFERENCING;
|
|
101
|
+
const isWrappingUp = state === _constants.TaskState.WRAPPING_UP;
|
|
102
|
+
const selfInMainCall = Boolean(selfAgentId) && Boolean(mainCallId) && Boolean(interaction?.media?.[mainCallId]?.participants?.includes(selfAgentId));
|
|
103
|
+
const conferenceActive = isConferencing || conferenceFromBackend || consultFromConference;
|
|
104
|
+
// Treat consult initiator as "in conference" even if mainCall participant list lags while consulting.
|
|
105
|
+
const inConference = conferenceActive && (isConferencing || selfInMainCall || consultInitiator);
|
|
106
|
+
|
|
107
|
+
// Check if this is a consulted agent (must be after isConsulting is computed).
|
|
108
|
+
const isSoleAgentOnCall = participantCount <= 1 && !isConsulting && !inConference;
|
|
109
|
+
const isConsulted = inConference || isSoleAgentOnCall ? false : (0, _TaskUtils.getIsConsultedAgentForControls)(taskData, context, isConsulting);
|
|
110
|
+
|
|
111
|
+
// Active call = can perform call operations
|
|
112
|
+
const isActive = state === _constants.TaskState.CONNECTED || state === _constants.TaskState.HELD || state === _constants.TaskState.HOLD_INITIATING || state === _constants.TaskState.RESUME_INITIATING || isConsulting || isConferencing;
|
|
113
|
+
|
|
114
|
+
// Consulted agents have limited controls until they're in conference or wrapup
|
|
115
|
+
// Use inConference (not isConferencing) so controls remain enabled after state downgrade
|
|
116
|
+
const hasFullControls = !isConsulted || consultInitiator || inConference || isWrappingUp;
|
|
117
|
+
const consultOwnedBySelf = consultInitiator || Boolean(selfAgentId) && taskData?.consultingAgentId === selfAgentId;
|
|
118
|
+
const hasConsultMedia = Boolean(taskData?.consultMediaResourceId || Object.values(interaction?.media ?? {}).some(media => media?.mType === 'consult'));
|
|
119
|
+
const hasParallelConsultLeg = consultOwnedBySelf && !isConsulting && !isConsulted && (consultInProgress || consultCallHeld || hasConsultMedia);
|
|
120
|
+
const consultLegOnHold = isConsulting && consultCallHeld;
|
|
121
|
+
return {
|
|
122
|
+
// Accept/Decline: Voice tasks in offered state
|
|
123
|
+
// For outdial, accept is disabled (auto-answer handles it), decline remains enabled
|
|
124
|
+
// For Extension mode (non-WebRTC), accept shows as disabled "Ringing" button
|
|
125
|
+
accept: state === _constants.TaskState.OFFERED && !interaction?.isTerminated ? {
|
|
126
|
+
isVisible: true,
|
|
127
|
+
isEnabled: isWebrtc && !isOutdial
|
|
128
|
+
} : DISABLED,
|
|
129
|
+
decline: isWebrtc && state === _constants.TaskState.OFFERED && !interaction?.isTerminated ? VISIBLE_ENABLED : DISABLED,
|
|
130
|
+
// Hold: visible in connected/held/conference, disabled in conference/consulting
|
|
131
|
+
hold: (() => {
|
|
132
|
+
if (!hasFullControls) return DISABLED;
|
|
133
|
+
if (consultOwnedBySelf && (isConsulting || hasParallelConsultLeg || consultCallHeld)) {
|
|
134
|
+
return DISABLED;
|
|
135
|
+
}
|
|
136
|
+
if (hasParallelConsultLeg) return DISABLED;
|
|
137
|
+
if (state === _constants.TaskState.OFFERED) return DISABLED;
|
|
138
|
+
if (isWrappingUp) return DISABLED;
|
|
139
|
+
// Visibility: connected || held || inConference
|
|
140
|
+
if (!(isConnected || isHeld || inConference)) return DISABLED;
|
|
141
|
+
// Enabled: (connected || held) && !inConference && !isConsulting
|
|
142
|
+
const canHold = (isConnected || isHeld) && !inConference && !isConsulting;
|
|
143
|
+
return canHold ? VISIBLE_ENABLED : VISIBLE_DISABLED;
|
|
144
|
+
})(),
|
|
145
|
+
// Mute: WebRTC only, active calls; hidden entirely during wrapup
|
|
146
|
+
mute: (() => {
|
|
147
|
+
if (!isWebrtc) return DISABLED;
|
|
148
|
+
if (isWrappingUp) return DISABLED;
|
|
149
|
+
if (isConsulting) return VISIBLE_ENABLED;
|
|
150
|
+
if (isConnected || isHeld || isConferencing) {
|
|
151
|
+
if (inConference) return VISIBLE_ENABLED;
|
|
152
|
+
return isHeld ? VISIBLE_DISABLED : VISIBLE_ENABLED;
|
|
153
|
+
}
|
|
154
|
+
return DISABLED;
|
|
155
|
+
})(),
|
|
156
|
+
// End: varies by state; during consulting only on main leg (consult held)
|
|
157
|
+
end: (() => {
|
|
158
|
+
if (!config.isEndTaskEnabled) return DISABLED;
|
|
159
|
+
if (hasParallelConsultLeg) return VISIBLE_DISABLED;
|
|
160
|
+
if (isConsulting) {
|
|
161
|
+
if (currentLeg === 'consult' && consultCallHeld) return DISABLED;
|
|
162
|
+
return consultInitiator && consultCallHeld ? VISIBLE_ENABLED : DISABLED;
|
|
163
|
+
}
|
|
164
|
+
if (inConference) {
|
|
165
|
+
if (isConsulted) return DISABLED;
|
|
166
|
+
if (consultInProgress) return VISIBLE_DISABLED;
|
|
167
|
+
return isWrappingUp ? VISIBLE_DISABLED : VISIBLE_ENABLED;
|
|
168
|
+
}
|
|
169
|
+
if (!hasFullControls) return DISABLED;
|
|
170
|
+
if (isActive) return isHeld || isWrappingUp ? VISIBLE_DISABLED : VISIBLE_ENABLED;
|
|
171
|
+
return DISABLED;
|
|
172
|
+
})(),
|
|
173
|
+
// Transfer: connected/held, not in conference
|
|
174
|
+
transfer: (() => {
|
|
175
|
+
if (hasParallelConsultLeg) {
|
|
176
|
+
if (state === _constants.TaskState.CONNECTED) return VISIBLE_ENABLED;
|
|
177
|
+
if (state === _constants.TaskState.HELD) return VISIBLE_DISABLED;
|
|
178
|
+
}
|
|
179
|
+
if (isConsulting) {
|
|
180
|
+
if (!consultInitiator) return DISABLED;
|
|
181
|
+
if (consultLegOnHold) return VISIBLE_DISABLED;
|
|
182
|
+
return consultDestinationAgentJoined ? VISIBLE_ENABLED : VISIBLE_DISABLED;
|
|
183
|
+
}
|
|
184
|
+
if (!hasFullControls || inConference) return DISABLED;
|
|
185
|
+
if (state === _constants.TaskState.CONNECTED || state === _constants.TaskState.HELD) return VISIBLE_ENABLED;
|
|
186
|
+
return DISABLED;
|
|
187
|
+
})(),
|
|
188
|
+
// Consult: connected/held/conference when conditions met
|
|
189
|
+
consult: (() => {
|
|
190
|
+
const isConnectedOrHeld = state === _constants.TaskState.CONNECTED || state === _constants.TaskState.HELD;
|
|
191
|
+
if (hasParallelConsultLeg) return DISABLED;
|
|
192
|
+
if (!hasFullControls || !(isConnectedOrHeld || inConference)) {
|
|
193
|
+
return DISABLED;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Enabled conditions differ by state
|
|
197
|
+
const canFromConnected = !maxParticipants && customerPresent && !consultInProgress && !isConsulted;
|
|
198
|
+
const canFromConference = !maxParticipants && customerPresent && !consultInProgress && !isConsulting;
|
|
199
|
+
const isEnabled = inConference ? canFromConference : canFromConnected;
|
|
200
|
+
return {
|
|
201
|
+
isVisible: true,
|
|
202
|
+
isEnabled
|
|
203
|
+
};
|
|
204
|
+
})(),
|
|
205
|
+
// ConsultTransfer: always hidden (use transfer button)
|
|
206
|
+
consultTransfer: DISABLED,
|
|
207
|
+
// EndConsult: during consulting
|
|
208
|
+
endConsult: (() => {
|
|
209
|
+
if (!isConsulting) return DISABLED;
|
|
210
|
+
if (isConsulted && isConferencing) return DISABLED;
|
|
211
|
+
if (!isConsulted && isConferencing && !(consultInitiator && conferenceFromBackend)) {
|
|
212
|
+
return DISABLED;
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
isVisible: true,
|
|
216
|
+
isEnabled: consultInitiator || config.isEndConsultEnabled
|
|
217
|
+
};
|
|
218
|
+
})(),
|
|
219
|
+
// Recording: connected/held only, not in consult/conference
|
|
220
|
+
recording: (() => {
|
|
221
|
+
if (!recordingControlsAvailable || !config.isRecordingEnabled) return DISABLED;
|
|
222
|
+
if (!hasFullControls || isConsulting || inConference) return DISABLED;
|
|
223
|
+
if (state === _constants.TaskState.CONNECTED || state === _constants.TaskState.HELD) {
|
|
224
|
+
return VISIBLE_ENABLED;
|
|
225
|
+
}
|
|
226
|
+
return DISABLED;
|
|
227
|
+
})(),
|
|
228
|
+
// Conference: during consulting, enabled on both legs when agent joined
|
|
229
|
+
// Label changes based on leg: "Conference" on main leg, "Merge" on consult leg
|
|
230
|
+
conference: (() => {
|
|
231
|
+
if (hasParallelConsultLeg) {
|
|
232
|
+
if (state === _constants.TaskState.CONNECTED) {
|
|
233
|
+
return maxParticipants ? VISIBLE_DISABLED : VISIBLE_ENABLED;
|
|
234
|
+
}
|
|
235
|
+
if (state === _constants.TaskState.HELD) return VISIBLE_DISABLED;
|
|
236
|
+
return DISABLED;
|
|
237
|
+
}
|
|
238
|
+
if (!hasFullControls || !isConsulting) return DISABLED;
|
|
239
|
+
if (!consultInitiator) return DISABLED;
|
|
240
|
+
if (consultLegOnHold) return VISIBLE_DISABLED;
|
|
241
|
+
return consultDestinationAgentJoined && !maxParticipants ? VISIBLE_ENABLED : VISIBLE_DISABLED;
|
|
242
|
+
})(),
|
|
243
|
+
// Wrapup: wrapping up state
|
|
244
|
+
wrapup: isWrappingUp ? VISIBLE_ENABLED : DISABLED,
|
|
245
|
+
// ExitConference: in conference, not consulting from conference
|
|
246
|
+
exitConference: (() => {
|
|
247
|
+
if (isConsulted && !isConferencing) return DISABLED;
|
|
248
|
+
if (!inConference) return DISABLED;
|
|
249
|
+
const consultingFromConference = consultInitiator && isConsulting && conferenceFromBackend;
|
|
250
|
+
return consultingFromConference ? VISIBLE_DISABLED : VISIBLE_ENABLED;
|
|
251
|
+
})(),
|
|
252
|
+
// TransferConference: in conference with active consult, owner consulting from conference
|
|
253
|
+
transferConference: (() => {
|
|
254
|
+
if (hasParallelConsultLeg || consultLegOnHold) return DISABLED;
|
|
255
|
+
if (!inConference || !isConsulting) return DISABLED;
|
|
256
|
+
if (!consultInitiator || isConsulted) return DISABLED;
|
|
257
|
+
return consultDestinationAgentJoined ? VISIBLE_ENABLED : VISIBLE_DISABLED;
|
|
258
|
+
})(),
|
|
259
|
+
// MergeToConference: mirrors conference control, enabled on both legs
|
|
260
|
+
mergeToConference: (() => {
|
|
261
|
+
if (!isConsulting || !consultInitiator) return DISABLED;
|
|
262
|
+
if (consultLegOnHold) return VISIBLE_DISABLED;
|
|
263
|
+
return consultDestinationAgentJoined && !maxParticipants ? VISIBLE_ENABLED : VISIBLE_DISABLED;
|
|
264
|
+
})(),
|
|
265
|
+
// Switch: visible only on the currently active leg
|
|
266
|
+
switch: (() => {
|
|
267
|
+
if (currentLeg === 'consult') {
|
|
268
|
+
if (!isConsulting || !consultInitiator || consultCallHeld) return DISABLED;
|
|
269
|
+
return consultDestinationAgentJoined ? VISIBLE_ENABLED : VISIBLE_DISABLED;
|
|
270
|
+
}
|
|
271
|
+
if (hasParallelConsultLeg && state === _constants.TaskState.CONNECTED) {
|
|
272
|
+
return consultDestinationAgentJoined ? VISIBLE_ENABLED : VISIBLE_DISABLED;
|
|
273
|
+
}
|
|
274
|
+
return DISABLED;
|
|
275
|
+
})()
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
function computeDigitalInteractionUIControls(state, context, fallbackTaskData) {
|
|
279
|
+
const taskData = context.taskData ?? fallbackTaskData ?? null;
|
|
280
|
+
const isTerminated = taskData?.interaction?.isTerminated ?? false;
|
|
281
|
+
const isConnected = state === _constants.TaskState.CONNECTED;
|
|
282
|
+
const isWrappingUp = state === _constants.TaskState.WRAPPING_UP;
|
|
283
|
+
return {
|
|
284
|
+
accept: state === _constants.TaskState.OFFERED ? VISIBLE_ENABLED : DISABLED,
|
|
285
|
+
decline: DISABLED,
|
|
286
|
+
hold: DISABLED,
|
|
287
|
+
mute: DISABLED,
|
|
288
|
+
end: isConnected && !isWrappingUp ? VISIBLE_ENABLED : DISABLED,
|
|
289
|
+
transfer: isConnected && !isWrappingUp ? VISIBLE_ENABLED : DISABLED,
|
|
290
|
+
consult: DISABLED,
|
|
291
|
+
consultTransfer: DISABLED,
|
|
292
|
+
endConsult: DISABLED,
|
|
293
|
+
recording: DISABLED,
|
|
294
|
+
conference: DISABLED,
|
|
295
|
+
wrapup: isTerminated || isWrappingUp ? VISIBLE_ENABLED : DISABLED,
|
|
296
|
+
exitConference: DISABLED,
|
|
297
|
+
transferConference: DISABLED,
|
|
298
|
+
mergeToConference: DISABLED,
|
|
299
|
+
switch: DISABLED
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function getVoiceLegState(currentState, context, config, fallbackTaskData) {
|
|
303
|
+
if (currentState === _constants.TaskState.WRAPPING_UP) {
|
|
304
|
+
return {
|
|
305
|
+
hasConsultLeg: false,
|
|
306
|
+
activeLeg: 'main',
|
|
307
|
+
mainState: currentState,
|
|
308
|
+
consultState: _constants.TaskState.CONSULTING
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
const taskData = context.taskData ?? fallbackTaskData ?? null;
|
|
312
|
+
const interaction = taskData?.interaction;
|
|
313
|
+
const mainCallId = interaction?.mainInteractionId || taskData?.interactionId;
|
|
314
|
+
const selfAgentId = config.agentId ?? taskData?.agentId;
|
|
315
|
+
const consultInProgress = (0, _TaskUtils.getIsConsultInProgressForConferenceControls)(interaction, mainCallId, selfAgentId);
|
|
316
|
+
const isConsultingState = currentState === _constants.TaskState.CONSULTING || currentState === _constants.TaskState.CONSULT_INITIATING || currentState === _constants.TaskState.CONF_INITIATING;
|
|
317
|
+
const consultOwnedBySelf = context.consultInitiator || Boolean(selfAgentId) && taskData?.consultingAgentId === selfAgentId;
|
|
318
|
+
const hasConsultMedia = Boolean(taskData?.consultMediaResourceId || Object.values(interaction?.media ?? {}).some(media => media?.mType === 'consult'));
|
|
319
|
+
const hasConsultLeg = Boolean(consultOwnedBySelf && !taskData?.isConsulted && !interaction?.isTerminated && (consultInProgress || isConsultingState || context.consultCallHeld || hasConsultMedia));
|
|
320
|
+
if (!hasConsultLeg) {
|
|
321
|
+
return {
|
|
322
|
+
hasConsultLeg: false,
|
|
323
|
+
activeLeg: 'main',
|
|
324
|
+
mainState: currentState,
|
|
325
|
+
consultState: _constants.TaskState.CONSULTING
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
return {
|
|
329
|
+
hasConsultLeg: true,
|
|
330
|
+
activeLeg: context.consultCallHeld ? 'main' : 'consult',
|
|
331
|
+
mainState: context.consultCallHeld ? _constants.TaskState.CONNECTED : _constants.TaskState.HELD,
|
|
332
|
+
consultState: isConsultingState ? currentState : _constants.TaskState.CONSULTING
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
function computeUIControls(currentState, context, fallbackTaskData) {
|
|
336
|
+
if (currentState === _constants.TaskState.TERMINATED || currentState === _constants.TaskState.COMPLETED) {
|
|
337
|
+
return getDefaultUIControls();
|
|
338
|
+
}
|
|
339
|
+
switch (context.uiControlConfig.channelType) {
|
|
340
|
+
case _types.TASK_CHANNEL_TYPE.VOICE:
|
|
341
|
+
{
|
|
342
|
+
const {
|
|
343
|
+
hasConsultLeg,
|
|
344
|
+
activeLeg,
|
|
345
|
+
mainState,
|
|
346
|
+
consultState
|
|
347
|
+
} = getVoiceLegState(currentState, context, context.uiControlConfig, fallbackTaskData);
|
|
348
|
+
const mainControls = computeVoiceInteractionUIControls(mainState, context, context.uiControlConfig, fallbackTaskData, 'main');
|
|
349
|
+
const consultControls = hasConsultLeg ? computeVoiceInteractionUIControls(consultState, context, context.uiControlConfig, fallbackTaskData, 'consult') : getDefaultInteractionUIControls();
|
|
350
|
+
return createTaskUIControls(mainControls, consultControls, activeLeg);
|
|
351
|
+
}
|
|
352
|
+
case _types.TASK_CHANNEL_TYPE.DIGITAL:
|
|
353
|
+
return createTaskUIControls(computeDigitalInteractionUIControls(currentState, context, fallbackTaskData), getDefaultInteractionUIControls(), 'main');
|
|
354
|
+
default:
|
|
355
|
+
return getDefaultUIControls();
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function haveInteractionUIControlsChanged(previous, next) {
|
|
359
|
+
return Object.keys(next).some(key => {
|
|
360
|
+
const prev = previous[key];
|
|
361
|
+
const curr = next[key];
|
|
362
|
+
return prev.isVisible !== curr.isVisible || prev.isEnabled !== curr.isEnabled;
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
function haveUIControlsChanged(previous, next) {
|
|
366
|
+
if (!previous) return true;
|
|
367
|
+
return previous.activeLeg !== next.activeLeg || haveInteractionUIControlsChanged(previous.main, next.main) || haveInteractionUIControlsChanged(previous.consult, next.consult);
|
|
368
|
+
}
|
|
369
|
+
//# sourceMappingURL=uiControlsComputer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["_types","require","_constants","_TaskUtils","DISABLED","isVisible","isEnabled","VISIBLE_ENABLED","VISIBLE_DISABLED","getDefaultInteractionUIControls","accept","decline","hold","mute","end","transfer","consult","consultTransfer","endConsult","recording","conference","wrapup","exitConference","transferConference","mergeToConference","switch","createTaskUIControls","main","activeLeg","getDefaultUIControls","computeVoiceInteractionUIControls","state","context","config","fallbackTaskData","currentLeg","TaskState","IDLE","taskData","interaction","mainCallId","mainInteractionId","interactionId","isWebrtc","voiceVariant","VOICE_VARIANT","WEBRTC","isOutdial","outboundType","serverHold","getServerHoldStateForControls","customerInCall","getIsCustomerInCall","customerPresent","Boolean","participants","Object","values","some","p","pType","hasLeft","participantCount","getConferenceParticipantsCount","maxParticipants","MAX_PARTICIPANTS_IN_MULTIPARTY_CONFERENCE","selfAgentId","agentId","consultInProgress","getIsConsultInProgressForConferenceControls","conferenceFromBackend","getIsConferenceInProgress","consultInitiator","consultDestinationAgentJoined","consultCallHeld","consultFromConference","recordingControlsAvailable","stateImpliesHeld","HELD","RESUME_INITIATING","stateImpliesConnected","CONNECTED","HOLD_INITIATING","isHeld","isConnected","isConsulting","CONSULTING","CONSULT_INITIATING","CONF_INITIATING","isConferencing","CONFERENCING","isWrappingUp","WRAPPING_UP","selfInMainCall","media","includes","conferenceActive","inConference","isSoleAgentOnCall","isConsulted","getIsConsultedAgentForControls","isActive","hasFullControls","consultOwnedBySelf","consultingAgentId","hasConsultMedia","consultMediaResourceId","mType","hasParallelConsultLeg","consultLegOnHold","OFFERED","isTerminated","canHold","isEndTaskEnabled","isConnectedOrHeld","canFromConnected","canFromConference","isEndConsultEnabled","isRecordingEnabled","consultingFromConference","computeDigitalInteractionUIControls","getVoiceLegState","currentState","hasConsultLeg","mainState","consultState","isConsultingState","computeUIControls","TERMINATED","COMPLETED","uiControlConfig","channelType","TASK_CHANNEL_TYPE","VOICE","mainControls","consultControls","DIGITAL","haveInteractionUIControlsChanged","previous","next","keys","key","prev","curr","haveUIControlsChanged"],"sources":["uiControlsComputer.ts"],"sourcesContent":["/**\n * UI Controls Computer - Centralized logic for computing UI control states\n */\n\nimport {\n InteractionUIControls,\n TASK_CHANNEL_TYPE,\n TaskData,\n TaskUILeg,\n TaskUIControls,\n VOICE_VARIANT,\n} from '../types';\nimport {TaskContext, UIControlConfig} from './types';\nimport {TaskState, MAX_PARTICIPANTS_IN_MULTIPARTY_CONFERENCE} from './constants';\nimport {\n getIsCustomerInCall,\n getConferenceParticipantsCount,\n getIsConferenceInProgress,\n getIsConsultInProgressForConferenceControls,\n getIsConsultedAgentForControls,\n getServerHoldStateForControls,\n} from '../TaskUtils';\n\nconst DISABLED = {isVisible: false, isEnabled: false} as const;\nconst VISIBLE_ENABLED = {isVisible: true, isEnabled: true} as const;\nconst VISIBLE_DISABLED = {isVisible: true, isEnabled: false} as const;\n\nfunction getDefaultInteractionUIControls(): InteractionUIControls {\n return {\n accept: DISABLED,\n decline: DISABLED,\n hold: DISABLED,\n mute: DISABLED,\n end: DISABLED,\n transfer: DISABLED,\n consult: DISABLED,\n consultTransfer: DISABLED,\n endConsult: DISABLED,\n recording: DISABLED,\n conference: DISABLED,\n wrapup: DISABLED,\n exitConference: DISABLED,\n transferConference: DISABLED,\n mergeToConference: DISABLED,\n switch: DISABLED,\n };\n}\n\nfunction createTaskUIControls(\n main: InteractionUIControls,\n consult: InteractionUIControls,\n activeLeg: TaskUILeg\n): TaskUIControls {\n return {\n main,\n consult,\n activeLeg,\n };\n}\n\nexport function getDefaultUIControls(): TaskUIControls {\n return createTaskUIControls(\n getDefaultInteractionUIControls(),\n getDefaultInteractionUIControls(),\n 'main'\n );\n}\n\nfunction computeVoiceInteractionUIControls(\n state: TaskState,\n context: TaskContext,\n config: UIControlConfig,\n fallbackTaskData?: TaskData,\n currentLeg: TaskUILeg = 'main'\n): InteractionUIControls {\n // Early exit for idle\n if (state === TaskState.IDLE) {\n return getDefaultInteractionUIControls();\n }\n\n // Essential data\n const taskData = context.taskData ?? fallbackTaskData ?? null;\n const interaction = taskData?.interaction;\n const mainCallId = interaction?.mainInteractionId || taskData?.interactionId;\n const isWebrtc = config.voiceVariant === VOICE_VARIANT.WEBRTC;\n const isOutdial = interaction?.outboundType === 'OUTDIAL';\n const serverHold = getServerHoldStateForControls(context, mainCallId, fallbackTaskData);\n\n // Backend-derived checks\n const customerInCall =\n interaction && mainCallId ? getIsCustomerInCall(interaction, mainCallId) : false;\n // EP-DN/secondary legs can have incomplete media participant lists; fall back to participants map.\n const customerPresent =\n customerInCall ||\n Boolean(\n interaction &&\n interaction.participants &&\n Object.values(interaction.participants).some(\n (p: any) => p?.pType === 'Customer' && !p?.hasLeft\n )\n );\n const participantCount =\n interaction && mainCallId ? getConferenceParticipantsCount(interaction, mainCallId) : 0;\n const maxParticipants = participantCount >= MAX_PARTICIPANTS_IN_MULTIPARTY_CONFERENCE;\n const selfAgentId = config.agentId ?? taskData?.agentId;\n const consultInProgress = getIsConsultInProgressForConferenceControls(\n interaction,\n mainCallId,\n selfAgentId\n );\n const conferenceFromBackend = taskData ? getIsConferenceInProgress(taskData) : false;\n // Note: ownership is used by some controls; keep computations local to those controls\n\n // Context flags (set by state machine actions)\n const {consultInitiator, consultDestinationAgentJoined, consultCallHeld, consultFromConference} =\n context;\n const {recordingControlsAvailable} = context;\n\n const stateImpliesHeld = state === TaskState.HELD || state === TaskState.RESUME_INITIATING;\n const stateImpliesConnected =\n state === TaskState.CONNECTED || state === TaskState.HOLD_INITIATING;\n const isHeld = stateImpliesHeld || serverHold === true;\n const isConnected = stateImpliesConnected || (!stateImpliesHeld && serverHold === false);\n\n // State categories for cleaner logic\n const isConsulting =\n state === TaskState.CONSULTING ||\n state === TaskState.CONSULT_INITIATING ||\n state === TaskState.CONF_INITIATING;\n const isConferencing = state === TaskState.CONFERENCING;\n const isWrappingUp = state === TaskState.WRAPPING_UP;\n const selfInMainCall =\n Boolean(selfAgentId) &&\n Boolean(mainCallId) &&\n Boolean(interaction?.media?.[mainCallId]?.participants?.includes(selfAgentId as string));\n const conferenceActive = isConferencing || conferenceFromBackend || consultFromConference;\n // Treat consult initiator as \"in conference\" even if mainCall participant list lags while consulting.\n const inConference = conferenceActive && (isConferencing || selfInMainCall || consultInitiator);\n\n // Check if this is a consulted agent (must be after isConsulting is computed).\n const isSoleAgentOnCall = participantCount <= 1 && !isConsulting && !inConference;\n const isConsulted =\n inConference || isSoleAgentOnCall\n ? false\n : getIsConsultedAgentForControls(taskData, context, isConsulting);\n\n // Active call = can perform call operations\n const isActive =\n state === TaskState.CONNECTED ||\n state === TaskState.HELD ||\n state === TaskState.HOLD_INITIATING ||\n state === TaskState.RESUME_INITIATING ||\n isConsulting ||\n isConferencing;\n\n // Consulted agents have limited controls until they're in conference or wrapup\n // Use inConference (not isConferencing) so controls remain enabled after state downgrade\n const hasFullControls = !isConsulted || consultInitiator || inConference || isWrappingUp;\n const consultOwnedBySelf =\n consultInitiator || (Boolean(selfAgentId) && taskData?.consultingAgentId === selfAgentId);\n const hasConsultMedia = Boolean(\n taskData?.consultMediaResourceId ||\n Object.values(interaction?.media ?? {}).some((media: any) => media?.mType === 'consult')\n );\n const hasParallelConsultLeg =\n consultOwnedBySelf &&\n !isConsulting &&\n !isConsulted &&\n (consultInProgress || consultCallHeld || hasConsultMedia);\n const consultLegOnHold = isConsulting && consultCallHeld;\n\n return {\n // Accept/Decline: Voice tasks in offered state\n // For outdial, accept is disabled (auto-answer handles it), decline remains enabled\n // For Extension mode (non-WebRTC), accept shows as disabled \"Ringing\" button\n accept:\n state === TaskState.OFFERED && !interaction?.isTerminated\n ? {isVisible: true, isEnabled: isWebrtc && !isOutdial}\n : DISABLED,\n decline:\n isWebrtc && state === TaskState.OFFERED && !interaction?.isTerminated\n ? VISIBLE_ENABLED\n : DISABLED,\n\n // Hold: visible in connected/held/conference, disabled in conference/consulting\n hold: (() => {\n if (!hasFullControls) return DISABLED;\n if (consultOwnedBySelf && (isConsulting || hasParallelConsultLeg || consultCallHeld)) {\n return DISABLED;\n }\n if (hasParallelConsultLeg) return DISABLED;\n if (state === TaskState.OFFERED) return DISABLED;\n if (isWrappingUp) return DISABLED;\n // Visibility: connected || held || inConference\n if (!(isConnected || isHeld || inConference)) return DISABLED;\n // Enabled: (connected || held) && !inConference && !isConsulting\n const canHold = (isConnected || isHeld) && !inConference && !isConsulting;\n\n return canHold ? VISIBLE_ENABLED : VISIBLE_DISABLED;\n })(),\n\n // Mute: WebRTC only, active calls; hidden entirely during wrapup\n mute: (() => {\n if (!isWebrtc) return DISABLED;\n if (isWrappingUp) return DISABLED;\n if (isConsulting) return VISIBLE_ENABLED;\n\n if (isConnected || isHeld || isConferencing) {\n if (inConference) return VISIBLE_ENABLED;\n\n return isHeld ? VISIBLE_DISABLED : VISIBLE_ENABLED;\n }\n\n return DISABLED;\n })(),\n\n // End: varies by state; during consulting only on main leg (consult held)\n end: (() => {\n if (!config.isEndTaskEnabled) return DISABLED;\n if (hasParallelConsultLeg) return VISIBLE_DISABLED;\n\n if (isConsulting) {\n if (currentLeg === 'consult' && consultCallHeld) return DISABLED;\n\n return consultInitiator && consultCallHeld ? VISIBLE_ENABLED : DISABLED;\n }\n\n if (inConference) {\n if (isConsulted) return DISABLED;\n\n if (consultInProgress) return VISIBLE_DISABLED;\n\n return isWrappingUp ? VISIBLE_DISABLED : VISIBLE_ENABLED;\n }\n if (!hasFullControls) return DISABLED;\n if (isActive) return isHeld || isWrappingUp ? VISIBLE_DISABLED : VISIBLE_ENABLED;\n\n return DISABLED;\n })(),\n\n // Transfer: connected/held, not in conference\n transfer: (() => {\n if (hasParallelConsultLeg) {\n if (state === TaskState.CONNECTED) return VISIBLE_ENABLED;\n if (state === TaskState.HELD) return VISIBLE_DISABLED;\n }\n if (isConsulting) {\n if (!consultInitiator) return DISABLED;\n if (consultLegOnHold) return VISIBLE_DISABLED;\n\n return consultDestinationAgentJoined ? VISIBLE_ENABLED : VISIBLE_DISABLED;\n }\n if (!hasFullControls || inConference) return DISABLED;\n if (state === TaskState.CONNECTED || state === TaskState.HELD) return VISIBLE_ENABLED;\n\n return DISABLED;\n })(),\n\n // Consult: connected/held/conference when conditions met\n consult: (() => {\n const isConnectedOrHeld = state === TaskState.CONNECTED || state === TaskState.HELD;\n\n if (hasParallelConsultLeg) return DISABLED;\n if (!hasFullControls || !(isConnectedOrHeld || inConference)) {\n return DISABLED;\n }\n\n // Enabled conditions differ by state\n const canFromConnected =\n !maxParticipants && customerPresent && !consultInProgress && !isConsulted;\n const canFromConference =\n !maxParticipants && customerPresent && !consultInProgress && !isConsulting;\n\n const isEnabled = inConference ? canFromConference : canFromConnected;\n\n return {isVisible: true, isEnabled};\n })(),\n\n // ConsultTransfer: always hidden (use transfer button)\n consultTransfer: DISABLED,\n\n // EndConsult: during consulting\n endConsult: (() => {\n if (!isConsulting) return DISABLED;\n if (isConsulted && isConferencing) return DISABLED;\n if (!isConsulted && isConferencing && !(consultInitiator && conferenceFromBackend)) {\n return DISABLED;\n }\n\n return {isVisible: true, isEnabled: consultInitiator || config.isEndConsultEnabled};\n })(),\n\n // Recording: connected/held only, not in consult/conference\n recording: (() => {\n if (!recordingControlsAvailable || !config.isRecordingEnabled) return DISABLED;\n if (!hasFullControls || isConsulting || inConference) return DISABLED;\n if (state === TaskState.CONNECTED || state === TaskState.HELD) {\n return VISIBLE_ENABLED;\n }\n\n return DISABLED;\n })(),\n\n // Conference: during consulting, enabled on both legs when agent joined\n // Label changes based on leg: \"Conference\" on main leg, \"Merge\" on consult leg\n conference: (() => {\n if (hasParallelConsultLeg) {\n if (state === TaskState.CONNECTED) {\n return maxParticipants ? VISIBLE_DISABLED : VISIBLE_ENABLED;\n }\n if (state === TaskState.HELD) return VISIBLE_DISABLED;\n\n return DISABLED;\n }\n if (!hasFullControls || !isConsulting) return DISABLED;\n if (!consultInitiator) return DISABLED;\n if (consultLegOnHold) return VISIBLE_DISABLED;\n\n return consultDestinationAgentJoined && !maxParticipants ? VISIBLE_ENABLED : VISIBLE_DISABLED;\n })(),\n\n // Wrapup: wrapping up state\n wrapup: isWrappingUp ? VISIBLE_ENABLED : DISABLED,\n\n // ExitConference: in conference, not consulting from conference\n exitConference: (() => {\n if (isConsulted && !isConferencing) return DISABLED;\n if (!inConference) return DISABLED;\n const consultingFromConference = consultInitiator && isConsulting && conferenceFromBackend;\n\n return consultingFromConference ? VISIBLE_DISABLED : VISIBLE_ENABLED;\n })(),\n\n // TransferConference: in conference with active consult, owner consulting from conference\n transferConference: (() => {\n if (hasParallelConsultLeg || consultLegOnHold) return DISABLED;\n if (!inConference || !isConsulting) return DISABLED;\n if (!consultInitiator || isConsulted) return DISABLED;\n\n return consultDestinationAgentJoined ? VISIBLE_ENABLED : VISIBLE_DISABLED;\n })(),\n\n // MergeToConference: mirrors conference control, enabled on both legs\n mergeToConference: (() => {\n if (!isConsulting || !consultInitiator) return DISABLED;\n if (consultLegOnHold) return VISIBLE_DISABLED;\n\n return consultDestinationAgentJoined && !maxParticipants ? VISIBLE_ENABLED : VISIBLE_DISABLED;\n })(),\n\n // Switch: visible only on the currently active leg\n switch: (() => {\n if (currentLeg === 'consult') {\n if (!isConsulting || !consultInitiator || consultCallHeld) return DISABLED;\n\n return consultDestinationAgentJoined ? VISIBLE_ENABLED : VISIBLE_DISABLED;\n }\n\n if (hasParallelConsultLeg && state === TaskState.CONNECTED) {\n return consultDestinationAgentJoined ? VISIBLE_ENABLED : VISIBLE_DISABLED;\n }\n\n return DISABLED;\n })(),\n };\n}\n\nfunction computeDigitalInteractionUIControls(\n state: TaskState,\n context: TaskContext,\n fallbackTaskData?: TaskData\n): InteractionUIControls {\n const taskData = context.taskData ?? fallbackTaskData ?? null;\n const isTerminated = taskData?.interaction?.isTerminated ?? false;\n\n const isConnected = state === TaskState.CONNECTED;\n const isWrappingUp = state === TaskState.WRAPPING_UP;\n\n return {\n accept: state === TaskState.OFFERED ? VISIBLE_ENABLED : DISABLED,\n decline: DISABLED,\n hold: DISABLED,\n mute: DISABLED,\n end: isConnected && !isWrappingUp ? VISIBLE_ENABLED : DISABLED,\n transfer: isConnected && !isWrappingUp ? VISIBLE_ENABLED : DISABLED,\n consult: DISABLED,\n consultTransfer: DISABLED,\n endConsult: DISABLED,\n recording: DISABLED,\n conference: DISABLED,\n wrapup: isTerminated || isWrappingUp ? VISIBLE_ENABLED : DISABLED,\n exitConference: DISABLED,\n transferConference: DISABLED,\n mergeToConference: DISABLED,\n switch: DISABLED,\n };\n}\n\nfunction getVoiceLegState(\n currentState: TaskState,\n context: TaskContext,\n config: UIControlConfig,\n fallbackTaskData?: TaskData\n): {hasConsultLeg: boolean; activeLeg: TaskUILeg; mainState: TaskState; consultState: TaskState} {\n if (currentState === TaskState.WRAPPING_UP) {\n return {\n hasConsultLeg: false,\n activeLeg: 'main',\n mainState: currentState,\n consultState: TaskState.CONSULTING,\n };\n }\n\n const taskData = context.taskData ?? fallbackTaskData ?? null;\n const interaction = taskData?.interaction;\n const mainCallId = interaction?.mainInteractionId || taskData?.interactionId;\n const selfAgentId = config.agentId ?? taskData?.agentId;\n const consultInProgress = getIsConsultInProgressForConferenceControls(\n interaction,\n mainCallId,\n selfAgentId\n );\n const isConsultingState =\n currentState === TaskState.CONSULTING ||\n currentState === TaskState.CONSULT_INITIATING ||\n currentState === TaskState.CONF_INITIATING;\n const consultOwnedBySelf =\n context.consultInitiator ||\n (Boolean(selfAgentId) && taskData?.consultingAgentId === selfAgentId);\n const hasConsultMedia = Boolean(\n taskData?.consultMediaResourceId ||\n Object.values(interaction?.media ?? {}).some((media: any) => media?.mType === 'consult')\n );\n const hasConsultLeg = Boolean(\n consultOwnedBySelf &&\n !taskData?.isConsulted &&\n !interaction?.isTerminated &&\n (consultInProgress || isConsultingState || context.consultCallHeld || hasConsultMedia)\n );\n\n if (!hasConsultLeg) {\n return {\n hasConsultLeg: false,\n activeLeg: 'main',\n mainState: currentState,\n consultState: TaskState.CONSULTING,\n };\n }\n\n return {\n hasConsultLeg: true,\n activeLeg: context.consultCallHeld ? 'main' : 'consult',\n mainState: context.consultCallHeld ? TaskState.CONNECTED : TaskState.HELD,\n consultState: isConsultingState ? currentState : TaskState.CONSULTING,\n };\n}\n\nexport function computeUIControls(\n currentState: TaskState,\n context: TaskContext,\n fallbackTaskData?: TaskData\n): TaskUIControls {\n if (currentState === TaskState.TERMINATED || currentState === TaskState.COMPLETED) {\n return getDefaultUIControls();\n }\n\n switch (context.uiControlConfig.channelType) {\n case TASK_CHANNEL_TYPE.VOICE: {\n const {hasConsultLeg, activeLeg, mainState, consultState} = getVoiceLegState(\n currentState,\n context,\n context.uiControlConfig,\n fallbackTaskData\n );\n\n const mainControls = computeVoiceInteractionUIControls(\n mainState,\n context,\n context.uiControlConfig,\n fallbackTaskData,\n 'main'\n );\n const consultControls = hasConsultLeg\n ? computeVoiceInteractionUIControls(\n consultState,\n context,\n context.uiControlConfig,\n fallbackTaskData,\n 'consult'\n )\n : getDefaultInteractionUIControls();\n\n return createTaskUIControls(mainControls, consultControls, activeLeg);\n }\n case TASK_CHANNEL_TYPE.DIGITAL:\n return createTaskUIControls(\n computeDigitalInteractionUIControls(currentState, context, fallbackTaskData),\n getDefaultInteractionUIControls(),\n 'main'\n );\n default:\n return getDefaultUIControls();\n }\n}\n\nfunction haveInteractionUIControlsChanged(\n previous: InteractionUIControls,\n next: InteractionUIControls\n): boolean {\n return (Object.keys(next) as (keyof InteractionUIControls)[]).some((key) => {\n const prev = previous[key];\n const curr = next[key];\n\n return prev.isVisible !== curr.isVisible || prev.isEnabled !== curr.isEnabled;\n });\n}\n\nexport function haveUIControlsChanged(\n previous: TaskUIControls | undefined,\n next: TaskUIControls\n): boolean {\n if (!previous) return true;\n\n return (\n previous.activeLeg !== next.activeLeg ||\n haveInteractionUIControlsChanged(previous.main, next.main) ||\n haveInteractionUIControlsChanged(previous.consult, next.consult)\n );\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.normalizeTaskData = normalizeTaskData;
|
|
7
|
+
const booleanKeys = ['recordingStarted', 'recordInProgress', 'isPaused', 'pauseResumeEnabled', 'ctqInProgress', 'outdialTransferToQueueEnabled', 'taskToBeSelfServiced', 'CONTINUE_RECORDING_ON_TRANSFER', 'isParked', 'participantInviteTimeout', 'checkAgentAvailability'];
|
|
8
|
+
const interactionBooleanKeys = ['isFcManaged', 'isMediaForked', 'isTerminated'];
|
|
9
|
+
const participantBooleanKeys = ['autoAnswerEnabled', 'hasJoined', 'hasLeft', 'isConsulted', 'isInPredial', 'isOffered', 'isWrapUp', 'isWrappedUp'];
|
|
10
|
+
const toBoolean = value => {
|
|
11
|
+
if (typeof value === 'boolean') {
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
if (typeof value === 'string') {
|
|
15
|
+
const normalized = value.toLowerCase();
|
|
16
|
+
if (normalized === 'true') {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
if (normalized === 'false') {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
};
|
|
25
|
+
const normalizeFields = (obj, keys) => {
|
|
26
|
+
let updated;
|
|
27
|
+
keys.forEach(key => {
|
|
28
|
+
const normalized = toBoolean(obj[key]);
|
|
29
|
+
if (typeof normalized !== 'undefined') {
|
|
30
|
+
if (!updated) {
|
|
31
|
+
updated = {
|
|
32
|
+
...obj
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
updated[key] = normalized;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
return updated;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Normalize backend task payload quirks so downstream code can rely on actual booleans.
|
|
43
|
+
*
|
|
44
|
+
* Applies to every Agent Contact websocket event before it reaches the state machine:
|
|
45
|
+
* - Converts string booleans in callProcessingDetails to actual booleans.
|
|
46
|
+
* - Also normalizes known boolean fields on interaction and participants.
|
|
47
|
+
* - Keeps payload shape intact; only coerces known boolean fields.
|
|
48
|
+
*/
|
|
49
|
+
function normalizeTaskData(data) {
|
|
50
|
+
const interaction = data?.interaction;
|
|
51
|
+
if (!interaction) {
|
|
52
|
+
return data;
|
|
53
|
+
}
|
|
54
|
+
const details = interaction.callProcessingDetails;
|
|
55
|
+
const updatedDetails = details ? normalizeFields(details, booleanKeys) : undefined;
|
|
56
|
+
const updatedInteractionBooleans = normalizeFields(interaction, interactionBooleanKeys);
|
|
57
|
+
let updatedParticipants;
|
|
58
|
+
const participants = interaction.participants || {};
|
|
59
|
+
Object.keys(participants).forEach(id => {
|
|
60
|
+
const participant = participants[id];
|
|
61
|
+
const normalized = normalizeFields(participant, participantBooleanKeys);
|
|
62
|
+
if (normalized) {
|
|
63
|
+
if (!updatedParticipants) {
|
|
64
|
+
updatedParticipants = {
|
|
65
|
+
...participants
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
updatedParticipants[id] = normalized;
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
let updatedMedia;
|
|
72
|
+
const mediaEntries = interaction.media || {};
|
|
73
|
+
Object.keys(mediaEntries).forEach(id => {
|
|
74
|
+
const media = mediaEntries[id];
|
|
75
|
+
const normalized = normalizeFields(media, ['isHold']);
|
|
76
|
+
if (normalized) {
|
|
77
|
+
if (!updatedMedia) {
|
|
78
|
+
updatedMedia = {
|
|
79
|
+
...mediaEntries
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
updatedMedia[id] = normalized;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
if (!updatedDetails && !updatedInteractionBooleans && !updatedParticipants && !updatedMedia) {
|
|
86
|
+
return data;
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
...data,
|
|
90
|
+
interaction: {
|
|
91
|
+
...interaction,
|
|
92
|
+
...(updatedInteractionBooleans || {}),
|
|
93
|
+
callProcessingDetails: updatedDetails || details,
|
|
94
|
+
participants: updatedParticipants || interaction.participants,
|
|
95
|
+
media: updatedMedia || interaction.media
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=taskDataNormalizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["booleanKeys","interactionBooleanKeys","participantBooleanKeys","toBoolean","value","normalized","toLowerCase","undefined","normalizeFields","obj","keys","updated","forEach","key","normalizeTaskData","data","interaction","details","callProcessingDetails","updatedDetails","updatedInteractionBooleans","updatedParticipants","participants","Object","id","participant","updatedMedia","mediaEntries","media"],"sources":["taskDataNormalizer.ts"],"sourcesContent":["import {\n CallProcessingBooleanKey,\n InteractionBooleanKey,\n ParticipantBooleanKey,\n TaskData,\n} from './types';\n\nconst booleanKeys: CallProcessingBooleanKey[] = [\n 'recordingStarted',\n 'recordInProgress',\n 'isPaused',\n 'pauseResumeEnabled',\n 'ctqInProgress',\n 'outdialTransferToQueueEnabled',\n 'taskToBeSelfServiced',\n 'CONTINUE_RECORDING_ON_TRANSFER',\n 'isParked',\n 'participantInviteTimeout',\n 'checkAgentAvailability',\n];\n\nconst interactionBooleanKeys: InteractionBooleanKey[] = [\n 'isFcManaged',\n 'isMediaForked',\n 'isTerminated',\n];\n\nconst participantBooleanKeys: ParticipantBooleanKey[] = [\n 'autoAnswerEnabled',\n 'hasJoined',\n 'hasLeft',\n 'isConsulted',\n 'isInPredial',\n 'isOffered',\n 'isWrapUp',\n 'isWrappedUp',\n];\n\nconst toBoolean = (value: unknown): boolean | undefined => {\n if (typeof value === 'boolean') {\n return value;\n }\n\n if (typeof value === 'string') {\n const normalized = value.toLowerCase();\n\n if (normalized === 'true') {\n return true;\n }\n if (normalized === 'false') {\n return false;\n }\n }\n\n return undefined;\n};\n\nconst normalizeFields = <T extends Record<string, any>>(obj: T, keys: string[]): T | undefined => {\n let updated: T | undefined;\n\n keys.forEach((key) => {\n const normalized = toBoolean(obj[key]);\n\n if (typeof normalized !== 'undefined') {\n if (!updated) {\n updated = {...obj};\n }\n (updated as any)[key] = normalized;\n }\n });\n\n return updated;\n};\n\n/**\n * Normalize backend task payload quirks so downstream code can rely on actual booleans.\n *\n * Applies to every Agent Contact websocket event before it reaches the state machine:\n * - Converts string booleans in callProcessingDetails to actual booleans.\n * - Also normalizes known boolean fields on interaction and participants.\n * - Keeps payload shape intact; only coerces known boolean fields.\n */\nexport function normalizeTaskData(data: TaskData): TaskData {\n const interaction = data?.interaction;\n\n if (!interaction) {\n return data;\n }\n\n const details = interaction.callProcessingDetails;\n const updatedDetails = details ? normalizeFields(details, booleanKeys) : undefined;\n const updatedInteractionBooleans = normalizeFields(\n interaction,\n interactionBooleanKeys as string[]\n );\n\n let updatedParticipants: typeof interaction.participants | undefined;\n const participants = interaction.participants || {};\n Object.keys(participants).forEach((id) => {\n const participant = participants[id];\n const normalized = normalizeFields(participant, participantBooleanKeys);\n if (normalized) {\n if (!updatedParticipants) {\n updatedParticipants = {...participants};\n }\n updatedParticipants[id] = normalized;\n }\n });\n\n let updatedMedia: typeof interaction.media | undefined;\n const mediaEntries = interaction.media || {};\n Object.keys(mediaEntries).forEach((id) => {\n const media = mediaEntries[id];\n const normalized = normalizeFields(media, ['isHold']);\n if (normalized) {\n if (!updatedMedia) {\n updatedMedia = {...mediaEntries};\n }\n updatedMedia[id] = normalized;\n }\n });\n\n if (!updatedDetails && !updatedInteractionBooleans && !updatedParticipants && !updatedMedia) {\n return data;\n }\n\n return {\n ...data,\n interaction: {\n ...interaction,\n ...(updatedInteractionBooleans || {}),\n callProcessingDetails: updatedDetails || details,\n participants: updatedParticipants || interaction.participants,\n media: updatedMedia || interaction.media,\n },\n };\n}\n"],"mappings":"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","ignoreList":[]}
|