@webex/contact-center 3.12.0-next.8 → 3.12.0-task-refactor.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 (200) hide show
  1. package/AGENTS.md +438 -0
  2. package/ai-docs/README.md +131 -0
  3. package/ai-docs/RULES.md +455 -0
  4. package/ai-docs/patterns/event-driven-patterns.md +485 -0
  5. package/ai-docs/patterns/testing-patterns.md +480 -0
  6. package/ai-docs/patterns/typescript-patterns.md +365 -0
  7. package/ai-docs/templates/README.md +102 -0
  8. package/ai-docs/templates/documentation/create-agents-md.md +240 -0
  9. package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
  10. package/ai-docs/templates/existing-service/bug-fix.md +254 -0
  11. package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
  12. package/ai-docs/templates/new-method/00-master.md +80 -0
  13. package/ai-docs/templates/new-method/01-requirements.md +232 -0
  14. package/ai-docs/templates/new-method/02-implementation.md +295 -0
  15. package/ai-docs/templates/new-method/03-tests.md +201 -0
  16. package/ai-docs/templates/new-method/04-validation.md +141 -0
  17. package/ai-docs/templates/new-service/00-master.md +109 -0
  18. package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
  19. package/ai-docs/templates/new-service/02-code-generation.md +346 -0
  20. package/ai-docs/templates/new-service/03-integration.md +178 -0
  21. package/ai-docs/templates/new-service/04-test-generation.md +205 -0
  22. package/ai-docs/templates/new-service/05-validation.md +145 -0
  23. package/dist/cc.js +65 -123
  24. package/dist/cc.js.map +1 -1
  25. package/dist/constants.js +13 -2
  26. package/dist/constants.js.map +1 -1
  27. package/dist/index.js +13 -5
  28. package/dist/index.js.map +1 -1
  29. package/dist/metrics/behavioral-events.js +26 -13
  30. package/dist/metrics/behavioral-events.js.map +1 -1
  31. package/dist/metrics/constants.js +7 -6
  32. package/dist/metrics/constants.js.map +1 -1
  33. package/dist/services/ApiAiAssistant.js +0 -3
  34. package/dist/services/ApiAiAssistant.js.map +1 -1
  35. package/dist/services/config/Util.js +2 -3
  36. package/dist/services/config/Util.js.map +1 -1
  37. package/dist/services/config/types.js +16 -14
  38. package/dist/services/config/types.js.map +1 -1
  39. package/dist/services/constants.js +0 -1
  40. package/dist/services/constants.js.map +1 -1
  41. package/dist/services/core/Err.js.map +1 -1
  42. package/dist/services/core/Utils.js +79 -55
  43. package/dist/services/core/Utils.js.map +1 -1
  44. package/dist/services/core/aqm-reqs.js +17 -92
  45. package/dist/services/core/aqm-reqs.js.map +1 -1
  46. package/dist/services/core/websocket/WebSocketManager.js +5 -25
  47. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  48. package/dist/services/core/websocket/types.js.map +1 -1
  49. package/dist/services/index.js +1 -2
  50. package/dist/services/index.js.map +1 -1
  51. package/dist/services/task/Task.js +644 -0
  52. package/dist/services/task/Task.js.map +1 -0
  53. package/dist/services/task/TaskFactory.js +45 -0
  54. package/dist/services/task/TaskFactory.js.map +1 -0
  55. package/dist/services/task/TaskManager.js +556 -532
  56. package/dist/services/task/TaskManager.js.map +1 -1
  57. package/dist/services/task/TaskUtils.js +132 -28
  58. package/dist/services/task/TaskUtils.js.map +1 -1
  59. package/dist/services/task/constants.js +7 -6
  60. package/dist/services/task/constants.js.map +1 -1
  61. package/dist/services/task/dialer.js +0 -51
  62. package/dist/services/task/dialer.js.map +1 -1
  63. package/dist/services/task/digital/Digital.js +77 -0
  64. package/dist/services/task/digital/Digital.js.map +1 -0
  65. package/dist/services/task/state-machine/TaskStateMachine.js +634 -0
  66. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  67. package/dist/services/task/state-machine/actions.js +366 -0
  68. package/dist/services/task/state-machine/actions.js.map +1 -0
  69. package/dist/services/task/state-machine/constants.js +139 -0
  70. package/dist/services/task/state-machine/constants.js.map +1 -0
  71. package/dist/services/task/state-machine/guards.js +256 -0
  72. package/dist/services/task/state-machine/guards.js.map +1 -0
  73. package/dist/services/task/state-machine/index.js +53 -0
  74. package/dist/services/task/state-machine/index.js.map +1 -0
  75. package/dist/services/task/state-machine/types.js +54 -0
  76. package/dist/services/task/state-machine/types.js.map +1 -0
  77. package/dist/services/task/state-machine/uiControlsComputer.js +369 -0
  78. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  79. package/dist/services/task/taskDataNormalizer.js +99 -0
  80. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  81. package/dist/services/task/types.js +157 -18
  82. package/dist/services/task/types.js.map +1 -1
  83. package/dist/services/task/voice/Voice.js +1031 -0
  84. package/dist/services/task/voice/Voice.js.map +1 -0
  85. package/dist/services/task/voice/WebRTC.js +149 -0
  86. package/dist/services/task/voice/WebRTC.js.map +1 -0
  87. package/dist/types/cc.d.ts +4 -33
  88. package/dist/types/constants.d.ts +13 -2
  89. package/dist/types/index.d.ts +11 -5
  90. package/dist/types/metrics/constants.d.ts +5 -3
  91. package/dist/types/services/ApiAiAssistant.d.ts +1 -1
  92. package/dist/types/services/config/types.d.ts +97 -25
  93. package/dist/types/services/core/Err.d.ts +0 -2
  94. package/dist/types/services/core/Utils.d.ts +25 -23
  95. package/dist/types/services/core/aqm-reqs.d.ts +0 -49
  96. package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -1
  97. package/dist/types/services/core/websocket/connection-service.d.ts +0 -1
  98. package/dist/types/services/core/websocket/types.d.ts +1 -1
  99. package/dist/types/services/index.d.ts +1 -1
  100. package/dist/types/services/task/Task.d.ts +146 -0
  101. package/dist/types/services/task/TaskFactory.d.ts +12 -0
  102. package/dist/types/services/task/TaskUtils.d.ts +39 -8
  103. package/dist/types/services/task/constants.d.ts +5 -4
  104. package/dist/types/services/task/dialer.d.ts +0 -15
  105. package/dist/types/services/task/digital/Digital.d.ts +22 -0
  106. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +906 -0
  107. package/dist/types/services/task/state-machine/actions.d.ts +8 -0
  108. package/dist/types/services/task/state-machine/constants.d.ts +91 -0
  109. package/dist/types/services/task/state-machine/guards.d.ts +78 -0
  110. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  111. package/dist/types/services/task/state-machine/types.d.ts +256 -0
  112. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  113. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  114. package/dist/types/services/task/types.d.ts +539 -88
  115. package/dist/types/services/task/voice/Voice.d.ts +183 -0
  116. package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
  117. package/dist/types/types.d.ts +68 -0
  118. package/dist/types/webex.d.ts +1 -0
  119. package/dist/types.js +70 -0
  120. package/dist/types.js.map +1 -1
  121. package/dist/webex.js +14 -2
  122. package/dist/webex.js.map +1 -1
  123. package/package.json +14 -11
  124. package/src/cc.ts +91 -177
  125. package/src/constants.ts +13 -2
  126. package/src/index.ts +14 -5
  127. package/src/metrics/ai-docs/AGENTS.md +348 -0
  128. package/src/metrics/ai-docs/ARCHITECTURE.md +336 -0
  129. package/src/metrics/behavioral-events.ts +28 -14
  130. package/src/metrics/constants.ts +7 -8
  131. package/src/services/ApiAiAssistant.ts +2 -4
  132. package/src/services/agent/ai-docs/AGENTS.md +238 -0
  133. package/src/services/agent/ai-docs/ARCHITECTURE.md +302 -0
  134. package/src/services/ai-docs/AGENTS.md +384 -0
  135. package/src/services/config/Util.ts +2 -3
  136. package/src/services/config/ai-docs/AGENTS.md +253 -0
  137. package/src/services/config/ai-docs/ARCHITECTURE.md +424 -0
  138. package/src/services/config/types.ts +108 -20
  139. package/src/services/constants.ts +0 -1
  140. package/src/services/core/Err.ts +0 -1
  141. package/src/services/core/Utils.ts +90 -67
  142. package/src/services/core/ai-docs/AGENTS.md +379 -0
  143. package/src/services/core/ai-docs/ARCHITECTURE.md +696 -0
  144. package/src/services/core/aqm-reqs.ts +22 -100
  145. package/src/services/core/websocket/WebSocketManager.ts +4 -23
  146. package/src/services/core/websocket/types.ts +1 -1
  147. package/src/services/index.ts +1 -2
  148. package/src/services/task/Task.ts +785 -0
  149. package/src/services/task/TaskFactory.ts +55 -0
  150. package/src/services/task/TaskManager.ts +567 -633
  151. package/src/services/task/TaskUtils.ts +175 -31
  152. package/src/services/task/ai-docs/AGENTS.md +448 -0
  153. package/src/services/task/ai-docs/ARCHITECTURE.md +573 -0
  154. package/src/services/task/constants.ts +5 -4
  155. package/src/services/task/dialer.ts +1 -56
  156. package/src/services/task/digital/Digital.ts +95 -0
  157. package/src/services/task/state-machine/TaskStateMachine.ts +793 -0
  158. package/src/services/task/state-machine/actions.ts +409 -0
  159. package/src/services/task/state-machine/ai-docs/AGENTS.md +495 -0
  160. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1135 -0
  161. package/src/services/task/state-machine/constants.ts +150 -0
  162. package/src/services/task/state-machine/guards.ts +295 -0
  163. package/src/services/task/state-machine/index.ts +28 -0
  164. package/src/services/task/state-machine/types.ts +228 -0
  165. package/src/services/task/state-machine/uiControlsComputer.ts +529 -0
  166. package/src/services/task/taskDataNormalizer.ts +137 -0
  167. package/src/services/task/types.ts +641 -95
  168. package/src/services/task/voice/Voice.ts +1255 -0
  169. package/src/services/task/voice/WebRTC.ts +187 -0
  170. package/src/types.ts +88 -5
  171. package/src/utils/AGENTS.md +276 -0
  172. package/src/webex.js +2 -0
  173. package/test/unit/spec/cc.ts +59 -142
  174. package/test/unit/spec/logger-proxy.ts +70 -0
  175. package/test/unit/spec/services/ApiAiAssistant.ts +17 -0
  176. package/test/unit/spec/services/config/index.ts +26 -55
  177. package/test/unit/spec/services/core/Utils.ts +103 -52
  178. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +48 -112
  179. package/test/unit/spec/services/core/websocket/connection-service.ts +5 -4
  180. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  181. package/test/unit/spec/services/task/Task.ts +416 -0
  182. package/test/unit/spec/services/task/TaskFactory.ts +62 -0
  183. package/test/unit/spec/services/task/TaskManager.ts +781 -1735
  184. package/test/unit/spec/services/task/TaskUtils.ts +125 -0
  185. package/test/unit/spec/services/task/dialer.ts +112 -198
  186. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  187. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +473 -0
  188. package/test/unit/spec/services/task/state-machine/guards.ts +288 -0
  189. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  190. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +147 -0
  191. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  192. package/test/unit/spec/services/task/voice/Voice.ts +587 -0
  193. package/test/unit/spec/services/task/voice/WebRTC.ts +242 -0
  194. package/umd/contact-center.min.js +2 -2
  195. package/umd/contact-center.min.js.map +1 -1
  196. package/dist/services/task/index.js +0 -1525
  197. package/dist/services/task/index.js.map +0 -1
  198. package/dist/types/services/task/index.d.ts +0 -650
  199. package/src/services/task/index.ts +0 -1801
  200. package/test/unit/spec/services/task/index.ts +0 -2184
