@webex/contact-center 3.11.0 → 3.12.0-auth-prejoin-fetch.1

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