@webex/contact-center 3.12.0-llmrefactor.2 → 3.12.0-llmrefactor.3

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 (187) hide show
  1. package/.sdd/manifest.json +5 -4
  2. package/ai-docs/ARCHITECTURE.md +1 -1
  3. package/ai-docs/CONTRACTS.md +5 -1
  4. package/ai-docs/SECURITY.md +1 -1
  5. package/ai-docs/contact-center-spec.md +22 -4
  6. package/ai-docs/features/consult-transfer-list-policy/spec/feature-spec.md +362 -0
  7. package/dist/cc.js +440 -10
  8. package/dist/cc.js.map +1 -1
  9. package/dist/config.js +7 -0
  10. package/dist/config.js.map +1 -1
  11. package/dist/constants.js +10 -2
  12. package/dist/constants.js.map +1 -1
  13. package/dist/index.js.map +1 -1
  14. package/dist/metrics/behavioral-events.js +13 -0
  15. package/dist/metrics/behavioral-events.js.map +1 -1
  16. package/dist/metrics/constants.js +2 -0
  17. package/dist/metrics/constants.js.map +1 -1
  18. package/dist/services/AddressBook.js +18 -15
  19. package/dist/services/AddressBook.js.map +1 -1
  20. package/dist/services/AnswerCallOnWebexService.js +174 -0
  21. package/dist/services/AnswerCallOnWebexService.js.map +1 -0
  22. package/dist/services/EntryPoint.js +46 -68
  23. package/dist/services/EntryPoint.js.map +1 -1
  24. package/dist/services/Queue.js +27 -22
  25. package/dist/services/Queue.js.map +1 -1
  26. package/dist/services/WebexCrossClientService.js +171 -0
  27. package/dist/services/WebexCrossClientService.js.map +1 -0
  28. package/dist/services/WxAppTelephonyMercurySync.js +93 -0
  29. package/dist/services/WxAppTelephonyMercurySync.js.map +1 -0
  30. package/dist/services/config/Util.js +3 -0
  31. package/dist/services/config/Util.js.map +1 -1
  32. package/dist/services/config/constants.js +10 -6
  33. package/dist/services/config/constants.js.map +1 -1
  34. package/dist/services/config/types.js +4 -0
  35. package/dist/services/config/types.js.map +1 -1
  36. package/dist/services/core/Err.js.map +1 -1
  37. package/dist/services/core/WebexRequest.js +6 -2
  38. package/dist/services/core/WebexRequest.js.map +1 -1
  39. package/dist/services/core/aqm-reqs.js +28 -14
  40. package/dist/services/core/aqm-reqs.js.map +1 -1
  41. package/dist/services/core/types.js.map +1 -1
  42. package/dist/services/task/Task.js +73 -7
  43. package/dist/services/task/Task.js.map +1 -1
  44. package/dist/services/task/TaskFactory.js +8 -4
  45. package/dist/services/task/TaskFactory.js.map +1 -1
  46. package/dist/services/task/TaskManager.js +176 -20
  47. package/dist/services/task/TaskManager.js.map +1 -1
  48. package/dist/services/task/WebexCallingUtils.js +70 -0
  49. package/dist/services/task/WebexCallingUtils.js.map +1 -0
  50. package/dist/services/task/constants.js +4 -1
  51. package/dist/services/task/constants.js.map +1 -1
  52. package/dist/services/task/contact.js +29 -0
  53. package/dist/services/task/contact.js.map +1 -1
  54. package/dist/services/task/digital/Digital.js +3 -2
  55. package/dist/services/task/digital/Digital.js.map +1 -1
  56. package/dist/services/task/state-machine/TaskStateMachine.js +135 -37
  57. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -1
  58. package/dist/services/task/state-machine/actions.js +6 -1
  59. package/dist/services/task/state-machine/actions.js.map +1 -1
  60. package/dist/services/task/state-machine/guards.js +38 -11
  61. package/dist/services/task/state-machine/guards.js.map +1 -1
  62. package/dist/services/task/state-machine/types.js.map +1 -1
  63. package/dist/services/task/state-machine/uiControlsComputer.js +118 -18
  64. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -1
  65. package/dist/services/task/types.js +16 -1
  66. package/dist/services/task/types.js.map +1 -1
  67. package/dist/services/task/voice/Voice.js +350 -14
  68. package/dist/services/task/voice/Voice.js.map +1 -1
  69. package/dist/services/task/voice/WebRTC.js +4 -1
  70. package/dist/services/task/voice/WebRTC.js.map +1 -1
  71. package/dist/services/task/voice/wxAppVoiceMethods.js +201 -0
  72. package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -0
  73. package/dist/services/wxAppTelephonyUtils.js +19 -0
  74. package/dist/services/wxAppTelephonyUtils.js.map +1 -0
  75. package/dist/types/cc.d.ts +77 -4
  76. package/dist/types/config.d.ts +7 -0
  77. package/dist/types/constants.d.ts +8 -0
  78. package/dist/types/index.d.ts +3 -1
  79. package/dist/types/metrics/constants.d.ts +2 -0
  80. package/dist/types/services/AddressBook.d.ts +2 -1
  81. package/dist/types/services/AnswerCallOnWebexService.d.ts +37 -0
  82. package/dist/types/services/EntryPoint.d.ts +7 -5
  83. package/dist/types/services/Queue.d.ts +5 -3
  84. package/dist/types/services/WebexCrossClientService.d.ts +28 -0
  85. package/dist/types/services/WxAppTelephonyMercurySync.d.ts +28 -0
  86. package/dist/types/services/config/constants.d.ts +8 -5
  87. package/dist/types/services/config/types.d.ts +17 -4
  88. package/dist/types/services/core/Err.d.ts +2 -0
  89. package/dist/types/services/core/WebexRequest.d.ts +1 -0
  90. package/dist/types/services/core/types.d.ts +2 -0
  91. package/dist/types/services/task/Task.d.ts +20 -2
  92. package/dist/types/services/task/TaskFactory.d.ts +2 -1
  93. package/dist/types/services/task/WebexCallingUtils.d.ts +11 -0
  94. package/dist/types/services/task/constants.d.ts +3 -0
  95. package/dist/types/services/task/contact.d.ts +4 -0
  96. package/dist/types/services/task/digital/Digital.d.ts +2 -2
  97. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +260 -56
  98. package/dist/types/services/task/state-machine/guards.d.ts +6 -5
  99. package/dist/types/services/task/state-machine/types.d.ts +9 -1
  100. package/dist/types/services/task/types.d.ts +81 -4
  101. package/dist/types/services/task/voice/Voice.d.ts +48 -9
  102. package/dist/types/services/task/voice/WebRTC.d.ts +2 -2
  103. package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +52 -0
  104. package/dist/types/services/wxAppTelephonyUtils.d.ts +5 -0
  105. package/dist/types/types.d.ts +14 -8
  106. package/dist/types/utils/PageCache.d.ts +20 -3
  107. package/dist/types.js +7 -2
  108. package/dist/types.js.map +1 -1
  109. package/dist/utils/PageCache.js +19 -5
  110. package/dist/utils/PageCache.js.map +1 -1
  111. package/dist/webex.js +1 -1
  112. package/package.json +1 -1
  113. package/src/cc.ts +506 -9
  114. package/src/config.ts +7 -0
  115. package/src/constants.ts +8 -0
  116. package/src/index.ts +2 -0
  117. package/src/metrics/ai-docs/metrics-spec.md +9 -3
  118. package/src/metrics/behavioral-events.ts +14 -0
  119. package/src/metrics/constants.ts +2 -0
  120. package/src/services/AddressBook.ts +17 -6
  121. package/src/services/AnswerCallOnWebexService.ts +206 -0
  122. package/src/services/EntryPoint.ts +59 -60
  123. package/src/services/Queue.ts +29 -12
  124. package/src/services/WebexCrossClientService.ts +212 -0
  125. package/src/services/WxAppTelephonyMercurySync.ts +115 -0
  126. package/src/services/ai-docs/AGENTS.md +10 -10
  127. package/src/services/ai-docs/services-spec.md +6 -1
  128. package/src/services/config/Util.ts +3 -0
  129. package/src/services/config/ai-docs/AGENTS.md +1 -1
  130. package/src/services/config/ai-docs/ARCHITECTURE.md +2 -2
  131. package/src/services/config/ai-docs/config-spec.md +6 -0
  132. package/src/services/config/constants.ts +10 -6
  133. package/src/services/config/types.ts +16 -4
  134. package/src/services/core/Err.ts +1 -0
  135. package/src/services/core/WebexRequest.ts +3 -1
  136. package/src/services/core/ai-docs/core-spec.md +5 -1
  137. package/src/services/core/aqm-reqs.ts +30 -15
  138. package/src/services/core/types.ts +2 -0
  139. package/src/services/task/Task.ts +79 -8
  140. package/src/services/task/TaskFactory.ts +8 -3
  141. package/src/services/task/TaskManager.ts +254 -15
  142. package/src/services/task/WebexCallingUtils.ts +136 -0
  143. package/src/services/task/ai-docs/ARCHITECTURE.md +1 -0
  144. package/src/services/task/ai-docs/task-spec.md +117 -2
  145. package/src/services/task/constants.ts +3 -0
  146. package/src/services/task/contact.ts +30 -0
  147. package/src/services/task/digital/Digital.ts +4 -2
  148. package/src/services/task/state-machine/TaskStateMachine.ts +210 -63
  149. package/src/services/task/state-machine/actions.ts +5 -2
  150. package/src/services/task/state-machine/ai-docs/AGENTS.md +8 -4
  151. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +17 -8
  152. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +46 -22
  153. package/src/services/task/state-machine/guards.ts +64 -11
  154. package/src/services/task/state-machine/types.ts +16 -1
  155. package/src/services/task/state-machine/uiControlsComputer.ts +206 -32
  156. package/src/services/task/types.ts +114 -4
  157. package/src/services/task/voice/Voice.ts +462 -12
  158. package/src/services/task/voice/WebRTC.ts +5 -1
  159. package/src/services/task/voice/wxAppVoiceMethods.ts +307 -0
  160. package/src/services/wxAppTelephonyUtils.ts +14 -0
  161. package/src/types.ts +33 -9
  162. package/src/utils/AGENTS.md +21 -10
  163. package/src/utils/PageCache.ts +38 -5
  164. package/src/utils/ai-docs/utils-spec.md +21 -11
  165. package/test/unit/spec/cc.ts +1274 -0
  166. package/test/unit/spec/metrics/behavioral-events.ts +18 -0
  167. package/test/unit/spec/services/AddressBook.ts +37 -6
  168. package/test/unit/spec/services/AnswerCallOnWebexService.ts +223 -0
  169. package/test/unit/spec/services/EntryPoint.ts +87 -40
  170. package/test/unit/spec/services/Queue.ts +123 -12
  171. package/test/unit/spec/services/WebexCrossClientService.ts +261 -0
  172. package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +113 -0
  173. package/test/unit/spec/services/config/Util.ts +85 -0
  174. package/test/unit/spec/services/core/WebexRequest.ts +3 -1
  175. package/test/unit/spec/services/core/aqm-reqs.ts +113 -1
  176. package/test/unit/spec/services/task/Task.ts +200 -0
  177. package/test/unit/spec/services/task/TaskFactory.ts +39 -2
  178. package/test/unit/spec/services/task/TaskManager.ts +653 -1
  179. package/test/unit/spec/services/task/WebexCallingUtils.ts +153 -0
  180. package/test/unit/spec/services/task/contact.ts +33 -0
  181. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +854 -72
  182. package/test/unit/spec/services/task/state-machine/guards.ts +210 -8
  183. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +445 -7
  184. package/test/unit/spec/services/task/voice/Voice.ts +874 -0
  185. package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +459 -0
  186. package/umd/contact-center.min.js +2 -2
  187. package/umd/contact-center.min.js.map +1 -1
