@webex/contact-center 3.12.0-next.84 → 3.12.0-next.85

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 (47) hide show
  1. package/.sdd/manifest.json +876 -0
  2. package/AGENTS.md +65 -409
  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 +25 -18
  8. package/ai-docs/REVIEW_CHECKLIST.md +41 -0
  9. package/ai-docs/RULES.md +187 -198
  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/templates/README.md +1 -1
  21. package/ai-docs/templates/new-method/04-validation.md +1 -1
  22. package/ai-docs/templates/new-service/05-validation.md +1 -1
  23. package/dist/webex.js +1 -1
  24. package/package.json +1 -1
  25. package/src/metrics/ai-docs/AGENTS.md +2 -0
  26. package/src/metrics/ai-docs/ARCHITECTURE.md +2 -0
  27. package/src/metrics/ai-docs/metrics-spec.md +854 -0
  28. package/src/services/agent/ai-docs/AGENTS.md +3 -1
  29. package/src/services/agent/ai-docs/ARCHITECTURE.md +2 -0
  30. package/src/services/agent/ai-docs/agent-spec.md +504 -0
  31. package/src/services/ai-docs/AGENTS.md +4 -2
  32. package/src/services/ai-docs/services-spec.md +492 -0
  33. package/src/services/config/ai-docs/AGENTS.md +3 -1
  34. package/src/services/config/ai-docs/ARCHITECTURE.md +2 -0
  35. package/src/services/config/ai-docs/config-spec.md +669 -0
  36. package/src/services/core/ai-docs/AGENTS.md +4 -2
  37. package/src/services/core/ai-docs/ARCHITECTURE.md +2 -0
  38. package/src/services/core/ai-docs/core-spec.md +783 -0
  39. package/src/services/task/ai-docs/AGENTS.md +2 -0
  40. package/src/services/task/ai-docs/ARCHITECTURE.md +11 -2
  41. package/src/services/task/ai-docs/task-spec.md +1319 -0
  42. package/src/services/task/state-machine/ai-docs/AGENTS.md +38 -75
  43. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +6 -4
  44. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2115 -0
  45. package/src/utils/AGENTS.md +3 -1
  46. package/src/utils/ai-docs/utils-spec.md +381 -0
  47. package/umd/contact-center.min.js +2 -2
