@memberjunction/ng-conversations 5.40.2 → 5.41.0

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 (378) hide show
  1. package/README.md +57 -0
  2. package/dist/__tests__/channel-optional-surface.test.d.ts +2 -0
  3. package/dist/__tests__/channel-optional-surface.test.d.ts.map +1 -0
  4. package/dist/__tests__/channel-optional-surface.test.js +53 -0
  5. package/dist/__tests__/channel-optional-surface.test.js.map +1 -0
  6. package/dist/__tests__/chat-events.test.d.ts +14 -0
  7. package/dist/__tests__/chat-events.test.d.ts.map +1 -0
  8. package/dist/__tests__/chat-events.test.js +109 -0
  9. package/dist/__tests__/chat-events.test.js.map +1 -0
  10. package/dist/__tests__/conversation-naming.test.d.ts +2 -0
  11. package/dist/__tests__/conversation-naming.test.d.ts.map +1 -0
  12. package/dist/__tests__/conversation-naming.test.js +110 -0
  13. package/dist/__tests__/conversation-naming.test.js.map +1 -0
  14. package/dist/__tests__/delegation-result-parser.test.d.ts +2 -0
  15. package/dist/__tests__/delegation-result-parser.test.d.ts.map +1 -0
  16. package/dist/__tests__/delegation-result-parser.test.js +107 -0
  17. package/dist/__tests__/delegation-result-parser.test.js.map +1 -0
  18. package/dist/__tests__/event-wiring.test.d.ts +15 -0
  19. package/dist/__tests__/event-wiring.test.d.ts.map +1 -0
  20. package/dist/__tests__/event-wiring.test.js +100 -0
  21. package/dist/__tests__/event-wiring.test.js.map +1 -0
  22. package/dist/__tests__/narration-template.test.d.ts +2 -0
  23. package/dist/__tests__/narration-template.test.d.ts.map +1 -0
  24. package/dist/__tests__/narration-template.test.js +76 -0
  25. package/dist/__tests__/narration-template.test.js.map +1 -0
  26. package/dist/__tests__/realtime-agent-picker-models.test.d.ts +2 -0
  27. package/dist/__tests__/realtime-agent-picker-models.test.d.ts.map +1 -0
  28. package/dist/__tests__/realtime-agent-picker-models.test.js +49 -0
  29. package/dist/__tests__/realtime-agent-picker-models.test.js.map +1 -0
  30. package/dist/__tests__/realtime-audio-visuals.test.d.ts +2 -0
  31. package/dist/__tests__/realtime-audio-visuals.test.d.ts.map +1 -0
  32. package/dist/__tests__/realtime-audio-visuals.test.js +123 -0
  33. package/dist/__tests__/realtime-audio-visuals.test.js.map +1 -0
  34. package/dist/__tests__/realtime-delegation-card-cancel.test.d.ts +2 -0
  35. package/dist/__tests__/realtime-delegation-card-cancel.test.d.ts.map +1 -0
  36. package/dist/__tests__/realtime-delegation-card-cancel.test.js +48 -0
  37. package/dist/__tests__/realtime-delegation-card-cancel.test.js.map +1 -0
  38. package/dist/__tests__/realtime-disclosure.test.d.ts +2 -0
  39. package/dist/__tests__/realtime-disclosure.test.d.ts.map +1 -0
  40. package/dist/__tests__/realtime-disclosure.test.js +164 -0
  41. package/dist/__tests__/realtime-disclosure.test.js.map +1 -0
  42. package/dist/__tests__/realtime-pairing.test.d.ts +2 -0
  43. package/dist/__tests__/realtime-pairing.test.d.ts.map +1 -0
  44. package/dist/__tests__/realtime-pairing.test.js +207 -0
  45. package/dist/__tests__/realtime-pairing.test.js.map +1 -0
  46. package/dist/__tests__/realtime-review-lifecycle.test.d.ts +2 -0
  47. package/dist/__tests__/realtime-review-lifecycle.test.d.ts.map +1 -0
  48. package/dist/__tests__/realtime-review-lifecycle.test.js +154 -0
  49. package/dist/__tests__/realtime-review-lifecycle.test.js.map +1 -0
  50. package/dist/__tests__/realtime-session-cancel-usage.test.d.ts +2 -0
  51. package/dist/__tests__/realtime-session-cancel-usage.test.d.ts.map +1 -0
  52. package/dist/__tests__/realtime-session-cancel-usage.test.js +230 -0
  53. package/dist/__tests__/realtime-session-cancel-usage.test.js.map +1 -0
  54. package/dist/__tests__/realtime-session-channels.test.d.ts +2 -0
  55. package/dist/__tests__/realtime-session-channels.test.d.ts.map +1 -0
  56. package/dist/__tests__/realtime-session-channels.test.js +252 -0
  57. package/dist/__tests__/realtime-session-channels.test.js.map +1 -0
  58. package/dist/__tests__/realtime-session-client-tools.test.d.ts +2 -0
  59. package/dist/__tests__/realtime-session-client-tools.test.d.ts.map +1 -0
  60. package/dist/__tests__/realtime-session-client-tools.test.js +103 -0
  61. package/dist/__tests__/realtime-session-client-tools.test.js.map +1 -0
  62. package/dist/__tests__/realtime-session-minimized.test.d.ts +2 -0
  63. package/dist/__tests__/realtime-session-minimized.test.d.ts.map +1 -0
  64. package/dist/__tests__/realtime-session-minimized.test.js +32 -0
  65. package/dist/__tests__/realtime-session-minimized.test.js.map +1 -0
  66. package/dist/__tests__/realtime-session-mint.test.d.ts +2 -0
  67. package/dist/__tests__/realtime-session-mint.test.d.ts.map +1 -0
  68. package/dist/__tests__/realtime-session-mint.test.js +69 -0
  69. package/dist/__tests__/realtime-session-mint.test.js.map +1 -0
  70. package/dist/__tests__/realtime-session-policy.test.d.ts +2 -0
  71. package/dist/__tests__/realtime-session-policy.test.d.ts.map +1 -0
  72. package/dist/__tests__/realtime-session-policy.test.js +303 -0
  73. package/dist/__tests__/realtime-session-policy.test.js.map +1 -0
  74. package/dist/__tests__/realtime-session-review.service.test.d.ts +2 -0
  75. package/dist/__tests__/realtime-session-review.service.test.d.ts.map +1 -0
  76. package/dist/__tests__/realtime-session-review.service.test.js +743 -0
  77. package/dist/__tests__/realtime-session-review.service.test.js.map +1 -0
  78. package/dist/__tests__/realtime-session-state.test.d.ts +2 -0
  79. package/dist/__tests__/realtime-session-state.test.d.ts.map +1 -0
  80. package/dist/__tests__/realtime-session-state.test.js +83 -0
  81. package/dist/__tests__/realtime-session-state.test.js.map +1 -0
  82. package/dist/__tests__/realtime-session-timeline-card.test.d.ts +2 -0
  83. package/dist/__tests__/realtime-session-timeline-card.test.d.ts.map +1 -0
  84. package/dist/__tests__/realtime-session-timeline-card.test.js +106 -0
  85. package/dist/__tests__/realtime-session-timeline-card.test.js.map +1 -0
  86. package/dist/__tests__/realtime-session-timeline.test.d.ts +2 -0
  87. package/dist/__tests__/realtime-session-timeline.test.d.ts.map +1 -0
  88. package/dist/__tests__/realtime-session-timeline.test.js +142 -0
  89. package/dist/__tests__/realtime-session-timeline.test.js.map +1 -0
  90. package/dist/__tests__/realtime-sessions-adapter.test.d.ts +19 -0
  91. package/dist/__tests__/realtime-sessions-adapter.test.d.ts.map +1 -0
  92. package/dist/__tests__/realtime-sessions-adapter.test.js +188 -0
  93. package/dist/__tests__/realtime-sessions-adapter.test.js.map +1 -0
  94. package/dist/__tests__/realtime-surface-panel-prefs.test.d.ts +2 -0
  95. package/dist/__tests__/realtime-surface-panel-prefs.test.d.ts.map +1 -0
  96. package/dist/__tests__/realtime-surface-panel-prefs.test.js +100 -0
  97. package/dist/__tests__/realtime-surface-panel-prefs.test.js.map +1 -0
  98. package/dist/__tests__/realtime-surface-tabs-model.test.d.ts +2 -0
  99. package/dist/__tests__/realtime-surface-tabs-model.test.d.ts.map +1 -0
  100. package/dist/__tests__/realtime-surface-tabs-model.test.js +193 -0
  101. package/dist/__tests__/realtime-surface-tabs-model.test.js.map +1 -0
  102. package/dist/__tests__/remote-browser-audio-player.test.d.ts +2 -0
  103. package/dist/__tests__/remote-browser-audio-player.test.d.ts.map +1 -0
  104. package/dist/__tests__/remote-browser-audio-player.test.js +137 -0
  105. package/dist/__tests__/remote-browser-audio-player.test.js.map +1 -0
  106. package/dist/__tests__/remote-browser-channel.test.d.ts +2 -0
  107. package/dist/__tests__/remote-browser-channel.test.d.ts.map +1 -0
  108. package/dist/__tests__/remote-browser-channel.test.js +423 -0
  109. package/dist/__tests__/remote-browser-channel.test.js.map +1 -0
  110. package/dist/__tests__/slot-defaults.test.d.ts +24 -0
  111. package/dist/__tests__/slot-defaults.test.d.ts.map +1 -0
  112. package/dist/__tests__/slot-defaults.test.js +63 -0
  113. package/dist/__tests__/slot-defaults.test.js.map +1 -0
  114. package/dist/__tests__/user-authorization.test.d.ts +2 -0
  115. package/dist/__tests__/user-authorization.test.d.ts.map +1 -0
  116. package/dist/__tests__/user-authorization.test.js +97 -0
  117. package/dist/__tests__/user-authorization.test.js.map +1 -0
  118. package/dist/__tests__/voice-session-narration.test.d.ts +2 -0
  119. package/dist/__tests__/voice-session-narration.test.d.ts.map +1 -0
  120. package/dist/__tests__/voice-session-narration.test.js +609 -0
  121. package/dist/__tests__/voice-session-narration.test.js.map +1 -0
  122. package/dist/__tests__/whiteboard-artifact-viewer.test.d.ts +2 -0
  123. package/dist/__tests__/whiteboard-artifact-viewer.test.d.ts.map +1 -0
  124. package/dist/__tests__/whiteboard-artifact-viewer.test.js +101 -0
  125. package/dist/__tests__/whiteboard-artifact-viewer.test.js.map +1 -0
  126. package/dist/__tests__/whiteboard-channel.test.d.ts +2 -0
  127. package/dist/__tests__/whiteboard-channel.test.d.ts.map +1 -0
  128. package/dist/__tests__/whiteboard-channel.test.js +260 -0
  129. package/dist/__tests__/whiteboard-channel.test.js.map +1 -0
  130. package/dist/__tests__/whiteboard-restore-state.test.d.ts +2 -0
  131. package/dist/__tests__/whiteboard-restore-state.test.d.ts.map +1 -0
  132. package/dist/__tests__/whiteboard-restore-state.test.js +108 -0
  133. package/dist/__tests__/whiteboard-restore-state.test.js.map +1 -0
  134. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +205 -3
  135. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
  136. package/dist/lib/components/conversation/conversation-chat-area.component.js +911 -342
  137. package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
  138. package/dist/lib/components/mention/mention-dropdown.component.js +35 -17
  139. package/dist/lib/components/mention/mention-dropdown.component.js.map +1 -1
  140. package/dist/lib/components/mention/mention-editor.component.d.ts +4 -0
  141. package/dist/lib/components/mention/mention-editor.component.d.ts.map +1 -1
  142. package/dist/lib/components/mention/mention-editor.component.js +43 -19
  143. package/dist/lib/components/mention/mention-editor.component.js.map +1 -1
  144. package/dist/lib/components/message/message-input-box.component.d.ts +17 -1
  145. package/dist/lib/components/message/message-input-box.component.d.ts.map +1 -1
  146. package/dist/lib/components/message/message-input-box.component.js +73 -15
  147. package/dist/lib/components/message/message-input-box.component.js.map +1 -1
  148. package/dist/lib/components/message/message-input.component.d.ts +142 -6
  149. package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
  150. package/dist/lib/components/message/message-input.component.js +328 -82
  151. package/dist/lib/components/message/message-input.component.js.map +1 -1
  152. package/dist/lib/components/message/message-item.component.d.ts +28 -3
  153. package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
  154. package/dist/lib/components/message/message-item.component.js +180 -108
  155. package/dist/lib/components/message/message-item.component.js.map +1 -1
  156. package/dist/lib/components/message/message-list.component.d.ts +81 -2
  157. package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
  158. package/dist/lib/components/message/message-list.component.js +252 -87
  159. package/dist/lib/components/message/message-list.component.js.map +1 -1
  160. package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts +282 -0
  161. package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts.map +1 -0
  162. package/dist/lib/components/realtime/channels/base-realtime-channel-client.js +158 -0
  163. package/dist/lib/components/realtime/channels/base-realtime-channel-client.js.map +1 -0
  164. package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.d.ts +25 -0
  165. package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.d.ts.map +1 -0
  166. package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.js +140 -0
  167. package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.js.map +1 -0
  168. package/dist/lib/components/realtime/channels/realtime-channel-pane.component.d.ts +35 -0
  169. package/dist/lib/components/realtime/channels/realtime-channel-pane.component.d.ts.map +1 -0
  170. package/dist/lib/components/realtime/channels/realtime-channel-pane.component.js +58 -0
  171. package/dist/lib/components/realtime/channels/realtime-channel-pane.component.js.map +1 -0
  172. package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts +63 -0
  173. package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts.map +1 -0
  174. package/dist/lib/components/realtime/realtime-activity-rail.component.js +260 -0
  175. package/dist/lib/components/realtime/realtime-activity-rail.component.js.map +1 -0
  176. package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts +117 -0
  177. package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts.map +1 -0
  178. package/dist/lib/components/realtime/realtime-agent-banner.component.js +504 -0
  179. package/dist/lib/components/realtime/realtime-agent-banner.component.js.map +1 -0
  180. package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts +168 -0
  181. package/dist/lib/components/realtime/realtime-agent-picker.component.d.ts.map +1 -0
  182. package/dist/lib/components/realtime/realtime-agent-picker.component.js +556 -0
  183. package/dist/lib/components/realtime/realtime-agent-picker.component.js.map +1 -0
  184. package/dist/lib/components/realtime/realtime-audio-visuals.d.ts +97 -0
  185. package/dist/lib/components/realtime/realtime-audio-visuals.d.ts.map +1 -0
  186. package/dist/lib/components/realtime/realtime-audio-visuals.js +139 -0
  187. package/dist/lib/components/realtime/realtime-audio-visuals.js.map +1 -0
  188. package/dist/lib/components/realtime/realtime-channel-strip.component.d.ts +29 -0
  189. package/dist/lib/components/realtime/realtime-channel-strip.component.d.ts.map +1 -0
  190. package/dist/lib/components/realtime/realtime-channel-strip.component.js +69 -0
  191. package/dist/lib/components/realtime/realtime-channel-strip.component.js.map +1 -0
  192. package/dist/lib/components/realtime/realtime-composer.component.d.ts +65 -0
  193. package/dist/lib/components/realtime/realtime-composer.component.d.ts.map +1 -0
  194. package/dist/lib/components/realtime/realtime-composer.component.js +256 -0
  195. package/dist/lib/components/realtime/realtime-composer.component.js.map +1 -0
  196. package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts +71 -0
  197. package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts.map +1 -0
  198. package/dist/lib/components/realtime/realtime-delegation-card.component.js +324 -0
  199. package/dist/lib/components/realtime/realtime-delegation-card.component.js.map +1 -0
  200. package/dist/lib/components/realtime/realtime-disclosure.d.ts +135 -0
  201. package/dist/lib/components/realtime/realtime-disclosure.d.ts.map +1 -0
  202. package/dist/lib/components/realtime/realtime-disclosure.js +188 -0
  203. package/dist/lib/components/realtime/realtime-disclosure.js.map +1 -0
  204. package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts +491 -0
  205. package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts.map +1 -0
  206. package/dist/lib/components/realtime/realtime-session-overlay.component.js +1274 -0
  207. package/dist/lib/components/realtime/realtime-session-overlay.component.js.map +1 -0
  208. package/dist/lib/components/realtime/realtime-session-state.d.ts +191 -0
  209. package/dist/lib/components/realtime/realtime-session-state.d.ts.map +1 -0
  210. package/dist/lib/components/realtime/realtime-session-state.js +244 -0
  211. package/dist/lib/components/realtime/realtime-session-state.js.map +1 -0
  212. package/dist/lib/components/realtime/realtime-session-thread.component.d.ts +56 -0
  213. package/dist/lib/components/realtime/realtime-session-thread.component.d.ts.map +1 -0
  214. package/dist/lib/components/realtime/realtime-session-thread.component.js +246 -0
  215. package/dist/lib/components/realtime/realtime-session-thread.component.js.map +1 -0
  216. package/dist/lib/components/realtime/realtime-session-timeline-card.component.d.ts +51 -0
  217. package/dist/lib/components/realtime/realtime-session-timeline-card.component.d.ts.map +1 -0
  218. package/dist/lib/components/realtime/realtime-session-timeline-card.component.js +193 -0
  219. package/dist/lib/components/realtime/realtime-session-timeline-card.component.js.map +1 -0
  220. package/dist/lib/components/realtime/realtime-surface-panel-prefs.d.ts +77 -0
  221. package/dist/lib/components/realtime/realtime-surface-panel-prefs.d.ts.map +1 -0
  222. package/dist/lib/components/realtime/realtime-surface-panel-prefs.js +114 -0
  223. package/dist/lib/components/realtime/realtime-surface-panel-prefs.js.map +1 -0
  224. package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts +173 -0
  225. package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts.map +1 -0
  226. package/dist/lib/components/realtime/realtime-surface-tabs.component.js +496 -0
  227. package/dist/lib/components/realtime/realtime-surface-tabs.component.js.map +1 -0
  228. package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts +181 -0
  229. package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts.map +1 -0
  230. package/dist/lib/components/realtime/realtime-surface-tabs.model.js +223 -0
  231. package/dist/lib/components/realtime/realtime-surface-tabs.model.js.map +1 -0
  232. package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.d.ts +163 -0
  233. package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.d.ts.map +1 -0
  234. package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.js +309 -0
  235. package/dist/lib/components/realtime/remote-browser/remote-browser-audio-player.js.map +1 -0
  236. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts +168 -0
  237. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts.map +1 -0
  238. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js +524 -0
  239. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js.map +1 -0
  240. package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.d.ts +346 -0
  241. package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.d.ts.map +1 -0
  242. package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.js +851 -0
  243. package/dist/lib/components/realtime/remote-browser/remote-browser-surface.component.js.map +1 -0
  244. package/dist/lib/components/realtime/remote-browser/remote-browser-tools.d.ts +86 -0
  245. package/dist/lib/components/realtime/remote-browser/remote-browser-tools.d.ts.map +1 -0
  246. package/dist/lib/components/realtime/remote-browser/remote-browser-tools.js +210 -0
  247. package/dist/lib/components/realtime/remote-browser/remote-browser-tools.js.map +1 -0
  248. package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.d.ts +48 -0
  249. package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.d.ts.map +1 -0
  250. package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.js +180 -0
  251. package/dist/lib/components/realtime/whiteboard/whiteboard-artifact-viewer.component.js.map +1 -0
  252. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts +119 -0
  253. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts.map +1 -0
  254. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js +274 -0
  255. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js.map +1 -0
  256. package/dist/lib/components/slots/mj-chat-agent-presence-default.component.d.ts +11 -0
  257. package/dist/lib/components/slots/mj-chat-agent-presence-default.component.d.ts.map +1 -0
  258. package/dist/lib/components/slots/mj-chat-agent-presence-default.component.js +98 -0
  259. package/dist/lib/components/slots/mj-chat-agent-presence-default.component.js.map +1 -0
  260. package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.d.ts +9 -0
  261. package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.d.ts.map +1 -0
  262. package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.js +35 -0
  263. package/dist/lib/components/slots/mj-chat-demonstration-surface-default.component.js.map +1 -0
  264. package/dist/lib/components/slots/mj-chat-empty-state-default.component.d.ts +28 -0
  265. package/dist/lib/components/slots/mj-chat-empty-state-default.component.d.ts.map +1 -0
  266. package/dist/lib/components/slots/mj-chat-empty-state-default.component.js +104 -0
  267. package/dist/lib/components/slots/mj-chat-empty-state-default.component.js.map +1 -0
  268. package/dist/lib/components/slots/mj-chat-header-default.component.d.ts +11 -0
  269. package/dist/lib/components/slots/mj-chat-header-default.component.d.ts.map +1 -0
  270. package/dist/lib/components/slots/mj-chat-header-default.component.js +103 -0
  271. package/dist/lib/components/slots/mj-chat-header-default.component.js.map +1 -0
  272. package/dist/lib/components/slots/mj-chat-message-bubble-default.component.d.ts +15 -0
  273. package/dist/lib/components/slots/mj-chat-message-bubble-default.component.d.ts.map +1 -0
  274. package/dist/lib/components/slots/mj-chat-message-bubble-default.component.js +73 -0
  275. package/dist/lib/components/slots/mj-chat-message-bubble-default.component.js.map +1 -0
  276. package/dist/lib/components/slots/mj-chat-message-extra-default.component.d.ts +9 -0
  277. package/dist/lib/components/slots/mj-chat-message-extra-default.component.d.ts.map +1 -0
  278. package/dist/lib/components/slots/mj-chat-message-extra-default.component.js +34 -0
  279. package/dist/lib/components/slots/mj-chat-message-extra-default.component.js.map +1 -0
  280. package/dist/lib/components/slots/slot-interfaces.d.ts +95 -0
  281. package/dist/lib/components/slots/slot-interfaces.d.ts.map +1 -0
  282. package/dist/lib/components/slots/slot-interfaces.js +18 -0
  283. package/dist/lib/components/slots/slot-interfaces.js.map +1 -0
  284. package/dist/lib/components/workspace/conversation-workspace.component.d.ts +11 -0
  285. package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
  286. package/dist/lib/components/workspace/conversation-workspace.component.js +28 -4
  287. package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
  288. package/dist/lib/conversations.module.d.ts +12 -1
  289. package/dist/lib/conversations.module.d.ts.map +1 -1
  290. package/dist/lib/conversations.module.js +93 -5
  291. package/dist/lib/conversations.module.js.map +1 -1
  292. package/dist/lib/directives/chat-slot.directive.d.ts +44 -0
  293. package/dist/lib/directives/chat-slot.directive.d.ts.map +1 -0
  294. package/dist/lib/directives/chat-slot.directive.js +54 -0
  295. package/dist/lib/directives/chat-slot.directive.js.map +1 -0
  296. package/dist/lib/events/chat-events.d.ts +137 -0
  297. package/dist/lib/events/chat-events.d.ts.map +1 -0
  298. package/dist/lib/events/chat-events.js +189 -0
  299. package/dist/lib/events/chat-events.js.map +1 -0
  300. package/dist/lib/models/conversation-state.model.d.ts +2 -1
  301. package/dist/lib/models/conversation-state.model.d.ts.map +1 -1
  302. package/dist/lib/models/conversation-state.model.js.map +1 -1
  303. package/dist/lib/services/artifact-state.service.d.ts.map +1 -1
  304. package/dist/lib/services/artifact-state.service.js +23 -6
  305. package/dist/lib/services/artifact-state.service.js.map +1 -1
  306. package/dist/lib/services/conversation-agent.service.d.ts +60 -74
  307. package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
  308. package/dist/lib/services/conversation-agent.service.js +100 -313
  309. package/dist/lib/services/conversation-agent.service.js.map +1 -1
  310. package/dist/lib/services/conversation-bridge.service.d.ts +11 -70
  311. package/dist/lib/services/conversation-bridge.service.d.ts.map +1 -1
  312. package/dist/lib/services/conversation-bridge.service.js +51 -85
  313. package/dist/lib/services/conversation-bridge.service.js.map +1 -1
  314. package/dist/lib/services/conversation-naming.d.ts +63 -0
  315. package/dist/lib/services/conversation-naming.d.ts.map +1 -0
  316. package/dist/lib/services/conversation-naming.js +58 -0
  317. package/dist/lib/services/conversation-naming.js.map +1 -0
  318. package/dist/lib/services/conversation-streaming.service.d.ts +24 -154
  319. package/dist/lib/services/conversation-streaming.service.d.ts.map +1 -1
  320. package/dist/lib/services/conversation-streaming.service.js +39 -361
  321. package/dist/lib/services/conversation-streaming.service.js.map +1 -1
  322. package/dist/lib/services/conversations-runtime-bootstrap.service.d.ts +10 -0
  323. package/dist/lib/services/conversations-runtime-bootstrap.service.d.ts.map +1 -0
  324. package/dist/lib/services/conversations-runtime-bootstrap.service.js +104 -0
  325. package/dist/lib/services/conversations-runtime-bootstrap.service.js.map +1 -0
  326. package/dist/lib/services/delegation-result-parser.d.ts +45 -0
  327. package/dist/lib/services/delegation-result-parser.d.ts.map +1 -0
  328. package/dist/lib/services/delegation-result-parser.js +48 -0
  329. package/dist/lib/services/delegation-result-parser.js.map +1 -0
  330. package/dist/lib/services/mention-autocomplete.service.d.ts +19 -4
  331. package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -1
  332. package/dist/lib/services/mention-autocomplete.service.js +65 -4
  333. package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
  334. package/dist/lib/services/mention-parser.service.d.ts +8 -53
  335. package/dist/lib/services/mention-parser.service.d.ts.map +1 -1
  336. package/dist/lib/services/mention-parser.service.js +32 -243
  337. package/dist/lib/services/mention-parser.service.js.map +1 -1
  338. package/dist/lib/services/narration-template.d.ts +42 -0
  339. package/dist/lib/services/narration-template.d.ts.map +1 -0
  340. package/dist/lib/services/narration-template.js +73 -0
  341. package/dist/lib/services/narration-template.js.map +1 -0
  342. package/dist/lib/services/realtime-pairing.d.ts +120 -0
  343. package/dist/lib/services/realtime-pairing.d.ts.map +1 -0
  344. package/dist/lib/services/realtime-pairing.js +150 -0
  345. package/dist/lib/services/realtime-pairing.js.map +1 -0
  346. package/dist/lib/services/realtime-session-review.service.d.ts +233 -0
  347. package/dist/lib/services/realtime-session-review.service.d.ts.map +1 -0
  348. package/dist/lib/services/realtime-session-review.service.js +417 -0
  349. package/dist/lib/services/realtime-session-review.service.js.map +1 -0
  350. package/dist/lib/services/realtime-session.service.d.ts +739 -0
  351. package/dist/lib/services/realtime-session.service.d.ts.map +1 -0
  352. package/dist/lib/services/realtime-session.service.js +1647 -0
  353. package/dist/lib/services/realtime-session.service.js.map +1 -0
  354. package/dist/lib/services/realtime-sessions-adapter.d.ts +54 -0
  355. package/dist/lib/services/realtime-sessions-adapter.d.ts.map +1 -0
  356. package/dist/lib/services/realtime-sessions-adapter.js +154 -0
  357. package/dist/lib/services/realtime-sessions-adapter.js.map +1 -0
  358. package/dist/lib/services/user-authorization.d.ts +67 -0
  359. package/dist/lib/services/user-authorization.d.ts.map +1 -0
  360. package/dist/lib/services/user-authorization.js +66 -0
  361. package/dist/lib/services/user-authorization.js.map +1 -0
  362. package/dist/lib/utils/realtime-session-timeline.d.ts +84 -0
  363. package/dist/lib/utils/realtime-session-timeline.d.ts.map +1 -0
  364. package/dist/lib/utils/realtime-session-timeline.js +94 -0
  365. package/dist/lib/utils/realtime-session-timeline.js.map +1 -0
  366. package/dist/public-api.d.ts +41 -0
  367. package/dist/public-api.d.ts.map +1 -1
  368. package/dist/public-api.js +50 -0
  369. package/dist/public-api.js.map +1 -1
  370. package/package.json +27 -24
  371. package/dist/__tests__/conversation-bridge.service.test.d.ts +0 -2
  372. package/dist/__tests__/conversation-bridge.service.test.d.ts.map +0 -1
  373. package/dist/__tests__/conversation-bridge.service.test.js +0 -98
  374. package/dist/__tests__/conversation-bridge.service.test.js.map +0 -1
  375. package/dist/__tests__/mention-parser.test.d.ts +0 -2
  376. package/dist/__tests__/mention-parser.test.d.ts.map +0 -1
  377. package/dist/__tests__/mention-parser.test.js +0 -154
  378. package/dist/__tests__/mention-parser.test.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-review-lifecycle.test.js","sourceRoot":"","sources":["../../src/__tests__/realtime-review-lifecycle.test.ts"],"names":[],"mappings":"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","sourcesContent":["// Angular components in this package are partial-compiled — load the JIT compiler first\n// (same convention as the other component suites in this node test environment).\nimport '@angular/compiler';\nimport { describe, it, expect, vi } from 'vitest';\nimport { ConversationChatAreaComponent } from '../lib/components/conversation/conversation-chat-area.component';\nimport { RealtimeSessionReview } from '../lib/services/realtime-session-review.service';\n\n/**\n * SESSION-REVIEW overlay lifecycle on the chat area — every path that must CLEAR (or\n * refuse to host) a review so the overlay can never go stale:\n *\n * 1. Changing the active conversation clears any hosted review (the original stuck\n * repro: an old session opened in review mode survived conversation switches).\n * 2. The overlay's Close handler clears.\n * 3. The async open path discards stale loads: a live call (before or during the\n * load) wins; a conversation switch mid-load discards the review UNLESS the\n * review belongs to the newly active conversation (the deep-link race).\n *\n * Class-level tests without TestBed: the component is created via\n * `Object.create(prototype)` (skipping ctor/inject) and the few collaborators the\n * review paths touch are stubbed directly.\n */\n\nfunction review(overrides: Partial<RealtimeSessionReview> = {}): RealtimeSessionReview {\n return {\n SessionID: 'SESSION-1',\n AgentID: 'AGENT-1',\n AgentName: 'Voice Co-Agent',\n TargetAgentID: 'AGENT-1',\n ConversationID: 'CONV-A',\n Status: 'Closed',\n CloseReason: 'Explicit',\n StartedAt: new Date(2026, 5, 1, 10, 0, 0),\n LastActiveAt: null,\n ClosedAt: new Date(2026, 5, 1, 10, 10, 0),\n Turns: [],\n DelegatedRuns: [],\n ChannelStates: [],\n Legs: [],\n Artifacts: [],\n ...overrides\n };\n}\n\ninterface HarnessStubs {\n voiceSession: { IsActive: boolean };\n loadSessionReview: ReturnType<typeof vi.fn>;\n detectChanges: ReturnType<typeof vi.fn>;\n}\n\n/** Bare component over the real prototype: real lifecycle methods, stubbed collaborators. */\nfunction createHarness(): { component: ConversationChatAreaComponent; stubs: HarnessStubs } {\n const component = Object.create(ConversationChatAreaComponent.prototype) as ConversationChatAreaComponent;\n const stubs: HarnessStubs = {\n voiceSession: { IsActive: false },\n loadSessionReview: vi.fn(),\n detectChanges: vi.fn()\n };\n const open = component as unknown as Record<string, unknown>;\n open['RealtimeReview'] = null;\n open['_conversationId'] = null;\n open['isInitialized'] = false; // keep the setter from invoking the heavy load path\n open['Provider'] = {}; // ProviderToUse resolves to this stub, never the global Metadata.Provider\n open['RealtimeSession'] = stubs.voiceSession;\n open['realtimeReviewService'] = { LoadSessionReview: stubs.loadSessionReview };\n open['cdr'] = { detectChanges: stubs.detectChanges };\n return { component, stubs };\n}\n\ndescribe('Conversation change clears the session review (the stuck-overlay repro)', () => {\n it('clears a hosted review when the conversationId input changes', () => {\n const { component } = createHarness();\n component.conversationId = 'CONV-A';\n component.RealtimeReview = review();\n\n component.conversationId = 'CONV-B';\n\n expect(component.RealtimeReview).toBeNull();\n });\n\n it('clears a hosted review when the conversation changes to NULL (no selection)', () => {\n const { component } = createHarness();\n component.conversationId = 'CONV-A';\n component.RealtimeReview = review();\n\n component.conversationId = null;\n\n expect(component.RealtimeReview).toBeNull();\n });\n\n it('keeps the review when the input re-fires with the SAME conversation', () => {\n const { component } = createHarness();\n component.conversationId = 'CONV-A';\n const hosted = review();\n component.RealtimeReview = hosted;\n\n component.conversationId = 'CONV-A';\n\n expect(component.RealtimeReview).toBe(hosted);\n });\n});\n\ndescribe('Overlay Close + explicit clear', () => {\n it('onReviewClosed (the overlay ReviewClosed output) clears the review', () => {\n const { component } = createHarness();\n component.RealtimeReview = review();\n\n component.onReviewClosed();\n\n expect(component.RealtimeReview).toBeNull();\n });\n\n it('ClearRealtimeSessionReview is a safe no-op when nothing is hosted', () => {\n const { component } = createHarness();\n expect(() => component.ClearRealtimeSessionReview()).not.toThrow();\n expect(component.RealtimeReview).toBeNull();\n });\n});\n\ndescribe('OpenRealtimeSessionReview — hosting and staleness guards', () => {\n it('hosts the loaded review for the current conversation and reports success', async () => {\n const { component, stubs } = createHarness();\n component.conversationId = 'CONV-A';\n stubs.loadSessionReview.mockResolvedValue(review({ ConversationID: 'CONV-A' }));\n\n const opened = await component.OpenRealtimeSessionReview('SESSION-1');\n\n expect(opened).toBe(true);\n expect(component.RealtimeReview?.SessionID).toBe('SESSION-1');\n expect(stubs.detectChanges).toHaveBeenCalled();\n });\n\n it('refuses while a LIVE call is active (never fights the live overlay) and does not even load', async () => {\n const { component, stubs } = createHarness();\n stubs.voiceSession.IsActive = true;\n\n const opened = await component.OpenRealtimeSessionReview('SESSION-1');\n\n expect(opened).toBe(false);\n expect(stubs.loadSessionReview).not.toHaveBeenCalled();\n expect(component.RealtimeReview).toBeNull();\n });\n\n it('reports failure (and hosts nothing) when the session cannot be loaded', async () => {\n const { component, stubs } = createHarness();\n stubs.loadSessionReview.mockResolvedValue(null);\n\n const opened = await component.OpenRealtimeSessionReview('SESSION-1');\n\n expect(opened).toBe(false);\n expect(component.RealtimeReview).toBeNull();\n });\n\n it('discards a load that resolves AFTER a live call started (the call wins)', async () => {\n const { component, stubs } = createHarness();\n stubs.loadSessionReview.mockImplementation(async () => {\n stubs.voiceSession.IsActive = true; // call starts while the review loads\n return review();\n });\n\n const opened = await component.OpenRealtimeSessionReview('SESSION-1');\n\n expect(opened).toBe(false);\n expect(component.RealtimeReview).toBeNull();\n });\n\n it('discards a load that resolves after the user switched to a DIFFERENT conversation', async () => {\n const { component, stubs } = createHarness();\n component.conversationId = 'CONV-A';\n stubs.loadSessionReview.mockImplementation(async () => {\n component.conversationId = 'CONV-B'; // user moved on mid-load\n return review({ ConversationID: 'CONV-A' });\n });\n\n const opened = await component.OpenRealtimeSessionReview('SESSION-1');\n\n expect(opened).toBe(false);\n expect(component.RealtimeReview).toBeNull();\n });\n\n it('still hosts when the conversation switched mid-load but the review BELONGS to the new one (deep-link race)', async () => {\n const { component, stubs } = createHarness();\n component.conversationId = 'CONV-A';\n stubs.loadSessionReview.mockImplementation(async () => {\n component.conversationId = 'CONV-B'; // deep link selected the session's own conversation\n return review({ ConversationID: 'conv-b' }); // case-insensitive UUID compare\n });\n\n const opened = await component.OpenRealtimeSessionReview('SESSION-1');\n\n expect(opened).toBe(true);\n expect(component.RealtimeReview?.ConversationID).toBe('conv-b');\n });\n});\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=realtime-session-cancel-usage.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-session-cancel-usage.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/realtime-session-cancel-usage.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,230 @@
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
2
+ import { RealtimeSessionService } from '../lib/services/realtime-session.service';
3
+ function internals(service) {
4
+ return service;
5
+ }
6
+ function collectResults(service) {
7
+ const results = [];
8
+ service.DelegationResult$.subscribe(r => results.push(r));
9
+ return results;
10
+ }
11
+ describe('RealtimeSessionService — CancelDelegation (per-card ✕, explicit user intent)', () => {
12
+ let service;
13
+ let executeGQL;
14
+ beforeEach(() => {
15
+ service = new RealtimeSessionService();
16
+ executeGQL = vi.fn(async () => ({ CancelRealtimeSessionTool: { AbortedCount: 1, Success: true } }));
17
+ service.Provider = { ExecuteGQL: executeGQL };
18
+ internals(service).agentSessionId = 'sess-1';
19
+ internals(service).inFlightCallIds.add('call-1');
20
+ });
21
+ it('calls the CancelRealtimeSessionTool mutation with the session + call id, selecting the structured result', async () => {
22
+ await service.CancelDelegation('call-1');
23
+ expect(executeGQL).toHaveBeenCalledTimes(1);
24
+ expect(executeGQL).toHaveBeenCalledWith(expect.stringContaining('CancelRealtimeSessionTool'), { agentSessionId: 'sess-1', callId: 'call-1' });
25
+ // The mutation document selects the structured result fields (server contract).
26
+ const mutation = executeGQL.mock.calls[0][0];
27
+ expect(mutation).toContain('AbortedCount');
28
+ expect(mutation).toContain('Success');
29
+ expect(mutation).toContain('ErrorMessage');
30
+ });
31
+ it('flips the card to a FAILED "Cancelled by user" result when the server aborted the run', async () => {
32
+ const results = collectResults(service);
33
+ const ok = await service.CancelDelegation('call-1');
34
+ expect(ok).toBe(true);
35
+ expect(results).toEqual([{ CallID: 'call-1', Success: false, Output: 'Cancelled by user' }]);
36
+ expect(internals(service).inFlightCallIds.has('call-1')).toBe(false);
37
+ });
38
+ it('suppresses the aborted run\'s LATE real result so it cannot overwrite the cancelled card', async () => {
39
+ const results = collectResults(service);
40
+ await service.CancelDelegation('call-1');
41
+ // The original ExecuteRealtimeSessionTool mutation eventually resolves with the aborted outcome.
42
+ internals(service).emitDelegationResult('call-1', JSON.stringify({ success: false, error: 'Delegation failed: aborted' }));
43
+ expect(results).toHaveLength(1); // only the "Cancelled by user" card flip
44
+ expect(internals(service).cancelledCallIds.size).toBe(0); // the suppression marker was consumed
45
+ });
46
+ it('a DIFFERENT call\'s result still emits normally after a cancel', async () => {
47
+ const results = collectResults(service);
48
+ internals(service).inFlightCallIds.add('call-2');
49
+ await service.CancelDelegation('call-1');
50
+ internals(service).emitDelegationResult('call-2', JSON.stringify({ success: true, output: 'done' }));
51
+ expect(results).toHaveLength(2);
52
+ expect(results[1]).toMatchObject({ CallID: 'call-2', Success: true, Output: 'done' });
53
+ });
54
+ it('returns false WITHOUT flipping the card when the server had nothing to abort (result is racing in)', async () => {
55
+ executeGQL.mockResolvedValue({ CancelRealtimeSessionTool: { AbortedCount: 0, Success: true } });
56
+ const results = collectResults(service);
57
+ const ok = await service.CancelDelegation('call-1');
58
+ expect(ok).toBe(false);
59
+ expect(results).toEqual([]);
60
+ // The call stays tracked so the racing real result is processed normally.
61
+ expect(internals(service).inFlightCallIds.has('call-1')).toBe(true);
62
+ });
63
+ it('treats a STRUCTURED failure (Success: false) as nothing aborted — card untouched', async () => {
64
+ executeGQL.mockResolvedValue({
65
+ CancelRealtimeSessionTool: { AbortedCount: 0, Success: false, ErrorMessage: 'registry exploded' },
66
+ });
67
+ const results = collectResults(service);
68
+ const ok = await service.CancelDelegation('call-1');
69
+ expect(ok).toBe(false);
70
+ expect(results).toEqual([]);
71
+ expect(internals(service).inFlightCallIds.has('call-1')).toBe(true);
72
+ });
73
+ it('is a no-op (no mutation) for a call id that is not tracked in flight', async () => {
74
+ const ok = await service.CancelDelegation('call-unknown');
75
+ expect(ok).toBe(false);
76
+ expect(executeGQL).not.toHaveBeenCalled();
77
+ });
78
+ it('is a no-op (no mutation) when no session is open', async () => {
79
+ internals(service).agentSessionId = null;
80
+ const ok = await service.CancelDelegation('call-1');
81
+ expect(ok).toBe(false);
82
+ expect(executeGQL).not.toHaveBeenCalled();
83
+ });
84
+ it('tolerates a failed mutation (false, logged, card untouched)', async () => {
85
+ executeGQL.mockRejectedValue(new Error('network down'));
86
+ const results = collectResults(service);
87
+ const ok = await service.CancelDelegation('call-1');
88
+ expect(ok).toBe(false);
89
+ expect(results).toEqual([]);
90
+ });
91
+ });
92
+ describe('RealtimeSessionService — CancelInFlightDelegations (sweep cancel)', () => {
93
+ let service;
94
+ let executeGQL;
95
+ beforeEach(() => {
96
+ service = new RealtimeSessionService();
97
+ executeGQL = vi.fn(async () => ({ CancelRealtimeSessionTool: { AbortedCount: 2, Success: true } }));
98
+ service.Provider = { ExecuteGQL: executeGQL };
99
+ internals(service).agentSessionId = 'sess-1';
100
+ });
101
+ it('cancels ALL tracked in-flight calls with ONE callId-less mutation and flips every card', async () => {
102
+ internals(service).inFlightCallIds.add('call-1');
103
+ internals(service).inFlightCallIds.add('call-2');
104
+ const results = collectResults(service);
105
+ const count = await service.CancelInFlightDelegations();
106
+ expect(count).toBe(2);
107
+ expect(executeGQL).toHaveBeenCalledTimes(1);
108
+ expect(executeGQL).toHaveBeenCalledWith(expect.stringContaining('CancelRealtimeSessionTool'), { agentSessionId: 'sess-1', callId: null });
109
+ expect(results.map(r => r.CallID).sort()).toEqual(['call-1', 'call-2']);
110
+ expect(results.every(r => !r.Success && r.Output === 'Cancelled by user')).toBe(true);
111
+ expect(internals(service).inFlightCallIds.size).toBe(0);
112
+ });
113
+ it('returns 0 without a mutation when nothing is tracked in flight', async () => {
114
+ const count = await service.CancelInFlightDelegations();
115
+ expect(count).toBe(0);
116
+ expect(executeGQL).not.toHaveBeenCalled();
117
+ });
118
+ it('returns 0 (cards untouched) when the server aborted nothing', async () => {
119
+ executeGQL.mockResolvedValue({ CancelRealtimeSessionTool: { AbortedCount: 0, Success: true } });
120
+ internals(service).inFlightCallIds.add('call-1');
121
+ const results = collectResults(service);
122
+ const count = await service.CancelInFlightDelegations();
123
+ expect(count).toBe(0);
124
+ expect(results).toEqual([]);
125
+ expect(internals(service).inFlightCallIds.has('call-1')).toBe(true);
126
+ });
127
+ });
128
+ describe('RealtimeSessionService — usage telemetry relay (OnUsage → RelayRealtimeUsage)', () => {
129
+ let service;
130
+ let executeGQL;
131
+ beforeEach(() => {
132
+ vi.useFakeTimers();
133
+ service = new RealtimeSessionService();
134
+ executeGQL = vi.fn(async () => ({ RelayRealtimeUsage: true }));
135
+ service.Provider = { ExecuteGQL: executeGQL };
136
+ internals(service).agentSessionId = 'sess-1';
137
+ });
138
+ afterEach(() => {
139
+ vi.useRealTimers();
140
+ });
141
+ it('accumulates deltas and relays ONE debounced mutation (~10s) with the summed totals', async () => {
142
+ internals(service).onUsageDelta({ InputTokens: 100, OutputTokens: 10 });
143
+ internals(service).onUsageDelta({ InputTokens: 20, OutputTokens: 5 });
144
+ await vi.advanceTimersByTimeAsync(9999);
145
+ expect(executeGQL).not.toHaveBeenCalled();
146
+ await vi.advanceTimersByTimeAsync(1);
147
+ expect(executeGQL).toHaveBeenCalledTimes(1);
148
+ expect(executeGQL).toHaveBeenCalledWith(expect.stringContaining('RelayRealtimeUsage'), { agentSessionId: 'sess-1', inputTokens: 120, outputTokens: 15 });
149
+ // Accumulators were consumed by the flush.
150
+ expect(internals(service).pendingUsageInput).toBe(0);
151
+ expect(internals(service).pendingUsageOutput).toBe(0);
152
+ });
153
+ it('ignores all-zero / negative / missing deltas (no timer armed, no mutation)', async () => {
154
+ internals(service).onUsageDelta({});
155
+ internals(service).onUsageDelta({ InputTokens: -5, OutputTokens: 0 });
156
+ internals(service).onUsageDelta({ InputTokens: Number.NaN });
157
+ await vi.advanceTimersByTimeAsync(60000);
158
+ expect(executeGQL).not.toHaveBeenCalled();
159
+ expect(internals(service).usageFlushTimer).toBeNull();
160
+ });
161
+ it('clamps a negative component while accumulating the positive one', async () => {
162
+ internals(service).onUsageDelta({ InputTokens: -3, OutputTokens: 7 });
163
+ await vi.advanceTimersByTimeAsync(10000);
164
+ expect(executeGQL).toHaveBeenCalledWith(expect.stringContaining('RelayRealtimeUsage'), { agentSessionId: 'sess-1', inputTokens: 0, outputTokens: 7 });
165
+ });
166
+ it('re-accumulates on a failed relay so the next flush retries the same deltas', async () => {
167
+ executeGQL.mockRejectedValueOnce(new Error('network down'));
168
+ internals(service).onUsageDelta({ InputTokens: 50, OutputTokens: 5 });
169
+ await vi.advanceTimersByTimeAsync(10000); // flush fails
170
+ expect(internals(service).pendingUsageInput).toBe(50);
171
+ expect(internals(service).pendingUsageOutput).toBe(5);
172
+ internals(service).onUsageDelta({ InputTokens: 10, OutputTokens: 0 });
173
+ await vi.advanceTimersByTimeAsync(10000); // retry carries the combined totals
174
+ expect(executeGQL).toHaveBeenLastCalledWith(expect.stringContaining('RelayRealtimeUsage'), { agentSessionId: 'sess-1', inputTokens: 60, outputTokens: 5 });
175
+ });
176
+ it('flushes the unrelayed remainder at teardown BEFORE closing the server session (no debounce wait)', async () => {
177
+ internals(service).onUsageDelta({ InputTokens: 33, OutputTokens: 4 });
178
+ await internals(service).teardown(true);
179
+ const mutations = executeGQL.mock.calls.map(c => c[0]);
180
+ const usageIdx = mutations.findIndex(m => m.includes('RelayRealtimeUsage'));
181
+ const closeIdx = mutations.findIndex(m => m.includes('CloseAgentSession'));
182
+ expect(usageIdx).toBeGreaterThanOrEqual(0);
183
+ expect(closeIdx).toBeGreaterThan(usageIdx); // usage flushed first, then close
184
+ expect(executeGQL.mock.calls[usageIdx][1]).toEqual({ agentSessionId: 'sess-1', inputTokens: 33, outputTokens: 4 });
185
+ // Teardown leaves the relay reset (no dangling timer / pending totals).
186
+ expect(internals(service).usageFlushTimer).toBeNull();
187
+ expect(internals(service).pendingUsageInput).toBe(0);
188
+ expect(internals(service).pendingUsageOutput).toBe(0);
189
+ });
190
+ it('teardown without pending usage relays nothing (no spurious zero-delta mutation)', async () => {
191
+ await internals(service).teardown(true);
192
+ const mutations = executeGQL.mock.calls.map(c => c[0]);
193
+ expect(mutations.some(m => m.includes('RelayRealtimeUsage'))).toBe(false);
194
+ expect(mutations.some(m => m.includes('CloseAgentSession'))).toBe(true);
195
+ });
196
+ it('flushPendingUsage is a no-op without a session id', async () => {
197
+ internals(service).agentSessionId = null;
198
+ internals(service).onUsageDelta({ InputTokens: 9 });
199
+ await internals(service).flushPendingUsage();
200
+ expect(executeGQL).not.toHaveBeenCalled();
201
+ // The deltas survive for a later flush once a session id exists.
202
+ expect(internals(service).pendingUsageInput).toBe(9);
203
+ });
204
+ });
205
+ describe('RealtimeSessionService — OnUsage wiring through wireClientHandlers', () => {
206
+ /** Fake client capturing every handler wireClientHandlers registers. */
207
+ class FakeWiredClient {
208
+ UsageHandler = null;
209
+ OnStateChange() { }
210
+ OnTranscript() { }
211
+ OnToolCall() { }
212
+ OnError() { }
213
+ OnInterruption() { }
214
+ OnUsage(handler) {
215
+ this.UsageHandler = handler;
216
+ }
217
+ }
218
+ it('registers an OnUsage handler that feeds the accumulator', () => {
219
+ const service = new RealtimeSessionService();
220
+ const seam = service;
221
+ seam.agentSessionId = 'sess-1';
222
+ const client = new FakeWiredClient();
223
+ seam.wireClientHandlers(client);
224
+ expect(client.UsageHandler).toBeTypeOf('function');
225
+ client.UsageHandler?.({ InputTokens: 11, OutputTokens: 3 });
226
+ expect(seam.pendingUsageInput).toBe(11);
227
+ expect(seam.pendingUsageOutput).toBe(3);
228
+ });
229
+ });
230
+ //# sourceMappingURL=realtime-session-cancel-usage.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-session-cancel-usage.test.js","sourceRoot":"","sources":["../../src/__tests__/realtime-session-cancel-usage.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';\nimport { IMetadataProvider } from '@memberjunction/core';\nimport { RealtimeClientUsage } from '@memberjunction/ai-realtime-client';\nimport { RealtimeSessionService, VoiceDelegationResult } from '../lib/services/realtime-session.service';\n\n/**\n * The explicit delegation-CANCEL channel ({@link RealtimeSessionService.CancelDelegation} /\n * {@link RealtimeSessionService.CancelInFlightDelegations}) and the usage-telemetry relay\n * (`OnUsage` deltas → debounced `RelayRealtimeUsage` + teardown flush).\n *\n * Same narrow typed-seam approach as the sibling voice-session suites: a fake Provider whose\n * `ExecuteGQL` captures mutations, with private members reached through a typed internals cast.\n */\n\n/** The private surface the tests drive — no `any`, just the members under test. */\ninterface RealtimeSessionCancelUsageInternals {\n agentSessionId: string | null;\n inFlightCallIds: Set<string>;\n cancelledCallIds: Set<string>;\n pendingUsageInput: number;\n pendingUsageOutput: number;\n usageFlushTimer: ReturnType<typeof setTimeout> | null;\n emitDelegationResult(callId: string, resultJson: string): void;\n onUsageDelta(usage: RealtimeClientUsage): void;\n flushPendingUsage(agentSessionId?: string | null): Promise<void>;\n teardown(closeServerSession: boolean): Promise<void>;\n}\n\nfunction internals(service: RealtimeSessionService): RealtimeSessionCancelUsageInternals {\n return service as unknown as RealtimeSessionCancelUsageInternals;\n}\n\nfunction collectResults(service: RealtimeSessionService): VoiceDelegationResult[] {\n const results: VoiceDelegationResult[] = [];\n service.DelegationResult$.subscribe(r => results.push(r));\n return results;\n}\n\ndescribe('RealtimeSessionService — CancelDelegation (per-card ✕, explicit user intent)', () => {\n let service: RealtimeSessionService;\n let executeGQL: ReturnType<typeof vi.fn>;\n\n beforeEach(() => {\n service = new RealtimeSessionService();\n executeGQL = vi.fn(async () => ({ CancelRealtimeSessionTool: { AbortedCount: 1, Success: true } }));\n service.Provider = { ExecuteGQL: executeGQL } as unknown as IMetadataProvider;\n internals(service).agentSessionId = 'sess-1';\n internals(service).inFlightCallIds.add('call-1');\n });\n\n it('calls the CancelRealtimeSessionTool mutation with the session + call id, selecting the structured result', async () => {\n await service.CancelDelegation('call-1');\n\n expect(executeGQL).toHaveBeenCalledTimes(1);\n expect(executeGQL).toHaveBeenCalledWith(\n expect.stringContaining('CancelRealtimeSessionTool'),\n { agentSessionId: 'sess-1', callId: 'call-1' }\n );\n // The mutation document selects the structured result fields (server contract).\n const mutation = executeGQL.mock.calls[0][0] as string;\n expect(mutation).toContain('AbortedCount');\n expect(mutation).toContain('Success');\n expect(mutation).toContain('ErrorMessage');\n });\n\n it('flips the card to a FAILED \"Cancelled by user\" result when the server aborted the run', async () => {\n const results = collectResults(service);\n\n const ok = await service.CancelDelegation('call-1');\n\n expect(ok).toBe(true);\n expect(results).toEqual([{ CallID: 'call-1', Success: false, Output: 'Cancelled by user' }]);\n expect(internals(service).inFlightCallIds.has('call-1')).toBe(false);\n });\n\n it('suppresses the aborted run\\'s LATE real result so it cannot overwrite the cancelled card', async () => {\n const results = collectResults(service);\n await service.CancelDelegation('call-1');\n\n // The original ExecuteRealtimeSessionTool mutation eventually resolves with the aborted outcome.\n internals(service).emitDelegationResult('call-1', JSON.stringify({ success: false, error: 'Delegation failed: aborted' }));\n\n expect(results).toHaveLength(1); // only the \"Cancelled by user\" card flip\n expect(internals(service).cancelledCallIds.size).toBe(0); // the suppression marker was consumed\n });\n\n it('a DIFFERENT call\\'s result still emits normally after a cancel', async () => {\n const results = collectResults(service);\n internals(service).inFlightCallIds.add('call-2');\n await service.CancelDelegation('call-1');\n\n internals(service).emitDelegationResult('call-2', JSON.stringify({ success: true, output: 'done' }));\n\n expect(results).toHaveLength(2);\n expect(results[1]).toMatchObject({ CallID: 'call-2', Success: true, Output: 'done' });\n });\n\n it('returns false WITHOUT flipping the card when the server had nothing to abort (result is racing in)', async () => {\n executeGQL.mockResolvedValue({ CancelRealtimeSessionTool: { AbortedCount: 0, Success: true } });\n const results = collectResults(service);\n\n const ok = await service.CancelDelegation('call-1');\n\n expect(ok).toBe(false);\n expect(results).toEqual([]);\n // The call stays tracked so the racing real result is processed normally.\n expect(internals(service).inFlightCallIds.has('call-1')).toBe(true);\n });\n\n it('treats a STRUCTURED failure (Success: false) as nothing aborted — card untouched', async () => {\n executeGQL.mockResolvedValue({\n CancelRealtimeSessionTool: { AbortedCount: 0, Success: false, ErrorMessage: 'registry exploded' },\n });\n const results = collectResults(service);\n\n const ok = await service.CancelDelegation('call-1');\n\n expect(ok).toBe(false);\n expect(results).toEqual([]);\n expect(internals(service).inFlightCallIds.has('call-1')).toBe(true);\n });\n\n it('is a no-op (no mutation) for a call id that is not tracked in flight', async () => {\n const ok = await service.CancelDelegation('call-unknown');\n expect(ok).toBe(false);\n expect(executeGQL).not.toHaveBeenCalled();\n });\n\n it('is a no-op (no mutation) when no session is open', async () => {\n internals(service).agentSessionId = null;\n const ok = await service.CancelDelegation('call-1');\n expect(ok).toBe(false);\n expect(executeGQL).not.toHaveBeenCalled();\n });\n\n it('tolerates a failed mutation (false, logged, card untouched)', async () => {\n executeGQL.mockRejectedValue(new Error('network down'));\n const results = collectResults(service);\n\n const ok = await service.CancelDelegation('call-1');\n\n expect(ok).toBe(false);\n expect(results).toEqual([]);\n });\n});\n\ndescribe('RealtimeSessionService — CancelInFlightDelegations (sweep cancel)', () => {\n let service: RealtimeSessionService;\n let executeGQL: ReturnType<typeof vi.fn>;\n\n beforeEach(() => {\n service = new RealtimeSessionService();\n executeGQL = vi.fn(async () => ({ CancelRealtimeSessionTool: { AbortedCount: 2, Success: true } }));\n service.Provider = { ExecuteGQL: executeGQL } as unknown as IMetadataProvider;\n internals(service).agentSessionId = 'sess-1';\n });\n\n it('cancels ALL tracked in-flight calls with ONE callId-less mutation and flips every card', async () => {\n internals(service).inFlightCallIds.add('call-1');\n internals(service).inFlightCallIds.add('call-2');\n const results = collectResults(service);\n\n const count = await service.CancelInFlightDelegations();\n\n expect(count).toBe(2);\n expect(executeGQL).toHaveBeenCalledTimes(1);\n expect(executeGQL).toHaveBeenCalledWith(\n expect.stringContaining('CancelRealtimeSessionTool'),\n { agentSessionId: 'sess-1', callId: null }\n );\n expect(results.map(r => r.CallID).sort()).toEqual(['call-1', 'call-2']);\n expect(results.every(r => !r.Success && r.Output === 'Cancelled by user')).toBe(true);\n expect(internals(service).inFlightCallIds.size).toBe(0);\n });\n\n it('returns 0 without a mutation when nothing is tracked in flight', async () => {\n const count = await service.CancelInFlightDelegations();\n expect(count).toBe(0);\n expect(executeGQL).not.toHaveBeenCalled();\n });\n\n it('returns 0 (cards untouched) when the server aborted nothing', async () => {\n executeGQL.mockResolvedValue({ CancelRealtimeSessionTool: { AbortedCount: 0, Success: true } });\n internals(service).inFlightCallIds.add('call-1');\n const results = collectResults(service);\n\n const count = await service.CancelInFlightDelegations();\n\n expect(count).toBe(0);\n expect(results).toEqual([]);\n expect(internals(service).inFlightCallIds.has('call-1')).toBe(true);\n });\n});\n\ndescribe('RealtimeSessionService — usage telemetry relay (OnUsage → RelayRealtimeUsage)', () => {\n let service: RealtimeSessionService;\n let executeGQL: ReturnType<typeof vi.fn>;\n\n beforeEach(() => {\n vi.useFakeTimers();\n service = new RealtimeSessionService();\n executeGQL = vi.fn(async () => ({ RelayRealtimeUsage: true }));\n service.Provider = { ExecuteGQL: executeGQL } as unknown as IMetadataProvider;\n internals(service).agentSessionId = 'sess-1';\n });\n\n afterEach(() => {\n vi.useRealTimers();\n });\n\n it('accumulates deltas and relays ONE debounced mutation (~10s) with the summed totals', async () => {\n internals(service).onUsageDelta({ InputTokens: 100, OutputTokens: 10 });\n internals(service).onUsageDelta({ InputTokens: 20, OutputTokens: 5 });\n\n await vi.advanceTimersByTimeAsync(9999);\n expect(executeGQL).not.toHaveBeenCalled();\n\n await vi.advanceTimersByTimeAsync(1);\n expect(executeGQL).toHaveBeenCalledTimes(1);\n expect(executeGQL).toHaveBeenCalledWith(\n expect.stringContaining('RelayRealtimeUsage'),\n { agentSessionId: 'sess-1', inputTokens: 120, outputTokens: 15 }\n );\n // Accumulators were consumed by the flush.\n expect(internals(service).pendingUsageInput).toBe(0);\n expect(internals(service).pendingUsageOutput).toBe(0);\n });\n\n it('ignores all-zero / negative / missing deltas (no timer armed, no mutation)', async () => {\n internals(service).onUsageDelta({});\n internals(service).onUsageDelta({ InputTokens: -5, OutputTokens: 0 });\n internals(service).onUsageDelta({ InputTokens: Number.NaN });\n\n await vi.advanceTimersByTimeAsync(60000);\n expect(executeGQL).not.toHaveBeenCalled();\n expect(internals(service).usageFlushTimer).toBeNull();\n });\n\n it('clamps a negative component while accumulating the positive one', async () => {\n internals(service).onUsageDelta({ InputTokens: -3, OutputTokens: 7 });\n\n await vi.advanceTimersByTimeAsync(10000);\n expect(executeGQL).toHaveBeenCalledWith(\n expect.stringContaining('RelayRealtimeUsage'),\n { agentSessionId: 'sess-1', inputTokens: 0, outputTokens: 7 }\n );\n });\n\n it('re-accumulates on a failed relay so the next flush retries the same deltas', async () => {\n executeGQL.mockRejectedValueOnce(new Error('network down'));\n internals(service).onUsageDelta({ InputTokens: 50, OutputTokens: 5 });\n await vi.advanceTimersByTimeAsync(10000); // flush fails\n\n expect(internals(service).pendingUsageInput).toBe(50);\n expect(internals(service).pendingUsageOutput).toBe(5);\n\n internals(service).onUsageDelta({ InputTokens: 10, OutputTokens: 0 });\n await vi.advanceTimersByTimeAsync(10000); // retry carries the combined totals\n\n expect(executeGQL).toHaveBeenLastCalledWith(\n expect.stringContaining('RelayRealtimeUsage'),\n { agentSessionId: 'sess-1', inputTokens: 60, outputTokens: 5 }\n );\n });\n\n it('flushes the unrelayed remainder at teardown BEFORE closing the server session (no debounce wait)', async () => {\n internals(service).onUsageDelta({ InputTokens: 33, OutputTokens: 4 });\n\n await internals(service).teardown(true);\n\n const mutations = executeGQL.mock.calls.map(c => c[0] as string);\n const usageIdx = mutations.findIndex(m => m.includes('RelayRealtimeUsage'));\n const closeIdx = mutations.findIndex(m => m.includes('CloseAgentSession'));\n expect(usageIdx).toBeGreaterThanOrEqual(0);\n expect(closeIdx).toBeGreaterThan(usageIdx); // usage flushed first, then close\n expect(executeGQL.mock.calls[usageIdx][1]).toEqual({ agentSessionId: 'sess-1', inputTokens: 33, outputTokens: 4 });\n // Teardown leaves the relay reset (no dangling timer / pending totals).\n expect(internals(service).usageFlushTimer).toBeNull();\n expect(internals(service).pendingUsageInput).toBe(0);\n expect(internals(service).pendingUsageOutput).toBe(0);\n });\n\n it('teardown without pending usage relays nothing (no spurious zero-delta mutation)', async () => {\n await internals(service).teardown(true);\n\n const mutations = executeGQL.mock.calls.map(c => c[0] as string);\n expect(mutations.some(m => m.includes('RelayRealtimeUsage'))).toBe(false);\n expect(mutations.some(m => m.includes('CloseAgentSession'))).toBe(true);\n });\n\n it('flushPendingUsage is a no-op without a session id', async () => {\n internals(service).agentSessionId = null;\n internals(service).onUsageDelta({ InputTokens: 9 });\n\n await internals(service).flushPendingUsage();\n\n expect(executeGQL).not.toHaveBeenCalled();\n // The deltas survive for a later flush once a session id exists.\n expect(internals(service).pendingUsageInput).toBe(9);\n });\n});\n\ndescribe('RealtimeSessionService — OnUsage wiring through wireClientHandlers', () => {\n /** Fake client capturing every handler wireClientHandlers registers. */\n class FakeWiredClient {\n public UsageHandler: ((u: RealtimeClientUsage) => void) | null = null;\n OnStateChange(): void { /* captured elsewhere */ }\n OnTranscript(): void { /* not exercised */ }\n OnToolCall(): void { /* not exercised */ }\n OnError(): void { /* not exercised */ }\n OnInterruption(): void { /* not exercised */ }\n OnUsage(handler: (u: RealtimeClientUsage) => void): void {\n this.UsageHandler = handler;\n }\n }\n\n interface WiringInternals {\n agentSessionId: string | null;\n pendingUsageInput: number;\n pendingUsageOutput: number;\n wireClientHandlers(client: FakeWiredClient): void;\n }\n\n it('registers an OnUsage handler that feeds the accumulator', () => {\n const service = new RealtimeSessionService();\n const seam = service as unknown as WiringInternals;\n seam.agentSessionId = 'sess-1';\n const client = new FakeWiredClient();\n\n seam.wireClientHandlers(client);\n expect(client.UsageHandler).toBeTypeOf('function');\n\n client.UsageHandler?.({ InputTokens: 11, OutputTokens: 3 });\n expect(seam.pendingUsageInput).toBe(11);\n expect(seam.pendingUsageOutput).toBe(3);\n });\n});\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=realtime-session-channels.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime-session-channels.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/realtime-session-channels.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,252 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
8
+ import { AIEngineBase } from '@memberjunction/ai-engine-base';
9
+ import { RegisterClass } from '@memberjunction/global';
10
+ import { RealtimeSessionService } from '../lib/services/realtime-session.service';
11
+ import { BaseRealtimeChannelClient } from '../lib/components/realtime/channels/base-realtime-channel-client';
12
+ class FakeRealtimeClient {
13
+ ToolResults = [];
14
+ SendToolResult(callId, resultJson) {
15
+ this.ToolResults.push({ CallID: callId, ResultJson: resultJson });
16
+ }
17
+ SendContextNote(_text) {
18
+ // not under test here
19
+ }
20
+ async Disconnect() {
21
+ // no-op for teardown
22
+ }
23
+ }
24
+ function internals(service) {
25
+ return service;
26
+ }
27
+ /** A minimal surface stand-in (never instantiated by these node tests). */
28
+ class FakeSurface {
29
+ }
30
+ /**
31
+ * A fake interactive-channel plugin, registered exactly like production plugins
32
+ * (`@RegisterClass(BaseRealtimeChannelClient, '<ClientPluginClass>')`).
33
+ */
34
+ let TestEchoChannel = class TestEchoChannel extends BaseRealtimeChannelClient {
35
+ AppliedCalls = [];
36
+ Disposed = false;
37
+ BoundSurface = null;
38
+ get ChannelName() {
39
+ return 'Echo';
40
+ }
41
+ get ToolNamePrefix() {
42
+ return 'Echo.';
43
+ }
44
+ get TabTitle() {
45
+ return 'Echo';
46
+ }
47
+ get TabIcon() {
48
+ return 'fa-solid fa-microphone';
49
+ }
50
+ GetToolDefinitions() {
51
+ return [
52
+ { Name: 'Echo.Say', Description: 'Echo a phrase', ParametersSchema: { type: 'object' } },
53
+ { Name: 'Echo.Clear', Description: 'Clear echoes', ParametersSchema: { type: 'object' } }
54
+ ];
55
+ }
56
+ GetSurfaceComponent() {
57
+ return FakeSurface;
58
+ }
59
+ BindSurface(instance) {
60
+ this.BoundSurface = instance;
61
+ }
62
+ ApplyAgentTool(toolName, argsJson) {
63
+ this.AppliedCalls.push({ ToolName: toolName, ArgsJson: argsJson });
64
+ return JSON.stringify({ success: true, echoed: toolName });
65
+ }
66
+ Dispose() {
67
+ this.Disposed = true;
68
+ super.Dispose();
69
+ }
70
+ /** Test seam: expose the host context the service handed us. */
71
+ get Ctx() {
72
+ return this.Context;
73
+ }
74
+ };
75
+ TestEchoChannel = __decorate([
76
+ RegisterClass(BaseRealtimeChannelClient, 'TestEchoChannel')
77
+ ], TestEchoChannel);
78
+ /**
79
+ * Stubs the provider-scoped AIEngineBase whose cached `AgentChannels` the service reads
80
+ * (rows default to ACTIVE; pass `IsActive: false` to exercise the active-only filter).
81
+ * `success = false` makes the engine load reject — the registry-failure degradation path.
82
+ */
83
+ function mockChannelRegistry(rows, success = true) {
84
+ const configFn = vi.fn(async () => {
85
+ if (!success) {
86
+ throw new Error('boom');
87
+ }
88
+ });
89
+ const fakeEngine = {
90
+ Config: configFn,
91
+ get AgentChannels() {
92
+ return rows.map(r => ({ IsActive: true, ...r }));
93
+ }
94
+ };
95
+ vi.spyOn(AIEngineBase, 'GetProviderInstance').mockReturnValue(fakeEngine);
96
+ return configFn;
97
+ }
98
+ describe('RealtimeSessionService — interactive-channel registry resolution', () => {
99
+ let service;
100
+ beforeEach(() => {
101
+ service = new RealtimeSessionService();
102
+ });
103
+ afterEach(() => {
104
+ vi.restoreAllMocks();
105
+ });
106
+ it('resolves ACTIVE registry rows into per-session plugin instances via the ClassFactory', async () => {
107
+ const configFn = mockChannelRegistry([
108
+ { ID: 'c1', Name: 'Echo', ClientPluginClass: 'TestEchoChannel' },
109
+ // Inactive rows never become session plugins (parity with the old `IsActive = 1` filter).
110
+ { ID: 'c2', Name: 'Dormant', ClientPluginClass: 'TestEchoChannel', IsActive: false }
111
+ ]);
112
+ await internals(service).startChannels();
113
+ // Lazily configures the provider-scoped engine before reading the cached registry.
114
+ expect(configFn).toHaveBeenCalledWith(false, undefined, service.Provider);
115
+ expect(service.ActiveChannels).toHaveLength(1);
116
+ expect(service.ActiveChannels[0]).toBeInstanceOf(TestEchoChannel);
117
+ });
118
+ it('creates a FRESH instance per session (plugins are not singletons)', async () => {
119
+ mockChannelRegistry([{ ID: 'c1', Name: 'Echo', ClientPluginClass: 'TestEchoChannel' }]);
120
+ await internals(service).startChannels();
121
+ const first = service.ActiveChannels[0];
122
+ await internals(service).startChannels();
123
+ expect(service.ActiveChannels[0]).not.toBe(first);
124
+ });
125
+ it('skips rows whose ClientPluginClass has no registration (logged, never fatal)', async () => {
126
+ const warn = vi.spyOn(console, 'warn').mockImplementation(() => undefined);
127
+ mockChannelRegistry([
128
+ { ID: 'c1', Name: 'Ghost', ClientPluginClass: 'NoSuchChannelPlugin' },
129
+ { ID: 'c2', Name: 'Echo', ClientPluginClass: 'TestEchoChannel' }
130
+ ]);
131
+ await internals(service).startChannels();
132
+ expect(service.ActiveChannels).toHaveLength(1);
133
+ expect(service.ActiveChannels[0]).toBeInstanceOf(TestEchoChannel);
134
+ expect(warn).toHaveBeenCalledWith(expect.stringContaining("No client plugin registered for channel 'Ghost'"));
135
+ });
136
+ it('degrades to NO channels when the registry load fails (the voice session must proceed)', async () => {
137
+ vi.spyOn(console, 'warn').mockImplementation(() => undefined);
138
+ mockChannelRegistry([], false);
139
+ const tools = await internals(service).startChannels();
140
+ expect(tools).toEqual([]);
141
+ expect(service.ActiveChannels).toEqual([]);
142
+ });
143
+ it('degrades to NO channels when the engine acquisition throws', async () => {
144
+ vi.spyOn(console, 'warn').mockImplementation(() => undefined);
145
+ vi.spyOn(AIEngineBase, 'GetProviderInstance').mockImplementation(() => {
146
+ throw new Error('no provider');
147
+ });
148
+ const tools = await internals(service).startChannels();
149
+ expect(tools).toEqual([]);
150
+ expect(service.ActiveChannels).toEqual([]);
151
+ });
152
+ it('aggregates every plugin tool definition into the clientTools set for the mint', async () => {
153
+ mockChannelRegistry([{ ID: 'c1', Name: 'Echo', ClientPluginClass: 'TestEchoChannel' }]);
154
+ const tools = await internals(service).startChannels();
155
+ expect(tools.map(t => t.Name)).toEqual(['Echo.Say', 'Echo.Clear']);
156
+ });
157
+ it('emits the resolved plugins on ActiveChannels$ (the overlay registers tabs from it)', async () => {
158
+ mockChannelRegistry([{ ID: 'c1', Name: 'Echo', ClientPluginClass: 'TestEchoChannel' }]);
159
+ const emissions = [];
160
+ const sub = service.ActiveChannels$.subscribe(channels => emissions.push(channels.length));
161
+ await internals(service).startChannels();
162
+ sub.unsubscribe();
163
+ expect(emissions).toEqual([0, 1]); // BehaviorSubject replay, then the resolved set
164
+ });
165
+ });
166
+ describe('RealtimeSessionService — per-plugin tool routing + host context', () => {
167
+ let service;
168
+ let fakeClient;
169
+ let plugin;
170
+ beforeEach(async () => {
171
+ service = new RealtimeSessionService();
172
+ fakeClient = new FakeRealtimeClient();
173
+ internals(service).client = fakeClient;
174
+ mockChannelRegistry([{ ID: 'c1', Name: 'Echo', ClientPluginClass: 'TestEchoChannel' }]);
175
+ await internals(service).startChannels();
176
+ plugin = service.ActiveChannels[0];
177
+ });
178
+ afterEach(() => {
179
+ vi.restoreAllMocks();
180
+ });
181
+ it('routes prefix-matched tool calls to the plugin and feeds its result back to the model', async () => {
182
+ await internals(service).handleToolCall({ CallID: 'call-1', ToolName: 'Echo.Say', ArgumentsJson: '{"phrase":"hi"}' });
183
+ expect(plugin.AppliedCalls).toEqual([{ ToolName: 'Echo.Say', ArgsJson: '{"phrase":"hi"}' }]);
184
+ expect(fakeClient.ToolResults).toEqual([
185
+ { CallID: 'call-1', ResultJson: JSON.stringify({ success: true, echoed: 'Echo.Say' }) }
186
+ ]);
187
+ });
188
+ it('hands each plugin a context whose AgentName matches the session agent', () => {
189
+ expect(plugin.Ctx?.AgentName).toBe(service.CurrentAgentName);
190
+ });
191
+ it('SetFocusMode rides ChannelFocus$ tagged with the requesting plugin', () => {
192
+ const events = [];
193
+ const sub = service.ChannelFocus$.subscribe(e => events.push(e));
194
+ plugin.Ctx?.SetFocusMode(true);
195
+ plugin.Ctx?.SetFocusMode(false);
196
+ sub.unsubscribe();
197
+ expect(events).toEqual([
198
+ { Channel: plugin, Focused: true },
199
+ { Channel: plugin, Focused: false }
200
+ ]);
201
+ });
202
+ });
203
+ describe('RealtimeSessionService — debounced channel saves + teardown flush/dispose', () => {
204
+ let service;
205
+ let plugin;
206
+ let saveSpy;
207
+ beforeEach(async () => {
208
+ vi.useFakeTimers();
209
+ service = new RealtimeSessionService();
210
+ internals(service).agentSessionId = 'session-123';
211
+ saveSpy = vi.spyOn(service, 'SaveChannelState').mockResolvedValue(true);
212
+ mockChannelRegistry([{ ID: 'c1', Name: 'Echo', ClientPluginClass: 'TestEchoChannel' }]);
213
+ await internals(service).startChannels();
214
+ plugin = service.ActiveChannels[0];
215
+ });
216
+ afterEach(() => {
217
+ vi.useRealTimers();
218
+ vi.restoreAllMocks();
219
+ });
220
+ it('debounces RequestSave: a change burst becomes ONE save with the LATEST state', () => {
221
+ plugin.Ctx?.RequestSave('{"v":1}');
222
+ plugin.Ctx?.RequestSave('{"v":2}');
223
+ plugin.Ctx?.RequestSave('{"v":3}');
224
+ expect(saveSpy).not.toHaveBeenCalled();
225
+ vi.advanceTimersByTime(3000);
226
+ expect(saveSpy).toHaveBeenCalledTimes(1);
227
+ expect(saveSpy).toHaveBeenCalledWith('Echo', '{"v":3}', 'session-123');
228
+ });
229
+ it('teardown FLUSHES a pending save immediately (captured session id) and disposes plugins', async () => {
230
+ plugin.Ctx?.RequestSave('{"final":true}');
231
+ await internals(service).teardown(false);
232
+ expect(saveSpy).toHaveBeenCalledTimes(1);
233
+ expect(saveSpy).toHaveBeenCalledWith('Echo', '{"final":true}', 'session-123');
234
+ expect(plugin.Disposed).toBe(true);
235
+ expect(service.ActiveChannels).toEqual([]);
236
+ });
237
+ it('teardown with nothing pending saves nothing but still disposes', async () => {
238
+ await internals(service).teardown(false);
239
+ expect(saveSpy).not.toHaveBeenCalled();
240
+ expect(plugin.Disposed).toBe(true);
241
+ });
242
+ it('a Dispose error in one plugin is contained (teardown completes)', async () => {
243
+ const error = vi.spyOn(console, 'error').mockImplementation(() => undefined);
244
+ vi.spyOn(plugin, 'Dispose').mockImplementation(() => {
245
+ throw new Error('plugin exploded');
246
+ });
247
+ await internals(service).teardown(false);
248
+ expect(service.ActiveChannels).toEqual([]);
249
+ expect(error).toHaveBeenCalledWith(expect.stringContaining("Channel 'Echo' Dispose failed"), expect.any(Error));
250
+ });
251
+ });
252
+ //# sourceMappingURL=realtime-session-channels.test.js.map