@webex/contact-center 3.12.0-next.9 → 3.12.0-next.91

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 (229) hide show
  1. package/.sdd/manifest.json +876 -0
  2. package/AGENTS.md +94 -0
  3. package/ai-docs/ARCHITECTURE.md +168 -0
  4. package/ai-docs/CONTRACTS.md +46 -0
  5. package/ai-docs/GETTING_STARTED.md +168 -0
  6. package/ai-docs/GLOSSARY.md +43 -0
  7. package/ai-docs/README.md +138 -0
  8. package/ai-docs/REVIEW_CHECKLIST.md +41 -0
  9. package/ai-docs/RULES.md +444 -0
  10. package/ai-docs/SECURITY.md +52 -0
  11. package/ai-docs/SERVICE_STATE.md +48 -0
  12. package/ai-docs/SPEC_INDEX.md +65 -0
  13. package/ai-docs/adr/0001-spec-source-policy.md +55 -0
  14. package/ai-docs/adr/README.md +8 -0
  15. package/ai-docs/adr/_adr-template.md +31 -0
  16. package/ai-docs/contact-center-spec.md +341 -0
  17. package/ai-docs/features/generated-spec-conformance-fidelity-remediation/spec/feature-spec.md +117 -0
  18. package/ai-docs/features/residual-warning-coverage-completion/spec/feature-spec.md +203 -0
  19. package/ai-docs/features/validator-code-fidelity-drift-fix/spec/feature-spec.md +315 -0
  20. package/ai-docs/patterns/event-driven-patterns.md +485 -0
  21. package/ai-docs/patterns/testing-patterns.md +480 -0
  22. package/ai-docs/patterns/typescript-patterns.md +365 -0
  23. package/ai-docs/templates/README.md +102 -0
  24. package/ai-docs/templates/documentation/create-agents-md.md +240 -0
  25. package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
  26. package/ai-docs/templates/existing-service/bug-fix.md +254 -0
  27. package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
  28. package/ai-docs/templates/new-method/00-master.md +80 -0
  29. package/ai-docs/templates/new-method/01-requirements.md +232 -0
  30. package/ai-docs/templates/new-method/02-implementation.md +295 -0
  31. package/ai-docs/templates/new-method/03-tests.md +201 -0
  32. package/ai-docs/templates/new-method/04-validation.md +141 -0
  33. package/ai-docs/templates/new-service/00-master.md +109 -0
  34. package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
  35. package/ai-docs/templates/new-service/02-code-generation.md +346 -0
  36. package/ai-docs/templates/new-service/03-integration.md +178 -0
  37. package/ai-docs/templates/new-service/04-test-generation.md +205 -0
  38. package/ai-docs/templates/new-service/05-validation.md +145 -0
  39. package/dist/cc.js +265 -29
  40. package/dist/cc.js.map +1 -1
  41. package/dist/config.js +6 -0
  42. package/dist/config.js.map +1 -1
  43. package/dist/constants.js +17 -1
  44. package/dist/constants.js.map +1 -1
  45. package/dist/index.js +20 -5
  46. package/dist/index.js.map +1 -1
  47. package/dist/metrics/behavioral-events.js +101 -0
  48. package/dist/metrics/behavioral-events.js.map +1 -1
  49. package/dist/metrics/constants.js +25 -4
  50. package/dist/metrics/constants.js.map +1 -1
  51. package/dist/services/ApiAiAssistant.js +153 -8
  52. package/dist/services/ApiAiAssistant.js.map +1 -1
  53. package/dist/services/UserPreference.js +427 -0
  54. package/dist/services/UserPreference.js.map +1 -0
  55. package/dist/services/config/Util.js +3 -3
  56. package/dist/services/config/Util.js.map +1 -1
  57. package/dist/services/config/constants.js +23 -2
  58. package/dist/services/config/constants.js.map +1 -1
  59. package/dist/services/config/types.js +49 -9
  60. package/dist/services/config/types.js.map +1 -1
  61. package/dist/services/core/Err.js.map +1 -1
  62. package/dist/services/core/Utils.js +107 -32
  63. package/dist/services/core/Utils.js.map +1 -1
  64. package/dist/services/core/websocket/WebSocketManager.js +2 -1
  65. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  66. package/dist/services/core/websocket/types.js.map +1 -1
  67. package/dist/services/index.js +1 -1
  68. package/dist/services/index.js.map +1 -1
  69. package/dist/services/task/Task.js +688 -0
  70. package/dist/services/task/Task.js.map +1 -0
  71. package/dist/services/task/TaskFactory.js +45 -0
  72. package/dist/services/task/TaskFactory.js.map +1 -0
  73. package/dist/services/task/TaskManager.js +728 -527
  74. package/dist/services/task/TaskManager.js.map +1 -1
  75. package/dist/services/task/TaskUtils.js +162 -26
  76. package/dist/services/task/TaskUtils.js.map +1 -1
  77. package/dist/services/task/constants.js +9 -2
  78. package/dist/services/task/constants.js.map +1 -1
  79. package/dist/services/task/dialer.js +78 -0
  80. package/dist/services/task/dialer.js.map +1 -1
  81. package/dist/services/task/digital/Digital.js +77 -0
  82. package/dist/services/task/digital/Digital.js.map +1 -0
  83. package/dist/services/task/state-machine/TaskStateMachine.js +837 -0
  84. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  85. package/dist/services/task/state-machine/actions.js +543 -0
  86. package/dist/services/task/state-machine/actions.js.map +1 -0
  87. package/dist/services/task/state-machine/constants.js +161 -0
  88. package/dist/services/task/state-machine/constants.js.map +1 -0
  89. package/dist/services/task/state-machine/guards.js +340 -0
  90. package/dist/services/task/state-machine/guards.js.map +1 -0
  91. package/dist/services/task/state-machine/index.js +53 -0
  92. package/dist/services/task/state-machine/index.js.map +1 -0
  93. package/dist/services/task/state-machine/types.js +54 -0
  94. package/dist/services/task/state-machine/types.js.map +1 -0
  95. package/dist/services/task/state-machine/uiControlsComputer.js +553 -0
  96. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  97. package/dist/services/task/taskDataNormalizer.js +99 -0
  98. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  99. package/dist/services/task/types.js +212 -4
  100. package/dist/services/task/types.js.map +1 -1
  101. package/dist/services/task/voice/Voice.js +1042 -0
  102. package/dist/services/task/voice/Voice.js.map +1 -0
  103. package/dist/services/task/voice/WebRTC.js +149 -0
  104. package/dist/services/task/voice/WebRTC.js.map +1 -0
  105. package/dist/types/cc.d.ts +94 -1
  106. package/dist/types/config.d.ts +6 -0
  107. package/dist/types/constants.d.ts +17 -1
  108. package/dist/types/index.d.ts +21 -6
  109. package/dist/types/metrics/constants.d.ts +21 -1
  110. package/dist/types/services/ApiAiAssistant.d.ts +22 -4
  111. package/dist/types/services/UserPreference.d.ts +118 -0
  112. package/dist/types/services/config/constants.d.ts +21 -0
  113. package/dist/types/services/config/types.d.ts +171 -10
  114. package/dist/types/services/core/Err.d.ts +4 -0
  115. package/dist/types/services/core/Utils.d.ts +33 -13
  116. package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
  117. package/dist/types/services/core/websocket/types.d.ts +1 -1
  118. package/dist/types/services/index.d.ts +1 -1
  119. package/dist/types/services/task/Task.d.ts +157 -0
  120. package/dist/types/services/task/TaskFactory.d.ts +12 -0
  121. package/dist/types/services/task/TaskUtils.d.ts +46 -2
  122. package/dist/types/services/task/constants.d.ts +7 -0
  123. package/dist/types/services/task/dialer.d.ts +30 -0
  124. package/dist/types/services/task/digital/Digital.d.ts +22 -0
  125. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1144 -0
  126. package/dist/types/services/task/state-machine/actions.d.ts +10 -0
  127. package/dist/types/services/task/state-machine/constants.d.ts +107 -0
  128. package/dist/types/services/task/state-machine/guards.d.ts +90 -0
  129. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  130. package/dist/types/services/task/state-machine/types.d.ts +267 -0
  131. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  132. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  133. package/dist/types/services/task/types.d.ts +603 -66
  134. package/dist/types/services/task/voice/Voice.d.ts +184 -0
  135. package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
  136. package/dist/types/types.d.ts +135 -0
  137. package/dist/types/webex.d.ts +1 -0
  138. package/dist/types.js +118 -2
  139. package/dist/types.js.map +1 -1
  140. package/dist/webex.js +14 -2
  141. package/dist/webex.js.map +1 -1
  142. package/package.json +15 -12
  143. package/src/cc.ts +329 -30
  144. package/src/config.ts +6 -0
  145. package/src/constants.ts +17 -1
  146. package/src/index.ts +23 -5
  147. package/src/metrics/ai-docs/AGENTS.md +350 -0
  148. package/src/metrics/ai-docs/ARCHITECTURE.md +338 -0
  149. package/src/metrics/ai-docs/metrics-spec.md +854 -0
  150. package/src/metrics/behavioral-events.ts +106 -0
  151. package/src/metrics/constants.ts +27 -4
  152. package/src/services/ApiAiAssistant.ts +203 -8
  153. package/src/services/UserPreference.ts +509 -0
  154. package/src/services/agent/ai-docs/AGENTS.md +240 -0
  155. package/src/services/agent/ai-docs/ARCHITECTURE.md +304 -0
  156. package/src/services/agent/ai-docs/agent-spec.md +504 -0
  157. package/src/services/ai-docs/AGENTS.md +386 -0
  158. package/src/services/ai-docs/services-spec.md +492 -0
  159. package/src/services/config/Util.ts +3 -3
  160. package/src/services/config/ai-docs/AGENTS.md +255 -0
  161. package/src/services/config/ai-docs/ARCHITECTURE.md +426 -0
  162. package/src/services/config/ai-docs/config-spec.md +669 -0
  163. package/src/services/config/constants.ts +25 -1
  164. package/src/services/config/types.ts +174 -11
  165. package/src/services/core/Err.ts +2 -0
  166. package/src/services/core/Utils.ts +123 -37
  167. package/src/services/core/ai-docs/AGENTS.md +381 -0
  168. package/src/services/core/ai-docs/ARCHITECTURE.md +698 -0
  169. package/src/services/core/ai-docs/core-spec.md +783 -0
  170. package/src/services/core/websocket/WebSocketManager.ts +2 -0
  171. package/src/services/core/websocket/types.ts +1 -1
  172. package/src/services/index.ts +1 -1
  173. package/src/services/task/Task.ts +837 -0
  174. package/src/services/task/TaskFactory.ts +55 -0
  175. package/src/services/task/TaskManager.ts +739 -614
  176. package/src/services/task/TaskUtils.ts +205 -25
  177. package/src/services/task/ai-docs/AGENTS.md +457 -0
  178. package/src/services/task/ai-docs/ARCHITECTURE.md +594 -0
  179. package/src/services/task/ai-docs/task-spec.md +1319 -0
  180. package/src/services/task/constants.ts +7 -0
  181. package/src/services/task/dialer.ts +80 -0
  182. package/src/services/task/digital/Digital.ts +95 -0
  183. package/src/services/task/state-machine/TaskStateMachine.ts +1077 -0
  184. package/src/services/task/state-machine/actions.ts +685 -0
  185. package/src/services/task/state-machine/ai-docs/AGENTS.md +458 -0
  186. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1137 -0
  187. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2115 -0
  188. package/src/services/task/state-machine/constants.ts +172 -0
  189. package/src/services/task/state-machine/guards.ts +406 -0
  190. package/src/services/task/state-machine/index.ts +28 -0
  191. package/src/services/task/state-machine/types.ts +241 -0
  192. package/src/services/task/state-machine/uiControlsComputer.ts +867 -0
  193. package/src/services/task/taskDataNormalizer.ts +137 -0
  194. package/src/services/task/types.ts +710 -71
  195. package/src/services/task/voice/Voice.ts +1267 -0
  196. package/src/services/task/voice/WebRTC.ts +187 -0
  197. package/src/types.ts +166 -2
  198. package/src/utils/AGENTS.md +278 -0
  199. package/src/utils/ai-docs/utils-spec.md +381 -0
  200. package/src/webex.js +2 -0
  201. package/test/unit/spec/cc.ts +343 -23
  202. package/test/unit/spec/logger-proxy.ts +70 -0
  203. package/test/unit/spec/services/ApiAiAssistant.ts +178 -20
  204. package/test/unit/spec/services/UserPreference.ts +401 -0
  205. package/test/unit/spec/services/WebCallingService.ts +7 -1
  206. package/test/unit/spec/services/config/index.ts +30 -30
  207. package/test/unit/spec/services/core/Utils.ts +425 -8
  208. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +66 -40
  209. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  210. package/test/unit/spec/services/task/Task.ts +477 -0
  211. package/test/unit/spec/services/task/TaskFactory.ts +62 -0
  212. package/test/unit/spec/services/task/TaskManager.ts +834 -1704
  213. package/test/unit/spec/services/task/TaskUtils.ts +206 -0
  214. package/test/unit/spec/services/task/dialer.ts +190 -0
  215. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  216. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +1825 -0
  217. package/test/unit/spec/services/task/state-machine/guards.ts +479 -0
  218. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  219. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +2020 -0
  220. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  221. package/test/unit/spec/services/task/voice/Voice.ts +631 -0
  222. package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
  223. package/umd/contact-center.min.js +2 -2
  224. package/umd/contact-center.min.js.map +1 -1
  225. package/dist/services/task/index.js +0 -1525
  226. package/dist/services/task/index.js.map +0 -1
  227. package/dist/types/services/task/index.d.ts +0 -650
  228. package/src/services/task/index.ts +0 -1801
  229. package/test/unit/spec/services/task/index.ts +0 -2184