@@ -0,0 +1,409 @@
1
+ /**
2
+ * Task State Machine Actions - Action implementations executed during state transitions
3
+ */
4
+
5
+ import {assign} from 'xstate';
6
+ import {
7
+ TaskContext,
8
+ TaskEventPayload,
9
+ UIControlConfig,
10
+ TaskActionsMap,
11
+ TaskActionArgs,
12
+ RecordingStateUpdate,
13
+ } from './types';
14
+ import {TaskEvent, TaskState} from './constants';
15
+ import {DestinationType, TaskData} from '../types';
16
+ import {computeUIControls, getDefaultUIControls} from './uiControlsComputer';
17
+
18
+ const determineConsultInitiator = (
19
+ taskData: TaskData | undefined,
20
+ selfAgentId: string | undefined
21
+ ): boolean | undefined => {
22
+ if (!selfAgentId) return undefined;
23
+
24
+ if (taskData?.consultingAgentId) {
25
+ return taskData.consultingAgentId === selfAgentId;
26
+ }
27
+
28
+ if (taskData?.isConsulted === true) return false;
29
+
30
+ return undefined;
31
+ };
32
+
33
+ const deriveRecordingState = (taskData?: TaskData | null): RecordingStateUpdate => {
34
+ const callProcessingDetails = taskData?.interaction?.callProcessingDetails;
35
+
36
+ if (!callProcessingDetails) {
37
+ return {};
38
+ }
39
+
40
+ const update: RecordingStateUpdate = {};
41
+ const {recordingStarted, recordInProgress, isPaused} = callProcessingDetails as {
42
+ recordingStarted?: boolean;
43
+ recordInProgress?: boolean;
44
+ isPaused?: boolean;
45
+ };
46
+
47
+ if (recordingStarted !== undefined) {
48
+ update.recordingControlsAvailable = recordingStarted;
49
+ if (!recordingStarted) {
50
+ update.recordingInProgress = false;
51
+ }
52
+ }
53
+
54
+ if (recordInProgress !== undefined) {
55
+ update.recordingControlsAvailable = recordInProgress || recordingStarted || false;
56
+ update.recordingInProgress = recordInProgress;
57
+ }
58
+
59
+ if (
60
+ update.recordingControlsAvailable === undefined &&
61
+ update.recordingInProgress === undefined &&
62
+ recordingStarted
63
+ ) {
64
+ update.recordingControlsAvailable = true;
65
+ update.recordingInProgress = true;
66
+ }
67
+
68
+ if (isPaused !== undefined) {
69
+ update.recordingControlsAvailable = true;
70
+ update.recordingInProgress = !isPaused;
71
+ }
72
+
73
+ return update;
74
+ };
75
+
76
+ const deriveTaskDataUpdates = (context: TaskContext, taskData: TaskData | undefined) =>
77
+ taskData
78
+ ? (() => {
79
+ const updates: Partial<TaskContext> = {
80
+ taskData,
81
+ ...deriveRecordingState(taskData),
82
+ };
83
+
84
+ if (taskData.destAgentId) {
85
+ updates.consultDestinationAgentId = taskData.destAgentId;
86
+ }
87
+ if (taskData.interaction?.state === 'consulting' && taskData.destinationType) {
88
+ updates.consultDestinationType = taskData.destinationType as DestinationType;
89
+ }
90
+
91
+ if (!context.consultInitiator) {
92
+ const selfAgentId = context.uiControlConfig.agentId ?? taskData?.agentId;
93
+ const consultInitiator = determineConsultInitiator(taskData, selfAgentId);
94
+ if (consultInitiator !== undefined) {
95
+ updates.consultInitiator = consultInitiator;
96
+ } else if (
97
+ taskData.interaction?.state === 'consulting' &&
98
+ taskData.isConsulted === false
99
+ ) {
100
+ updates.consultInitiator = true;
101
+ }
102
+ }
103
+
104
+ if (taskData.interaction?.state === 'consulting') {
105
+ if (!context.consultDestinationAgentJoined) {
106
+ const hasJoinedConsultee = Boolean(
107
+ taskData.interaction.participants &&
108
+ Object.values(taskData.interaction.participants).some(
109
+ (p: any) => p?.isConsulted === true && !p?.hasLeft
110
+ )
111
+ );
112
+ if (hasJoinedConsultee) updates.consultDestinationAgentJoined = true;
113
+ }
114
+
115
+ const effectiveConsultInitiator = updates.consultInitiator ?? context.consultInitiator;
116
+ if (effectiveConsultInitiator) {
117
+ const consultMediaId = taskData.consultMediaResourceId;
118
+ const consultMedia: any = consultMediaId
119
+ ? taskData.interaction.media?.[consultMediaId]
120
+ : Object.values(taskData.interaction.media ?? {}).find(
121
+ (m: any) => m?.mType === 'consult'
122
+ );
123
+ if (consultMedia) {
124
+ updates.consultCallHeld = Boolean(consultMedia.isHold);
125
+ }
126
+ }
127
+ }
128
+
129
+ return updates;
130
+ })()
131
+ : {};
132
+
133
+ const getTaskDataFromEvent = (event?: TaskEventPayload): TaskData | undefined =>
134
+ event && typeof event === 'object' ? (event as any).taskData : undefined;
135
+
136
+ export function createInitialContext(
137
+ uiControlConfig: UIControlConfig,
138
+ initialState: TaskState = TaskState.IDLE
139
+ ): TaskContext {
140
+ const baseContext: TaskContext = {
141
+ taskData: null,
142
+ consultInitiator: false,
143
+ exitingConference: false,
144
+ consultFromConference: false,
145
+ transferConferenceRequested: false,
146
+ consultDestinationType: null,
147
+ consultDestinationAgentId: null,
148
+ consultDestinationAgentJoined: false,
149
+ consultCallHeld: false,
150
+ recordingControlsAvailable: false,
151
+ recordingInProgress: false,
152
+ uiControlConfig,
153
+ uiControls: getDefaultUIControls(),
154
+ };
155
+
156
+ baseContext.uiControls = computeUIControls(initialState, baseContext);
157
+
158
+ return baseContext;
159
+ }
160
+
161
+ export function updateUIControls(currentState: TaskState) {
162
+ return assign(({context}: TaskActionArgs) => ({
163
+ uiControls: computeUIControls(currentState, context),
164
+ }));
165
+ }
166
+
167
+ export const actions: TaskActionsMap = {
168
+ initializeTask: assign(({context, event}: {context: TaskContext; event: TaskEventPayload}) => {
169
+ return {
170
+ consultInitiator: false,
171
+ exitingConference: false,
172
+ consultDestinationType: null,
173
+ consultDestinationAgentJoined: false,
174
+ ...deriveTaskDataUpdates(context, getTaskDataFromEvent(event)),
175
+ };
176
+ }),
177
+
178
+ /**
179
+ * Update task data from ASSIGN event
180
+ */
181
+ updateTaskData: assign(({context, event}: TaskActionArgs) => {
182
+ return deriveTaskDataUpdates(context, getTaskDataFromEvent(event));
183
+ }),
184
+
185
+ /**
186
+ * Set consult initiator flag
187
+ *
188
+ * IMPORTANT: This action is called for CONSULT (user action) and CONSULT_CREATED (backend event).
189
+ * For CONSULT (user action): The user explicitly clicked Consult, so they ARE the initiator.
190
+ * For CONSULT_CREATED (backend event): Check taskData.isConsulted to determine if this agent
191
+ * is the initiator. If isConsulted === true, this is Agent B (the consulted party), NOT the initiator.
192
+ *
193
+ * This prevents all agents in a conference from becoming consultInitiator when one agent
194
+ * starts a new consult.
195
+ */
196
+ setConsultInitiator: assign(({event}: TaskActionArgs) => {
197
+ const taskData = getTaskDataFromEvent(event);
198
+
199
+ if (event.type === TaskEvent.CONSULT) return {consultInitiator: true};
200
+
201
+ const selfAgentId = taskData?.agentId;
202
+ const consultInitiator = determineConsultInitiator(taskData, selfAgentId);
203
+ if (consultInitiator === true) return {consultInitiator: true};
204
+ if (consultInitiator === false) return {consultInitiator: false};
205
+
206
+ return {};
207
+ }),
208
+
209
+ handleConsultFailed: assign({consultDestinationAgentJoined: false, consultInitiator: false}),
210
+ handleConferenceStarted: assign({consultInitiator: false}),
211
+
212
+ setConsultDestination: assign(({event}: TaskActionArgs) => {
213
+ if (!event || event.type !== TaskEvent.CONSULT) {
214
+ return {};
215
+ }
216
+
217
+ const taskData = getTaskDataFromEvent(event);
218
+ const consultDestinationType =
219
+ 'destinationType' in event ? event.destinationType ?? null : null;
220
+ const consultDestinationAgentId = 'destAgentId' in event ? event.destAgentId ?? null : null;
221
+
222
+ return {
223
+ consultDestinationType,
224
+ consultDestinationAgentId,
225
+ consultDestinationAgentJoined: false,
226
+ consultFromConference: false,
227
+ taskData: {
228
+ ...taskData,
229
+ destAgentId: consultDestinationAgentId,
230
+ },
231
+ };
232
+ }),
233
+
234
+ setConsultFromConference: assign({consultFromConference: true}),
235
+
236
+ forceConsultInitiator: assign({consultInitiator: true}),
237
+
238
+ setConsultAgentJoined: assign(
239
+ ({context, event}: {context: TaskContext; event: TaskEventPayload}) => {
240
+ if (context.consultDestinationAgentJoined) {
241
+ return {};
242
+ }
243
+
244
+ if (
245
+ !event ||
246
+ event.type !== TaskEvent.CONSULTING_ACTIVE ||
247
+ !('consultDestinationAgentJoined' in event)
248
+ ) {
249
+ return {};
250
+ }
251
+
252
+ const eventValue = (event as {consultDestinationAgentJoined: boolean})
253
+ .consultDestinationAgentJoined;
254
+
255
+ return eventValue ? {consultDestinationAgentJoined: true} : {};
256
+ }
257
+ ),
258
+
259
+ setRecordingState: assign(({event}: TaskActionArgs) => {
260
+ if (!event || !('type' in event)) {
261
+ return {};
262
+ }
263
+
264
+ if (event.type === TaskEvent.PAUSE_RECORDING) {
265
+ return {
266
+ recordingControlsAvailable: true,
267
+ recordingInProgress: false,
268
+ };
269
+ }
270
+ if (event.type === TaskEvent.RESUME_RECORDING) {
271
+ return {
272
+ recordingControlsAvailable: true,
273
+ recordingInProgress: true,
274
+ };
275
+ }
276
+
277
+ return {};
278
+ }),
279
+
280
+ clearConsultState: assign({
281
+ consultDestinationType: null,
282
+ consultDestinationAgentId: null,
283
+ consultDestinationAgentJoined: false,
284
+ consultInitiator: false,
285
+ exitingConference: false,
286
+ consultCallHeld: false,
287
+ consultFromConference: false,
288
+ transferConferenceRequested: false,
289
+ }),
290
+
291
+ setTransferConferenceRequested: assign({transferConferenceRequested: true}),
292
+ clearTransferConferenceRequested: assign({transferConferenceRequested: false}),
293
+
294
+ setConsultCallHeld: assign({consultCallHeld: true}),
295
+ clearConsultCallHeld: assign({consultCallHeld: false}),
296
+ handleSwitchToMainCall: assign({consultCallHeld: true}),
297
+ handleSwitchToConsult: assign({consultCallHeld: false}),
298
+
299
+ handleConferenceFailed: assign(({event}: TaskActionArgs) => {
300
+ const taskData = getTaskDataFromEvent(event);
301
+
302
+ return taskData ? {taskData} : {};
303
+ }),
304
+ handleParticipantLeft: assign(({event}: TaskActionArgs) => {
305
+ const taskData = getTaskDataFromEvent(event);
306
+
307
+ return taskData ? {taskData} : {};
308
+ }),
309
+
310
+ setExitingConference: assign({exitingConference: true}),
311
+
312
+ handleTransferConferenceSuccess: assign(({event}: TaskActionArgs) => {
313
+ const taskData = getTaskDataFromEvent(event);
314
+
315
+ return taskData ? {taskData} : {};
316
+ }),
317
+
318
+ setHoldState: assign(({context, event}: TaskActionArgs) => {
319
+ if (
320
+ !event ||
321
+ (event.type !== TaskEvent.HOLD_SUCCESS && event.type !== TaskEvent.UNHOLD_SUCCESS)
322
+ ) {
323
+ return {};
324
+ }
325
+
326
+ const mediaResourceId =
327
+ 'mediaResourceId' in event
328
+ ? (event as {mediaResourceId?: string}).mediaResourceId
329
+ : undefined;
330
+
331
+ if (!mediaResourceId) {
332
+ return {};
333
+ }
334
+
335
+ const interaction = context.taskData?.interaction;
336
+ const mediaEntry = interaction?.media?.[mediaResourceId];
337
+
338
+ if (!interaction || !mediaEntry) {
339
+ return {};
340
+ }
341
+
342
+ const updatedMedia = {
343
+ ...interaction.media,
344
+ [mediaResourceId]: {
345
+ ...mediaEntry,
346
+ isHold: event.type === TaskEvent.HOLD_SUCCESS,
347
+ },
348
+ };
349
+
350
+ const updates: Partial<TaskContext> = {
351
+ taskData: {
352
+ ...(context.taskData as TaskData),
353
+ interaction: {
354
+ ...interaction,
355
+ media: updatedMedia,
356
+ },
357
+ },
358
+ };
359
+
360
+ const consultMediaId = context.taskData?.consultMediaResourceId;
361
+ const isConsultMedia = consultMediaId
362
+ ? mediaResourceId === consultMediaId
363
+ : mediaEntry?.mType === 'consult';
364
+
365
+ if (isConsultMedia && context.consultInitiator) {
366
+ updates.consultCallHeld = event.type === TaskEvent.HOLD_SUCCESS;
367
+ }
368
+
369
+ return updates;
370
+ }),
371
+
372
+ markEnded: assign(() => ({
373
+ recordingControlsAvailable: false,
374
+ recordingInProgress: false,
375
+ })),
376
+
377
+ requestAutoAnswer: () => undefined,
378
+ requestCleanup: () => undefined,
379
+ cleanupResources: () => undefined,
380
+
381
+ // Event emitters - placeholders overridden by consumers
382
+ emitTaskIncoming: () => undefined,
383
+ emitTaskHydrate: () => undefined,
384
+ emitTaskOfferContact: () => undefined,
385
+ emitTaskAssigned: () => undefined,
386
+ emitTaskHold: () => undefined,
387
+ emitTaskResume: () => undefined,
388
+ emitTaskEnd: () => undefined,
389
+ emitTaskOfferConsult: () => undefined,
390
+ emitTaskConsultCreated: () => undefined,
391
+ emitTaskConsulting: () => undefined,
392
+ emitTaskConsultAccepted: () => undefined,
393
+ emitTaskConsultEnd: () => undefined,
394
+ emitTaskReject: () => undefined,
395
+ emitTaskWrapup: () => undefined,
396
+ emitTaskRecordingStarted: () => undefined,
397
+ emitTaskRecordingPaused: () => undefined,
398
+ emitTaskRecordingResumed: () => undefined,
399
+ emitTaskWrappedup: () => undefined,
400
+ emitTaskParticipantLeft: () => undefined,
401
+ emitTaskConferenceStarted: () => undefined,
402
+ emitTaskConferenceEnded: () => undefined,
403
+ emitTaskExitConference: () => undefined,
404
+ emitTaskTransferConference: () => undefined,
405
+ emitTaskSwitchCall: () => undefined,
406
+ emitTaskConferenceFailed: () => undefined,
407
+ emitTaskTransferConferenceFailed: () => undefined,
408
+ emitTaskOutdialFailed: () => undefined,
409
+ };