@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.
- package/AGENTS.md +438 -0
- package/ai-docs/README.md +131 -0
- package/ai-docs/RULES.md +455 -0
- package/ai-docs/patterns/event-driven-patterns.md +485 -0
- package/ai-docs/patterns/testing-patterns.md +480 -0
- package/ai-docs/patterns/typescript-patterns.md +365 -0
- package/ai-docs/templates/README.md +102 -0
- package/ai-docs/templates/documentation/create-agents-md.md +240 -0
- package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
- package/ai-docs/templates/existing-service/bug-fix.md +254 -0
- package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
- package/ai-docs/templates/new-method/00-master.md +80 -0
- package/ai-docs/templates/new-method/01-requirements.md +232 -0
- package/ai-docs/templates/new-method/02-implementation.md +295 -0
- package/ai-docs/templates/new-method/03-tests.md +201 -0
- package/ai-docs/templates/new-method/04-validation.md +141 -0
- package/ai-docs/templates/new-service/00-master.md +109 -0
- package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
- package/ai-docs/templates/new-service/02-code-generation.md +346 -0
- package/ai-docs/templates/new-service/03-integration.md +178 -0
- package/ai-docs/templates/new-service/04-test-generation.md +205 -0
- package/ai-docs/templates/new-service/05-validation.md +145 -0
- package/dist/cc.js +65 -123
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +13 -2
- package/dist/constants.js.map +1 -1
- package/dist/index.js +13 -5
- package/dist/index.js.map +1 -1
- package/dist/metrics/behavioral-events.js +26 -13
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +7 -6
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/ApiAiAssistant.js +0 -3
- package/dist/services/ApiAiAssistant.js.map +1 -1
- package/dist/services/config/Util.js +2 -3
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/types.js +16 -14
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/constants.js +0 -1
- package/dist/services/constants.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +79 -55
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +17 -92
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +5 -25
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/core/websocket/types.js.map +1 -1
- package/dist/services/index.js +1 -2
- package/dist/services/index.js.map +1 -1
- package/dist/services/task/Task.js +644 -0
- package/dist/services/task/Task.js.map +1 -0
- package/dist/services/task/TaskFactory.js +45 -0
- package/dist/services/task/TaskFactory.js.map +1 -0
- package/dist/services/task/TaskManager.js +570 -535
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +132 -28
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/constants.js +7 -6
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/dialer.js +0 -51
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/digital/Digital.js +77 -0
- package/dist/services/task/digital/Digital.js.map +1 -0
- package/dist/services/task/state-machine/TaskStateMachine.js +634 -0
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
- package/dist/services/task/state-machine/actions.js +372 -0
- package/dist/services/task/state-machine/actions.js.map +1 -0
- package/dist/services/task/state-machine/constants.js +139 -0
- package/dist/services/task/state-machine/constants.js.map +1 -0
- package/dist/services/task/state-machine/guards.js +263 -0
- package/dist/services/task/state-machine/guards.js.map +1 -0
- package/dist/services/task/state-machine/index.js +53 -0
- package/dist/services/task/state-machine/index.js.map +1 -0
- package/dist/services/task/state-machine/types.js +54 -0
- package/dist/services/task/state-machine/types.js.map +1 -0
- package/dist/services/task/state-machine/uiControlsComputer.js +377 -0
- package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
- package/dist/services/task/taskDataNormalizer.js +99 -0
- package/dist/services/task/taskDataNormalizer.js.map +1 -0
- package/dist/services/task/types.js +157 -18
- package/dist/services/task/types.js.map +1 -1
- package/dist/services/task/voice/Voice.js +1031 -0
- package/dist/services/task/voice/Voice.js.map +1 -0
- package/dist/services/task/voice/WebRTC.js +149 -0
- package/dist/services/task/voice/WebRTC.js.map +1 -0
- package/dist/types/cc.d.ts +4 -33
- package/dist/types/constants.d.ts +13 -2
- package/dist/types/index.d.ts +11 -5
- package/dist/types/metrics/constants.d.ts +5 -3
- package/dist/types/services/ApiAiAssistant.d.ts +1 -1
- package/dist/types/services/config/types.d.ts +97 -25
- package/dist/types/services/core/Err.d.ts +0 -2
- package/dist/types/services/core/Utils.d.ts +25 -23
- package/dist/types/services/core/aqm-reqs.d.ts +0 -49
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -1
- package/dist/types/services/core/websocket/connection-service.d.ts +0 -1
- package/dist/types/services/core/websocket/types.d.ts +1 -1
- package/dist/types/services/index.d.ts +1 -1
- package/dist/types/services/task/Task.d.ts +146 -0
- package/dist/types/services/task/TaskFactory.d.ts +12 -0
- package/dist/types/services/task/TaskUtils.d.ts +39 -8
- package/dist/types/services/task/constants.d.ts +5 -4
- package/dist/types/services/task/dialer.d.ts +0 -15
- package/dist/types/services/task/digital/Digital.d.ts +22 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +906 -0
- package/dist/types/services/task/state-machine/actions.d.ts +8 -0
- package/dist/types/services/task/state-machine/constants.d.ts +91 -0
- package/dist/types/services/task/state-machine/guards.d.ts +78 -0
- package/dist/types/services/task/state-machine/index.d.ts +13 -0
- package/dist/types/services/task/state-machine/types.d.ts +256 -0
- package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
- package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
- package/dist/types/services/task/types.d.ts +539 -88
- package/dist/types/services/task/voice/Voice.d.ts +183 -0
- package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
- package/dist/types/types.d.ts +68 -0
- package/dist/types/webex.d.ts +1 -0
- package/dist/types.js +70 -0
- package/dist/types.js.map +1 -1
- package/dist/webex.js +14 -2
- package/dist/webex.js.map +1 -1
- package/package.json +14 -11
- package/src/cc.ts +91 -177
- package/src/constants.ts +13 -2
- package/src/index.ts +14 -5
- package/src/metrics/ai-docs/AGENTS.md +348 -0
- package/src/metrics/ai-docs/ARCHITECTURE.md +336 -0
- package/src/metrics/behavioral-events.ts +28 -14
- package/src/metrics/constants.ts +7 -8
- package/src/services/ApiAiAssistant.ts +2 -4
- package/src/services/agent/ai-docs/AGENTS.md +238 -0
- package/src/services/agent/ai-docs/ARCHITECTURE.md +302 -0
- package/src/services/ai-docs/AGENTS.md +384 -0
- package/src/services/config/Util.ts +2 -3
- package/src/services/config/ai-docs/AGENTS.md +253 -0
- package/src/services/config/ai-docs/ARCHITECTURE.md +424 -0
- package/src/services/config/types.ts +108 -20
- package/src/services/constants.ts +0 -1
- package/src/services/core/Err.ts +0 -1
- package/src/services/core/Utils.ts +90 -67
- package/src/services/core/ai-docs/AGENTS.md +379 -0
- package/src/services/core/ai-docs/ARCHITECTURE.md +696 -0
- package/src/services/core/aqm-reqs.ts +22 -100
- package/src/services/core/websocket/WebSocketManager.ts +4 -23
- package/src/services/core/websocket/types.ts +1 -1
- package/src/services/index.ts +1 -2
- package/src/services/task/Task.ts +785 -0
- package/src/services/task/TaskFactory.ts +55 -0
- package/src/services/task/TaskManager.ts +579 -633
- package/src/services/task/TaskUtils.ts +175 -31
- package/src/services/task/ai-docs/AGENTS.md +448 -0
- package/src/services/task/ai-docs/ARCHITECTURE.md +573 -0
- package/src/services/task/constants.ts +5 -4
- package/src/services/task/dialer.ts +1 -56
- package/src/services/task/digital/Digital.ts +95 -0
- package/src/services/task/state-machine/TaskStateMachine.ts +793 -0
- package/src/services/task/state-machine/actions.ts +422 -0
- package/src/services/task/state-machine/ai-docs/AGENTS.md +495 -0
- package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1135 -0
- package/src/services/task/state-machine/constants.ts +150 -0
- package/src/services/task/state-machine/guards.ts +303 -0
- package/src/services/task/state-machine/index.ts +28 -0
- package/src/services/task/state-machine/types.ts +228 -0
- package/src/services/task/state-machine/uiControlsComputer.ts +542 -0
- package/src/services/task/taskDataNormalizer.ts +137 -0
- package/src/services/task/types.ts +641 -95
- package/src/services/task/voice/Voice.ts +1255 -0
- package/src/services/task/voice/WebRTC.ts +187 -0
- package/src/types.ts +88 -5
- package/src/utils/AGENTS.md +276 -0
- package/src/webex.js +2 -0
- package/test/unit/spec/cc.ts +59 -142
- package/test/unit/spec/logger-proxy.ts +70 -0
- package/test/unit/spec/services/ApiAiAssistant.ts +17 -0
- package/test/unit/spec/services/config/index.ts +26 -55
- package/test/unit/spec/services/core/Utils.ts +103 -52
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +48 -112
- package/test/unit/spec/services/core/websocket/connection-service.ts +5 -4
- package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
- package/test/unit/spec/services/task/Task.ts +416 -0
- package/test/unit/spec/services/task/TaskFactory.ts +62 -0
- package/test/unit/spec/services/task/TaskManager.ts +781 -1735
- package/test/unit/spec/services/task/TaskUtils.ts +125 -0
- package/test/unit/spec/services/task/dialer.ts +112 -198
- package/test/unit/spec/services/task/digital/Digital.ts +105 -0
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +473 -0
- package/test/unit/spec/services/task/state-machine/guards.ts +288 -0
- package/test/unit/spec/services/task/state-machine/types.ts +18 -0
- package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +147 -0
- package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
- package/test/unit/spec/services/task/voice/Voice.ts +587 -0
- package/test/unit/spec/services/task/voice/WebRTC.ts +242 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
- package/dist/services/task/index.js +0 -1525
- package/dist/services/task/index.js.map +0 -1
- package/dist/types/services/task/index.d.ts +0 -650
- package/src/services/task/index.ts +0 -1801
- 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 &&
|
|
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
|
|
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
|
-
|
|
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
|
|
192
|
+
const {participants} = data.interaction;
|
|
66
193
|
|
|
67
194
|
const agentParticipants = new Set();
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
)
|
|
78
|
-
|
|
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
|
|
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
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
* @param
|
|
229
|
-
* @
|
|
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
|
|
232
|
-
|
|
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
|