@webex/contact-center 3.12.0-llmrefactor.1 → 3.12.0-llmrefactor.10

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 (198) hide show
  1. package/.sdd/manifest.json +7 -5
  2. package/ai-docs/ARCHITECTURE.md +1 -1
  3. package/ai-docs/CONTRACTS.md +8 -3
  4. package/ai-docs/SECURITY.md +1 -1
  5. package/ai-docs/contact-center-spec.md +22 -4
  6. package/ai-docs/features/consult-transfer-list-policy/spec/feature-spec.md +362 -0
  7. package/dist/cc.js +544 -12
  8. package/dist/cc.js.map +1 -1
  9. package/dist/config.js +7 -0
  10. package/dist/config.js.map +1 -1
  11. package/dist/constants.js +10 -2
  12. package/dist/constants.js.map +1 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/metrics/MetricsManager.js +16 -0
  15. package/dist/metrics/MetricsManager.js.map +1 -1
  16. package/dist/metrics/behavioral-events.js +104 -0
  17. package/dist/metrics/behavioral-events.js.map +1 -1
  18. package/dist/metrics/constants.js +18 -0
  19. package/dist/metrics/constants.js.map +1 -1
  20. package/dist/services/AddressBook.js +18 -15
  21. package/dist/services/AddressBook.js.map +1 -1
  22. package/dist/services/AnswerCallOnWebexService.js +202 -0
  23. package/dist/services/AnswerCallOnWebexService.js.map +1 -0
  24. package/dist/services/EntryPoint.js +46 -68
  25. package/dist/services/EntryPoint.js.map +1 -1
  26. package/dist/services/Queue.js +27 -22
  27. package/dist/services/Queue.js.map +1 -1
  28. package/dist/services/WebexCrossClientService.js +222 -0
  29. package/dist/services/WebexCrossClientService.js.map +1 -0
  30. package/dist/services/WxAppTelephonyMercurySync.js +108 -0
  31. package/dist/services/WxAppTelephonyMercurySync.js.map +1 -0
  32. package/dist/services/config/Util.js +3 -0
  33. package/dist/services/config/Util.js.map +1 -1
  34. package/dist/services/config/constants.js +10 -6
  35. package/dist/services/config/constants.js.map +1 -1
  36. package/dist/services/config/types.js +4 -0
  37. package/dist/services/config/types.js.map +1 -1
  38. package/dist/services/core/Err.js.map +1 -1
  39. package/dist/services/core/WebexRequest.js +6 -2
  40. package/dist/services/core/WebexRequest.js.map +1 -1
  41. package/dist/services/core/aqm-reqs.js +28 -14
  42. package/dist/services/core/aqm-reqs.js.map +1 -1
  43. package/dist/services/core/types.js.map +1 -1
  44. package/dist/services/task/Task.js +73 -7
  45. package/dist/services/task/Task.js.map +1 -1
  46. package/dist/services/task/TaskFactory.js +8 -4
  47. package/dist/services/task/TaskFactory.js.map +1 -1
  48. package/dist/services/task/TaskManager.js +490 -21
  49. package/dist/services/task/TaskManager.js.map +1 -1
  50. package/dist/services/task/WebexCallingUtils.js +70 -0
  51. package/dist/services/task/WebexCallingUtils.js.map +1 -0
  52. package/dist/services/task/constants.js +4 -1
  53. package/dist/services/task/constants.js.map +1 -1
  54. package/dist/services/task/contact.js +29 -0
  55. package/dist/services/task/contact.js.map +1 -1
  56. package/dist/services/task/digital/Digital.js +3 -2
  57. package/dist/services/task/digital/Digital.js.map +1 -1
  58. package/dist/services/task/state-machine/TaskStateMachine.js +162 -53
  59. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
  60. package/dist/services/task/state-machine/actions.js +6 -1
  61. package/dist/services/task/state-machine/actions.js.map +1 -1
  62. package/dist/services/task/state-machine/guards.js +7 -10
  63. package/dist/services/task/state-machine/guards.js.map +1 -1
  64. package/dist/services/task/state-machine/types.js.map +1 -1
  65. package/dist/services/task/state-machine/uiControlsComputer.js +118 -18
  66. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -1
  67. package/dist/services/task/types.js +16 -1
  68. package/dist/services/task/types.js.map +1 -1
  69. package/dist/services/task/voice/Voice.js +456 -15
  70. package/dist/services/task/voice/Voice.js.map +1 -1
  71. package/dist/services/task/voice/WebRTC.js +4 -1
  72. package/dist/services/task/voice/WebRTC.js.map +1 -1
  73. package/dist/services/task/voice/wxAppVoiceMethods.js +354 -0
  74. package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -0
  75. package/dist/services/wxAppDiagnosticLogging.js +122 -0
  76. package/dist/services/wxAppDiagnosticLogging.js.map +1 -0
  77. package/dist/services/wxAppTelephonyUtils.js +19 -0
  78. package/dist/services/wxAppTelephonyUtils.js.map +1 -0
  79. package/dist/types/cc.d.ts +78 -4
  80. package/dist/types/config.d.ts +7 -0
  81. package/dist/types/constants.d.ts +8 -0
  82. package/dist/types/index.d.ts +3 -1
  83. package/dist/types/metrics/MetricsManager.d.ts +6 -0
  84. package/dist/types/metrics/constants.d.ts +17 -0
  85. package/dist/types/services/AddressBook.d.ts +2 -1
  86. package/dist/types/services/AnswerCallOnWebexService.d.ts +38 -0
  87. package/dist/types/services/EntryPoint.d.ts +7 -5
  88. package/dist/types/services/Queue.d.ts +5 -3
  89. package/dist/types/services/WebexCrossClientService.d.ts +32 -0
  90. package/dist/types/services/WxAppTelephonyMercurySync.d.ts +28 -0
  91. package/dist/types/services/config/constants.d.ts +8 -5
  92. package/dist/types/services/config/types.d.ts +17 -4
  93. package/dist/types/services/core/Err.d.ts +2 -0
  94. package/dist/types/services/core/WebexRequest.d.ts +1 -0
  95. package/dist/types/services/core/types.d.ts +2 -0
  96. package/dist/types/services/task/Task.d.ts +20 -2
  97. package/dist/types/services/task/TaskFactory.d.ts +2 -1
  98. package/dist/types/services/task/WebexCallingUtils.d.ts +11 -0
  99. package/dist/types/services/task/constants.d.ts +3 -0
  100. package/dist/types/services/task/contact.d.ts +4 -0
  101. package/dist/types/services/task/digital/Digital.d.ts +2 -2
  102. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +304 -56
  103. package/dist/types/services/task/state-machine/guards.d.ts +6 -5
  104. package/dist/types/services/task/state-machine/types.d.ts +9 -1
  105. package/dist/types/services/task/types.d.ts +81 -4
  106. package/dist/types/services/task/voice/Voice.d.ts +52 -9
  107. package/dist/types/services/task/voice/WebRTC.d.ts +2 -2
  108. package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +55 -0
  109. package/dist/types/services/wxAppDiagnosticLogging.d.ts +51 -0
  110. package/dist/types/services/wxAppTelephonyUtils.d.ts +5 -0
  111. package/dist/types/types.d.ts +14 -8
  112. package/dist/types/utils/PageCache.d.ts +20 -3
  113. package/dist/types.js +7 -2
  114. package/dist/types.js.map +1 -1
  115. package/dist/utils/PageCache.js +19 -5
  116. package/dist/utils/PageCache.js.map +1 -1
  117. package/dist/webex.js +1 -1
  118. package/package.json +8 -8
  119. package/src/cc.ts +657 -12
  120. package/src/config.ts +7 -0
  121. package/src/constants.ts +8 -0
  122. package/src/index.ts +2 -0
  123. package/src/metrics/MetricsManager.ts +16 -0
  124. package/src/metrics/ai-docs/metrics-spec.md +78 -10
  125. package/src/metrics/behavioral-events.ts +106 -0
  126. package/src/metrics/constants.ts +20 -0
  127. package/src/services/AddressBook.ts +17 -6
  128. package/src/services/AnswerCallOnWebexService.ts +239 -0
  129. package/src/services/EntryPoint.ts +59 -60
  130. package/src/services/Queue.ts +29 -12
  131. package/src/services/WebexCrossClientService.ts +293 -0
  132. package/src/services/WxAppTelephonyMercurySync.ts +142 -0
  133. package/src/services/agent/ai-docs/agent-spec.md +1 -0
  134. package/src/services/ai-docs/AGENTS.md +10 -10
  135. package/src/services/ai-docs/services-spec.md +6 -1
  136. package/src/services/config/Util.ts +3 -0
  137. package/src/services/config/ai-docs/AGENTS.md +1 -1
  138. package/src/services/config/ai-docs/ARCHITECTURE.md +2 -2
  139. package/src/services/config/ai-docs/config-spec.md +6 -0
  140. package/src/services/config/constants.ts +10 -6
  141. package/src/services/config/types.ts +16 -4
  142. package/src/services/core/Err.ts +1 -0
  143. package/src/services/core/WebexRequest.ts +3 -1
  144. package/src/services/core/ai-docs/core-spec.md +5 -1
  145. package/src/services/core/aqm-reqs.ts +30 -15
  146. package/src/services/core/types.ts +2 -0
  147. package/src/services/task/Task.ts +79 -8
  148. package/src/services/task/TaskFactory.ts +8 -3
  149. package/src/services/task/TaskManager.ts +716 -17
  150. package/src/services/task/WebexCallingUtils.ts +136 -0
  151. package/src/services/task/ai-docs/ARCHITECTURE.md +1 -0
  152. package/src/services/task/ai-docs/task-spec.md +156 -3
  153. package/src/services/task/constants.ts +3 -0
  154. package/src/services/task/contact.ts +30 -0
  155. package/src/services/task/digital/Digital.ts +4 -2
  156. package/src/services/task/state-machine/TaskStateMachine.ts +235 -81
  157. package/src/services/task/state-machine/actions.ts +5 -2
  158. package/src/services/task/state-machine/ai-docs/AGENTS.md +6 -4
  159. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +35 -16
  160. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +103 -33
  161. package/src/services/task/state-machine/guards.ts +7 -10
  162. package/src/services/task/state-machine/types.ts +16 -1
  163. package/src/services/task/state-machine/uiControlsComputer.ts +206 -32
  164. package/src/services/task/types.ts +114 -4
  165. package/src/services/task/voice/Voice.ts +595 -14
  166. package/src/services/task/voice/WebRTC.ts +5 -1
  167. package/src/services/task/voice/wxAppVoiceMethods.ts +516 -0
  168. package/src/services/wxAppDiagnosticLogging.ts +174 -0
  169. package/src/services/wxAppTelephonyUtils.ts +14 -0
  170. package/src/types.ts +33 -9
  171. package/src/utils/AGENTS.md +21 -10
  172. package/src/utils/PageCache.ts +38 -5
  173. package/src/utils/ai-docs/utils-spec.md +21 -11
  174. package/test/unit/spec/cc.ts +1522 -19
  175. package/test/unit/spec/metrics/MetricsManager.ts +25 -0
  176. package/test/unit/spec/metrics/behavioral-events.ts +60 -0
  177. package/test/unit/spec/services/AddressBook.ts +37 -6
  178. package/test/unit/spec/services/AnswerCallOnWebexService.ts +264 -0
  179. package/test/unit/spec/services/EntryPoint.ts +87 -40
  180. package/test/unit/spec/services/Queue.ts +123 -12
  181. package/test/unit/spec/services/WebexCrossClientService.ts +467 -0
  182. package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +136 -0
  183. package/test/unit/spec/services/config/Util.ts +85 -0
  184. package/test/unit/spec/services/core/WebexRequest.ts +3 -1
  185. package/test/unit/spec/services/core/aqm-reqs.ts +113 -1
  186. package/test/unit/spec/services/task/Task.ts +274 -6
  187. package/test/unit/spec/services/task/TaskFactory.ts +39 -2
  188. package/test/unit/spec/services/task/TaskManager.ts +1717 -9
  189. package/test/unit/spec/services/task/WebexCallingUtils.ts +153 -0
  190. package/test/unit/spec/services/task/contact.ts +33 -0
  191. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +1485 -109
  192. package/test/unit/spec/services/task/state-machine/guards.ts +210 -8
  193. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +445 -7
  194. package/test/unit/spec/services/task/voice/Voice.ts +1412 -0
  195. package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +603 -0
  196. package/test/unit/spec/services/wxAppDiagnosticLogging.ts +143 -0
  197. package/umd/contact-center.min.js +2 -2
  198. package/umd/contact-center.min.js.map +1 -1
