@webex/contact-center 3.12.0-next.16 → 3.12.0-next.161

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
@@ -1,8 +1,173 @@
1
1
  /* eslint-disable import/prefer-default-export */
2
2
  import {Interaction, ITask, TaskData, MEDIA_CHANNEL} from './types';
3
- import {CC_EVENTS} from '../config/types';
4
- import {OUTDIAL_DIRECTION, OUTDIAL_MEDIA_TYPE, OUTBOUND_TYPE} from '../../constants';
5
3
  import {LoginOption} from '../../types';
4
+ import {
5
+ PARTICIPANT_TYPE,
6
+ MEDIA_TYPE_MAIN_CALL,
7
+ CONSULT_STATE,
8
+ INTERACTION_STATE,
9
+ MEDIA_TYPE_CONSULT,
10
+ } from './state-machine/constants';
11
+ import {TaskContext} from './state-machine/types';
12
+ import {CC_EVENTS} from '../config/types';
13
+ import {OUTBOUND_TYPE, OUTDIAL_DIRECTION, OUTDIAL_MEDIA_TYPE} from '../../constants';
14
+
15
+ type AISummaryError = Error & {data?: Record<string, unknown>};
16
+
17
+ export const isNonEmptyString = (value: unknown): value is string =>
18
+ typeof value === 'string' && value.length > 0;
19
+
20
+ export const createSummaryError = (errorCode: string): AISummaryError => {
21
+ const error = new Error(errorCode) as AISummaryError;
22
+
23
+ error.data = {errorCode};
24
+
25
+ return error;
26
+ };
27
+
28
+ const CAMPAIGN_PREVIEW_OUTBOUND_TYPES = ['STANDARD_PREVIEW_CAMPAIGN', 'DIRECT_PREVIEW_CAMPAIGN'];
29
+ const CAMPAIGN_PREVIEW_CAMPAIGN_TYPES = ['preview_standard', 'preview_direct'];
30
+
31
+ /**
32
+ * Checks if the customer is still in the call (not left)
33
+ *
34
+ * @param interaction - The interaction object
35
+ * @param interactionId - The main interaction ID
36
+ * @returns true if customer is in the call
37
+ */
38
+ export const getIsCustomerInCall = (interaction: Interaction, interactionId: string): boolean => {
39
+ const mainCallMedia = interaction.media?.[interactionId];
40
+ const participants = interaction.participants;
41
+ if (!mainCallMedia?.participants || !participants) {
42
+ return false;
43
+ }
44
+
45
+ return mainCallMedia.participants.some((participantId: string) => {
46
+ const participant = participants[participantId];
47
+
48
+ return participant?.pType === PARTICIPANT_TYPE.CUSTOMER && !participant.hasLeft;
49
+ });
50
+ };
51
+
52
+ /**
53
+ * Gets the count of active agent participants in the conference
54
+ * Excludes Customer, Supervisor, and VVA participant types
55
+ *
56
+ * @param interaction - The interaction object
57
+ * @param interactionId - The main interaction ID
58
+ * @returns Number of active agent participants
59
+ */
60
+ export const getConferenceParticipantsCount = (
61
+ interaction: Interaction,
62
+ interactionId: string
63
+ ): number => {
64
+ const mainCallMedia = interaction.media?.[interactionId];
65
+ const participants = interaction.participants;
66
+ if (!mainCallMedia?.participants || !participants) {
67
+ return 0;
68
+ }
69
+
70
+ let count = 0;
71
+ for (const participantId of mainCallMedia.participants) {
72
+ const participant = participants[participantId];
73
+ if (
74
+ participant &&
75
+ participant.pType !== PARTICIPANT_TYPE.CUSTOMER &&
76
+ participant.pType !== PARTICIPANT_TYPE.SUPERVISOR &&
77
+ participant.pType !== PARTICIPANT_TYPE.VVA &&
78
+ !participant.hasLeft
79
+ ) {
80
+ count += 1;
81
+ }
82
+ }
83
+
84
+ return count;
85
+ };
86
+
87
+ /**
88
+ * Determines if a consult is actively in-progress for conference control gating.
89
+ * This is used to disable conference controls (End/Consult) only when a consult leg
90
+ * still exists outside the main call participants.
91
+ */
92
+ export const getIsConsultInProgressForConferenceControls = (
93
+ interaction: Interaction | undefined,
94
+ mainCallId: string | undefined,
95
+ selfAgentId: string | undefined
96
+ ): boolean => {
97
+ if (!interaction || !mainCallId) return false;
98
+
99
+ const mainParticipants = interaction.media?.[mainCallId]?.participants;
100
+ if (!Array.isArray(mainParticipants) || mainParticipants.length === 0) return false;
101
+
102
+ const mainSet = new Set(mainParticipants);
103
+ const media = interaction.media;
104
+ if (!media) return false;
105
+
106
+ return Object.values(media).some((m: any) => {
107
+ if (!m || m.mType !== 'consult') return false;
108
+ if (!Array.isArray(m.participants) || m.participants.length === 0) return false;
109
+
110
+ return m.participants.some((participantId: string) => {
111
+ const p: any = interaction.participants?.[participantId];
112
+ if (!p || p.hasLeft) return false;
113
+ if (selfAgentId && participantId === selfAgentId) return false;
114
+ if (mainSet.has(participantId)) return false;
115
+
116
+ const participantType = String(p.pType ?? '').toUpperCase();
117
+ const isEpDnParticipant =
118
+ participantType === 'EP-DN' || participantType === 'EP_DN' || participantType === 'DN';
119
+ if (isEpDnParticipant) {
120
+ if (!selfAgentId) {
121
+ return true;
122
+ }
123
+ const selfConsultState = interaction.participants?.[selfAgentId]?.consultState as
124
+ | string
125
+ | undefined;
126
+
127
+ return (
128
+ selfConsultState === 'consulting' ||
129
+ selfConsultState === 'conferencing' ||
130
+ selfConsultState === 'consultInitiated'
131
+ );
132
+ }
133
+
134
+ const consultState = p.consultState as string | undefined;
135
+ const isRonaPendingConsultee = consultState === 'consultReserved' && p.hasJoined === false;
136
+ const consultLegActive =
137
+ consultState === 'consulting' ||
138
+ p.currentState === 'consulting' ||
139
+ (p.isConsulted === true && consultState !== 'consultCompleted' && !isRonaPendingConsultee);
140
+
141
+ return consultLegActive;
142
+ });
143
+ });
144
+ };
145
+
146
+ export const getIsConsultedAgentForControls = (
147
+ taskData: TaskData | null,
148
+ context: TaskContext,
149
+ isConsultingState: boolean
150
+ ): boolean => {
151
+ return Boolean(taskData?.isConsulted) || (isConsultingState && !context.consultInitiator);
152
+ };
153
+
154
+ export const getServerHoldStateForControls = (
155
+ context: TaskContext,
156
+ mainCallId?: string,
157
+ fallbackTaskData?: TaskData | null
158
+ ): boolean | undefined => {
159
+ const media = context.taskData?.interaction?.media ?? fallbackTaskData?.interaction?.media;
160
+ if (!media) return undefined;
161
+
162
+ if (mainCallId && media[mainCallId]) {
163
+ return media[mainCallId].isHold ?? false;
164
+ }
165
+
166
+ const mediaId = context.taskData?.mediaResourceId ?? fallbackTaskData?.mediaResourceId;
167
+ if (!mediaId) return undefined;
168
+
169
+ return media[mediaId]?.isHold;
170
+ };
6
171
 