@@ -33,7 +33,7 @@ Manage task lifecycle including inbound/outbound calls, hold/resume, consult, tr
33
33
 
34
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
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.
36
+ - **AQM Contact Operations**: `contact.ts` builds the AQM request surface for call control (accept, hold, consult, transfer, participant Drop, wrapup, end) and is the primary bridge from `Task`/`Voice`/`WebRTC`/`Digital` methods to WCC task APIs.
37
37
 
38
38
  - **Outbound and Preview-Campaign Dialing**: `dialer.ts` exposes `startOutdial` plus `acceptPreviewContact`, `skipPreviewContact`, and `removePreviewContact`; ContactCenter publishes these operations through typed `cc` methods.
39
39
 
@@ -141,6 +141,8 @@ services/task/
141
141
  |---|---|---|---|---|---|---|
142
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
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
+ | `task.consult-transfer-controls` | SDK task controls | `TaskUIControls.consultTransferDestinations` with ordered `consult` and `transfer` arrays. | Surface default destination availability on every Task without an extra policy method. | Additive semver-public field/types; array order is meaningful and the first item is the default. | `src/services/task/types.ts`, `src/services/task/state-machine/uiControlsComputer.ts` | `../../../../ai-docs/CONTRACTS.md` |
145
+ | `task.conference-participant-drop` | SDK/AQM API | `task.dropConferenceParticipant(payload: DropConferenceParticipantPayload): Promise<TaskResponse>`. | Remove a supported target from a voice conference after correlated routing completion. | Additive semver-public method and payload; removals or signature changes are breaking. | `src/services/task/voice/Voice.ts`, `src/services/task/contact.ts`, `src/services/task/types.ts` | `../../../../ai-docs/CONTRACTS.md` |
144
146
 
