@webex/contact-center 3.12.0-next.12 → 3.12.0-next.121

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