@webex/contact-center 3.12.0-next.13 → 3.12.0-next.130

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 (312) hide show
  1. package/.sdd/manifest.json +884 -0
  2. package/AGENTS.md +94 -0
  3. package/ai-docs/ARCHITECTURE.md +168 -0
  4. package/ai-docs/CONTRACTS.md +53 -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 +52 -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 +382 -0
  17. package/ai-docs/features/consult-transfer-list-policy/spec/feature-spec.md +362 -0
  18. package/ai-docs/features/contact-owner-change-refactor.md +78 -0
  19. package/ai-docs/features/generated-spec-conformance-fidelity-remediation/spec/feature-spec.md +117 -0
  20. package/ai-docs/features/residual-warning-coverage-completion/spec/feature-spec.md +203 -0
  21. package/ai-docs/features/validator-code-fidelity-drift-fix/spec/feature-spec.md +315 -0
  22. package/ai-docs/patterns/event-driven-patterns.md +485 -0
  23. package/ai-docs/patterns/testing-patterns.md +480 -0
  24. package/ai-docs/patterns/typescript-patterns.md +365 -0
  25. package/ai-docs/templates/README.md +102 -0
  26. package/ai-docs/templates/documentation/create-agents-md.md +240 -0
  27. package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
  28. package/ai-docs/templates/existing-service/bug-fix.md +254 -0
  29. package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
  30. package/ai-docs/templates/new-method/00-master.md +80 -0
  31. package/ai-docs/templates/new-method/01-requirements.md +232 -0
  32. package/ai-docs/templates/new-method/02-implementation.md +295 -0
  33. package/ai-docs/templates/new-method/03-tests.md +201 -0
  34. package/ai-docs/templates/new-method/04-validation.md +141 -0
  35. package/ai-docs/templates/new-service/00-master.md +109 -0
  36. package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
  37. package/ai-docs/templates/new-service/02-code-generation.md +346 -0
  38. package/ai-docs/templates/new-service/03-integration.md +178 -0
  39. package/ai-docs/templates/new-service/04-test-generation.md +205 -0
  40. package/ai-docs/templates/new-service/05-validation.md +145 -0
  41. package/dist/cc.js +971 -42
  42. package/dist/cc.js.map +1 -1
  43. package/dist/config.js +13 -0
  44. package/dist/config.js.map +1 -1
  45. package/dist/constants.js +31 -3
  46. package/dist/constants.js.map +1 -1
  47. package/dist/index.js +45 -15
  48. package/dist/index.js.map +1 -1
  49. package/dist/metrics/MetricsManager.js +16 -0
  50. package/dist/metrics/MetricsManager.js.map +1 -1
  51. package/dist/metrics/behavioral-events.js +205 -0
  52. package/dist/metrics/behavioral-events.js.map +1 -1
  53. package/dist/metrics/constants.js +48 -4
  54. package/dist/metrics/constants.js.map +1 -1
  55. package/dist/services/AddressBook.js +18 -15
  56. package/dist/services/AddressBook.js.map +1 -1
  57. package/dist/services/AnswerCallOnWebexService.js +202 -0
  58. package/dist/services/AnswerCallOnWebexService.js.map +1 -0
  59. package/dist/services/ApiAiAssistant.js +269 -9
  60. package/dist/services/ApiAiAssistant.js.map +1 -1
  61. package/dist/services/EntryPoint.js +46 -68
  62. package/dist/services/EntryPoint.js.map +1 -1
  63. package/dist/services/Queue.js +27 -22
  64. package/dist/services/Queue.js.map +1 -1
  65. package/dist/services/UserPreference.js +427 -0
  66. package/dist/services/UserPreference.js.map +1 -0
  67. package/dist/services/WebexCrossClientService.js +222 -0
  68. package/dist/services/WebexCrossClientService.js.map +1 -0
  69. package/dist/services/WxAppTelephonyMercurySync.js +108 -0
  70. package/dist/services/WxAppTelephonyMercurySync.js.map +1 -0
  71. package/dist/services/config/Util.js +11 -4
  72. package/dist/services/config/Util.js.map +1 -1
  73. package/dist/services/config/constants.js +41 -7
  74. package/dist/services/config/constants.js.map +1 -1
  75. package/dist/services/config/index.js +51 -0
  76. package/dist/services/config/index.js.map +1 -1
  77. package/dist/services/config/types.js +57 -12
  78. package/dist/services/config/types.js.map +1 -1
  79. package/dist/services/core/Err.js.map +1 -1
  80. package/dist/services/core/Utils.js +111 -32
  81. package/dist/services/core/Utils.js.map +1 -1
  82. package/dist/services/core/WebexRequest.js +6 -2
  83. package/dist/services/core/WebexRequest.js.map +1 -1
  84. package/dist/services/core/aqm-reqs.js +28 -14
  85. package/dist/services/core/aqm-reqs.js.map +1 -1
  86. package/dist/services/core/types.js.map +1 -1
  87. package/dist/services/core/websocket/WebSocketManager.js +2 -1
  88. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  89. package/dist/services/core/websocket/types.js.map +1 -1
  90. package/dist/services/index.js +1 -1
  91. package/dist/services/index.js.map +1 -1
  92. package/dist/services/task/Task.js +754 -0
  93. package/dist/services/task/Task.js.map +1 -0
  94. package/dist/services/task/TaskFactory.js +49 -0
  95. package/dist/services/task/TaskFactory.js.map +1 -0
  96. package/dist/services/task/TaskManager.js +1172 -521
  97. package/dist/services/task/TaskManager.js.map +1 -1
  98. package/dist/services/task/TaskUtils.js +211 -27
  99. package/dist/services/task/TaskUtils.js.map +1 -1
  100. package/dist/services/task/WebexCallingUtils.js +70 -0
  101. package/dist/services/task/WebexCallingUtils.js.map +1 -0
  102. package/dist/services/task/constants.js +12 -2
  103. package/dist/services/task/constants.js.map +1 -1
  104. package/dist/services/task/contact.js +29 -0
  105. package/dist/services/task/contact.js.map +1 -1
  106. package/dist/services/task/dialer.js +78 -0
  107. package/dist/services/task/dialer.js.map +1 -1
  108. package/dist/services/task/digital/Digital.js +78 -0
  109. package/dist/services/task/digital/Digital.js.map +1 -0
  110. package/dist/services/task/state-machine/TaskStateMachine.js +982 -0
  111. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  112. package/dist/services/task/state-machine/actions.js +572 -0
  113. package/dist/services/task/state-machine/actions.js.map +1 -0
  114. package/dist/services/task/state-machine/constants.js +161 -0
  115. package/dist/services/task/state-machine/constants.js.map +1 -0
  116. package/dist/services/task/state-machine/guards.js +379 -0
  117. package/dist/services/task/state-machine/guards.js.map +1 -0
  118. package/dist/services/task/state-machine/index.js +53 -0
  119. package/dist/services/task/state-machine/index.js.map +1 -0
  120. package/dist/services/task/state-machine/types.js +54 -0
  121. package/dist/services/task/state-machine/types.js.map +1 -0
  122. package/dist/services/task/state-machine/uiControlsComputer.js +703 -0
  123. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  124. package/dist/services/task/taskDataNormalizer.js +99 -0
  125. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  126. package/dist/services/task/types.js +228 -5
  127. package/dist/services/task/types.js.map +1 -1
  128. package/dist/services/task/voice/Voice.js +1485 -0
  129. package/dist/services/task/voice/Voice.js.map +1 -0
  130. package/dist/services/task/voice/WebRTC.js +152 -0
  131. package/dist/services/task/voice/WebRTC.js.map +1 -0
  132. package/dist/services/task/voice/wxAppVoiceMethods.js +354 -0
  133. package/dist/services/task/voice/wxAppVoiceMethods.js.map +1 -0
  134. package/dist/services/wxAppDiagnosticLogging.js +122 -0
  135. package/dist/services/wxAppDiagnosticLogging.js.map +1 -0
  136. package/dist/services/wxAppTelephonyUtils.js +19 -0
  137. package/dist/services/wxAppTelephonyUtils.js.map +1 -0
  138. package/dist/types/cc.d.ts +193 -6
  139. package/dist/types/config.d.ts +13 -0
  140. package/dist/types/constants.d.ts +29 -1
  141. package/dist/types/index.d.ts +35 -8
  142. package/dist/types/metrics/MetricsManager.d.ts +6 -0
  143. package/dist/types/metrics/constants.d.ts +43 -1
  144. package/dist/types/services/AddressBook.d.ts +2 -1
  145. package/dist/types/services/AnswerCallOnWebexService.d.ts +38 -0
  146. package/dist/types/services/ApiAiAssistant.d.ts +56 -5
  147. package/dist/types/services/EntryPoint.d.ts +7 -5
  148. package/dist/types/services/Queue.d.ts +5 -3
  149. package/dist/types/services/UserPreference.d.ts +118 -0
  150. package/dist/types/services/WebexCrossClientService.d.ts +32 -0
  151. package/dist/types/services/WxAppTelephonyMercurySync.d.ts +28 -0
  152. package/dist/types/services/config/constants.d.ts +34 -5
  153. package/dist/types/services/config/types.d.ts +197 -12
  154. package/dist/types/services/core/Err.d.ts +6 -0
  155. package/dist/types/services/core/Utils.d.ts +35 -13
  156. package/dist/types/services/core/WebexRequest.d.ts +1 -0
  157. package/dist/types/services/core/types.d.ts +2 -0
  158. package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -0
  159. package/dist/types/services/core/websocket/types.d.ts +1 -1
  160. package/dist/types/services/index.d.ts +1 -1
  161. package/dist/types/services/task/Task.d.ts +175 -0
  162. package/dist/types/services/task/TaskFactory.d.ts +13 -0
  163. package/dist/types/services/task/TaskUtils.d.ts +48 -2
  164. package/dist/types/services/task/WebexCallingUtils.d.ts +11 -0
  165. package/dist/types/services/task/constants.d.ts +10 -0
  166. package/dist/types/services/task/contact.d.ts +4 -0
  167. package/dist/types/services/task/dialer.d.ts +30 -0
  168. package/dist/types/services/task/digital/Digital.d.ts +22 -0
  169. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1442 -0
  170. package/dist/types/services/task/state-machine/actions.d.ts +10 -0
  171. package/dist/types/services/task/state-machine/constants.d.ts +107 -0
  172. package/dist/types/services/task/state-machine/guards.d.ts +103 -0
  173. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  174. package/dist/types/services/task/state-machine/types.d.ts +277 -0
  175. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  176. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  177. package/dist/types/services/task/types.d.ts +680 -66
  178. package/dist/types/services/task/voice/Voice.d.ts +227 -0
  179. package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
  180. package/dist/types/services/task/voice/wxAppVoiceMethods.d.ts +55 -0
  181. package/dist/types/services/wxAppDiagnosticLogging.d.ts +51 -0
  182. package/dist/types/services/wxAppTelephonyUtils.d.ts +5 -0
  183. package/dist/types/types.d.ts +205 -9
  184. package/dist/types/utils/PageCache.d.ts +20 -3
  185. package/dist/types/webex.d.ts +1 -0
  186. package/dist/types.js +162 -4
  187. package/dist/types.js.map +1 -1
  188. package/dist/utils/PageCache.js +19 -5
  189. package/dist/utils/PageCache.js.map +1 -1
  190. package/dist/webex.js +22 -2
  191. package/dist/webex.js.map +1 -1
  192. package/package.json +16 -12
  193. package/src/cc.ts +1289 -112
  194. package/src/config.ts +13 -0
  195. package/src/constants.ts +29 -1
  196. package/src/index.ts +36 -5
  197. package/src/metrics/MetricsManager.ts +16 -0
  198. package/src/metrics/ai-docs/AGENTS.md +350 -0
  199. package/src/metrics/ai-docs/ARCHITECTURE.md +338 -0
  200. package/src/metrics/ai-docs/metrics-spec.md +921 -0
  201. package/src/metrics/behavioral-events.ts +212 -0
  202. package/src/metrics/constants.ts +52 -4
  203. package/src/services/AddressBook.ts +17 -6
  204. package/src/services/AnswerCallOnWebexService.ts +239 -0
  205. package/src/services/ApiAiAssistant.ts +356 -9
  206. package/src/services/EntryPoint.ts +59 -60
  207. package/src/services/Queue.ts +29 -12
  208. package/src/services/UserPreference.ts +509 -0
  209. package/src/services/WebexCrossClientService.ts +293 -0
  210. package/src/services/WxAppTelephonyMercurySync.ts +142 -0
  211. package/src/services/agent/ai-docs/AGENTS.md +240 -0
  212. package/src/services/agent/ai-docs/ARCHITECTURE.md +304 -0
  213. package/src/services/agent/ai-docs/agent-spec.md +505 -0
  214. package/src/services/ai-docs/AGENTS.md +386 -0
  215. package/src/services/ai-docs/services-spec.md +505 -0
  216. package/src/services/config/Util.ts +12 -3
  217. package/src/services/config/ai-docs/AGENTS.md +255 -0
  218. package/src/services/config/ai-docs/ARCHITECTURE.md +426 -0
  219. package/src/services/config/ai-docs/config-spec.md +686 -0
  220. package/src/services/config/constants.ts +46 -6
  221. package/src/services/config/index.ts +65 -0
  222. package/src/services/config/types.ts +197 -13
  223. package/src/services/core/Err.ts +3 -0
  224. package/src/services/core/Utils.ts +127 -37
  225. package/src/services/core/WebexRequest.ts +3 -1
  226. package/src/services/core/ai-docs/AGENTS.md +381 -0
  227. package/src/services/core/ai-docs/ARCHITECTURE.md +698 -0
  228. package/src/services/core/ai-docs/core-spec.md +787 -0
  229. package/src/services/core/aqm-reqs.ts +30 -15
  230. package/src/services/core/types.ts +2 -0
  231. package/src/services/core/websocket/WebSocketManager.ts +2 -0
  232. package/src/services/core/websocket/types.ts +1 -1
  233. package/src/services/index.ts +1 -1
  234. package/src/services/task/Task.ts +908 -0
  235. package/src/services/task/TaskFactory.ts +60 -0
  236. package/src/services/task/TaskManager.ts +1419 -608
  237. package/src/services/task/TaskUtils.ts +303 -25
  238. package/src/services/task/WebexCallingUtils.ts +136 -0
  239. package/src/services/task/ai-docs/AGENTS.md +457 -0
  240. package/src/services/task/ai-docs/ARCHITECTURE.md +595 -0
  241. package/src/services/task/ai-docs/task-spec.md +1474 -0
  242. package/src/services/task/constants.ts +10 -0
  243. package/src/services/task/contact.ts +30 -0
  244. package/src/services/task/dialer.ts +80 -0
  245. package/src/services/task/digital/Digital.ts +97 -0
  246. package/src/services/task/state-machine/TaskStateMachine.ts +1320 -0
  247. package/src/services/task/state-machine/actions.ts +741 -0
  248. package/src/services/task/state-machine/ai-docs/AGENTS.md +460 -0
  249. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1156 -0
  250. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2247 -0
  251. package/src/services/task/state-machine/constants.ts +172 -0
  252. package/src/services/task/state-machine/guards.ts +442 -0
  253. package/src/services/task/state-machine/index.ts +28 -0
  254. package/src/services/task/state-machine/types.ts +258 -0
  255. package/src/services/task/state-machine/uiControlsComputer.ts +1135 -0
  256. package/src/services/task/taskDataNormalizer.ts +137 -0
  257. package/src/services/task/types.ts +819 -70
  258. package/src/services/task/voice/Voice.ts +1851 -0
  259. package/src/services/task/voice/WebRTC.ts +191 -0
  260. package/src/services/task/voice/wxAppVoiceMethods.ts +516 -0
  261. package/src/services/wxAppDiagnosticLogging.ts +174 -0
  262. package/src/services/wxAppTelephonyUtils.ts +14 -0
  263. package/src/types.ts +262 -11
  264. package/src/utils/AGENTS.md +289 -0
  265. package/src/utils/PageCache.ts +38 -5
  266. package/src/utils/ai-docs/utils-spec.md +391 -0
  267. package/src/webex.js +10 -0
  268. package/test/unit/spec/cc.ts +2210 -119
  269. package/test/unit/spec/logger-proxy.ts +70 -0
  270. package/test/unit/spec/metrics/MetricsManager.ts +25 -0
  271. package/test/unit/spec/metrics/behavioral-events.ts +60 -0
  272. package/test/unit/spec/services/AddressBook.ts +37 -6
  273. package/test/unit/spec/services/AnswerCallOnWebexService.ts +264 -0
  274. package/test/unit/spec/services/ApiAiAssistant.ts +311 -21
  275. package/test/unit/spec/services/EntryPoint.ts +87 -40
  276. package/test/unit/spec/services/Queue.ts +123 -12
  277. package/test/unit/spec/services/UserPreference.ts +401 -0
  278. package/test/unit/spec/services/WebCallingService.ts +7 -1
  279. package/test/unit/spec/services/WebexCrossClientService.ts +467 -0
  280. package/test/unit/spec/services/WxAppTelephonyMercurySync.ts +136 -0
  281. package/test/unit/spec/services/config/Util.ts +129 -0
  282. package/test/unit/spec/services/config/index.ts +97 -29
  283. package/test/unit/spec/services/core/Utils.ts +425 -8
  284. package/test/unit/spec/services/core/WebexRequest.ts +3 -1
  285. package/test/unit/spec/services/core/aqm-reqs.ts +113 -1
  286. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +66 -40
  287. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  288. package/test/unit/spec/services/task/Task.ts +745 -0
  289. package/test/unit/spec/services/task/TaskFactory.ts +99 -0
  290. package/test/unit/spec/services/task/TaskManager.ts +2518 -1494
  291. package/test/unit/spec/services/task/TaskUtils.ts +235 -0
  292. package/test/unit/spec/services/task/WebexCallingUtils.ts +153 -0
  293. package/test/unit/spec/services/task/contact.ts +33 -0
  294. package/test/unit/spec/services/task/dialer.ts +190 -0
  295. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  296. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +4060 -0
  297. package/test/unit/spec/services/task/state-machine/guards.ts +839 -0
  298. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  299. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +3101 -0
  300. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  301. package/test/unit/spec/services/task/voice/Voice.ts +2061 -0
  302. package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
  303. package/test/unit/spec/services/task/voice/wxAppVoiceMethods.ts +603 -0
  304. package/test/unit/spec/services/wxAppDiagnosticLogging.ts +143 -0
  305. package/test/unit/spec/wellnessSampleUtils.ts +288 -0
  306. package/umd/contact-center.min.js +2 -2
  307. package/umd/contact-center.min.js.map +1 -1
  308. package/dist/services/task/index.js +0 -1525
  309. package/dist/services/task/index.js.map +0 -1
  310. package/dist/types/services/task/index.d.ts +0 -650
  311. package/src/services/task/index.ts +0 -1801
  312. package/test/unit/spec/services/task/index.ts +0 -2184