145
147
  Compatibility notes:
146
148
  - Do not remove or reinterpret exported symbols/events without a documented consumer migration.
@@ -151,6 +153,8 @@ Compatibility notes:
151
153
 
152
154
  - `PreviewContactPayload` (`types.ts`) with `interactionId` and campaign-name `campaignId`
153
155
 
156
+ - `DropConferenceParticipantPayload` (`types.ts`) with a runtime-validated non-empty `participantId`
157
+
154
158
  - `ITask`, `IVoice`, `IWebRTC`, `IDigital` (`types.ts`)
155
159
 
156
160
  - `MEDIA_CHANNEL`, `TASK_CHANNEL_TYPE`, `VOICE_VARIANT` (`types.ts`)
@@ -346,6 +350,20 @@ End consultation without transfer.
346
350
 
347
351
  **Returns**: `Promise<TaskResponse>`
348
352
 
353
+ Remove another participant from a voice conference.
354
+
355
+ **Parameters**:
356
+
357
+ - `participantId` (string, required): backend participant target identifier; blank and non-string values reject before AQM registration or telemetry.
358
+
359
+ **Returns**: `Promise<TaskResponse>` after `ParticipantLeftConference` is received for the latest `interaction.mainInteractionId || task.data.interactionId`.
360
+
361
+ **Failure**: `ParticipantDropConferenceFailed`, HTTP rejection, or the default 20-second AQM timeout rejects the promise. The operation does not add a task state-machine event or optimistically change the roster.
362
+
363
+ ```typescript
364
+ await task.dropConferenceParticipant({participantId});
365
+ ```
366
+
349
367
  ### Complete TASK_EVENTS inventory