@@ -52,12 +52,81 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
52
52
  * AgentContactHeld -> TaskEvent.HOLD_SUCCESS
53
53
  * AgentContactUnheld -> TaskEvent.UNHOLD_SUCCESS
54
54
  * AgentConsultEnded -> TaskEvent.CONSULT_END
55
+ * ParticipantLeftConference -> TaskEvent.PARTICIPANT_LEAVE
55
56
  * AgentContactEnded -> TaskEvent.CONTACT_ENDED
56
57
  * AgentWrapup -> TaskEvent.TASK_WRAPUP (wrapUpRequired)
57
58
  * AgentWrappedup -> TaskEvent.WRAPUP_COMPLETE
58
59
  *
59
60
  * (See TaskManager.mapEventToTaskStateMachineEvent for the full mapping table.)
60
61
  */
62
+ // XState evaluates these guarded transition arrays in order. Each returned entry is a
63
+ // complete transition with its own target and actions; sharing them keeps lifecycle-event
64
+ // handling consistent across active states where ParticipantLeftConference can arrive.
65
+ const currentAgentParticipantLeaveTransitions = () => [
66
+ {
67
+ guard: (params) =>
68
+ guards.didCurrentAgentLeaveMainInteraction(params) && guards.shouldWrapUp(params),
69
+ target: TaskState.WRAPPING_UP,
70
+ actions: [
71
+ 'updateTaskData',
72
+ 'handleParticipantLeft',
73
+ 'markEnded',
74
+ 'clearConsultState',
75
+ 'emitTaskParticipantLeft',
76
+ ],
77
+ },
78
+ {
79
+ guard: guards.didCurrentAgentLeaveMainInteraction,
80
+ target: TaskState.TERMINATED,
81
+ actions: [
82
+ 'updateTaskData',
83
+ 'handleParticipantLeft',
84
+ 'markEnded',
85
+ 'clearConsultState',
86
+ 'emitTaskParticipantLeft',
87
+ 'emitTaskEnd',
88
+ ],
89
+ },
90
+ ];
91
+
92
+ const exitConferenceSuccessTransitions = () => [
93
+ {
94
+ guard: guards.shouldWrapUp,
95
+ target: TaskState.WRAPPING_UP,
96
+ actions: ['updateTaskData', 'markEnded', 'clearConsultState'],
97
+ },
98
+ {
99
+ target: TaskState.TERMINATED,
100
+ actions: ['updateTaskData', 'markEnded', 'clearConsultState', 'emitTaskEnd'],
101
+ },
102
+ ];
103
+
104
+ const currentAgentConsultEndTransitions = () => [
105
+ {
106
+ guard: (params) =>
107
+ guards.didCurrentAgentLeaveMainInteraction(params) && guards.shouldWrapUp(params),
108
+ target: TaskState.WRAPPING_UP,
109
+ actions: [
110
+ 'updateTaskData',
111
+ 'markEnded',
112
+ 'clearConsultState',
113
+ 'emitTaskConsultEnd',
114
+ 'emitTaskWrapup',
115
+ ],
116
+ },
117
+ {
118
+ guard: guards.didCurrentAgentLeaveMainInteraction,
119
+ target: TaskState.TERMINATED,
120
+ actions: [
121
+ 'updateTaskData',
122
+ 'markEnded',
123
+ 'clearConsultState',
124
+ 'emitTaskConsultEnd',
125
+ 'emitTaskEnd',
126
+ ],
127
+ },
128
+ ];
129
+
61
130
  return {
62
131
  id: 'taskStateMachine',
63
132
  initial: TaskState.IDLE,
@@ -70,7 +139,7 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
70
139
  actions: ['updateTaskData', 'syncTaskDataFromEvent'],
71
140
  },
