@webex/contact-center 3.12.0-next.9 → 3.12.0-task-refactor.2

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 +570 -535
  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 +372 -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 +263 -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 +377 -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 +579 -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 +422 -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 +303 -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 +542 -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,422 @@
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
+ const cpd = taskData.interaction?.callProcessingDetails;
113
+ const backendSaysJoined = cpd?.consultDestinationAgentJoined === 'true';
114
+ if (hasJoinedConsultee || backendSaysJoined)
115
+ updates.consultDestinationAgentJoined = true;
116
+ }
117
+
118
+ if (!context.consultDestinationType && !updates.consultDestinationType) {
119
+ const hasEpDnParticipant = Boolean(
120
+ taskData.interaction.participants &&
121
+ Object.values(taskData.interaction.participants).some(
122
+ (p: any) => p?.pType === 'EP-DN' && !p?.hasLeft
123
+ )
124
+ );
125
+ if (hasEpDnParticipant) updates.consultDestinationType = 'entryPoint' as any;
126
+ }
127
+
128
+ const effectiveConsultInitiator = updates.consultInitiator ?? context.consultInitiator;
129
+ if (effectiveConsultInitiator) {
130
+ const consultMediaId = taskData.consultMediaResourceId;
131
+ const consultMedia: any = consultMediaId
132
+ ? taskData.interaction.media?.[consultMediaId]
133
+ : Object.values(taskData.interaction.media ?? {}).find(
134
+ (m: any) => m?.mType === 'consult'
135
+ );
136
+ if (consultMedia) {
137
+ updates.consultCallHeld = Boolean(consultMedia.isHold);
138
+ }
139
+ }
140
+ }
141
+
142
+ return updates;
143
+ })()
144
+ : {};
145
+
146
+ const getTaskDataFromEvent = (event?: TaskEventPayload): TaskData | undefined =>
147
+ event && typeof event === 'object' ? (event as any).taskData : undefined;
148
+
149
+ export function createInitialContext(
150
+ uiControlConfig: UIControlConfig,
151
+ initialState: TaskState = TaskState.IDLE
152
+ ): TaskContext {
153
+ const baseContext: TaskContext = {
154
+ taskData: null,
155
+ consultInitiator: false,
156
+ exitingConference: false,
157
+ consultFromConference: false,
158
+ transferConferenceRequested: false,
159
+ consultDestinationType: null,
160
+ consultDestinationAgentId: null,
161
+ consultDestinationAgentJoined: false,
162
+ consultCallHeld: false,
163
+ recordingControlsAvailable: false,
164
+ recordingInProgress: false,
165
+ uiControlConfig,
166
+ uiControls: getDefaultUIControls(),
167
+ };
168
+
169
+ baseContext.uiControls = computeUIControls(initialState, baseContext);
170
+
171
+ return baseContext;
172
+ }
173
+
174
+ export function updateUIControls(currentState: TaskState) {
175
+ return assign(({context}: TaskActionArgs) => ({
176
+ uiControls: computeUIControls(currentState, context),
177
+ }));
178
+ }
179
+
180
+ export const actions: TaskActionsMap = {
181
+ initializeTask: assign(({context, event}: {context: TaskContext; event: TaskEventPayload}) => {
182
+ return {
183
+ consultInitiator: false,
184
+ exitingConference: false,
185
+ consultDestinationType: null,
186
+ consultDestinationAgentJoined: false,
187
+ ...deriveTaskDataUpdates(context, getTaskDataFromEvent(event)),
188
+ };
189
+ }),
190
+
191
+ /**
192
+ * Update task data from ASSIGN event
193
+ */
194
+ updateTaskData: assign(({context, event}: TaskActionArgs) => {
195
+ return deriveTaskDataUpdates(context, getTaskDataFromEvent(event));
196
+ }),
197
+
198
+ /**
199
+ * Set consult initiator flag
200
+ *
201
+ * IMPORTANT: This action is called for CONSULT (user action) and CONSULT_CREATED (backend event).
202
+ * For CONSULT (user action): The user explicitly clicked Consult, so they ARE the initiator.
203
+ * For CONSULT_CREATED (backend event): Check taskData.isConsulted to determine if this agent
204
+ * is the initiator. If isConsulted === true, this is Agent B (the consulted party), NOT the initiator.
205
+ *
206
+ * This prevents all agents in a conference from becoming consultInitiator when one agent
207
+ * starts a new consult.
208
+ */
209
+ setConsultInitiator: assign(({event}: TaskActionArgs) => {
210
+ const taskData = getTaskDataFromEvent(event);
211
+
212
+ if (event.type === TaskEvent.CONSULT) return {consultInitiator: true};
213
+
214
+ const selfAgentId = taskData?.agentId;
215
+ const consultInitiator = determineConsultInitiator(taskData, selfAgentId);
216
+ if (consultInitiator === true) return {consultInitiator: true};
217
+ if (consultInitiator === false) return {consultInitiator: false};
218
+
219
+ return {};
220
+ }),
221
+
222
+ handleConsultFailed: assign({consultDestinationAgentJoined: false, consultInitiator: false}),
223
+ handleConferenceStarted: assign({consultInitiator: false}),
224
+
225
+ setConsultDestination: assign(({event}: TaskActionArgs) => {
226
+ if (!event || event.type !== TaskEvent.CONSULT) {
227
+ return {};
228
+ }
229
+
230
+ const taskData = getTaskDataFromEvent(event);
231
+ const consultDestinationType =
232
+ 'destinationType' in event ? event.destinationType ?? null : null;
233
+ const consultDestinationAgentId = 'destAgentId' in event ? event.destAgentId ?? null : null;
234
+
235
+ return {
236
+ consultDestinationType,
237
+ consultDestinationAgentId,
238
+ consultDestinationAgentJoined: false,
239
+ consultFromConference: false,
240
+ taskData: {
241
+ ...taskData,
242
+ destAgentId: consultDestinationAgentId,
243
+ },
244
+ };
245
+ }),
246
+
247
+ setConsultFromConference: assign({consultFromConference: true}),
248
+
249
+ forceConsultInitiator: assign({consultInitiator: true}),
250
+
251
+ setConsultAgentJoined: assign(
252
+ ({context, event}: {context: TaskContext; event: TaskEventPayload}) => {
253
+ if (context.consultDestinationAgentJoined) {
254
+ return {};
255
+ }
256
+
257
+ if (
258
+ !event ||
259
+ event.type !== TaskEvent.CONSULTING_ACTIVE ||
260
+ !('consultDestinationAgentJoined' in event)
261
+ ) {
262
+ return {};
263
+ }
264
+
265
+ const eventValue = (event as {consultDestinationAgentJoined: boolean})
266
+ .consultDestinationAgentJoined;
267
+
268
+ return eventValue ? {consultDestinationAgentJoined: true} : {};
269
+ }
270
+ ),
271
+
272
+ setRecordingState: assign(({event}: TaskActionArgs) => {
273
+ if (!event || !('type' in event)) {
274
+ return {};
275
+ }
276
+
277
+ if (event.type === TaskEvent.PAUSE_RECORDING) {
278
+ return {
279
+ recordingControlsAvailable: true,
280
+ recordingInProgress: false,
281
+ };
282
+ }
283
+ if (event.type === TaskEvent.RESUME_RECORDING) {
284
+ return {
285
+ recordingControlsAvailable: true,
286
+ recordingInProgress: true,
287
+ };
288
+ }
289
+
290
+ return {};
291
+ }),
292
+
293
+ clearConsultState: assign({
294
+ consultDestinationType: null,
295
+ consultDestinationAgentId: null,
296
+ consultDestinationAgentJoined: false,
297
+ consultInitiator: false,
298
+ exitingConference: false,
299
+ consultCallHeld: false,
300
+ consultFromConference: false,
301
+ transferConferenceRequested: false,
302
+ }),
303
+
304
+ setTransferConferenceRequested: assign({transferConferenceRequested: true}),
305
+ clearTransferConferenceRequested: assign({transferConferenceRequested: false}),
306
+
307
+ setConsultCallHeld: assign({consultCallHeld: true}),
308
+ clearConsultCallHeld: assign({consultCallHeld: false}),
309
+ handleSwitchToMainCall: assign({consultCallHeld: true}),
310
+ handleSwitchToConsult: assign({consultCallHeld: false}),
311
+
312
+ handleConferenceFailed: assign(({event}: TaskActionArgs) => {
313
+ const taskData = getTaskDataFromEvent(event);
314
+
315
+ return taskData ? {taskData} : {};
316
+ }),
317
+ handleParticipantLeft: assign(({event}: TaskActionArgs) => {
318
+ const taskData = getTaskDataFromEvent(event);
319
+
320
+ return taskData ? {taskData} : {};
321
+ }),
322
+
323
+ setExitingConference: assign({exitingConference: true}),
324
+
325
+ handleTransferConferenceSuccess: assign(({event}: TaskActionArgs) => {
326
+ const taskData = getTaskDataFromEvent(event);
327
+
328
+ return taskData ? {taskData} : {};
329
+ }),
330
+
331
+ setHoldState: assign(({context, event}: TaskActionArgs) => {
332
+ if (
333
+ !event ||
334
+ (event.type !== TaskEvent.HOLD_SUCCESS && event.type !== TaskEvent.UNHOLD_SUCCESS)
335
+ ) {
336
+ return {};
337
+ }
338
+
339
+ const mediaResourceId =
340
+ 'mediaResourceId' in event
341
+ ? (event as {mediaResourceId?: string}).mediaResourceId
342
+ : undefined;
343
+
344
+ if (!mediaResourceId) {
345
+ return {};
346
+ }
347
+
348
+ const interaction = context.taskData?.interaction;
349
+ const mediaEntry = interaction?.media?.[mediaResourceId];
350
+
351
+ if (!interaction || !mediaEntry) {
352
+ return {};
353
+ }
354
+
355
+ const updatedMedia = {
356
+ ...interaction.media,
357
+ [mediaResourceId]: {
358
+ ...mediaEntry,
359
+ isHold: event.type === TaskEvent.HOLD_SUCCESS,
360
+ },
361
+ };
362
+
363
+ const updates: Partial<TaskContext> = {
364
+ taskData: {
365
+ ...(context.taskData as TaskData),
366
+ interaction: {
367
+ ...interaction,
368
+ media: updatedMedia,
369
+ },
370
+ },
371
+ };
372
+
373
+ const consultMediaId = context.taskData?.consultMediaResourceId;
374
+ const isConsultMedia = consultMediaId
375
+ ? mediaResourceId === consultMediaId
376
+ : mediaEntry?.mType === 'consult';
377
+
378
+ if (isConsultMedia && context.consultInitiator) {
379
+ updates.consultCallHeld = event.type === TaskEvent.HOLD_SUCCESS;
380
+ }
381
+
382
+ return updates;
383
+ }),
384
+
385
+ markEnded: assign(() => ({
386
+ recordingControlsAvailable: false,
387
+ recordingInProgress: false,
388
+ })),
389
+
390
+ requestAutoAnswer: () => undefined,
391
+ requestCleanup: () => undefined,
392
+ cleanupResources: () => undefined,
393
+
394
+ // Event emitters - placeholders overridden by consumers
395
+ emitTaskIncoming: () => undefined,
396
+ emitTaskHydrate: () => undefined,
397
+ emitTaskOfferContact: () => undefined,
398
+ emitTaskAssigned: () => undefined,
399
+ emitTaskHold: () => undefined,
400
+ emitTaskResume: () => undefined,
401
+ emitTaskEnd: () => undefined,
402
+ emitTaskOfferConsult: () => undefined,
403
+ emitTaskConsultCreated: () => undefined,
404
+ emitTaskConsulting: () => undefined,
405
+ emitTaskConsultAccepted: () => undefined,
406
+ emitTaskConsultEnd: () => undefined,
407
+ emitTaskReject: () => undefined,
408
+ emitTaskWrapup: () => undefined,
409
+ emitTaskRecordingStarted: () => undefined,
410
+ emitTaskRecordingPaused: () => undefined,
411
+ emitTaskRecordingResumed: () => undefined,
412
+ emitTaskWrappedup: () => undefined,
413
+ emitTaskParticipantLeft: () => undefined,
414
+ emitTaskConferenceStarted: () => undefined,
415
+ emitTaskConferenceEnded: () => undefined,
416
+ emitTaskExitConference: () => undefined,
417
+ emitTaskTransferConference: () => undefined,
418
+ emitTaskSwitchCall: () => undefined,
419
+ emitTaskConferenceFailed: () => undefined,
420
+ emitTaskTransferConferenceFailed: () => undefined,
421
+ emitTaskOutdialFailed: () => undefined,
422
+ };