350
368
 
351
369
  The public `TASK_EVENTS` enum contains 49 members; every member is listed below from `src/services/task/types.ts`.
@@ -417,6 +435,8 @@ The public `TASK_EVENTS` enum contains 49 members; every member is listed below
417
435
  | 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
436
  | 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
437
  | 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 |
438
+ | TASK-R-008 | Voice `dropConferenceParticipant` must validate the public target, resolve the latest main interaction, POST an empty body to the encoded participant-drop route, and settle only from `ParticipantLeftConference`, `ParticipantDropConferenceFailed`, HTTP failure, or the existing AQM timeout. | Participant removal is backend-authoritative and must preserve event-driven roster/state synchronization without exposing participant identifiers through telemetry or logs. | `src/services/task/voice/Voice.ts`, `src/services/task/contact.ts`, `src/services/task/types.ts` | `test/unit/spec/services/task/voice/Voice.ts`, `test/unit/spec/services/task/contact.ts`, `test/unit/spec/services/core/aqm-reqs.ts` | Backend authorization and media removal are remote-service responsibilities. | PRESENT |
439
+ | TASK-R-009 | ContactCenter must inject Desktop Profile collaboration policy into TaskFactory, and every created voice/digital Task must expose ordered Consult and Transfer destination categories through `TaskUIControls.consultTransferDestinations`. | Task consumers need one already-computed policy surface and must not fetch profile flags or duplicate destination decisions. | `src/cc.ts`, `src/services/task/TaskFactory.ts`, `src/services/task/types.ts` | `test/unit/spec/cc.ts`, `test/unit/spec/services/task/TaskFactory.ts` | Host-specific UI options may only further hide an SDK-allowed category. | PRESENT |
420
440
 