72
141
  [TaskEvent.CONTACT_OWNER_CHANGED]: {
73
- actions: ['updateTaskData', 'syncTaskDataFromEvent'],
142
+ actions: ['updateTaskData', 'syncTaskDataFromEvent', 'emitTaskHydrate'],
74
143
  },
75
144
  // HYDRATE: Update task data from AgentContact event
76
145
  // Note: State restoration with transitions is handled in IDLE state.
@@ -190,10 +259,17 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
190
259
  target: TaskState.TERMINATED,
191
260
  actions: ['updateTaskData', 'markEnded', 'emitTaskReject'],
192
261
  },
193
- [TaskEvent.TASK_WRAPUP]: {
194
- target: TaskState.TERMINATED,
195
- actions: ['updateTaskData', 'markEnded', 'emitTaskEnd'],
196
- },
262
+ [TaskEvent.TASK_WRAPUP]: [
263
+ {
264
+ guard: guards.shouldWrapUp,
265
+ target: TaskState.WRAPPING_UP,
266
+ actions: ['updateTaskData', 'markEnded'],
267
+ },
268
+ {
269
+ target: TaskState.TERMINATED,
270
+ actions: ['updateTaskData', 'markEnded', 'emitTaskEnd'],
271
+ },
272
+ ],
197
273
  [TaskEvent.CONTACT_ENDED]: {
198
274
  target: TaskState.TERMINATED,
199
275
  actions: ['updateTaskData', 'markEnded', 'emitTaskEnd'],
@@ -334,6 +410,7 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
334
410
  },
