@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.
Files changed (200) hide show
  1. package/AGENTS.md +438 -0
  2. package/ai-docs/README.md +131 -0
  3. package/ai-docs/RULES.md +455 -0
  4. package/ai-docs/patterns/event-driven-patterns.md +485 -0
  5. package/ai-docs/patterns/testing-patterns.md +480 -0
  6. package/ai-docs/patterns/typescript-patterns.md +365 -0
  7. package/ai-docs/templates/README.md +102 -0
  8. package/ai-docs/templates/documentation/create-agents-md.md +240 -0
  9. package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
  10. package/ai-docs/templates/existing-service/bug-fix.md +254 -0
  11. package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
  12. package/ai-docs/templates/new-method/00-master.md +80 -0
  13. package/ai-docs/templates/new-method/01-requirements.md +232 -0
  14. package/ai-docs/templates/new-method/02-implementation.md +295 -0
  15. package/ai-docs/templates/new-method/03-tests.md +201 -0
  16. package/ai-docs/templates/new-method/04-validation.md +141 -0
  17. package/ai-docs/templates/new-service/00-master.md +109 -0
  18. package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
  19. package/ai-docs/templates/new-service/02-code-generation.md +346 -0
  20. package/ai-docs/templates/new-service/03-integration.md +178 -0
  21. package/ai-docs/templates/new-service/04-test-generation.md +205 -0
  22. package/ai-docs/templates/new-service/05-validation.md +145 -0
  23. package/dist/cc.js +65 -123
  24. package/dist/cc.js.map +1 -1
  25. package/dist/constants.js +13 -2
  26. package/dist/constants.js.map +1 -1
  27. package/dist/index.js +13 -5
  28. package/dist/index.js.map +1 -1
  29. package/dist/metrics/behavioral-events.js +26 -13
  30. package/dist/metrics/behavioral-events.js.map +1 -1
  31. package/dist/metrics/constants.js +7 -6
  32. package/dist/metrics/constants.js.map +1 -1
  33. package/dist/services/ApiAiAssistant.js +0 -3
  34. package/dist/services/ApiAiAssistant.js.map +1 -1
  35. package/dist/services/config/Util.js +2 -3
  36. package/dist/services/config/Util.js.map +1 -1
  37. package/dist/services/config/types.js +16 -14
  38. package/dist/services/config/types.js.map +1 -1
  39. package/dist/services/constants.js +0 -1
  40. package/dist/services/constants.js.map +1 -1
  41. package/dist/services/core/Err.js.map +1 -1
  42. package/dist/services/core/Utils.js +79 -55
  43. package/dist/services/core/Utils.js.map +1 -1
  44. package/dist/services/core/aqm-reqs.js +17 -92
  45. package/dist/services/core/aqm-reqs.js.map +1 -1
  46. package/dist/services/core/websocket/WebSocketManager.js +5 -25
  47. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  48. package/dist/services/core/websocket/types.js.map +1 -1
  49. package/dist/services/index.js +1 -2
  50. package/dist/services/index.js.map +1 -1
  51. package/dist/services/task/Task.js +644 -0
  52. package/dist/services/task/Task.js.map +1 -0
  53. package/dist/services/task/TaskFactory.js +45 -0
  54. package/dist/services/task/TaskFactory.js.map +1 -0
  55. package/dist/services/task/TaskManager.js +570 -535
  56. package/dist/services/task/TaskManager.js.map +1 -1
  57. package/dist/services/task/TaskUtils.js +132 -28
  58. package/dist/services/task/TaskUtils.js.map +1 -1
  59. package/dist/services/task/constants.js +7 -6
  60. package/dist/services/task/constants.js.map +1 -1
  61. package/dist/services/task/dialer.js +0 -51
  62. package/dist/services/task/dialer.js.map +1 -1
  63. package/dist/services/task/digital/Digital.js +77 -0
  64. package/dist/services/task/digital/Digital.js.map +1 -0
  65. package/dist/services/task/state-machine/TaskStateMachine.js +634 -0
  66. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  67. package/dist/services/task/state-machine/actions.js +372 -0
  68. package/dist/services/task/state-machine/actions.js.map +1 -0
  69. package/dist/services/task/state-machine/constants.js +139 -0
  70. package/dist/services/task/state-machine/constants.js.map +1 -0
  71. package/dist/services/task/state-machine/guards.js +263 -0
  72. package/dist/services/task/state-machine/guards.js.map +1 -0
  73. package/dist/services/task/state-machine/index.js +53 -0
  74. package/dist/services/task/state-machine/index.js.map +1 -0
  75. package/dist/services/task/state-machine/types.js +54 -0
  76. package/dist/services/task/state-machine/types.js.map +1 -0
  77. package/dist/services/task/state-machine/uiControlsComputer.js +377 -0
  78. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  79. package/dist/services/task/taskDataNormalizer.js +99 -0
  80. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  81. package/dist/services/task/types.js +157 -18
  82. package/dist/services/task/types.js.map +1 -1
  83. package/dist/services/task/voice/Voice.js +1031 -0
  84. package/dist/services/task/voice/Voice.js.map +1 -0
  85. package/dist/services/task/voice/WebRTC.js +149 -0
  86. package/dist/services/task/voice/WebRTC.js.map +1 -0
  87. package/dist/types/cc.d.ts +4 -33
  88. package/dist/types/constants.d.ts +13 -2
  89. package/dist/types/index.d.ts +11 -5
  90. package/dist/types/metrics/constants.d.ts +5 -3
  91. package/dist/types/services/ApiAiAssistant.d.ts +1 -1
  92. package/dist/types/services/config/types.d.ts +97 -25
  93. package/dist/types/services/core/Err.d.ts +0 -2
  94. package/dist/types/services/core/Utils.d.ts +25 -23
  95. package/dist/types/services/core/aqm-reqs.d.ts +0 -49
  96. package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -1
  97. package/dist/types/services/core/websocket/connection-service.d.ts +0 -1
  98. package/dist/types/services/core/websocket/types.d.ts +1 -1
  99. package/dist/types/services/index.d.ts +1 -1
  100. package/dist/types/services/task/Task.d.ts +146 -0
  101. package/dist/types/services/task/TaskFactory.d.ts +12 -0
  102. package/dist/types/services/task/TaskUtils.d.ts +39 -8
  103. package/dist/types/services/task/constants.d.ts +5 -4
  104. package/dist/types/services/task/dialer.d.ts +0 -15
  105. package/dist/types/services/task/digital/Digital.d.ts +22 -0
  106. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +906 -0
  107. package/dist/types/services/task/state-machine/actions.d.ts +8 -0
  108. package/dist/types/services/task/state-machine/constants.d.ts +91 -0
  109. package/dist/types/services/task/state-machine/guards.d.ts +78 -0
  110. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  111. package/dist/types/services/task/state-machine/types.d.ts +256 -0
  112. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  113. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  114. package/dist/types/services/task/types.d.ts +539 -88
  115. package/dist/types/services/task/voice/Voice.d.ts +183 -0
  116. package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
  117. package/dist/types/types.d.ts +68 -0
  118. package/dist/types/webex.d.ts +1 -0
  119. package/dist/types.js +70 -0
  120. package/dist/types.js.map +1 -1
  121. package/dist/webex.js +14 -2
  122. package/dist/webex.js.map +1 -1
  123. package/package.json +14 -11
  124. package/src/cc.ts +91 -177
  125. package/src/constants.ts +13 -2
  126. package/src/index.ts +14 -5
  127. package/src/metrics/ai-docs/AGENTS.md +348 -0
  128. package/src/metrics/ai-docs/ARCHITECTURE.md +336 -0
  129. package/src/metrics/behavioral-events.ts +28 -14
  130. package/src/metrics/constants.ts +7 -8
  131. package/src/services/ApiAiAssistant.ts +2 -4
  132. package/src/services/agent/ai-docs/AGENTS.md +238 -0
  133. package/src/services/agent/ai-docs/ARCHITECTURE.md +302 -0
  134. package/src/services/ai-docs/AGENTS.md +384 -0
  135. package/src/services/config/Util.ts +2 -3
  136. package/src/services/config/ai-docs/AGENTS.md +253 -0
  137. package/src/services/config/ai-docs/ARCHITECTURE.md +424 -0
  138. package/src/services/config/types.ts +108 -20
  139. package/src/services/constants.ts +0 -1
  140. package/src/services/core/Err.ts +0 -1
  141. package/src/services/core/Utils.ts +90 -67
  142. package/src/services/core/ai-docs/AGENTS.md +379 -0
  143. package/src/services/core/ai-docs/ARCHITECTURE.md +696 -0
  144. package/src/services/core/aqm-reqs.ts +22 -100
  145. package/src/services/core/websocket/WebSocketManager.ts +4 -23
  146. package/src/services/core/websocket/types.ts +1 -1
  147. package/src/services/index.ts +1 -2
  148. package/src/services/task/Task.ts +785 -0
  149. package/src/services/task/TaskFactory.ts +55 -0
  150. package/src/services/task/TaskManager.ts +579 -633
  151. package/src/services/task/TaskUtils.ts +175 -31
  152. package/src/services/task/ai-docs/AGENTS.md +448 -0
  153. package/src/services/task/ai-docs/ARCHITECTURE.md +573 -0
  154. package/src/services/task/constants.ts +5 -4
  155. package/src/services/task/dialer.ts +1 -56
  156. package/src/services/task/digital/Digital.ts +95 -0
  157. package/src/services/task/state-machine/TaskStateMachine.ts +793 -0
  158. package/src/services/task/state-machine/actions.ts +422 -0
  159. package/src/services/task/state-machine/ai-docs/AGENTS.md +495 -0
  160. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1135 -0
  161. package/src/services/task/state-machine/constants.ts +150 -0
  162. package/src/services/task/state-machine/guards.ts +303 -0
  163. package/src/services/task/state-machine/index.ts +28 -0
  164. package/src/services/task/state-machine/types.ts +228 -0
  165. package/src/services/task/state-machine/uiControlsComputer.ts +542 -0
  166. package/src/services/task/taskDataNormalizer.ts +137 -0
  167. package/src/services/task/types.ts +641 -95
  168. package/src/services/task/voice/Voice.ts +1255 -0
  169. package/src/services/task/voice/WebRTC.ts +187 -0
  170. package/src/types.ts +88 -5
  171. package/src/utils/AGENTS.md +276 -0
  172. package/src/webex.js +2 -0
  173. package/test/unit/spec/cc.ts +59 -142
  174. package/test/unit/spec/logger-proxy.ts +70 -0
  175. package/test/unit/spec/services/ApiAiAssistant.ts +17 -0
  176. package/test/unit/spec/services/config/index.ts +26 -55
  177. package/test/unit/spec/services/core/Utils.ts +103 -52
  178. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +48 -112
  179. package/test/unit/spec/services/core/websocket/connection-service.ts +5 -4
  180. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  181. package/test/unit/spec/services/task/Task.ts +416 -0
  182. package/test/unit/spec/services/task/TaskFactory.ts +62 -0
  183. package/test/unit/spec/services/task/TaskManager.ts +781 -1735
  184. package/test/unit/spec/services/task/TaskUtils.ts +125 -0
  185. package/test/unit/spec/services/task/dialer.ts +112 -198
  186. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  187. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +473 -0
  188. package/test/unit/spec/services/task/state-machine/guards.ts +288 -0
  189. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  190. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +147 -0
  191. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  192. package/test/unit/spec/services/task/voice/Voice.ts +587 -0
  193. package/test/unit/spec/services/task/voice/WebRTC.ts +242 -0
  194. package/umd/contact-center.min.js +2 -2
  195. package/umd/contact-center.min.js.map +1 -1
  196. package/dist/services/task/index.js +0 -1525
  197. package/dist/services/task/index.js.map +0 -1
  198. package/dist/types/services/task/index.d.ts +0 -650
  199. package/src/services/task/index.ts +0 -1801
  200. 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