421
441
  ## Design Overview
422
442
  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.
@@ -494,6 +514,8 @@ Returns an object of AQM request methods wired to `TASK_API` and `TASK_MESSAGE_T
494
514
 
495
515
  - `consultConference`
496
516
 
517
+ - `dropConferenceParticipant`
518
+
497
519
  - `exitConference`
498
520
 
499
521
  - `conferenceTransfer`
@@ -532,6 +554,8 @@ Uses `contact` for:
532
554
 
533
555
  - `consultConference`, `exitConference`, `conferenceTransfer`
534
556
 
557
+ - `dropConferenceParticipant` (Voice only; base Task rejects as unsupported)
558
+
535
559
  Uses `contact.accept` in `accept()`.
536
560
 
537
561
  TaskManager maintains a map of active tasks:
@@ -588,6 +612,14 @@ this.webSocketManager.on('message', (event) => {
588
612
 
589
613
  This keeps transcript and suggestion delivery aligned on the same per-task event surface.
590
614
 
615
+ **wxApp outdial `ContactEnded` / `AgentOutboundFailed` mapping:** Applies only when the current agent is a **wxApp-managed** outdial (`enableWxBetterTogether` + agent participant `deviceType: wxApp`). Pre-accept decline maps to `CONTACT_ENDED` when `wxAppAnswerPending` is false and `agentsPendingWrapUp` is empty — even if the backend sends misleading `interaction.state: wrapUp`. Post-accept wxApp agent end remaps to `OUTBOUND_FAILED` / `AGENT_ENDS` when `wxAppAnswerPending`, `agentsPendingWrapUp`, or `interaction.state === 'wrapUp'` indicates wrapup. **Non-wxApp** agent-terminated `AgentOutboundFailed` (`AGENT_ENDS` / `terminatingParty: Agent`) stays on `OUTBOUND_FAILED` so the OFFERED-state `shouldWrapUp` path runs; `suppressOutdialFailedPopup` on `taskData` skips the outdial-failed modal only. Non-wxApp `ContactEnded` keeps the legacy path. Other failure reasons (`CUSTOMER_BUSY`, etc.) still use `OUTBOUND_FAILED` with the popup. See `TaskManager.isWxAppManagedOutdialTask`, `isAgentTerminatedOutdialWrapup`, and `task-state-machine-spec.md`.
616
+
617
+ **wxApp consumer contract (WXCC-6026):** Hosts enable `enableWxBetterTogether` at init (Phase 1 init-only; re-init to change), bind UI to `task.uiControls` (including optional `main.keypad`), and call **`task.accept()`**, **`task.decline()`**, **`task.toggleMute({ muted? })`**, **`task.transmitDtmf({ dtmf })`**. SDK routes wxApp telephony internally on `Voice`. Shared-line `lineOwnerId` defaults from the wxApp agent participant when omitted.
618
+
619
+ **wxApp offer UI (`uiControlsComputer`):** `wxAppAcceptInFlight` disables accept/decline during the accept REST call. `wxAppAnswerPending` additionally disables accept and decline for **inbound** offers until ASSIGN; wxApp **outdial** keeps decline enabled during the post-accept "Calling…" phase so `cancelTask` remains available.
620
+
621
+ **wxApp mute backfill guard (`Voice.syncWxAppMuteFromCallDetails`):** Skips telephony `GET /calls/{callId}` when the interaction is terminated or the task is a pre-accept wxApp OFFERED offer (`wxAppAnswerPending` false). Post-accept OFFERED and engaged CONNECTED sync still run. `isWxAppEngagedForControls` and `getWebexCallingCallId` exclude `TERMINATED` and `COMPLETED`. Expected 400 / "Call not found" / `101002` responses are not logged as errors.
622
+
591
623
  For BROWSER login, TaskManager integrates with WebCalling:
592
624
 
593
625
  ```mermaid