335
411
  // AgentConsultEnded from Stable Prod while on connected leg (external end consult).
336
412
  [TaskEvent.CONSULT_END]: [
413
+ ...currentAgentConsultEndTransitions(),
337
414
  {
338
415
  guard: ({context, event}) => {
339
416
  if (context.consultInitiator !== true) return false;
@@ -378,14 +455,26 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
378
455
  ],
379
456
  [TaskEvent.TASK_WRAPUP]: {
380
457
  target: TaskState.WRAPPING_UP,
381
- actions: ['updateTaskData', 'markEnded', 'emitTaskWrapup'],
458
+ actions: ['updateTaskData', 'markEnded'],
382
459
  },
460
+ [TaskEvent.OUTBOUND_FAILED]: [
461
+ {
462
+ guard: guards.shouldWrapUp,
463
+ target: TaskState.WRAPPING_UP,
464
+ actions: ['updateTaskData', 'markEnded', 'emitTaskOutdialFailed', 'emitTaskWrapup'],
465
+ },
466
+ {
467
+ target: TaskState.TERMINATED,
468
+ actions: ['updateTaskData', 'markEnded', 'emitTaskOutdialFailed', 'emitTaskEnd'],
469
+ },
470
+ ],
383
471
  [TaskEvent.PAUSE_RECORDING]: {
384
472
  actions: ['updateTaskData', 'setRecordingState', 'emitTaskRecordingPaused'],
385
473
  },
386
474
  [TaskEvent.RESUME_RECORDING]: {
387
475
  actions: ['updateTaskData', 'setRecordingState', 'emitTaskRecordingResumed'],
388
476
  },
477
+ [TaskEvent.EXIT_CONFERENCE_SUCCESS]: exitConferenceSuccessTransitions(),
389
478
  },
390
479
  },
391
480
 