@@ -0,0 +1,1319 @@
1
+ # Task — SPEC
2
+
3
+ > Start here → root [`AGENTS.md`](../../../../AGENTS.md) · router [`SPEC_INDEX.md`](../../../../ai-docs/SPEC_INDEX.md) · system [`ARCHITECTURE.md`](../../../../ai-docs/ARCHITECTURE.md). This is the module's canonical specification.
4
+
5
+ ## Metadata
6
+
7
+ | Field | Value |
8
+ |---|---|
9
+ | Module id | `task` |
10
+ | Source path(s) | `src/services/task` |
11
+ | Doc kind | Module spec |
12
+ | Coverage score | Partial (manifest-authoritative); 15/15 required document fields present |
13
+ | Generated from | `module-spec` @ SDLC template library `0.2.1` |
14
+ | generated_by / approved_by / updated_at | Codex generator / developer-approved follow-up review remediation / 2026-07-21 |
15
+ | Validation status | Follow-up validation passed (independent Claude fallback, 2026-07-21); 1 existing test-coverage gap; coverage remains Partial |
16
+
17
+ ## Evidence Rules
18
+ Every requirement cites stable source and test file paths. Code/tests are the behavioral referee; routed source text supplies explicit intent and rationale. Missing or contradictory evidence blocks promotion.
19
+
20
+ ## Source Material Register
21
+ | Source material | Scope | Decision | Detail location or disposition |
22
+ |---|---|---|---|
23
+ | Reviewed prior module guides and architecture material | overview / architecture / API / tests | used and code-checked | Content is placed by meaning throughout this specification; exact routing remains in the manifest. |
24
+
25
+ ## Overview
26
+ Task is one of nine confirmed Contact Center SDK modules. Own task creation, media-specific behavior, call-control operations, lifecycle orchestration, task events, and integration with the task state machine. Existing reviewed documentation is migrated by meaning and code/tests remain the behavioral referee.
27
+
28
+ Manage task lifecycle including inbound/outbound calls, hold/resume, consult, transfer, conference, and wrapup.
29
+
30
+ - **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.
31
+
32
+ - **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.
33
+
34
+ - **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.
35
+
36
+ - **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.
37
+
38
+ - **Outbound and Preview-Campaign Dialing**: `dialer.ts` exposes `startOutdial` plus `acceptPreviewContact`, `skipPreviewContact`, and `removePreviewContact`; ContactCenter publishes these operations through typed `cc` methods.
39
+
40
+ - **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/task-state-machine-spec.md`.
41
+
42
+ 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.
43
+
44
+ - **Listener Setup**: Registers WebSocket listeners to receive CC events and map them to `TaskEvent` payloads.
45
+
46
+ - **Task Registry**: Creates tasks via `TaskFactory`, stores them in the task collection, and updates task data on incoming events.
47
+
48
+ - **Event Emission**: Re-emits `task:*` events on the task or `cc` object for SDK consumers.
49
+
50
+ - **Hydration/Recovery**: Handles state updates and transitions during reconnect/hydrate flows.
51
+
52
+ Example (backend event to state machine):
53
+
54
+ ```typescript
55
+ const payload = TaskManager.mapEventToTaskStateMachineEvent(event, taskData);
56
+ if (payload) {
57
+ task.sendStateMachineEvent(payload);
58
+ }
59
+ ```
60
+
61
+ - **`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.
62
+
63
+ - **`dialer.ts`**: Exposes `startOutdial` and the three preview-campaign AQM requests used by `cc.startOutdial()`, `cc.acceptPreviewContact()`, `cc.skipPreviewContact()`, and `cc.removePreviewContact()`.
64
+
65
+ Example (task method delegating to AQM):
66
+
67
+ ```typescript
68
+ // task.hold() -> contact.hold(...) -> stateMachine events on response
69
+ await contact.hold({interactionId});
70
+ stateMachineService.send({type: TaskEvent.HOLD_INITIATED, mediaResourceId});
71
+ ```
72
+
73
+ ## Purpose / Responsibility
74
+ Own task creation, media-specific behavior, call-control operations, lifecycle orchestration, task events, and integration with the task state machine.
75
+
76
+ ## Stack
77
+ TypeScript 5.4, EventEmitter, XState 5, @webex/calling, AQM/WebSocket integrations, Jest 27.
78
+
79
+ ## Folder / Package Structure
80
+ ```text
81
+ src/services/task/
82
+ ├── AutoWrapup.ts
83
+ ├── Task.ts
84
+ ├── TaskFactory.ts
85
+ ├── TaskManager.ts
86
+ ├── TaskUtils.ts
87
+ ├── constants.ts
88
+ ├── contact.ts
89
+ ├── dialer.ts
90
+ ├── digital/
91
+ ├── state-machine/
92
+ ├── taskDataNormalizer.ts
93
+ ├── types.ts
94
+ ├── voice/
95
+ ```
96
+
97
+ ```text
98
+ services/task/
99
+ ├── Task.ts # Task class (ITask implementation)
100
+ ├── TaskManager.ts # Singleton task manager
101
+ ├── contact.ts # Contact operations (AQM)
102
+ ├── dialer.ts # Outbound dialing (AQM)
103
+ ├── AutoWrapup.ts # Auto wrapup handler
104
+ ├── TaskUtils.ts # Helper functions
105
+ ├── types.ts # Task types and events
106
+ ├── constants.ts # Task constants
107
+ ├── TaskFactory.ts # Task factory
108
+ ├── taskDataNormalizer.ts # Task data normalization helpers
109
+ ├── digital/ # Digital task implementations
110
+ │ └── Digital.ts
111
+ ├── voice/ # Voice task implementations
112
+ │ ├── Voice.ts
113
+ │ └── WebRTC.ts
114
+ ├── state-machine/ # XState task lifecycle engine
115
+ │ ├── TaskStateMachine.ts
116
+ │ ├── actions.ts
117
+ │ ├── guards.ts
118
+ │ ├── uiControlsComputer.ts
119
+ │ ├── constants.ts
120
+ │ ├── types.ts
121
+ │ └── ai-docs/
122
+ │ ├── AGENTS.md
123
+ │ └── ARCHITECTURE.md
124
+ └── ai-docs/
125
+ ├── AGENTS.md # Usage documentation
126
+ └── ARCHITECTURE.md # Task service architecture
127
+ ```
128
+
129
+ ## Key Files (source of truth)
130
+ | File | Holds |
131
+ |---|---|
132
+ | `src/services/task/Task.ts` | Authoritative Task implementation or contract source. |
133
+ | `src/services/task/TaskManager.ts` | Authoritative Task implementation or contract source. |
134
+ | `src/services/task/TaskFactory.ts` | Authoritative Task implementation or contract source. |
135
+ | `src/services/task/contact.ts` | Authoritative Task implementation or contract source. |
136
+ | `src/services/task/dialer.ts` | Authoritative Task implementation or contract source. |
137
+ | `src/services/task/types.ts` | Authoritative Task implementation or contract source. |
138
+
139
+ ## Public Surface
140
+ | Contract ID | Type | Surface | Purpose | Compatibility / deprecation | Schema / detail link | Root index |
141
+ |---|---|---|---|---|---|---|
142
+ | `task.surface` | SDK / event / internal API | Exported Task/types/events plus application-facing task instances and call-control methods. | Stable module consumption boundary. | Additive changes by default; breaking package exports require a major-version transition. | `src/services/task/Task.ts` | `../../../../ai-docs/CONTRACTS.md` |
143
+ | `task.preview-campaign` | SDK/AQM API | `acceptPreviewContact`, `skipPreviewContact`, `removePreviewContact`, and `PreviewContactPayload`. | Accept, skip, or remove a reserved campaign preview contact; each method returns `Promise<TaskResponse>`. | Additive semver-public methods; removals or signature changes are breaking. | `src/cc.ts`, `src/services/task/dialer.ts`, `src/services/task/types.ts` | `../../../../ai-docs/CONTRACTS.md` |
144
+
145
+ Compatibility notes:
146
+ - Do not remove or reinterpret exported symbols/events without a documented consumer migration.
147
+
148
+ - `TASK_EVENTS` enum (`types.ts`)
149
+
150
+ - `TaskData`, `TaskId`, `TaskResponse`, `TaskUIControls` (`types.ts`)
151
+
152
+ - `PreviewContactPayload` (`types.ts`) with `interactionId` and campaign-name `campaignId`
153
+
154
+ - `ITask`, `IVoice`, `IWebRTC`, `IDigital` (`types.ts`)
155
+
156
+ - `MEDIA_CHANNEL`, `TASK_CHANNEL_TYPE`, `VOICE_VARIANT` (`types.ts`)
157
+
158
+ - State machine: `TaskState`, `TaskEvent` (`state-machine/constants.ts`)
159
+
160
+ | Event | When Emitted |
161
+ |---|---|
162
+ | `task:incoming` | New task offered to agent |
163
+ | `task:hydrate` | Task data updated |
164
+ | `task:merged` | Tasks merged (EPDN transfer) |
165
+
166
+ | Event | When Emitted |
167
+ |---|---|
168
+ | `task:assigned` | Task assigned to agent |
169
+ | `task:media` | Media stream/track updates are available |
170
+ | `task:unassigned` | Task is unassigned from agent |
171
+ | `task:offerContact` | Contact offer received/updated |
172
+ | `task:offerConsult` | Consult offer received |
173
+ | `task:hold` | Task placed on hold |
174
+ | `task:resume` | Task resumed from hold |
175
+ | `task:end` | Task ended |
176
+ | `task:rejected` | Task rejected / failure path emitted |
177
+ | `task:wrapup` | Task entering wrapup |
178
+ | `task:wrappedup` | Wrapup completed |
179
+ | `task:consulting` | Consult is in progress |
180
+ | `task:consultAccepted` | Consult accepted by destination party |
181
+ | `task:consultCreated` | Consultation started |
182
+ | `task:consultEnd` | Consultation ended |
183
+ | `task:autoAnswered` | Task was auto-answered |
184
+ | `task:recordingStarted` / `task:recordingPaused` / `task:recordingResumed` | Recording lifecycle updates |
185
+ | `task:conferenceStarted` / `task:conferenceEnded` / `task:conferenceFailed` | Conference lifecycle updates |
186
+ | `task:participantJoined` / `task:participantLeft` | Conference participant updates |
187
+ | `task:switchCall` | Switched between consult and main call |
188
+ | `task:outdialFailed` | Outdial operation failed |
189
+ | `task:ui-controls-updated` | UI controls changed due to state transition |
190
+ | `task:cleanup` | Internal cleanup signal emitted by state machine |
191
+
192
+ > Full list is defined in `TASK_EVENTS` (`types.ts`).
193
+
194
+ | Event | When Emitted |
195
+ |---|---|
196
+ | `REAL_TIME_TRANSCRIPTION` | A realtime transcript payload is received for the task interaction |
197
+ | `SUGGESTED_RESPONSE` | A final AI Assistant suggestion payload is received for the task interaction |
198
+
199
+ Initiate outbound call.
200
+
201
+ **Parameters**:
202
+
203
+ - `destination` (string): Phone number to call
204
+
205
+ - `origin` (string): Outbound ANI/caller ID
206
+
207
+ **Returns**: `Promise<TaskResponse>` (AQM response, not a Task instance)
208
+
209
+ **Example**:
210
+
211
+ > **Host-application example:** `updateCallStatus` represents consumer-owned UI handling. SDK package implementation must use `LoggerProxy` and must not log raw runtime values.
212
+
213
+ ```typescript
214
+ const response = await cc.startOutdial('+14155551234', '+18005551000');
215
+
216
+ // Outdial task object is created asynchronously via TaskManager.
217
+ // Listen on cc/task events instead of treating startOutdial response as an ITask.
218
+ cc.on('task:incoming', (task) => {
219
+ task.on('task:assigned', () => {
220
+ updateCallStatus('connected');
221
+ });
222
+
223
+ task.on('task:end', () => {
224
+ updateCallStatus('ended');
225
+ });
226
+ });
227
+ ```
228
+
229
+ Accept an incoming task.
230
+
231
+ **Returns**: `Promise<TaskResponse>`
232
+
233
+ **Example**:
234
+
235
+ ```typescript
236
+ cc.on('task:incoming', async (task) => {
237
+ await task.accept();
238
+ });
239
+ ```
240
+
241
+ Put task on hold or resume.
242
+
243
+ **Parameters**:
244
+
245
+ - Concrete `Task.hold()` / `Voice.hold()` and `Task.resume()` / `Voice.resume()` accept no parameters. The optional `mediaResourceId` exists only on the broader `ITask` compatibility declaration; the implementations derive the active media resource from task state.
246
+
247
+ **Returns**: `Promise<TaskResponse>`
248
+
249
+ **Example**:
250
+
251
+ ```typescript
252
+ // Put on hold
253
+ await task.hold();
254
+
255
+ // Resume
256
+ await task.resume();
257
+ ```
258
+
259
+ End the current task.
260
+
261
+ **Returns**: `Promise<TaskResponse>`
262
+
263
+ **Example**:
264
+
265
+ ```typescript
266
+ await task.end();
267
+ ```
268
+
269
+ Complete task with wrapup code.
270
+
271
+ **Parameters**:
272
+
273
+ - `wrapUpReason` (string, required): Wrapup reason text
274
+
275
+ - `auxCodeId` (string, required): Wrapup code ID
276
+
277
+ **Returns**: `Promise<TaskResponse>`
278
+
279
+ **Example**:
280
+
281
+ ```typescript
282
+ await task.wrapup({
283
+ wrapUpReason: 'Customer issue resolved',
284
+ auxCodeId: 'resolved-code',
285
+ });
286
+ ```
287
+
288
+ Transfer task to another destination.
289
+
290
+ **Parameters**:
291
+
292
+ - `to` (string): Agent ID, queue ID, or phone number
293
+
294
+ - `destinationType` ('queue' | 'agent' | 'dialNumber'): Destination type
295
+
296
+ **Returns**: `Promise<TaskResponse>`
297
+
298
+ **Example**:
299
+
300
+ ```typescript
301
+ // Transfer to queue
302
+ await task.transfer({
303
+ to: 'queue-123',
304
+ destinationType: 'queue',
305
+ });
306
+
307
+ // Transfer to agent
308
+ await task.transfer({
309
+ to: 'agent-456',
310
+ destinationType: 'agent',
311
+ });
312
+ ```
313
+
314
+ Start consultation.
315
+
316
+ **Parameters**:
317
+
318
+ - `to` (string): Agent/queue/phone to consult
319
+
320
+ - `destinationType` ('queue' | 'agent' | 'dialNumber' | 'entryPoint'): Type
321
+
322
+ **Returns**: `Promise<TaskResponse>`
323
+
324
+ **Example**:
325
+
326
+ ```typescript
327
+ await task.consult({
328
+ to: 'agent-456',
329
+ destinationType: 'agent',
330
+ });
331
+
332
+ // Later: complete transfer (consulting voice flow uses transfer())
333
+ await task.transfer({
334
+ to: 'queue-123',
335
+ destinationType: 'queue',
336
+ });
337
+ // Or end consult
338
+ await task.endConsult();
339
+ ```
340
+
341
+ End consultation without transfer.
342
+
343
+ **Parameters**:
344
+
345
+ - `consultEndPayload` (optional `ConsultEndPayload`)
346
+
347
+ **Returns**: `Promise<TaskResponse>`
348
+
349
+ ### Complete TASK_EVENTS inventory
350
+
351
+ The public `TASK_EVENTS` enum contains 49 members; every member is listed below from `src/services/task/types.ts`.
352
+
353
+ | Constant | Event string |
354
+ |---|---|
355
+ | `TASK_INCOMING` | `task:incoming` |
356
+ | `TASK_ASSIGNED` | `task:assigned` |
357
+ | `TASK_MEDIA` | `task:media` |
358
+ | `TASK_UNASSIGNED` | `task:unassigned` |
359
+ | `TASK_HOLD` | `task:hold` |
360
+ | `TASK_RESUME` | `task:resume` |
361
+ | `TASK_CONSULT_END` | `task:consultEnd` |
362
+ | `TASK_CONSULT_QUEUE_CANCELLED` | `task:consultQueueCancelled` |
363
+ | `TASK_CONSULT_QUEUE_FAILED` | `task:consultQueueFailed` |
364
+ | `TASK_UI_CONTROLS_UPDATED` | `task:ui-controls-updated` |
365
+ | `TASK_CONSULT_ACCEPTED` | `task:consultAccepted` |
366
+ | `TASK_CONSULTING` | `task:consulting` |
367
+ | `TASK_CONSULT_CREATED` | `task:consultCreated` |
368
+ | `TASK_OFFER_CONSULT` | `task:offerConsult` |
369
+ | `TASK_END` | `task:end` |
370
+ | `TASK_WRAPUP` | `task:wrapup` |
371
+ | `TASK_WRAPPEDUP` | `task:wrappedup` |
372
+ | `TASK_CLEANUP` | `task:cleanup` |
373
+ | `TASK_RECORDING_STARTED` | `task:recordingStarted` |
374
+ | `TASK_RECORDING_PAUSED` | `task:recordingPaused` |
375
+ | `TASK_RECORDING_PAUSE_FAILED` | `task:recordingPauseFailed` |
376
+ | `TASK_RECORDING_RESUMED` | `task:recordingResumed` |
377
+ | `TASK_RECORDING_RESUME_FAILED` | `task:recordingResumeFailed` |
378
+ | `TASK_REJECT` | `task:rejected` |
379
+ | `TASK_OUTDIAL_FAILED` | `task:outdialFailed` |
380
+ | `TASK_HYDRATE` | `task:hydrate` |
381
+ | `TASK_OFFER_CONTACT` | `task:offerContact` |
382
+ | `TASK_AUTO_ANSWERED` | `task:autoAnswered` |
383
+ | `TASK_CONFERENCE_ESTABLISHING` | `task:conferenceEstablishing` |
384
+ | `TASK_CONFERENCE_STARTED` | `task:conferenceStarted` |
385
+ | `TASK_CONFERENCE_FAILED` | `task:conferenceFailed` |
386
+ | `TASK_CONFERENCE_ENDED` | `task:conferenceEnded` |
387
+ | `TASK_PARTICIPANT_JOINED` | `task:participantJoined` |
388
+ | `TASK_PARTICIPANT_LEFT` | `task:participantLeft` |
389
+ | `TASK_CONFERENCE_TRANSFERRED` | `task:conferenceTransferred` |
390
+ | `TASK_CONFERENCE_TRANSFER_FAILED` | `task:conferenceTransferFailed` |
391
+ | `TASK_CONFERENCE_END_FAILED` | `task:conferenceEndFailed` |
392
+ | `TASK_PARTICIPANT_LEFT_FAILED` | `task:participantLeftFailed` |
393
+ | `TASK_EXIT_CONFERENCE` | `task:exitConference` |
394
+ | `TASK_TRANSFER_CONFERENCE` | `task:transferConference` |
395
+ | `TASK_SWITCH_CALL` | `task:switchCall` |
396
+ | `TASK_MERGED` | `task:merged` |
397
+ | `TASK_POST_CALL_ACTIVITY` | `task:postCallActivity` |
398
+ | `TASK_MULTI_LOGIN_HYDRATE` | `task:multiLoginHydrate` |
399
+ | `TASK_CAMPAIGN_PREVIEW_RESERVATION` | `task:campaignPreviewReservation` |
400
+ | `TASK_CAMPAIGN_PREVIEW_ACCEPT_FAILED` | `task:campaignPreviewAcceptFailed` |
401
+ | `TASK_CAMPAIGN_PREVIEW_SKIP_FAILED` | `task:campaignPreviewSkipFailed` |
402
+ | `TASK_CAMPAIGN_PREVIEW_REMOVE_FAILED` | `task:campaignPreviewRemoveFailed` |
403
+ | `TASK_CAMPAIGN_CONTACT_UPDATED` | `task:campaignContactUpdated` |
404
+
405
+ ## Requires (dependencies)
406
+ - Services contact/dialer AQM factories
407
+ - WebSocket and RTD WebSocket managers
408
+ - WebCallingService, MetricsManager, and task state machine
409
+
410
+ ## Requirements
411
+ | ID | WHAT | WHY | Source Evidence | Test / Example Evidence | Assumptions / Gaps | Confidence |
412
+ |---|---|---|---|---|---|---|
413
+ | TASK-R-001 | Create only supported Voice/Digital Task implementations and throw `Unknown media type` for unsupported media. | Returning a generic task for unsupported channels would advertise controls the implementation cannot perform. | `src/services/task/TaskFactory.ts` | `test/unit/spec/services/task/TaskFactory.ts` | None; source and test evidence rechecked during the 2026-07-09 remediation; independent document revalidation pending. | PRESENT |
414
+ | TASK-R-002 | Concrete Task/Voice `hold()` and `resume()` implementations remain parameterless while `ITask` retains its optional compatibility parameter. | Documentation must distinguish the broad public interface from concrete runtime signatures. | `src/services/task/Task.ts` | `test/unit/spec/services/task/Task.ts` | None; source and test evidence rechecked during the 2026-07-09 remediation; independent document revalidation pending. | PRESENT |
415
+ | TASK-R-003 | Task and media subclasses must route contact/calling operations into typed state-machine events and emit the complete TASK_EVENTS contract. | Consumers coordinate UI and interaction lifecycle from those events. | `src/services/task/Task.ts` | `test/unit/spec/services/task/Task.ts` | None; source and test evidence rechecked during the 2026-07-09 remediation; independent document revalidation pending. | PRESENT |
416
+ | TASK-R-004 | TaskManager must consume primary/RTD streams and manage task creation, hydration, cleanup, campaign, and AI-assistant flows. | A single task owner prevents duplicate instances and inconsistent state across realtime sources. | `src/services/task/TaskManager.ts` | `test/unit/spec/services/task/TaskManager.ts` | None; source and test evidence rechecked during the 2026-07-09 remediation; independent document revalidation pending. | PRESENT |
417
+ | TASK-R-005 | The contact dependency belongs to Task/TaskFactory-created tasks; dialer is an AqmReqs request factory without that constructor. | Misattributing constructor dependencies causes invalid instantiation examples. | `src/services/task/TaskFactory.ts` | `test/unit/spec/services/task/dialer.ts` | None; source and test evidence rechecked during the 2026-07-09 remediation; independent document revalidation pending. | PRESENT |
418
+ | TASK-R-006 | Keep credentials and authentication outside Task; remote operations delegate through contact/dialer routing, AqmReqs, and Core/WebexRequest. | Task lifecycle objects should never duplicate host token handling or leak authentication state into interaction data. | `src/services/task/Task.ts`, `src/services/task/contact.ts`, `src/services/core/WebexRequest.ts` | `test/unit/spec/services/task/Task.ts`, `test/unit/spec/services/task/contact.ts` | None; authentication ownership is explicit. | PRESENT |
419
+ | TASK-R-007 | Route enabled preview-campaign accept, skip, and remove operations through the dialer AQM factory using `PreviewContactPayload`, returning `Promise<TaskResponse>` from the public ContactCenter methods. Before routing skip/remove, reject the operation when the matching task's disable flag is `'true'`. | Preview reservations require typed payloads and correlated backend completion, while campaign controls must block prohibited skip/remove requests before transport begins. | `src/cc.ts`, `src/services/task/dialer.ts`, `src/services/task/types.ts` | `test/unit/spec/cc.ts`, `test/unit/spec/services/task/dialer.ts` | Public delegation and dialer requests are covered; the `campaignPreviewSkipDisabled` and `campaignPreviewRemoveDisabled` early-exit guards lack direct unit coverage. Independent review identified this gap on 2026-07-15. | PRESENT |
420
+
421
+ ## Design Overview
422
+ Task separates its stable consumption boundary from collaborators so ownership and failure behavior stay explicit. A shared Task base preserves a stable API while media-specific subclasses and a separate state engine enforce capability differences.
423
+
424
+ | Channel | Description |
425
+ |---|---|
426
+ | `telephony` | Voice calls |
427
+ | `chat` | Web chat |
428
+ | `email` | Email interactions |
429
+ | `social` | Social media |
430
+ | `sms` | Unsupported by TaskFactory; throws `Unknown media type` |
431
+ | `facebook` | Unsupported by TaskFactory; throws `Unknown media type` |
432
+ | `whatsapp` | Unsupported by TaskFactory; throws `Unknown media type` |
433
+
434
+ If enabled in agent profile, wrapup completes automatically after timeout:
435
+
436
+ > **Host-application example:** `updateWrapupStatus` is a consumer-owned UI callback, not SDK package logging.
437
+
438
+ ```typescript
439
+ task.on('task:wrappedup', () => {
440
+ updateWrapupStatus('completed');
441
+ });
442
+ ```
443
+
444
+ > **Purpose**: Technical documentation for task lifecycle management.
445
+
446
+ TaskManager is a singleton that:
447
+
448
+ 1. Listens for WebSocket task events
449
+
450
+ 2. Creates/manages Task objects
451
+
452
+ 3. Routes events to appropriate tasks
453
+
454
+ 4. Handles WebRTC call mapping
455
+
456
+ ```typescript
457
+ // Singleton access
458
+ const taskManager = TaskManager.getTaskManager(contact, webCallingService, webSocketManager);
459
+ ```
460
+
461
+ Returns an object of AQM request methods wired to `TASK_API` and `TASK_MESSAGE_TYPE`.
462
+
463
+ **Methods**
464
+
465
+ - `accept`
466
+
467
+ - `hold`
468
+
469
+ - `unHold`
470
+
471
+ - `pauseRecording`
472
+
473
+ - `resumeRecording`
474
+
475
+ - `consult`
476
+
477
+ - `consultEnd`
478
+
479
+ - `consultAccept`
480
+
481
+ - `blindTransfer`
482
+
483
+ - `vteamTransfer`
484
+
485
+ - `consultTransfer`
486
+
487
+ - `end`
488
+
489
+ - `wrapup`
490
+
491
+ - `cancelTask`
492
+
493
+ - `cancelCtq`
494
+
495
+ - `consultConference`
496
+
497
+ - `exitConference`
498
+
499
+ - `conferenceTransfer`
500
+
501
+ **Notes**
502
+
503
+ - Uses `WCC_API_GATEWAY`.
504
+
505
+ - Consult with `DESTINATION_TYPE.QUEUE` uses `TIMEOUT_REQ` = `'disabled'` for the request timeout.
506
+
507
+ Returns an object of AQM request methods for outbound dialing.
508
+
509
+ **Methods**
510
+
511
+ - `startOutdial` (success: `CC_EVENTS.AGENT_OFFER_CONTACT`, failure: `CC_EVENTS.AGENT_OUTBOUND_FAILED`)
512
+
513
+ - Task/TaskFactory-created task instances receive `contact: ReturnType<typeof routingContact>`; `dialer.ts` has no such constructor and is an AqmReqs factory.
514
+
515
+ - Uses:
516
+
517
+ - `contact.vteamTransfer` / `contact.blindTransfer` in `transfer(...)`.
518
+
519
+ - While in consulting state, `transfer(...)` internally routes through consult-transfer behavior.
520
+
521
+ - `contact.end` in `end()`.
522
+
523
+ - `contact.wrapup` in `wrapup(...)`.
524
+
525
+ Uses `contact` for:
526
+
527
+ - `hold`, `unHold`
528
+
529
+ - `pauseRecording`, `resumeRecording`
530
+
531
+ - `consult`, `consultEnd`, `consultTransfer`
532
+
533
+ - `consultConference`, `exitConference`, `conferenceTransfer`
534
+
535
+ Uses `contact.accept` in `accept()`.
536
+
537
+ TaskManager maintains a map of active tasks:
538
+
539
+ ```typescript
540
+ private taskCollection: Record<TaskId, ITask> = {};
541
+
542
+ // Tasks indexed by interactionId
543
+ this.taskCollection[interactionId] = task;
544
+
545
+ // Retrieve task
546
+ const task = this.taskCollection[interactionId];
547
+ ```
548
+
549
+ TaskManager uses a staged pipeline in `registerTaskListeners()`:
550
+
551
+ ```typescript
552
+ this.webSocketManager.on('message', (event) => {
553
+ // 1) Parse and validate message
554
+ const message = TaskManager.parseWebSocketMessage(event);
555
+ if (!message) return;
556
+
557
+ // 2) Build event context (task, payload, mapped state-machine event)
558
+ const eventContext = this.prepareEventContext(message);
559
+ if (!eventContext) return;
560
+
561
+ // 3) Handle lifecycle changes (create/update/remove task)
562
+ const actions = this.handleTaskLifecycleEvent(eventContext);
563
+ const {task} = actions;
564
+ if (!task) return;
565
+
566
+ // 4) Keep task.data synchronized
567
+ const {payload, stateMachineEvent} = eventContext;
568
+ if (payload) this.updateTaskData(task, payload);
569
+
570
+ // 5) Drive state machine (which emits TASK_EVENTS)
571
+ if (stateMachineEvent) {
572
+ task.sendStateMachineEvent(stateMachineEvent);
573
+ }
574
+ });
575
+ ```
576
+
577
+ `TaskManager.handleRealtimeWebsocketEvent()` handles payloads arriving on the realtime subscription socket used for AI features. It:
578
+
579
+ 1. Parses the JSON websocket envelope
580
+
581
+ 2. Reads `conversationId` from `payload.data.data.conversationId` and resolves the owning task
582
+
583
+ 3. Emits the payload type and `payload.data` on the task for `REAL_TIME_TRANSCRIPTION`
584
+
585
+ 4. Does the same for `SUGGESTED_RESPONSE`
586
+
587
+ 5. Logs and returns when JSON parsing fails or the interaction has no task; other payload types, including `SUGGESTED_RESPONSE_ACKNOWLEDGE`, fall through without task emission
588
+
589
+ This keeps transcript and suggestion delivery aligned on the same per-task event surface.
590
+
591
+ For BROWSER login, TaskManager integrates with WebCalling:
592
+
593
+ ```mermaid
594
+ flowchart TD
595
+ A[AgentContactReserved event] --> B[Determine media + loginOption]
596
+ B --> C[TaskFactory chooses Voice/WebRTC class]
597
+ C --> D[Create Task object]
598
+ D --> E[Store in taskCollection]
599
+ E --> F[Send TASK_INCOMING to state machine]
600
+ F --> G[Emit task:incoming]
601
+ H[Independent LINE_EVENTS.INCOMING_CALL] --> I[Find current non-preview telephony task]
602
+ I --> J[Map call ID to interaction ID]
603
+ J --> K[Send TASK_INCOMING association event]
604
+ ```
605
+
606
+ ```typescript
607
+ // WebCallingService maps call IDs to interaction IDs
608
+ this.webCallingService.mapCallToTask(callId, interactionId);
609
+
610
+ // Task uses call for media operations
611
+ this.webCallingService.answerCall(localAudioStream: LocalMicrophoneStream, taskId: string);
612
+ ```
613
+
614
+ AutoWrapup handles automatic task completion:
615
+
616
+ ```typescript
617
+ // AutoWrapup.ts
618
+ export default class AutoWrapup {
619
+ private timer: ReturnType<typeof setTimeout> | null = null;
620
+ private readonly interval: number;
621
+
622
+ start(onComplete: () => void) {
623
+ this.timer = setTimeout(onComplete, this.interval);
624
+ }
625
+
626
+ clear() {
627
+ if (this.timer) {
628
+ clearTimeout(this.timer);
629
+ this.timer = null;
630
+ }
631
+ }
632
+
633
+ getTimeLeft() {}
634
+ isRunning() {}
635
+ getTimeLeftSeconds() {}
636
+ }
637
+ ```
638
+
639
+ Each task operation maps to an AQM request:
640
+
641
+ ```typescript
642
+ // contact.ts
643
+ export default function routingContact(routing: AqmReqs) {
644
+ return {
645
+ accept: routing.req((p) => ({
646
+ url: '/v1/tasks/.../accept',
647
+ notifSuccess: { bind: { type: CC_EVENTS.AGENT_CONTACT_ASSIGNED }},
648
+ notifFail: { bind: { type: CC_EVENTS.AGENT_CONTACT_ASSIGN_FAILED }},
649
+ })),
650
+
651
+ hold: routing.req((p) => ({...})),
652
+ unHold: routing.req((p) => ({...})),
653
+ consultAccept: routing.req((p) => ({...})),
654
+ cancelTask: routing.req((p) => ({...})),
655
+ cancelCtq: routing.req((p) => ({...})),
656
+ end: routing.req((p) => ({...})),
657
+ wrapup: routing.req((p) => ({...})),
658
+ blindTransfer: routing.req((p) => ({...})),
659
+ consult: routing.req((p) => ({...})),
660
+ consultTransfer: routing.req((p) => ({...})),
661
+ // ... more operations
662
+ };
663
+ }
664
+ ```
665
+
666
+ Helper functions for task state analysis:
667
+
668
+ ```typescript
669
+ // TaskUtils.ts
670
+
671
+ // Check if participant is in main interaction
672
+ isParticipantInMainInteraction(task, agentId);
673
+
674
+ // Check if conference is in progress
675
+ getIsConferenceInProgress(taskData);
676
+
677
+ // Check if agent is primary
678
+ isPrimary(task, agentId);
679
+
680
+ // Check if secondary EPDN agent
681
+ isSecondaryEpDnAgent(interaction);
682
+ ```
683
+
684
+ | Metric | Type | When Tracked |
685
+ |---|---|---|
686
+ | `TASK_ACCEPT_SUCCESS` | behavioral, business | Task accepted |
687
+ | `TASK_HOLD_SUCCESS` | operational | Hold succeeded |
688
+ | `TASK_END_SUCCESS` | behavioral, business | Task ended |
689
+ | `TASK_WRAPUP_SUCCESS` | operational | Wrapup completed |
690
+ | `TASK_TRANSFER_SUCCESS` | behavioral, business | Transfer completed |
691
+ | `TASK_OUTDIAL_SUCCESS` | behavioral, business | Outdial completed |
692
+
693
+ ## Data Flow
694
+ 1. **WebSocket event arrives** → `TaskManager` maps CC event to `TaskEvent`.
695
+
696
+ 2. **Task creation** (if new) → `TaskFactory` builds `Voice`/`WebRTC`/`Digital`.
697
+
698
+ 3. **State machine actor starts** → `Task` wires emitters + UI control updates.
699
+
700
+ 4. **Task method called** (e.g., hold/transfer) → delegates to `contact.ts` or `dialer.ts`.
701
+
702
+ 5. **State transitions** → guards/actions update context and emit `task:*` events.
703
+
704
+ 6. **SDK consumers update UI** → `TaskUIControls` reflect the latest state.
705
+
706
+ ```mermaid
707
+ flowchart TD
708
+ A[WebSocket event arrives] --> B[TaskManager maps CC event to TaskEvent]
709
+ B --> C{Task exists?}
710
+ C -- No --> D[TaskFactory creates Voice/WebRTC/Digital]
711
+ C -- Yes --> E[Use existing task]
712
+ D --> F[Task initializes state machine actor]
713
+ E --> F
714
+ F --> G[Task method called (hold/transfer/etc)]
715
+ G --> H[contact.ts or dialer.ts API call]
716
+ H --> I[State machine transition]
717
+ I --> J[Actions + guards update context]
718
+ J --> K[Emit task:* events]
719
+ K --> L[TaskUIControls updated for SDK UI]
720
+ ```
721
+
722
+ ## Sequence Diagram(s)
723
+ Sequence coverage:
724
+
725
+ | Operation group | Diagram | Failure / recovery coverage |
726
+ |---|---|---|
727
+ | Incoming task creation | Incoming task | Unsupported media or incomplete event context prevents publication of a partial task. |
728
+ | Voice hold/resume | Hold or resume | Invalid state rejects before transport; failure notification/HTTP error/timeout sends the matching failure event and throws. |
729
+ | Consult/transfer/conference | Consult and transfer | Backend failure notifications drive explicit failure actions and preserve stable call context. |
730
+ | Wrapup/end | Wrapup or end | Validation and backend failures throw; backend events retain distinct WRAPPING_UP/COMPLETED/TERMINATED outcomes. |
731
+ | WebRTC and digital accept | Channel-specific accept | WebRTC media/calling failure and Digital AQM failure follow different rejection paths. |
732
+
733
+ ### Incoming task
734
+
735
+ ```mermaid
736
+ sequenceDiagram
737
+ participant Backend
738
+ participant WS as Primary WebSocket
739
+ participant TM as TaskManager
740
+ participant TF as TaskFactory
741
+ participant Task
742
+ participant CC as ContactCenter
743
+ participant App as Application
744
+ Backend-->>WS: AgentContactReserved event
745
+ WS-->>TM: message event
746
+ TM->>TM: parse and prepare event context
747
+ alt task is new and media is supported
748
+ TM->>TF: createTask(data, dependencies)
749
+ TF-->>TM: Voice / WebRTC / Digital
750
+ TM->>Task: initialize and store
751
+ TM->>Task: send mapped TaskEvent
752
+ TM-->>CC: emit TASK_INCOMING
753
+ CC-->>App: task:incoming
754
+ opt a BROWSER telephony INCOMING_CALL arrives
755
+ TM->>TM: map call ID to current telephony task
756
+ TM->>Task: send TASK_INCOMING for call/task association
757
+ end
758
+ else unsupported media
759
+ TF-->>TM: throw Unknown media type
760
+ else invalid/unmapped event
761
+ TM->>TM: ignore without publishing a partial task
762
+ end
763
+ ```
764
+
765
+ ### Hold or resume
766
+
767
+ ```mermaid
768
+ sequenceDiagram
769
+ participant App
770
+ participant Voice
771
+ participant Actor as Task state-machine actor
772
+ participant Contact as routingContact
773
+ participant AQM as AqmReqs
774
+ participant WR as WebexRequest
775
+ participant WS as Primary WebSocket
776
+ App->>Voice: hold() or resume()
777
+ Voice->>Voice: derive media resource and validate CONNECTED/HELD state
778
+ alt invalid state
779
+ Voice-->>App: throw before transport
780
+ else valid state
781
+ Voice->>Actor: HOLD_INITIATED or UNHOLD_INITIATED
782
+ Voice->>Contact: hold/unHold({interactionId, data: {mediaResourceId}})
783
+ Contact->>AQM: generated request with success/failure binds
784
+ AQM->>WR: authenticated HTTP request
785
+ WR-->>AQM: acknowledgement only
786
+ alt matching success notification
787
+ WS-->>AQM: AGENT_CONTACT_HELD/UNHELD
788
+ AQM-->>Voice: TaskResponse
789
+ Voice->>Actor: HOLD_SUCCESS or UNHOLD_SUCCESS
790
+ Voice-->>App: resolve
791
+ else matching failure, HTTP rejection, or timeout
792
+ WS-->>AQM: failure notification or no completion
793
+ Voice->>Actor: HOLD_FAILED or UNHOLD_FAILED
794
+ Voice-->>App: throw detailed error
795
+ end
796
+ end
797
+ ```
798
+
799
+ ### Consult and transfer
800
+
801
+ ```mermaid
802
+ sequenceDiagram
803
+ participant App
804
+ participant Voice
805
+ participant Actor as Task state-machine actor
806
+ participant Contact as routingContact
807
+ participant WS as Primary WebSocket
808
+ App->>Voice: consult/transfer/conference operation
809
+ Voice->>Actor: initiating event
810
+ Voice->>Contact: correlated AQM operation
811
+ alt matching success notification
812
+ WS-->>Contact: consult/transfer/conference success
813
+ Contact-->>Voice: TaskResponse
814
+ Voice->>Actor: success event and updated call context
815
+ Voice-->>App: resolve
816
+ else failure/cancel/timeout
817
+ WS-->>Contact: failure or cancel notification
818
+ Voice->>Actor: matching failure/end event
819
+ Voice-->>App: throw while preserving stable main/consult context
820
+ end
821
+ ```
822
+
823
+ ### Wrapup or end
824
+
825
+ ```mermaid
826
+ sequenceDiagram
827
+ participant App
828
+ participant Task
829
+ participant Contact as routingContact
830
+ participant WS as Primary WebSocket
831
+ participant Actor as Task state-machine actor
832
+ App->>Task: wrapup(payload) or end()
833
+ alt invalid wrapup payload or missing task data
834
+ Task-->>App: throw validation error
835
+ else valid request
836
+ Task->>Contact: wrapup/end({interactionId, data})
837
+ alt matching success notification
838
+ WS-->>Contact: wrapup/end success
839
+ Contact-->>Task: TaskResponse
840
+ WS-->>Actor: backend event selects WRAPPING_UP/COMPLETED/TERMINATED
841
+ Task-->>App: resolve
842
+ else failure, HTTP rejection, or timeout
843
+ Contact-->>Task: structured rejection
844
+ Task-->>App: throw detailed error
845
+ end
846
+ end
847
+ ```
848
+
849
+ ### Channel-specific accept
850
+
851
+ ```mermaid
852
+ sequenceDiagram
853
+ participant App
854
+ participant TF as TaskFactory
855
+ participant WebRTC
856
+ participant Digital
857
+ participant Calling as WebCallingService
858
+ participant Contact as routingContact
859
+ TF-->>App: WebRTC or Digital task
860
+ alt WebRTC accept
861
+ App->>WebRTC: accept()
862
+ WebRTC->>WebRTC: getUserMedia(audio)
863
+ WebRTC->>Calling: answerCall(localAudioStream, interactionId)
864
+ alt media/calling succeeds
865
+ WebRTC-->>App: resolve
866
+ else media/calling failure
867
+ WebRTC-->>App: throw detailed error
868
+ end
869
+ else Digital accept
870
+ App->>Digital: accept()
871
+ Digital->>Contact: accept({interactionId})
872
+ alt AQM success notification
873
+ Contact-->>Digital: TaskResponse
874
+ Digital-->>App: resolve
875
+ else failure or timeout
876
+ Digital-->>App: throw detailed error
877
+ end
878
+ end
879
+ ```
880
+
881
+ ## Class / Component Relationships
882
+ - **Hierarchy**: `Task` (base) → `Voice` → `WebRTC`; `Digital` extends `Task`.
883
+
884
+ - **`Task` (base)**: Holds task data, emits SDK events, and provides default (unsupported) implementations for call control APIs.
885
+
886
+ - **`Voice`**: Adds hold/resume and consult-related capabilities for telephony tasks.
887
+
888
+ - **`WebRTC`**: Overrides `accept/decline` for WebRTC calls and hooks media events.
889
+
890
+ - **`Digital`**: Implements `accept` and refreshes digital task data/UI controls.
891
+
892
+ | Component | File | Responsibility |
893
+ |---|---|---|
894
+ | `TaskManager` | `task/TaskManager.ts` | Task lifecycle coordination |
895
+ | `Task` | `task/Task.ts` | Individual task operations |
896
+ | `contact` | `task/contact.ts` | AQM request definitions |
897
+ | `dialer` | `task/dialer.ts` | Outbound call initiation |
898
+ | `AutoWrapup` | `task/AutoWrapup.ts` | Auto wrapup timer |
899
+ | `taskDataNormalizer` | `task/taskDataNormalizer.ts` | Normalizes backend task payloads |
900
+ | `TaskUtils` | `task/TaskUtils.ts` | Utility functions |
901
+ | `state-machine` | `task/state-machine/*` | Task state transitions, guards, and UI control computation |
902
+
903
+ **File:** `Task.ts`
904
+
905
+ **Properties**
906
+
907
+ - `data: TaskData`
908
+
909
+ - `webCallMap: Record<TaskId, CallId>`
910
+
911
+ - `stateMachineService?: ActorRefFrom<TaskStateMachine>`
912
+
913
+ - `state?: SnapshotFrom<TaskStateMachine>`
914
+
915
+ - `autoWrapup?: AutoWrapup`
916
+
917
+ - `uiControls: TaskUIControls` (getter)
918
+
919
+ **Methods**
920
+
921
+ - `accept(): Promise<TaskResponse>` (abstract)
922
+
923
+ - `decline(): Promise<TaskResponse>` (default: unsupportedMethodError)
924
+
925
+ - `pauseRecording(): Promise<TaskResponse>` (default: unsupportedMethodError)
926
+
927
+ - `resumeRecording(resumeRecordingPayload: ResumeRecordingPayload): Promise<TaskResponse>` (default: unsupportedMethodError)
928
+
929
+ - `consult(consultPayload: ConsultPayload): Promise<TaskResponse>` (default: unsupportedMethodError)
930
+
931
+ - `endConsult(consultEndPayload?: ConsultEndPayload): Promise<TaskResponse>` (default: unsupportedMethodError)
932
+
933
+ - `consultTransfer(consultTransferPayload?: ConsultTransferPayLoad): Promise<TaskResponse>` (default: unsupportedMethodError)
934
+
935
+ - `consultConference(): Promise<TaskResponse>` (default: unsupportedMethodError)
936
+
937
+ - `exitConference(): Promise<TaskResponse>` (default: unsupportedMethodError)
938
+
939
+ - `transferConference(): Promise<TaskResponse>` (default: unsupportedMethodError)
940
+
941
+ - `switchCall(): Promise<TaskResponse>` (default: unsupportedMethodError)
942
+
943
+ - `toggleMute(): Promise<void>` (default: unsupportedMethodError)
944
+
945
+ - `unregisterWebCallListeners(): void` (default: no-op + log)
946
+
947
+ - `cancelAutoWrapupTimer(): void`
948
+
949
+ - Concrete `Task.hold()` / `Voice.hold()` are parameterless; the `ITask` compatibility interface permits `hold(mediaResourceId?: string)`.
950
+
951
+ - Concrete `Task.resume()` / `Voice.resume()` are parameterless; the `ITask` compatibility interface permits `resume(mediaResourceId?: string)`.
952
+
953
+ - `holdResume(): Promise<TaskResponse>` (default: unsupportedMethodError)
954
+
955
+ - `sendStateMachineEvent(event: TaskEventPayload): void`
956
+
957
+ - `updateTaskData(updatedData: TaskData, shouldOverwrite = false): ITask`
958
+
959
+ - `transfer(transferPayload: TransferPayLoad): Promise<TaskResponse>`
960
+
961
+ - `end(): Promise<TaskResponse>`
962
+
963
+ - `wrapup(wrapupPayload: WrapupPayLoad): Promise<TaskResponse>`
964
+
965
+ **File:** `voice/Voice.ts`
966
+
967
+ **Notes**
968
+
969
+ - Extends `Task`.
970
+
971
+ - Provides `hold()` and `resume()` that delegate to `holdResume()`.
972
+
973
+ - Explicitly overrides `accept()` and `decline()` to throw `unsupportedMethodError`.
974
+
975
+ - `WebRTC` then overrides these methods with concrete implementations.
976
+
977
+ **File:** `voice/WebRTC.ts`
978
+
979
+ **Notes**
980
+
981
+ - Extends `Voice`.
982
+
983
+ - Overrides `accept()` and `decline()` for WebRTC calls.
984
+
985
+ - Emits `TASK_EVENTS.TASK_MEDIA` on remote media (`CALL_EVENT_KEYS.REMOTE_MEDIA`).
986
+
987
+ - Overrides `unregisterWebCallListeners()`.
988
+
989
+ **File:** `digital/Digital.ts`
990
+
991
+ **Notes**
992
+
993
+ - Extends `Task`.
994
+
995
+ - Implements `accept()`.
996
+
997
+ - Overrides `updateTaskData()` to refresh digital task data and UI controls.
998
+
999
+ **File:** `TaskFactory.ts`
1000
+
1001
+ **API**
1002
+
1003
+ - `createTask(contact, webCallingService, data, configFlags, wrapupData?, agentId?): Task`
1004
+
1005
+ **Behavior**
1006
+
1007
+ - Chooses `WebRTC` vs `Voice` for `MEDIA_CHANNEL.TELEPHONY` based on `webCallingService.loginOption`.
1008
+
1009
+ - Chooses `Digital` for `MEDIA_CHANNEL.CHAT`, `MEDIA_CHANNEL.EMAIL`, `MEDIA_CHANNEL.SOCIAL`.
1010
+
1011
+ - Throws `Error` for unknown media types.
1012
+
1013
+ ```mermaid
1014
+ classDiagram
1015
+ class Task {
1016
+ <<abstract>>
1017
+ # contact
1018
+ # metricsManager
1019
+ + data: TaskData
1020
+ + webCallMap: Record~TaskId, CallId~
1021
+ + stateMachineService
1022
+ + state
1023
+ # currentUiControls: TaskUIControls
1024
+ # uiControlConfig: UIControlConfig
1025
+ + autoWrapup: AutoWrapup
1026
+ + accept() TaskResponse
1027
+ + transfer(payload) TaskResponse
1028
+ + end() TaskResponse
1029
+ + wrapup(payload) TaskResponse
1030
+ + updateTaskData(updatedData, shouldOverwrite) ITask
1031
+ + sendStateMachineEvent(event) void
1032
+ + hold() TaskResponse
1033
+ + resume() TaskResponse
1034
+ + holdResume() TaskResponse
1035
+ + consult(payload) TaskResponse
1036
+ + endConsult(payload) TaskResponse
1037
+ + consultTransfer(payload) TaskResponse
1038
+ + consultConference() TaskResponse
1039
+ + exitConference() TaskResponse
1040
+ + transferConference() TaskResponse
1041
+ + pauseRecording() TaskResponse
1042
+ + resumeRecording(payload) TaskResponse
1043
+ + toggleMute() void
1044
+ + unregisterWebCallListeners() void
1045
+ + cancelAutoWrapupTimer() void
1046
+ }
1047
+
1048
+ class Voice {
1049
+ + accept() TaskResponse
1050
+ + decline() TaskResponse
1051
+ + hold() TaskResponse
1052
+ + resume() TaskResponse
1053
+ + holdResume() TaskResponse
1054
+ + pauseRecording() TaskResponse
1055
+ + resumeRecording(payload) TaskResponse
1056
+ + consult(payload) TaskResponse
1057
+ + endConsult(payload) TaskResponse
1058
+ + transfer(payload) TaskResponse
1059
+ + consultConference() TaskResponse
1060
+ + exitConference() TaskResponse
1061
+ + transferConference() TaskResponse
1062
+ }
1063
+
1064
+ class WebRTC {
1065
+ - localAudioStream: LocalMicrophoneStream
1066
+ - webCallingService: WebCallingService
1067
+ + accept() TaskResponse
1068
+ + decline() TaskResponse
1069
+ + toggleMute() void
1070
+ + unregisterWebCallListeners() void
1071
+ }
1072
+
1073
+ class Digital {
1074
+ + accept() TaskResponse
1075
+ + updateTaskData(newData, shouldOverwrite) IDigital
1076
+ }
1077
+
1078
+ class TaskFactory {
1079
+ + createTask(contact, webCallingService, data, configFlags, wrapupData, agentId) Task
1080
+ }
1081
+
1082
+ Task <|-- Voice
1083
+ Voice <|-- WebRTC
1084
+ Task <|-- Digital
1085
+
1086
+ TaskFactory ..> Task : creates
1087
+ TaskFactory ..> Voice : creates
1088
+ TaskFactory ..> WebRTC : creates
1089
+ TaskFactory ..> Digital : creates
1090
+ ```
1091
+
1092
+ ## Use Cases
1093
+ - **UC-1 Incoming task creation:** TaskManager maps a backend offer/reservation, TaskFactory creates a supported Voice/WebRTC/Digital task, and ContactCenter emits the typed incoming event. Evidence: `src/services/task/TaskManager.ts`, `src/services/task/TaskFactory.ts`, `test/unit/spec/services/task`.
1094
+ - **UC-2 Accept/hold/resume:** the Task or Voice method delegates the remote operation and sends the matching typed event to its actor; concrete hold/resume implementations are parameterless. Evidence: `src/services/task/Task.ts`, `src/services/task/voice/Voice.ts`, `test/unit/spec/services/task`.
1095
+ - **UC-3 Consult/transfer/conference:** Voice coordinates contact routing with initiating/stable actor states and emits the corresponding complete `TASK_EVENTS` contract. Evidence: `src/services/task/voice/Voice.ts`, `src/services/task/types.ts`, `test/unit/spec/services/task`.
1096
+ - **UC-4 Wrapup/end:** backend end/wrapup notifications drive WRAPPING_UP and final COMPLETED/TERMINATED outcomes without collapsing them into one result. Evidence: `src/services/task/Task.ts`, `src/services/task/state-machine/TaskStateMachine.ts`, `test/unit/spec/services/task`.
1097
+ - **UC-5 WebRTC and digital behavior:** TaskFactory selects channel-specific subclasses; unsupported SMS/Facebook/WhatsApp values throw `Unknown media type`. Evidence: `src/services/task/TaskFactory.ts`, `test/unit/spec/services/task/TaskFactory.ts`.
1098
+
1099
+ > **Host-application example:** This consumer callback performs task operations without logging raw interaction data.
1100
+
1101
+ ```typescript
1102
+ // Listen for incoming tasks
1103
+ cc.on('task:incoming', async (task) => {
1104
+ // Accept the task
1105
+ await task.accept();
1106
+
1107
+ // Task operations
1108
+ await task.hold();
1109
+ await task.resume();
1110
+ await task.end();
1111
+ await task.wrapup({
1112
+ wrapUpReason: 'Resolved',
1113
+ auxCodeId: 'wrapup-code',
1114
+ });
1115
+ });
1116
+ ```
1117
+
1118
+ ## State Model
1119
+ Each Task owns an XState actor and current task data. TaskManager maps backend notifications into actor events; backend task data remains authoritative for hydration. Stable interaction states and initiating/terminal states are defined by the nested task-state-machine module.
1120
+
1121
+ ## Business Rules & Invariants
1122
+ - TaskFactory creates only implemented media subclasses and throws for unsupported media values.
1123
+ - Concrete Task/Voice `hold()` and `resume()` methods are parameterless even though the broader `ITask` declaration retains an optional media-resource parameter.
1124
+ - Task event names come from `TASK_EVENTS`; actor transition names come from `TaskEvent`, and callers must not substitute raw strings.
1125
+ - Task owns no credentials or authentication policy; contact/dialer factories delegate authenticated requests through AqmReqs and Core/WebexRequest.
1126
+ - Preview skip/remove delegation is conditional: `campaignPreviewSkipDisabled === 'true'` or `campaignPreviewRemoveDisabled === 'true'` causes ContactCenter to throw before the dialer starts an HTTP or WebSocket-correlated AQM operation. Accept has no equivalent pre-guard.
1127
+
1128
+ ## Concurrency & Reactive Flow
1129
+ - Remote contact/dialer operations complete asynchronously through AQM correlation. Backend WebSocket notifications are separately mapped by TaskManager and delivered to the owning actor in arrival order.
1130
+
1131
+ ## State Machine
1132
+ ```mermaid
1133
+ stateDiagram-v2
1134
+ [*] --> IDLE
1135
+ IDLE --> OFFERED: task offer
1136
+ OFFERED --> CONNECTED: assignment / accept
1137
+ CONNECTED --> HELD: hold succeeds
1138
+ HELD --> CONNECTED: resume succeeds
1139
+ CONNECTED --> CONSULTING: consult succeeds
1140
+ HELD --> CONSULTING: consult succeeds
1141
+ CONSULTING --> CONFERENCING: conference succeeds
1142
+ CONNECTED --> WRAPPING_UP: wrapup required after end
1143
+ HELD --> WRAPPING_UP: wrapup required after end
1144
+ WRAPPING_UP --> COMPLETED: wrapup complete
1145
+ CONNECTED --> TERMINATED: end without wrapup
1146
+ COMPLETED --> [*]
1147
+ TERMINATED --> [*]
1148
+ ```
1149
+
1150
+ - **Factory**: `TaskFactory.ts` selects `WebRTC`, `Voice`, or `Digital` based on `MEDIA_CHANNEL` and `webCallingService.loginOption`.
1151
+
1152
+ - **Initialization**: `Task.ts` creates a state machine actor using `createTaskStateMachine(...)`, wires action overrides (emitters), and starts the actor.
1153
+
1154
+ - **Task State**: The task holds `stateMachineService` and uses it to send `TaskEvent` payloads.
1155
+
1156
+ Example (state machine init inside a task object):
1157
+
1158
+ ```typescript
1159
+ const machine = createTaskStateMachine(uiControlConfig, {
1160
+ actions: {
1161
+ emitTaskIncoming: ({event}) => task.emit('task:incoming', task),
1162
+ },
1163
+ });
1164
+ const actor = createActor(machine);
1165
+ actor.start();
1166
+ ```
1167
+
1168
+ `Task` delegates lifecycle transitions and control-state derivation to the state machine:
1169
+
1170
+ - Transition graph: `state-machine/TaskStateMachine.ts`
1171
+
1172
+ - Transition conditions: `state-machine/guards.ts`
1173
+
1174
+ - Context mutation and integration hooks: `state-machine/actions.ts`
1175
+
1176
+ - UI control derivation: `state-machine/uiControlsComputer.ts`
1177
+
1178
+ For state-machine-specific implementation guidance, use:
1179
+
1180
+ - `../state-machine/ai-docs/task-state-machine-spec.md`
1181
+
1182
+ - **Active lifecycle + intermediate states**:
1183
+
1184
+ - `IDLE`, `OFFERED`, `CONNECTED`
1185
+
1186
+ - `HOLD_INITIATING`, `HELD`, `RESUME_INITIATING`
1187
+
1188
+ - `CONSULT_INITIATING`, `CONSULTING`, `CONF_INITIATING`
1189
+
1190
+ - `CONFERENCING`, `WRAPPING_UP`, `COMPLETED`, `TERMINATED`
1191
+
1192
+ - **Future placeholders (defined, not currently implemented in transitions)**:
1193
+
1194
+ - `CONSULT_INITIATED`, `CONSULT_COMPLETED`, `POST_CALL`, `PARKED`, `MONITORING`
1195
+
1196
+ ### Signature and ownership clarifications
1197
+
1198
+ - `Task.endConsult(consultEndPayload)` requires a payload at the base class; `Voice.endConsult(consultEndPayload?)` accepts it optionally.
1199
+ - `ITask.hold(mediaResourceId?)` and `ITask.resume(mediaResourceId?)` are compatibility-interface shapes; current concrete Task/Voice methods accept no argument.
1200
+ - TaskFactory supports the media implementations present in its switch and throws for unsupported media values rather than silently constructing a generic task.
1201
+
1202
+ ## Protocol / Wire Format
1203
+ - Request, response, and event payload ownership is anchored in `src/services/task/Task.ts`. HTTP initiates backend work where applicable; WebSocket messages provide realtime events and, for AQM flows, correlated completion.
1204
+
1205
+ ## Error Handling & Failure Modes
1206
+ | Condition | Signal (error/code/result) | Caller recovery |
1207
+ |---|---|---|
1208
+ | Dependency rejection | Typed/rethrown error or failure event | Inspect structured details, preserve tracking id, and retry only when the operation is safe. |
1209
+ | Timeout or missing async completion | Timeout/recovery state | Follow the module-specific recovery path; never synthesize success. |
1210
+
1211
+ > **Host-application example:** `showTransferFailure` represents consumer-owned error presentation. SDK package implementation must use `LoggerProxy` and avoid logging raw runtime values.
1212
+
1213
+ ```typescript
1214
+ try {
1215
+ await task.transfer({
1216
+ to: 'queue-123',
1217
+ destinationType: 'queue',
1218
+ });
1219
+ } catch (error) {
1220
+ showTransferFailure(error);
1221
+ }
1222
+ ```
1223
+
1224
+ **Cause**: Agent not available or TaskManager not initialized
1225
+
1226
+ **Solution**:
1227
+
1228
+ 1. Ensure `cc.register()` completed
1229
+
1230
+ 2. Ensure `cc.stationLogin()` completed
1231
+
1232
+ 3. Ensure agent state is Available
1233
+
1234
+ **Cause**: Task state doesn't allow operation
1235
+
1236
+ **Solution**: Check task state before operation:
1237
+
1238
+ ```typescript
1239
+ if (task.uiControls.main.hold.isEnabled) {
1240
+ await task.hold();
1241
+ }
1242
+
1243
+ // During a consult, use the consult-leg controls instead.
1244
+ if (task.uiControls.consult.hold.isEnabled) {
1245
+ // Render or enable the consult-leg hold action.
1246
+ }
1247
+ ```
1248
+
1249
+ **Cause**: Call not mapped to task
1250
+
1251
+ **Solution**: Ensure BROWSER login and mercury connected:
1252
+
1253
+ ```typescript
1254
+ await webex.internal.mercury.connect();
1255
+ await cc.stationLogin({ loginOption: 'BROWSER', ... });
1256
+ ```
1257
+
1258
+ ## Pitfalls
1259
+ - Concrete `Task`/`Voice` hold and resume methods are parameterless even though the broader `ITask` declaration retains an optional compatibility argument.
1260
+ - AQM HTTP acknowledgement never completes a task operation; success/failure binds or timeout settle the promise and must stay aligned with actor events.
1261
+ - Primary and RTD WebSockets have different ownership: TaskManager uses the RTD stream for transcript/suggestion events and must not emit acknowledgement payloads as public suggestions.
1262
+
1263
+ ## Module Do's / Don'ts
1264
+ - DO send initiating and success/failure events to the task actor around remote Voice operations.
1265
+ - DO let TaskFactory select Voice/WebRTC/Digital from media type and login option.
1266
+ - DON'T create a generic task for unsupported media or treat `startOutdial()` as returning an ITask.
1267
+ - DON'T derive hold/resume completion from the HTTP response.
1268
+
1269
+ ## Key Design Trade-off
1270
+ - A shared Task base preserves a stable API while media-specific subclasses and a separate state engine enforce capability differences.
1271
+
1272
+ ## Test-Case Strategy (module)
1273
+ Use `test/unit/spec/services/task/Task.ts`, `TaskFactory.ts`, `TaskManager.ts`, media-specific suites, contact/dialer suites, and state-machine suites. Cover concrete-versus-interface method signatures, every TASK_EVENTS group, unsupported media rejection, primary/RTD event ownership, injected state actions, preview-campaign accept/skip/remove payloads and failure paths, the disabled skip/remove pre-guards, and success/failure/timeout paths.
1274
+
1275
+ | Behavior / Requirement | Existing test evidence | Gap |
1276
+ |---|---|---|
1277
+ | `TASK-R-001` | `test/unit/spec/services/task/TaskFactory.ts` | None. |
1278
+ | `TASK-R-002` | `test/unit/spec/services/task/Task.ts`, `test/unit/spec/services/task/voice/Voice.ts` | None. |
1279
+ | `TASK-R-003` | `test/unit/spec/services/task/Task.ts`, `test/unit/spec/services/task/state-machine/TaskStateMachine.ts` | Keep event-catalog parity checks synchronized with `TASK_EVENTS`. |
1280
+ | `TASK-R-004` | `test/unit/spec/services/task/TaskManager.ts` | None. |
1281
+ | `TASK-R-005` | `test/unit/spec/services/task/TaskFactory.ts`, `test/unit/spec/services/task/dialer.ts` | None. |
1282
+ | `TASK-R-006` | `test/unit/spec/services/task/contact.ts`, `test/unit/spec/services/core/WebexRequest.ts` | Authentication ownership is verified across routing/Core boundaries. |
1283
+ | `TASK-R-007` | `test/unit/spec/cc.ts`, `test/unit/spec/services/task/dialer.ts` | Add direct tests proving disabled skip/remove flags throw before dialer invocation; keep public signatures, metrics/error handling, and AQM request contracts synchronized. |
1284
+
1285
+ ## Traceability
1286
+ - Repo architecture: `../../../../ai-docs/ARCHITECTURE.md` · Registry: `../../../../ai-docs/SPEC_INDEX.md`
1287
+ - Coverage state and contracts baseline: `../../../../.sdd/manifest.json`
1288
+
1289
+ - Task creation: `TaskFactory.ts`
1290
+
1291
+ - Task APIs and behavior: `Task.ts`, `voice/Voice.ts`, `voice/WebRTC.ts`, `digital/Digital.ts`
1292
+
1293
+ - Task management: `TaskManager.ts`
1294
+
1295
+ - Shared task types: `types.ts`, `constants.ts`
1296
+
1297
+ - Task lifecycle state machine: `state-machine/TaskStateMachine.ts`
1298
+
1299
+ - State machine types/events: `state-machine/constants.ts`, `state-machine/types.ts`
1300
+
1301
+ - [TaskManager.ts](../TaskManager.ts) - Manager implementation
1302
+
1303
+ - [types.ts](../types.ts) - Type definitions
1304
+
1305
+ - [../state-machine/ai-docs/task-state-machine-spec.md](../state-machine/ai-docs/task-state-machine-spec.md) - State machine implementation guide
1306
+
1307
+ - [../state-machine/ai-docs/task-state-machine-spec.md](../state-machine/ai-docs/task-state-machine-spec.md) - State machine internals
1308
+
1309
+ - [cc.ts](../../../cc.ts) - Main plugin
1310
+
1311
+ - [TaskManager.ts](../TaskManager.ts) - Manager
1312
+
1313
+ - [contact.ts](../contact.ts) - Contact operations
1314
+
1315
+ - [types.ts](../types.ts) - Type definitions
1316
+
1317
+ - [../state-machine/ai-docs/task-state-machine-spec.md](../state-machine/ai-docs/task-state-machine-spec.md) - State machine guide
1318
+
1319
+ - [../state-machine/ai-docs/task-state-machine-spec.md](../state-machine/ai-docs/task-state-machine-spec.md) - State machine architecture