7
172
  /**
8
173
  * Determines if the given agent is the primary agent (owner) of the task
@@ -32,7 +197,9 @@ export const isParticipantInMainInteraction = (task: ITask, agentId: string): bo
32
197
 
33
198
  return Object.values(task.data.interaction.media).some(
34
199
  (mediaObj) =>
35
- mediaObj && mediaObj.mType === 'mainCall' && mediaObj.participants?.includes(agentId)
200
+ mediaObj &&
201
+ mediaObj.mType === MEDIA_TYPE_MAIN_CALL &&
202
+ mediaObj.participants?.includes(agentId)
36
203
  );
37
204
  };
38
205
 
@@ -56,33 +223,110 @@ export const checkParticipantNotInInteraction = (task: ITask, agentId: string):
56
223
 
57
224
  /**
58
225
  * Determines if a conference is currently in progress based on the number of active agent participants
59
- * @param TaskData - The payLoad data to check for conference status
226
+ * @param data - The task data to check for conference status
60
227
  * @returns true if there are 2 or more active agent participants in the main call, false otherwise
228
+ *
229
+ * For Agent B (consulted agent), their task's interactionId may be different from the main call.
230
+ * We use mainInteractionId from the interaction if available, otherwise fallback to interactionId.
61
231
  */
62
232
  export const getIsConferenceInProgress = (data: TaskData): boolean => {
63
- const mediaMainCall = data.interaction.media?.[data?.interactionId];
233
+ if (!data.interaction) return false;
234
+
235
+ const mainCallId = data.interaction.mainInteractionId || data.interactionId;
236
+ const mediaMainCall = data.interaction.media?.[mainCallId];
64
237
  const participantsInMainCall = new Set(mediaMainCall?.participants);
65
- const participants = data.interaction.participants;
238
+ const {participants} = data.interaction;
66
239
 
67
240
  const agentParticipants = new Set();
68
- if (participantsInMainCall.size > 0) {
69
- participantsInMainCall.forEach((participantId: string) => {
70
- const participant = participants?.[participantId];
71
- if (
72
- participant &&
73
- participant.pType !== 'Customer' &&
74
- participant.pType !== 'Supervisor' &&
75
- !participant.hasLeft &&
76
- participant.pType !== 'VVA'
77
- ) {
78
- agentParticipants.add(participantId);
79
- }
80
- });
81
- }
241
+ participantsInMainCall.forEach((participantId: string) => {
242
+ const participant = participants[participantId];
243
+ if (
244
+ participant &&
245
+ participant.pType !== PARTICIPANT_TYPE.CUSTOMER &&
246
+ participant.pType !== PARTICIPANT_TYPE.SUPERVISOR &&
247
+ participant.pType !== PARTICIPANT_TYPE.VVA &&
248
+ !participant.hasLeft
249
+ ) {
250
+ agentParticipants.add(participantId);
251
+ }
252
+ });
82
253
 
83
254
  return agentParticipants.size >= 2;
84
255
  };
