@webex/contact-center 3.12.0-next.9 → 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
@@ -1,8 +1,127 @@
1
1
  /* eslint-disable import/prefer-default-export */
2
2
  import {Interaction, ITask, TaskData, MEDIA_CHANNEL} from './types';
3
- import {CC_EVENTS} from '../config/types';
4
3
  import {OUTDIAL_DIRECTION, OUTDIAL_MEDIA_TYPE, OUTBOUND_TYPE} from '../../constants';
5
4
  import {LoginOption} from '../../types';
5
+ import {PARTICIPANT_TYPE, MEDIA_TYPE_MAIN_CALL} from './state-machine/constants';
6
+ import {TaskContext} from './state-machine/types';
7
+
8
+ /**
9
+ * Checks if the customer is still in the call (not left)
10
+ *
11
+ * @param interaction - The interaction object
12
+ * @param interactionId - The main interaction ID
13
+ * @returns true if customer is in the call
14
+ */
15
+ export const getIsCustomerInCall = (interaction: Interaction, interactionId: string): boolean => {
16
+ const mainCallMedia = interaction.media[interactionId];
17
+ if (!mainCallMedia?.participants) {
18
+ return false;
19
+ }
20
+
21
+ return mainCallMedia.participants.some((participantId: string) => {
22
+ const participant = interaction.participants[participantId];
23
+
24
+ return participant?.pType === PARTICIPANT_TYPE.CUSTOMER && !participant.hasLeft;
25
+ });
26
+ };
27
+
28
+ /**
29
+ * Gets the count of active agent participants in the conference
30
+ * Excludes Customer, Supervisor, and VVA participant types
31
+ *
32
+ * @param interaction - The interaction object
33
+ * @param interactionId - The main interaction ID
34
+ * @returns Number of active agent participants
35
+ */
36
+ export const getConferenceParticipantsCount = (
37
+ interaction: Interaction,
38
+ interactionId: string
39
+ ): number => {
40
+ const mainCallMedia = interaction.media[interactionId];
41
+ if (!mainCallMedia?.participants) {
42
+ return 0;
43
+ }
44
+
45
+ let count = 0;
46
+ for (const participantId of mainCallMedia.participants) {
47
+ const participant = interaction.participants[participantId];
48
+ if (
49
+ participant &&
50
+ participant.pType !== PARTICIPANT_TYPE.CUSTOMER &&
51
+ participant.pType !== PARTICIPANT_TYPE.SUPERVISOR &&
52
+ participant.pType !== PARTICIPANT_TYPE.VVA &&
53
+ !participant.hasLeft
54
+ ) {
55
+ count += 1;
56
+ }
57
+ }
58
+
59
+ return count;
60
+ };
61
+
62
+ /**
63
+ * Determines if a consult is actively in-progress for conference control gating.
64
+ * This is used to disable conference controls (End/Consult) only when a consult leg
65
+ * still exists outside the main call participants.
66
+ */
67
+ export const getIsConsultInProgressForConferenceControls = (
68
+ interaction: Interaction | undefined,
69
+ mainCallId: string | undefined,
70
+ selfAgentId: string | undefined
71
+ ): boolean => {
72
+ if (!interaction || !mainCallId) return false;
73
+
74
+ const mainParticipants = interaction.media?.[mainCallId]?.participants;
75
+ if (!Array.isArray(mainParticipants) || mainParticipants.length === 0) return false;
76
+
77
+ const mainSet = new Set(mainParticipants);
78
+ const media = interaction.media;
79
+ if (!media) return false;
80
+
81
+ return Object.values(media).some((m: any) => {
82
+ if (!m || m.mType !== 'consult') return false;
83
+ if (!Array.isArray(m.participants) || m.participants.length === 0) return false;
84
+
85
+ return m.participants.some((participantId: string) => {
86
+ const p: any = interaction.participants?.[participantId];
87
+ if (!p || p.hasLeft) return false;
88
+ if (selfAgentId && participantId === selfAgentId) return false;
89
+
90
+ const consultLegActive =
91
+ p.consultState === 'consulting' ||
92
+ p.isConsulted === true ||
93
+ p.currentState === 'consulting';
94
+
95
+ return consultLegActive && !mainSet.has(participantId);
96
+ });
97
+ });
98
+ };
99
+
100
+ export const getIsConsultedAgentForControls = (
101
+ taskData: TaskData | null,
102
+ context: TaskContext,
103
+ isConsultingState: boolean
104
+ ): boolean => {
105
+ return Boolean(taskData?.isConsulted) || (isConsultingState && !context.consultInitiator);
106
+ };
107
+
108
+ export const getServerHoldStateForControls = (
109
+ context: TaskContext,
110
+ mainCallId?: string,
111
+ fallbackTaskData?: TaskData | null
112
+ ): boolean | undefined => {
113
+ const media = context.taskData?.interaction?.media ?? fallbackTaskData?.interaction?.media;
114
+ if (!media) return undefined;
115
+
116
+ if (mainCallId && media[mainCallId]) {
117
+ return media[mainCallId].isHold ?? false;
118
+ }
119
+
120
+ const mediaId = context.taskData?.mediaResourceId ?? fallbackTaskData?.mediaResourceId;
121
+ if (!mediaId) return undefined;
122
+
123
+ return media[mediaId]?.isHold;
124
+ };
6
125
 