@@ -401,6 +490,21 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
401
490
  target: TaskState.CONNECTED,
402
491
  actions: ['updateTaskData'],
403
492
  },
493
+ [TaskEvent.OUTBOUND_FAILED]: [
494
+ {
495
+ guard: guards.shouldWrapUp,
496
+ target: TaskState.WRAPPING_UP,
497
+ actions: ['updateTaskData', 'markEnded', 'emitTaskOutdialFailed', 'emitTaskWrapup'],
498
+ },
499
+ {
500
+ target: TaskState.TERMINATED,
501
+ actions: ['updateTaskData', 'markEnded', 'emitTaskOutdialFailed', 'emitTaskEnd'],
502
+ },
503
+ ],
504
+ [TaskEvent.TASK_WRAPUP]: {
505
+ target: TaskState.WRAPPING_UP,
506
+ actions: ['updateTaskData', 'markEnded'],
507
+ },
404
508
  },
405
509
  },
406
510
 
@@ -487,14 +591,31 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
487
591
  },
488
592
  ],
489
593
  // AgentConsultEnded from Stable Prod while main leg is held (external end consult).
490
- [TaskEvent.CONSULT_END]: {
491
- actions: ['updateTaskData', 'clearConsultState', 'emitTaskConsultEnd'],
492
- },
594
+ [TaskEvent.CONSULT_END]: [
595
+ ...currentAgentConsultEndTransitions(),
596
+ {actions: ['updateTaskData', 'clearConsultState', 'emitTaskConsultEnd']},
597
+ ],
598
+ [TaskEvent.PARTICIPANT_LEAVE]: [
599
+ ...currentAgentParticipantLeaveTransitions(),
600
+ {actions: ['updateTaskData', 'handleParticipantLeft', 'emitTaskParticipantLeft']},
601
+ ],
493
602
  // TODO: This may not be a valid transition, this needs to be checked as well
494
603
  [TaskEvent.TASK_WRAPUP]: {
495
604
  target: TaskState.WRAPPING_UP,
496
- actions: ['updateTaskData', 'markEnded', 'emitTaskWrapup'],
605
+ actions: ['updateTaskData', 'markEnded'],
497
606
  },
607
+ [TaskEvent.OUTBOUND_FAILED]: [
608
+ {
609
+ guard: guards.shouldWrapUp,
610
+ target: TaskState.WRAPPING_UP,
611
+ actions: ['updateTaskData', 'markEnded', 'emitTaskOutdialFailed', 'emitTaskWrapup'],
612
+ },
613
+ {
614
+ target: TaskState.TERMINATED,
615
+ actions: ['updateTaskData', 'markEnded', 'emitTaskOutdialFailed', 'emitTaskEnd'],
616
+ },
617
+ ],
618
+ [TaskEvent.EXIT_CONFERENCE_SUCCESS]: exitConferenceSuccessTransitions(),
498
619
  },
499
620
  },
500
621
 
@@ -507,6 +628,25 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
507
628
  [TaskEvent.UNHOLD_FAILED]: {
508
629
  target: TaskState.HELD,
509
630
  },
631
+ [TaskEvent.PARTICIPANT_LEAVE]: [
632
+ ...currentAgentParticipantLeaveTransitions(),
633
+ {actions: ['updateTaskData', 'handleParticipantLeft', 'emitTaskParticipantLeft']},
634
+ ],
635
+ [TaskEvent.OUTBOUND_FAILED]: [
636
+ {
637
+ guard: guards.shouldWrapUp,
638
+ target: TaskState.WRAPPING_UP,
639
+ actions: ['updateTaskData', 'markEnded', 'emitTaskOutdialFailed', 'emitTaskWrapup'],
640
+ },
641
+ {
642
+ target: TaskState.TERMINATED,
643
+ actions: ['updateTaskData', 'markEnded', 'emitTaskOutdialFailed', 'emitTaskEnd'],
644
+ },
645
+ ],
646
+ [TaskEvent.TASK_WRAPUP]: {
647
+ target: TaskState.WRAPPING_UP,
648
+ actions: ['updateTaskData', 'markEnded'],
649
+ },
510
650
  },
511
651
  },
512
652
 
@@ -569,6 +709,7 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
569
709
  ],
570
710
  // AgentConsultEnded from Stable Prod during consult initiation (external end consult).
571
711
  [TaskEvent.CONSULT_END]: [
712
+ ...currentAgentConsultEndTransitions(),
572
713
  {
573
714
  guard: guards.isPrimaryMediaOnHold,
574
715
  target: TaskState.HELD,
@@ -579,6 +720,25 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
579
720
  actions: ['updateTaskData', 'clearConsultState', 'emitTaskConsultEnd'],
580
721
  },
581
722
  ],
