@webex/contact-center 3.12.0-next.17 → 3.12.0-next.170

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