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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. package/.sdd/manifest.json +882 -0
  2. package/AGENTS.md +94 -0
  3. package/ai-docs/ARCHITECTURE.md +168 -0
  4. package/ai-docs/CONTRACTS.md +46 -0
  5. package/ai-docs/GETTING_STARTED.md +168 -0
  6. package/ai-docs/GLOSSARY.md +43 -0
  7. package/ai-docs/README.md +138 -0
  8. package/ai-docs/REVIEW_CHECKLIST.md +41 -0
  9. package/ai-docs/RULES.md +444 -0
  10. package/ai-docs/SECURITY.md +52 -0
  11. package/ai-docs/SERVICE_STATE.md +48 -0
  12. package/ai-docs/SPEC_INDEX.md +65 -0
  13. package/ai-docs/adr/0001-spec-source-policy.md +55 -0
  14. package/ai-docs/adr/README.md +8 -0
  15. package/ai-docs/adr/_adr-template.md +31 -0
  16. package/ai-docs/contact-center-spec.md +341 -0
  17. package/ai-docs/features/generated-spec-conformance-fidelity-remediation/spec/feature-spec.md +117 -0
  18. package/ai-docs/features/residual-warning-coverage-completion/spec/feature-spec.md +203 -0
  19. package/ai-docs/features/validator-code-fidelity-drift-fix/spec/feature-spec.md +315 -0
  20. package/ai-docs/patterns/event-driven-patterns.md +485 -0
  21. package/ai-docs/patterns/testing-patterns.md +480 -0
  22. package/ai-docs/patterns/typescript-patterns.md +365 -0
  23. package/ai-docs/templates/README.md +102 -0
  24. package/ai-docs/templates/documentation/create-agents-md.md +240 -0
  25. package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
  26. package/ai-docs/templates/existing-service/bug-fix.md +254 -0
  27. package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
  28. package/ai-docs/templates/new-method/00-master.md +80 -0
  29. package/ai-docs/templates/new-method/01-requirements.md +232 -0
  30. package/ai-docs/templates/new-method/02-implementation.md +295 -0
  31. package/ai-docs/templates/new-method/03-tests.md +201 -0
  32. package/ai-docs/templates/new-method/04-validation.md +141 -0
  33. package/ai-docs/templates/new-service/00-master.md +109 -0
  34. package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
  35. package/ai-docs/templates/new-service/02-code-generation.md +346 -0
  36. package/ai-docs/templates/new-service/03-integration.md +178 -0
  37. package/ai-docs/templates/new-service/04-test-generation.md +205 -0
  38. package/ai-docs/templates/new-service/05-validation.md +145 -0
  39. package/dist/cc.js +379 -50
  40. package/dist/cc.js.map +1 -1
  41. package/dist/config.js +6 -0
  42. package/dist/config.js.map +1 -1
  43. package/dist/constants.js +22 -2
  44. package/dist/constants.js.map +1 -1
  45. package/dist/index.js +27 -5
  46. package/dist/index.js.map +1 -1
  47. package/dist/metrics/behavioral-events.js +114 -0
  48. package/dist/metrics/behavioral-events.js.map +1 -1
  49. package/dist/metrics/constants.js +32 -3
  50. package/dist/metrics/constants.js.map +1 -1
  51. package/dist/services/ApiAiAssistant.js +318 -0
  52. package/dist/services/ApiAiAssistant.js.map +1 -0
  53. package/dist/services/UserPreference.js +427 -0
  54. package/dist/services/UserPreference.js.map +1 -0
  55. package/dist/services/agent/types.js.map +1 -1
  56. package/dist/services/config/Util.js +8 -4
  57. package/dist/services/config/Util.js.map +1 -1
  58. package/dist/services/config/constants.js +35 -2
  59. package/dist/services/config/constants.js.map +1 -1
  60. package/dist/services/config/index.js +41 -2
  61. package/dist/services/config/index.js.map +1 -1
  62. package/dist/services/config/types.js +66 -8
  63. package/dist/services/config/types.js.map +1 -1
  64. package/dist/services/constants.js +27 -1
  65. package/dist/services/constants.js.map +1 -1
  66. package/dist/services/core/Err.js.map +1 -1
  67. package/dist/services/core/Utils.js +122 -25
  68. package/dist/services/core/Utils.js.map +1 -1
  69. package/dist/services/core/aqm-reqs.js +92 -17
  70. package/dist/services/core/aqm-reqs.js.map +1 -1
  71. package/dist/services/core/websocket/WebSocketManager.js +22 -6
  72. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  73. package/dist/services/core/websocket/connection-service.js +3 -1
  74. package/dist/services/core/websocket/connection-service.js.map +1 -1
  75. package/dist/services/core/websocket/types.js.map +1 -1
  76. package/dist/services/index.js +6 -0
  77. package/dist/services/index.js.map +1 -1
  78. package/dist/services/task/Task.js +688 -0
  79. package/dist/services/task/Task.js.map +1 -0
  80. package/dist/services/task/TaskFactory.js +45 -0
  81. package/dist/services/task/TaskFactory.js.map +1 -0
  82. package/dist/services/task/TaskManager.js +751 -457
  83. package/dist/services/task/TaskManager.js.map +1 -1
  84. package/dist/services/task/TaskUtils.js +220 -23
  85. package/dist/services/task/TaskUtils.js.map +1 -1
  86. package/dist/services/task/constants.js +23 -2
  87. package/dist/services/task/constants.js.map +1 -1
  88. package/dist/services/task/dialer.js +129 -0
  89. package/dist/services/task/dialer.js.map +1 -1
  90. package/dist/services/task/digital/Digital.js +77 -0
  91. package/dist/services/task/digital/Digital.js.map +1 -0
  92. package/dist/services/task/state-machine/TaskStateMachine.js +873 -0
  93. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  94. package/dist/services/task/state-machine/actions.js +567 -0
  95. package/dist/services/task/state-machine/actions.js.map +1 -0
  96. package/dist/services/task/state-machine/constants.js +161 -0
  97. package/dist/services/task/state-machine/constants.js.map +1 -0
  98. package/dist/services/task/state-machine/guards.js +382 -0
  99. package/dist/services/task/state-machine/guards.js.map +1 -0
  100. package/dist/services/task/state-machine/index.js +53 -0
  101. package/dist/services/task/state-machine/index.js.map +1 -0
  102. package/dist/services/task/state-machine/types.js +54 -0
  103. package/dist/services/task/state-machine/types.js.map +1 -0
  104. package/dist/services/task/state-machine/uiControlsComputer.js +603 -0
  105. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  106. package/dist/services/task/taskDataNormalizer.js +99 -0
  107. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  108. package/dist/services/task/types.js +227 -4
  109. package/dist/services/task/types.js.map +1 -1
  110. package/dist/services/task/voice/Voice.js +1044 -0
  111. package/dist/services/task/voice/Voice.js.map +1 -0
  112. package/dist/services/task/voice/WebRTC.js +149 -0
  113. package/dist/services/task/voice/WebRTC.js.map +1 -0
  114. package/dist/types/cc.d.ts +894 -0
  115. package/dist/types/config.d.ts +72 -0
  116. package/dist/types/constants.d.ts +66 -0
  117. package/dist/types/index.d.ts +199 -0
  118. package/dist/types/logger-proxy.d.ts +71 -0
  119. package/dist/types/metrics/MetricsManager.d.ts +223 -0
  120. package/dist/types/metrics/behavioral-events.d.ts +29 -0
  121. package/dist/types/metrics/constants.d.ts +181 -0
  122. package/dist/types/services/AddressBook.d.ts +74 -0
  123. package/dist/types/services/ApiAiAssistant.d.ts +49 -0
  124. package/dist/types/services/EntryPoint.d.ts +67 -0
  125. package/dist/types/services/Queue.d.ts +76 -0
  126. package/dist/types/services/UserPreference.d.ts +118 -0
  127. package/dist/types/services/WebCallingService.d.ts +1 -0
  128. package/dist/types/services/agent/index.d.ts +46 -0
  129. package/dist/types/services/agent/types.d.ts +413 -0
  130. package/dist/types/services/config/Util.d.ts +20 -0
  131. package/dist/types/services/config/constants.d.ts +270 -0
  132. package/dist/types/services/config/index.d.ts +177 -0
  133. package/dist/types/services/config/types.d.ts +1368 -0
  134. package/dist/types/services/constants.d.ts +110 -0
  135. package/dist/types/services/core/Err.d.ts +125 -0
  136. package/dist/types/services/core/GlobalTypes.d.ts +58 -0
  137. package/dist/types/services/core/Utils.d.ts +121 -0
  138. package/dist/types/services/core/WebexRequest.d.ts +22 -0
  139. package/dist/types/services/core/aqm-reqs.d.ts +65 -0
  140. package/dist/types/services/core/constants.d.ts +99 -0
  141. package/dist/types/services/core/types.d.ts +47 -0
  142. package/dist/types/services/core/websocket/WebSocketManager.d.ts +36 -0
  143. package/dist/types/services/core/websocket/connection-service.d.ts +27 -0
  144. package/dist/types/services/core/websocket/keepalive.worker.d.ts +2 -0
  145. package/dist/types/services/core/websocket/types.d.ts +37 -0
  146. package/dist/types/services/index.d.ts +54 -0
  147. package/dist/types/services/task/AutoWrapup.d.ts +40 -0
  148. package/dist/types/services/task/Task.d.ts +157 -0
  149. package/dist/types/services/task/TaskFactory.d.ts +12 -0
  150. package/dist/types/services/task/TaskManager.d.ts +1 -0
  151. package/dist/types/services/task/TaskUtils.d.ts +138 -0
  152. package/dist/types/services/task/constants.d.ts +91 -0
  153. package/dist/types/services/task/contact.d.ts +69 -0
  154. package/dist/types/services/task/dialer.d.ts +73 -0
  155. package/dist/types/services/task/digital/Digital.d.ts +22 -0
  156. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1194 -0
  157. package/dist/types/services/task/state-machine/actions.d.ts +10 -0
  158. package/dist/types/services/task/state-machine/constants.d.ts +107 -0
  159. package/dist/types/services/task/state-machine/guards.d.ts +102 -0
  160. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  161. package/dist/types/services/task/state-machine/types.d.ts +269 -0
  162. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  163. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  164. package/dist/types/services/task/types.d.ts +1856 -0
  165. package/dist/types/services/task/voice/Voice.d.ts +184 -0
  166. package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
  167. package/dist/types/types.d.ts +778 -0
  168. package/dist/types/utils/PageCache.d.ts +173 -0
  169. package/dist/types/webex-config.d.ts +53 -0
  170. package/dist/types/webex.d.ts +8 -0
  171. package/dist/types.js +130 -1
  172. package/dist/types.js.map +1 -1
  173. package/dist/webex.js +14 -2
  174. package/dist/webex.js.map +1 -1
  175. package/package.json +16 -12
  176. package/src/cc.ts +477 -51
  177. package/src/config.ts +6 -0
  178. package/src/constants.ts +21 -1
  179. package/src/index.ts +24 -5
  180. package/src/metrics/ai-docs/AGENTS.md +350 -0
  181. package/src/metrics/ai-docs/ARCHITECTURE.md +338 -0
  182. package/src/metrics/ai-docs/metrics-spec.md +854 -0
  183. package/src/metrics/behavioral-events.ts +120 -0
  184. package/src/metrics/constants.ts +37 -3
  185. package/src/services/ApiAiAssistant.ts +412 -0
  186. package/src/services/UserPreference.ts +509 -0
  187. package/src/services/agent/ai-docs/AGENTS.md +240 -0
  188. package/src/services/agent/ai-docs/ARCHITECTURE.md +304 -0
  189. package/src/services/agent/ai-docs/agent-spec.md +504 -0
  190. package/src/services/agent/types.ts +1 -1
  191. package/src/services/ai-docs/AGENTS.md +386 -0
  192. package/src/services/ai-docs/services-spec.md +492 -0
  193. package/src/services/config/Util.ts +10 -2
  194. package/src/services/config/ai-docs/AGENTS.md +255 -0
  195. package/src/services/config/ai-docs/ARCHITECTURE.md +426 -0
  196. package/src/services/config/ai-docs/config-spec.md +669 -0
  197. package/src/services/config/constants.ts +37 -1
  198. package/src/services/config/index.ts +45 -1
  199. package/src/services/config/types.ts +241 -11
  200. package/src/services/constants.ts +29 -0
  201. package/src/services/core/Err.ts +3 -0
  202. package/src/services/core/Utils.ts +143 -30
  203. package/src/services/core/ai-docs/AGENTS.md +381 -0
  204. package/src/services/core/ai-docs/ARCHITECTURE.md +698 -0
  205. package/src/services/core/ai-docs/core-spec.md +783 -0
  206. package/src/services/core/aqm-reqs.ts +100 -22
  207. package/src/services/core/websocket/WebSocketManager.ts +23 -6
  208. package/src/services/core/websocket/connection-service.ts +5 -1
  209. package/src/services/core/websocket/types.ts +1 -1
  210. package/src/services/index.ts +4 -0
  211. package/src/services/task/Task.ts +837 -0
  212. package/src/services/task/TaskFactory.ts +55 -0
  213. package/src/services/task/TaskManager.ts +793 -521
  214. package/src/services/task/TaskUtils.ts +314 -24
  215. package/src/services/task/ai-docs/AGENTS.md +457 -0
  216. package/src/services/task/ai-docs/ARCHITECTURE.md +594 -0
  217. package/src/services/task/ai-docs/task-spec.md +1319 -0
  218. package/src/services/task/constants.ts +23 -0
  219. package/src/services/task/dialer.ts +136 -1
  220. package/src/services/task/digital/Digital.ts +95 -0
  221. package/src/services/task/state-machine/TaskStateMachine.ts +1166 -0
  222. package/src/services/task/state-machine/actions.ts +738 -0
  223. package/src/services/task/state-machine/ai-docs/AGENTS.md +458 -0
  224. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1137 -0
  225. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2177 -0
  226. package/src/services/task/state-machine/constants.ts +172 -0
  227. package/src/services/task/state-machine/guards.ts +445 -0
  228. package/src/services/task/state-machine/index.ts +28 -0
  229. package/src/services/task/state-machine/types.ts +243 -0
  230. package/src/services/task/state-machine/uiControlsComputer.ts +961 -0
  231. package/src/services/task/taskDataNormalizer.ts +137 -0
  232. package/src/services/task/types.ts +734 -71
  233. package/src/services/task/voice/Voice.ts +1270 -0
  234. package/src/services/task/voice/WebRTC.ts +187 -0
  235. package/src/types.ts +205 -2
  236. package/src/utils/AGENTS.md +278 -0
  237. package/src/utils/ai-docs/utils-spec.md +381 -0
  238. package/src/webex.js +2 -0
  239. package/test/unit/spec/cc.ts +503 -43
  240. package/test/unit/spec/logger-proxy.ts +70 -0
  241. package/test/unit/spec/services/ApiAiAssistant.ts +273 -0
  242. package/test/unit/spec/services/UserPreference.ts +401 -0
  243. package/test/unit/spec/services/WebCallingService.ts +7 -1
  244. package/test/unit/spec/services/config/index.ts +85 -29
  245. package/test/unit/spec/services/core/Utils.ts +481 -2
  246. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +137 -41
  247. package/test/unit/spec/services/core/websocket/connection-service.ts +3 -1
  248. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  249. package/test/unit/spec/services/task/Task.ts +477 -0
  250. package/test/unit/spec/services/task/TaskFactory.ts +62 -0
  251. package/test/unit/spec/services/task/TaskManager.ts +1001 -1003
  252. package/test/unit/spec/services/task/TaskUtils.ts +235 -0
  253. package/test/unit/spec/services/task/dialer.ts +372 -96
  254. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  255. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +2651 -0
  256. package/test/unit/spec/services/task/state-machine/guards.ts +637 -0
  257. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  258. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +2663 -0
  259. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  260. package/test/unit/spec/services/task/voice/Voice.ts +649 -0
  261. package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
  262. package/umd/contact-center.min.js +2 -2
  263. package/umd/contact-center.min.js.map +1 -1
  264. package/dist/services/task/index.js +0 -1525
  265. package/dist/services/task/index.js.map +0 -1
  266. package/src/services/task/index.ts +0 -1801
  267. package/test/unit/spec/services/task/index.ts +0 -2184