@@ -0,0 +1,1485 @@
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 _guards = require("../state-machine/guards");
16
+ var _TaskUtils = require("../TaskUtils");
17
+ var _wxAppTelephonyUtils = require("../../wxAppTelephonyUtils");
18
+ var _wxAppVoiceMethods = require("./wxAppVoiceMethods");
19
+ var _wxAppDiagnosticLogging = require("../../wxAppDiagnosticLogging");
20
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
21
+ const hasNonemptyPendingWrapUp = taskData => Array.isArray(taskData?.agentsPendingWrapUp) && taskData.agentsPendingWrapUp.length > 0;
22
+
23
+ /**
24
+ * Same consulted-non-owner rule as TaskManager leave stamp. Used only for the
25
+ * HTTP exitConference self-exit fallback (shouldWrapUpForThisAgent stays unchanged).
26
+ */
27
+ const isConsultedNonOwner = (taskData, fallback) => {
28
+ const isConsulted = taskData.isConsulted ?? fallback?.isConsulted;
29
+ if (isConsulted !== true) {
30
+ return false;
31
+ }
32
+ const owner = taskData.interaction?.owner ?? fallback?.interaction?.owner;
33
+ const selfAgentId = taskData.agentId ?? fallback?.agentId;
34
+ return Boolean(selfAgentId && owner && owner !== selfAgentId);
35
+ };
36
+ class Voice extends _Task.default {
37
+ static WXAPP_MUTE_SYNC_RETRY_DELAY_MS = 50;
38
+ static WXAPP_MUTE_SYNC_MAX_RETRIES = 3;
39
+ enableWxBetterTogether = false;
40
+ wxAppMuted = false;
41
+ wxAppAnswerPending = false;
42
+ wxAppAcceptInFlight = false;
43
+ wxAppMuteToggleTail = Promise.resolve();
44
+ wxAppDtmfTail = Promise.resolve();
45
+ constructor(contact, data, callOptions, wrapupData, agentId) {
46
+ const resolvedOptions = {
47
+ isEndTaskEnabled: callOptions?.isEndTaskEnabled ?? true,
48
+ isEndConsultEnabled: callOptions?.isEndConsultEnabled ?? true,
49
+ voiceVariant: callOptions?.voiceVariant ?? _types.VOICE_VARIANT.PSTN,
50
+ isRecordingEnabled: callOptions?.isRecordingEnabled ?? true,
51
+ enableWxBetterTogether: callOptions?.enableWxBetterTogether ?? false
52
+ };
53
+ super(contact, data, {
54
+ ...resolvedOptions,
55
+ consultTransferConfig: callOptions?.consultTransferConfig
56
+ }, wrapupData, agentId);
57
+ this.enableWxBetterTogether = resolvedOptions.enableWxBetterTogether;
58
+ this.answerCallOnWebexService = callOptions?.answerCallOnWebexService;
59
+ }
60
+ getWxAppVoiceDependencies() {
61
+ return {
62
+ enableWxBetterTogether: this.enableWxBetterTogether,
63
+ answerCallOnWebexService: this.answerCallOnWebexService,
64
+ agentId: this.agentId,
65
+ metricsManager: this.metricsManager,
66
+ getTaskData: () => this.data,
67
+ getTaskState: () => this.stateMachineService?.getSnapshot?.()?.value,
68
+ getWxAppMuted: () => this.wxAppMuted,
69
+ setWxAppMuted: muted => {
70
+ this.wxAppMuted = muted;
71
+ }
72
+ };
73
+ }
74
+
75
+ /** @deprecated Use {@link Voice.getWxAppVoiceDependencies} */
76
+ getWxAppVoiceDeps() {
77
+ return this.getWxAppVoiceDependencies();
78
+ }
79
+ isWebexAppCallingOffer() {
80
+ return (0, _wxAppVoiceMethods.isWebexAppCallingOffer)(this.getWxAppVoiceDeps());
81
+ }
82
+ isWebexAppInboundCallingOffer() {
83
+ return (0, _wxAppVoiceMethods.isWebexAppInboundCallingOffer)(this.getWxAppVoiceDeps());
84
+ }
85
+ setWxAppAnswerPending(pending) {
86
+ this.wxAppAnswerPending = pending;
87
+ this.uiControlConfig = {
88
+ ...this.uiControlConfig,
89
+ wxAppAnswerPending: pending
90
+ };
91
+ this.updateUiControls(true);
92
+ }
93
+ setWxAppAcceptInFlight(inFlight) {
94
+ this.wxAppAcceptInFlight = inFlight;
95
+ this.uiControlConfig = {
96
+ ...this.uiControlConfig,
97
+ wxAppAcceptInFlight: inFlight
98
+ };
99
+ this.updateUiControls(true);
100
+ }
101
+ setEnableWxBetterTogether(enabled) {
102
+ this.enableWxBetterTogether = enabled;
103
+ this.uiControlConfig = {
104
+ ...this.uiControlConfig,
105
+ enableWxBetterTogether: enabled
106
+ };
107
+ this.updateUiControls(true);
108
+ }
109
+ updateUiControls(forceEmit = false) {
110
+ super.updateUiControls(forceEmit);
111
+ this.logWxAppOfferDecisionIfNeeded();
112
+ }
113
+ logWxAppOfferDecisionIfNeeded() {
114
+ if (!this.enableWxBetterTogether) {
115
+ return;
116
+ }
117
+ const state = this.stateMachineService?.getSnapshot?.()?.value;
118
+ if (state !== _stateMachine.TaskState.OFFERED) {
119
+ return;
120
+ }
121
+ const accept = this.currentUiControls?.main?.accept;
122
+ if (!accept?.isVisible) {
123
+ return;
124
+ }
125
+ const deps = this.getWxAppVoiceDependencies();
126
+ const isOutdial = this.data?.interaction?.outboundType === 'OUTDIAL';
127
+ const isWxAppInboundOffer = this.isWebexAppInboundCallingOffer();
128
+ const isWxAppOutdialOffer = this.isWebexAppCallingOffer() && isOutdial;
129
+ const deviceDetails = (0, _wxAppVoiceMethods.getCallingDeviceDetails)(deps);
130
+ const acceptReason = (0, _wxAppDiagnosticLogging.deriveWxAppAcceptReason)({
131
+ isWxAppInboundOffer,
132
+ isWxAppOutdialOffer,
133
+ isWebrtc: this.uiControlConfig.voiceVariant === _types.VOICE_VARIANT.WEBRTC,
134
+ isOutdial,
135
+ wxAppAcceptInFlight: this.wxAppAcceptInFlight,
136
+ wxAppAnswerPending: this.wxAppAnswerPending,
137
+ enableWxBetterTogether: this.enableWxBetterTogether,
138
+ hasDeviceCallId: Boolean(deviceDetails?.deviceCallId)
139
+ });
140
+ if (acceptReason === this.lastLoggedWxAppAcceptReason) {
141
+ return;
142
+ }
143
+ this.lastLoggedWxAppAcceptReason = acceptReason;
144
+ (0, _wxAppDiagnosticLogging.logWxAppOfferDecision)({
145
+ interactionId: this.data.interactionId,
146
+ acceptVisible: accept.isVisible,
147
+ acceptEnabled: accept.isEnabled,
148
+ acceptReason,
149
+ wxAppParticipantDeviceType: deviceDetails?.deviceType,
150
+ hasDeviceCallId: Boolean(deviceDetails?.deviceCallId)
151
+ });
152
+ }
153
+ applyWxAppMuteStateFromSync(incomingCallId, muted) {
154
+ if (!this.enableWxBetterTogether) {
155
+ return;
156
+ }
157
+ const activeCallId = this.getWebexCallingCallId();
158
+ const incomingSuffix = (0, _wxAppDiagnosticLogging.callIdSuffix)(incomingCallId);
159
+ const activeSuffix = (0, _wxAppDiagnosticLogging.callIdSuffix)(activeCallId);
160
+ if (!activeCallId || !incomingCallId.endsWith(activeCallId)) {
161
+ (0, _wxAppDiagnosticLogging.logWxAppMercuryMuteSync)({
162
+ phase: 'dropped',
163
+ muted,
164
+ callIdSuffix: incomingSuffix,
165
+ interactionId: this.data.interactionId,
166
+ dropReason: !activeCallId ? 'no_active_wxApp_call' : 'call_id_mismatch'
167
+ });
168
+ return;
169
+ }
170
+ if (this.wxAppMuted === muted) {
171
+ (0, _wxAppDiagnosticLogging.logWxAppMercuryMuteSync)({
172
+ phase: 'dropped',
173
+ muted,
174
+ callIdSuffix: incomingSuffix,
175
+ interactionId: this.data.interactionId,
176
+ dropReason: 'mute_state_unchanged'
177
+ });
178
+ return;
179
+ }
180
+ this.wxAppMuted = muted;
181
+ (0, _wxAppDiagnosticLogging.logWxAppMercuryMuteSync)({
182
+ phase: 'applied',
183
+ muted,
184
+ callIdSuffix: activeSuffix,
185
+ interactionId: this.data.interactionId
186
+ });
187
+ this.emit(_types.TASK_EVENTS.TASK_WXAPP_MUTE_STATE_UPDATED, {
188
+ muted
189
+ });
190
+ }
191
+ getWxAppMuted() {
192
+ return this.wxAppMuted;
193
+ }
194
+ static delay(ms) {
195
+ return new Promise(resolve => {
196
+ setTimeout(resolve, ms);
197
+ });
198
+ }
199
+ canRetryWxAppMuteSync() {
200
+ if (!this.enableWxBetterTogether) {
201
+ return false;
202
+ }
203
+ return Boolean((0, _wxAppVoiceMethods.getCallingDeviceDetails)(this.getWxAppVoiceDeps())?.deviceCallId) && !this.getWebexCallingCallId();
204
+ }
205
+ shouldSkipWxAppMuteSync() {
206
+ if (this.data?.interaction?.isTerminated) {
207
+ return true;
208
+ }
209
+ return this.isWebexAppCallingOffer() && !this.uiControlConfig?.wxAppAnswerPending;
210
+ }
211
+ applyWxAppMuteFromBackfill(callId, muted) {
212
+ if (!this.enableWxBetterTogether || this.wxAppMuted === muted) {
213
+ return;
214
+ }
215
+ const activeCallId = this.getWebexCallingCallId();
216
+ if (activeCallId && !callId.endsWith(activeCallId) && !activeCallId.endsWith(callId)) {
217
+ return;
218
+ }
219
+ this.wxAppMuted = muted;
220
+ this.emit(_types.TASK_EVENTS.TASK_WXAPP_MUTE_STATE_UPDATED, {
221
+ muted
222
+ });
223
+ }
224
+ async syncWxAppMuteFromCallDetails(retryAttempt = 0) {
225
+ if (!this.enableWxBetterTogether || !this.answerCallOnWebexService) {
226
+ return undefined;
227
+ }
228
+ if (retryAttempt === 0 && this.shouldSkipWxAppMuteSync()) {
229
+ return undefined;
230
+ }
231
+ if (retryAttempt === 0 && this.wxAppMuteSyncInFlight) {
232
+ return this.wxAppMuteSyncInFlight;
233
+ }
234
+ const syncPromise = this.performWxAppMuteSync(retryAttempt);
235
+ if (retryAttempt === 0) {
236
+ this.wxAppMuteSyncInFlight = syncPromise;
237
+ syncPromise.finally(() => {
238
+ if (this.wxAppMuteSyncInFlight === syncPromise) {
239
+ this.wxAppMuteSyncInFlight = undefined;
240
+ }
241
+ }).catch(() => undefined);
242
+ return syncPromise;
243
+ }
244
+ return syncPromise;
245
+ }
246
+ async performWxAppMuteSync(retryAttempt) {
247
+ if (this.shouldSkipWxAppMuteSync()) {
248
+ return undefined;
249
+ }
250
+ let callId = this.getWebexCallingCallId();
251
+ if (!callId) {
252
+ if (retryAttempt < Voice.WXAPP_MUTE_SYNC_MAX_RETRIES && this.canRetryWxAppMuteSync()) {
253
+ await Voice.delay(Voice.WXAPP_MUTE_SYNC_RETRY_DELAY_MS);
254
+ return this.performWxAppMuteSync(retryAttempt + 1);
255
+ }
256
+ callId = (0, _wxAppVoiceMethods.getCallingDeviceDetails)(this.getWxAppVoiceDeps())?.deviceCallId ?? null;
257
+ if (!callId) {
258
+ return undefined;
259
+ }
260
+ }
261
+ try {
262
+ const lineOwnerId = (0, _wxAppVoiceMethods.resolveWxAppLineOwnerId)(this.getWxAppVoiceDeps());
263
+ const details = await this.answerCallOnWebexService.getCallDetails({
264
+ callId,
265
+ lineOwnerId
266
+ });
267
+ if (details.muted !== undefined) {
268
+ this.applyWxAppMuteStateFromSync(callId, details.muted);
269
+ if (this.wxAppMuted !== details.muted) {
270
+ this.applyWxAppMuteFromBackfill(callId, details.muted);
271
+ }
272
+ return details.muted;
273
+ }
274
+ return this.wxAppMuted;
275
+ } catch (error) {
276
+ if (!(0, _wxAppTelephonyUtils.isWxAppCallNotFoundError)(error)) {
277
+ _loggerProxy.default.error(`Failed to sync wxApp mute from call details: ${error}`, {
278
+ module: _constants.CC_FILE,
279
+ method: _constants.METHODS.GET_CALL_DETAILS_ON_WEBEX,
280
+ interactionId: this.data?.interactionId
281
+ });
282
+ }
283
+ return undefined;
284
+ }
285
+ }
286
+ onTaskAssigned() {
287
+ this.setWxAppAnswerPending(false);
288
+ queueMicrotask(() => {
289
+ this.syncWxAppMuteFromCallDetails().catch(() => undefined);
290
+ });
291
+ }
292
+ onTaskHydrated() {
293
+ const state = this.stateMachineService?.getSnapshot?.()?.value;
294
+ const answerStillPending = state === _stateMachine.TaskState.OFFERED && (this.uiControlConfig?.wxAppAcceptInFlight || this.uiControlConfig?.wxAppAnswerPending);
295
+ if (!answerStillPending) {
296
+ this.setWxAppAnswerPending(false);
297
+ }
298
+ this.syncWxAppMuteFromCallDetails().catch(() => undefined);
299
+ }
300
+ getCallingDeviceDetails() {
301
+ return (0, _wxAppVoiceMethods.getCallingDeviceDetails)(this.getWxAppVoiceDeps());
302
+ }
303
+ getWebexCallingCallId() {
304
+ return (0, _wxAppVoiceMethods.getWebexCallingCallId)(this.getWxAppVoiceDeps());
305
+ }
306
+ createWxAppLifecycle() {
307
+ return {
308
+ setWxAppAcceptInFlight: inFlight => this.setWxAppAcceptInFlight(inFlight),
309
+ setWxAppAnswerPending: pending => this.setWxAppAnswerPending(pending),
310
+ resetWxAppMuted: () => {
311
+ this.wxAppMuted = false;
312
+ },
313
+ syncWxAppMuteFromCallDetails: () => this.syncWxAppMuteFromCallDetails(),
314
+ mapWxAppVoiceError: (error, method) => (0, _wxAppVoiceMethods.mapWxAppVoiceError)(error, method, _constants.CC_FILE)
315
+ };
316
+ }
317
+ getStateMachineSnapshot() {
318
+ return this.stateMachineService?.getSnapshot?.();
319
+ }
320
+
321
+ /**
322
+ * Accepts the task. Routes to wxApp telephony answer when `enableWxBetterTogether` and wxApp offer.
323
+ */
324
+ async accept() {
325
+ if (this.enableWxBetterTogether && this.isWebexAppCallingOffer()) {
326
+ await (0, _wxAppVoiceMethods.runWxAppAccept)(this.getWxAppVoiceDependencies(), this.createWxAppLifecycle());
327
+ return Promise.resolve();
328
+ }
329
+ return super.unsupportedMethodError(_constants.METHODS.ACCEPT);
330
+ }
331
+
332
+ /**
333
+ * Declines the task. Routes wxApp inbound to telephony reject; wxApp outdial offer to cancelTask.
334
+ */
335
+ async decline() {
336
+ if (this.enableWxBetterTogether && this.isWebexAppInboundCallingOffer()) {
337
+ await (0, _wxAppVoiceMethods.runWxAppReject)(this.getWxAppVoiceDependencies(), this.createWxAppLifecycle());
338
+ return Promise.resolve();
339
+ }
340
+ const isOutdial = this.data?.interaction?.outboundType === 'OUTDIAL';
341
+ if (!this.enableWxBetterTogether || !isOutdial || !this.isWebexAppCallingOffer()) {
342
+ return super.unsupportedMethodError(_constants.METHODS.REJECT);
343
+ }
344
+ const interactionId = this.data.interactionId;
345
+ _loggerProxy.default.log(`Declining wxApp outdial task for taskId:${interactionId}`, {
346
+ module: _constants.CC_FILE,
347
+ method: _constants.METHODS.REJECT,
348
+ interactionId
349
+ });
350
+ try {
351
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_DECLINE_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_DECLINE_FAILED]);
352
+ this.setWxAppAnswerPending(false);
353
+ const response = await (0, _wxAppVoiceMethods.runWxAppOutdialDecline)(this.getWxAppVoiceDependencies(), () => this.contact.cancelTask({
354
+ interactionId
355
+ }));
356
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_DECLINE_SUCCESS, {
357
+ taskId: interactionId,
358
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response)
359
+ }, ['operational', 'behavioral']);
360
+ return response;
361
+ } catch (error) {
362
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_DECLINE_FAILED, {
363
+ taskId: interactionId,
364
+ error: error.toString(),
365
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
366
+ }, ['operational', 'behavioral']);
367
+ const {
368
+ error: detailedError
369
+ } = (0, _Utils.getErrorDetails)(error, _constants.METHODS.REJECT, _constants.CC_FILE);
370
+ throw detailedError;
371
+ }
372
+ }
373
+
374
+ /**
375
+ * Toggles mute for wxApp engaged telephony when `enableWxBetterTogether` is active.
376
+ */
377
+ async toggleMute(options) {
378
+ if (this.enableWxBetterTogether && this.getWebexCallingCallId()) {
379
+ const togglePromise = this.wxAppMuteToggleTail.catch(() => undefined).then(() => (0, _wxAppVoiceMethods.runWxAppToggleMute)(this.getWxAppVoiceDependencies(), this.createWxAppLifecycle(), options));
380
+ this.wxAppMuteToggleTail = togglePromise.catch(() => undefined);
381
+ await togglePromise;
382
+ return;
383
+ }
384
+ super.unsupportedMethodError(_constants.METHODS.TOGGLE_MUTE);
385
+ }
386
+
387
+ /**
388
+ * Sends in-call DTMF for wxApp engaged telephony when `enableWxBetterTogether` is active.
389
+ */
390
+ async transmitDtmf(options) {
391
+ if (this.enableWxBetterTogether && this.getWebexCallingCallId()) {
392
+ const dtmfPromise = this.wxAppDtmfTail.catch(() => undefined).then(() => (0, _wxAppVoiceMethods.runWxAppTransmitDtmf)(this.getWxAppVoiceDependencies(), this.createWxAppLifecycle(), options));
393
+ this.wxAppDtmfTail = dtmfPromise.catch(() => undefined);
394
+ await dtmfPromise;
395
+ return;
396
+ }
397
+ super.unsupportedMethodError(_constants.METHODS.TRANSMIT_DTMF);
398
+ }
399
+
400
+ /**
401
+ * This is used to hold the task.
402
+ * @returns Promise<TaskResponse>
403
+ * @throws Error
404
+ * @example
405
+ * ```typescript
406
+ * task.hold().then(()=>{}).catch(()=>{})
407
+ * ```
408
+ * */
409
+ async hold() {
410
+ return this.holdResume();
411
+ }
412
+
413
+ /**
414
+ * This is used to resume the task.
415
+ * @returns Promise<TaskResponse>
416
+ * @throws Error
417
+ * @example
418
+ * ```typescript
419
+ * task.resume().then(()=>{}).catch(()=>{})
420
+ * ```
421
+ * */
422
+ async resume() {
423
+ return this.holdResume();
424
+ }
425
+
426
+ /**
427
+ * This is used to hold or resume the task.
428
+ * @param isHeld: boolean - true to hold the task, false to resume it
429
+ * @returns Promise<TaskResponse>
430
+ * @throws Error
431
+ * @example
432
+ * ```typescript
433
+ * task.holdResume(isHeld: true).then(()=>{}).catch(()=>{})
434
+ * ```
435
+ * */
436
+ async holdResume() {
437
+ /*
438
+ Determine if the task is being held or resumed based on the media resource state
439
+ If the media resource is not found, default to resuming the task
440
+ */
441
+ const snapshot = this.getStateMachineSnapshot();
442
+ const snapshotState = snapshot?.value;
443
+ const mainInteractionId = this.data.interaction?.mainInteractionId || this.data.interactionId;
444
+ const mainMediaResource = this.data.interaction?.media?.[mainInteractionId]?.mediaResourceId || this.data.mediaResourceId;
445
+ const mediaHoldState = this.data.interaction?.media?.[mainInteractionId]?.isHold ?? this.data.interaction.media?.[mainMediaResource]?.isHold;
446
+ let shouldHold = !(mediaHoldState ?? false);
447
+ if (snapshotState === _stateMachine.TaskState.HELD || snapshotState === _stateMachine.TaskState.RESUME_INITIATING) {
448
+ shouldHold = false;
449
+ } else if (snapshotState === _stateMachine.TaskState.CONNECTED && mediaHoldState !== true) {
450
+ // CONNECTED with isHold:true can happen after consult RONA/decline when event ordering
451
+ // leaves the machine in CONNECTED while main media is still on hold — resume on first click.
452
+ shouldHold = true;
453
+ }
454
+
455
+ // Validate operation is allowed in current state
456
+ const state = snapshot;
457
+ if (state) {
458
+ const currentState = state.value;
459
+ if (shouldHold) {
460
+ if (!state.matches(_stateMachine.TaskState.CONNECTED)) {
461
+ const error = new Error(`Cannot hold call in current state: ${currentState}`);
462
+ _loggerProxy.default.error('Hold operation not allowed', {
463
+ module: _constants.CC_FILE,
464
+ method: _constants.METHODS.HOLD_RESUME,
465
+ interactionId: this.data.interactionId
466
+ });
467
+ throw error;
468
+ }
469
+ } else if (!state.matches(_stateMachine.TaskState.HELD) && !(state.matches(_stateMachine.TaskState.CONFERENCING) && mediaHoldState === true) && !(state.matches(_stateMachine.TaskState.CONNECTED) && mediaHoldState === true)) {
470
+ const error = new Error(`Cannot resume call in current state: ${currentState}`);
471
+ _loggerProxy.default.error('Resume operation not allowed', {
472
+ module: _constants.CC_FILE,
473
+ method: _constants.METHODS.HOLD_RESUME,
474
+ interactionId: this.data.interactionId
475
+ });
476
+ throw error;
477
+ }
478
+ }
479
+
480
+ // Send initiating event to transition to intermediate state
481
+ if (this.stateMachineService) {
482
+ const initiatingEvent = shouldHold ? _stateMachine.TaskEvent.HOLD_INITIATED : _stateMachine.TaskEvent.UNHOLD_INITIATED;
483
+ this.stateMachineService.send({
484
+ type: initiatingEvent,
485
+ mediaResourceId: mainMediaResource
486
+ });
487
+ }
488
+ _loggerProxy.default.info(`${shouldHold ? 'Holding' : 'Resuming'} task`, {
489
+ module: _constants.CC_FILE,
490
+ method: _constants.METHODS.HOLD_RESUME,
491
+ interactionId: this.data.interactionId
492
+ });
493
+ 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];
494
+ this.metricsManager.timeEvent([successEvt, failedEvt]);
495
+ try {
496
+ let response;
497
+ if (shouldHold) {
498
+ response = await this.contact.hold({
499
+ interactionId: this.data.interactionId,
500
+ data: {
501
+ mediaResourceId: mainMediaResource
502
+ }
503
+ });
504
+
505
+ // Send success event to complete the transition
506
+ if (this.stateMachineService) {
507
+ this.stateMachineService.send({
508
+ type: _stateMachine.TaskEvent.HOLD_SUCCESS,
509
+ mediaResourceId: mainMediaResource
510
+ });
511
+ }
512
+ this.metricsManager.trackEvent(successEvt, {
513
+ taskId: this.data.interactionId,
514
+ mediaResourceId: mainMediaResource,
515
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response)
516
+ }, ['operational', 'behavioral']);
517
+ _loggerProxy.default.log(`Task placed on hold successfully`, {
518
+ module: _constants.CC_FILE,
519
+ method: _constants.METHODS.HOLD_RESUME,
520
+ trackingId: response.trackingId,
521
+ interactionId: this.data.interactionId
522
+ });
523
+ } else {
524
+ response = await this.contact.unHold({
525
+ interactionId: this.data.interactionId,
526
+ data: {
527
+ mediaResourceId: mainMediaResource
528
+ }
529
+ });
530
+
531
+ // Send success event to complete the transition
532
+ if (this.stateMachineService) {
533
+ this.stateMachineService.send({
534
+ type: _stateMachine.TaskEvent.UNHOLD_SUCCESS,
535
+ mediaResourceId: mainMediaResource
536
+ });
537
+ }
538
+ this.metricsManager.trackEvent(successEvt, {
539
+ taskId: this.data.interactionId,
540
+ mainInteractionId,
541
+ mediaResourceId: mainMediaResource,
542
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response)
543
+ }, ['operational', 'behavioral']);
544
+ _loggerProxy.default.log(`Task resumed successfully`, {
545
+ module: _constants.CC_FILE,
546
+ method: _constants.METHODS.HOLD_RESUME,
547
+ trackingId: response.trackingId,
548
+ interactionId: this.data.interactionId
549
+ });
550
+ }
551
+ return response;
552
+ } catch (error) {
553
+ const failureEvent = shouldHold ? _stateMachine.TaskEvent.HOLD_FAILED : _stateMachine.TaskEvent.UNHOLD_FAILED;
554
+ this.stateMachineService.send({
555
+ type: failureEvent,
556
+ reason: error.toString(),
557
+ mediaResourceId: this.data.mediaResourceId
558
+ });
559
+ const {
560
+ error: detailedError
561
+ } = (0, _Utils.getErrorDetails)(error, 'holdResume', _constants.CC_FILE);
562
+ this.metricsManager.trackEvent(failedEvt, shouldHold ? {
563
+ taskId: this.data.interactionId,
564
+ mediaResourceId: this.data.mediaResourceId,
565
+ error: error.toString(),
566
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
567
+ } : {
568
+ taskId: this.data.interactionId,
569
+ error: error.toString(),
570
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
571
+ }, ['operational', 'behavioral']);
572
+ throw detailedError;
573
+ }
574
+ }
575
+
576
+ /**
577
+ * This is used to pause the call recording
578
+ * @returns Promise<TaskResponse>
579
+ * @throws Error
580
+ * @example
581
+ * ```typescript
582
+ * task.pauseRecording().then(()=>{}).catch(()=>{});
583
+ * ```
584
+ */
585
+ async pauseRecording() {
586
+ // Validate recording is active
587
+ const state = this.getStateMachineSnapshot();
588
+ if (state) {
589
+ const {
590
+ recordingControlsAvailable,
591
+ recordingInProgress
592
+ } = state.context;
593
+ const recordingActive = Boolean(recordingControlsAvailable && recordingInProgress);
594
+ if (!recordingActive) {
595
+ const error = new Error('Recording is not active or already paused');
596
+ _loggerProxy.default.error('Pause recording operation not allowed', {
597
+ module: _constants.CC_FILE,
598
+ method: 'pauseRecording',
599
+ interactionId: this.data.interactionId
600
+ });
601
+ throw error;
602
+ }
603
+ }
604
+ try {
605
+ _loggerProxy.default.info(`Pausing recording`, {
606
+ module: _constants.CC_FILE,
607
+ method: 'pauseRecording',
608
+ interactionId: this.data.interactionId
609
+ });
610
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_FAILED]);
611
+ const result = await this.contact.pauseRecording({
612
+ interactionId: this.data.interactionId
613
+ });
614
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_SUCCESS, {
615
+ taskId: this.data.interactionId,
616
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result)
617
+ }, ['operational', 'behavioral', 'business']);
618
+ _loggerProxy.default.log(`Recording paused successfully`, {
619
+ module: _constants.CC_FILE,
620
+ method: 'pauseRecording',
621
+ trackingId: result.trackingId,
622
+ interactionId: this.data.interactionId
623
+ });
624
+ return result;
625
+ } catch (error) {
626
+ const {
627
+ error: detailedError
628
+ } = (0, _Utils.getErrorDetails)(error, 'pauseRecording', _constants.CC_FILE);
629
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_PAUSE_RECORDING_FAILED, {
630
+ taskId: this.data.interactionId,
631
+ error: error.toString(),
632
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
633
+ }, ['operational', 'behavioral', 'business']);
634
+ throw detailedError;
635
+ }
636
+ }
637
+
638
+ /**
639
+ * This is used to pause the call recording
640
+ * @param resumeRecordingPayload
641
+ * @returns Promise<TaskResponse>
642
+ * @throws Error
643
+ * @example
644
+ * ```typescript
645
+ * task.resumeRecording(resumeRecordingPayload).then(()=>{}).catch(()=>{});
646
+ * ```
647
+ */
648
+ async resumeRecording(resumeRecordingPayload) {
649
+ // Validate recording is paused
650
+ const state = this.getStateMachineSnapshot();
651
+ if (state) {
652
+ const {
653
+ recordingControlsAvailable,
654
+ recordingInProgress
655
+ } = state.context;
656
+ const recordingPaused = Boolean(recordingControlsAvailable && !recordingInProgress);
657
+ if (!recordingPaused) {
658
+ const error = new Error('Recording is not paused');
659
+ _loggerProxy.default.error('Resume recording operation not allowed', {
660
+ module: _constants.CC_FILE,
661
+ method: 'resumeRecording',
662
+ interactionId: this.data.interactionId
663
+ });
664
+ throw error;
665
+ }
666
+ }
667
+ try {
668
+ _loggerProxy.default.info(`Resuming recording`, {
669
+ module: _constants.CC_FILE,
670
+ method: 'resumeRecording',
671
+ interactionId: this.data.interactionId
672
+ });
673
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_FAILED]);
674
+ resumeRecordingPayload ??= {
675
+ autoResumed: false
676
+ };
677
+ const result = await this.contact.resumeRecording({
678
+ interactionId: this.data.interactionId,
679
+ data: resumeRecordingPayload
680
+ });
681
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_SUCCESS, {
682
+ taskId: this.data.interactionId,
683
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result)
684
+ }, ['operational', 'behavioral', 'business']);
685
+ _loggerProxy.default.log(`Recording resumed successfully`, {
686
+ module: _constants.CC_FILE,
687
+ method: 'resumeRecording',
688
+ trackingId: result.trackingId,
689
+ interactionId: this.data.interactionId
690
+ });
691
+ return result;
692
+ } catch (error) {
693
+ const {
694
+ error: detailedError
695
+ } = (0, _Utils.getErrorDetails)(error, 'resumeRecording', _constants.CC_FILE);
696
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_RESUME_RECORDING_FAILED, {
697
+ taskId: this.data.interactionId,
698
+ error: error.toString(),
699
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
700
+ }, ['operational', 'behavioral', 'business']);
701
+ throw detailedError;
702
+ }
703
+ }
704
+
705
+ /**
706
+ * This is used to consult the task
707
+ * @param consultPayload
708
+ * @returns Promise<TaskResponse>
709
+ * @throws Error
710
+ * @example
711
+ * ```typescript
712
+ * const consultPayload = {
713
+ * destination: 'myBuddyAgentId',
714
+ * destinationType: DESTINATION_TYPE.AGENT,
715
+ * }
716
+ * task.consult(consultPayload).then(()=>{}).catch(()=>{});
717
+ * ```
718
+ * */
719
+ async consult(consultPayload) {
720
+ // Validate consult is allowed
721
+ const state = this.getStateMachineSnapshot();
722
+ const canConsult = state && (state.matches(_stateMachine.TaskState.CONNECTED) || state.matches(_stateMachine.TaskState.HELD) || state.matches(_stateMachine.TaskState.CONFERENCING));
723
+ if (!canConsult) {
724
+ const currentState = state?.value;
725
+ const error = new Error(`Cannot initiate consult in ${currentState} state`);
726
+ _loggerProxy.default.error('Consult operation not allowed', {
727
+ module: _constants.CC_FILE,
728
+ method: 'consult',
729
+ interactionId: this.data.interactionId
730
+ });
731
+ throw error;
732
+ }
733
+
734
+ // Send initiating event to transition to CONSULT_INITIATING state
735
+ if (this.stateMachineService) {
736
+ this.stateMachineService.send({
737
+ type: _stateMachine.TaskEvent.CONSULT,
738
+ destination: consultPayload.to,
739
+ destinationType: consultPayload.destinationType
740
+ });
741
+ }
742
+ const requestInteractionId = this.data.interaction?.mainInteractionId || this.data.interactionId;
743
+ try {
744
+ _loggerProxy.default.info(`Starting consult`, {
745
+ module: _constants.CC_FILE,
746
+ method: 'consult',
747
+ interactionId: requestInteractionId
748
+ });
749
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_CONSULT_START_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_CONSULT_START_FAILED]);
750
+ const result = await this.contact.consult({
751
+ interactionId: requestInteractionId,
752
+ data: consultPayload
753
+ });
754
+
755
+ // Send success event to transition to CONSULTING state
756
+ if (this.stateMachineService) {
757
+ this.stateMachineService.send({
758
+ type: _stateMachine.TaskEvent.CONSULT_SUCCESS,
759
+ taskData: result.data
760
+ });
761
+ }
762
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONSULT_START_SUCCESS, {
763
+ taskId: this.data.interactionId,
764
+ requestInteractionId,
765
+ destination: consultPayload.to,
766
+ destinationType: consultPayload.destinationType,
767
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result)
768
+ }, ['operational', 'behavioral', 'business']);
769
+ _loggerProxy.default.log(`Consult successfully initiated to ${consultPayload.to}`, {
770
+ module: _constants.CC_FILE,
771
+ method: 'consult',
772
+ trackingId: result.trackingId,
773
+ interactionId: requestInteractionId
774
+ });
775
+ return result;
776
+ } catch (error) {
777
+ this.stateMachineService.send({
778
+ type: _stateMachine.TaskEvent.CONSULT_FAILED,
779
+ reason: error.toString()
780
+ });
781
+ const {
782
+ error: detailedError
783
+ } = (0, _Utils.getErrorDetails)(error, 'consult', _constants.CC_FILE);
784
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONSULT_START_FAILED, {
785
+ taskId: this.data.interactionId,
786
+ requestInteractionId,
787
+ destination: consultPayload.to,
788
+ destinationType: consultPayload.destinationType,
789
+ error: error.toString(),
790
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
791
+ }, ['operational', 'behavioral', 'business']);
792
+ throw detailedError;
793
+ }
794
+ }
795
+
796
+ /**
797
+ * This is used to end the consult session on the task.
798
+ * @param consultEndPayload - Payload indicating consult end flags and identifiers
799
+ * @returns Promise<TaskResponse>
800
+ * @throws Error
801
+ * @example
802
+ * ```typescript
803
+ * task.endConsult({
804
+ * isConsult: true,
805
+ * queueId: 'myQueueId',
806
+ * taskId: 'taskId',
807
+ * });
808
+ * ```
809
+ */
810
+ async endConsult(consultEndPayload) {
811
+ const requestInteractionId = this.data.interaction?.mainInteractionId || this.data.interactionId;
812
+ try {
813
+ _loggerProxy.default.info(`Ending consult`, {
814
+ module: _constants.CC_FILE,
815
+ method: 'endConsult',
816
+ interactionId: requestInteractionId
817
+ });
818
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_CONSULT_END_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_CONSULT_END_FAILED]);
819
+ const result = await this.contact.consultEnd({
820
+ interactionId: requestInteractionId,
821
+ data: consultEndPayload
822
+ });
823
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONSULT_END_SUCCESS, {
824
+ taskId: this.data.interactionId,
825
+ requestInteractionId,
826
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result)
827
+ }, ['operational', 'behavioral', 'business']);
828
+ _loggerProxy.default.log(`Consult ended successfully`, {
829
+ module: _constants.CC_FILE,
830
+ method: 'endConsult',
831
+ trackingId: result.trackingId,
832
+ interactionId: this.data.interactionId
833
+ });
834
+ return result;
835
+ } catch (error) {
836
+ const {
837
+ error: detailedError
838
+ } = (0, _Utils.getErrorDetails)(error, 'endConsult', _constants.CC_FILE);
839
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONSULT_END_FAILED, {
840
+ taskId: this.data.interactionId,
841
+ requestInteractionId,
842
+ error: error.toString(),
843
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
844
+ }, ['operational', 'behavioral', 'business']);
845
+ throw detailedError;
846
+ }
847
+ }
848
+
849
+ /**
850
+ * This is used to transfer the task.
851
+ * @param payload - Transfer payload
852
+ * @returns Promise<TaskResponse>
853
+ * @throws Error
854
+ * @example
855
+ * ```typescript
856
+ * task.transfer({
857
+ * to: 'destinationId',
858
+ * destinationType: DESTINATION_TYPE.AGENT,
859
+ * consult: true, // Optional, if true will perform a consult transfer else blind transfer
860
+ * });
861
+ * ```
862
+ */
863
+ async transfer(payload) {
864
+ try {
865
+ _loggerProxy.default.info(`Transferring task to ${payload.to}`, {
866
+ module: _constants.CC_FILE,
867
+ method: _constants.METHODS.TRANSFER_CALL,
868
+ interactionId: this.data.interactionId
869
+ });
870
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED]);
871
+
872
+ // consult transfer path
873
+ if (this.data.interaction.state === 'consulting') {
874
+ const normalizedDestinationType = payload.destinationType === 'Agent' || payload.destinationType === 'Queue' ? payload.destinationType.toLowerCase() : payload.destinationType;
875
+ let consultPayload = {
876
+ to: payload.to,
877
+ destinationType: normalizedDestinationType
878
+ };
879
+ if (normalizedDestinationType === _types.CONSULT_TRANSFER_DESTINATION_TYPE.QUEUE) {
880
+ const consultContext = this.getStateMachineSnapshot()?.context;
881
+ const destAgent = consultContext?.consultDestinationAgentId || this.data.destAgentId;
882
+ if (!destAgent) {
883
+ throw new Error('No agent has accepted this queue consult yet');
884
+ }
885
+ consultPayload = {
886
+ to: destAgent,
887
+ destinationType: _types.CONSULT_TRANSFER_DESTINATION_TYPE.AGENT
888
+ };
889
+ }
890
+ const result = await this.contact.consultTransfer({
891
+ interactionId: this.data.interactionId,
892
+ data: consultPayload
893
+ });
894
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_TRANSFER_SUCCESS, {
895
+ taskId: this.data.interactionId,
896
+ destination: consultPayload.to,
897
+ destinationType: consultPayload.destinationType,
898
+ isConsultTransfer: true,
899
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(result)
900
+ }, ['operational', 'behavioral', 'business']);
901
+ _loggerProxy.default.log(`Consult transfer completed successfully to ${consultPayload.to}`, {
902
+ module: _constants.CC_FILE,
903
+ method: _constants.METHODS.TRANSFER_CALL,
904
+ trackingId: result.trackingId,
905
+ interactionId: this.data.interactionId
906
+ });
907
+ return result;
908
+ }
909
+
910
+ // standard blind transfer
911
+ return await super.transfer({
912
+ to: payload.to,
913
+ destinationType: payload.destinationType
914
+ });
915
+ } catch (err) {
916
+ const {
917
+ error: detailedError
918
+ } = (0, _Utils.getErrorDetails)(err, _constants.METHODS.TRANSFER_CALL, _constants.CC_FILE);
919
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_TRANSFER_FAILED, {
920
+ taskId: this.data.interactionId,
921
+ destination: payload.to,
922
+ destinationType: payload.destinationType,
923
+ isConsultTransfer: this.data.interaction.state === 'consulting',
924
+ error: err.toString(),
925
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(err.details || {})
926
+ }, ['operational', 'behavioral', 'business']);
927
+ throw detailedError;
928
+ }
929
+ }
930
+
931
+ /**
932
+ * Start a consult conference, merging main and consult calls.
933
+ */
934
+ async consultConference() {
935
+ const derivedDestAgentId = this.data.interaction && this.data.agentId ? (0, _Utils.calculateDestAgentId)(this.data.interaction, this.data.agentId) : '';
936
+ const derivedDestType = this.data.interaction && this.data.agentId ? (0, _Utils.calculateDestType)(this.data.interaction, this.data.agentId) : '';
937
+
938
+ // derivedDestType is most reliable as it inspects live interaction participants
939
+ const resolvedDestinationType = derivedDestType || this.getStateMachineSnapshot()?.context?.consultDestinationType || this.data.destinationType || 'agent';
940
+ const consultationData = {
941
+ agentId: this.data.agentId,
942
+ destinationType: resolvedDestinationType,
943
+ // derivedDestAgentId is most reliable as it resolves epId for EP_DN
944
+ // and agent ID for regular agents from live interaction data
945
+ destAgentId: derivedDestAgentId || this.getStateMachineSnapshot()?.context?.consultDestinationAgentId || this.data.destAgentId
946
+ };
947
+
948
+ // Send state machine event to transition to CONF_INITIATING
949
+ if (this.stateMachineService) {
950
+ this.stateMachineService.send({
951
+ type: _stateMachine.TaskEvent.MERGE_TO_CONFERENCE
952
+ });
953
+ }
954
+ try {
955
+ if (!consultationData.destAgentId) {
956
+ throw new Error('Unable to determine consult destination for conference');
957
+ }
958
+ _loggerProxy.default.info(`Initiating consult conference to ${consultationData.destAgentId}`, {
959
+ module: _constants.CC_FILE,
960
+ method: _constants.METHODS.CONSULT_CONFERENCE,
961
+ interactionId: this.data.interactionId
962
+ });
963
+ const paramsDataForConferenceV2 = (0, _Utils.buildConsultConferenceParamData)(consultationData, this.data.interactionId);
964
+ const response = await this.contact.consultConference({
965
+ interactionId: paramsDataForConferenceV2.interactionId,
966
+ data: paramsDataForConferenceV2.data
967
+ });
968
+
969
+ // Send success event to transition to CONFERENCING
970
+ if (this.stateMachineService) {
971
+ this.stateMachineService.send({
972
+ type: _stateMachine.TaskEvent.CONFERENCE_START
973
+ });
974
+ }
975
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_START_SUCCESS, {
976
+ taskId: this.data.interactionId,
977
+ destination: paramsDataForConferenceV2.data.to,
978
+ destinationType: paramsDataForConferenceV2.data.destinationType,
979
+ agentId: paramsDataForConferenceV2.data.agentId,
980
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response)
981
+ }, ['operational', 'behavioral', 'business']);
982
+ _loggerProxy.default.log(`Consult conference started successfully`, {
983
+ module: _constants.CC_FILE,
984
+ method: _constants.METHODS.CONSULT_CONFERENCE,
985
+ interactionId: this.data.interactionId
986
+ });
987
+ return response;
988
+ } catch (error) {
989
+ // Send failure event to revert state
990
+ if (this.stateMachineService) {
991
+ this.stateMachineService.send({
992
+ type: _stateMachine.TaskEvent.CONFERENCE_FAILED,
993
+ reason: error.toString()
994
+ });
995
+ }
996
+ const {
997
+ error: detailedError
998
+ } = (0, _Utils.getErrorDetails)(error, _constants.METHODS.CONSULT_CONFERENCE, _constants.CC_FILE);
999
+ const failedParamsData = (0, _Utils.buildConsultConferenceParamData)(consultationData, this.data.interactionId);
1000
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_START_FAILED, {
1001
+ taskId: this.data.interactionId,
1002
+ destination: failedParamsData.data.to,
1003
+ destinationType: failedParamsData.data.destinationType,
1004
+ agentId: failedParamsData.data.agentId,
1005
+ error: error.toString(),
1006
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
1007
+ }, ['operational', 'behavioral', 'business']);
1008
+ _loggerProxy.default.error(`Failed to start consult conference`, {
1009
+ module: _constants.CC_FILE,
1010
+ method: _constants.METHODS.CONSULT_CONFERENCE,
1011
+ interactionId: this.data.interactionId
1012
+ });
1013
+ throw detailedError;
1014
+ }
1015
+ }
1016
+
1017
+ /**
1018
+ * Removes another participant from an active conference.
1019
+ * Completion is correlated with ParticipantLeftConference by the AQM request layer.
1020
+ * @param payload - Participant identifier to remove
1021
+ * @returns Promise<TaskResponse>
1022
+ */
1023
+ async dropConferenceParticipant(payload) {
1024
+ // The TypeScript contract requires payload, but the published SDK is also callable from
1025
+ // untyped JavaScript. Keep an explicit runtime boundary check before direct property access.
1026
+ if (!payload) {
1027
+ throw new Error('participantId must be a non-empty string');
1028
+ }
1029
+ const {
1030
+ participantId
1031
+ } = payload;
1032
+ if (typeof participantId !== 'string' || participantId.trim().length === 0) {
1033
+ throw new Error('participantId must be a non-empty string');
1034
+ }
1035
+ const requestInteractionId = this.data.interaction?.mainInteractionId || this.data.interactionId;
1036
+ // taskId identifies the SDK task instance, which can remain child-interaction keyed after
1037
+ // consult/EP-DN flows. requestInteractionId identifies the main interaction sent to AQM.
1038
+
1039
+ _loggerProxy.default.info('Dropping conference participant', {
1040
+ module: _constants.CC_FILE,
1041
+ method: _constants.METHODS.DROP_CONFERENCE_PARTICIPANT,
1042
+ interactionId: requestInteractionId
1043
+ });
1044
+ try {
1045
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_PARTICIPANT_DROP_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_PARTICIPANT_DROP_FAILED]);
1046
+ const response = await this.contact.dropConferenceParticipant({
1047
+ interactionId: requestInteractionId,
1048
+ participantId
1049
+ });
1050
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_PARTICIPANT_DROP_SUCCESS, {
1051
+ taskId: this.data.interactionId,
1052
+ requestInteractionId,
1053
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response),
1054
+ agentId: this.agentId
1055
+ }, ['operational', 'behavioral', 'business']);
1056
+ _loggerProxy.default.log('Successfully dropped conference participant', {
1057
+ module: _constants.CC_FILE,
1058
+ method: _constants.METHODS.DROP_CONFERENCE_PARTICIPANT,
1059
+ trackingId: response?.trackingId,
1060
+ interactionId: requestInteractionId
1061
+ });
1062
+ return response;
1063
+ } catch (error) {
1064
+ const failureTrackingFields = _MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error?.details || {});
1065
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_PARTICIPANT_DROP_FAILED, {
1066
+ taskId: this.data.interactionId,
1067
+ requestInteractionId,
1068
+ agentId: this.agentId,
1069
+ trackingId: failureTrackingFields.trackingId,
1070
+ notifTrackingId: failureTrackingFields.notifTrackingId,
1071
+ orgId: failureTrackingFields.orgId,
1072
+ failureType: failureTrackingFields.failureType,
1073
+ reasonCode: failureTrackingFields.reasonCode
1074
+ }, ['operational', 'behavioral', 'business']);
1075
+ _loggerProxy.default.error('Failed to drop conference participant', {
1076
+ module: _constants.CC_FILE,
1077
+ method: _constants.METHODS.DROP_CONFERENCE_PARTICIPANT,
1078
+ trackingId: failureTrackingFields.trackingId,
1079
+ interactionId: requestInteractionId
1080
+ });
1081
+ throw error;
1082
+ }
1083
+ }
1084
+
1085
+ /**
1086
+ * Exit from a conference call.
1087
+ * Per conference-spec.md:
1088
+ * - Primary agent exits to wrapup
1089
+ * - Non-primary agent exits to available/connected
1090
+ * - Other participants continue the call
1091
+ *
1092
+ * @returns Promise<TaskResponse>
1093
+ * @throws Error if not in conference or exit fails
1094
+ * @example
1095
+ * ```typescript
1096
+ * task.exitConference().then(() => {}).catch(() => {});
1097
+ * ```
1098
+ */
1099
+ async exitConference() {
1100
+ // Validate we're in conference state OR conference is in progress per task data
1101
+ // This handles cases where:
1102
+ // 1. State machine is in CONFERENCING state
1103
+ // 2. State machine is in CONNECTED but conference is active (e.g., ownership transferred)
1104
+ const state = this.getStateMachineSnapshot();
1105
+ const isConferencingState = state?.matches(_stateMachine.TaskState.CONFERENCING);
1106
+ const isConferenceInProgressFromData = this.data ? (0, _TaskUtils.getIsConferenceInProgress)(this.data) : false;
1107
+ if (!state || !isConferencingState && !isConferenceInProgressFromData) {
1108
+ const currentState = state?.value;
1109
+ const error = new Error(`Cannot exit conference in ${currentState} state`);
1110
+ _loggerProxy.default.error('Exit conference operation not allowed', {
1111
+ module: _constants.CC_FILE,
1112
+ method: 'exitConference',
1113
+ interactionId: this.data.interactionId
1114
+ });
1115
+ throw error;
1116
+ }
1117
+
1118
+ // Send state machine event
1119
+ if (this.stateMachineService) {
1120
+ this.stateMachineService.send({
1121
+ type: _stateMachine.TaskEvent.EXIT_CONFERENCE,
1122
+ agentId: this.data.agentId
1123
+ });
1124
+ }
1125
+ const requestInteractionId = this.data.interaction?.mainInteractionId || this.data.interactionId;
1126
+ try {
1127
+ _loggerProxy.default.info(`Exiting conference`, {
1128
+ module: _constants.CC_FILE,
1129
+ method: 'exitConference',
1130
+ interactionId: requestInteractionId
1131
+ });
1132
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_EXIT_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_EXIT_FAILED]);
1133
+ const response = await this.contact.exitConference({
1134
+ interactionId: requestInteractionId
1135
+ });
1136
+
1137
+ // Send success event to transition state
1138
+ if (this.stateMachineService) {
1139
+ const responseTaskData = response?.data ?? {};
1140
+ const snapshot = this.stateMachineService.getSnapshot();
1141
+ const wrapUpContext = snapshot?.context ?? {
1142
+ uiControlConfig: this.uiControlConfig,
1143
+ taskData: this.data
1144
+ };
1145
+ const mergedTaskData = {
1146
+ ...this.data,
1147
+ ...responseTaskData
1148
+ };
1149
+ // Pending-list precedence is response-only; do not keep a cached snapshot list.
1150
+ if (!Object.prototype.hasOwnProperty.call(responseTaskData, 'agentsPendingWrapUp')) {
1151
+ delete mergedTaskData.agentsPendingWrapUp;
1152
+ }
1153
+ let wrapUpRequired = (0, _guards.shouldWrapUpForThisAgent)(wrapUpContext, mergedTaskData);
1154
+ // Confirmed self-exit: empty/absent response pending matches TaskManager leave stamp.
1155
+ if (!wrapUpRequired && !hasNonemptyPendingWrapUp(responseTaskData)) {
1156
+ wrapUpRequired = !isConsultedNonOwner(mergedTaskData, this.data);
1157
+ }
1158
+ this.stateMachineService.send({
1159
+ type: _stateMachine.TaskEvent.EXIT_CONFERENCE_SUCCESS,
1160
+ taskData: {
1161
+ ...responseTaskData,
1162
+ wrapUpRequired
1163
+ }
1164
+ });
1165
+ }
1166
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_EXIT_SUCCESS, {
1167
+ taskId: this.data.interactionId,
1168
+ requestInteractionId,
1169
+ agentId: this.data.agentId,
1170
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response)
1171
+ }, ['operational', 'behavioral', 'business']);
1172
+ _loggerProxy.default.log(`Successfully exited conference`, {
1173
+ module: _constants.CC_FILE,
1174
+ method: 'exitConference',
1175
+ trackingId: response.trackingId,
1176
+ interactionId: requestInteractionId
1177
+ });
1178
+ return response;
1179
+ } catch (error) {
1180
+ // Send failure event
1181
+ if (this.stateMachineService) {
1182
+ this.stateMachineService.send({
1183
+ type: _stateMachine.TaskEvent.EXIT_CONFERENCE_FAILED,
1184
+ reason: error.toString()
1185
+ });
1186
+ }
1187
+ const {
1188
+ error: detailedError
1189
+ } = (0, _Utils.getErrorDetails)(error, 'exitConference', _constants.CC_FILE);
1190
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_EXIT_FAILED, {
1191
+ taskId: this.data.interactionId,
1192
+ requestInteractionId,
1193
+ agentId: this.data.agentId,
1194
+ error: error.toString(),
1195
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
1196
+ }, ['operational', 'behavioral', 'business']);
1197
+ _loggerProxy.default.error(`Failed to exit conference`, {
1198
+ module: _constants.CC_FILE,
1199
+ method: 'exitConference',
1200
+ interactionId: requestInteractionId
1201
+ });
1202
+ throw detailedError;
1203
+ }
1204
+ }
1205
+
1206
+ /**
1207
+ * Transfer the conference to another participant.
1208
+ * Per conference-spec.md: Only primary agent can transfer conference.
1209
+ * After transfer, the transferring agent exits to wrapup.
1210
+ *
1211
+ * @returns Promise<TaskResponse>
1212
+ * @throws Error if not in conference or transfer fails
1213
+ * @example
1214
+ * ```typescript
1215
+ * task.transferConference().then(() => {}).catch(() => {});
1216
+ * ```
1217
+ */
1218
+ async transferConference() {
1219
+ // Validate we're in conference or consulting state
1220
+ // CONSULTING is allowed because agent can transfer conference while consulting
1221
+ // (transfers ownership to the consulted agent)
1222
+ const state = this.getStateMachineSnapshot();
1223
+ const isValidState = state && (state.matches(_stateMachine.TaskState.CONFERENCING) || state.matches(_stateMachine.TaskState.CONSULTING));
1224
+ if (!isValidState) {
1225
+ const currentState = state?.value;
1226
+ const error = new Error(`Cannot transfer conference in ${currentState} state`);
1227
+ _loggerProxy.default.error('Transfer conference operation not allowed', {
1228
+ module: _constants.CC_FILE,
1229
+ method: 'transferConference',
1230
+ interactionId: this.data.interactionId
1231
+ });
1232
+ throw error;
1233
+ }
1234
+
1235
+ // Send state machine event
1236
+ if (this.stateMachineService) {
1237
+ this.stateMachineService.send({
1238
+ type: _stateMachine.TaskEvent.TRANSFER_CONFERENCE,
1239
+ agentId: this.data.agentId
1240
+ });
1241
+ }
1242
+ const requestInteractionId = this.data.interaction?.mainInteractionId || this.data.interactionId;
1243
+ try {
1244
+ _loggerProxy.default.info(`Transferring conference`, {
1245
+ module: _constants.CC_FILE,
1246
+ method: 'transferConference',
1247
+ interactionId: requestInteractionId
1248
+ });
1249
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_FAILED]);
1250
+ const response = await this.contact.conferenceTransfer({
1251
+ interactionId: requestInteractionId
1252
+ });
1253
+
1254
+ // Send success event to transition state
1255
+ if (this.stateMachineService) {
1256
+ this.stateMachineService.send({
1257
+ type: _stateMachine.TaskEvent.TRANSFER_CONFERENCE_SUCCESS,
1258
+ taskData: response.data
1259
+ });
1260
+ }
1261
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_SUCCESS, {
1262
+ taskId: this.data.interactionId,
1263
+ requestInteractionId,
1264
+ agentId: this.data.agentId,
1265
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response)
1266
+ }, ['operational', 'behavioral', 'business']);
1267
+ _loggerProxy.default.log(`Successfully transferred conference`, {
1268
+ module: _constants.CC_FILE,
1269
+ method: 'transferConference',
1270
+ trackingId: response.trackingId,
1271
+ interactionId: requestInteractionId
1272
+ });
1273
+ return response;
1274
+ } catch (error) {
1275
+ // Send failure event
1276
+ if (this.stateMachineService) {
1277
+ this.stateMachineService.send({
1278
+ type: _stateMachine.TaskEvent.TRANSFER_CONFERENCE_FAILED,
1279
+ reason: error.toString()
1280
+ });
1281
+ }
1282
+ const {
1283
+ error: detailedError
1284
+ } = (0, _Utils.getErrorDetails)(error, 'transferConference', _constants.CC_FILE);
1285
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_CONFERENCE_TRANSFER_FAILED, {
1286
+ taskId: this.data.interactionId,
1287
+ requestInteractionId,
1288
+ agentId: this.data.agentId,
1289
+ error: error.toString(),
1290
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
1291
+ }, ['operational', 'behavioral', 'business']);
1292
+ _loggerProxy.default.error(`Failed to transfer conference`, {
1293
+ module: _constants.CC_FILE,
1294
+ method: 'transferConference',
1295
+ interactionId: requestInteractionId
1296
+ });
1297
+ throw detailedError;
1298
+ }
1299
+ }
1300
+
1301
+ /**
1302
+ * Toggle between consult call and main call during consulting.
1303
+ * If on consult leg (consultCallHeld = false), switches to main call by holding consult.
1304
+ * If on main call (consultCallHeld = true), switches to consult by resuming consult.
1305
+ *
1306
+ * @returns Promise<TaskResponse>
1307
+ * @throws Error if not in CONSULTING state or no consult media resource
1308
+ * @example
1309
+ * ```typescript
1310
+ * await task.switchCall();
1311
+ * ```
1312
+ */
1313
+ async switchCall() {
1314
+ // Validate we're in CONSULTING state
1315
+ const state = this.getStateMachineSnapshot();
1316
+ if (!state?.matches(_stateMachine.TaskState.CONSULTING)) {
1317
+ const currentState = state?.value;
1318
+ const error = new Error(`Cannot switch call in ${currentState} state`);
1319
+ _loggerProxy.default.error('Switch call operation not allowed', {
1320
+ module: _constants.CC_FILE,
1321
+ method: 'switchCall',
1322
+ interactionId: this.data.interactionId
1323
+ });
1324
+ throw error;
1325
+ }
1326
+
1327
+ // Validate we have a consult media resource
1328
+ const consultMediaResourceId = (0, _TaskUtils.getConsultMediaResourceId)(this.data.interaction, this.data.consultMediaResourceId, this.data.agentId);
1329
+ if (!consultMediaResourceId) {
1330
+ const error = new Error('No consult media resource available');
1331
+ _loggerProxy.default.error('Switch call failed - no consult media resource', {
1332
+ module: _constants.CC_FILE,
1333
+ method: 'switchCall',
1334
+ interactionId: this.data.interactionId
1335
+ });
1336
+ throw error;
1337
+ }
1338
+ const context = state.context;
1339
+ const isOnConsultLeg = !context.consultCallHeld;
1340
+
1341
+ // Determine direction and send appropriate state machine event
1342
+ const targetEvent = isOnConsultLeg ? _stateMachine.TaskEvent.SWITCH_TO_MAIN_CALL : _stateMachine.TaskEvent.SWITCH_TO_CONSULT;
1343
+ const revertEvent = isOnConsultLeg ? _stateMachine.TaskEvent.SWITCH_TO_CONSULT : _stateMachine.TaskEvent.SWITCH_TO_MAIN_CALL;
1344
+ if (this.stateMachineService) {
1345
+ this.stateMachineService.send({
1346
+ type: targetEvent
1347
+ });
1348
+ }
1349
+ this.metricsManager.timeEvent([_constants2.METRIC_EVENT_NAMES.TASK_SWITCH_CALL_SUCCESS, _constants2.METRIC_EVENT_NAMES.TASK_SWITCH_CALL_FAILED]);
1350
+ try {
1351
+ if (isOnConsultLeg) {
1352
+ const response = await this.contact.unHold({
1353
+ interactionId: this.data.interactionId,
1354
+ data: {
1355
+ mediaResourceId: this.data.mediaResourceId
1356
+ }
1357
+ });
1358
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_SWITCH_CALL_SUCCESS, {
1359
+ taskId: this.data.interactionId,
1360
+ direction: 'toMainCall',
1361
+ mediaResourceId: consultMediaResourceId,
1362
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response)
1363
+ }, ['operational', 'behavioral']);
1364
+ _loggerProxy.default.log(`Switched to main call successfully`, {
1365
+ module: _constants.CC_FILE,
1366
+ method: 'switchCall',
1367
+ trackingId: response.trackingId,
1368
+ interactionId: this.data.interactionId
1369
+ });
1370
+ return response;
1371
+ }
1372
+ const response = await this.contact.hold({
1373
+ interactionId: this.data.interactionId,
1374
+ data: {
1375
+ mediaResourceId: this.data.mediaResourceId
1376
+ }
1377
+ });
1378
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_SWITCH_CALL_SUCCESS, {
1379
+ taskId: this.data.interactionId,
1380
+ direction: 'toConsultCall',
1381
+ mediaResourceId: consultMediaResourceId,
1382
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponse(response)
1383
+ }, ['operational', 'behavioral']);
1384
+ _loggerProxy.default.log(`Switched to consult call successfully`, {
1385
+ module: _constants.CC_FILE,
1386
+ method: 'switchCall',
1387
+ trackingId: response.trackingId,
1388
+ interactionId: this.data.interactionId
1389
+ });
1390
+ return response;
1391
+ } catch (error) {
1392
+ if (this.stateMachineService) {
1393
+ this.stateMachineService.send({
1394
+ type: revertEvent
1395
+ });
1396
+ }
1397
+ this.metricsManager.trackEvent(_constants2.METRIC_EVENT_NAMES.TASK_SWITCH_CALL_FAILED, {
1398
+ taskId: this.data.interactionId,
1399
+ direction: isOnConsultLeg ? 'toMainCall' : 'toConsultCall',
1400
+ mediaResourceId: consultMediaResourceId,
1401
+ error: error.toString(),
1402
+ ..._MetricsManager.default.getCommonTrackingFieldForAQMResponseFailed(error.details || {})
1403
+ }, ['operational', 'behavioral']);
1404
+ const {
1405
+ error: detailedError
1406
+ } = (0, _Utils.getErrorDetails)(error, 'switchCall', _constants.CC_FILE);
1407
+ _loggerProxy.default.error(`Failed to switch call`, {
1408
+ module: _constants.CC_FILE,
1409
+ method: 'switchCall',
1410
+ interactionId: this.data.interactionId
1411
+ });
1412
+ throw detailedError;
1413
+ }
1414
+ }
1415
+ getChannelSpecificActionOverrides() {
1416
+ const baseOverrides = super.getChannelSpecificActionOverrides();
1417
+ return {
1418
+ ...baseOverrides,
1419
+ emitTaskHold: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_HOLD, {
1420
+ updateTaskData: true
1421
+ }),
1422
+ emitTaskResume: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_RESUME, {
1423
+ updateTaskData: true
1424
+ }),
1425
+ emitTaskRecordingStarted: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_RECORDING_STARTED, {
1426
+ updateTaskData: true
1427
+ }),
1428
+ emitTaskRecordingPaused: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_RECORDING_PAUSED, {
1429
+ updateTaskData: true
1430
+ }),
1431
+ emitTaskRecordingPauseFailed: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_RECORDING_PAUSE_FAILED, {
1432
+ updateTaskData: true
1433
+ }),
1434
+ emitTaskRecordingResumed: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_RECORDING_RESUMED, {
1435
+ updateTaskData: true
1436
+ }),
1437
+ emitTaskRecordingResumeFailed: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_RECORDING_RESUME_FAILED, {
1438
+ updateTaskData: true
1439
+ }),
1440
+ // Conference event emitters
1441
+ emitTaskParticipantJoined: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_PARTICIPANT_JOINED, {
1442
+ updateTaskData: true
1443
+ }),
1444
+ emitTaskParticipantLeft: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_PARTICIPANT_LEFT, {
1445
+ updateTaskData: true
1446
+ }),
1447
+ emitTaskConferenceStarted: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_CONFERENCE_STARTED, {
1448
+ updateTaskData: true
1449
+ }),
1450
+ emitTaskConferenceEnded: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_CONFERENCE_ENDED, {
1451
+ updateTaskData: true
1452
+ }),
1453
+ emitTaskConferenceFailed: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_CONFERENCE_FAILED, {
1454
+ updateTaskData: true
1455
+ }),
1456
+ emitTaskExitConference: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_EXIT_CONFERENCE, {
1457
+ updateTaskData: false
1458
+ }),
1459
+ emitTaskTransferConference: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_TRANSFER_CONFERENCE, {
1460
+ updateTaskData: false
1461
+ }),
1462
+ emitTaskSwitchCall: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_SWITCH_CALL, {
1463
+ updateTaskData: false
1464
+ }),
1465
+ emitTaskTransferConferenceFailed: this.createEmitSelfAction(_types.TASK_EVENTS.TASK_CONFERENCE_TRANSFER_FAILED, {
1466
+ updateTaskData: true
1467
+ }),
1468
+ emitTaskOutdialFailed: ({
1469
+ event
1470
+ }) => {
1471
+ const taskData = event?.taskData;
1472
+ if (taskData) {
1473
+ this.updateTaskData(taskData);
1474
+ }
1475
+ if (taskData?.suppressOutdialFailedPopup) {
1476
+ return;
1477
+ }
1478
+ const reason = event?.reason || 'Outdial failed';
1479
+ this.emit(_types.TASK_EVENTS.TASK_OUTDIAL_FAILED, reason);
1480
+ }
1481
+ };
1482
+ }
1483
+ }
1484
+ exports.default = Voice;
1485
+ //# sourceMappingURL=Voice.js.map