@webex/contact-center 3.12.0-next.9 → 3.12.0-next.90

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