@@ -689,6 +721,7 @@ isSecondaryEpDnAgent(interaction);
689
721
  | `TASK_WRAPUP_SUCCESS` | operational | Wrapup completed |
690
722
  | `TASK_TRANSFER_SUCCESS` | behavioral, business | Transfer completed |
691
723
  | `TASK_OUTDIAL_SUCCESS` | behavioral, business | Outdial completed |
724
+ | `TASK_CONFERENCE_PARTICIPANT_DROP_SUCCESS` | operational, behavioral, business | Participant Drop completed |
692
725
 
693
726
  ## Data Flow
694
727
  1. **WebSocket event arrives** → `TaskManager` maps CC event to `TaskEvent`.
@@ -727,6 +760,7 @@ Sequence coverage:
727
760
  | Incoming task creation | Incoming task | Unsupported media or incomplete event context prevents publication of a partial task. |
728
761
  | Voice hold/resume | Hold or resume | Invalid state rejects before transport; failure notification/HTTP error/timeout sends the matching failure event and throws. |
729
762
  | Consult/transfer/conference | Consult and transfer | Backend failure notifications drive explicit failure actions and preserve stable call context. |
763
+ | Participant Drop | Participant Drop | Validation rejects locally; AQM success/failure/HTTP/timeout paths settle and clean up without optimistic state changes. |
730
764
  | Wrapup/end | Wrapup or end | Validation and backend failures throw; backend events retain distinct WRAPPING_UP/COMPLETED/TERMINATED outcomes. |
731
765
  | WebRTC and digital accept | Channel-specific accept | WebRTC media/calling failure and Digital AQM failure follow different rejection paths. |
732
766
 
@@ -820,6 +854,85 @@ sequenceDiagram
820
854
  end