@@ -0,0 +1,458 @@
1
+ # Task State Machine - AI Agent Guide
2
+
3
+ > **Legacy/reference-only.** Canonical SDD: [`task-state-machine-spec.md`](task-state-machine-spec.md). Use the package [manifest](../../../../../.sdd/manifest.json) and [`SPEC_INDEX.md`](../../../../../ai-docs/SPEC_INDEX.md) for routing; code and tests remain the behavioral referee.
4
+
5
+ ## Purpose
6
+
7
+ Guide AI agents working on task lifecycle transitions, guard logic, executable actions and UI control computation in the XState-based task state machine.
8
+
9
+ ---
10
+
11
+ ## Scope
12
+
13
+ This guide is for internal state management for the task lifecycle in:
14
+
15
+ - State machine configuration: `TaskStateMachine.ts`
16
+ - Actions and context mutation: `actions.ts`
17
+ - Guard logic: `guards.ts`
18
+ - UI control computation: `uiControlsComputer.ts`
19
+ - Event types and payloads: `constants.ts`, `types.ts`
20
+
21
+ Use this doc when implementing:
22
+
23
+ - new state transitions
24
+ - event mapping and payload extensions
25
+ - guard/action fixes
26
+ - UI control behavior changes tied to task state
27
+
28
+ ---
29
+
30
+
31
+
32
+ ## File Structure
33
+
34
+ ```text
35
+ state-machine/
36
+ ├── TaskStateMachine.ts # State graph and transition configuration
37
+ ├── actions.ts # Assign actions and emitter placeholders
38
+ ├── guards.ts # Pure guard predicates
39
+ ├── uiControlsComputer.ts # Voice/Digital UI control computation
40
+ ├── constants.ts # TaskState, TaskEvent, machine constants
41
+ ├── types.ts # Context and typed event payload map
42
+ ├── index.ts # Public exports
43
+ └── ai-docs/
44
+ ├── AGENTS.md # AI coding guide
45
+ └── ARCHITECTURE.md # State machine architecture guide
46
+ ```
47
+
48
+ ---
49
+
50
+
51
+
52
+ ## Source of Truth
53
+
54
+ - Task lifecycle state machine: `TaskStateMachine.ts`
55
+ - State machine types/events: `constants.ts`, `types.ts`
56
+ - Guard logic: `guards.ts`
57
+ - Actions and context mutation: `actions.ts`
58
+ - UI control computation: `uiControlsComputer.ts`
59
+
60
+ ---
61
+
62
+
63
+
64
+ ## Key Capabilities
65
+
66
+ - **State Graph and Transition Rules**: `TaskStateMachine.ts` defines all states, transition tables, and event handlers that drive the task lifecycle.
67
+ - **Deterministic Context Updates**: `actions.ts` implements XState actions for task context mutation and provides emitter placeholders that `Task` overrides to surface SDK events.
68
+ - **Transition Eligibility**: `guards.ts` contains pure predicates that gate transitions based on current context, task data, and backend state.
69
+ - **UI Controls Computation**: `uiControlsComputer.ts` derives `TaskUIControls` from state and context for voice/digital channels, keeping UI enablement centralized.
70
+ - **Typed Event Contracts**: `constants.ts` and `types.ts` define `TaskState`, `TaskEvent`, and the `TaskEventPayloadMap` so transitions and payloads stay type-safe.
71
+ - **Public Exports**: `index.ts` exposes the state machine factory, event enums, and types for consumption by the task layer.
72
+
73
+ ---
74
+
75
+
76
+
77
+ ## State Machine Overview
78
+
79
+ **Transition Source**: `getTaskStateMachineConfig()` in `TaskStateMachine.ts`
80
+
81
+ API-driven transition from `voice/Voice.ts`:
82
+
83
+ ```typescript
84
+ // task.hold() / task.resume() -> holdResume()
85
+ stateMachineService.send({type: TaskEvent.HOLD_INITIATED, mediaResourceId});
86
+ // ... backend call succeeds
87
+ stateMachineService.send({type: TaskEvent.HOLD_SUCCESS, mediaResourceId});
88
+ ```
89
+
90
+ Backend-driven transition from `TaskManager.ts`:
91
+
92
+ ```typescript
93
+ const eventPayload = TaskManager.mapEventToTaskStateMachineEvent(
94
+ CC_EVENTS.AGENT_CONTACT_RESERVED,
95
+ taskData
96
+ );
97
+ if (eventPayload) {
98
+ task.sendStateMachineEvent(eventPayload);
99
+ }
100
+ ```
101
+
102
+ ---
103
+
104
+
105
+
106
+ ### Transition Contract
107
+
108
+ Backend CC events from WebSocket are mapped to `TaskEvent` in `TaskManager.mapEventToTaskStateMachineEvent`.
109
+ The state machine consumes only `TaskEvent` and never raw CC events.
110
+
111
+ ### Payload Contract
112
+
113
+ Source of truth: `TaskEventPayloadMap` in `types.ts`.
114
+ All new events must add a typed payload entry in `TaskEventPayloadMap`.
115
+
116
+ ---
117
+
118
+
119
+
120
+ ## Non-goals
121
+
122
+ - API contracts for external services.
123
+ - Mercury or CC WebSocket protocols (see `TaskManager.ts` mapping).
124
+
125
+
126
+
127
+ ## Guards
128
+
129
+ Guards are boolean conditions that determine determine if a state transition is allowed. These functions validate the current context before allowing transitions.
130
+
131
+ ### Principles
132
+
133
+ - Guards must be pure and must return boolean only
134
+ - No mutation or side-effects.
135
+ - Reuse helper accessors (e.g., `getTaskDataFromEvent`).
136
+
137
+
138
+
139
+ ### State-Based Guards
140
+
141
+ ```typescript
142
+ // Check if interaction is in terminated state
143
+ isInteractionTerminated(context, event) {
144
+ return event.taskData?.interaction?.isTerminated === true;
145
+ }
146
+
147
+ // Check if interaction is consulting
148
+ isInteractionConsulting(context, event) {
149
+ return event.taskData?.interaction?.state === 'consulting';
150
+ }
151
+
152
+ // Check if interaction is held
153
+ isInteractionHeld(context, event) {
154
+ return event.taskData?.interaction?.state === 'hold';
155
+ }
156
+
157
+ // Check if interaction is connected
158
+ isInteractionConnected(context, event) {
159
+ return event.taskData?.interaction?.state === 'connected';
160
+ }
161
+ ```
162
+
163
+
164
+
165
+ ### Consult Guards
166
+
167
+ ```typescript
168
+ // Check if current agent initiated consult
169
+ didInitiateConsult(context, event) {
170
+ if (event.taskData?.isConsulted === true) return false;
171
+ return event.taskData?.consultingAgentId
172
+ ? isSelfConsultingAgent(context, event.taskData)
173
+ : context.consultInitiator === true;
174
+ }
175
+ ```
176
+
177
+
178
+
179
+ ### Conference Guards
180
+
181
+ ```typescript
182
+ // Check if conference is in progress from event taskData
183
+ conferenceInProgressFromEvent(context, event) {
184
+ const taskData = event.taskData;
185
+ if (!taskData?.interaction) return false;
186
+ return getIsConferenceInProgress(taskData);
187
+ }
188
+
189
+ // Check if conference is in progress by participants
190
+ isConferencingByParticipants(context, event) {
191
+ const taskData = event.taskData;
192
+ if (!taskData) return false;
193
+
194
+ const mainCallId = taskData.interaction?.mainInteractionId || taskData.interactionId;
195
+ const media = taskData.interaction?.media?.[mainCallId];
196
+ const participants = taskData.interaction?.participants;
197
+ if (!media?.participants || !participants) return false;
198
+
199
+ let agentCount = 0;
200
+ for (const pId of media.participants) {
201
+ const p = participants[pId];
202
+ if (p && p.pType !== 'Customer' && p.pType !== 'Supervisor' && !p.hasLeft) {
203
+ agentCount += 1;
204
+ }
205
+ }
206
+
207
+ return agentCount >= 2;
208
+ }
209
+
210
+ // Check if conference should downgrade to connected
211
+ shouldDowngradeConferenceToConnected(context, event) {
212
+ const taskData = event.taskData ?? context.taskData;
213
+ if (!taskData?.interaction) return false;
214
+
215
+ const selfAgentId = getSelfAgentId(context, taskData);
216
+ if (!selfAgentId) return false;
217
+
218
+ const mainCallId = taskData?.interaction?.mainInteractionId || taskData?.interactionId;
219
+ if (!mainCallId) return false;
220
+
221
+ // Do not downgrade while backend still reports active conference state
222
+ if (taskData.interaction.state === 'conference') return false;
223
+
224
+ const agentParticipantsCount = getConferenceParticipantsCount(taskData?.interaction, mainCallId);
225
+ if (agentParticipantsCount >= 2) return false;
226
+
227
+ const customerInCall = getIsCustomerInCall(taskData?.interaction, mainCallId);
228
+ if (!customerInCall) return false;
229
+
230
+ const selfInMainCall = Boolean(
231
+ taskData?.interaction?.media?.[mainCallId]?.participants?.includes(selfAgentId)
232
+ );
233
+ return selfInMainCall;
234
+ }
235
+ ```
236
+
237
+
238
+
239
+ ### Wrapup Guards
240
+
241
+ ```typescript
242
+ // Check if this agent should move to wrapup
243
+ shouldWrapUp(context, event) {
244
+ const taskData = event.taskData;
245
+ if (!taskData) return false;
246
+
247
+ if (event.type === TaskEvent.CONFERENCE_END) {
248
+ const selfAgentId = getSelfAgentId(context, taskData);
249
+ if (!selfAgentId) return false;
250
+
251
+ const pending = taskData.agentsPendingWrapUp;
252
+ if (Array.isArray(pending) && pending.length > 0) {
253
+ return pending.includes(selfAgentId);
254
+ }
255
+
256
+ const participantWrapUp = taskData.interaction?.participants?.[selfAgentId]?.isWrapUp === true;
257
+ const wrapUpRequired = taskData.wrapUpRequired === true;
258
+ return wrapUpRequired || participantWrapUp;
259
+ }
260
+
261
+ return shouldWrapUpForThisAgent(context, taskData);
262
+ }
263
+
264
+ // Check if wrapup is required OR current agent is consult initiator
265
+ shouldWrapUpOrIsInitiator(context, event) {
266
+ return Boolean(event.taskData?.wrapUpRequired || context.consultInitiator);
267
+ }
268
+
269
+ // Check whether the leaving participant is the current agent
270
+ didCurrentAgentLeaveConference(context, event) {
271
+ const selfAgentId = getSelfAgentId(context, event.taskData);
272
+ if (!selfAgentId) return false;
273
+
274
+ const participantIdFromEvent = 'participantId' in event ? event.participantId : undefined;
275
+ const participantId = participantIdFromEvent ?? event.taskData?.participantId;
276
+ return Boolean(participantId) && participantId === selfAgentId;
277
+ }
278
+ ```
279
+
280
+
281
+
282
+ ### Server State Guards
283
+
284
+ ```typescript
285
+ // Check if primary media leg is on hold
286
+ isPrimaryMediaOnHold(context, event) {
287
+ const taskData = event.taskData;
288
+ if (!taskData || !taskData.mediaResourceId) return false;
289
+
290
+ return taskData.interaction?.media?.[taskData.mediaResourceId]?.isHold === true;
291
+ }
292
+ ```
293
+
294
+ ---
295
+
296
+
297
+
298
+ ## Actions
299
+
300
+ Actions are side effects executed during state machine transitions from current state to target state(next state).
301
+ Actions contain:
302
+
303
+ - Context synchronization (`initializeTask`, `updateTaskData`, `syncTaskDataFromEvent`)
304
+ - Lifecycle mutations (`clearConsultState`, `markEnded`, consult/conference flags)
305
+ - Integration hooks (`requestAutoAnswer`, `requestCleanup`, emitter placeholders)
306
+
307
+
308
+
309
+ ### Principles
310
+
311
+ - Context mutations should be centralized in `assign(...)` actions
312
+ - Emitter actions intentionally no-op defaults and overridden by `Task` to bridge machine transitions to SDK events.
313
+ - Deterministic updates from `taskData`.
314
+
315
+
316
+
317
+ ### Context Update Actions
318
+
319
+ ```typescript
320
+ // Initialize context for incoming task
321
+ initializeTask(context, event) {
322
+ return {
323
+ consultInitiator: false,
324
+ exitingConference: false,
325
+ consultDestinationType: null,
326
+ consultDestinationAgentJoined: false,
327
+ ...deriveTaskDataUpdates(context, event.taskData),
328
+ };
329
+ }
330
+
331
+ // Update taskData + derived recording/consult fields
332
+ updateTaskData(context, event) {
333
+ return deriveTaskDataUpdates(context, event.taskData);
334
+ }
335
+
336
+ // Keep Task instance data in sync (Task.ts action override)
337
+ syncTaskDataFromEvent(event) {
338
+ this.updateTaskFromEvent(event);
339
+ }
340
+
341
+ // Update hold flag on specific media leg in context.taskData.interaction.media
342
+ setHoldState(context, event) {
343
+ // Handles HOLD_SUCCESS and UNHOLD_SUCCESS for event.mediaResourceId
344
+ }
345
+
346
+ // Conference/consult lifecycle mutators
347
+ handleConferenceStarted() { return {consultInitiator: false}; }
348
+ handleConsultFailed() { return {consultDestinationAgentJoined: false, consultInitiator: false}; }
349
+ handleParticipantLeft(event) { return event.taskData ? {taskData: event.taskData} : {}; }
350
+ handleTransferConferenceSuccess(event) { return event.taskData ? {taskData: event.taskData} : {}; }
351
+
352
+ // Consult destination and mode flags
353
+ setConsultDestination(event) { /* sets consultDestinationType and resets consult flags */ }
354
+ setConsultFromConference() { return {consultFromConference: true}; }
355
+ setConsultAgentJoined(event) { /* sets consultDestinationAgentJoined on CONSULTING_ACTIVE */ }
356
+ setExitingConference() { return {exitingConference: true}; }
357
+
358
+ // Conference transfer flags
359
+ setTransferConferenceRequested() { return {transferConferenceRequested: true}; }
360
+ clearTransferConferenceRequested() { return {transferConferenceRequested: false}; }
361
+
362
+ // Consult call hold flags
363
+ setConsultCallHeld() { return {consultCallHeld: true}; }
364
+ clearConsultCallHeld() { return {consultCallHeld: false}; }
365
+
366
+ // Recording state mutator for pause/resume events
367
+ setRecordingState(event) {
368
+ // PAUSE_RECORDING => recordingInProgress false
369
+ // RESUME_RECORDING => recordingInProgress true
370
+ }
371
+
372
+ // Reset consult/conference-related context
373
+ clearConsultState() {
374
+ return {
375
+ consultDestinationType: null,
376
+ consultDestinationAgentJoined: false,
377
+ consultInitiator: false,
378
+ exitingConference: false,
379
+ consultCallHeld: false,
380
+ consultFromConference: false,
381
+ transferConferenceRequested: false,
382
+ };
383
+ }
384
+
385
+ // End-of-task cleanup for recording flags
386
+ markEnded() {
387
+ return {recordingControlsAvailable: false, recordingInProgress: false};
388
+ }
389
+ ```
390
+
391
+ > Note: `forceConsultInitiator`, `handleConferenceFailed`, `handleSwitchToMainCall`, and
392
+ > `handleSwitchToConsult` are not present in current `actions.ts`/`TaskStateMachine.ts`.
393
+
394
+
395
+
396
+ ### Event Emission Actions
397
+
398
+ ```typescript
399
+ // Emit task incoming
400
+ emitTaskIncoming(context, event) {
401
+ task.emit(TASK_EVENTS.TASK_INCOMING, task);
402
+ }
403
+
404
+ // Emit task assigned
405
+ emitTaskAssigned(context, event) {
406
+ task.emit(TASK_EVENTS.TASK_ASSIGNED, task);
407
+ }
408
+
409
+ // Emit task hold
410
+ emitTaskHold(context, event) {
411
+ task.emit(TASK_EVENTS.TASK_HOLD, task);
412
+ }
413
+
414
+ // Emit task wrapup
415
+ emitTaskWrapup(context, event) {
416
+ if (context.taskData.wrapUpRequired) {
417
+ task.emit(TASK_EVENTS.TASK_WRAPUP, task);
418
+ }
419
+ }
420
+
421
+ // ... more emission actions for each event type
422
+ ```
423
+
424
+
425
+
426
+ ### Cleanup Actions
427
+
428
+ ```typescript
429
+ // NOTE: These are no-op placeholders in actions.ts and are overridden in Task.ts.
430
+
431
+ // Request cleanup (remove from collection, keep task object)
432
+ requestCleanup(context, event) {
433
+ task.emit(TASK_EVENTS.TASK_CLEANUP, task, {removeFromCollection: false});
434
+ }
435
+
436
+ // Cleanup resources (remove from collection)
437
+ cleanupResources(context, event) {
438
+ task.emit(TASK_EVENTS.TASK_CLEANUP, task, {removeFromCollection: true});
439
+ }
440
+ ```
441
+
442
+
443
+
444
+ ### Auto-Answer Actions
445
+
446
+ ```typescript
447
+ // NOTE: requestAutoAnswer is a placeholder in actions.ts and is overridden in Task.ts.
448
+
449
+ // Request auto-answer
450
+ requestAutoAnswer(context, event) {
451
+ if (event.taskData?.isAutoAnswering) {
452
+ // Trigger accept() method
453
+ autoAnswerIfNeeded();
454
+ }
455
+ }
456
+ ```
457
+
458
+ ---