7
126
  /**
8
127
  * Determines if the given agent is the primary agent (owner) of the task
@@ -32,7 +151,9 @@ export const isParticipantInMainInteraction = (task: ITask, agentId: string): bo
32
151
 
33
152
  return Object.values(task.data.interaction.media).some(
34
153
  (mediaObj) =>
35
- mediaObj && mediaObj.mType === 'mainCall' && mediaObj.participants?.includes(agentId)
154
+ mediaObj &&
155
+ mediaObj.mType === MEDIA_TYPE_MAIN_CALL &&
156
+ mediaObj.participants?.includes(agentId)
36
157
  );
37
158
  };
38
159
 
@@ -56,29 +177,33 @@ export const checkParticipantNotInInteraction = (task: ITask, agentId: string):
56
177
 
57
178
  /**
58
179
  * Determines if a conference is currently in progress based on the number of active agent participants
59
- * @param TaskData - The payLoad data to check for conference status
180
+ * @param data - The task data to check for conference status
60
181
  * @returns true if there are 2 or more active agent participants in the main call, false otherwise
182
+ *
183
+ * For Agent B (consulted agent), their task's interactionId may be different from the main call.
184
+ * We use mainInteractionId from the interaction if available, otherwise fallback to interactionId.
61
185
  */
62
186
  export const getIsConferenceInProgress = (data: TaskData): boolean => {
63
- const mediaMainCall = data.interaction.media?.[data?.interactionId];
187
+ if (!data.interaction) return false;
188
+
189
+ const mainCallId = data.interaction.mainInteractionId || data.interactionId;
190
+ const mediaMainCall = data.interaction.media?.[mainCallId];
64
191
  const participantsInMainCall = new Set(mediaMainCall?.participants);
65
- const participants = data.interaction.participants;
192
+ const {participants} = data.interaction;
66
193
 
67
194
  const agentParticipants = new Set();
68
- if (participantsInMainCall.size > 0) {
69
- participantsInMainCall.forEach((participantId: string) => {
70
- const participant = participants?.[participantId];
71
- if (
72
- participant &&
73
- participant.pType !== 'Customer' &&
74
- participant.pType !== 'Supervisor' &&
75
- !participant.hasLeft &&
76
- participant.pType !== 'VVA'
77
- ) {
78
- agentParticipants.add(participantId);
79
- }
80
- });
81
- }
195
+ participantsInMainCall.forEach((participantId: string) => {
196
+ const participant = participants[participantId];
197
+ if (
198
+ participant &&
199
+ participant.pType !== PARTICIPANT_TYPE.CUSTOMER &&
200
+ participant.pType !== PARTICIPANT_TYPE.SUPERVISOR &&
201
+ participant.pType !== PARTICIPANT_TYPE.VVA &&
202
+ !participant.hasLeft
203
+ ) {
204
+ agentParticipants.add(participantId);
205
+ }
206
+ });
82
207
 
83
208
  return agentParticipants.size >= 2;
84
209
  };
