@webex/contact-center 3.12.0-next.95 → 3.12.0-next.97
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/dist/services/task/TaskUtils.js +51 -3
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/state-machine/TaskStateMachine.js +50 -14
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
- package/dist/services/task/state-machine/actions.js +57 -33
- package/dist/services/task/state-machine/actions.js.map +1 -1
- package/dist/services/task/state-machine/guards.js +42 -0
- package/dist/services/task/state-machine/guards.js.map +1 -1
- package/dist/services/task/state-machine/types.js.map +1 -1
- package/dist/services/task/state-machine/uiControlsComputer.js +78 -28
- package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -1
- package/dist/services/task/voice/Voice.js +5 -3
- package/dist/services/task/voice/Voice.js.map +1 -1
- package/dist/types/services/task/TaskUtils.d.ts +2 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +72 -22
- package/dist/types/services/task/state-machine/guards.d.ts +12 -0
- package/dist/types/services/task/state-machine/types.d.ts +2 -0
- package/dist/webex.js +1 -1
- package/package.json +2 -2
- package/src/services/task/TaskUtils.ts +100 -2
- package/src/services/task/state-machine/TaskStateMachine.ts +101 -12
- package/src/services/task/state-machine/actions.ts +88 -35
- package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +73 -11
- package/src/services/task/state-machine/guards.ts +39 -0
- package/src/services/task/state-machine/types.ts +2 -0
- package/src/services/task/state-machine/uiControlsComputer.ts +135 -41
- package/src/services/task/voice/Voice.ts +6 -3
- package/test/unit/spec/services/task/TaskUtils.ts +29 -0
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +826 -0
- package/test/unit/spec/services/task/state-machine/guards.ts +158 -0
- package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +648 -5
- package/test/unit/spec/services/task/voice/Voice.ts +18 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
package/dist/webex.js
CHANGED
package/package.json
CHANGED
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@types/platform": "1.3.4",
|
|
48
|
-
"@webex/calling": "3.12.0-next.
|
|
48
|
+
"@webex/calling": "3.12.0-next.82",
|
|
49
49
|
"@webex/internal-plugin-mercury": "3.12.0-next.40",
|
|
50
50
|
"@webex/internal-plugin-metrics": "3.12.0-next.37",
|
|
51
51
|
"@webex/internal-plugin-support": "3.12.0-next.41",
|
|
@@ -83,5 +83,5 @@
|
|
|
83
83
|
"typedoc": "^0.25.0",
|
|
84
84
|
"typescript": "5.4.5"
|
|
85
85
|
},
|
|
86
|
-
"version": "3.12.0-next.
|
|
86
|
+
"version": "3.12.0-next.97"
|
|
87
87
|
}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
/* eslint-disable import/prefer-default-export */
|
|
2
2
|
import {Interaction, ITask, TaskData, MEDIA_CHANNEL} from './types';
|
|
3
3
|
import {LoginOption} from '../../types';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
PARTICIPANT_TYPE,
|
|
6
|
+
MEDIA_TYPE_MAIN_CALL,
|
|
7
|
+
CONSULT_STATE,
|
|
8
|
+
INTERACTION_STATE,
|
|
9
|
+
MEDIA_TYPE_CONSULT,
|
|
10
|
+
} from './state-machine/constants';
|
|
5
11
|
import {TaskContext} from './state-machine/types';
|
|
6
12
|
import {CC_EVENTS} from '../config/types';
|
|
7
13
|
import {OUTBOUND_TYPE, OUTDIAL_DIRECTION, OUTDIAL_MEDIA_TYPE} from '../../constants';
|
|
@@ -92,6 +98,25 @@ export const getIsConsultInProgressForConferenceControls = (
|
|
|
92
98
|
const p: any = interaction.participants?.[participantId];
|
|
93
99
|
if (!p || p.hasLeft) return false;
|
|
94
100
|
if (selfAgentId && participantId === selfAgentId) return false;
|
|
101
|
+
if (mainSet.has(participantId)) return false;
|
|
102
|
+
|
|
103
|
+
const participantType = String(p.pType ?? '').toUpperCase();
|
|
104
|
+
const isEpDnParticipant =
|
|
105
|
+
participantType === 'EP-DN' || participantType === 'EP_DN' || participantType === 'DN';
|
|
106
|
+
if (isEpDnParticipant) {
|
|
107
|
+
if (!selfAgentId) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
const selfConsultState = interaction.participants?.[selfAgentId]?.consultState as
|
|
111
|
+
| string
|
|
112
|
+
| undefined;
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
selfConsultState === 'consulting' ||
|
|
116
|
+
selfConsultState === 'conferencing' ||
|
|
117
|
+
selfConsultState === 'consultInitiated'
|
|
118
|
+
);
|
|
119
|
+
}
|
|
95
120
|
|
|
96
121
|
const consultState = p.consultState as string | undefined;
|
|
97
122
|
const isRonaPendingConsultee = consultState === 'consultReserved' && p.hasJoined === false;
|
|
@@ -100,7 +125,7 @@ export const getIsConsultInProgressForConferenceControls = (
|
|
|
100
125
|
p.currentState === 'consulting' ||
|
|
101
126
|
(p.isConsulted === true && consultState !== 'consultCompleted' && !isRonaPendingConsultee);
|
|
102
127
|
|
|
103
|
-
return consultLegActive
|
|
128
|
+
return consultLegActive;
|
|
104
129
|
});
|
|
105
130
|
});
|
|
106
131
|
};
|
|
@@ -216,6 +241,79 @@ export const getIsConferenceInProgress = (data: TaskData): boolean => {
|
|
|
216
241
|
return agentParticipants.size >= 2;
|
|
217
242
|
};
|
|
218
243
|
|
|
244
|
+
const EP_DN_MERGE_TERMINAL_CONSULT_EVENTS = new Set<string>([
|
|
245
|
+
CC_EVENTS.AGENT_CONSULT_ENDED,
|
|
246
|
+
CC_EVENTS.AGENT_CONSULT_CONFERENCE_ENDED,
|
|
247
|
+
]);
|
|
248
|
+
|
|
249
|
+
const isEpDnParticipantType = (pType: string | undefined): boolean => {
|
|
250
|
+
const normalized = String(pType ?? '').toUpperCase();
|
|
251
|
+
|
|
252
|
+
return normalized === 'EP-DN' || normalized === 'EP_DN' || normalized === 'DN';
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
/** EP-DN consult merged to conference before a second agent joins the main leg (CAI-8329). */
|
|
256
|
+
export const isEpDnConsultPendingConferenceMerge = (
|
|
257
|
+
taskData: TaskData | undefined,
|
|
258
|
+
selfAgentId: string | undefined
|
|
259
|
+
): boolean => {
|
|
260
|
+
if (!taskData?.interaction || !selfAgentId) return false;
|
|
261
|
+
|
|
262
|
+
if (taskData.type && EP_DN_MERGE_TERMINAL_CONSULT_EVENTS.has(taskData.type)) {
|
|
263
|
+
return false;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const interaction = taskData.interaction;
|
|
267
|
+
const mainCallId = interaction.mainInteractionId || taskData.interactionId;
|
|
268
|
+
if (!mainCallId) return false;
|
|
269
|
+
|
|
270
|
+
if (getConferenceParticipantsCount(interaction, mainCallId) > 1) return false;
|
|
271
|
+
|
|
272
|
+
const selfParticipant = interaction.participants?.[selfAgentId] as
|
|
273
|
+
| {consultState?: string}
|
|
274
|
+
| undefined;
|
|
275
|
+
if (taskData.isConsulted === true || selfParticipant?.consultState === 'consultCompleted') {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const mainParticipantIds = new Set(interaction.media?.[mainCallId]?.participants ?? []);
|
|
280
|
+
const hasEpDnOnConsultLegNotMain = Object.values(interaction.media ?? {}).some((media: any) => {
|
|
281
|
+
if (media?.mType !== MEDIA_TYPE_CONSULT) return false;
|
|
282
|
+
|
|
283
|
+
return (media.participants ?? []).some((participantId: string) => {
|
|
284
|
+
if (participantId === selfAgentId || mainParticipantIds.has(participantId)) return false;
|
|
285
|
+
const participant = interaction.participants?.[participantId] as
|
|
286
|
+
| {hasLeft?: boolean; pType?: string}
|
|
287
|
+
| undefined;
|
|
288
|
+
if (!participant || participant.hasLeft) return false;
|
|
289
|
+
|
|
290
|
+
return isEpDnParticipantType(participant.pType);
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
if (!hasEpDnOnConsultLegNotMain) return false;
|
|
294
|
+
|
|
295
|
+
const callProcessingDetails = interaction.callProcessingDetails as
|
|
296
|
+
| {hasCustomerLeft?: string}
|
|
297
|
+
| undefined;
|
|
298
|
+
const customerLeft =
|
|
299
|
+
callProcessingDetails?.hasCustomerLeft === 'true' ||
|
|
300
|
+
!getIsCustomerInCall(interaction, mainCallId);
|
|
301
|
+
|
|
302
|
+
const conferenceFromPayload =
|
|
303
|
+
interaction.state === INTERACTION_STATE.CONFERENCE ||
|
|
304
|
+
getIsConferenceInProgress(taskData) ||
|
|
305
|
+
(interaction.state === INTERACTION_STATE.POST_CALL && customerLeft);
|
|
306
|
+
if (!conferenceFromPayload) return false;
|
|
307
|
+
|
|
308
|
+
return (
|
|
309
|
+
taskData.consultingAgentId === selfAgentId ||
|
|
310
|
+
interaction.owner === selfAgentId ||
|
|
311
|
+
selfParticipant?.consultState === CONSULT_STATE.CONSULTING ||
|
|
312
|
+
selfParticipant?.consultState === CONSULT_STATE.CONFERENCING ||
|
|
313
|
+
selfParticipant?.consultState === 'consultInitiated'
|
|
314
|
+
);
|
|
315
|
+
};
|
|
316
|
+
|
|
219
317
|
/**
|
|
220
318
|
* Checks if the current agent is a secondary agent in a consultation scenario.
|
|
221
319
|
* Secondary agents are those who were consulted (not the original call owner).
|
|
@@ -101,6 +101,11 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
|
|
|
101
101
|
target: TaskState.WRAPPING_UP,
|
|
102
102
|
actions: ['updateTaskData', 'markEnded', 'emitTaskHydrate'],
|
|
103
103
|
},
|
|
104
|
+
{
|
|
105
|
+
guard: guards.isEpDnPendingConferenceMergeHydrate,
|
|
106
|
+
target: TaskState.CONFERENCING,
|
|
107
|
+
actions: ['updateTaskData', 'emitTaskHydrate'],
|
|
108
|
+
},
|
|
104
109
|
{
|
|
105
110
|
guard: guards.isInteractionConsulting,
|
|
106
111
|
target: TaskState.CONSULTING,
|
|
@@ -299,6 +304,14 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
|
|
|
299
304
|
target: TaskState.HELD,
|
|
300
305
|
actions: ['updateTaskData', 'setHoldState', 'emitTaskHold'],
|
|
301
306
|
},
|
|
307
|
+
[TaskEvent.UNHOLD_INITIATED]: {
|
|
308
|
+
target: TaskState.RESUME_INITIATING,
|
|
309
|
+
},
|
|
310
|
+
// Resume while machine is CONNECTED but main media is still on hold (stale state after consult RONA/decline).
|
|
311
|
+
[TaskEvent.UNHOLD_SUCCESS]: {
|
|
312
|
+
target: TaskState.CONNECTED,
|
|
313
|
+
actions: ['updateTaskData', 'setHoldState', 'emitTaskResume'],
|
|
314
|
+
},
|
|
302
315
|
// Click of the consult button
|
|
303
316
|
[TaskEvent.CONSULT]: {
|
|
304
317
|
target: TaskState.CONSULT_INITIATING,
|
|
@@ -691,6 +704,48 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
|
|
|
691
704
|
actions: ['updateTaskData', 'setHoldState', 'emitTaskResume'],
|
|
692
705
|
},
|
|
693
706
|
|
|
707
|
+
[TaskEvent.PARTICIPANT_LEAVE]: [
|
|
708
|
+
{
|
|
709
|
+
guard: (params) =>
|
|
710
|
+
guards.didCurrentAgentLeaveConference(params) && guards.shouldWrapUp(params),
|
|
711
|
+
target: TaskState.WRAPPING_UP,
|
|
712
|
+
actions: [
|
|
713
|
+
'updateTaskData',
|
|
714
|
+
'handleParticipantLeft',
|
|
715
|
+
'markEnded',
|
|
716
|
+
'clearConsultState',
|
|
717
|
+
'emitTaskParticipantLeft',
|
|
718
|
+
'emitTaskWrapup',
|
|
719
|
+
],
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
guard: guards.didCurrentAgentLeaveConference,
|
|
723
|
+
target: TaskState.TERMINATED,
|
|
724
|
+
actions: [
|
|
725
|
+
'updateTaskData',
|
|
726
|
+
'handleParticipantLeft',
|
|
727
|
+
'markEnded',
|
|
728
|
+
'clearConsultState',
|
|
729
|
+
'emitTaskParticipantLeft',
|
|
730
|
+
'emitTaskEnd',
|
|
731
|
+
],
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
guard: (params) =>
|
|
735
|
+
!guards.didCurrentAgentLeaveConference(params) &&
|
|
736
|
+
guards.shouldDowngradeConferenceToConnected(params),
|
|
737
|
+
target: TaskState.CONNECTED,
|
|
738
|
+
actions: [
|
|
739
|
+
'updateTaskData',
|
|
740
|
+
'handleParticipantLeft',
|
|
741
|
+
'clearConsultState',
|
|
742
|
+
'emitTaskParticipantLeft',
|
|
743
|
+
'emitTaskConferenceEnded',
|
|
744
|
+
],
|
|
745
|
+
},
|
|
746
|
+
{actions: ['updateTaskData', 'handleParticipantLeft', 'emitTaskParticipantLeft']},
|
|
747
|
+
],
|
|
748
|
+
|
|
694
749
|
[TaskEvent.TRANSFER_SUCCESS]: [
|
|
695
750
|
{
|
|
696
751
|
guard: guards.shouldWrapUpOrIsInitiator,
|
|
@@ -711,14 +766,6 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
|
|
|
711
766
|
actions: ['setTransferConferenceRequested', 'emitTaskTransferConference'],
|
|
712
767
|
},
|
|
713
768
|
[TaskEvent.TRANSFER_CONFERENCE_SUCCESS]: [
|
|
714
|
-
{
|
|
715
|
-
guard: ({context}) => context.transferConferenceRequested !== true,
|
|
716
|
-
actions: [
|
|
717
|
-
'updateTaskData',
|
|
718
|
-
'handleTransferConferenceSuccess',
|
|
719
|
-
'clearTransferConferenceRequested',
|
|
720
|
-
],
|
|
721
|
-
},
|
|
722
769
|
{
|
|
723
770
|
guard: guards.shouldWrapUp,
|
|
724
771
|
target: TaskState.WRAPPING_UP,
|
|
@@ -732,8 +779,8 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
|
|
|
732
779
|
],
|
|
733
780
|
},
|
|
734
781
|
{
|
|
735
|
-
// Non-initiator (
|
|
736
|
-
guard: (
|
|
782
|
+
// Non-initiator (e.g. consultee receiving transfer) moves to CONFERENCING
|
|
783
|
+
guard: (params) => !guards.isSelfConferenceTransferInitiator(params),
|
|
737
784
|
target: TaskState.CONFERENCING,
|
|
738
785
|
actions: [
|
|
739
786
|
'updateTaskData',
|
|
@@ -977,14 +1024,46 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
|
|
|
977
1024
|
},
|
|
978
1025
|
[TaskEvent.TRANSFER_CONFERENCE_SUCCESS]: [
|
|
979
1026
|
{
|
|
980
|
-
|
|
981
|
-
|
|
1027
|
+
guard: guards.isPassiveConferenceTransferObserver,
|
|
1028
|
+
actions: [
|
|
1029
|
+
'updateTaskData',
|
|
1030
|
+
'handleTransferConferenceSuccess',
|
|
1031
|
+
'clearTransferConferenceRequested',
|
|
1032
|
+
],
|
|
1033
|
+
},
|
|
1034
|
+
{
|
|
1035
|
+
guard: guards.shouldWrapUp,
|
|
1036
|
+
target: TaskState.WRAPPING_UP,
|
|
1037
|
+
actions: [
|
|
1038
|
+
'updateTaskData',
|
|
1039
|
+
'markEnded',
|
|
1040
|
+
'clearConsultState',
|
|
1041
|
+
'handleTransferConferenceSuccess',
|
|
1042
|
+
'clearTransferConferenceRequested',
|
|
1043
|
+
'emitTaskWrapup',
|
|
1044
|
+
],
|
|
1045
|
+
},
|
|
1046
|
+
{
|
|
1047
|
+
guard: (params) => !guards.isSelfConferenceTransferInitiator(params),
|
|
1048
|
+
target: TaskState.CONFERENCING,
|
|
982
1049
|
actions: [
|
|
983
1050
|
'updateTaskData',
|
|
1051
|
+
'clearConsultState',
|
|
984
1052
|
'handleTransferConferenceSuccess',
|
|
985
1053
|
'clearTransferConferenceRequested',
|
|
986
1054
|
],
|
|
987
1055
|
},
|
|
1056
|
+
{
|
|
1057
|
+
target: TaskState.TERMINATED,
|
|
1058
|
+
actions: [
|
|
1059
|
+
'updateTaskData',
|
|
1060
|
+
'markEnded',
|
|
1061
|
+
'clearConsultState',
|
|
1062
|
+
'handleTransferConferenceSuccess',
|
|
1063
|
+
'clearTransferConferenceRequested',
|
|
1064
|
+
'emitTaskEnd',
|
|
1065
|
+
],
|
|
1066
|
+
},
|
|
988
1067
|
],
|
|
989
1068
|
[TaskEvent.TRANSFER_CONFERENCE_FAILED]: {
|
|
990
1069
|
actions: ['clearTransferConferenceRequested', 'emitTaskTransferConferenceFailed'],
|
|
@@ -1026,6 +1105,16 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
|
|
|
1026
1105
|
actions: ['updateTaskData', 'requestCleanup'],
|
|
1027
1106
|
},
|
|
1028
1107
|
],
|
|
1108
|
+
|
|
1109
|
+
[TaskEvent.CONSULT_FAILED]: {
|
|
1110
|
+
actions: ['updateTaskData', 'handleConsultFailed'],
|
|
1111
|
+
},
|
|
1112
|
+
|
|
1113
|
+
[TaskEvent.TASK_WRAPUP]: {
|
|
1114
|
+
guard: guards.shouldWrapUp,
|
|
1115
|
+
target: TaskState.WRAPPING_UP,
|
|
1116
|
+
actions: ['updateTaskData', 'markEnded', 'clearConsultState', 'emitTaskWrapup'],
|
|
1117
|
+
},
|
|
1029
1118
|
},
|
|
1030
1119
|
},
|
|
1031
1120
|
|
|
@@ -19,10 +19,17 @@ import {
|
|
|
19
19
|
MEDIA_TYPE_CONSULT,
|
|
20
20
|
} from './constants';
|
|
21
21
|
import {DestinationType, TaskData} from '../types';
|
|
22
|
+
import {CC_EVENTS} from '../../config/types';
|
|
22
23
|
import {computeUIControls, getDefaultUIControls} from './uiControlsComputer';
|
|
23
|
-
import {getIsConferenceInProgress} from '../TaskUtils';
|
|
24
|
+
import {getIsConferenceInProgress, isEpDnConsultPendingConferenceMerge} from '../TaskUtils';
|
|
24
25
|
import {hasActiveConsultInPostCall} from './guards';
|
|
25
26
|
|
|
27
|
+
/** Terminal consult events that clear consult context in deriveTaskDataUpdates. */
|
|
28
|
+
const CONSULT_CONTEXT_CLEAR_EVENTS = new Set<string>([
|
|
29
|
+
CC_EVENTS.AGENT_CONSULT_ENDED,
|
|
30
|
+
CC_EVENTS.AGENT_CONSULT_FAILED,
|
|
31
|
+
]);
|
|
32
|
+
|
|
26
33
|
const determineConsultInitiator = (
|
|
27
34
|
taskData: TaskData | undefined,
|
|
28
35
|
selfAgentId: string | undefined
|
|
@@ -98,7 +105,8 @@ const isActiveConsultState = (taskData: TaskData | undefined, selfAgentId?: stri
|
|
|
98
105
|
return false;
|
|
99
106
|
}
|
|
100
107
|
if (
|
|
101
|
-
selfParticipant?.consultState === CONSULT_STATE.CONSULTING
|
|
108
|
+
(selfParticipant?.consultState === CONSULT_STATE.CONSULTING ||
|
|
109
|
+
selfParticipant?.consultState === CONSULT_STATE.CONFERENCING) &&
|
|
102
110
|
hasConsultMedia &&
|
|
103
111
|
taskData?.isConsulted === false
|
|
104
112
|
) {
|
|
@@ -125,6 +133,7 @@ const isSelfConsultingOrPending = (
|
|
|
125
133
|
|
|
126
134
|
return (
|
|
127
135
|
selfParticipant?.consultState === CONSULT_STATE.CONSULTING ||
|
|
136
|
+
selfParticipant?.consultState === CONSULT_STATE.CONFERENCING ||
|
|
128
137
|
selfParticipant?.consultState === 'consultInitiated'
|
|
129
138
|
);
|
|
130
139
|
};
|
|
@@ -225,6 +234,11 @@ export const getTaskStateForUiControls = (
|
|
|
225
234
|
return TaskState.WRAPPING_UP;
|
|
226
235
|
}
|
|
227
236
|
|
|
237
|
+
// EP-DN consult merged to conference before Agent 2 joins main — UI is conferencing (CAI-8329).
|
|
238
|
+
if (isEpDnConsultPendingConferenceMerge(taskData, selfAgentId)) {
|
|
239
|
+
return TaskState.CONFERENCING;
|
|
240
|
+
}
|
|
241
|
+
|
|
228
242
|
if (isActiveConsultState(taskData, selfAgentId)) {
|
|
229
243
|
return TaskState.CONSULTING;
|
|
230
244
|
}
|
|
@@ -250,34 +264,43 @@ export const getTaskStateForUiControls = (
|
|
|
250
264
|
const deriveTaskDataUpdates = (context: TaskContext, taskData: TaskData | undefined) =>
|
|
251
265
|
taskData
|
|
252
266
|
? (() => {
|
|
267
|
+
const enrichedTaskData: TaskData =
|
|
268
|
+
taskData.type === 'AgentConsultCreated' && taskData.agentId
|
|
269
|
+
? {...taskData, consultingAgentId: taskData.agentId}
|
|
270
|
+
: taskData;
|
|
271
|
+
|
|
253
272
|
const updates: Partial<TaskContext> = {
|
|
254
|
-
taskData,
|
|
255
|
-
...deriveRecordingState(
|
|
273
|
+
taskData: enrichedTaskData,
|
|
274
|
+
...deriveRecordingState(enrichedTaskData),
|
|
256
275
|
};
|
|
257
276
|
|
|
258
|
-
const selfAgentId = context.uiControlConfig.agentId ??
|
|
277
|
+
const selfAgentId = context.uiControlConfig.agentId ?? enrichedTaskData?.agentId;
|
|
259
278
|
const consultingActive =
|
|
260
|
-
isActiveConsultState(
|
|
261
|
-
hasActiveConsultInPostCall(
|
|
279
|
+
isActiveConsultState(enrichedTaskData, selfAgentId) ||
|
|
280
|
+
hasActiveConsultInPostCall(enrichedTaskData, selfAgentId);
|
|
262
281
|
const conferenceFromPayload =
|
|
263
|
-
|
|
264
|
-
getIsConferenceInProgress(
|
|
265
|
-
const selfConsultingOrPending = isSelfConsultingOrPending(
|
|
282
|
+
enrichedTaskData?.interaction?.state === INTERACTION_STATE.CONFERENCE ||
|
|
283
|
+
getIsConferenceInProgress(enrichedTaskData);
|
|
284
|
+
const selfConsultingOrPending = isSelfConsultingOrPending(enrichedTaskData, selfAgentId);
|
|
266
285
|
const inferredConsultingInitiator =
|
|
267
|
-
selfConsultingOrPending &&
|
|
286
|
+
selfConsultingOrPending && enrichedTaskData?.isConsulted === false;
|
|
268
287
|
|
|
269
|
-
if (
|
|
288
|
+
if (enrichedTaskData.destAgentId) {
|
|
270
289
|
const isEpDnWithStoredId =
|
|
271
290
|
context.consultDestinationType === 'entryPoint' && context.consultDestinationAgentId;
|
|
272
291
|
if (!isEpDnWithStoredId) {
|
|
273
|
-
updates.consultDestinationAgentId =
|
|
292
|
+
updates.consultDestinationAgentId = enrichedTaskData.destAgentId;
|
|
274
293
|
}
|
|
275
294
|
}
|
|
276
295
|
|
|
277
296
|
const isConsultTerminalEvent =
|
|
278
|
-
|
|
297
|
+
enrichedTaskData.type != null && CONSULT_CONTEXT_CLEAR_EVENTS.has(enrichedTaskData.type);
|
|
298
|
+
const epDnPendingMergeActive = isEpDnConsultPendingConferenceMerge(
|
|
299
|
+
enrichedTaskData,
|
|
300
|
+
selfAgentId
|
|
301
|
+
);
|
|
279
302
|
|
|
280
|
-
if (isConsultTerminalEvent) {
|
|
303
|
+
if (isConsultTerminalEvent && !epDnPendingMergeActive) {
|
|
281
304
|
updates.consultInitiator = false;
|
|
282
305
|
updates.consultFromConference = false;
|
|
283
306
|
updates.consultDestinationAgentJoined = false;
|
|
@@ -285,21 +308,38 @@ const deriveTaskDataUpdates = (context: TaskContext, taskData: TaskData | undefi
|
|
|
285
308
|
updates.consultDestinationType = null;
|
|
286
309
|
updates.consultDestinationAgentId = null;
|
|
287
310
|
updates.transferConferenceRequested = false;
|
|
311
|
+
updates.hideBlindTransferForEpDnPendingMerge = false;
|
|
288
312
|
}
|
|
289
313
|
|
|
290
|
-
|
|
291
|
-
|
|
314
|
+
updates.hideBlindTransferForEpDnPendingMerge = isEpDnConsultPendingConferenceMerge(
|
|
315
|
+
enrichedTaskData,
|
|
316
|
+
selfAgentId
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
if (consultingActive && enrichedTaskData.destinationType) {
|
|
320
|
+
updates.consultDestinationType = mapConsultDestinationType(
|
|
321
|
+
enrichedTaskData.destinationType
|
|
322
|
+
);
|
|
292
323
|
}
|
|
293
324
|
|
|
294
|
-
if (!isConsultTerminalEvent
|
|
295
|
-
const consultInitiator = determineConsultInitiator(
|
|
325
|
+
if (!isConsultTerminalEvent) {
|
|
326
|
+
const consultInitiator = determineConsultInitiator(enrichedTaskData, selfAgentId);
|
|
296
327
|
if (consultInitiator !== undefined) {
|
|
297
328
|
updates.consultInitiator = consultInitiator;
|
|
298
329
|
} else if (
|
|
299
|
-
|
|
300
|
-
|
|
330
|
+
enrichedTaskData.type === 'AgentConsultCreated' &&
|
|
331
|
+
enrichedTaskData.agentId &&
|
|
332
|
+
enrichedTaskData.agentId !== selfAgentId &&
|
|
333
|
+
conferenceFromPayload
|
|
301
334
|
) {
|
|
302
|
-
updates.consultInitiator =
|
|
335
|
+
updates.consultInitiator = false;
|
|
336
|
+
} else if (!context.consultInitiator) {
|
|
337
|
+
if (
|
|
338
|
+
inferredConsultingInitiator ||
|
|
339
|
+
(consultingActive && enrichedTaskData.isConsulted === false)
|
|
340
|
+
) {
|
|
341
|
+
updates.consultInitiator = true;
|
|
342
|
+
}
|
|
303
343
|
}
|
|
304
344
|
}
|
|
305
345
|
|
|
@@ -308,7 +348,9 @@ const deriveTaskDataUpdates = (context: TaskContext, taskData: TaskData | undefi
|
|
|
308
348
|
!isConsultTerminalEvent &&
|
|
309
349
|
effectiveConsultInitiator &&
|
|
310
350
|
conferenceFromPayload &&
|
|
311
|
-
(consultingActive ||
|
|
351
|
+
(consultingActive ||
|
|
352
|
+
selfConsultingOrPending ||
|
|
353
|
+
Boolean(enrichedTaskData?.consultMediaResourceId))
|
|
312
354
|
) {
|
|
313
355
|
updates.consultFromConference = true;
|
|
314
356
|
}
|
|
@@ -316,16 +358,16 @@ const deriveTaskDataUpdates = (context: TaskContext, taskData: TaskData | undefi
|
|
|
316
358
|
if (
|
|
317
359
|
!isConsultTerminalEvent &&
|
|
318
360
|
(consultingActive || selfConsultingOrPending) &&
|
|
319
|
-
|
|
361
|
+
enrichedTaskData.interaction
|
|
320
362
|
) {
|
|
321
|
-
const joinedConsultee = hasJoinedConsultDestination(
|
|
363
|
+
const joinedConsultee = hasJoinedConsultDestination(enrichedTaskData, selfAgentId);
|
|
322
364
|
const selfParticipant = selfAgentId
|
|
323
|
-
? (
|
|
365
|
+
? (enrichedTaskData.interaction.participants?.[selfAgentId] as
|
|
324
366
|
| {consultState?: string}
|
|
325
367
|
| undefined)
|
|
326
368
|
: undefined;
|
|
327
369
|
const agentConsultingSelfJoined =
|
|
328
|
-
|
|
370
|
+
enrichedTaskData.type === 'AgentConsulting' &&
|
|
329
371
|
effectiveConsultInitiator &&
|
|
330
372
|
selfParticipant?.consultState === CONSULT_STATE.CONSULTING;
|
|
331
373
|
|
|
@@ -337,8 +379,8 @@ const deriveTaskDataUpdates = (context: TaskContext, taskData: TaskData | undefi
|
|
|
337
379
|
|
|
338
380
|
if (!context.consultDestinationType && !updates.consultDestinationType) {
|
|
339
381
|
const hasEpDnParticipant = Boolean(
|
|
340
|
-
|
|
341
|
-
Object.values(
|
|
382
|
+
enrichedTaskData.interaction.participants &&
|
|
383
|
+
Object.values(enrichedTaskData.interaction.participants).some((p: any) => {
|
|
342
384
|
if (p?.hasLeft) return false;
|
|
343
385
|
const pType = String(p?.pType ?? '').toUpperCase();
|
|
344
386
|
|
|
@@ -349,7 +391,7 @@ const deriveTaskDataUpdates = (context: TaskContext, taskData: TaskData | undefi
|
|
|
349
391
|
}
|
|
350
392
|
|
|
351
393
|
if (effectiveConsultInitiator) {
|
|
352
|
-
const consultCallHeld = deriveConsultCallHeldFromTaskData(
|
|
394
|
+
const consultCallHeld = deriveConsultCallHeldFromTaskData(enrichedTaskData);
|
|
353
395
|
if (consultCallHeld !== undefined) {
|
|
354
396
|
updates.consultCallHeld = consultCallHeld;
|
|
355
397
|
}
|
|
@@ -360,9 +402,9 @@ const deriveTaskDataUpdates = (context: TaskContext, taskData: TaskData | undefi
|
|
|
360
402
|
...context,
|
|
361
403
|
...updates,
|
|
362
404
|
} as TaskContext;
|
|
363
|
-
const inferredState = getTaskStateForUiControls(
|
|
405
|
+
const inferredState = getTaskStateForUiControls(enrichedTaskData, selfAgentId);
|
|
364
406
|
|
|
365
|
-
updates.uiControls = computeUIControls(inferredState, nextContext,
|
|
407
|
+
updates.uiControls = computeUIControls(inferredState, nextContext, enrichedTaskData);
|
|
366
408
|
|
|
367
409
|
return updates;
|
|
368
410
|
})()
|
|
@@ -385,6 +427,7 @@ export function createInitialContext(
|
|
|
385
427
|
consultDestinationAgentId: null,
|
|
386
428
|
consultDestinationAgentJoined: false,
|
|
387
429
|
consultCallHeld: false,
|
|
430
|
+
hideBlindTransferForEpDnPendingMerge: false,
|
|
388
431
|
recordingControlsAvailable: false,
|
|
389
432
|
recordingInProgress: false,
|
|
390
433
|
uiControlConfig,
|
|
@@ -446,6 +489,11 @@ export const actions: TaskActionsMap = {
|
|
|
446
489
|
|
|
447
490
|
handleConsultFailed: assign(({context, event}: TaskActionArgs) => {
|
|
448
491
|
const taskData = getTaskDataFromEvent(event) ?? context.taskData;
|
|
492
|
+
const selfAgentId = context.uiControlConfig.agentId ?? taskData?.agentId;
|
|
493
|
+
const hideBlindTransferForEpDnPendingMerge = isEpDnConsultPendingConferenceMerge(
|
|
494
|
+
taskData,
|
|
495
|
+
selfAgentId
|
|
496
|
+
);
|
|
449
497
|
const cleared = {
|
|
450
498
|
consultDestinationType: null,
|
|
451
499
|
consultDestinationAgentId: null,
|
|
@@ -455,8 +503,8 @@ export const actions: TaskActionsMap = {
|
|
|
455
503
|
consultCallHeld: false,
|
|
456
504
|
consultFromConference: false,
|
|
457
505
|
transferConferenceRequested: false,
|
|
506
|
+
hideBlindTransferForEpDnPendingMerge,
|
|
458
507
|
};
|
|
459
|
-
const selfAgentId = context.uiControlConfig.agentId ?? taskData?.agentId;
|
|
460
508
|
const nextContext = {...context, ...cleared, taskData} as TaskContext;
|
|
461
509
|
const inferredState = getTaskStateForUiControls(taskData, selfAgentId);
|
|
462
510
|
|
|
@@ -539,6 +587,12 @@ export const actions: TaskActionsMap = {
|
|
|
539
587
|
}),
|
|
540
588
|
|
|
541
589
|
clearConsultState: assign(({context, event}: TaskActionArgs) => {
|
|
590
|
+
const taskData = context.taskData ?? getTaskDataFromEvent(event);
|
|
591
|
+
const selfAgentId = context.uiControlConfig.agentId ?? taskData?.agentId;
|
|
592
|
+
const hideBlindTransferForEpDnPendingMerge = isEpDnConsultPendingConferenceMerge(
|
|
593
|
+
taskData,
|
|
594
|
+
selfAgentId
|
|
595
|
+
);
|
|
542
596
|
const cleared = {
|
|
543
597
|
consultDestinationType: null,
|
|
544
598
|
consultDestinationAgentId: null,
|
|
@@ -548,9 +602,8 @@ export const actions: TaskActionsMap = {
|
|
|
548
602
|
consultCallHeld: false,
|
|
549
603
|
consultFromConference: false,
|
|
550
604
|
transferConferenceRequested: false,
|
|
605
|
+
hideBlindTransferForEpDnPendingMerge,
|
|
551
606
|
};
|
|
552
|
-
const taskData = context.taskData ?? getTaskDataFromEvent(event);
|
|
553
|
-
const selfAgentId = context.uiControlConfig.agentId ?? taskData?.agentId;
|
|
554
607
|
const nextContext = {...context, ...cleared, taskData} as TaskContext;
|
|
555
608
|
const inferredState = getTaskStateForUiControls(taskData, selfAgentId);
|
|
556
609
|
|