@webex/contact-center 3.12.0-next.12 → 3.12.0-next.121

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