@@ -0,0 +1,1044 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _constants = require("../../../constants");
8
+ var _Utils = require("../../core/Utils");
9
+ var _types = require("../types");
10
+ var _Task = _interopRequireDefault(require("../Task"));
11
+ var _loggerProxy = _interopRequireDefault(require("../../../logger-proxy"));
12
+ var _MetricsManager = _interopRequireDefault(require("../../../metrics/MetricsManager"));
13
+ var _constants2 = require("../../../metrics/constants");
14
+ var _stateMachine = require("../state-machine");
15
+ var _TaskUtils = require("../TaskUtils");
16
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
17
+ class Voice extends _Task.default {
18
+ constructor(contact, data, callOptions, wrapupData, agentId) {
19
+ const resolvedOptions = {
20
+ isEndTaskEnabled: callOptions?.isEndTaskEnabled ?? true,
21
+ isEndConsultEnabled: callOptions?.isEndConsultEnabled ?? true,
22
+ voiceVariant: callOptions?.voiceVariant ?? _types.VOICE_VARIANT.PSTN,
23
+ isRecordingEnabled: callOptions?.isRecordingEnabled ?? true
24
+ };
25
+ super(contact, data, {
26
+ ...resolvedOptions
27
+ }, wrapupData, agentId);
28
+ }
29
+ getStateMachineSnapshot() {
30
+ return this.stateMachineService?.getSnapshot?.();
31
+ }
32
+
33
+ /**
34
+ * This method is used to accept the task.
35
+ * It is expected to be overridden by child classes.
36
+ * @returns Promise<TaskResponse>
37
+ * @throws Error
38
+ */
39
+ async accept() {
40
+ super.unsupportedMethodError(_constants.METHODS.ACCEPT);
41
+ }
42
+
43
+ /**
44
+ * This method is used to decline the task.
45
+ * It is expected to be overridden by child classes.
46
+ * @returns Promise<TaskResponse>
47
+ * @throws Error
48
+ */
49
+ async decline() {
50
+ super.unsupportedMethodError(_constants.METHODS.REJECT);
51
+ }
52
+
53
+ /**
54
+ * This is used to hold the task.
55
+ * @returns Promise<TaskResponse>
56
+ * @throws Error
57
+ * @example
58
+ * ```typescript
59
+ * task.hold().then(()=>{}).catch(()=>{})
60
+ * ```
61
+ * */
62
+ async hold() {
63
+ return this.holdResume();
64
+ }
65
+
66
+ /**
67
+ * This is used to resume the task.
68
+ * @returns Promise<TaskResponse>
69
+ * @throws Error
70
+ * @example
71
+ * ```typescript
72
+ * task.resume().then(()=>{}).catch(()=>{})
73
+ * ```
74
+ * */
75
+ async resume() {
76
+ return this.holdResume();
77
+ }
78
+
79
+ /**
80
+ * This is used to hold or resume the task.
81
+ * @param isHeld: boolean - true to hold the task, false to resume it
82
+ * @returns Promise<TaskResponse>
83
+ * @throws Error
84
+ * @example
85
+ * ```typescript
86
+ * task.holdResume(isHeld: true).then(()=>{}).catch(()=>{})
87
+ * ```
88
+ * */
89
+ async holdResume() {
90
+ /*
91
+ Determine if the task is being held or resumed based on the media resource state
92
+ If the media resource is not found, default to resuming the task
93
+ */
94
+ const snapshot = this.getStateMachineSnapshot();
95
+ const snapshotState = snapshot?.value;
96
+ const mainInteractionId = this.data.interaction?.mainInteractionId || this.data.interactionId;
97
+ const mainMediaResource = this.data.interaction?.media?.[mainInteractionId]?.mediaResourceId || this.data.mediaResourceId;
98
+ const mediaHoldState = this.data.interaction?.media?.[mainInteractionId]?.isHold ?? this.data.interaction.media?.[mainMediaResource]?.isHold;
99
+ let shouldHold = !(mediaHoldState ?? false);
100
+ if (snapshotState === _stateMachine.TaskState.HELD || snapshotState === _stateMachine.TaskState.RESUME_INITIATING) {
101
+ shouldHold = false;
102
+ } else if (snapshotState === _stateMachine.TaskState.CONNECTED && mediaHoldState !== true) {
103
+ // CONNECTED with isHold:true can happen after consult RONA/decline when event ordering
104
+ // leaves the machine in CONNECTED while main media is still on hold — resume on first click.
105
+ shouldHold = true;
106
+ }
107
+
108
+ // Validate operation is allowed in current state
109
+ const state = snapshot;
110
+ if (state) {
111
+ const currentState = state.value;
112
+ if (shouldHold) {
113
+ if (!state.matches(_stateMachine.TaskState.CONNECTED)) {
114
+ const error = new Error(`Cannot hold call in current state: ${currentState}`);
115
+ _loggerProxy.default.error('Hold operation not allowed', {
116
+ module: _constants.CC_FILE,
117
+ method: _constants.METHODS.HOLD_RESUME,
118
+ interactionId: this.data.interactionId
119
+ });
120
+ throw error;
121
+ }
122
+ } else if (!state.matches(_stateMachine.TaskState.HELD) && !(state.matches(_stateMachine.TaskState.CONFERENCING) && mediaHoldState === true) && !(state.matches(_stateMachine.TaskState.CONNECTED) && mediaHoldState === true)) {
123
+ const error = new Error(`Cannot resume call in current state: ${currentState}`);
124
+ _loggerProxy.default.error('Resume operation not allowed', {
125
+ module: _constants.CC_FILE,
126
+ method: _constants.METHODS.HOLD_RESUME,
127
+ interactionId: this.data.interactionId
128
+ });
129
+ throw error;
130
+ }
131
+ }
132
+
133
+ // Send initiating event to transition to intermediate state
134
+ if (this.stateMachineService) {
135
+ const initiatingEvent = shouldHold ? _stateMachine.TaskEvent.HOLD_INITIATED : _stateMachine.TaskEvent.UNHOLD_INITIATED;
136
+ this.stateMachineService.send({
137
+ type: initiatingEvent,
138
+ mediaResourceId: mainMediaResource
139
+ });
140
+ }
141
+ _loggerProxy.default.info(`${shouldHold ? 'Holding' : 'Resuming'} task`, {
142
+ module: _constants.CC_FILE,
143
+ method: _constants.METHODS.HOLD_RESUME,
144
+ interactionId: this.data.interactionId
145
+ });
146
+ const [successEvt, failedEvt] = shouldHold ? [_constants2.METRIC_EVENT_NAMES.TASK_HOLD_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_HOLD_FAILED] : [_constants2.METRIC_EVENT_NAMES.TASK_RESUME_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_RESUME_FAILED];
147
+ this.metricsManager.timeEvent([successEvt, failedEvt]);
148
+ try {
149
+ let response;
150
+ if (shouldHold) {
151
+ response = await this.contact.hold({
152
+ interactionId: this.data.interactionId,
153
+ data: {
154
+ mediaResourceId: mainMediaResource
155
+ }
156
+ });
157
+
158
+ // Send success event to complete the transition
159
+ if (this.stateMachineService) {
160
+ this.stateMachineService.send({
161
+ type: _stateMachine.TaskEvent.HOLD_SUCCESS,
162
+ mediaResourceId: mainMediaResource
163
+ });
164
+ }
165
+ this.metricsManager.trackEvent(successEvt, {
166
+ taskId: this.data.interactionId,
167
+ mediaResourceId: mainMediaResource,
168
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response)
169
+ }, ['operational', 'behavioral']);
170
+ _loggerProxy.default.log(`Task placed on hold successfully`, {
171
+ module: _constants.CC_FILE,
172
+ method: _constants.METHODS.HOLD_RESUME,
173
+ trackingId: response.trackingId,
174
+ interactionId: this.data.interactionId
175
+ });
176
+ } else {
177
+ response = await this.contact.unHold({
178
+ interactionId: this.data.interactionId,
179
+ data: {
180
+ mediaResourceId: mainMediaResource
181
+ }
182
+ });
183
+
184
+ // Send success event to complete the transition
185
+ if (this.stateMachineService) {
186
+ this.stateMachineService.send({
187
+ type: _stateMachine.TaskEvent.UNHOLD_SUCCESS,
188
+ mediaResourceId: mainMediaResource
189
+ });
190
+ }
191
+ this.metricsManager.trackEvent(successEvt, {
192
+ taskId: this.data.interactionId,
193
+ mainInteractionId,
194
+ mediaResourceId: mainMediaResource,
195
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response)
196
+ }, ['operational', 'behavioral']);
197
+ _loggerProxy.default.log(`Task resumed successfully`, {
198
+ module: _constants.CC_FILE,
199
+ method: _constants.METHODS.HOLD_RESUME,
200
+ trackingId: response.trackingId,
201
+ interactionId: this.data.interactionId
202
+ });
203
+ }
204
+ return response;
205
+ } catch (error) {
206
+ const failureEvent = shouldHold ? _stateMachine.TaskEvent.HOLD_FAILED : _stateMachine.TaskEvent.UNHOLD_FAILED;
207
+ this.stateMachineService.send({
208
+ type: failureEvent,
209
+ reason: error.toString(),
210
+ mediaResourceId: this.data.mediaResourceId
211
+ });
212
+ const {
213
+ error: detailedError
214
+ } = (0, _Utils.getErrorDetails)(error, 'holdResume', _constants.CC_FILE);
215
+ this.metricsManager.trackEvent(failedEvt, shouldHold ? {
216
+ taskId: this.data.interactionId,
217
+ mediaResourceId: this.data.mediaResourceId,
218
+ error: error.toString(),
219
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
220
+ } : {
221
+ taskId: this.data.interactionId,
222
+ error: error.toString(),
223
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
224
+ }, ['operational', 'behavioral']);
225
+ throw detailedError;
226
+ }
227
+ }
228
+
229
+ /**
230
+ * This is used to pause the call recording
231
+ * @returns Promise<TaskResponse>
232
+ * @throws Error
233
+ * @example
234
+ * ```typescript
235
+ * task.pauseRecording().then(()=>{}).catch(()=>{});
236
+ * ```
237
+ */
238
+ async pauseRecording() {
239
+ // Validate recording is active
240
+ const state = this.getStateMachineSnapshot();
241
+ if (state) {
242
+ const {
243
+ recordingControlsAvailable,
244
+ recordingInProgress
245
+ } = state.context;
246
+ const recordingActive = Boolean(recordingControlsAvailable && recordingInProgress);
247
+ if (!recordingActive) {
248
+ const error = new Error('Recording is not active or already paused');
249
+ _loggerProxy.default.error('Pause recording operation not allowed', {
250
+ module: _constants.CC_FILE,
251
+ method: 'pauseRecording',
252
+ interactionId: this.data.interactionId
253
+ });
254
+ throw error;
255
+ }
256
+ }
257
+ try {
258
+ _loggerProxy.default.info(`Pausing recording`, {
259
+ module: _constants.CC_FILE,
260
+ method: 'pauseRecording',
261
+ interactionId: this.data.interactionId
262
+ });
263
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_FAILED]);
264
+ const result = await this.contact.pauseRecording({
265
+ interactionId: this.data.interactionId
266
+ });
267
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_SUCCESS, {
268
+ taskId: this.data.interactionId,
269
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result)
270
+ }, ['operational', 'behavioral', 'business']);
271
+ _loggerProxy.default.log(`Recording paused successfully`, {
272
+ module: _constants.CC_FILE,
273
+ method: 'pauseRecording',
274
+ trackingId: result.trackingId,
275
+ interactionId: this.data.interactionId
276
+ });
277
+ return result;
278
+ } catch (error) {
279
+ const {
280
+ error: detailedError
281
+ } = (0, _Utils.getErrorDetails)(error, 'pauseRecording', _constants.CC_FILE);
282
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_FAILED, {
283
+ taskId: this.data.interactionId,
284
+ error: error.toString(),
285
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
286
+ }, ['operational', 'behavioral', 'business']);
287
+ throw detailedError;
288
+ }
289
+ }
290
+
291
+ /**
292
+ * This is used to pause the call recording
293
+ * @param resumeRecordingPayload
294
+ * @returns Promise<TaskResponse>
295
+ * @throws Error
296
+ * @example
297
+ * ```typescript
298
+ * task.resumeRecording(resumeRecordingPayload).then(()=>{}).catch(()=>{});
299
+ * ```
300
+ */
301
+ async resumeRecording(resumeRecordingPayload) {
302
+ // Validate recording is paused
303
+ const state = this.getStateMachineSnapshot();
304
+ if (state) {
305
+ const {
306
+ recordingControlsAvailable,
307
+ recordingInProgress
308
+ } = state.context;
309
+ const recordingPaused = Boolean(recordingControlsAvailable && !recordingInProgress);
310
+ if (!recordingPaused) {
311
+ const error = new Error('Recording is not paused');
312
+ _loggerProxy.default.error('Resume recording operation not allowed', {
313
+ module: _constants.CC_FILE,
314
+ method: 'resumeRecording',
315
+ interactionId: this.data.interactionId
316
+ });
317
+ throw error;
318
+ }
319
+ }
320
+ try {
321
+ _loggerProxy.default.info(`Resuming recording`, {
322
+ module: _constants.CC_FILE,
323
+ method: 'resumeRecording',
324
+ interactionId: this.data.interactionId
325
+ });
326
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_FAILED]);
327
+ resumeRecordingPayload ??= {
328
+ autoResumed: false
329
+ };
330
+ const result = await this.contact.resumeRecording({
331
+ interactionId: this.data.interactionId,
332
+ data: resumeRecordingPayload
333
+ });
334
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_SUCCESS, {
335
+ taskId: this.data.interactionId,
336
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result)
337
+ }, ['operational', 'behavioral', 'business']);
338
+ _loggerProxy.default.log(`Recording resumed successfully`, {
339
+ module: _constants.CC_FILE,
340
+ method: 'resumeRecording',
341
+ trackingId: result.trackingId,
342
+ interactionId: this.data.interactionId
343
+ });
344
+ return result;
345
+ } catch (error) {
346
+ const {
347
+ error: detailedError
348
+ } = (0, _Utils.getErrorDetails)(error, 'resumeRecording', _constants.CC_FILE);
349
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_FAILED, {
350
+ taskId: this.data.interactionId,
351
+ error: error.toString(),
352
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
353
+ }, ['operational', 'behavioral', 'business']);
354
+ throw detailedError;
355
+ }
356
+ }
357
+
358
+ /**
359
+ * This is used to consult the task
360
+ * @param consultPayload
361
+ * @returns Promise<TaskResponse>
362
+ * @throws Error
363
+ * @example
364
+ * ```typescript
365
+ * const consultPayload = {
366
+ * destination: 'myBuddyAgentId',
367
+ * destinationType: DESTINATION_TYPE.AGENT,
368
+ * }
369
+ * task.consult(consultPayload).then(()=>{}).catch(()=>{});
370
+ * ```
371
+ * */
372
+ async consult(consultPayload) {
373
+ // Validate consult is allowed
374
+ const state = this.getStateMachineSnapshot();
375
+ const canConsult = state && (state.matches(_stateMachine.TaskState.CONNECTED) || state.matches(_stateMachine.TaskState.HELD) || state.matches(_stateMachine.TaskState.CONFERENCING));
376
+ if (!canConsult) {
377
+ const currentState = state?.value;
378
+ const error = new Error(`Cannot initiate consult in ${currentState} state`);
379
+ _loggerProxy.default.error('Consult operation not allowed', {
380
+ module: _constants.CC_FILE,
381
+ method: 'consult',
382
+ interactionId: this.data.interactionId
383
+ });
384
+ throw error;
385
+ }
386
+
387
+ // Send initiating event to transition to CONSULT_INITIATING state
388
+ if (this.stateMachineService) {
389
+ this.stateMachineService.send({
390
+ type: _stateMachine.TaskEvent.CONSULT,
391
+ destination: consultPayload.to,
392
+ destinationType: consultPayload.destinationType
393
+ });
394
+ }
395
+ const requestInteractionId = this.data.interaction?.mainInteractionId || this.data.interactionId;
396
+ try {
397
+ _loggerProxy.default.info(`Starting consult`, {
398
+ module: _constants.CC_FILE,
399
+ method: 'consult',
400
+ interactionId: requestInteractionId
401
+ });
402
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_CONSULT_START_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_CONSULT_START_FAILED]);
403
+ const result = await this.contact.consult({
404
+ interactionId: requestInteractionId,
405
+ data: consultPayload
406
+ });
407
+
408
+ // Send success event to transition to CONSULTING state
409
+ if (this.stateMachineService) {
410
+ this.stateMachineService.send({
411
+ type: _stateMachine.TaskEvent.CONSULT_SUCCESS,
412
+ taskData: result.data
413
+ });
414
+ }
415
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONSULT_START_SUCCESS, {
416
+ taskId: this.data.interactionId,
417
+ requestInteractionId,
418
+ destination: consultPayload.to,
419
+ destinationType: consultPayload.destinationType,
420
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result)
421
+ }, ['operational', 'behavioral', 'business']);
422
+ _loggerProxy.default.log(`Consult successfully initiated to ${consultPayload.to}`, {
423
+ module: _constants.CC_FILE,
424
+ method: 'consult',
425
+ trackingId: result.trackingId,
426
+ interactionId: requestInteractionId
427
+ });
428
+ return result;
429
+ } catch (error) {
430
+ this.stateMachineService.send({
431
+ type: _stateMachine.TaskEvent.CONSULT_FAILED,
432
+ reason: error.toString()
433
+ });
434
+ const {
435
+ error: detailedError
436
+ } = (0, _Utils.getErrorDetails)(error, 'consult', _constants.CC_FILE);
437
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONSULT_START_FAILED, {
438
+ taskId: this.data.interactionId,
439
+ requestInteractionId,
440
+ destination: consultPayload.to,
441
+ destinationType: consultPayload.destinationType,
442
+ error: error.toString(),
443
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
444
+ }, ['operational', 'behavioral', 'business']);
445
+ throw detailedError;
446
+ }
447
+ }
448
+
449
+ /**
450
+ * This is used to end the consult session on the task.
451
+ * @param consultEndPayload - Payload indicating consult end flags and identifiers
452
+ * @returns Promise<TaskResponse>
453
+ * @throws Error
454
+ * @example
455
+ * ```typescript
456
+ * task.endConsult({
457
+ * isConsult: true,
458
+ * queueId: 'myQueueId',
459
+ * taskId: 'taskId',
460
+ * });
461
+ * ```
462
+ */
463
+ async endConsult(consultEndPayload) {
464
+ const requestInteractionId = this.data.interaction?.mainInteractionId || this.data.interactionId;
465
+ try {
466
+ _loggerProxy.default.info(`Ending consult`, {
467
+ module: _constants.CC_FILE,
468
+ method: 'endConsult',
469
+ interactionId: requestInteractionId
470
+ });
471
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_CONSULT_END_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_CONSULT_END_FAILED]);
472
+ const result = await this.contact.consultEnd({
473
+ interactionId: requestInteractionId,
474
+ data: consultEndPayload
475
+ });
476
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONSULT_END_SUCCESS, {
477
+ taskId: this.data.interactionId,
478
+ requestInteractionId,
479
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result)
480
+ }, ['operational', 'behavioral', 'business']);
481
+ _loggerProxy.default.log(`Consult ended successfully`, {
482
+ module: _constants.CC_FILE,
483
+ method: 'endConsult',
484
+ trackingId: result.trackingId,
485
+ interactionId: this.data.interactionId
486
+ });
487
+ return result;
488
+ } catch (error) {
489
+ const {
490
+ error: detailedError
491
+ } = (0, _Utils.getErrorDetails)(error, 'endConsult', _constants.CC_FILE);
492
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONSULT_END_FAILED, {
493
+ taskId: this.data.interactionId,
494
+ requestInteractionId,
495
+ error: error.toString(),
496
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
497
+ }, ['operational', 'behavioral', 'business']);
498
+ throw detailedError;
499
+ }
500
+ }
501
+
502
+ /**
503
+ * This is used to transfer the task.
504
+ * @param payload - Transfer payload
505
+ * @returns Promise<TaskResponse>
506
+ * @throws Error
507
+ * @example
508
+ * ```typescript
509
+ * task.transfer({
510
+ * to: 'destinationId',
511
+ * destinationType: DESTINATION_TYPE.AGENT,
512
+ * consult: true, // Optional, if true will perform a consult transfer else blind transfer
513
+ * });
514
+ * ```
515
+ */
516
+ async transfer(payload) {
517
+ try {
518
+ _loggerProxy.default.info(`Transferring task to ${payload.to}`, {
519
+ module: _constants.CC_FILE,
520
+ method: _constants.METHODS.TRANSFER_CALL,
521
+ interactionId: this.data.interactionId
522
+ });
523
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED]);
524
+
525
+ // consult transfer path
526
+ if (this.data.interaction.state === 'consulting') {
527
+ const normalizedDestinationType = payload.destinationType === 'Agent' || payload.destinationType === 'Queue' ? payload.destinationType.toLowerCase() : payload.destinationType;
528
+ let consultPayload = {
529
+ to: payload.to,
530
+ destinationType: normalizedDestinationType
531
+ };
532
+ if (normalizedDestinationType === _types.CONSULT_TRANSFER_DESTINATION_TYPE.QUEUE) {
533
+ const consultContext = this.getStateMachineSnapshot()?.context;
534
+ const destAgent = consultContext?.consultDestinationAgentId || this.data.destAgentId;
535
+ if (!destAgent) {
536
+ throw new Error('No agent has accepted this queue consult yet');
537
+ }
538
+ consultPayload = {
539
+ to: destAgent,
540
+ destinationType: _types.CONSULT_TRANSFER_DESTINATION_TYPE.AGENT
541
+ };
542
+ }
543
+ const result = await this.contact.consultTransfer({
544
+ interactionId: this.data.interactionId,
545
+ data: consultPayload
546
+ });
547
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS, {
548
+ taskId: this.data.interactionId,
549
+ destination: consultPayload.to,
550
+ destinationType: consultPayload.destinationType,
551
+ isConsultTransfer: true,
552
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result)
553
+ }, ['operational', 'behavioral', 'business']);
554
+ _loggerProxy.default.log(`Consult transfer completed successfully to ${consultPayload.to}`, {
555
+ module: _constants.CC_FILE,
556
+ method: _constants.METHODS.TRANSFER_CALL,
557
+ trackingId: result.trackingId,
558
+ interactionId: this.data.interactionId
559
+ });
560
+ return result;
561
+ }
562
+
563
+ // standard blind transfer
564
+ return await super.transfer({
565
+ to: payload.to,
566
+ destinationType: payload.destinationType
567
+ });
568
+ } catch (err) {
569
+ const {
570
+ error: detailedError
571
+ } = (0, _Utils.getErrorDetails)(err, _constants.METHODS.TRANSFER_CALL, _constants.CC_FILE);
572
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED, {
573
+ taskId: this.data.interactionId,
574
+ destination: payload.to,
575
+ destinationType: payload.destinationType,
576
+ isConsultTransfer: this.data.interaction.state === 'consulting',
577
+ error: err.toString(),
578
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(err.details || {})
579
+ }, ['operational', 'behavioral', 'business']);
580
+ throw detailedError;
581
+ }
582
+ }
583
+
584
+ /**
585
+ * Start a consult conference, merging main and consult calls.
586
+ */
587
+ async consultConference() {
588
+ const derivedDestAgentId = this.data.interaction && this.data.agentId ? (0, _Utils.calculateDestAgentId)(this.data.interaction, this.data.agentId) : '';
589
+ const derivedDestType = this.data.interaction && this.data.agentId ? (0, _Utils.calculateDestType)(this.data.interaction, this.data.agentId) : '';
590
+
591
+ // derivedDestType is most reliable as it inspects live interaction participants
592
+ const resolvedDestinationType = derivedDestType || this.getStateMachineSnapshot()?.context?.consultDestinationType || this.data.destinationType || 'agent';
593
+ const consultationData = {
594
+ agentId: this.data.agentId,
595
+ destinationType: resolvedDestinationType,
596
+ // derivedDestAgentId is most reliable as it resolves epId for EP_DN
597
+ // and agent ID for regular agents from live interaction data
598
+ destAgentId: derivedDestAgentId || this.getStateMachineSnapshot()?.context?.consultDestinationAgentId || this.data.destAgentId
599
+ };
600
+
601
+ // Send state machine event to transition to CONF_INITIATING
602
+ if (this.stateMachineService) {
603
+ this.stateMachineService.send({
604
+ type: _stateMachine.TaskEvent.MERGE_TO_CONFERENCE
605
+ });
606
+ }
607
+ try {
608
+ if (!consultationData.destAgentId) {
609
+ throw new Error('Unable to determine consult destination for conference');
610
+ }
611
+ _loggerProxy.default.info(`Initiating consult conference to ${consultationData.destAgentId}`, {
612
+ module: _constants.CC_FILE,
613
+ method: _constants.METHODS.CONSULT_CONFERENCE,
614
+ interactionId: this.data.interactionId
615
+ });
616
+ const paramsDataForConferenceV2 = (0, _Utils.buildConsultConferenceParamData)(consultationData, this.data.interactionId);
617
+ const response = await this.contact.consultConference({
618
+ interactionId: paramsDataForConferenceV2.interactionId,
619
+ data: paramsDataForConferenceV2.data
620
+ });
621
+
622
+ // Send success event to transition to CONFERENCING
623
+ if (this.stateMachineService) {
624
+ this.stateMachineService.send({
625
+ type: _stateMachine.TaskEvent.CONFERENCE_START
626
+ });
627
+ }
628
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_START_SUCCESS, {
629
+ taskId: this.data.interactionId,
630
+ destination: paramsDataForConferenceV2.data.to,
631
+ destinationType: paramsDataForConferenceV2.data.destinationType,
632
+ agentId: paramsDataForConferenceV2.data.agentId,
633
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response)
634
+ }, ['operational', 'behavioral', 'business']);
635
+ _loggerProxy.default.log(`Consult conference started successfully`, {
636
+ module: _constants.CC_FILE,
637
+ method: _constants.METHODS.CONSULT_CONFERENCE,
638
+ interactionId: this.data.interactionId
639
+ });
640
+ return response;
641
+ } catch (error) {
642
+ // Send failure event to revert state
643
+ if (this.stateMachineService) {
644
+ this.stateMachineService.send({
645
+ type: _stateMachine.TaskEvent.CONFERENCE_FAILED,
646
+ reason: error.toString()
647
+ });
648
+ }
649
+ const {
650
+ error: detailedError
651
+ } = (0, _Utils.getErrorDetails)(error, _constants.METHODS.CONSULT_CONFERENCE, _constants.CC_FILE);
652
+ const failedParamsData = (0, _Utils.buildConsultConferenceParamData)(consultationData, this.data.interactionId);
653
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_START_FAILED, {
654
+ taskId: this.data.interactionId,
655
+ destination: failedParamsData.data.to,
656
+ destinationType: failedParamsData.data.destinationType,
657
+ agentId: failedParamsData.data.agentId,
658
+ error: error.toString(),
659
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
660
+ }, ['operational', 'behavioral', 'business']);
661
+ _loggerProxy.default.error(`Failed to start consult conference`, {
662
+ module: _constants.CC_FILE,
663
+ method: _constants.METHODS.CONSULT_CONFERENCE,
664
+ interactionId: this.data.interactionId
665
+ });
666
+ throw detailedError;
667
+ }
668
+ }
669
+
670
+ /**
671
+ * Exit from a conference call.
672
+ * Per conference-spec.md:
673
+ * - Primary agent exits to wrapup
674
+ * - Non-primary agent exits to available/connected
675
+ * - Other participants continue the call
676
+ *
677
+ * @returns Promise<TaskResponse>
678
+ * @throws Error if not in conference or exit fails
679
+ * @example
680
+ * ```typescript
681
+ * task.exitConference().then(() => {}).catch(() => {});
682
+ * ```
683
+ */
684
+ async exitConference() {
685
+ // Validate we're in conference state OR conference is in progress per task data
686
+ // This handles cases where:
687
+ // 1. State machine is in CONFERENCING state
688
+ // 2. State machine is in CONNECTED but conference is active (e.g., ownership transferred)
689
+ const state = this.getStateMachineSnapshot();
690
+ const isConferencingState = state?.matches(_stateMachine.TaskState.CONFERENCING);
691
+ const isConferenceInProgressFromData = this.data ? (0, _TaskUtils.getIsConferenceInProgress)(this.data) : false;
692
+ if (!state || !isConferencingState && !isConferenceInProgressFromData) {
693
+ const currentState = state?.value;
694
+ const error = new Error(`Cannot exit conference in ${currentState} state`);
695
+ _loggerProxy.default.error('Exit conference operation not allowed', {
696
+ module: _constants.CC_FILE,
697
+ method: 'exitConference',
698
+ interactionId: this.data.interactionId
699
+ });
700
+ throw error;
701
+ }
702
+
703
+ // Send state machine event
704
+ if (this.stateMachineService) {
705
+ this.stateMachineService.send({
706
+ type: _stateMachine.TaskEvent.EXIT_CONFERENCE,
707
+ agentId: this.data.agentId
708
+ });
709
+ }
710
+ const requestInteractionId = this.data.interaction?.mainInteractionId || this.data.interactionId;
711
+ try {
712
+ _loggerProxy.default.info(`Exiting conference`, {
713
+ module: _constants.CC_FILE,
714
+ method: 'exitConference',
715
+ interactionId: requestInteractionId
716
+ });
717
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_EXIT_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_EXIT_FAILED]);
718
+ const response = await this.contact.exitConference({
719
+ interactionId: requestInteractionId
720
+ });
721
+
722
+ // Send success event to transition state
723
+ if (this.stateMachineService) {
724
+ this.stateMachineService.send({
725
+ type: _stateMachine.TaskEvent.EXIT_CONFERENCE_SUCCESS,
726
+ taskData: response.data
727
+ });
728
+ }
729
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_EXIT_SUCCESS, {
730
+ taskId: this.data.interactionId,
731
+ requestInteractionId,
732
+ agentId: this.data.agentId,
733
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response)
734
+ }, ['operational', 'behavioral', 'business']);
735
+ _loggerProxy.default.log(`Successfully exited conference`, {
736
+ module: _constants.CC_FILE,
737
+ method: 'exitConference',
738
+ trackingId: response.trackingId,
739
+ interactionId: requestInteractionId
740
+ });
741
+ return response;
742
+ } catch (error) {
743
+ // Send failure event
744
+ if (this.stateMachineService) {
745
+ this.stateMachineService.send({
746
+ type: _stateMachine.TaskEvent.EXIT_CONFERENCE_FAILED,
747
+ reason: error.toString()
748
+ });
749
+ }
750
+ const {
751
+ error: detailedError
752
+ } = (0, _Utils.getErrorDetails)(error, 'exitConference', _constants.CC_FILE);
753
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_EXIT_FAILED, {
754
+ taskId: this.data.interactionId,
755
+ requestInteractionId,
756
+ agentId: this.data.agentId,
757
+ error: error.toString(),
758
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
759
+ }, ['operational', 'behavioral', 'business']);
760
+ _loggerProxy.default.error(`Failed to exit conference`, {
761
+ module: _constants.CC_FILE,
762
+ method: 'exitConference',
763
+ interactionId: requestInteractionId
764
+ });
765
+ throw detailedError;
766
+ }
767
+ }
768
+
769
+ /**
770
+ * Transfer the conference to another participant.
771
+ * Per conference-spec.md: Only primary agent can transfer conference.
772
+ * After transfer, the transferring agent exits to wrapup.
773
+ *
774
+ * @returns Promise<TaskResponse>
775
+ * @throws Error if not in conference or transfer fails
776
+ * @example
777
+ * ```typescript
778
+ * task.transferConference().then(() => {}).catch(() => {});
779
+ * ```
780
+ */
781
+ async transferConference() {
782
+ // Validate we're in conference or consulting state
783
+ // CONSULTING is allowed because agent can transfer conference while consulting
784
+ // (transfers ownership to the consulted agent)
785
+ const state = this.getStateMachineSnapshot();
786
+ const isValidState = state && (state.matches(_stateMachine.TaskState.CONFERENCING) || state.matches(_stateMachine.TaskState.CONSULTING));
787
+ if (!isValidState) {
788
+ const currentState = state?.value;
789
+ const error = new Error(`Cannot transfer conference in ${currentState} state`);
790
+ _loggerProxy.default.error('Transfer conference operation not allowed', {
791
+ module: _constants.CC_FILE,
792
+ method: 'transferConference',
793
+ interactionId: this.data.interactionId
794
+ });
795
+ throw error;
796
+ }
797
+
798
+ // Send state machine event
799
+ if (this.stateMachineService) {
800
+ this.stateMachineService.send({
801
+ type: _stateMachine.TaskEvent.TRANSFER_CONFERENCE,
802
+ agentId: this.data.agentId
803
+ });
804
+ }
805
+ const requestInteractionId = this.data.interaction?.mainInteractionId || this.data.interactionId;
806
+ try {
807
+ _loggerProxy.default.info(`Transferring conference`, {
808
+ module: _constants.CC_FILE,
809
+ method: 'transferConference',
810
+ interactionId: requestInteractionId
811
+ });
812
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_FAILED]);
813
+ const response = await this.contact.conferenceTransfer({
814
+ interactionId: requestInteractionId
815
+ });
816
+
817
+ // Send success event to transition state
818
+ if (this.stateMachineService) {
819
+ this.stateMachineService.send({
820
+ type: _stateMachine.TaskEvent.TRANSFER_CONFERENCE_SUCCESS,
821
+ taskData: response.data
822
+ });
823
+ }
824
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_SUCCESS, {
825
+ taskId: this.data.interactionId,
826
+ requestInteractionId,
827
+ agentId: this.data.agentId,
828
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response)
829
+ }, ['operational', 'behavioral', 'business']);
830
+ _loggerProxy.default.log(`Successfully transferred conference`, {
831
+ module: _constants.CC_FILE,
832
+ method: 'transferConference',
833
+ trackingId: response.trackingId,
834
+ interactionId: requestInteractionId
835
+ });
836
+ return response;
837
+ } catch (error) {
838
+ // Send failure event
839
+ if (this.stateMachineService) {
840
+ this.stateMachineService.send({
841
+ type: _stateMachine.TaskEvent.TRANSFER_CONFERENCE_FAILED,
842
+ reason: error.toString()
843
+ });
844
+ }
845
+ const {
846
+ error: detailedError
847
+ } = (0, _Utils.getErrorDetails)(error, 'transferConference', _constants.CC_FILE);
848
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_FAILED, {
849
+ taskId: this.data.interactionId,
850
+ requestInteractionId,
851
+ agentId: this.data.agentId,
852
+ error: error.toString(),
853
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
854
+ }, ['operational', 'behavioral', 'business']);
855
+ _loggerProxy.default.error(`Failed to transfer conference`, {
856
+ module: _constants.CC_FILE,
857
+ method: 'transferConference',
858
+ interactionId: requestInteractionId
859
+ });
860
+ throw detailedError;
861
+ }
862
+ }
863
+
864
+ /**
865
+ * Toggle between consult call and main call during consulting.
866
+ * If on consult leg (consultCallHeld = false), switches to main call by holding consult.
867
+ * If on main call (consultCallHeld = true), switches to consult by resuming consult.
868
+ *
869
+ * @returns Promise<TaskResponse>
870
+ * @throws Error if not in CONSULTING state or no consult media resource
871
+ * @example
872
+ * ```typescript
873
+ * await task.switchCall();
874
+ * ```
875
+ */
876
+ async switchCall() {
877
+ // Validate we're in CONSULTING state
878
+ const state = this.getStateMachineSnapshot();
879
+ if (!state?.matches(_stateMachine.TaskState.CONSULTING)) {
880
+ const currentState = state?.value;
881
+ const error = new Error(`Cannot switch call in ${currentState} state`);
882
+ _loggerProxy.default.error('Switch call operation not allowed', {
883
+ module: _constants.CC_FILE,
884
+ method: 'switchCall',
885
+ interactionId: this.data.interactionId
886
+ });
887
+ throw error;
888
+ }
889
+
890
+ // Validate we have a consult media resource
891
+ const consultMediaResourceId = (0, _TaskUtils.getConsultMediaResourceId)(this.data.interaction, this.data.consultMediaResourceId, this.data.agentId);
892
+ if (!consultMediaResourceId) {
893
+ const error = new Error('No consult media resource available');
894
+ _loggerProxy.default.error('Switch call failed - no consult media resource', {
895
+ module: _constants.CC_FILE,
896
+ method: 'switchCall',
897
+ interactionId: this.data.interactionId
898
+ });
899
+ throw error;
900
+ }
901
+ const context = state.context;
902
+ const isOnConsultLeg = !context.consultCallHeld;
903
+
904
+ // Determine direction and send appropriate state machine event
905
+ const targetEvent = isOnConsultLeg ? _stateMachine.TaskEvent.SWITCH_TO_MAIN_CALL : _stateMachine.TaskEvent.SWITCH_TO_CONSULT;
906
+ const revertEvent = isOnConsultLeg ? _stateMachine.TaskEvent.SWITCH_TO_CONSULT : _stateMachine.TaskEvent.SWITCH_TO_MAIN_CALL;
907
+ if (this.stateMachineService) {
908
+ this.stateMachineService.send({
909
+ type: targetEvent
910
+ });
911
+ }
912
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_SWITCH_CALL_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_SWITCH_CALL_FAILED]);
913
+ try {
914
+ if (isOnConsultLeg) {
915
+ const response = await this.contact.unHold({
916
+ interactionId: this.data.interactionId,
917
+ data: {
918
+ mediaResourceId: this.data.mediaResourceId
919
+ }
920
+ });
921
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_SWITCH_CALL_SUCCESS, {
922
+ taskId: this.data.interactionId,
923
+ direction: 'toMainCall',
924
+ mediaResourceId: consultMediaResourceId,
925
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response)
926
+ }, ['operational', 'behavioral']);
927
+ _loggerProxy.default.log(`Switched to main call successfully`, {
928
+ module: _constants.CC_FILE,
929
+ method: 'switchCall',
930
+ trackingId: response.trackingId,
931
+ interactionId: this.data.interactionId
932
+ });
933
+ return response;
934
+ }
935
+ const response = await this.contact.hold({
936
+ interactionId: this.data.interactionId,
937
+ data: {
938
+ mediaResourceId: this.data.mediaResourceId
939
+ }
940
+ });
941
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_SWITCH_CALL_SUCCESS, {
942
+ taskId: this.data.interactionId,
943
+ direction: 'toConsultCall',
944
+ mediaResourceId: consultMediaResourceId,
945
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response)
946
+ }, ['operational', 'behavioral']);
947
+ _loggerProxy.default.log(`Switched to consult call successfully`, {
948
+ module: _constants.CC_FILE,
949
+ method: 'switchCall',
950
+ trackingId: response.trackingId,
951
+ interactionId: this.data.interactionId
952
+ });
953
+ return response;
954
+ } catch (error) {
955
+ if (this.stateMachineService) {
956
+ this.stateMachineService.send({
957
+ type: revertEvent
958
+ });
959
+ }
960
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_SWITCH_CALL_FAILED, {
961
+ taskId: this.data.interactionId,
962
+ direction: isOnConsultLeg ? 'toMainCall' : 'toConsultCall',
963
+ mediaResourceId: consultMediaResourceId,
964
+ error: error.toString(),
965
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
966
+ }, ['operational', 'behavioral']);
967
+ const {
968
+ error: detailedError
969
+ } = (0, _Utils.getErrorDetails)(error, 'switchCall', _constants.CC_FILE);
970
+ _loggerProxy.default.error(`Failed to switch call`, {
971
+ module: _constants.CC_FILE,
972
+ method: 'switchCall',
973
+ interactionId: this.data.interactionId
974
+ });
975
+ throw detailedError;
976
+ }
977
+ }
978
+ getChannelSpecificActionOverrides() {
979
+ const baseOverrides = super.getChannelSpecificActionOverrides();
980
+ return {
981
+ ...baseOverrides,
982
+ emitTaskHold: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_HOLD, {
983
+ updateTaskData: true
984
+ }),
985
+ emitTaskResume: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_RESUME, {
986
+ updateTaskData: true
987
+ }),
988
+ emitTaskRecordingStarted: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_RECORDING_STARTED, {
989
+ updateTaskData: true
990
+ }),
991
+ emitTaskRecordingPaused: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_RECORDING_PAUSED, {
992
+ updateTaskData: true
993
+ }),
994
+ emitTaskRecordingPauseFailed: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_RECORDING_PAUSE_FAILED, {
995
+ updateTaskData: true
996
+ }),
997
+ emitTaskRecordingResumed: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_RECORDING_RESUMED, {
998
+ updateTaskData: true
999
+ }),
1000
+ emitTaskRecordingResumeFailed: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_RECORDING_RESUME_FAILED, {
1001
+ updateTaskData: true
1002
+ }),
1003
+ // Conference event emitters
1004
+ emitTaskParticipantJoined: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_PARTICIPANT_JOINED, {
1005
+ updateTaskData: true
1006
+ }),
1007
+ emitTaskParticipantLeft: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_PARTICIPANT_LEFT, {
1008
+ updateTaskData: true
1009
+ }),
1010
+ emitTaskConferenceStarted: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_CONFERENCE_STARTED, {
1011
+ updateTaskData: true
1012
+ }),
1013
+ emitTaskConferenceEnded: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_CONFERENCE_ENDED, {
1014
+ updateTaskData: true
1015
+ }),
1016
+ emitTaskConferenceFailed: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_CONFERENCE_FAILED, {
1017
+ updateTaskData: true
1018
+ }),
1019
+ emitTaskExitConference: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_EXIT_CONFERENCE, {
1020
+ updateTaskData: false
1021
+ }),
1022
+ emitTaskTransferConference: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_TRANSFER_CONFERENCE, {
1023
+ updateTaskData: false
1024
+ }),
1025
+ emitTaskSwitchCall: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_SWITCH_CALL, {
1026
+ updateTaskData: false
1027
+ }),
1028
+ emitTaskTransferConferenceFailed: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_CONFERENCE_TRANSFER_FAILED, {
1029
+ updateTaskData: true
1030
+ }),
1031
+ emitTaskOutdialFailed: ({
1032
+ event
1033
+ }) => {
1034
+ if (event && 'taskData' in event && event.taskData) {
1035
+ this.updateTaskData(event.taskData);
1036
+ }
1037
+ const reason = event?.reason || 'Outdial failed';
1038
+ this.emit(_types.TASK_EVENTS.TASK_OUTDIAL_FAILED, reason);
1039
+ }
1040
+ };
1041
+ }
1042
+ }
1043
+ exports.default = Voice;
1044
+ //# sourceMappingURL=Voice.js.map