85
256
 
257
+ const EP_DN_MERGE_TERMINAL_CONSULT_EVENTS = new Set<string>([
258
+ CC_EVENTS.AGENT_CONSULT_ENDED,
259
+ CC_EVENTS.AGENT_CONSULT_CONFERENCE_ENDED,
260
+ ]);
261
+
262
+ const isEpDnParticipantType = (pType: string | undefined): boolean => {
263
+ const normalized = String(pType ?? '').toUpperCase();
264
+
265
+ return normalized === 'EP-DN' || normalized === 'EP_DN' || normalized === 'DN';
266
+ };
267
+
268
+ /** EP-DN consult merged to conference before a second agent joins the main leg (CAI-8329). */
269
+ export const isEpDnConsultPendingConferenceMerge = (
270
+ taskData: TaskData | undefined,
271
+ selfAgentId: string | undefined
272
+ ): boolean => {
273
+ if (!taskData?.interaction || !selfAgentId) return false;
274
+
275
+ if (taskData.type && EP_DN_MERGE_TERMINAL_CONSULT_EVENTS.has(taskData.type)) {
276
+ return false;
277
+ }
278
+
279
+ const interaction = taskData.interaction;
280
+ const mainCallId = interaction.mainInteractionId || taskData.interactionId;
281
+ if (!mainCallId) return false;
282
+
283
+ if (getConferenceParticipantsCount(interaction, mainCallId) > 1) return false;
284
+
285
+ const selfParticipant = interaction.participants?.[selfAgentId] as
286
+ | {consultState?: string}
287
+ | undefined;
288
+ if (taskData.isConsulted === true || selfParticipant?.consultState === 'consultCompleted') {
289
+ return false;
290
+ }
291
+
292
+ const mainParticipantIds = new Set(interaction.media?.[mainCallId]?.participants ?? []);
293
+ const hasEpDnOnConsultLegNotMain = Object.values(interaction.media ?? {}).some((media: any) => {
294
+ if (media?.mType !== MEDIA_TYPE_CONSULT) return false;
295
+
296
+ return (media.participants ?? []).some((participantId: string) => {
297
+ if (participantId === selfAgentId || mainParticipantIds.has(participantId)) return false;
298
+ const participant = interaction.participants?.[participantId] as
299
+ | {hasLeft?: boolean; pType?: string}
300
+ | undefined;
301
+ if (!participant || participant.hasLeft) return false;
302
+
303
+ return isEpDnParticipantType(participant.pType);
304
+ });
305
+ });
306
+ if (!hasEpDnOnConsultLegNotMain) return false;
307
+
308
+ const callProcessingDetails = interaction.callProcessingDetails as
309
+ | {hasCustomerLeft?: string}
310
+ | undefined;
311
+ const customerLeft =
312
+ callProcessingDetails?.hasCustomerLeft === 'true' ||
313
+ !getIsCustomerInCall(interaction, mainCallId);
314
+
315
+ const conferenceFromPayload =
316
+ interaction.state === INTERACTION_STATE.CONFERENCE ||
317
+ getIsConferenceInProgress(taskData) ||
318
+ (interaction.state === INTERACTION_STATE.POST_CALL && customerLeft);
319
+ if (!conferenceFromPayload) return false;
320
+
321
+ return (
322
+ taskData.consultingAgentId === selfAgentId ||
323
+ interaction.owner === selfAgentId ||
324
+ selfParticipant?.consultState === CONSULT_STATE.CONSULTING ||
325
+ selfParticipant?.consultState === CONSULT_STATE.CONFERENCING ||
326
+ selfParticipant?.consultState === 'consultInitiated'
327
+ );
328
+ };
329
+
86
330
  /**
87
331
  * Checks if the current agent is a secondary agent in a consultation scenario.
88
332
  * Secondary agents are those who were consulted (not the original call owner).
@@ -104,17 +348,30 @@ export const isSecondaryAgent = (interaction: Interaction): boolean => {
104
348
  /**
105
349
  * Checks if the current agent is a secondary EP-DN (Entry Point Dial Number) agent.
106
350
  * This is specifically for telephony consultations to external numbers/entry points.
107
- * @param task - The task object containing interaction details
351
+ * @param interaction - The interaction object
108
352
  * @returns true if this is a secondary EP-DN agent in telephony consultation, false otherwise
109
353
  */
