@webex/contact-center 3.12.0-next.9 → 3.12.0-task-refactor.2
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 +570 -535
- 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 +372 -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 +263 -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 +377 -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 +579 -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 +422 -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 +303 -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 +542 -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,1135 @@
|
|
|
1
|
+
# Task State Machine - Architecture
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Technical reference for the complete state machine using XState to drive state transitions and UI control behavior. It orchestrates state transitions, guards, and actions for task lifecycle management.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Architecture Overview
|
|
10
|
+
|
|
11
|
+
The task state machine is built with `xstate` and organized into:
|
|
12
|
+
|
|
13
|
+
- **State graph** (`TaskStateMachine.ts`)
|
|
14
|
+
- **Context mutators** (`actions.ts`)
|
|
15
|
+
- **Guard predicates** (`guards.ts`)
|
|
16
|
+
- **UI control derivation** (`uiControlsComputer.ts`)
|
|
17
|
+
- **Event/context contracts** (`types.ts`)
|
|
18
|
+
|
|
19
|
+
It is instantiated by `Task` and receives mapped backend/user events through `sendStateMachineEvent(...)`.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Runtime Integration
|
|
24
|
+
|
|
25
|
+
`Task` bootstraps and owns the actor lifecycle:
|
|
26
|
+
|
|
27
|
+
1. `createTaskStateMachine(uiControlConfig, {actions: overrides})`
|
|
28
|
+
2. `createActor(machine).start()`
|
|
29
|
+
3. `TaskManager` and task APIs map external signals to `TaskEvent`
|
|
30
|
+
4. Actor transitions update context and execute action overrides
|
|
31
|
+
5. `Task` recomputes UI controls and emits task-level events
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## States
|
|
36
|
+
|
|
37
|
+
### IDLE
|
|
38
|
+
|
|
39
|
+
**Description**: Initial state before a task is offered or restored.
|
|
40
|
+
|
|
41
|
+
**How this state is reached (incoming transitions)**:
|
|
42
|
+
|
|
43
|
+
- Machine start -> `IDLE` (no event, no actions)
|
|
44
|
+
|
|
45
|
+
**Valid transitions from `IDLE` state**:
|
|
46
|
+
|
|
47
|
+
- `TASK_INCOMING` -> `OFFERED`
|
|
48
|
+
- Guard: none
|
|
49
|
+
- Actions: `initializeTask`, `emitTaskIncoming`
|
|
50
|
+
- `HYDRATE` -> `WRAPPING_UP`
|
|
51
|
+
- Guard: `guards.isInteractionTerminated`
|
|
52
|
+
- Actions: `updateTaskData`, `markEnded`, `emitTaskHydrate`
|
|
53
|
+
- `HYDRATE` -> `CONSULTING`
|
|
54
|
+
- Guard: `guards.isInteractionConsulting`
|
|
55
|
+
- Actions: `updateTaskData`, `emitTaskHydrate`
|
|
56
|
+
- `HYDRATE` -> `HELD`
|
|
57
|
+
- Guard: `guards.isInteractionHeld`
|
|
58
|
+
- Actions: `updateTaskData`, `emitTaskHydrate`
|
|
59
|
+
- `HYDRATE` -> `CONNECTED`
|
|
60
|
+
- Guard: `guards.isInteractionConnected`
|
|
61
|
+
- Actions: `updateTaskData`, `emitTaskHydrate`
|
|
62
|
+
- `HYDRATE` -> `CONFERENCING`
|
|
63
|
+
- Guard: `guards.isConferencingByParticipants`
|
|
64
|
+
- Actions: `updateTaskData`, `emitTaskHydrate`
|
|
65
|
+
- `HYDRATE` -> stay `IDLE` (default hydrate branch)
|
|
66
|
+
- Guard: default
|
|
67
|
+
- Actions: `updateTaskData`, `emitTaskHydrate`
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
### OFFERED
|
|
72
|
+
|
|
73
|
+
**Description**: Task has been offered/reserved and is waiting for assignment or termination paths.
|
|
74
|
+
|
|
75
|
+
**How this state is reached (incoming transitions)**:
|
|
76
|
+
|
|
77
|
+
- `IDLE --TASK_INCOMING--> OFFERED`
|
|
78
|
+
- Guard: none
|
|
79
|
+
- Actions: `initializeTask`, `emitTaskIncoming`
|
|
80
|
+
|
|
81
|
+
**Valid transitions from `OFFERED`**:
|
|
82
|
+
|
|
83
|
+
- `TASK_OFFERED` -> Stay `OFFERED`
|
|
84
|
+
- Guard: none
|
|
85
|
+
- Actions: `updateTaskData`, `emitTaskOfferContact`, `requestAutoAnswer`
|
|
86
|
+
- `OFFER_CONSULT` -> Stay `OFFERED`
|
|
87
|
+
- Guard: none
|
|
88
|
+
- Actions: `updateTaskData`, `emitTaskOfferConsult`, `requestAutoAnswer`
|
|
89
|
+
- `ASSIGN` -> `CONNECTED`
|
|
90
|
+
- Guard: none
|
|
91
|
+
- Actions: `updateTaskData`, `emitTaskAssigned`
|
|
92
|
+
- `CONSULTING_ACTIVE` -> `CONSULTING`
|
|
93
|
+
- Guard: none
|
|
94
|
+
- Actions: `updateTaskData`, `setConsultAgentJoined`, `emitTaskConsultAccepted`, `emitTaskConsulting`
|
|
95
|
+
- `TASK_WRAPUP` -> `TERMINATED`
|
|
96
|
+
- Guard: none
|
|
97
|
+
- Actions: `updateTaskData`, `markEnded`, `emitTaskEnd`
|
|
98
|
+
- `RONA` / `ASSIGN_FAILED` / `INVITE_FAILED` / `OUTBOUND_FAILED` -> `TERMINATED`
|
|
99
|
+
- Guard: none
|
|
100
|
+
- Actions: `updateTaskData`, `markEnded`, `emitTaskReject`
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
### CONNECTED
|
|
105
|
+
|
|
106
|
+
**Description**: Agent is connected on the main customer interaction leg.
|
|
107
|
+
|
|
108
|
+
**How this state is reached (incoming transitions)**:
|
|
109
|
+
|
|
110
|
+
- `OFFERED --ASSIGN--> CONNECTED`
|
|
111
|
+
- Guard: none
|
|
112
|
+
- Actions: `updateTaskData`, `emitTaskAssigned`
|
|
113
|
+
- `IDLE --HYDRATE--> CONNECTED`
|
|
114
|
+
- Guard: `guards.isInteractionConnected`
|
|
115
|
+
- Actions: `updateTaskData`, `emitTaskHydrate`
|
|
116
|
+
- `RESUME_INITIATING --UNHOLD_SUCCESS--> CONNECTED`
|
|
117
|
+
- Guard: none
|
|
118
|
+
- Actions: `updateTaskData`, `setHoldState`, `emitTaskResume`
|
|
119
|
+
- `HELD --TRANSFER_SUCCESS--> CONNECTED` (receiver/default branch)
|
|
120
|
+
- Guard: default branch when `guards.shouldWrapUpOrIsInitiator` is false
|
|
121
|
+
- Actions: `updateTaskData`, `clearConsultState`
|
|
122
|
+
- `CONSULTING --ASSIGN--> CONNECTED`
|
|
123
|
+
- Guard: none
|
|
124
|
+
- Actions: `updateTaskData`, `emitTaskAssigned`
|
|
125
|
+
|
|
126
|
+
**Valid transitions from `CONNECTED`**:
|
|
127
|
+
|
|
128
|
+
- `ASSIGN` -> `CONNECTED` (self-transition)
|
|
129
|
+
- Guard: none
|
|
130
|
+
- Actions: `updateTaskData`, `emitTaskAssigned`
|
|
131
|
+
- `HOLD_INITIATED` -> `HOLD_INITIATING`
|
|
132
|
+
- Guard: none
|
|
133
|
+
- Actions: none
|
|
134
|
+
- `CONSULT` -> `CONSULT_INITIATING`
|
|
135
|
+
- Guard: none
|
|
136
|
+
- Actions: `setConsultInitiator`, `setConsultDestination`
|
|
137
|
+
- `CONSULTING_ACTIVE` -> `CONSULTING`
|
|
138
|
+
- Guard: none
|
|
139
|
+
- Actions: `updateTaskData`, `setConsultAgentJoined`, `emitTaskConsultAccepted`, `emitTaskConsulting`
|
|
140
|
+
- `TRANSFER_SUCCESS` -> `WRAPPING_UP`
|
|
141
|
+
- Guard: `guards.shouldWrapUpOrIsInitiator`
|
|
142
|
+
- Actions: `updateTaskData`, `markEnded`, `emitTaskWrapup`
|
|
143
|
+
- `TRANSFER_SUCCESS` -> Stay `CONNECTED` (receiver/default branch)
|
|
144
|
+
- Guard: default
|
|
145
|
+
- Actions: `updateTaskData`, `clearConsultState`
|
|
146
|
+
- `TRANSFER_FAILED` -> Stay `CONNECTED`
|
|
147
|
+
- Guard: none
|
|
148
|
+
- Actions: `updateTaskData`
|
|
149
|
+
- `CONTACT_ENDED` -> `CONFERENCING`
|
|
150
|
+
- Guard: `guards.conferenceInProgressFromEvent`
|
|
151
|
+
- Actions: `updateTaskData`, `emitTaskConferenceStarted`, `requestCleanup`
|
|
152
|
+
- `CONTACT_ENDED` -> `WRAPPING_UP`
|
|
153
|
+
- Guard: `guards.shouldWrapUp`
|
|
154
|
+
- Actions: `updateTaskData`, `markEnded`, `emitTaskWrapup`, `requestCleanup`
|
|
155
|
+
- `CONTACT_ENDED` -> `TERMINATED` (default branch)
|
|
156
|
+
- Guard: default
|
|
157
|
+
- Actions: `updateTaskData`, `markEnded`, `emitTaskEnd`
|
|
158
|
+
- `TASK_WRAPUP` -> `WRAPPING_UP`
|
|
159
|
+
- Guard: none
|
|
160
|
+
- Actions: `updateTaskData`, `markEnded`, `emitTaskWrapup`
|
|
161
|
+
- `PAUSE_RECORDING` / `RESUME_RECORDING` -> Stay `CONNECTED`
|
|
162
|
+
- Guard: none
|
|
163
|
+
- Actions: `updateTaskData`, `setRecordingState`, `emitTaskRecordingPaused` / `emitTaskRecordingResumed`
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
### HELD
|
|
168
|
+
|
|
169
|
+
**Description**: Main call is on hold.
|
|
170
|
+
|
|
171
|
+
**How this state is reached (incoming transitions)**:
|
|
172
|
+
|
|
173
|
+
- `IDLE --HYDRATE--> HELD`
|
|
174
|
+
- Guard: `guards.isInteractionHeld`
|
|
175
|
+
- Actions: `updateTaskData`, `emitTaskHydrate`
|
|
176
|
+
- `HOLD_INITIATING --HOLD_SUCCESS--> HELD`
|
|
177
|
+
- Guard: none
|
|
178
|
+
- Actions: `updateTaskData`, `setHoldState`, `emitTaskHold`
|
|
179
|
+
- `RESUME_INITIATING --UNHOLD_FAILED--> HELD`
|
|
180
|
+
- Guard: none
|
|
181
|
+
- Actions: none
|
|
182
|
+
- `CONSULTING --CONSULT_END--> HELD`
|
|
183
|
+
- Guard: inline `context.consultInitiator === true`
|
|
184
|
+
- Actions: `updateTaskData`, `clearConsultState`, `emitTaskConsultEnd`
|
|
185
|
+
- `CONSULT_INITIATING --CONSULT_FAILED--> HELD`
|
|
186
|
+
- Guard: `guards.isPrimaryMediaOnHold`
|
|
187
|
+
- Actions: `updateTaskData`, `handleConsultFailed`
|
|
188
|
+
- `CONSULT_INITIATING --CTQ_CANCEL--> HELD`
|
|
189
|
+
- Guard: `guards.isPrimaryMediaOnHold`
|
|
190
|
+
- Actions: `updateTaskData`, `clearConsultState`
|
|
191
|
+
|
|
192
|
+
**Valid transitions from `HELD`**:
|
|
193
|
+
|
|
194
|
+
- `UNHOLD_INITIATED` -> `RESUME_INITIATING`
|
|
195
|
+
- Guard: none
|
|
196
|
+
- Actions: none
|
|
197
|
+
- `CONSULT` -> `CONSULT_INITIATING`
|
|
198
|
+
- Guard: none
|
|
199
|
+
- Actions: `setConsultInitiator`, `setConsultDestination`
|
|
200
|
+
- `TRANSFER_SUCCESS` -> `WRAPPING_UP`
|
|
201
|
+
- Guard: `guards.shouldWrapUpOrIsInitiator`
|
|
202
|
+
- Actions: `updateTaskData`, `markEnded`, `emitTaskWrapup`
|
|
203
|
+
- `TRANSFER_SUCCESS` -> `CONNECTED` (receiver/default branch)
|
|
204
|
+
- Guard: default
|
|
205
|
+
- Actions: `updateTaskData`, `clearConsultState`
|
|
206
|
+
- `TRANSFER_FAILED` -> stay `HELD`
|
|
207
|
+
- Guard: none
|
|
208
|
+
- Actions: `updateTaskData`
|
|
209
|
+
- `CONTACT_ENDED` -> `CONFERENCING`
|
|
210
|
+
- Guard: `guards.conferenceInProgressFromEvent`
|
|
211
|
+
- Actions: `updateTaskData`, `emitTaskConferenceStarted`, `requestCleanup`
|
|
212
|
+
- `CONTACT_ENDED` -> `WRAPPING_UP`
|
|
213
|
+
- Guard: `guards.shouldWrapUp`
|
|
214
|
+
- Actions: `updateTaskData`, `markEnded`, `emitTaskWrapup`, `requestCleanup`
|
|
215
|
+
- `CONTACT_ENDED` -> `TERMINATED` (default branch)
|
|
216
|
+
- Guard: default
|
|
217
|
+
- Actions: `updateTaskData`, `markEnded`, `emitTaskEnd`
|
|
218
|
+
- `TASK_WRAPUP` -> `WRAPPING_UP`
|
|
219
|
+
- Guard: none
|
|
220
|
+
- Actions: `updateTaskData`, `markEnded`, `emitTaskWrapup`
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
### HOLD_INITIATING
|
|
225
|
+
|
|
226
|
+
**Description**: Hold request has been sent and is awaiting backend confirmation.
|
|
227
|
+
|
|
228
|
+
**How this state is reached (incoming transitions)**:
|
|
229
|
+
|
|
230
|
+
- `CONNECTED --HOLD_INITIATED--> HOLD_INITIATING`
|
|
231
|
+
- Guard: none
|
|
232
|
+
- Actions: none
|
|
233
|
+
|
|
234
|
+
**Valid transitions from `HOLD_INITIATING`**:
|
|
235
|
+
|
|
236
|
+
- `HOLD_SUCCESS` -> `HELD`
|
|
237
|
+
- Guard: none
|
|
238
|
+
- Actions: `updateTaskData`, `setHoldState`, `emitTaskHold`
|
|
239
|
+
- `HOLD_FAILED` -> `CONNECTED`
|
|
240
|
+
- Guard: none
|
|
241
|
+
- Actions: `updateTaskData`
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
### RESUME_INITIATING
|
|
246
|
+
|
|
247
|
+
**Description**: Resume/unhold request has been sent and is awaiting backend confirmation.
|
|
248
|
+
|
|
249
|
+
**How this state is reached (incoming transitions)**:
|
|
250
|
+
|
|
251
|
+
- `HELD --UNHOLD_INITIATED--> RESUME_INITIATING`
|
|
252
|
+
- Guard: none
|
|
253
|
+
- Actions: none
|
|
254
|
+
|
|
255
|
+
**Valid transitions from `RESUME_INITIATING`**:
|
|
256
|
+
|
|
257
|
+
- `UNHOLD_SUCCESS` -> `CONNECTED`
|
|
258
|
+
- Guard: none
|
|
259
|
+
- Actions: `updateTaskData`, `setHoldState`, `emitTaskResume`
|
|
260
|
+
- `UNHOLD_FAILED` -> `HELD`
|
|
261
|
+
- Guard: none
|
|
262
|
+
- Actions: none
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
### CONSULT_INITIATING
|
|
267
|
+
|
|
268
|
+
**Description**: Consult request is in-flight.
|
|
269
|
+
|
|
270
|
+
**How this state is reached (incoming transitions)**:
|
|
271
|
+
|
|
272
|
+
- `CONNECTED --CONSULT--> CONSULT_INITIATING`
|
|
273
|
+
- Guard: none
|
|
274
|
+
- Actions: `setConsultInitiator`, `setConsultDestination`
|
|
275
|
+
- `HELD --CONSULT--> CONSULT_INITIATING`
|
|
276
|
+
- Guard: none
|
|
277
|
+
- Actions: `setConsultInitiator`, `setConsultDestination`
|
|
278
|
+
- `CONFERENCING --CONSULT--> CONSULT_INITIATING`
|
|
279
|
+
- Guard: none
|
|
280
|
+
- Actions: `setConsultInitiator`, `setConsultDestination`, `setConsultFromConference`
|
|
281
|
+
|
|
282
|
+
**Valid transitions from `CONSULT_INITIATING`**:
|
|
283
|
+
|
|
284
|
+
- `CONSULT_SUCCESS` -> `CONSULTING`
|
|
285
|
+
- Guard: none
|
|
286
|
+
- Actions: `updateTaskData`, `setConsultInitiator`
|
|
287
|
+
- `CONSULT_FAILED` -> `CONFERENCING`
|
|
288
|
+
- Guard: inline `context.consultFromConference === true`
|
|
289
|
+
- Actions: `updateTaskData`, `handleConsultFailed`
|
|
290
|
+
- `CONSULT_FAILED` -> `HELD`
|
|
291
|
+
- Guard: `guards.isPrimaryMediaOnHold`
|
|
292
|
+
- Actions: `updateTaskData`, `handleConsultFailed`
|
|
293
|
+
- `CONSULT_FAILED` -> `CONNECTED` (default branch)
|
|
294
|
+
- Guard: default
|
|
295
|
+
- Actions: `updateTaskData`, `handleConsultFailed`
|
|
296
|
+
- `CTQ_CANCEL` -> `HELD`
|
|
297
|
+
- Guard: `guards.isPrimaryMediaOnHold`
|
|
298
|
+
- Actions: `updateTaskData`, `clearConsultState`
|
|
299
|
+
- `CTQ_CANCEL` -> `CONNECTED` (default branch)
|
|
300
|
+
- Guard: default
|
|
301
|
+
- Actions: `updateTaskData`, `clearConsultState`
|
|
302
|
+
- `HOLD_SUCCESS` -> stay `CONSULT_INITIATING`
|
|
303
|
+
- Guard: none
|
|
304
|
+
- Actions: `updateTaskData`
|
|
305
|
+
- `HOLD_FAILED` -> `CONNECTED`
|
|
306
|
+
- Guard: none
|
|
307
|
+
- Actions: `updateTaskData`, `handleConsultFailed`
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
### CONSULTING
|
|
312
|
+
|
|
313
|
+
**Description**: Agent is in active consult leg.
|
|
314
|
+
|
|
315
|
+
**How this state is reached (incoming transitions)**:
|
|
316
|
+
|
|
317
|
+
- `IDLE --HYDRATE--> CONSULTING`
|
|
318
|
+
- Guard: `guards.isInteractionConsulting`
|
|
319
|
+
- Actions: `updateTaskData`, `emitTaskHydrate`
|
|
320
|
+
- `OFFERED --CONSULTING_ACTIVE--> CONSULTING`
|
|
321
|
+
- Guard: none
|
|
322
|
+
- Actions: `updateTaskData`, `setConsultAgentJoined`, `emitTaskConsultAccepted`, `emitTaskConsulting`
|
|
323
|
+
- `CONSULT_INITIATING --CONSULT_SUCCESS--> CONSULTING`
|
|
324
|
+
- Guard: none
|
|
325
|
+
- Actions: `updateTaskData`, `setConsultInitiator`
|
|
326
|
+
- `CONF_INITIATING --CONFERENCE_FAILED--> CONSULTING`
|
|
327
|
+
- Guard: none
|
|
328
|
+
- Actions: none
|
|
329
|
+
|
|
330
|
+
**Valid transitions from `CONSULTING`**:
|
|
331
|
+
|
|
332
|
+
- `CONSULTING_ACTIVE` -> stay `CONSULTING`
|
|
333
|
+
- Guard: none
|
|
334
|
+
- Actions: `updateTaskData`, `setConsultAgentJoined`, `emitTaskConsulting`
|
|
335
|
+
- `CONSULT_END` -> `CONFERENCING`
|
|
336
|
+
- Guard: inline `context.consultInitiator === true && context.consultFromConference === true`
|
|
337
|
+
- Actions: `updateTaskData`, `clearConsultState`, `emitTaskConsultEnd`
|
|
338
|
+
- `CONSULT_END` -> `HELD`
|
|
339
|
+
- Guard: inline `context.consultInitiator === true`
|
|
340
|
+
- Actions: `updateTaskData`, `clearConsultState`, `emitTaskConsultEnd`
|
|
341
|
+
- `CONSULT_END` -> `TERMINATED` (default branch)
|
|
342
|
+
- Guard: default
|
|
343
|
+
- Actions: `updateTaskData`
|
|
344
|
+
- `HOLD_SUCCESS` -> stay `CONSULTING`
|
|
345
|
+
- Guard: none
|
|
346
|
+
- Actions: `updateTaskData`, `setHoldState`, `setConsultCallHeld`
|
|
347
|
+
- `UNHOLD_SUCCESS` -> stay `CONSULTING`
|
|
348
|
+
- Guard: none
|
|
349
|
+
- Actions: `updateTaskData`, `setHoldState`, `clearConsultCallHeld`
|
|
350
|
+
- `TRANSFER_SUCCESS` -> `WRAPPING_UP`
|
|
351
|
+
- Guard: `guards.shouldWrapUpOrIsInitiator`
|
|
352
|
+
- Actions: `updateTaskData`, `markEnded`, `emitTaskWrapup`
|
|
353
|
+
- `TRANSFER_SUCCESS` -> `CONNECTED` (receiver/default branch)
|
|
354
|
+
- Guard: default
|
|
355
|
+
- Actions: `updateTaskData`, `clearConsultState`
|
|
356
|
+
- `TRANSFER_FAILED` -> stay `CONSULTING`
|
|
357
|
+
- Guard: none
|
|
358
|
+
- Actions: `updateTaskData`
|
|
359
|
+
- `TRANSFER_CONFERENCE` -> stay `CONSULTING`
|
|
360
|
+
- Guard: none
|
|
361
|
+
- Actions: `setTransferConferenceRequested`, `emitTaskTransferConference`
|
|
362
|
+
- `TRANSFER_CONFERENCE_SUCCESS` -> stay `CONSULTING`
|
|
363
|
+
- Guard: inline `context.transferConferenceRequested !== true`
|
|
364
|
+
- Actions: `updateTaskData`, `handleTransferConferenceSuccess`, `clearTransferConferenceRequested`
|
|
365
|
+
- `TRANSFER_CONFERENCE_SUCCESS` -> `WRAPPING_UP`
|
|
366
|
+
- Guard: `guards.shouldWrapUp`
|
|
367
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, `handleTransferConferenceSuccess`, `clearTransferConferenceRequested`, `emitTaskWrapup`
|
|
368
|
+
- `TRANSFER_CONFERENCE_SUCCESS` -> `CONFERENCING`
|
|
369
|
+
- Guard: inline `!context.consultInitiator`
|
|
370
|
+
- Actions: `updateTaskData`, `clearConsultState`, `handleTransferConferenceSuccess`, `clearTransferConferenceRequested`
|
|
371
|
+
- `TRANSFER_CONFERENCE_SUCCESS` -> `TERMINATED` (default branch)
|
|
372
|
+
- Guard: default
|
|
373
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, `handleTransferConferenceSuccess`, `clearTransferConferenceRequested`, `emitTaskEnd`
|
|
374
|
+
- `TRANSFER_CONFERENCE_FAILED` -> stay `CONSULTING`
|
|
375
|
+
- Guard: none
|
|
376
|
+
- Actions: `clearTransferConferenceRequested`
|
|
377
|
+
- `ASSIGN` -> `CONNECTED`
|
|
378
|
+
- Guard: none
|
|
379
|
+
- Actions: `updateTaskData`, `emitTaskAssigned`
|
|
380
|
+
- `CONTACT_ENDED` -> `WRAPPING_UP`
|
|
381
|
+
- Guard: none
|
|
382
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, `emitTaskWrapup`, `requestCleanup`
|
|
383
|
+
- `TASK_WRAPUP` -> `WRAPPING_UP`
|
|
384
|
+
- Guard: none
|
|
385
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, `emitTaskWrapup`
|
|
386
|
+
- `MERGE_TO_CONFERENCE` -> `CONF_INITIATING`
|
|
387
|
+
- Guard: none
|
|
388
|
+
- Actions: none
|
|
389
|
+
- `CONFERENCE_START` -> `CONFERENCING`
|
|
390
|
+
- Guard: none
|
|
391
|
+
- Actions: `handleConferenceStarted`, `clearConsultState`
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
### CONF_INITIATING
|
|
396
|
+
|
|
397
|
+
**Description**: Conference merge is being established.
|
|
398
|
+
|
|
399
|
+
**How this state is reached (incoming transitions)**:
|
|
400
|
+
|
|
401
|
+
- `CONSULTING --MERGE_TO_CONFERENCE--> CONF_INITIATING`
|
|
402
|
+
- Guard: none
|
|
403
|
+
- Actions: none
|
|
404
|
+
|
|
405
|
+
**Valid transitions from `CONF_INITIATING`**:
|
|
406
|
+
|
|
407
|
+
- `CONFERENCE_START` -> `CONFERENCING`
|
|
408
|
+
- Guard: none
|
|
409
|
+
- Actions: `handleConferenceStarted`
|
|
410
|
+
- `CONFERENCE_FAILED` -> `CONSULTING`
|
|
411
|
+
- Guard: none
|
|
412
|
+
- Actions: none
|
|
413
|
+
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
### CONFERENCING
|
|
417
|
+
|
|
418
|
+
**Description**: Active conference call state.
|
|
419
|
+
|
|
420
|
+
**How this state is reached (incoming transitions)**:
|
|
421
|
+
|
|
422
|
+
- `IDLE --HYDRATE--> CONFERENCING`
|
|
423
|
+
- Guard: `guards.isConferencingByParticipants`
|
|
424
|
+
- Actions: `updateTaskData`, `emitTaskHydrate`
|
|
425
|
+
- `CONNECTED --CONTACT_ENDED--> CONFERENCING`
|
|
426
|
+
- Guard: `guards.conferenceInProgressFromEvent`
|
|
427
|
+
- Actions: `updateTaskData`, `emitTaskConferenceStarted`, `requestCleanup`
|
|
428
|
+
- `HELD --CONTACT_ENDED--> CONFERENCING`
|
|
429
|
+
- Guard: `guards.conferenceInProgressFromEvent`
|
|
430
|
+
- Actions: `updateTaskData`, `emitTaskConferenceStarted`, `requestCleanup`
|
|
431
|
+
- `CONSULTING --CONFERENCE_START--> CONFERENCING`
|
|
432
|
+
- Guard: none
|
|
433
|
+
- Actions: `handleConferenceStarted`, `clearConsultState`
|
|
434
|
+
- `CONF_INITIATING --CONFERENCE_START--> CONFERENCING`
|
|
435
|
+
- Guard: none
|
|
436
|
+
- Actions: `handleConferenceStarted`
|
|
437
|
+
- `CONSULTING --TRANSFER_CONFERENCE_SUCCESS--> CONFERENCING`
|
|
438
|
+
- Guard: inline `!context.consultInitiator`
|
|
439
|
+
- Actions: `updateTaskData`, `clearConsultState`, `handleTransferConferenceSuccess`, `clearTransferConferenceRequested`
|
|
440
|
+
|
|
441
|
+
**Valid transitions from `CONFERENCING`**:
|
|
442
|
+
|
|
443
|
+
- `CONSULT` -> `CONSULT_INITIATING`
|
|
444
|
+
- Guard: none
|
|
445
|
+
- Actions: `setConsultInitiator`, `setConsultDestination`, `setConsultFromConference`
|
|
446
|
+
- `CONFERENCE_START` -> stay `CONFERENCING`
|
|
447
|
+
- Guard: none
|
|
448
|
+
- Actions: `updateTaskData`, `clearConsultState`, `emitTaskConferenceStarted`
|
|
449
|
+
- `CONSULT_END` -> stay `CONFERENCING`
|
|
450
|
+
- Guard: none
|
|
451
|
+
- Actions: `updateTaskData`, `clearConsultState`
|
|
452
|
+
- `HOLD_SUCCESS` / `UNHOLD_SUCCESS` -> stay `CONFERENCING`
|
|
453
|
+
- Guard: none
|
|
454
|
+
- Actions: `updateTaskData`, `setHoldState`, `emitTaskHold` / `emitTaskResume`
|
|
455
|
+
- `TRANSFER_CONFERENCE` -> stay `CONFERENCING`
|
|
456
|
+
- Guard: none
|
|
457
|
+
- Actions: `setTransferConferenceRequested`, `emitTaskTransferConference`
|
|
458
|
+
- `TRANSFER_CONFERENCE_SUCCESS` -> stay `CONFERENCING`
|
|
459
|
+
- Guard: inline `context.transferConferenceRequested !== true`
|
|
460
|
+
- Actions: `updateTaskData`, `handleTransferConferenceSuccess`, `clearTransferConferenceRequested`
|
|
461
|
+
- `TRANSFER_CONFERENCE_FAILED` -> stay `CONFERENCING`
|
|
462
|
+
- Guard: none
|
|
463
|
+
- Actions: `clearTransferConferenceRequested`
|
|
464
|
+
- `PARTICIPANT_LEAVE` -> `WRAPPING_UP`
|
|
465
|
+
- Guard: `guards.didCurrentAgentLeaveConference && guards.shouldWrapUp`
|
|
466
|
+
- Actions: `updateTaskData`, `handleParticipantLeft`, `markEnded`, `clearConsultState`, `emitTaskParticipantLeft`, `emitTaskWrapup`
|
|
467
|
+
- `PARTICIPANT_LEAVE` -> `TERMINATED`
|
|
468
|
+
- Guard: `guards.didCurrentAgentLeaveConference`
|
|
469
|
+
- Actions: `updateTaskData`, `handleParticipantLeft`, `markEnded`, `clearConsultState`, `emitTaskParticipantLeft`, `emitTaskEnd`
|
|
470
|
+
- `PARTICIPANT_LEAVE` -> `CONNECTED`
|
|
471
|
+
- Guard: `guards.shouldDowngradeConferenceToConnected`
|
|
472
|
+
- Actions: `updateTaskData`, `handleParticipantLeft`, `clearConsultState`, `emitTaskParticipantLeft`, `emitTaskConferenceEnded`
|
|
473
|
+
- `PARTICIPANT_LEAVE` -> stay `CONFERENCING` (default)
|
|
474
|
+
- Guard: default
|
|
475
|
+
- Actions: `updateTaskData`, `handleParticipantLeft`, `emitTaskParticipantLeft`
|
|
476
|
+
- `CONFERENCE_END` -> `WRAPPING_UP`
|
|
477
|
+
- Guard: `guards.shouldWrapUp`
|
|
478
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, `emitTaskWrapup`
|
|
479
|
+
- `CONFERENCE_END` -> `CONNECTED`
|
|
480
|
+
- Guard: inline `!context.exitingConference && customerInCall`
|
|
481
|
+
- Actions: `updateTaskData`, `clearConsultState`, `emitTaskConferenceEnded`
|
|
482
|
+
- `CONFERENCE_END` -> `TERMINATED` (default branch)
|
|
483
|
+
- Guard: default
|
|
484
|
+
- Actions: `updateTaskData`, `markEnded`, `clearConsultState`, `emitTaskEnd`
|
|
485
|
+
- `CONTACT_ENDED` -> stay `CONFERENCING`
|
|
486
|
+
- Guard: none
|
|
487
|
+
- Actions: `updateTaskData`, `requestCleanup`
|
|
488
|
+
|
|
489
|
+
---
|
|
490
|
+
|
|
491
|
+
### WRAPPING_UP
|
|
492
|
+
|
|
493
|
+
**Description**: Post-interaction work (ACW) is in progress.
|
|
494
|
+
|
|
495
|
+
**How this state is reached (incoming transitions)**:
|
|
496
|
+
|
|
497
|
+
- Reached from `CONNECTED`, `HELD`, `CONSULTING`, or `CONFERENCING` via `CONTACT_ENDED`, `TASK_WRAPUP`, `TRANSFER_SUCCESS`, `TRANSFER_CONFERENCE_SUCCESS`, `PARTICIPANT_LEAVE`, or `CONFERENCE_END` branches
|
|
498
|
+
- Entry always emits wrapup event after transition
|
|
499
|
+
|
|
500
|
+
**Entry Actions**:
|
|
501
|
+
|
|
502
|
+
- `emitTaskWrapup`
|
|
503
|
+
|
|
504
|
+
**Valid transitions from `WRAPPING_UP`**:
|
|
505
|
+
|
|
506
|
+
- `WRAPUP_COMPLETE` -> `COMPLETED`
|
|
507
|
+
- Guard: none
|
|
508
|
+
- Actions: `updateTaskData`
|
|
509
|
+
|
|
510
|
+
**Guards**: None
|
|
511
|
+
|
|
512
|
+
### COMPLETED
|
|
513
|
+
|
|
514
|
+
**Description**: Final wrapped-up terminal state.
|
|
515
|
+
|
|
516
|
+
**Entry Actions**:
|
|
517
|
+
|
|
518
|
+
- `emitTaskWrappedup`
|
|
519
|
+
- `cleanupResources`
|
|
520
|
+
|
|
521
|
+
**How this state is reached (incoming transitions)**:
|
|
522
|
+
|
|
523
|
+
- `WRAPPING_UP --WRAPUP_COMPLETE--> COMPLETED`
|
|
524
|
+
- Guard: none
|
|
525
|
+
- Actions: `updateTaskData`
|
|
526
|
+
|
|
527
|
+
**Valid transitions from `COMPLETED`**: None (final state)
|
|
528
|
+
|
|
529
|
+
**Guards**: None
|
|
530
|
+
|
|
531
|
+
---
|
|
532
|
+
|
|
533
|
+
### TERMINATED
|
|
534
|
+
|
|
535
|
+
**Description**: Final terminated terminal state.
|
|
536
|
+
|
|
537
|
+
**Entry Actions**:
|
|
538
|
+
|
|
539
|
+
- `cleanupResources`
|
|
540
|
+
|
|
541
|
+
**How this state is reached (incoming transitions)**:
|
|
542
|
+
|
|
543
|
+
- Reached from `OFFERED`, `CONNECTED`, `HELD`, `CONSULTING`, and `CONFERENCING` via terminating branches (`TASK_WRAPUP`, failure paths, and default end-of-contact/conference branches)
|
|
544
|
+
|
|
545
|
+
**Valid transitions from `TERMINATED`**: None (final state)
|
|
546
|
+
|
|
547
|
+
**Guards**: None
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## Events
|
|
552
|
+
|
|
553
|
+
Event names below are from `TaskEvent` in `constants.ts`.
|
|
554
|
+
|
|
555
|
+
### Core lifecycle and sync
|
|
556
|
+
|
|
557
|
+
- `TASK_INCOMING`, `TASK_OFFERED`, `HYDRATE`
|
|
558
|
+
- `CONTACT_UPDATED`, `CONTACT_OWNER_CHANGED`
|
|
559
|
+
- `ASSIGN`, `CONTACT_ENDED`, `TASK_WRAPUP`, `WRAPUP_COMPLETE`
|
|
560
|
+
|
|
561
|
+
### Hold/resume
|
|
562
|
+
|
|
563
|
+
- `HOLD_INITIATED`, `HOLD_SUCCESS`, `HOLD_FAILED`
|
|
564
|
+
- `UNHOLD_INITIATED`, `UNHOLD_SUCCESS`, `UNHOLD_FAILED`
|
|
565
|
+
|
|
566
|
+
### Consult
|
|
567
|
+
|
|
568
|
+
- `OFFER_CONSULT`, `CONSULT`, `CONSULT_SUCCESS`, `CONSULT_CREATED`
|
|
569
|
+
- `CONSULTING_ACTIVE`, `CONSULT_END`, `CONSULT_FAILED`
|
|
570
|
+
- `CTQ_CANCEL`, `CTQ_CANCEL_FAILED`
|
|
571
|
+
|
|
572
|
+
### Conference and conference-transfer
|
|
573
|
+
|
|
574
|
+
- `MERGE_TO_CONFERENCE`, `CONFERENCE_START`, `CONFERENCE_FAILED`, `CONFERENCE_END`
|
|
575
|
+
- `PARTICIPANT_LEAVE`
|
|
576
|
+
- `TRANSFER_CONFERENCE`, `TRANSFER_CONFERENCE_SUCCESS`, `TRANSFER_CONFERENCE_FAILED`
|
|
577
|
+
- `EXIT_CONFERENCE`, `EXIT_CONFERENCE_SUCCESS`, `EXIT_CONFERENCE_FAILED`
|
|
578
|
+
|
|
579
|
+
### Transfer
|
|
580
|
+
|
|
581
|
+
- `TRANSFER_SUCCESS`, `TRANSFER_FAILED`
|
|
582
|
+
|
|
583
|
+
### Recording
|
|
584
|
+
|
|
585
|
+
- `RECORDING_STARTED`, `PAUSE_RECORDING`, `RESUME_RECORDING`
|
|
586
|
+
|
|
587
|
+
### Failure/end events
|
|
588
|
+
|
|
589
|
+
- `RONA`, `INVITE_FAILED`, `ASSIGN_FAILED`, `OUTBOUND_FAILED`
|
|
590
|
+
|
|
591
|
+
---
|
|
592
|
+
|
|
593
|
+
## Single Transition Flow
|
|
594
|
+
|
|
595
|
+
The flow below shows a single transition in the requested form:
|
|
596
|
+
|
|
597
|
+
```mermaid
|
|
598
|
+
flowchart LR
|
|
599
|
+
A[User Action/CC Event Mapping] --> B[State Machine Event Trigger]
|
|
600
|
+
B --> C{Check against Current State: Valid Transition?}
|
|
601
|
+
C -- No --> X[Ignore/No-op]
|
|
602
|
+
C -- Yes --> D[StateMachine evaluates guards]
|
|
603
|
+
D -- No --> Y[Blocked by Guard]
|
|
604
|
+
D -- Yes --> E[Execute Associated Actions]
|
|
605
|
+
E --> F[Context updated]
|
|
606
|
+
F --> G[UI Controls Recomputed]
|
|
607
|
+
G --> H[Transition to Target State]
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
### Example: Hold Flow (Concrete)
|
|
611
|
+
|
|
612
|
+
```mermaid
|
|
613
|
+
flowchart LR
|
|
614
|
+
A[User invoked hold API] --> B[Event Trigger: HOLD_INITIATED]
|
|
615
|
+
B --> C{State = CONNECTED?}
|
|
616
|
+
C -- No --> X[Ignore/No-op]
|
|
617
|
+
C -- Yes --> D[Guards: none]
|
|
618
|
+
D -- Yes --> E[Actions: setHoldInitiated + updateTaskData]
|
|
619
|
+
E --> F[Context updated]
|
|
620
|
+
F --> G[UI controls recomputed]
|
|
621
|
+
G --> H[Transition: CONNECTED -> HOLD_INITIATING]
|
|
622
|
+
H --> I[CC Event: AGENT_CONTACT_HELD]
|
|
623
|
+
I --> J[Mapped: HOLD_SUCCESS]
|
|
624
|
+
J --> K{State = HOLD_INITIATING?}
|
|
625
|
+
K -- No --> X
|
|
626
|
+
K -- Yes --> L[Guards: none]
|
|
627
|
+
L -- Yes --> M[Actions: setHoldSuccess + updateTaskData]
|
|
628
|
+
M --> N[Context updated]
|
|
629
|
+
N --> O[UI controls recomputed]
|
|
630
|
+
O --> P[Transition: HOLD_INITIATING -> HELD]
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
---
|
|
634
|
+
|
|
635
|
+
## State Pattern (via XState)
|
|
636
|
+
|
|
637
|
+
### Purpose
|
|
638
|
+
|
|
639
|
+
Manage complex task lifecycle with clear states, transitions, guards, and actions.
|
|
640
|
+
|
|
641
|
+
### Implementation
|
|
642
|
+
|
|
643
|
+
```typescript
|
|
644
|
+
// Task.ts
|
|
645
|
+
export default abstract class Task extends EventEmitter {
|
|
646
|
+
public stateMachineService?: ActorRefFrom<TaskStateMachine>;
|
|
647
|
+
|
|
648
|
+
private initializeStateMachine(): void {
|
|
649
|
+
const machine: TaskStateMachine = createTaskStateMachine(this.uiControlConfig, {
|
|
650
|
+
actions: this.getStateMachineActionOverrides(),
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
this.stateMachineService = createActor(machine);
|
|
654
|
+
|
|
655
|
+
// Subscribe to state changes
|
|
656
|
+
this.stateMachineService.subscribe((snapshot) => {
|
|
657
|
+
const currentState = snapshot.value as TaskState;
|
|
658
|
+
this.state = snapshot;
|
|
659
|
+
this.updateUiControls(previousState !== currentState);
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
this.stateMachineService.start();
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
public sendStateMachineEvent(event: TaskEventPayload): void {
|
|
666
|
+
this.stateMachineService?.send(event);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
```
|
|
670
|
+
|
|
671
|
+
### State Machine Architecture
|
|
672
|
+
|
|
673
|
+
```typescript
|
|
674
|
+
// state-machine/TaskStateMachine.ts
|
|
675
|
+
export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
|
|
676
|
+
return {
|
|
677
|
+
id: 'taskStateMachine',
|
|
678
|
+
initial: TaskState.IDLE,
|
|
679
|
+
context: createInitialContext(uiControlConfig, TaskState.IDLE),
|
|
680
|
+
states: {
|
|
681
|
+
[TaskState.IDLE]: {
|
|
682
|
+
on: {
|
|
683
|
+
[TaskEvent.TASK_INCOMING]: {
|
|
684
|
+
target: TaskState.OFFERED,
|
|
685
|
+
actions: ['initializeTask', 'emitTaskIncoming'],
|
|
686
|
+
},
|
|
687
|
+
},
|
|
688
|
+
},
|
|
689
|
+
[TaskState.OFFERED]: {
|
|
690
|
+
on: {
|
|
691
|
+
[TaskEvent.ASSIGN]: {
|
|
692
|
+
target: TaskState.CONNECTED,
|
|
693
|
+
actions: ['updateTaskData', 'emitTaskAssigned'],
|
|
694
|
+
},
|
|
695
|
+
[TaskEvent.TASK_WRAPUP]: {
|
|
696
|
+
target: TaskState.TERMINATED,
|
|
697
|
+
actions: ['updateTaskData', 'markEnded', 'emitTaskEnd'],
|
|
698
|
+
},
|
|
699
|
+
},
|
|
700
|
+
},
|
|
701
|
+
// ... more states
|
|
702
|
+
},
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
## Backend CC Event Mapping Reference (CC_EVENTS -> TaskEvent -> Transition)
|
|
708
|
+
|
|
709
|
+
Complete mapping from backend CC_EVENTS to internal TaskEvent types.
|
|
710
|
+
|
|
711
|
+
| Backend CC Event | TaskEvent | Typical From State(s) | Target State | Notes / Guards |
|
|
712
|
+
| ---------------------------------- | ----------------------------- | ---------------------------------------------------- | ----------------------------------------------------------------------------- | --------------------------------- |
|
|
713
|
+
| `AGENT_CONTACT_RESERVED` | `TASK_INCOMING` | `IDLE` | `OFFERED` | Incoming task entry |
|
|
714
|
+
| `AGENT_OFFER_CONTACT` | `TASK_OFFERED` | `OFFERED` | `OFFERED` | Offer payload refresh |
|
|
715
|
+
| `AGENT_CONTACT` | `HYDRATE` | `IDLE` | `WRAPPING_UP` / `CONSULTING` / `HELD` / `CONNECTED` / `CONFERENCING` / `IDLE` | Guard-based restore |
|
|
716
|
+
| `CONTACT_UPDATED` | `CONTACT_UPDATED` | any | same | Context sync |
|
|
717
|
+
| `CONTACT_OWNER_CHANGED` | `CONTACT_OWNER_CHANGED` | any | same | Context sync |
|
|
718
|
+
| `AGENT_OFFER_CONSULT` | `OFFER_CONSULT` | `OFFERED` | `OFFERED` | Receiver-side consult offer |
|
|
719
|
+
| `AGENT_CONTACT_ASSIGNED` | `ASSIGN` | `OFFERED` / `CONNECTED` / `CONSULTING` | `CONNECTED` | Assign/reassign |
|
|
720
|
+
| `AGENT_CONTACT_HELD` | `HOLD_SUCCESS` | `HOLD_INITIATING` | `HELD` | Includes `mediaResourceId` |
|
|
721
|
+
| `AGENT_CONTACT_UNHELD` | `UNHOLD_SUCCESS` | `RESUME_INITIATING` | `CONNECTED` | Includes `mediaResourceId` |
|
|
722
|
+
| `AGENT_CONSULT_CREATED` | `CONSULT_CREATED` | varies | same | Context + emitter action |
|
|
723
|
+
| `AGENT_CONSULTING` | `CONSULTING_ACTIVE` | `OFFERED` / `CONSULTING` | `CONSULTING` | Sets consult joined flag |
|
|
724
|
+
| `AGENT_CONSULT_ENDED` | `CONSULT_END` | `CONSULTING` | `CONFERENCING` / `HELD` / `TERMINATED` | Depends on initiator flags |
|
|
725
|
+
| `AGENT_CONSULT_FAILED` | `CONSULT_FAILED` | `CONSULT_INITIATING` | `CONFERENCING` / `HELD` / `CONNECTED` | Guard-based fallback |
|
|
726
|
+
| `AGENT_CTQ_FAILED` | `CONSULT_FAILED` | `CONSULT_INITIATING` | `CONFERENCING` / `HELD` / `CONNECTED` | Same as consult failed |
|
|
727
|
+
| `AGENT_CTQ_CANCELLED` | `CTQ_CANCEL` | `CONSULT_INITIATING` | `HELD` / `CONNECTED` | Guarded by hold state |
|
|
728
|
+
| `AGENT_CTQ_CANCEL_FAILED` | `CTQ_CANCEL_FAILED` | varies | same | No transition mapping |
|
|
729
|
+
| `AGENT_BLIND_TRANSFERRED` | `TRANSFER_SUCCESS` | `CONNECTED` / `HELD` / `CONSULTING` | `WRAPPING_UP` / `CONNECTED` | `shouldWrapUpOrIsInitiator` |
|
|
730
|
+
| `AGENT_CONSULT_TRANSFERRED` | `TRANSFER_SUCCESS` | `CONNECTED` / `HELD` / `CONSULTING` | `WRAPPING_UP` / `CONNECTED` | Same path |
|
|
731
|
+
| `AGENT_VTEAM_TRANSFERRED` | `TRANSFER_SUCCESS` | `CONNECTED` / `HELD` / `CONSULTING` | `WRAPPING_UP` / `CONNECTED` | Same path |
|
|
732
|
+
| `AGENT_WRAPUP` | `TASK_WRAPUP` | `OFFERED` / `CONNECTED` / `HELD` / `CONSULTING` | `TERMINATED` / `WRAPPING_UP` | `OFFERED` terminates; others wrap |
|
|
733
|
+
| `AGENT_BLIND_TRANSFER_FAILED` | `TRANSFER_FAILED` | `CONNECTED` / `HELD` / `CONSULTING` | same | Context update |
|
|
734
|
+
| `AGENT_VTEAM_TRANSFER_FAILED` | `TRANSFER_FAILED` | `CONNECTED` / `HELD` / `CONSULTING` | same | Context update |
|
|
735
|
+
| `AGENT_CONSULT_TRANSFER_FAILED` | `TRANSFER_FAILED` | `CONNECTED` / `HELD` / `CONSULTING` | same | Context update |
|
|
736
|
+
| `AGENT_CONFERENCE_TRANSFER_FAILED` | `TRANSFER_FAILED` | `CONNECTED` / `HELD` / `CONSULTING` | same | Context update |
|
|
737
|
+
| `CONTACT_ENDED` | `CONTACT_ENDED` | `CONNECTED` / `HELD` / `CONSULTING` / `CONFERENCING` | `CONFERENCING` / `WRAPPING_UP` / `TERMINATED` / same | Guard-driven branch |
|
|
738
|
+
| `AGENT_INVITE_FAILED` | `INVITE_FAILED` | `OFFERED` | `TERMINATED` | Reject path |
|
|
739
|
+
| `AGENT_CONTACT_ASSIGN_FAILED` | `ASSIGN_FAILED` | `OFFERED` | `TERMINATED` | Reject path |
|
|
740
|
+
| `AGENT_CONTACT_OFFER_RONA` | `RONA` | `OFFERED` | `TERMINATED` | Timeout path |
|
|
741
|
+
| `AGENT_OUTBOUND_FAILED` | `OUTBOUND_FAILED` | `OFFERED` | `TERMINATED` | Outbound failure |
|
|
742
|
+
| `CONTACT_RECORDING_STARTED` | `RECORDING_STARTED` | any | same | Recording state update |
|
|
743
|
+
| `CONTACT_RECORDING_PAUSED` | `PAUSE_RECORDING` | `CONNECTED` | same | Recording state update |
|
|
744
|
+
| `CONTACT_RECORDING_RESUMED` | `RESUME_RECORDING` | `CONNECTED` | same | Recording state update |
|
|
745
|
+
| `AGENT_WRAPPEDUP` | `WRAPUP_COMPLETE` | `WRAPPING_UP` | `COMPLETED` | Final completion |
|
|
746
|
+
| `AGENT_CONSULT_CONFERENCED` | `CONFERENCE_START` | `CONSULTING` / `CONF_INITIATING` / `CONFERENCING` | `CONFERENCING` / same | Conference established |
|
|
747
|
+
| `PARTICIPANT_JOINED_CONFERENCE` | `CONFERENCE_START` | `CONSULTING` / `CONF_INITIATING` / `CONFERENCING` | `CONFERENCING` / same | Conference participant joined |
|
|
748
|
+
| `AGENT_CONSULT_CONFERENCE_FAILED` | `CONFERENCE_FAILED` | `CONF_INITIATING` | `CONSULTING` | Merge fail fallback |
|
|
749
|
+
| `AGENT_CONSULT_CONFERENCE_ENDED` | `CONFERENCE_END` | `CONFERENCING` | `WRAPPING_UP` / `CONNECTED` / `TERMINATED` | Guard-driven |
|
|
750
|
+
| `PARTICIPANT_LEFT_CONFERENCE` | `PARTICIPANT_LEAVE` | `CONFERENCING` | `WRAPPING_UP` / `TERMINATED` / `CONNECTED` / same | Ownership + downgrade guards |
|
|
751
|
+
| `AGENT_CONFERENCE_TRANSFERRED` | `TRANSFER_CONFERENCE_SUCCESS` | `CONSULTING` / `CONFERENCING` | `WRAPPING_UP` / `CONFERENCING` / `TERMINATED` / same | Initiator/receiver dependent |
|
|
752
|
+
|
|
753
|
+
### Explicitly not mapped to state machine
|
|
754
|
+
|
|
755
|
+
- `AGENT_CONTACT_UNASSIGNED` -> returns `null` in mapper (`TaskManager.mapEventToTaskStateMachineEvent`)
|
|
756
|
+
|
|
757
|
+
### Contact Lifecycle Mappings
|
|
758
|
+
|
|
759
|
+
| Backend Event | TaskEvent | State Transition | Notes |
|
|
760
|
+
| ------------------------ | ----------------------- | ------------------------------------------------------------- | ---------------------------------------------- |
|
|
761
|
+
| `AgentContactReserved` | `TASK_INCOMING` | IDLE → OFFERED | New task offered |
|
|
762
|
+
| `AgentOfferContact` | `TASK_OFFERED` | Stay in OFFERED | Offer confirmation |
|
|
763
|
+
| `AgentContact` | `HYDRATE` | Various | State restoration |
|
|
764
|
+
| `AgentContactAssigned` | `ASSIGN` | OFFERED → CONNECTED (also CONNECTED/CONSULTING refresh paths) | Task accepted/reassigned |
|
|
765
|
+
| `ContactUpdated` | `CONTACT_UPDATED` | No change | Data update only |
|
|
766
|
+
| `ContactOwnerChanged` | `CONTACT_OWNER_CHANGED` | No change | Owner update only |
|
|
767
|
+
| `ContactEnded` | `CONTACT_ENDED` | Guard-based branch | CONFERENCING / WRAPPING_UP / TERMINATED / stay |
|
|
768
|
+
| `AgentContactUnassigned` | None | N/A | Handled by other events |
|
|
769
|
+
|
|
770
|
+
### Hold/Resume Mappings
|
|
771
|
+
|
|
772
|
+
| Backend Event | TaskEvent | State Transition | Context Update |
|
|
773
|
+
| -------------------------- | ---------------- | ----------------------------- | ----------------------------------------------------------------------------------- |
|
|
774
|
+
| `AgentContactHeld` | `HOLD_SUCCESS` | HOLD_INITIATING → HELD | `setHoldState` updates `taskData.interaction.media[mediaResourceId].isHold = true` |
|
|
775
|
+
| `AgentContactUnheld` | `UNHOLD_SUCCESS` | RESUME_INITIATING → CONNECTED | `setHoldState` updates `taskData.interaction.media[mediaResourceId].isHold = false` |
|
|
776
|
+
| `AgentContactHoldFailed` | `HOLD_FAILED` | HOLD_INITIATING → CONNECTED | Context refreshed |
|
|
777
|
+
| `AgentContactUnholdFailed` | `UNHOLD_FAILED` | RESUME_INITIATING → HELD | No transition action |
|
|
778
|
+
|
|
779
|
+
### Consult Mappings
|
|
780
|
+
|
|
781
|
+
| Backend Event / API | TaskEvent | State Transition | Context Update |
|
|
782
|
+
| --------------------------------------- | ------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
|
783
|
+
| API `task.consult(...)` | `CONSULT` | CONNECTED/HELD/CONFERENCING → CONSULT_INITIATING | Sets consult initiator + destination (and `consultFromConference` in conference flow) |
|
|
784
|
+
| `AgentOfferConsult` | `OFFER_CONSULT` | OFFERED → OFFERED | Offer-only path |
|
|
785
|
+
| `AgentConsultCreated` | `CONSULT_CREATED` | No state transition wiring | Event exists but not consumed by transition table |
|
|
786
|
+
| `AgentConsulting` | `CONSULTING_ACTIVE` | OFFERED → CONSULTING, CONSULTING → CONSULTING | Sets `consultDestinationAgentJoined` |
|
|
787
|
+
| `AgentConsultEnded` | `CONSULT_END` | CONSULTING → CONFERENCING / HELD / TERMINATED | Depends on initiator and consult-from-conference |
|
|
788
|
+
| `AgentConsultFailed` / `AgentCtqFailed` | `CONSULT_FAILED` | CONSULT_INITIATING → CONFERENCING / HELD / CONNECTED | Guard-based fallback |
|
|
789
|
+
| `AgentCtqCancelled` | `CTQ_CANCEL` | CONSULT_INITIATING → HELD / CONNECTED | Guarded by `isPrimaryMediaOnHold` |
|
|
790
|
+
| `AgentCtqCancelFailed` | `CTQ_CANCEL_FAILED` | No state transition wiring | Event mapped but not consumed |
|
|
791
|
+
|
|
792
|
+
### Transfer Mappings
|
|
793
|
+
|
|
794
|
+
| Backend Event | TaskEvent | State Transition | Wrapup Logic |
|
|
795
|
+
| ---------------------------- | ------------------ | ---------------------------------- | ------------------------------------------------------------------------- |
|
|
796
|
+
| `AgentBlindTransferred` | `TRANSFER_SUCCESS` | → WRAPPING_UP/CONNECTED | Guard `shouldWrapUpOrIsInitiator` decides wrapup vs receiver/default path |
|
|
797
|
+
| `AgentVTeamTransferred` | `TRANSFER_SUCCESS` | → WRAPPING_UP/CONNECTED | Same transition logic as blind transfer |
|
|
798
|
+
| `AgentConsultTransferred` | `TRANSFER_SUCCESS` | CONSULTING → WRAPPING_UP/CONNECTED | Initiator/wrapup path vs receiver/default path |
|
|
799
|
+
| `AgentBlindTransferFailed` | `TRANSFER_FAILED` | No change | Emit failure, stay in current state |
|
|
800
|
+
| `AgentVTeamTransferFailed` | `TRANSFER_FAILED` | No change | Queue transfer failed |
|
|
801
|
+
| `AgentConsultTransferFailed` | `TRANSFER_FAILED` | No change | Consult transfer failed |
|
|
802
|
+
|
|
803
|
+
### Conference Mappings
|
|
804
|
+
|
|
805
|
+
| Backend Event / API | TaskEvent | State Transition | Context Update |
|
|
806
|
+
| -------------------------------- | ----------------------------- | ---------------------------------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
807
|
+
| API `task.consultConference()` | `MERGE_TO_CONFERENCE` | CONSULTING → CONF_INITIATING | Starts merge flow |
|
|
808
|
+
| `AgentConsultConferenced` | `CONFERENCE_START` | CONSULTING/CONF_INITIATING → CONFERENCING | `handleConferenceStarted` path |
|
|
809
|
+
| `ParticipantJoinedConference` | `CONFERENCE_START` | CONFERENCING → CONFERENCING | Refresh + emit conference started |
|
|
810
|
+
| `ParticipantLeftConference` | `PARTICIPANT_LEAVE` | CONFERENCING → WRAPPING_UP / TERMINATED / CONNECTED / stay | Uses `didCurrentAgentLeaveConference`, `shouldWrapUp`, `shouldDowngradeConferenceToConnected` |
|
|
811
|
+
| `AgentConsultConferenceEnded` | `CONFERENCE_END` | CONFERENCING → WRAPPING_UP / CONNECTED / TERMINATED | Guard-based branch |
|
|
812
|
+
| `AgentConsultConferenceFailed` | `CONFERENCE_FAILED` | CONF_INITIATING → CONSULTING | Merge failed fallback |
|
|
813
|
+
| `AgentConferenceTransferred` | `TRANSFER_CONFERENCE_SUCCESS` | CONSULTING/CONFERENCING branch logic | Initiator/receiver dependent |
|
|
814
|
+
| API/SDK conference transfer fail | `TRANSFER_CONFERENCE_FAILED` | CONSULTING/CONFERENCING stay | Clears transfer request flag |
|
|
815
|
+
|
|
816
|
+
### Recording Mappings
|
|
817
|
+
|
|
818
|
+
| Backend Event | TaskEvent | State Transition | Context Update |
|
|
819
|
+
| ------------------------- | ------------------- | ---------------- | ---------------------- |
|
|
820
|
+
| `ContactRecordingStarted` | `RECORDING_STARTED` | No change | Update recording state |
|
|
821
|
+
| `ContactRecordingPaused` | `PAUSE_RECORDING` | No change | Mark recording paused |
|
|
822
|
+
| `ContactRecordingResumed` | `RESUME_RECORDING` | No change | Mark recording active |
|
|
823
|
+
|
|
824
|
+
### Wrapup Mappings
|
|
825
|
+
|
|
826
|
+
| Backend Event | TaskEvent | State Transition | Notes |
|
|
827
|
+
| ---------------- | ----------------- | ----------------------- | --------------- |
|
|
828
|
+
| `AgentWrapup` | `TASK_WRAPUP` | → WRAPPING_UP | Enter ACW |
|
|
829
|
+
| `AgentWrappedup` | `WRAPUP_COMPLETE` | WRAPPING_UP → COMPLETED | Complete wrapup |
|
|
830
|
+
|
|
831
|
+
### Error Mappings
|
|
832
|
+
|
|
833
|
+
| Backend Event | TaskEvent | State Transition | Notes |
|
|
834
|
+
| -------------------------- | ----------------- | -------------------- | ------------------------ |
|
|
835
|
+
| `AgentContactOfferRona` | `RONA` | OFFERED → TERMINATED | Redirection on no answer |
|
|
836
|
+
| `AgentInviteFailed` | `INVITE_FAILED` | OFFERED → TERMINATED | Invite failed |
|
|
837
|
+
| `AgentContactAssignFailed` | `ASSIGN_FAILED` | OFFERED → TERMINATED | Assignment failed |
|
|
838
|
+
| `AgentOutboundFailed` | `OUTBOUND_FAILED` | OFFERED → TERMINATED | Outdial failed |
|
|
839
|
+
|
|
840
|
+
---
|
|
841
|
+
|
|
842
|
+
## State Transition Diagrams
|
|
843
|
+
|
|
844
|
+
This diagram represents state-to-state lifecycle transitions for the task state machine.
|
|
845
|
+
|
|
846
|
+
```mermaid
|
|
847
|
+
stateDiagram-v2
|
|
848
|
+
[*] --> IDLE
|
|
849
|
+
|
|
850
|
+
%% IDLE
|
|
851
|
+
IDLE --> OFFERED: AGENT_CONTACT_RESERVED (CC Event) -> TASK_INCOMING (State Machine Event)
|
|
852
|
+
|
|
853
|
+
%% OFFERED
|
|
854
|
+
OFFERED --> OFFERED: AGENT_OFFER_CONTACT (CC Event) -> TASK_OFFERED (State Machine Event)
|
|
855
|
+
OFFERED --> OFFERED: AGENT_OFFER_CONSULT (CC Event) -> OFFER_CONSULT (State Machine Event)
|
|
856
|
+
OFFERED --> CONNECTED: AGENT_CONTACT_ASSIGNED (CC Event) -> ASSIGN (State Machine Event)
|
|
857
|
+
OFFERED --> CONSULTING: AGENT_CONSULTING (CC Event) -> CONSULTING_ACTIVE (State Machine Event)
|
|
858
|
+
OFFERED --> TERMINATED: AGENT_CONTACT_OFFER_RONA (CC Event) -> RONA (State Machine Event)
|
|
859
|
+
OFFERED --> TERMINATED: AGENT_CONTACT_ASSIGN_FAILED (CC Event) -> ASSIGN_FAILED (State Machine Event)
|
|
860
|
+
OFFERED --> TERMINATED: AGENT_INVITE_FAILED (CC Event) -> INVITE_FAILED (State Machine Event)
|
|
861
|
+
OFFERED --> TERMINATED: AGENT_OUTBOUND_FAILED (CC Event) -> OUTBOUND_FAILED (State Machine Event)
|
|
862
|
+
OFFERED --> TERMINATED: AGENT_WRAPUP (CC Event) -> TASK_WRAPUP (State Machine Event)
|
|
863
|
+
|
|
864
|
+
%% CONNECTED
|
|
865
|
+
CONNECTED --> HOLD_INITIATING: API hold() -> HOLD_INITIATED (State Machine Event)
|
|
866
|
+
CONNECTED --> CONSULT_INITIATING: API consult() -> CONSULT (State Machine Event)
|
|
867
|
+
CONNECTED --> WRAPPING_UP: AGENT_*TRANSFERRED (CC Event) -> TRANSFER_SUCCESS (State Machine Event) [shouldWrapUpOrIsInitiator]
|
|
868
|
+
CONNECTED --> CONNECTED: AGENT_*TRANSFERRED (CC Event) -> TRANSFER_SUCCESS (State Machine Event) [receiver]
|
|
869
|
+
CONNECTED --> CONNECTED: AGENT_*TRANSFER_FAILED (CC Event) -> TRANSFER_FAILED (State Machine Event)
|
|
870
|
+
CONNECTED --> CONFERENCING: CONTACT_ENDED (CC Event) -> CONTACT_ENDED (State Machine Event) [conferenceInProgressFromEvent]
|
|
871
|
+
CONNECTED --> WRAPPING_UP: CONTACT_ENDED (CC Event) -> CONTACT_ENDED (State Machine Event) [shouldWrapUp]
|
|
872
|
+
CONNECTED --> TERMINATED: CONTACT_ENDED (CC Event) -> CONTACT_ENDED (State Machine Event) [default]
|
|
873
|
+
CONNECTED --> WRAPPING_UP: AGENT_WRAPUP (CC Event) -> TASK_WRAPUP (State Machine Event)
|
|
874
|
+
|
|
875
|
+
%% HOLD_INITIATING
|
|
876
|
+
HOLD_INITIATING --> HELD: HOLD_SUCCESS (State Machine Event)
|
|
877
|
+
HOLD_INITIATING --> CONNECTED: HOLD_FAILED (State Machine Event)
|
|
878
|
+
|
|
879
|
+
%% HELD
|
|
880
|
+
HELD --> RESUME_INITIATING: UNHOLD_INITIATED (State Machine Event)
|
|
881
|
+
HELD --> CONSULT_INITIATING: CONSULT (State Machine Event)
|
|
882
|
+
HELD --> WRAPPING_UP: TRANSFER_SUCCESS (State Machine Event) [shouldWrapUpOrIsInitiator]
|
|
883
|
+
HELD --> CONNECTED: TRANSFER_SUCCESS (State Machine Event) [receiver]
|
|
884
|
+
HELD --> CONFERENCING: CONTACT_ENDED (CC Event) -> CONTACT_ENDED (State Machine Event) [conferenceInProgressFromEvent]
|
|
885
|
+
HELD --> WRAPPING_UP: CONTACT_ENDED (CC Event) -> CONTACT_ENDED (State Machine Event) [shouldWrapUp]
|
|
886
|
+
HELD --> TERMINATED: CONTACT_ENDED (CC Event) -> CONTACT_ENDED (State Machine Event) [default]
|
|
887
|
+
HELD --> WRAPPING_UP: TASK_WRAPUP (State Machine Event)
|
|
888
|
+
HELD --> HELD: TRANSFER_FAILED (State Machine Event)
|
|
889
|
+
|
|
890
|
+
%% RESUME_INITIATING
|
|
891
|
+
RESUME_INITIATING --> CONNECTED: UNHOLD_SUCCESS (State Machine Event)
|
|
892
|
+
RESUME_INITIATING --> HELD: UNHOLD_FAILED (State Machine Event)
|
|
893
|
+
|
|
894
|
+
%% CONSULT_INITIATING
|
|
895
|
+
CONSULT_INITIATING --> CONSULTING: CONSULT_SUCCESS (State Machine Event)
|
|
896
|
+
CONSULT_INITIATING --> CONFERENCING: CONSULT_FAILED (State Machine Event) [consultFromConference]
|
|
897
|
+
CONSULT_INITIATING --> HELD: CONSULT_FAILED / CTQ_CANCEL (State Machine Event) [isPrimaryMediaOnHold]
|
|
898
|
+
CONSULT_INITIATING --> CONNECTED: HOLD_FAILED / CONSULT_FAILED / CTQ_CANCEL (State Machine Event) [default]
|
|
899
|
+
CONSULT_INITIATING --> CONSULT_INITIATING: HOLD_SUCCESS (State Machine Event)
|
|
900
|
+
|
|
901
|
+
%% CONSULTING
|
|
902
|
+
CONSULTING --> CONFERENCING: CONSULT_END (State Machine Event) [consultInitiator && consultFromConference]
|
|
903
|
+
CONSULTING --> HELD: CONSULT_END (State Machine Event) [consultInitiator]
|
|
904
|
+
CONSULTING --> TERMINATED: CONSULT_END (State Machine Event) [consulted agent]
|
|
905
|
+
CONSULTING --> WRAPPING_UP: TRANSFER_SUCCESS (State Machine Event) [shouldWrapUpOrIsInitiator]
|
|
906
|
+
CONSULTING --> CONNECTED: TRANSFER_SUCCESS (State Machine Event) [receiver]
|
|
907
|
+
CONSULTING --> WRAPPING_UP: CONTACT_ENDED (CC Event) -> CONTACT_ENDED (State Machine Event) / TASK_WRAPUP (State Machine Event)
|
|
908
|
+
CONSULTING --> CONNECTED: ASSIGN (State Machine Event)
|
|
909
|
+
CONSULTING --> CONF_INITIATING: MERGE_TO_CONFERENCE (State Machine Event)
|
|
910
|
+
CONSULTING --> CONFERENCING: CONFERENCE_START (State Machine Event)
|
|
911
|
+
CONSULTING --> CONFERENCING: TRANSFER_CONFERENCE_SUCCESS (State Machine Event) [!consultInitiator]
|
|
912
|
+
CONSULTING --> WRAPPING_UP: TRANSFER_CONFERENCE_SUCCESS (State Machine Event) [shouldWrapUp]
|
|
913
|
+
CONSULTING --> TERMINATED: TRANSFER_CONFERENCE_SUCCESS (State Machine Event) [default]
|
|
914
|
+
CONSULTING --> CONSULTING: CONSULTING_ACTIVE / HOLD_SUCCESS / UNHOLD_SUCCESS / TRANSFER_FAILED / TRANSFER_CONFERENCE / TRANSFER_CONFERENCE_FAILED (State Machine Event)
|
|
915
|
+
|
|
916
|
+
%% CONF_INITIATING
|
|
917
|
+
CONF_INITIATING --> CONFERENCING: CONFERENCE_START (State Machine Event)
|
|
918
|
+
CONF_INITIATING --> CONSULTING: CONFERENCE_FAILED (State Machine Event)
|
|
919
|
+
|
|
920
|
+
%% CONFERENCING
|
|
921
|
+
CONFERENCING --> CONSULT_INITIATING: CONSULT (State Machine Event)
|
|
922
|
+
CONFERENCING --> WRAPPING_UP: PARTICIPANT_LEAVE (CC Event) -> PARTICIPANT_LEAVE (State Machine Event) [didCurrentAgentLeaveConference && shouldWrapUp]
|
|
923
|
+
CONFERENCING --> TERMINATED: PARTICIPANT_LEAVE (CC Event) -> PARTICIPANT_LEAVE (State Machine Event) [didCurrentAgentLeaveConference]
|
|
924
|
+
CONFERENCING --> CONNECTED: PARTICIPANT_LEAVE (CC Event) -> PARTICIPANT_LEAVE (State Machine Event) [shouldDowngradeConferenceToConnected]
|
|
925
|
+
CONFERENCING --> WRAPPING_UP: CONFERENCE_END (CC Event) -> CONFERENCE_END (State Machine Event) [shouldWrapUp]
|
|
926
|
+
CONFERENCING --> CONNECTED: CONFERENCE_END (CC Event) -> CONFERENCE_END (State Machine Event) [customerInCall]
|
|
927
|
+
CONFERENCING --> TERMINATED: CONFERENCE_END (CC Event) -> CONFERENCE_END (State Machine Event) [default]
|
|
928
|
+
CONFERENCING --> CONFERENCING: CONFERENCE_START / CONSULT_END / HOLD_SUCCESS / UNHOLD_SUCCESS / PARTICIPANT_LEAVE(other) / TRANSFER_CONFERENCE / TRANSFER_CONFERENCE_SUCCESS / TRANSFER_CONFERENCE_FAILED / CONTACT_ENDED (State Machine Event)
|
|
929
|
+
|
|
930
|
+
%% WRAPPING_UP / FINAL
|
|
931
|
+
WRAPPING_UP --> COMPLETED: WRAPUP_COMPLETE (State Machine Event)
|
|
932
|
+
COMPLETED --> [*]
|
|
933
|
+
TERMINATED --> [*]
|
|
934
|
+
```
|
|
935
|
+
|
|
936
|
+
---
|
|
937
|
+
|
|
938
|
+
## Focused Transition Diagrams
|
|
939
|
+
|
|
940
|
+
The full diagram above is the source of truth. The diagrams below split above flows based on each feature.
|
|
941
|
+
|
|
942
|
+
### 1) Initial Task Assign Flow
|
|
943
|
+
|
|
944
|
+
```mermaid
|
|
945
|
+
stateDiagram-v2
|
|
946
|
+
[*] --> IDLE
|
|
947
|
+
IDLE --> OFFERED: AGENT_CONTACT_RESERVED -> TASK_INCOMING
|
|
948
|
+
OFFERED --> OFFERED: AGENT_CONTACT_OFFER -> TASK_OFFERED
|
|
949
|
+
OFFERED --> OFFERED: AGENT_CONSULT_OFFER -> OFFER_CONSULT
|
|
950
|
+
OFFERED --> CONNECTED: AGENT_CONTACT_ASSIGNED -> ASSIGN
|
|
951
|
+
OFFERED --> TERMINATED: AGENT_CONTACT_OFFER_RONA/AGENT_CONTACT_ASSIGN_FAILED/AGENT_INVITE_FAILED/AGENT_OUTBOUND_FAILED/AGENT_WRAPUP -> RONA/ASSIGN_FAILED/INVITE_FAILED/OUTBOUND_FAILED/TASK_WRAPUP
|
|
952
|
+
|
|
953
|
+
CONNECTED --> HOLD_INITIATING: task.hold() -> HOLD_INITIATED
|
|
954
|
+
CONNECTED --> CONSULT_INITIATING: task.consult() -> CONSULT
|
|
955
|
+
|
|
956
|
+
CONNECTED --> CONNECTED: PAUSE_RECORDING/RESUME_RECORDING
|
|
957
|
+
CONNECTED --> CONNECTED: AGENT_BLIND_TRANSFER_FAILED/AGENT_VTEAM_TRANSFER_FAILED -> TRANSFER_FAILED
|
|
958
|
+
|
|
959
|
+
CONNECTED --> WRAPPING_UP: AGENT_BLIND_TRANSFERRED/AGENT_VTEAM_TRANSFERRED -> TRANSFER_SUCCESS [shouldWrapUpOrIsInitiator]
|
|
960
|
+
CONNECTED --> WRAPPING_UP: CONTACT_ENDED -> CONTACT_ENDED [shouldWrapUp]
|
|
961
|
+
CONNECTED --> WRAPPING_UP: AGENT_WRAPUP -> TASK_WRAPUP
|
|
962
|
+
CONNECTED --> TERMINATED: CONTACT_ENDED -> CONTACT_ENDED [default]
|
|
963
|
+
|
|
964
|
+
WRAPPING_UP --> COMPLETED: AGENT_WRAPPEDUP -> WRAPUP_COMPLETE
|
|
965
|
+
COMPLETED --> [*]
|
|
966
|
+
TERMINATED --> [*]
|
|
967
|
+
```
|
|
968
|
+
|
|
969
|
+
### 2) Hold/Resume Flow
|
|
970
|
+
|
|
971
|
+
```mermaid
|
|
972
|
+
stateDiagram-v2
|
|
973
|
+
[*] --> CONNECTED
|
|
974
|
+
CONNECTED --> HOLD_INITIATING: task.hold() -> HOLD_INITIATED
|
|
975
|
+
HOLD_INITIATING --> HELD: AGENT_CONTACT_HELD -> HOLD_SUCCESS
|
|
976
|
+
HOLD_INITIATING --> CONNECTED: AGENT_CONTACT_HOLD_FAILED -> HOLD_FAILED
|
|
977
|
+
HELD --> RESUME_INITIATING: task.resume() -> UNHOLD_INITIATED
|
|
978
|
+
RESUME_INITIATING --> CONNECTED: AGENT_CONTACT_UNHELD -> UNHOLD_SUCCESS
|
|
979
|
+
RESUME_INITIATING --> HELD: AGENT_CONTACT_UNHOLD_FAILED -> UNHOLD_FAILED
|
|
980
|
+
HELD --> CONSULT_INITIATING: task.consult() -> Consult
|
|
981
|
+
CONSULT_INITIATING --> HELD: CONSULT_FAILED / CTQ_CANCEL [isPrimaryMediaOnHold]
|
|
982
|
+
HELD --> WRAPPING_UP: CONTACT_ENDED -> CONTACT_ENDED [shouldWrapUp]
|
|
983
|
+
HELD --> TERMINATED: CONTACT_ENDED -> CONTACT_ENDED [default]
|
|
984
|
+
|
|
985
|
+
WRAPPING_UP --> COMPLETED: AGENT_WRAPPEDUP -> WRAPUP_COMPLETE
|
|
986
|
+
COMPLETED --> [*]
|
|
987
|
+
TERMINATED --> [*]
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
### 3) Consult Flow
|
|
991
|
+
|
|
992
|
+
```mermaid
|
|
993
|
+
stateDiagram-v2
|
|
994
|
+
stateDiagram-v2
|
|
995
|
+
[*] --> CONNECTED
|
|
996
|
+
CONNECTED --> CONSULT_INITIATING: task.consult() -> CONSULT
|
|
997
|
+
HELD --> CONSULT_INITIATING: task.consult() -> CONSULT
|
|
998
|
+
CONFERENCING --> CONSULT_INITIATING: task.consult() -> CONSULT
|
|
999
|
+
|
|
1000
|
+
CONSULT_INITIATING --> CONSULT_INITIATING: AGENT_CONTACT_HELD -> HOLD_SUCCESS
|
|
1001
|
+
CONSULT_INITIATING --> CONNECTED: AGENT_CONTACT_HOLD_FAILED -> HOLD_FAILED
|
|
1002
|
+
CONSULT_INITIATING --> CONSULTING: API consult success -> CONSULT_SUCCESS
|
|
1003
|
+
CONSULT_INITIATING --> HELD: AGENT_CONSULT_FAILED/AGENT_CTQ_FAILED -> CONSULT_FAILED [isPrimaryMediaOnHold]
|
|
1004
|
+
CONSULT_INITIATING --> CONNECTED: AGENT_CONSULT_FAILED/AGENT_CTQ_FAILED -> CONSULT_FAILED [default]
|
|
1005
|
+
CONSULT_INITIATING --> CONFERENCING: AGENT_CONSULT_FAILED -> CONSULT_FAILED [consultFromConference]
|
|
1006
|
+
CONSULT_INITIATING --> HELD: AGENT_CTQ_CANCELLED -> CTQ_CANCEL [isPrimaryMediaOnHold]
|
|
1007
|
+
CONSULT_INITIATING --> CONNECTED: AGENT_CTQ_CANCELLED -> CTQ_CANCEL [default]
|
|
1008
|
+
|
|
1009
|
+
CONSULTING --> HELD: AGENT_CONSULT_ENDED -> CONSULT_END [consultInitiator]
|
|
1010
|
+
CONSULTING --> TERMINATED: AGENT_CONSULT_ENDED -> CONSULT_END [consulted agent]
|
|
1011
|
+
CONSULTING --> CONFERENCING: AGENT_CONSULT_ENDED -> CONSULT_END [consultInitiator && consultFromConference]
|
|
1012
|
+
CONSULTING --> CONNECTED: AGENT_CONSULT_TRANSFERRED/AGENT_CONTACT_ASSIGNED -> TRANSFER_SUCCESS/ASSIGN
|
|
1013
|
+
CONSULTING --> WRAPPING_UP: AGENT_CONSULT_TRANSFERRED -> TRANSFER_SUCCESS [shouldWrapUpOrIsInitiator]
|
|
1014
|
+
CONSULTING --> CONSULTING: AGENT_CONSULT_TRANSFER_FAILED -> TRANSFER_FAILED
|
|
1015
|
+
CONSULTING --> CONF_INITIATING: task.consultConference() -> MERGE_TO_CONFERENCE
|
|
1016
|
+
CONSULTING --> WRAPPING_UP: AGENT_CONTACT_ENDED -> CONTACT_ENDED
|
|
1017
|
+
|
|
1018
|
+
WRAPPING_UP --> COMPLETED: AGENT_WRAPPEDUP -> WRAPUP_COMPLETE
|
|
1019
|
+
COMPLETED --> [*]
|
|
1020
|
+
TERMINATED --> [*]
|
|
1021
|
+
```
|
|
1022
|
+
|
|
1023
|
+
### 4) Conference Flow
|
|
1024
|
+
|
|
1025
|
+
```mermaid
|
|
1026
|
+
stateDiagram-v2
|
|
1027
|
+
[*] --> CONSULTING
|
|
1028
|
+
CONSULTING --> CONF_INITIATING: task.consultConference() -> MERGE_TO_CONFERENCE
|
|
1029
|
+
CONF_INITIATING --> CONFERENCING: AGENT_CONSULT_CONFERENCED -> CONFERENCE_START
|
|
1030
|
+
CONF_INITIATING --> CONSULTING: AGENT_CONSULT_CONFERENCE_FAILED -> CONFERENCE_FAILED
|
|
1031
|
+
CONFERENCING --> CONSULT_INITIATING: task.consult() -> CONSULT
|
|
1032
|
+
CONSULTING --> CONFERENCING: AGENT_CONFERENCE_TRANSFERRED -> TRANSFER_CONFERENCE_SUCCESS [!consultInitiator]
|
|
1033
|
+
CONSULTING --> WRAPPING_UP: AGENT_CONFERENCE_TRANSFERRED -> TRANSFER_CONFERENCE_SUCCESS [shouldWrapUp]
|
|
1034
|
+
CONSULTING --> TERMINATED: AGENT_CONFERENCE_TRANSFERRED -> TRANSFER_CONFERENCE_SUCCESS [default]
|
|
1035
|
+
CONSULTING --> CONSULTING: AGENT_CONFERENCE_TRANSFER_FAILED -> TRANSFER_CONFERENCE_FAILED
|
|
1036
|
+
CONFERENCING --> CONFERENCING: AGENT_CONFERENCE_TRANSFERRED -> TRANSFER_CONFERENCE_SUCCESS
|
|
1037
|
+
CONFERENCING --> CONFERENCING: AGENT_CONFERENCE_TRANSFER_FAILED-> TRANSFER_CONFERENCE_FAILED
|
|
1038
|
+
|
|
1039
|
+
CONFERENCING --> WRAPPING_UP: PARTICIPANT_LEFT_CONFERENCE -> PARTICIPANT_LEAVE [didCurrentAgentLeaveConference && shouldWrapUp]
|
|
1040
|
+
CONFERENCING --> TERMINATED: PARTICIPANT_LEFT_CONFERENCE -> PARTICIPANT_LEAVE [didCurrentAgentLeaveConference]
|
|
1041
|
+
CONFERENCING --> CONNECTED: PARTICIPANT_LEFT_CONFERENCE -> PARTICIPANT_LEAVE [shouldDowngradeConferenceToConnected]
|
|
1042
|
+
CONFERENCING --> WRAPPING_UP: AGENT_CONSULT_CONFERENCE_ENDED -> CONFERENCE_END [shouldWrapUp]
|
|
1043
|
+
CONFERENCING --> CONNECTED: AGENT_CONSULT_CONFERENCE_ENDED -> CONFERENCE_END [customerInCall]
|
|
1044
|
+
CONFERENCING --> TERMINATED: AGENT_CONSULT_CONFERENCE_ENDED -> CONFERENCE_END [default]
|
|
1045
|
+
WRAPPING_UP --> COMPLETED: WRAPUP_COMPLETE
|
|
1046
|
+
COMPLETED --> [*]
|
|
1047
|
+
TERMINATED --> [*]
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
---
|
|
1051
|
+
|
|
1052
|
+
## State Machine Configuration Example
|
|
1053
|
+
|
|
1054
|
+
```typescript
|
|
1055
|
+
import {setup} from 'xstate';
|
|
1056
|
+
import {actions} from './actions';
|
|
1057
|
+
import {guards} from './guards';
|
|
1058
|
+
|
|
1059
|
+
const taskStateMachine = setup({
|
|
1060
|
+
types: {
|
|
1061
|
+
context: {} as TaskContext,
|
|
1062
|
+
events: {} as TaskEventPayload,
|
|
1063
|
+
},
|
|
1064
|
+
})
|
|
1065
|
+
.createMachine({
|
|
1066
|
+
id: 'taskStateMachine',
|
|
1067
|
+
initial: TaskState.IDLE,
|
|
1068
|
+
context: createInitialContext(uiControlConfig, TaskState.IDLE),
|
|
1069
|
+
states: {
|
|
1070
|
+
[TaskState.IDLE]: {
|
|
1071
|
+
on: {
|
|
1072
|
+
[TaskEvent.TASK_INCOMING]: {
|
|
1073
|
+
target: TaskState.OFFERED,
|
|
1074
|
+
actions: ['initializeTask', 'emitTaskIncoming'],
|
|
1075
|
+
},
|
|
1076
|
+
[TaskEvent.HYDRATE]: [
|
|
1077
|
+
{
|
|
1078
|
+
guard: guards.isInteractionTerminated,
|
|
1079
|
+
target: TaskState.WRAPPING_UP,
|
|
1080
|
+
actions: ['updateTaskData', 'markEnded', 'emitTaskHydrate'],
|
|
1081
|
+
},
|
|
1082
|
+
// ... more hydrate cases
|
|
1083
|
+
],
|
|
1084
|
+
},
|
|
1085
|
+
},
|
|
1086
|
+
[TaskState.OFFERED]: {
|
|
1087
|
+
on: {
|
|
1088
|
+
[TaskEvent.ASSIGN]: {
|
|
1089
|
+
target: TaskState.CONNECTED,
|
|
1090
|
+
actions: ['updateTaskData', 'emitTaskAssigned'],
|
|
1091
|
+
},
|
|
1092
|
+
[TaskEvent.TASK_WRAPUP]: {
|
|
1093
|
+
target: TaskState.TERMINATED,
|
|
1094
|
+
actions: ['updateTaskData', 'markEnded', 'emitTaskEnd'],
|
|
1095
|
+
},
|
|
1096
|
+
// ... more transitions
|
|
1097
|
+
},
|
|
1098
|
+
},
|
|
1099
|
+
// ... more states
|
|
1100
|
+
},
|
|
1101
|
+
})
|
|
1102
|
+
.provide({actions});
|
|
1103
|
+
```
|
|
1104
|
+
|
|
1105
|
+
---
|
|
1106
|
+
|
|
1107
|
+
## State Restoration (HYDRATE)
|
|
1108
|
+
|
|
1109
|
+
The HYDRATE event restores state machine state after page refresh or reconnection.
|
|
1110
|
+
|
|
1111
|
+
**Algorithm**:
|
|
1112
|
+
|
|
1113
|
+
1. Receive HYDRATE event with full task data
|
|
1114
|
+
2. Check interaction state and flags in order of precedence:
|
|
1115
|
+
- If `taskData.interaction.isTerminated === true` -> WRAPPING_UP
|
|
1116
|
+
- If `taskData.interaction.state === 'consulting'` -> CONSULTING
|
|
1117
|
+
- If `taskData.interaction.state === 'hold'` -> HELD
|
|
1118
|
+
- If `taskData.interaction.state === 'connected'` -> CONNECTED
|
|
1119
|
+
- If conferencing-by-participants guard passes (`agentCount >= 2` in main call) -> CONFERENCING
|
|
1120
|
+
- Default → Stay in IDLE
|
|
1121
|
+
3. Update context with hydrated data
|
|
1122
|
+
4. Emit TASK_HYDRATE event
|
|
1123
|
+
|
|
1124
|
+
---
|
|
1125
|
+
|
|
1126
|
+
## Related Files
|
|
1127
|
+
|
|
1128
|
+
- `../Task.ts` - actor lifecycle, action overrides, event emission
|
|
1129
|
+
- `../TaskManager.ts` - maps backend events to state-machine events
|
|
1130
|
+
- `../types.ts` - shared task data structures
|
|
1131
|
+
- `../../ai-docs/ARCHITECTURE.md` - broader task service architecture
|
|
1132
|
+
- [TaskStateMachine.ts](../state-machine/TaskStateMachine.ts) - Implementation
|
|
1133
|
+
- [guards.ts](../state-machine/guards.ts) - Guard functions
|
|
1134
|
+
- [actions.ts](../state-machine/actions.ts) - Action functions
|
|
1135
|
+
- [constants.ts](../state-machine/constants.ts) - State and event enums
|