@@ -104,14 +229,10 @@ export const isSecondaryAgent = (interaction: Interaction): boolean => {
104
229
  /**
105
230
  * Checks if the current agent is a secondary EP-DN (Entry Point Dial Number) agent.
106
231
  * This is specifically for telephony consultations to external numbers/entry points.
107
- * @param task - The task object containing interaction details
232
+ * @param interaction - The interaction object
108
233
  * @returns true if this is a secondary EP-DN agent in telephony consultation, false otherwise
109
234
  */
110
235
  export const isSecondaryEpDnAgent = (interaction: Interaction): boolean => {
111
- if (!interaction) {
112
- return false;
113
- }
114
-
115
236
  return interaction.mediaType === 'telephony' && isSecondaryAgent(interaction);
116
237
  };
117
238
 
@@ -222,12 +343,35 @@ export const shouldAutoAnswerTask = (
222
343
  };
223
344
 
224
345
  /**
225
- * Checks if a task is a campaign preview reservation that has not yet been accepted.
226
- * Campaign preview tasks should not trigger incoming call handling until the agent
227
- * explicitly accepts the preview contact.
228
- * @param task - The task to check
229
- * @returns true if the task is a pending campaign preview reservation, false otherwise
346
+ * Gets the consult media resource ID for switch-call operations.
347
+ * Searches for the consult media leg in the interaction.
348
+ *
349
+ * @param interaction - The interaction object
350
+ * @param consultMediaResourceId - The consult media resource ID from task data
351
+ * @param agentId - Current agent ID
352
+ * @returns The consult media resource ID or undefined
230
353
  */
231
- export const isCampaignPreviewReservation = (task: ITask): boolean => {
232
- return task?.data?.type === CC_EVENTS.AGENT_OFFER_CAMPAIGN_RESERVATION;
354
+ export const getConsultMediaResourceId = (
355
+ interaction: Interaction | undefined,
356
+ consultMediaResourceId: string | undefined,
357
+ agentId: string | undefined
358
+ ): string | undefined => {
359
+ // First priority: use consultMediaResourceId from task data if available
360
+ if (consultMediaResourceId) {
361
+ return consultMediaResourceId;
362
+ }
363
+
364
+ // Second priority: search for consult media leg in interaction.media
365
+ if (!interaction?.media || !agentId) {
366
+ return undefined;
367
+ }
368
+
369
+ // Find the consult media leg where this agent is a participant
370
+ for (const [mediaId, media] of Object.entries(interaction.media)) {
371
+ if (media.mType === 'consult' && media.participants?.includes(agentId)) {
372
+ return mediaId;
373
+ }
374
+ }
375
+
376
+ return undefined;
233
377
  };
@@ -0,0 +1,448 @@
1
+ # Task Service - AI Agent Guide
2
+
3
+ ## Purpose
4
+
5
+ Manage task lifecycle including inbound/outbound calls, hold/resume, consult, transfer, conference, and wrapup.
6
+
7
+ ---
8
+
9
+ ## File Structure
10
+
11
+ ```
12
+ services/task/
13
+ ├── Task.ts # Task class (ITask implementation)
14
+ ├── TaskManager.ts # Singleton task manager
15
+ ├── contact.ts # Contact operations (AQM)
16
+ ├── dialer.ts # Outbound dialing (AQM)
17
+ ├── AutoWrapup.ts # Auto wrapup handler
18
+ ├── TaskUtils.ts # Helper functions
19
+ ├── types.ts # Task types and events
20
+ ├── constants.ts # Task constants
21
+ ├── TaskFactory.ts # Task factory
22
+ ├── taskDataNormalizer.ts # Task data normalization helpers
23
+ ├── digital/ # Digital task implementations
24
+ │ └── Digital.ts
25
+ ├── voice/ # Voice task implementations
26
+ │ ├── Voice.ts
27
+ │ └── WebRTC.ts
28
+ ├── state-machine/ # XState task lifecycle engine
29
+ │ ├── TaskStateMachine.ts
30
+ │ ├── actions.ts
31
+ │ ├── guards.ts
32
+ │ ├── uiControlsComputer.ts
33
+ │ ├── constants.ts
34
+ │ ├── types.ts
35
+ │ └── ai-docs/
36
+ │ ├── AGENTS.md
37
+ │ └── ARCHITECTURE.md
38
+ └── ai-docs/
39
+ ├── AGENTS.md # Usage documentation
40
+ └── ARCHITECTURE.md # Task service architecture
41
+ ```
42
+
43
+ ## Source of Truth
44
+
45
+ - Task creation: `TaskFactory.ts`
46
+ - Task APIs and behavior: `Task.ts`, `voice/Voice.ts`, `voice/WebRTC.ts`, `digital/Digital.ts`
47
+ - Task management: `TaskManager.ts`
48
+ - Shared task types: `types.ts`, `constants.ts`
49
+ - Task lifecycle state machine: `state-machine/TaskStateMachine.ts`
50
+ - State machine types/events: `state-machine/constants.ts`, `state-machine/types.ts`
51
+
52
+ ## Public Types and Constants
53
+
54
+ - `TASK_EVENTS` enum (`types.ts`)
55
+ - `TaskData`, `TaskId`, `TaskResponse`, `TaskUIControls` (`types.ts`)
56
+ - `ITask`, `IVoice`, `IWebRTC`, `IDigital` (`types.ts`)
57
+ - `MEDIA_CHANNEL`, `TASK_CHANNEL_TYPE`, `VOICE_VARIANT` (`types.ts`)
58
+ - State machine: `TaskState`, `TaskEvent` (`state-machine/constants.ts`)
59
+
60
+ ## Key Capabilities
61
+
62
+ - **Task Creation by Channel**: `TaskFactory.ts` chooses `WebRTC`, `Voice`, or `Digital` based on `MEDIA_CHANNEL` and `webCallingService.loginOption`, so each task class exposes the correct capabilities for the media type.
63
+ - **Task Orchestration**: `TaskManager.ts` owns task lifecycle wiring—initializes listeners, receives task events, creates/updates tasks, emits SDK events, and exposes task collections for consumers.
64
+ - **Event Emission and Public APIs**: Task objects register listeners, update context, emit SDK events (e.g., `task:*`), and expose public methods that delegate to `contact.ts` for call control and to the state machine for transition validation.
65
+ - **AQM Contact Operations**: `contact.ts` builds the AQM request surface for call control (accept, hold, consult, transfer, wrapup, end) and is the primary bridge from `Task`/`Voice`/`WebRTC`/`Digital` methods to WCC task APIs.
66
+ - **Outbound Dialing**: `dialer.ts` exposes the AQM dialer request (`startOutdial`) used by `cc.startOutdial()` to create outbound voice tasks with success/failure event mapping.
67
+ - **State Machine Driven UI Controls**: The `state-machine/` folder provides the XState engine (`TaskStateMachine.ts`) plus `actions.ts`, `guards.ts`, `uiControlsComputer.ts`, `constants.ts`, and `types.ts` to compute valid transitions and UI control state. Capability-level details live in `state-machine/ai-docs/AGENTS.md`.
68
+
69
+ ---
70
+
71
+ ## Task Layer Overview
72
+
73
+ This section describes how the task layer constructs tasks, initializes the state machine, and wires AQM calls to task methods. It provides context for how the state machine fits into the end-to-end flow.
74
+
75
+ ### Task Class Hierarchy
76
+
77
+ - **Hierarchy**: `Task` (base) → `Voice` → `WebRTC`; `Digital` extends `Task`.
78
+ - **`Task` (base)**: Holds task data, emits SDK events, and provides default (unsupported) implementations for call control APIs.
79
+ - **`Voice`**: Adds hold/resume and consult-related capabilities for telephony tasks.
80
+ - **`WebRTC`**: Overrides `accept/decline` for WebRTC calls and hooks media events.
81
+ - **`Digital`**: Implements `accept` and refreshes digital task data/UI controls.
82
+
83
+ ### Task Creation and State Machine Initialization
84
+
85
+ - **Factory**: `TaskFactory.ts` selects `WebRTC`, `Voice`, or `Digital` based on `MEDIA_CHANNEL` and `webCallingService.loginOption`.
86
+ - **Initialization**: `Task.ts` creates a state machine actor using `createTaskStateMachine(...)`, wires action overrides (emitters), and starts the actor.
87
+ - **Task State**: The task holds `stateMachineService` and uses it to send `TaskEvent` payloads.
88
+
89
+ Example (state machine init inside a task object):
90
+
91
+ ```typescript
92
+ const machine = createTaskStateMachine(uiControlConfig, {
93
+ actions: {
94
+ emitTaskIncoming: ({event}) => task.emit('task:incoming', task),
95
+ },
96
+ });
97
+ const actor = createActor(machine);
98
+ actor.start();
99
+ ```
100
+
101
+ ### TaskManager Lifecycle Orchestration
102
+
103
+ - **Listener Setup**: Registers WebSocket listeners to receive CC events and map them to `TaskEvent` payloads.
104
+ - **Task Registry**: Creates tasks via `TaskFactory`, stores them in the task collection, and updates task data on incoming events.
105
+ - **Event Emission**: Re-emits `task:*` events on the task or `cc` object for SDK consumers.
106
+ - **Hydration/Recovery**: Handles state updates and transitions during reconnect/hydrate flows.
107
+
108
+ Example (backend event to state machine):
109
+
110
+ ```typescript
111
+ const payload = TaskManager.mapEventToTaskStateMachineEvent(event, taskData);
112
+ if (payload) {
113
+ task.sendStateMachineEvent(payload);
114
+ }
115
+ ```
116
+
117
+ ### AQM Call Control Integration
118
+
119
+ - **`contact.ts`**: Builds the AQM request surface for call control (hold, consult, transfer, wrapup, end). Task methods delegate to these calls, then drive state transitions based on success/failure events.
120
+ - **`dialer.ts`**: Exposes the `startOutdial` AQM request used by `cc.startOutdial()` to create outbound tasks.
121
+
122
+ Example (task method delegating to AQM):
123
+
124
+ ```typescript
125
+ // task.hold() -> contact.hold(...) -> stateMachine events on response
126
+ await contact.hold({interactionId});
127
+ stateMachineService.send({type: TaskEvent.HOLD_INITIATED, mediaResourceId});
128
+ ```
129
+
130
+ ### Sequential Flow (End-to-End)
131
+
132
+ 1. **WebSocket event arrives** → `TaskManager` maps CC event to `TaskEvent`.
133
+ 2. **Task creation** (if new) → `TaskFactory` builds `Voice`/`WebRTC`/`Digital`.
134
+ 3. **State machine actor starts** → `Task` wires emitters + UI control updates.
135
+ 4. **Task method called** (e.g., hold/transfer) → delegates to `contact.ts` or `dialer.ts`.
136
+ 5. **State transitions** → guards/actions update context and emit `task:*` events.
137
+ 6. **SDK consumers update UI** → `TaskUIControls` reflect the latest state.
138
+
139
+ ```mermaid
140
+ flowchart TD
141
+ A[WebSocket event arrives] --> B[TaskManager maps CC event to TaskEvent]
142
+ B --> C{Task exists?}
143
+ C -- No --> D[TaskFactory creates Voice/WebRTC/Digital]
144
+ C -- Yes --> E[Use existing task]
145
+ D --> F[Task initializes state machine actor]
146
+ E --> F
147
+ F --> G[Task method called (hold/transfer/etc)]
148
+ G --> H[contact.ts or dialer.ts API call]
149
+ H --> I[State machine transition]
150
+ I --> J[Actions + guards update context]
151
+ J --> K[Emit task:* events]
152
+ K --> L[TaskUIControls updated for SDK UI]
153
+ ```
154
+
155
+ ---
156
+
157
+ ## Quick Start
158
+
159
+ ```typescript
160
+ // Listen for incoming tasks
161
+ cc.on('task:incoming', async (task) => {
162
+ console.log('Incoming task:', task.data.interactionId);
163
+
164
+ // Accept the task
165
+ await task.accept();
166
+
167
+ // Task operations
168
+ await task.hold();
169
+ await task.resume();
170
+ await task.end();
171
+ await task.wrapup({
172
+ wrapUpReason: 'Resolved',
173
+ auxCodeId: 'wrapup-code',
174
+ });
175
+ });
176
+ ```
177
+
178
+ ---
179
+
180
+ ## Task Events
181
+
182
+ ### Emitted on `cc` object(ContactCenter)
183
+
184
+ | Event | When Emitted |
185
+ | --------------- | ---------------------------- |
186
+ | `task:incoming` | New task offered to agent |
187
+ | `task:hydrate` | Task data updated |
188
+ | `task:merged` | Tasks merged (EPDN transfer) |
189
+
190
+ ### Emitted on `task` object(ITask)
191
+
192
+ | Event | When Emitted |
193
+ | --------------------------------------------------------------------------- | ------------------------------------------------ |
194
+ | `task:assigned` | Task assigned to agent |
195
+ | `task:media` | Media stream/track updates are available |
196
+ | `task:unassigned` | Task is unassigned from agent |
197
+ | `task:offerContact` | Contact offer received/updated |
198
+ | `task:offerConsult` | Consult offer received |
199
+ | `task:hold` | Task placed on hold |
200
+ | `task:resume` | Task resumed from hold |
201
+ | `task:end` | Task ended |
202
+ | `task:rejected` | Task rejected / failure path emitted |
203
+ | `task:wrapup` | Task entering wrapup |
204
+ | `task:wrappedup` | Wrapup completed |
205
+ | `task:consulting` | Consult is in progress |
206
+ | `task:consultAccepted` | Consult accepted by destination party |
207
+ | `task:consultCreated` | Consultation started |
208
+ | `task:consultEnd` | Consultation ended |
209
+ | `task:autoAnswered` | Task was auto-answered |
210
+ | `task:recordingStarted` / `task:recordingPaused` / `task:recordingResumed` | Recording lifecycle updates |
211
+ | `task:conferenceStarted` / `task:conferenceEnded` / `task:conferenceFailed` | Conference lifecycle updates |
212
+ | `task:participantJoined` / `task:participantLeft` | Conference participant updates |
213
+ | `task:switchCall` | Switched between consult and main call |
214
+ | `task:outdialFailed` | Outdial operation failed |
215
+ | `task:ui-controls-updated` | UI controls changed due to state transition |
216
+ | `task:cleanup` | Internal cleanup signal emitted by state machine |
217
+
218
+ > Full list is defined in `TASK_EVENTS` (`types.ts`).
219
+
220
+ ---
221
+
222
+ ## API Reference
223
+
224
+ ### `cc.startOutdial(destination, origin)`
225
+
226
+ Initiate outbound call.
227
+
228
+ **Parameters**:
229
+
230
+ - `destination` (string): Phone number to call
231
+ - `origin` (string): Outbound ANI/caller ID
232
+
233
+ **Returns**: `Promise<TaskResponse>` (AQM response, not a Task instance)
234
+
235
+ **Example**:
236
+
237
+ ```typescript
238
+ const response = await cc.startOutdial('+14155551234', '+18005551000');
239
+
240
+ // Outdial task object is created asynchronously via TaskManager.
241
+ // Listen on cc/task events instead of treating startOutdial response as an ITask.
242
+ cc.on('task:incoming', (task) => {
243
+ task.on('task:assigned', () => {
244
+ console.log('Call connected');
245
+ });
246
+
247
+ task.on('task:end', () => {
248
+ console.log('Call ended');
249
+ });
250
+ });
251
+ ```
252
+
253
+ ---
254
+
255
+ ### `task.accept()`
256
+
257
+ Accept an incoming task.
258
+
259
+ **Returns**: `Promise<TaskResponse>`
260
+
261
+ **Example**:
262
+
263
+ ```typescript
264
+ cc.on('task:incoming', async (task) => {
265
+ await task.accept();
266
+ });
267
+ ```
268
+
269
+ ---
270
+
271
+ ### `task.hold(mediaResourceId?)` / `task.resume(mediaResourceId?)`
272
+
273
+ Put task on hold or resume.
274
+
275
+ **Parameters**:
276
+
277
+ - `mediaResourceId` (optional `string`): Media resource ID for the hold/resume operation
278
+
279
+ **Returns**: `Promise<TaskResponse>`
280
+
281
+ **Example**:
282
+
283
+ ```typescript
284
+ // Put on hold
285
+ await task.hold();
286
+
287
+ // Resume
288
+ await task.resume();
289
+ ```
290
+
291
+ ---
292
+
293
+ ### `task.end()`
294
+
295
+ End the current task.
296
+
297
+ **Returns**: `Promise<TaskResponse>`
298
+
299
+ **Example**:
300
+
301
+ ```typescript
302
+ await task.end();
303
+ ```
304
+
305
+ ---
306
+
307
+ ### `task.wrapup(params)`
308
+
309
+ Complete task with wrapup code.
310
+
311
+ **Parameters**:
312
+
313
+ - `wrapUpReason` (string, required): Wrapup reason text
314
+ - `auxCodeId` (string, required): Wrapup code ID
315
+
316
+ **Returns**: `Promise<TaskResponse>`
317
+
318
+ **Example**:
319
+
320
+ ```typescript
321
+ await task.wrapup({
322
+ wrapUpReason: 'Customer issue resolved',
323
+ auxCodeId: 'resolved-code',
324
+ });
325
+ ```
326
+
327
+ ---
328
+
329
+ ### `task.transfer(params)`
330
+
331
+ Transfer task to another destination.
332
+
333
+ **Parameters**:
334
+
335
+ - `to` (string): Agent ID, queue ID, or phone number
336
+ - `destinationType` ('queue' | 'agent' | 'dialNumber'): Destination type
337
+
338
+ **Returns**: `Promise<TaskResponse>`
339
+
340
+ **Example**:
341
+
342
+ ```typescript
343
+ // Transfer to queue
344
+ await task.transfer({
345
+ to: 'queue-123',
346
+ destinationType: 'queue',
347
+ });
348
+
349
+ // Transfer to agent
350
+ await task.transfer({
351
+ to: 'agent-456',
352
+ destinationType: 'agent',
353
+ });
354
+ ```
355
+
356
+ ---
357
+
358
+ ### `task.consult(params)`
359
+
360
+ Start consultation.
361
+
362
+ **Parameters**:
363
+
364
+ - `to` (string): Agent/queue/phone to consult
365
+ - `destinationType` ('queue' | 'agent' | 'dialNumber' | 'entryPoint'): Type
366
+
367
+ **Returns**: `Promise<TaskResponse>`
368
+
369
+ **Example**:
370
+
371
+ ```typescript
372
+ await task.consult({
373
+ to: 'agent-456',
374
+ destinationType: 'agent',
375
+ });
376
+
377
+ // Later: complete transfer (consulting voice flow uses transfer())
378
+ await task.transfer({
379
+ to: 'queue-123',
380
+ destinationType: 'queue',
381
+ });
382
+ // Or end consult
383
+ await task.endConsult();
384
+ ```
385
+
386
+ ---
387
+
388
+ ### `task.endConsult(consultEndPayload?)`
389
+
390
+ End consultation without transfer.
391
+
392
+ **Parameters**:
393
+
394
+ - `consultEndPayload` (optional `ConsultEndPayload`)
395
+
396
+ **Returns**: `Promise<TaskResponse>`
397
+
398
+ ---
399
+
400
+ ## Media Channels
401
+
402
+ | Channel | Description |
403
+ | ----------- | ------------------ |
404
+ | `telephony` | Voice calls |
405
+ | `chat` | Web chat |
406
+ | `email` | Email interactions |
407
+ | `social` | Social media |
408
+ | `sms` | SMS messages |
409
+ | `facebook` | Facebook Messenger |
410
+ | `whatsapp` | WhatsApp messages |
411
+
412
+ ---
413
+
414
+ ## Error Handling
415
+
416
+ ```typescript
417
+ try {
418
+ await task.transfer({
419
+ to: 'queue-123',
420
+ destinationType: 'queue',
421
+ });
422
+ } catch (error) {
423
+ console.error('Transfer failed:', error.message);
424
+ // error.data contains structured error info
425
+ }
426
+ ```
427
+
428
+ ---
429
+
430
+ ## Auto Wrapup
431
+
432
+ If enabled in agent profile, wrapup completes automatically after timeout:
433
+
434
+ ```typescript
435
+ task.on('task:wrappedup', () => {
436
+ console.log('Task wrapup completed');
437
+ });
438
+ ```
439
+
440
+ ---
441
+
442
+ ## Related
443
+
444
+ - [ARCHITECTURE.md](ARCHITECTURE.md) - Technical deep-dive
445
+ - [TaskManager.ts](../TaskManager.ts) - Manager implementation
446
+ - [types.ts](../types.ts) - Type definitions
447
+ - [../state-machine/ai-docs/AGENTS.md](../state-machine/ai-docs/AGENTS.md) - State machine implementation guide
448
+ - [../state-machine/ai-docs/ARCHITECTURE.md](../state-machine/ai-docs/ARCHITECTURE.md) - State machine internals