821
855
  ```
822
856
 
857
+ ### Participant Drop
858
+
859
+ ```mermaid
860
+ sequenceDiagram
861
+ participant App
862
+ participant Voice
863
+ participant Contact as routingContact
864
+ participant AQM as AqmReqs
865
+ participant Backend
866
+ participant WS as Primary WebSocket
867
+ participant TM as TaskManager/state machine
868
+ App->>Voice: dropConferenceParticipant({participantId})
869
+ alt participantId is invalid
870
+ Voice-->>App: reject before metrics, HTTP, or bind registration
871
+ else valid target
872
+ Voice->>Voice: select latest main interaction ID
873
+ Voice->>Contact: participant Drop request
874
+ Contact->>AQM: POST encoded path with empty body and sensitive-log redaction
875
+ AQM->>Backend: authenticated HTTP initiation
876
+ alt ParticipantLeftConference
877
+ Backend-->>WS: ParticipantLeftConference
878
+ WS-->>AQM: correlated success
879
+ AQM-->>Voice: TaskResponse
880
+ WS-->>TM: existing PARTICIPANT_LEAVE mapping updates roster/conference state
881
+ Voice-->>App: resolve
882
+ else ParticipantDropConferenceFailed, HTTP failure, or timeout
883
+ AQM-->>Voice: structured rejection
884
+ Voice-->>App: reject without participant data in logs/metrics
885
+ end
886
+ end
887
+ ```
888
+
889
+ `ParticipantLeftConference` is authoritative for both a customer hangup and an
890
+ agent-initiated customer Drop. The task removes only participants absent from
891
+ the latest interaction snapshot. TaskManager first uses the exact interaction
892
+ key, then the existing reservation fallback, and finally one unique task whose
893
+ `interaction.mainInteractionId`, interaction-level `parentInteractionId`, or
894
+ `callProcessingDetails.parentInteractionId` matches an identifier carried by the
895
+ lifecycle event. Object aliases are deduplicated and all aliases are removed
896
+ during terminal cleanup; ambiguous related-interaction matches are ignored
897
+ without logging participant data. Events carrying none of those correlation
898
+ identifiers are not routed. `ContactMerged` removes the EP-DN child task,
899
+ hydrates the main-interaction task, and publishes the existing `task:merged`
900
+ event so consumers can rebind to the surviving task object.
901
+
902
+ Dropping an Agent while that Agent is consulting continues through the existing
903
+ `PARTICIPANT_LEAVE` transition, which clears consult state and emits the normal
904
+ participant-left and task-end lifecycle. This self-departure check applies in
905
+ every active call-control state and uses an explicit self participant ID,
906
+ `hasLeft`, or removal of a previously active Agent from the participant map. A
907
+ participant-left event naming somebody else never infers self-departure from a
908
+ partial media roster. A consulted Agent receiving
909
+ `AgentConsultEnded` continues through `CONSULT_END`; current-agent departure is
910
+ evaluated before consult-initiator recovery. For the from-conference nested-consult
911
+ ordering race only, the guard also compares `mainCall` membership (located by
912
+ `mType`) when the Agent remains active in the participant map and is still present
913
+ on the consult leg. Missing, contradictory, or ordinary CONNECTED/HELD media snapshots
914
+ are non-terminal. Accepted consultees emit
915
+ `task:consultEnd` and `task:end`, while an unaccepted OFFERED consultee retains
916
+ the existing consult-end-only signal. A surviving consult initiator still
917
+ returns to the main-call state selected by the existing guards. Starting a
918
+ consult preserves the prior task snapshot so this membership comparison remains
919
+ available while the consult is initiating.
920
+
921
+ In the Contact Center sample roster, non-owner and Supervisor targets remain
922
+ visible but non-actionable; the UI does not display a separate read-only label.
923
+ The roster is visible whenever the viewing agent remains active on the main leg
924
+ and at least one supported non-customer participant (Agent, EP-DN, or Supervisor)
925
+ is visible. Consequently, Customer-only calls use the original 1-to-1 UI, while
926
+ a single Agent remains visible after the Customer leaves. Dropping the last
927
+ non-customer participant while the Customer remains restores the 1-to-1 UI.
928
+ Terminated tasks and viewing-agent departure hide the roster immediately.
929
+
930
+ The current consult leg may additionally contribute a pending EP-DN row before
931
+ conference merge. The sample displays its `dn`, then its participant/media key
932
+ as a fallback, and deduplicates the row once it joins the main leg. The owner can
933
+ see a disabled Drop action until merge; other active main-call agents see the
934
+ same row without an action. Consult-only Agent targets remain excluded.
935
+
823
936
  ### Wrapup or end
824
937
 
825
938
  ```mermaid
@@ -1270,7 +1383,7 @@ await cc.stationLogin({ loginOption: 'BROWSER', ... });
1270
1383
  - A shared Task base preserves a stable API while media-specific subclasses and a separate state engine enforce capability differences.
1271
1384
 
1272
1385
  ## 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.
1386
+ 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, Participant Drop validation/correlation/privacy, and success/failure/timeout paths.
1274
1387
 
1275
1388
  | Behavior / Requirement | Existing test evidence | Gap |
1276
1389
  |---|---|---|
@@ -1281,6 +1394,8 @@ Use `test/unit/spec/services/task/Task.ts`, `TaskFactory.ts`, `TaskManager.ts`,
1281
1394
  | `TASK-R-005` | `test/unit/spec/services/task/TaskFactory.ts`, `test/unit/spec/services/task/dialer.ts` | None. |
1282
1395
  | `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
1396
  | `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. |
1397
+ | `TASK-R-008` | `test/unit/spec/services/task/Task.ts`, `test/unit/spec/services/task/voice/Voice.ts`, `test/unit/spec/services/task/contact.ts`, `test/unit/spec/services/core/aqm-reqs.ts`, `test/unit/spec/services/task/TaskManager.ts` | None. |
1398
+ | `TASK-R-009` | `test/unit/spec/cc.ts`, `test/unit/spec/services/task/TaskFactory.ts`, `test/unit/spec/services/task/state-machine/uiControlsComputer.ts` | None. |
1284
1399
 