723
+ [TaskEvent.PARTICIPANT_LEAVE]: [
724
+ ...currentAgentParticipantLeaveTransitions(),
725
+ {actions: ['updateTaskData', 'handleParticipantLeft', 'emitTaskParticipantLeft']},
726
+ ],
727
+ [TaskEvent.OUTBOUND_FAILED]: [
728
+ {
729
+ guard: guards.shouldWrapUp,
730
+ target: TaskState.WRAPPING_UP,
731
+ actions: ['updateTaskData', 'markEnded', 'emitTaskOutdialFailed', 'emitTaskWrapup'],
732
+ },
733
+ {
734
+ target: TaskState.TERMINATED,
735
+ actions: ['updateTaskData', 'markEnded', 'emitTaskOutdialFailed', 'emitTaskEnd'],
736
+ },
737
+ ],
738
+ [TaskEvent.TASK_WRAPUP]: {
739
+ target: TaskState.WRAPPING_UP,
740
+ actions: ['updateTaskData', 'markEnded'],
741
+ },
582
742
  },
583
743
  },
584
744
 
@@ -619,6 +779,7 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
619
779
 
620
780
  // AgentConsultEnded
621
781
  [TaskEvent.CONSULT_END]: [
782
+ ...currentAgentConsultEndTransitions(),
622
783
  {
623
784
  // Initiator returning to conference only while conference is still active.
624
785
  guard: ({context, event}) =>
@@ -686,7 +847,13 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
686
847
  {
687
848
  // Consulted agent → TERMINATED
688
849
  target: TaskState.TERMINATED,
689
- actions: ['updateTaskData'],
850
+ actions: [
851
+ 'updateTaskData',
852
+ 'markEnded',
853
+ 'clearConsultState',
854
+ 'emitTaskConsultEnd',
855
+ 'emitTaskEnd',
856
+ ],
690
857
  },
691
858
  ],
692
859
 
@@ -705,34 +872,10 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
705
872
  },
706
873
 
707
874
  [TaskEvent.PARTICIPANT_LEAVE]: [
875
+ ...currentAgentParticipantLeaveTransitions(),
708
876
  {
709
877
  guard: (params) =>
710
- guards.didCurrentAgentLeaveConference(params) && guards.shouldWrapUp(params),
711
- target: TaskState.WRAPPING_UP,
712
- actions: [
713
- 'updateTaskData',
714
- 'handleParticipantLeft',
715
- 'markEnded',
716
- 'clearConsultState',
717
- 'emitTaskParticipantLeft',
718
- 'emitTaskWrapup',
719
- ],
720
- },
721
- {
722
- guard: guards.didCurrentAgentLeaveConference,
723
- target: TaskState.TERMINATED,
724
- actions: [
725
- 'updateTaskData',
726
- 'handleParticipantLeft',
727
- 'markEnded',
728
- 'clearConsultState',
729
- 'emitTaskParticipantLeft',
730
- 'emitTaskEnd',
731
- ],
732
- },
733
- {
734
- guard: (params) =>
735
- !guards.didCurrentAgentLeaveConference(params) &&
878
+ !guards.didCurrentAgentLeaveMainInteraction(params) &&
736
879
  guards.shouldDowngradeConferenceToConnected(params),
737
880
  target: TaskState.CONNECTED,
738
881
  actions: [
@@ -775,7 +918,6 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
775
918
  'clearConsultState',
776
919
  'handleTransferConferenceSuccess',
777
920
  'clearTransferConferenceRequested',
778
- 'emitTaskWrapup',
779
921
  ],
780
922
  },
781
923
  {
@@ -823,8 +965,19 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
823
965
  },
824
966
  [TaskEvent.TASK_WRAPUP]: {
825
967
  target: TaskState.WRAPPING_UP,
826
- actions: ['updateTaskData', 'markEnded', 'clearConsultState', 'emitTaskWrapup'],
968
+ actions: ['updateTaskData', 'markEnded', 'clearConsultState'],
827
969
  },
970
+ [TaskEvent.OUTBOUND_FAILED]: [
971
+ {
972
+ guard: guards.shouldWrapUp,
973
+ target: TaskState.WRAPPING_UP,
974
+ actions: ['updateTaskData', 'markEnded', 'emitTaskOutdialFailed', 'emitTaskWrapup'],
975
+ },
976
+ {
977
+ target: TaskState.TERMINATED,
978
+ actions: ['updateTaskData', 'markEnded', 'emitTaskOutdialFailed', 'emitTaskEnd'],
979
+ },
980
+ ],
828
981
  [TaskEvent.MERGE_TO_CONFERENCE]: {
829
982
  target: TaskState.CONF_INITIATING,
830
983
  },
@@ -860,6 +1013,7 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
860
1013
  },
861
1014
  // AgentConsultEnded while conference is initiating (end call before conference completes)