110
354
  export const isSecondaryEpDnAgent = (interaction: Interaction): boolean => {
111
- if (!interaction) {
112
- return false;
113
- }
114
-
115
355
  return interaction.mediaType === 'telephony' && isSecondaryAgent(interaction);
116
356
  };
117
357
 
358
+ /**
359
+ * Checks if the task belongs to a campaign preview interaction.
360
+ * Campaign preview ContactEnded events are terminal cleanup events and should not trigger wrapup.
361
+ */
362
+ export const isCampaignPreviewTask = (taskData?: TaskData | null): boolean => {
363
+ const outboundType = taskData?.interaction?.outboundType ?? '';
364
+ const cpd = taskData?.interaction?.callProcessingDetails as unknown as
365
+ | Record<string, string | undefined>
366
+ | undefined;
367
+ const campaignType = cpd?.campaignType ?? '';
368
+
369
+ return (
370
+ CAMPAIGN_PREVIEW_OUTBOUND_TYPES.includes(outboundType) ||
371
+ CAMPAIGN_PREVIEW_CAMPAIGN_TYPES.includes(campaignType)
372
+ );
373
+ };
374
+
118
375
  /**
119
376
  * Checks if auto-answer is enabled for the agent participant
120
377
  * @param interaction - The interaction object
@@ -221,6 +478,40 @@ export const shouldAutoAnswerTask = (
221
478
  return false;
222
479
  };
223
480
 
481
+ /**
482
+ * Gets the consult media resource ID for switch-call operations.
483
+ * Searches for the consult media leg in the interaction.
484
+ *
485
+ * @param interaction - The interaction object
486
+ * @param consultMediaResourceId - The consult media resource ID from task data
487
+ * @param agentId - Current agent ID
488
+ * @returns The consult media resource ID or undefined
489
+ */
490
+ export const getConsultMediaResourceId = (
491
+ interaction: Interaction | undefined,
492
+ consultMediaResourceId: string | undefined,
493
+ agentId: string | undefined
494
+ ): string | undefined => {
495
+ // First priority: use consultMediaResourceId from task data if available
496
+ if (consultMediaResourceId) {
497
+ return consultMediaResourceId;
498
+ }
499
+
500
+ // Second priority: search for consult media leg in interaction.media
501
+ if (!interaction?.media || !agentId) {
502
+ return undefined;
503
+ }
504
+
505
+ // Find the consult media leg where this agent is a participant
506
+ for (const [mediaId, media] of Object.entries(interaction.media)) {
507
+ if (media.mType === 'consult' && media.participants?.includes(agentId)) {
508
+ return mediaId;
509
+ }
510
+ }
511
+
512
+ return undefined;
513
+ };
514
+
224
515
  /**
225
516
  * Checks if a task is a campaign preview reservation that has not yet been accepted.
226
517
  * Campaign preview tasks should not trigger incoming call handling until the agent
@@ -0,0 +1,136 @@
1
+ import {TaskState} from './state-machine/constants';
2
+
3
+ export type WebexCallingDeviceDetails = {
4
+ deviceType: string;
5
+ deviceId: string;
6
+ deviceCallId: string;
7
+ };
8
+
9
+ type WebexCallingParticipant = {
10
+ id?: unknown;
11
+ deviceType?: unknown;
12
+ deviceCallId?: unknown;
13
+ deviceId?: unknown;
14
+ };
15
+
16
+ const normalizeId = (id: unknown): string | undefined => {
17
+ if (typeof id !== 'string') {
18
+ return undefined;
19
+ }
20
+
21
+ const normalizedId = id.trim();
22
+
23
+ return normalizedId === '' ? undefined : normalizedId;
24
+ };
25
+
26
+ const isParticipantsById = (participants: unknown): participants is Record<string, unknown> =>
27
+ Boolean(participants) &&
28
+ typeof participants === 'object' &&
29
+ !Array.isArray(participants) &&
30
+ !(participants instanceof Map);
31
+
32
+ const hasWebexCallingDeviceDetails = (
33
+ participant: unknown
34
+ ): participant is WebexCallingParticipant &
35
+ Pick<WebexCallingDeviceDetails, 'deviceCallId' | 'deviceId'> => {
36
+ const participantDetails = participant as WebexCallingParticipant | undefined;
37
+
38
+ return (
39
+ typeof participantDetails?.deviceCallId === 'string' &&
40
+ participantDetails.deviceCallId.trim() !== '' &&
41
+ typeof participantDetails?.deviceId === 'string' &&
42
+ participantDetails.deviceId.trim() !== ''
43
+ );
44
+ };
45
+
46
+ const isWebexCallingParticipantForAgent = (
47
+ participant: unknown,
48
+ agentId: string
49
+ ): participant is WebexCallingParticipant &
50
+ Pick<WebexCallingDeviceDetails, 'deviceCallId' | 'deviceId'> => {
51
+ const participantDetails = participant as WebexCallingParticipant | undefined;
52
+
53
+ return (
54
+ normalizeId(participantDetails?.id) === agentId && hasWebexCallingDeviceDetails(participant)
55
+ );
56
+ };
57
+
58
+ const getWebexCallingDeviceDetails = (
59
+ participant: WebexCallingParticipant &
60
+ Pick<WebexCallingDeviceDetails, 'deviceCallId' | 'deviceId'>
61
+ ): WebexCallingDeviceDetails => ({
62
+ deviceType: typeof participant.deviceType === 'string' ? participant.deviceType : '',
63
+ deviceId: participant.deviceId,
64
+ deviceCallId: participant.deviceCallId,
65
+ });
66
+
67
+ export function getWebexCallingDeviceDetailsForAgent(
68
+ agentId: string | undefined,
69
+ participants: unknown
70
+ ): WebexCallingDeviceDetails | undefined {
71
+ const normalizedAgentId = normalizeId(agentId);
72
+ if (!normalizedAgentId || !isParticipantsById(participants)) {
73
+ return undefined;
74
+ }
75
+
76
+ const agentParticipant = participants[normalizedAgentId];
77
+ const matchingAgentParticipant = hasWebexCallingDeviceDetails(agentParticipant)
78
+ ? agentParticipant
79
+ : Object.values(participants).find((participant) =>
80
+ isWebexCallingParticipantForAgent(participant, normalizedAgentId)
81
+ );
82
+
83
+ if (!matchingAgentParticipant || !hasWebexCallingDeviceDetails(matchingAgentParticipant)) {
84
+ return undefined;
85
+ }
86
+
87
+ return getWebexCallingDeviceDetails(matchingAgentParticipant);
88
+ }
89
+
90
+ export function isWebexCallingCallForAgent(
91
+ agentId: string | undefined,
92
+ participants: unknown
93
+ ): boolean {
94
+ return Boolean(getWebexCallingDeviceDetailsForAgent(agentId, participants));
95
+ }
96
+
97
+ /** Matches wxAppVoiceMethods.getWebexCallingCallId engaged semantics for uiControls. */
98
+ export function isWxAppEngagedForControls(
99
+ enableWxBetterTogether: boolean,
100
+ agentId: string | undefined,
101
+ participants: unknown,
102
+ state: TaskState
103
+ ): boolean {
104
+ if (!enableWxBetterTogether) {
105
+ return false;
106
+ }
107
+
108
+ if (
109
+ !state ||
110
+ state === TaskState.OFFERED ||
111
+ state === TaskState.IDLE ||
112
+ state === TaskState.WRAPPING_UP ||
113
+ state === TaskState.TERMINATED ||
114
+ state === TaskState.COMPLETED
115
+ ) {
116
+ return false;
117
+ }
118
+
119
+ const details = getWebexCallingDeviceDetailsForAgent(agentId, participants);
120
+
121
+ return details?.deviceType === 'wxApp';
122
+ }
123
+
124
+ export function decodedLineOwnerId(lineOwnerId?: string): string | undefined {
125
+ if (!lineOwnerId) {
126
+ return undefined;
127
+ }
128
+
129
+ try {
130
+ const decoded = atob(lineOwnerId);
131
+
132
+ return decoded.split('/').pop();
133
+ } catch {
134
+ return undefined;
135
+ }
136
+ }