1285
1400
  ## Traceability
1286
1401
  - Repo architecture: `../../../../ai-docs/ARCHITECTURE.md` · Registry: `../../../../ai-docs/SPEC_INDEX.md`
@@ -14,6 +14,8 @@ export const CONSULT = '/consult';
14
14
  export const CONSULT_ACCEPT = '/consult/accept';
15
15
  export const CONSULT_END = '/consult/end';
16
16
  export const TRANSFER = '/transfer';
17
+ /** Backend destination value for direct entry-point transfers through the vteam endpoint. */
18
+ export const ENTRY_POINT_TRANSFER_DESTINATION_TYPE = 'entrypointDialNumber';
17
19
  export const CONSULT_TRANSFER = '/consult/transfer';
18
20
  export const PAUSE = '/record/pause';
19
21
  export const RESUME = '/record/resume';
@@ -73,6 +75,7 @@ export const METHODS = {
73
75
  TRANSFER: 'transfer',
74
76
  CONSULT_TRANSFER: 'consultTransfer',
75
77
  CONSULT_CONFERENCE: 'consultConference',
78
+ DROP_CONFERENCE_PARTICIPANT: 'dropConferenceParticipant',
76
79
  EXIT_CONFERENCE: 'exitConference',
77
80
  TRANSFER_CONFERENCE: 'transferConference',
78
81
  UPDATE_TASK_DATA: 'updateTaskData',
@@ -458,6 +458,36 @@ export default function routingContact(aqm: AqmReqs) {
458
458
  })
459
459
  ),
460
460
 
461
+ /*
462
+ * Drop another participant from an active conference
463
+ */
464
+ dropConferenceParticipant: aqm.req((p: {interactionId: string; participantId: string}) => ({
465
+ url: `${TASK_API}${p.interactionId}/conference/participants/${encodeURIComponent(
466
+ p.participantId
467
+ )}/drop`,
468
+ data: {},
469
+ host: WCC_API_GATEWAY,
470
+ err,
471
+ redactSensitiveLogs: true,
472
+ notifSuccess: {
473
+ bind: {
474
+ type: TASK_MESSAGE_TYPE,
475
+ data: {
476
+ type: CC_EVENTS.PARTICIPANT_LEFT_CONFERENCE,
477
+ interactionId: p.interactionId,
478
+ },
479
+ },
480
+ msg: {} as Contact.AgentContact,
481
+ },
482
+ notifFail: {
483
+ bind: {
484
+ type: TASK_MESSAGE_TYPE,
485
+ data: {type: CC_EVENTS.PARTICIPANT_DROP_CONFERENCE_FAILED},
486
+ },
487
+ errId: 'Service.aqm.task.dropConferenceParticipant',
488
+ },
489
+ })),
490
+
461
491
  /*
462
492
  * Exit conference
463
493
  */
@@ -1,6 +1,6 @@
1
1
  import {CC_FILE, METHODS} from '../../../constants';
2
2
  import {getErrorDetails} from '../../core/Utils';
3
- import {IDigital, TaskResponse, TaskData} from '../types';
3
+ import {ConsultTransferDestinationConfig, IDigital, TaskResponse, TaskData} from '../types';
4
4
  import {WrapupData} from '../../config/types';
5
5
  import Task from '../Task';
6
6
  import routingContact from '../contact';
@@ -13,7 +13,8 @@ export default class Digital extends Task implements IDigital {
13
13
  contact: ReturnType<typeof routingContact>,
14
14
  data: TaskData,
15
15
  wrapupData?: WrapupData,
16
- agentId?: string
16
+ agentId?: string,
17
+ consultTransferConfig?: ConsultTransferDestinationConfig
17
18
  ) {
18
19
  super(
19
20
  contact,
@@ -22,6 +23,7 @@ export default class Digital extends Task implements IDigital {
22
23
  isEndTaskEnabled: true,
23
24
  isEndConsultEnabled: false,
24
25
  isRecordingEnabled: false,
26
+ consultTransferConfig,
25
27
  },
26
28
  wrapupData,
27
29
  agentId