862
1015
  [TaskEvent.CONSULT_END]: [
1016
+ ...currentAgentConsultEndTransitions(),
863
1017
  {
864
1018
  guard: ({event}) => {
865
1019
  const taskData = getTaskDataFromEvent(event);
@@ -874,6 +1028,21 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
874
1028
  actions: ['updateTaskData', 'clearConsultState'],
875
1029
  },
876
1030
  ],
1031
+ [TaskEvent.OUTBOUND_FAILED]: [
1032
+ {
1033
+ guard: guards.shouldWrapUp,
1034
+ target: TaskState.WRAPPING_UP,
1035
+ actions: ['updateTaskData', 'markEnded', 'emitTaskOutdialFailed', 'emitTaskWrapup'],
1036
+ },
1037
+ {
1038
+ target: TaskState.TERMINATED,
1039
+ actions: ['updateTaskData', 'markEnded', 'emitTaskOutdialFailed', 'emitTaskEnd'],
1040
+ },
1041
+ ],
1042
+ [TaskEvent.TASK_WRAPUP]: {
1043
+ target: TaskState.WRAPPING_UP,
1044
+ actions: ['updateTaskData', 'markEnded'],
1045
+ },
877
1046
  },
878
1047
  },
879
1048
 
@@ -887,20 +1056,11 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
887
1056
  'emitTaskConferenceStarted',
888
1057
  ],
889
1058
  },
890
- [TaskEvent.EXIT_CONFERENCE_SUCCESS]: [
891
- {
892
- guard: guards.shouldWrapUp,
893
- target: TaskState.WRAPPING_UP,
894
- actions: ['updateTaskData', 'markEnded', 'clearConsultState', 'emitTaskWrapup'],
895
- },
896
- {
897
- target: TaskState.TERMINATED,
898
- actions: ['updateTaskData', 'markEnded', 'clearConsultState', 'emitTaskEnd'],
899
- },
900
- ],
1059
+ [TaskEvent.EXIT_CONFERENCE_SUCCESS]: exitConferenceSuccessTransitions(),
901
1060
 
902
1061
  // Needed as all agents in conference get this event, hence we need to clear the consult state
