@webex/contact-center 3.12.0-next.122 → 3.12.0-next.124
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 +1 -1
- package/ai-docs/CONTRACTS.md +1 -1
- package/dist/services/task/TaskManager.js +137 -0
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/state-machine/TaskStateMachine.js +51 -23
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
- package/dist/services/task/voice/Voice.js +39 -1
- package/dist/services/task/voice/Voice.js.map +1 -1
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +86 -0
- package/dist/webex.js +1 -1
- package/package.json +2 -2
- package/src/services/task/TaskManager.ts +194 -1
- package/src/services/task/ai-docs/task-spec.md +3 -1
- package/src/services/task/state-machine/TaskStateMachine.ts +47 -20
- package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +18 -7
- package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +49 -9
- package/src/services/task/voice/Voice.ts +44 -2
- package/test/unit/spec/services/task/Task.ts +74 -6
- package/test/unit/spec/services/task/TaskManager.ts +416 -1
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +568 -0
- package/test/unit/spec/services/task/voice/Voice.ts +158 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
|
@@ -94,8 +94,11 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
94
94
|
- `CONSULTING_ACTIVE` -> `CONSULTING`
|
|
95
95
|
- Guard: none
|
|
96
96
|
- Actions: `updateTaskData`, `setConsultAgentJoined`, `emitTaskConsultAccepted`, `emitTaskConsulting`
|
|
97
|
-
- `TASK_WRAPUP` -> `
|
|
98
|
-
- Guard:
|
|
97
|
+
- `TASK_WRAPUP` -> `WRAPPING_UP`
|
|
98
|
+
- Guard: `guards.shouldWrapUp`
|
|
99
|
+
- Actions: `updateTaskData`, `markEnded`; `WRAPPING_UP` entry emits `task:wrapup`
|
|
100
|
+
- `TASK_WRAPUP` -> `TERMINATED` (default branch)
|
|
101
|
+
- Guard: default
|
|
99
102
|
- Actions: `updateTaskData`, `markEnded`, `emitTaskEnd`
|
|
100
103
|
- `RONA` / `ASSIGN_FAILED` / `INVITE_FAILED` / `OUTBOUND_FAILED` -> `TERMINATED`
|
|
101
104
|
- Guard: none
|
|
@@ -159,7 +162,7 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
159
162
|
- Actions: `updateTaskData`, `markEnded`, `emitTaskEnd`
|
|
160
163
|
- `TASK_WRAPUP` -> `WRAPPING_UP`
|
|
161
164
|
- Guard: none
|
|
162
|
-
- Actions: `updateTaskData`, `markEnded
|
|
165
|
+
- Actions: `updateTaskData`, `markEnded`; `WRAPPING_UP` entry emits `task:wrapup`
|
|
163
166
|
- `PAUSE_RECORDING` / `RESUME_RECORDING` -> Stay `CONNECTED`
|
|
164
167
|
- Guard: none
|
|
165
168
|
- Actions: `updateTaskData`, `setRecordingState`, `emitTaskRecordingPaused` / `emitTaskRecordingResumed`
|
|
@@ -366,7 +369,7 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
366
369
|
- Actions: `updateTaskData`, `handleTransferConferenceSuccess`, `clearTransferConferenceRequested`
|
|
367
370
|
- `TRANSFER_CONFERENCE_SUCCESS` -> `WRAPPING_UP`
|
|
368
371
|
- Guard: `guards.shouldWrapUp`
|
|
369
|
-
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, `handleTransferConferenceSuccess`, `clearTransferConferenceRequested
|
|
372
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, `handleTransferConferenceSuccess`, `clearTransferConferenceRequested`; `WRAPPING_UP` entry emits `task:wrapup`
|
|
370
373
|
- `TRANSFER_CONFERENCE_SUCCESS` -> `CONFERENCING`
|
|
371
374
|
- Guard: inline `!context.consultInitiator`
|
|
372
375
|
- Actions: `updateTaskData`, `clearConsultState`, `handleTransferConferenceSuccess`, `clearTransferConferenceRequested`
|
|
@@ -384,7 +387,7 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
384
387
|
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, `emitTaskWrapup`, `requestCleanup`
|
|
385
388
|
- `TASK_WRAPUP` -> `WRAPPING_UP`
|
|
386
389
|
- Guard: none
|
|
387
|
-
- Actions: `updateTaskData`, `markEnded`, `clearConsultState
|
|
390
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`; `WRAPPING_UP` entry emits `task:wrapup`
|
|
388
391
|
- `MERGE_TO_CONFERENCE` -> `CONF_INITIATING`
|
|
389
392
|
- Guard: none
|
|
390
393
|
- Actions: none
|
|
@@ -465,7 +468,7 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
465
468
|
- Actions: `clearTransferConferenceRequested`
|
|
466
469
|
- `PARTICIPANT_LEAVE` -> `WRAPPING_UP`
|
|
467
470
|
- Guard: `guards.didCurrentAgentLeaveMainInteraction && guards.shouldWrapUp`
|
|
468
|
-
- Actions: `updateTaskData`, `handleParticipantLeft`, `markEnded`, `clearConsultState`, `emitTaskParticipantLeft
|
|
471
|
+
- Actions: `updateTaskData`, `handleParticipantLeft`, `markEnded`, `clearConsultState`, `emitTaskParticipantLeft` (entry emits `task:wrapup`)
|
|
469
472
|
- `PARTICIPANT_LEAVE` -> `TERMINATED`
|
|
470
473
|
- Guard: `guards.didCurrentAgentLeaveMainInteraction`
|
|
471
474
|
- Actions: `updateTaskData`, `handleParticipantLeft`, `markEnded`, `clearConsultState`, `emitTaskParticipantLeft`, `emitTaskEnd`
|
|
@@ -483,15 +486,21 @@ is not treated as departure. `PARTICIPANT_LEAVE` is handled in `HELD`,
|
|
|
483
486
|
`RESUME_INITIATING`, `CONSULTING`, `CONSULT_INITIATING`, and `CONFERENCING`;
|
|
484
487
|
`CONNECTED`, `HOLD_INITIATING`, and `CONF_INITIATING` ignore the event.
|
|
485
488
|
|
|
489
|
+
- `EXIT_CONFERENCE_SUCCESS` -> `WRAPPING_UP` or `TERMINATED`
|
|
490
|
+
- Guard: `guards.shouldWrapUp` or default
|
|
491
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, or `emitTaskEnd` on terminate; `WRAPPING_UP` entry emits `task:wrapup` (shared helper on `CONNECTED`, `HELD`, and `CONFERENCING` for Voice HTTP exit)
|
|
486
492
|
- `CONFERENCE_END` -> `WRAPPING_UP`
|
|
487
493
|
- Guard: `guards.shouldWrapUp`
|
|
488
|
-
- Actions: `updateTaskData`, `markEnded`, `clearConsultState
|
|
494
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`; `WRAPPING_UP` entry emits `task:wrapup`
|
|
489
495
|
- `CONFERENCE_END` -> `CONNECTED`
|
|
490
496
|
- Guard: inline `!context.exitingConference && customerInCall`
|
|
491
497
|
- Actions: `updateTaskData`, `clearConsultState`, `emitTaskConferenceEnded`
|
|
492
498
|
- `CONFERENCE_END` -> `TERMINATED` (default branch)
|
|
493
499
|
- Guard: default
|
|
494
500
|
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, `emitTaskEnd`
|
|
501
|
+
- `TASK_WRAPUP` -> `WRAPPING_UP`
|
|
502
|
+
- Guard: `guards.shouldWrapUp`
|
|
503
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`; `WRAPPING_UP` entry emits `task:wrapup`
|
|
495
504
|
- `CONTACT_ENDED` -> stay `CONFERENCING`
|
|
496
505
|
- Guard: none
|
|
497
506
|
- Actions: `updateTaskData`, `requestCleanup`
|
|
@@ -513,6 +522,8 @@ is not treated as departure. `PARTICIPANT_LEAVE` is handled in `HELD`,
|
|
|
513
522
|
|
|
514
523
|
**Valid transitions from `WRAPPING_UP`**:
|
|
515
524
|
|
|
525
|
+
- `TASK_WRAPUP` -> stay `WRAPPING_UP`
|
|
526
|
+
- Guard: if `context.taskData.wrapUpRequired === true`, `updateTaskData` only; else `updateTaskData`, `emitTaskWrapup`
|
|
516
527
|
- `WRAPUP_COMPLETE` -> `COMPLETED`
|
|
517
528
|
- Guard: none
|
|
518
529
|
- Actions: `updateTaskData`
|
|
@@ -874,7 +874,7 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
874
874
|
|
|
875
875
|
- Guard: `guards.shouldWrapUp`
|
|
876
876
|
|
|
877
|
-
- Actions: `updateTaskData`, `markEnded
|
|
877
|
+
- Actions: `updateTaskData`, `markEnded`; `WRAPPING_UP` entry emits `task:wrapup`
|
|
878
878
|
|
|
879
879
|
- `TASK_WRAPUP` -> `TERMINATED`
|
|
880
880
|
|
|
@@ -988,7 +988,7 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
988
988
|
|
|
989
989
|
- Guard: none
|
|
990
990
|
|
|
991
|
-
- Actions: `updateTaskData`, `markEnded
|
|
991
|
+
- Actions: `updateTaskData`, `markEnded`; `WRAPPING_UP` entry emits `task:wrapup`
|
|
992
992
|
|
|
993
993
|
- `PAUSE_RECORDING` / `RESUME_RECORDING` -> Stay `CONNECTED`
|
|
994
994
|
|
|
@@ -996,6 +996,12 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
996
996
|
|
|
997
997
|
- Actions: `updateTaskData`, `setRecordingState`, `emitTaskRecordingPaused` / `emitTaskRecordingResumed`
|
|
998
998
|
|
|
999
|
+
- `EXIT_CONFERENCE_SUCCESS` -> `WRAPPING_UP` or `TERMINATED`
|
|
1000
|
+
|
|
1001
|
+
- Guard: `guards.shouldWrapUp` (wrap-up branch) or default (terminate)
|
|
1002
|
+
|
|
1003
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, or `emitTaskEnd`; `WRAPPING_UP` entry emits `task:wrapup` (same as `CONFERENCING`; Voice `exitConference()` while actor stays `CONNECTED` with conference in task data)
|
|
1004
|
+
|
|
999
1005
|
**Description**: Main call is on hold.
|
|
1000
1006
|
|
|
1001
1007
|
**How this state is reached (incoming transitions)**:
|
|
@@ -1090,7 +1096,13 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
1090
1096
|
|
|
1091
1097
|
- Guard: none
|
|
1092
1098
|
|
|
1093
|
-
- Actions: `updateTaskData`, `markEnded
|
|
1099
|
+
- Actions: `updateTaskData`, `markEnded`; `WRAPPING_UP` entry emits `task:wrapup`
|
|
1100
|
+
|
|
1101
|
+
- `EXIT_CONFERENCE_SUCCESS` -> `WRAPPING_UP` or `TERMINATED`
|
|
1102
|
+
|
|
1103
|
+
- Guard: `guards.shouldWrapUp` or default
|
|
1104
|
+
|
|
1105
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, or `emitTaskEnd`; `WRAPPING_UP` entry emits `task:wrapup` (shared with `CONNECTED` / `CONFERENCING`)
|
|
1094
1106
|
|
|
1095
1107
|
**Description**: Hold request has been sent and is awaiting backend confirmation.
|
|
1096
1108
|
|
|
@@ -1116,6 +1128,12 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
1116
1128
|
|
|
1117
1129
|
- Actions: `updateTaskData`
|
|
1118
1130
|
|
|
1131
|
+
- `TASK_WRAPUP` -> `WRAPPING_UP`
|
|
1132
|
+
|
|
1133
|
+
- Guard: none
|
|
1134
|
+
|
|
1135
|
+
- Actions: `updateTaskData`, `markEnded` (`WRAPPING_UP` entry emits `task:wrapup`)
|
|
1136
|
+
|
|
1119
1137
|
**Description**: Resume/unhold request has been sent and is awaiting backend confirmation.
|
|
1120
1138
|
|
|
1121
1139
|
**How this state is reached (incoming transitions)**:
|
|
@@ -1140,6 +1158,12 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
1140
1158
|
|
|
1141
1159
|
- Actions: none
|
|
1142
1160
|
|
|
1161
|
+
- `TASK_WRAPUP` -> `WRAPPING_UP`
|
|
1162
|
+
|
|
1163
|
+
- Guard: none
|
|
1164
|
+
|
|
1165
|
+
- Actions: `updateTaskData`, `markEnded` (`WRAPPING_UP` entry emits `task:wrapup`)
|
|
1166
|
+
|
|
1143
1167
|
**Description**: Consult request is in-flight.
|
|
1144
1168
|
|
|
1145
1169
|
**How this state is reached (incoming transitions)**:
|
|
@@ -1306,7 +1330,7 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
1306
1330
|
|
|
1307
1331
|
- Guard: `guards.shouldWrapUp`
|
|
1308
1332
|
|
|
1309
|
-
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, `handleTransferConferenceSuccess`, `clearTransferConferenceRequested
|
|
1333
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, `handleTransferConferenceSuccess`, `clearTransferConferenceRequested`; `WRAPPING_UP` entry emits `task:wrapup`
|
|
1310
1334
|
|
|
1311
1335
|
- `TRANSFER_CONFERENCE_SUCCESS` -> `CONFERENCING`
|
|
1312
1336
|
|
|
@@ -1330,7 +1354,7 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
1330
1354
|
|
|
1331
1355
|
- Guard: `guards.didCurrentAgentLeaveMainInteraction && guards.shouldWrapUp`
|
|
1332
1356
|
|
|
1333
|
-
- Actions: `updateTaskData`, `handleParticipantLeft`, `markEnded`, `clearConsultState`, `emitTaskParticipantLeft
|
|
1357
|
+
- Actions: `updateTaskData`, `handleParticipantLeft`, `markEnded`, `clearConsultState`, `emitTaskParticipantLeft` (entry emits `task:wrapup`)
|
|
1334
1358
|
|
|
1335
1359
|
- `PARTICIPANT_LEAVE` -> `TERMINATED`
|
|
1336
1360
|
|
|
@@ -1366,7 +1390,7 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
1366
1390
|
|
|
1367
1391
|
- Guard: none
|
|
1368
1392
|
|
|
1369
|
-
- Actions: `updateTaskData`, `markEnded`, `clearConsultState
|
|
1393
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`; `WRAPPING_UP` entry emits `task:wrapup`
|
|
1370
1394
|
|
|
1371
1395
|
- `MERGE_TO_CONFERENCE` -> `CONF_INITIATING`
|
|
1372
1396
|
|
|
@@ -1458,6 +1482,12 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
1458
1482
|
|
|
1459
1483
|
- Actions: `updateTaskData`, `clearConsultState`, `emitTaskConferenceStarted`
|
|
1460
1484
|
|
|
1485
|
+
- `EXIT_CONFERENCE_SUCCESS` -> `WRAPPING_UP` or `TERMINATED`
|
|
1486
|
+
|
|
1487
|
+
- Guard: `guards.shouldWrapUp` or default
|
|
1488
|
+
|
|
1489
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, or `emitTaskEnd`; `WRAPPING_UP` entry emits `task:wrapup` (shared with `CONNECTED` / `HELD`)
|
|
1490
|
+
|
|
1461
1491
|
- `CONSULT_END` -> stay `CONFERENCING`
|
|
1462
1492
|
|
|
1463
1493
|
- Guard: none
|
|
@@ -1486,7 +1516,7 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
1486
1516
|
|
|
1487
1517
|
- Guard: `guards.shouldWrapUp`
|
|
1488
1518
|
|
|
1489
|
-
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, `handleTransferConferenceSuccess`, `clearTransferConferenceRequested
|
|
1519
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, `handleTransferConferenceSuccess`, `clearTransferConferenceRequested`; `WRAPPING_UP` entry emits `task:wrapup`
|
|
1490
1520
|
|
|
1491
1521
|
- `TRANSFER_CONFERENCE_SUCCESS` -> `CONFERENCING`
|
|
1492
1522
|
|
|
@@ -1510,7 +1540,7 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
1510
1540
|
|
|
1511
1541
|
- Guard: `guards.didCurrentAgentLeaveMainInteraction && guards.shouldWrapUp`
|
|
1512
1542
|
|
|
1513
|
-
- Actions: `updateTaskData`, `handleParticipantLeft`, `markEnded`, `clearConsultState`, `emitTaskParticipantLeft
|
|
1543
|
+
- Actions: `updateTaskData`, `handleParticipantLeft`, `markEnded`, `clearConsultState`, `emitTaskParticipantLeft` (entry emits `task:wrapup`)
|
|
1514
1544
|
|
|
1515
1545
|
- `PARTICIPANT_LEAVE` -> `TERMINATED`
|
|
1516
1546
|
|
|
@@ -1542,7 +1572,7 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
1542
1572
|
|
|
1543
1573
|
- Guard: `guards.shouldWrapUp`
|
|
1544
1574
|
|
|
1545
|
-
- Actions: `updateTaskData`, `markEnded`, `clearConsultState
|
|
1575
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`; `WRAPPING_UP` entry emits `task:wrapup`
|
|
1546
1576
|
|
|
1547
1577
|
- `CONFERENCE_END` -> `CONNECTED`
|
|
1548
1578
|
|
|
@@ -1562,6 +1592,12 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
1562
1592
|
|
|
1563
1593
|
- Actions: `updateTaskData`, `requestCleanup`
|
|
1564
1594
|
|
|
1595
|
+
- `TASK_WRAPUP` -> `WRAPPING_UP`
|
|
1596
|
+
|
|
1597
|
+
- Guard: `guards.shouldWrapUp`
|
|
1598
|
+
|
|
1599
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`; `WRAPPING_UP` entry emits `task:wrapup`
|
|
1600
|
+
|
|
1565
1601
|
**Description**: Post-interaction work (ACW) is in progress.
|
|
1566
1602
|
|
|
1567
1603
|
**How this state is reached (incoming transitions)**:
|
|
@@ -1576,6 +1612,10 @@ It is instantiated by `Task` and receives mapped backend/user events through `se
|
|
|
1576
1612
|
|
|
1577
1613
|
**Valid transitions from `WRAPPING_UP`**:
|
|
1578
1614
|
|
|
1615
|
+
- `TASK_WRAPUP` -> stay `WRAPPING_UP`
|
|
1616
|
+
|
|
1617
|
+
- Guard: if `context.taskData.wrapUpRequired === true` before the event, actions are `updateTaskData` only; otherwise `updateTaskData`, `emitTaskWrapup` (late AgentWrapup after wrap-up was already published)
|
|
1618
|
+
|
|
1579
1619
|
- `WRAPUP_COMPLETE` -> `COMPLETED`
|
|
1580
1620
|
|
|
1581
1621
|
- Guard: none
|
|
@@ -28,7 +28,8 @@ import Task from '../Task';
|
|
|
28
28
|
import LoggerProxy from '../../../logger-proxy';
|
|
29
29
|
import MetricsManager from '../../../metrics/MetricsManager';
|
|
30
30
|
import {METRIC_EVENT_NAMES} from '../../../metrics/constants';
|
|
31
|
-
import {TaskState, TaskEvent, TaskActionArgs} from '../state-machine';
|
|
31
|
+
import {TaskState, TaskEvent, TaskActionArgs, type TaskContext} from '../state-machine';
|
|
32
|
+
import {shouldWrapUpForThisAgent} from '../state-machine/guards';
|
|
32
33
|
import {WrapupData} from '../../config/types';
|
|
33
34
|
import {getConsultMediaResourceId, getIsConferenceInProgress} from '../TaskUtils';
|
|
34
35
|
import AnswerCallOnWebexService from '../../AnswerCallOnWebexService';
|
|
@@ -56,6 +57,24 @@ import {
|
|
|
56
57
|
WxAppAcceptReason,
|
|
57
58
|
} from '../../wxAppDiagnosticLogging';
|
|
58
59
|
|
|
60
|
+
const hasNonemptyPendingWrapUp = (taskData?: TaskData): boolean =>
|
|
61
|
+
Array.isArray(taskData?.agentsPendingWrapUp) && taskData.agentsPendingWrapUp.length > 0;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Same consulted-non-owner rule as TaskManager leave stamp. Used only for the
|
|
65
|
+
* HTTP exitConference self-exit fallback (shouldWrapUpForThisAgent stays unchanged).
|
|
66
|
+
*/
|
|
67
|
+
const isConsultedNonOwner = (taskData: TaskData, fallback?: TaskData): boolean => {
|
|
68
|
+
const isConsulted = taskData.isConsulted ?? fallback?.isConsulted;
|
|
69
|
+
if (isConsulted !== true) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
const owner = taskData.interaction?.owner ?? fallback?.interaction?.owner;
|
|
73
|
+
const selfAgentId = taskData.agentId ?? fallback?.agentId;
|
|
74
|
+
|
|
75
|
+
return Boolean(selfAgentId && owner && owner !== selfAgentId);
|
|
76
|
+
};
|
|
77
|
+
|
|
59
78
|
export default class Voice extends Task implements IVoice {
|
|
60
79
|
private static readonly WXAPP_MUTE_SYNC_RETRY_DELAY_MS = 50;
|
|
61
80
|
private static readonly WXAPP_MUTE_SYNC_MAX_RETRIES = 3;
|
|
@@ -1426,9 +1445,32 @@ export default class Voice extends Task implements IVoice {
|
|
|
1426
1445
|
|
|
1427
1446
|
// Send success event to transition state
|
|
1428
1447
|
if (this.stateMachineService) {
|
|
1448
|
+
const responseTaskData = (response?.data ?? {}) as TaskData;
|
|
1449
|
+
const snapshot = this.stateMachineService.getSnapshot();
|
|
1450
|
+
const wrapUpContext = (snapshot?.context ?? {
|
|
1451
|
+
uiControlConfig: this.uiControlConfig,
|
|
1452
|
+
taskData: this.data,
|
|
1453
|
+
}) as TaskContext;
|
|
1454
|
+
const mergedTaskData = {
|
|
1455
|
+
...this.data,
|
|
1456
|
+
...responseTaskData,
|
|
1457
|
+
};
|
|
1458
|
+
// Pending-list precedence is response-only; do not keep a cached snapshot list.
|
|
1459
|
+
if (!Object.prototype.hasOwnProperty.call(responseTaskData, 'agentsPendingWrapUp')) {
|
|
1460
|
+
delete mergedTaskData.agentsPendingWrapUp;
|
|
1461
|
+
}
|
|
1462
|
+
let wrapUpRequired = shouldWrapUpForThisAgent(wrapUpContext, mergedTaskData);
|
|
1463
|
+
// Confirmed self-exit: empty/absent response pending matches TaskManager leave stamp.
|
|
1464
|
+
if (!wrapUpRequired && !hasNonemptyPendingWrapUp(responseTaskData)) {
|
|
1465
|
+
wrapUpRequired = !isConsultedNonOwner(mergedTaskData, this.data);
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1429
1468
|
this.stateMachineService.send({
|
|
1430
1469
|
type: TaskEvent.EXIT_CONFERENCE_SUCCESS,
|
|
1431
|
-
taskData:
|
|
1470
|
+
taskData: {
|
|
1471
|
+
...responseTaskData,
|
|
1472
|
+
wrapUpRequired,
|
|
1473
|
+
},
|
|
1432
1474
|
});
|
|
1433
1475
|
}
|
|
1434
1476
|
|
|
@@ -13,12 +13,18 @@ import LoggerProxy from '../../../../../src/logger-proxy';
|
|
|
13
13
|
import {createTaskData} from './taskTestUtils';
|
|
14
14
|
|
|
15
15
|
class DummyTask extends Task {
|
|
16
|
-
constructor(contact: any, data: TaskData) {
|
|
17
|
-
super(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
constructor(contact: any, data: TaskData, wrapupData?: any, agentId?: string) {
|
|
17
|
+
super(
|
|
18
|
+
contact,
|
|
19
|
+
data,
|
|
20
|
+
{
|
|
21
|
+
channelType: 'voice',
|
|
22
|
+
isEndTaskEnabled: true,
|
|
23
|
+
isEndConsultEnabled: true,
|
|
24
|
+
},
|
|
25
|
+
wrapupData,
|
|
26
|
+
agentId
|
|
27
|
+
);
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
public accept() {
|
|
@@ -250,6 +256,68 @@ describe('Task (base class)', () => {
|
|
|
250
256
|
expect(emitSpy).not.toHaveBeenCalledWith(TASK_EVENTS.TASK_WRAPUP, task);
|
|
251
257
|
});
|
|
252
258
|
|
|
259
|
+
it('starts auto wrap-up only when wrapUpRequired is true and profile autoWrapup is enabled', () => {
|
|
260
|
+
const wrapUpProps = {
|
|
261
|
+
autoWrapup: true,
|
|
262
|
+
autoWrapupInterval: 25000,
|
|
263
|
+
wrapUpReasonList: [{id: 'code-1', name: 'Default', isDefault: true, isSystem: false}],
|
|
264
|
+
};
|
|
265
|
+
const enabled = new DummyTask(
|
|
266
|
+
dummyContact,
|
|
267
|
+
createTaskData({wrapUpRequired: false}) as TaskData,
|
|
268
|
+
{wrapUpProps},
|
|
269
|
+
'agent-1'
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
expect(enabled.autoWrapup).toBeUndefined();
|
|
273
|
+
try {
|
|
274
|
+
enabled.updateTaskData(createTaskData({wrapUpRequired: true}) as TaskData);
|
|
275
|
+
expect(enabled.autoWrapup).toBeDefined();
|
|
276
|
+
|
|
277
|
+
const disabled = new DummyTask(
|
|
278
|
+
dummyContact,
|
|
279
|
+
createTaskData({wrapUpRequired: false}) as TaskData,
|
|
280
|
+
{wrapUpProps: {...wrapUpProps, autoWrapup: false}},
|
|
281
|
+
'agent-1'
|
|
282
|
+
);
|
|
283
|
+
disabled.updateTaskData(createTaskData({wrapUpRequired: true}) as TaskData);
|
|
284
|
+
expect(disabled.autoWrapup).toBeUndefined();
|
|
285
|
+
} finally {
|
|
286
|
+
enabled.cancelAutoWrapupTimer();
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('calls contact.wrapup when the auto wrap-up timer elapses after wrapUpRequired is stamped', async () => {
|
|
291
|
+
const wrapUpProps = {
|
|
292
|
+
autoWrapup: true,
|
|
293
|
+
autoWrapupInterval: 1000,
|
|
294
|
+
wrapUpReasonList: [{id: 'code-1', name: 'Default', isDefault: true, isSystem: false}],
|
|
295
|
+
};
|
|
296
|
+
const contact = {wrapup: jest.fn().mockResolvedValue({result: 'wrap'})};
|
|
297
|
+
const wrappingTask = new DummyTask(
|
|
298
|
+
contact,
|
|
299
|
+
createTaskData({wrapUpRequired: false}) as TaskData,
|
|
300
|
+
{wrapUpProps},
|
|
301
|
+
'agent-1'
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
jest.useFakeTimers();
|
|
305
|
+
try {
|
|
306
|
+
wrappingTask.updateTaskData(createTaskData({wrapUpRequired: true}) as TaskData);
|
|
307
|
+
expect(wrappingTask.autoWrapup).toBeDefined();
|
|
308
|
+
|
|
309
|
+
jest.advanceTimersByTime(1000);
|
|
310
|
+
|
|
311
|
+
expect(contact.wrapup).toHaveBeenCalledWith({
|
|
312
|
+
interactionId: 'interaction-1',
|
|
313
|
+
data: {wrapUpReason: 'Default', auxCodeId: 'code-1'},
|
|
314
|
+
});
|
|
315
|
+
} finally {
|
|
316
|
+
wrappingTask.cancelAutoWrapupTimer();
|
|
317
|
+
jest.useRealTimers();
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
|
|
253
321
|
it('throws for unsupported voice operations in the base class', async () => {
|
|
254
322
|
const fullData = createTaskData();
|
|
255
323
|
const voiceTask = new DummyTask(dummyContact, fullData);
|