903
1062
  [TaskEvent.CONSULT_END]: [
1063
+ ...currentAgentConsultEndTransitions(),
904
1064
  {
905
1065
  guard: ({context, event}) => {
906
1066
  const taskData = getTaskDataFromEvent(event);
@@ -975,37 +1135,11 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
975
1135
 
976
1136
  // Participant leaves - handle conference downgrade scenarios
977
1137
  [TaskEvent.PARTICIPANT_LEAVE]: [
978
- {
979
- // Only the leaving agent should wrap up → WRAPPING_UP
980
- guard: (params) =>
981
- guards.didCurrentAgentLeaveConference(params) && guards.shouldWrapUp(params),
982
- target: TaskState.WRAPPING_UP,
983
- actions: [
984
- 'updateTaskData',
985
- 'handleParticipantLeft',
986
- 'markEnded',
987
- 'clearConsultState',
988
- 'emitTaskParticipantLeft',
989
- 'emitTaskWrapup',
990
- ],
991
- },
992
- {
993
- // Only the leaving agent (no wrapup) → TERMINATED
994
- guard: guards.didCurrentAgentLeaveConference,
995
- target: TaskState.TERMINATED,
996
- actions: [
997
- 'updateTaskData',
998
- 'handleParticipantLeft',
999
- 'markEnded',
1000
- 'clearConsultState',
1001
- 'emitTaskParticipantLeft',
1002
- 'emitTaskEnd',
1003
- ],
1004
- },
1138
+ ...currentAgentParticipantLeaveTransitions(),
1005
1139
  {
1006
1140
  // Conference downgraded, customer present → CONNECTED
1007
1141
  guard: (params) =>
1008
- !guards.didCurrentAgentLeaveConference(params) &&
1142
+ !guards.didCurrentAgentLeaveMainInteraction(params) &&
1009
1143
  guards.shouldDowngradeConferenceToConnected(params),
1010
1144
  target: TaskState.CONNECTED,
1011
1145
  actions: [
@@ -1040,7 +1174,6 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
1040
1174
  'clearConsultState',
1041
1175
  'handleTransferConferenceSuccess',
1042
1176
  'clearTransferConferenceRequested',
1043
- 'emitTaskWrapup',
1044
1177
  ],
1045
1178
  },
1046
1179
  {
@@ -1075,7 +1208,7 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
1075
1208
  // Agent who should wrap up → WRAPPING_UP
1076
1209
  guard: guards.shouldWrapUp,
1077
1210
  target: TaskState.WRAPPING_UP,
1078
- actions: ['updateTaskData', 'markEnded', 'clearConsultState', 'emitTaskWrapup'],
1211
+ actions: ['updateTaskData', 'markEnded', 'clearConsultState'],
1079
1212
  },
1080
1213
  {
1081
1214
  // Customer still in call → CONNECTED
@@ -1113,8 +1246,19 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
1113
1246
  [TaskEvent.TASK_WRAPUP]: {
1114
1247
  guard: guards.shouldWrapUp,
1115
1248
  target: TaskState.WRAPPING_UP,
1116
- actions: ['updateTaskData', 'markEnded', 'clearConsultState', 'emitTaskWrapup'],
1249
+ actions: ['updateTaskData', 'markEnded', 'clearConsultState'],
1117
1250
  },
1251
+ [TaskEvent.OUTBOUND_FAILED]: [
1252
+ {
1253
+ guard: guards.shouldWrapUp,
1254
+ target: TaskState.WRAPPING_UP,
1255
+ actions: ['updateTaskData', 'markEnded', 'emitTaskOutdialFailed', 'emitTaskWrapup'],
1256
+ },
1257
+ {
1258
+ target: TaskState.TERMINATED,
1259
+ actions: ['updateTaskData', 'markEnded', 'emitTaskOutdialFailed', 'emitTaskEnd'],
1260
+ },
1261
+ ],
1118
1262
  },
1119
1263
  },
1120
1264
 
@@ -1122,6 +1266,16 @@ export function getTaskStateMachineConfig(uiControlConfig: UIControlConfig) {
1122
1266
  // Only emit wrapup event on entry - task:end should only be emitted when COMPLETED
1123
1267
  entry: ['emitTaskWrapup'],
1124
1268
  on: {
1269
+ // Late AgentWrapup after conference exit already entered WRAPPING_UP
1270
+ [TaskEvent.TASK_WRAPUP]: [
1271
+ {
1272
+ guard: ({context}) => context.taskData?.wrapUpRequired === true,
1273
+ actions: ['updateTaskData'],
1274
+ },
1275
+ {
1276
+ actions: ['updateTaskData', 'emitTaskWrapup'],
1277
+ },
1278
+ ],
1125
1279
  // AgentWrappedup Event
1126
1280
  [TaskEvent.WRAPUP_COMPLETE]: {
1127
1281
  target: TaskState.COMPLETED,
@@ -515,12 +515,15 @@ export const actions: TaskActionsMap = {
515
515
  }),
516
516
  handleConferenceStarted: assign({consultInitiator: false}),
517
517
 
518
- setConsultDestination: assign(({event}: TaskActionArgs) => {
518
+ setConsultDestination: assign(({context, event}: TaskActionArgs) => {
519
519
  if (!event || event.type !== TaskEvent.CONSULT) {
520
520
  return {};
521
521
  }
522
522
 
523
- const taskData = getTaskDataFromEvent(event);
523
+ // CONSULT is a user action and does not carry taskData. Preserve the current interaction
524
+ // snapshot while recording the destination; replacing it with only destAgentId would remove
525
+ // the main-leg membership needed to correlate an immediately following lifecycle event.
526
+ const taskData = context.taskData;
524
527
  const consultDestinationType =
525
528
  'destinationType' in event ? event.destinationType ?? null : null;
526
529
  const consultDestinationAgentId =
@@ -66,7 +66,7 @@ state-machine/
66
66
  - **State Graph and Transition Rules**: `TaskStateMachine.ts` defines all states, transition tables, and event handlers that drive the task lifecycle.
67
67
  - **Deterministic Context Updates**: `actions.ts` implements XState actions for task context mutation and provides emitter placeholders that `Task` overrides to surface SDK events.
68
68
  - **Transition Eligibility**: `guards.ts` contains pure predicates that gate transitions based on current context, task data, and backend state.
69
- - **UI Controls Computation**: `uiControlsComputer.ts` derives `TaskUIControls` from state and context for voice/digital channels, keeping UI enablement centralized.
69
+ - **UI Controls Computation**: `uiControlsComputer.ts` derives per-leg action controls and ordered Consult/Transfer destination categories from state, Task data, and injected profile policy for voice/digital channels. Consumers read `task.uiControls`; do not add a parallel policy method or UI-side decision tree.
70
70
  - **Typed Event Contracts**: `constants.ts` and `types.ts` define `TaskState`, `TaskEvent`, and the `TaskEventPayloadMap` so transitions and payloads stay type-safe.
71
71
  - **Public Exports**: `index.ts` exposes the state machine factory, event enums, and types for consumption by the task layer.
72
72
 
@@ -266,14 +266,16 @@ shouldWrapUpOrIsInitiator(context, event) {
266
266
  return Boolean(event.taskData?.wrapUpRequired || context.consultInitiator);
267
267
  }
268
268
 
269
- // Check whether the leaving participant is the current agent
270
- didCurrentAgentLeaveConference(context, event) {
269
+ // Check whether the current agent left the main interaction
270
+ didCurrentAgentLeaveMainInteraction(context, event) {
271
271
  const selfAgentId = getSelfAgentId(context, event.taskData);
272
272
  if (!selfAgentId) return false;
273
273
 
274
274
  const participantIdFromEvent = 'participantId' in event ? event.participantId : undefined;
275
275
  const participantId = participantIdFromEvent ?? event.taskData?.participantId;
276
- return Boolean(participantId) && participantId === selfAgentId;
276
+ if (Boolean(participantId) && participantId === selfAgentId) return true;
277
+ // Self missing from the updated participants map is terminal (EP-DN removal).
278
+ // Remaining in the map with hasLeft, or disappearing only from mainCall media, is not.
277
279
  }
278
280
  ```
279
281