@memberjunction/ng-conversations 5.40.1 → 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,423 @@
1
+ // The Remote Browser channel imports its standalone Angular surface component (partial-compiled
2
+ // Angular libs require the JIT compiler in this node test environment), so load the compiler FIRST.
3
+ import '@angular/compiler';
4
+ import { describe, it, expect, beforeEach } from 'vitest';
5
+ import { EventEmitter } from '@angular/core';
6
+ import { MJGlobal } from '@memberjunction/global';
7
+ import { BaseRealtimeChannelClient } from '../lib/components/realtime/channels/base-realtime-channel-client';
8
+ import { LoadRealtimeRemoteBrowserChannel, RemoteBrowserChannel } from '../lib/components/realtime/remote-browser/remote-browser-channel';
9
+ import { MapToViewportCoords, RemoteBrowserSurfaceComponent } from '../lib/components/realtime/remote-browser/remote-browser-surface.component';
10
+ import { MapToolToAction, REMOTE_BROWSER_TOOL_DEFINITIONS, REMOTE_BROWSER_TOOL_NAMES, REMOTE_BROWSER_TOOL_PREFIX, RemoteBrowserToolArgError } from '../lib/components/realtime/remote-browser/remote-browser-tools';
11
+ LoadRealtimeRemoteBrowserChannel();
12
+ /**
13
+ * Builds a fake channel context that records `SendContextNote` notes and `ExecuteServerAction`
14
+ * calls, and replies with a fixed server response. `sessionId` defaults to a live id so tools
15
+ * can run; pass `null` to exercise the no-session path.
16
+ */
17
+ function makeContext(log, response, sessionId = 'session-1') {
18
+ return {
19
+ AgentName: 'Sage',
20
+ SendContextNote: (text) => log.Notes.push(text),
21
+ RequestSave: () => undefined,
22
+ SetFocusMode: () => undefined,
23
+ SaveAsArtifact: async () => null,
24
+ AgentSessionID: sessionId,
25
+ ExecuteServerAction: async (query, variables) => {
26
+ log.Calls.push({ Query: query, Variables: variables });
27
+ return response;
28
+ }
29
+ };
30
+ }
31
+ /** Parses a channel tool-result JSON string into a typed shape. */
32
+ function parseResult(json) {
33
+ return JSON.parse(json);
34
+ }
35
+ describe('Remote Browser — MapToolToAction (tool → server action)', () => {
36
+ it('maps browser_OpenUrl to a navigate action carrying the url', () => {
37
+ expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.OpenUrl, { url: 'https://example.com' }))
38
+ .toEqual({ Kind: 'navigate', Url: 'https://example.com' });
39
+ });
40
+ it('throws a model-readable error when browser_OpenUrl is missing its url', () => {
41
+ expect(() => MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.OpenUrl, {})).toThrow(RemoteBrowserToolArgError);
42
+ });
43
+ it('maps browser_Click by selector', () => {
44
+ expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Click, { selector: '#go' }))
45
+ .toEqual({ Kind: 'click', Selector: '#go', X: undefined, Y: undefined });
46
+ });
47
+ it('maps browser_Click by x/y coordinates', () => {
48
+ expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Click, { x: 12, y: 34 }))
49
+ .toEqual({ Kind: 'click', Selector: undefined, X: 12, Y: 34 });
50
+ });
51
+ it('throws when browser_Click has neither a selector nor both coordinates', () => {
52
+ expect(() => MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Click, { x: 12 })).toThrow(RemoteBrowserToolArgError);
53
+ expect(() => MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Click, {})).toThrow(RemoteBrowserToolArgError);
54
+ });
55
+ it('maps browser_Type with optional selector, and allows empty text', () => {
56
+ expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Type, { text: 'hello', selector: '#q' }))
57
+ .toEqual({ Kind: 'type', Text: 'hello', Selector: '#q' });
58
+ // empty string is a VALID value to type (clears / types nothing) — only a non-string is rejected
59
+ expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Type, { text: '' }))
60
+ .toEqual({ Kind: 'type', Text: '', Selector: undefined });
61
+ });
62
+ it('throws when browser_Type is missing text entirely', () => {
63
+ expect(() => MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Type, {})).toThrow(RemoteBrowserToolArgError);
64
+ });
65
+ it('maps browser_Key, requiring a key', () => {
66
+ expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Key, { key: 'Enter' })).toEqual({ Kind: 'key', Key: 'Enter' });
67
+ expect(() => MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Key, {})).toThrow(RemoteBrowserToolArgError);
68
+ });
69
+ it('maps browser_Scroll with optional deltas + selector', () => {
70
+ expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Scroll, { deltaY: 200 }))
71
+ .toEqual({ Kind: 'scroll', DeltaX: undefined, DeltaY: 200, Selector: undefined });
72
+ expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Scroll, {}))
73
+ .toEqual({ Kind: 'scroll', DeltaX: undefined, DeltaY: undefined, Selector: undefined });
74
+ });
75
+ it('maps the argument-less navigation + read tools', () => {
76
+ expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Back, {})).toEqual({ Kind: 'back' });
77
+ expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Forward, {})).toEqual({ Kind: 'forward' });
78
+ expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.GetPageText, {})).toEqual({ Kind: 'getPageText' });
79
+ });
80
+ it('maps browser_Wait with optional ms + selector', () => {
81
+ expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Wait, { ms: 500 }))
82
+ .toEqual({ Kind: 'wait', Ms: 500, Selector: undefined });
83
+ });
84
+ it('throws on an unknown tool name', () => {
85
+ expect(() => MapToolToAction('browser_Teleport', {})).toThrow(RemoteBrowserToolArgError);
86
+ });
87
+ });
88
+ describe('RemoteBrowserChannel — plugin contract + ApplyAgentTool round-trip', () => {
89
+ let channel;
90
+ let log;
91
+ beforeEach(() => {
92
+ channel = new RemoteBrowserChannel();
93
+ log = { Notes: [], Calls: [] };
94
+ });
95
+ it('is resolvable from the ClassFactory by its registry ClientPluginClass key', () => {
96
+ const registration = MJGlobal.Instance.ClassFactory.GetRegistration(BaseRealtimeChannelClient, 'RealtimeRemoteBrowserChannel');
97
+ expect(registration).toBeTruthy();
98
+ const instance = MJGlobal.Instance.ClassFactory.CreateInstance(BaseRealtimeChannelClient, 'RealtimeRemoteBrowserChannel');
99
+ expect(instance).toBeInstanceOf(RemoteBrowserChannel);
100
+ });
101
+ it('contributes the channel contract: name, prefix, tab chrome, tool set, surface', () => {
102
+ expect(channel.ChannelName).toBe('Remote Browser');
103
+ expect(channel.ToolNamePrefix).toBe(REMOTE_BROWSER_TOOL_PREFIX);
104
+ expect(channel.TabTitle).toBe('Browser');
105
+ expect(channel.TabIcon).toBe('fa-solid fa-globe');
106
+ expect(channel.GetToolDefinitions()).toBe(REMOTE_BROWSER_TOOL_DEFINITIONS);
107
+ expect(channel.GetSurfaceComponent()).toBe(RemoteBrowserSurfaceComponent);
108
+ });
109
+ it('supplies first-run onboarding details (heading, description, tips, icon)', () => {
110
+ const details = channel.GetOnboardingDetails();
111
+ expect(details).not.toBeNull();
112
+ expect(details?.Heading).toBe('Remote Browser');
113
+ expect(details?.IconClass).toBe('fa-solid fa-globe');
114
+ expect(details?.Description?.length).toBeGreaterThan(0);
115
+ expect((details?.Tips?.length ?? 0)).toBeGreaterThan(0);
116
+ });
117
+ it('drives the ExecuteRemoteBrowserAction mutation with flat variables and reports success', async () => {
118
+ channel.Initialize(makeContext(log, { ExecuteRemoteBrowserAction: { Success: true, CurrentUrl: 'https://example.com', Detail: null } }));
119
+ const resultJson = await channel.ApplyAgentTool('browser_OpenUrl', JSON.stringify({ url: 'https://example.com' }));
120
+ const result = parseResult(resultJson);
121
+ expect(result.success).toBe(true);
122
+ expect(result.currentUrl).toBe('https://example.com');
123
+ expect(log.Calls).toHaveLength(1);
124
+ expect(log.Calls[0].Query).toContain('ExecuteRemoteBrowserAction');
125
+ expect(log.Calls[0].Variables).toMatchObject({
126
+ agentSessionID: 'session-1', kind: 'navigate', url: 'https://example.com',
127
+ selector: null, x: null, y: null, text: null, key: null, deltaX: null, deltaY: null, ms: null
128
+ });
129
+ // PERCEPTION: the agent is told where the page went (no spoken reply requested).
130
+ expect(log.Notes).toEqual(['[browser] current page: https://example.com']);
131
+ });
132
+ it('returns the server Detail for read tools and does NOT push a context note without a URL', async () => {
133
+ channel.Initialize(makeContext(log, { ExecuteRemoteBrowserAction: { Success: true, CurrentUrl: null, Detail: 'Page text here' } }));
134
+ const result = parseResult(await channel.ApplyAgentTool('browser_GetPageText', '{}'));
135
+ expect(result.success).toBe(true);
136
+ expect(result.detail).toBe('Page text here');
137
+ expect(log.Notes).toEqual([]);
138
+ });
139
+ it('maps a server failure to a failed result with the server Detail (never throws)', async () => {
140
+ channel.Initialize(makeContext(log, { ExecuteRemoteBrowserAction: { Success: false, CurrentUrl: null, Detail: 'Selector not found' } }));
141
+ const result = parseResult(await channel.ApplyAgentTool('browser_Click', JSON.stringify({ selector: '#missing' })));
142
+ expect(result.success).toBe(false);
143
+ expect(result.error).toBe('Selector not found');
144
+ });
145
+ it('returns a failed result (no server call) when arguments are invalid', async () => {
146
+ channel.Initialize(makeContext(log, { ExecuteRemoteBrowserAction: { Success: true, CurrentUrl: null, Detail: null } }));
147
+ const result = parseResult(await channel.ApplyAgentTool('browser_OpenUrl', '{}'));
148
+ expect(result.success).toBe(false);
149
+ expect(result.error).toContain('url');
150
+ expect(log.Calls).toHaveLength(0); // bad args never reach the server
151
+ });
152
+ it('returns a failed result when no live session is available', async () => {
153
+ channel.Initialize(makeContext(log, null, null));
154
+ const result = parseResult(await channel.ApplyAgentTool('browser_Back', '{}'));
155
+ expect(result.success).toBe(false);
156
+ expect(result.error).toContain('No live browser session');
157
+ expect(log.Calls).toHaveLength(0);
158
+ });
159
+ it('maps a null server response to a failed result (best-effort transport)', async () => {
160
+ channel.Initialize(makeContext(log, null));
161
+ const result = parseResult(await channel.ApplyAgentTool('browser_Back', '{}'));
162
+ expect(result.success).toBe(false);
163
+ expect(result.error).toContain('no response from the server');
164
+ });
165
+ it('tolerates malformed args JSON for an argument-less tool (parses to {})', async () => {
166
+ channel.Initialize(makeContext(log, { ExecuteRemoteBrowserAction: { Success: true, CurrentUrl: 'https://x', Detail: null } }));
167
+ const result = parseResult(await channel.ApplyAgentTool('browser_Forward', 'not json'));
168
+ expect(result.success).toBe(true);
169
+ expect(log.Calls[0].Variables.kind).toBe('forward');
170
+ });
171
+ });
172
+ describe('RemoteBrowserChannel — visual interpreter (browser_DescribePage / browser_LocateElement)', () => {
173
+ let channel;
174
+ let log;
175
+ beforeEach(() => {
176
+ channel = new RemoteBrowserChannel();
177
+ log = { Notes: [], Calls: [] };
178
+ });
179
+ it('routes browser_DescribePage to the InterpretRemoteBrowserPage mutation and returns the description', async () => {
180
+ channel.Initialize(makeContext(log, {
181
+ InterpretRemoteBrowserPage: { Description: 'A login page with email and password fields.', Elements: [], Detail: null }
182
+ }));
183
+ const resultJson = await channel.ApplyAgentTool('browser_DescribePage', '{}');
184
+ const result = JSON.parse(resultJson);
185
+ expect(log.Calls).toHaveLength(1);
186
+ expect(log.Calls[0].Query).toContain('InterpretRemoteBrowserPage');
187
+ // DescribePage carries no target query.
188
+ expect(log.Calls[0].Variables).toMatchObject({ agentSessionID: 'session-1', query: null });
189
+ expect(result.description).toBe('A login page with email and password fields.');
190
+ });
191
+ it('routes browser_LocateElement to the interpret mutation with the query and returns found + centroid', async () => {
192
+ channel.Initialize(makeContext(log, {
193
+ InterpretRemoteBrowserPage: {
194
+ Description: 'A login page.',
195
+ Elements: [{ Label: 'Sign In button', X: 320, Y: 480, Confidence: 0.9 }],
196
+ Detail: null
197
+ }
198
+ }));
199
+ const resultJson = await channel.ApplyAgentTool('browser_LocateElement', JSON.stringify({ description: 'the blue Sign In button' }));
200
+ const result = JSON.parse(resultJson);
201
+ expect(log.Calls).toHaveLength(1);
202
+ expect(log.Calls[0].Query).toContain('InterpretRemoteBrowserPage');
203
+ expect(log.Calls[0].Variables).toMatchObject({ agentSessionID: 'session-1', query: 'the blue Sign In button' });
204
+ expect(result.found).toBe(true);
205
+ expect(result.element).toEqual({ label: 'Sign In button', x: 320, y: 480 });
206
+ expect(result.all).toHaveLength(1);
207
+ });
208
+ it('reports found:false with a null element when no element is localized', async () => {
209
+ channel.Initialize(makeContext(log, {
210
+ InterpretRemoteBrowserPage: { Description: 'A login page.', Elements: [], Detail: null }
211
+ }));
212
+ const resultJson = await channel.ApplyAgentTool('browser_LocateElement', JSON.stringify({ description: 'the logout link' }));
213
+ const result = JSON.parse(resultJson);
214
+ expect(result.found).toBe(false);
215
+ expect(result.element).toBeNull();
216
+ });
217
+ it('rejects browser_LocateElement without a description (no server call)', async () => {
218
+ channel.Initialize(makeContext(log, { InterpretRemoteBrowserPage: { Description: null, Elements: [], Detail: null } }));
219
+ const result = parseResult(await channel.ApplyAgentTool('browser_LocateElement', '{}'));
220
+ expect(result.success).toBe(false);
221
+ expect(result.error).toContain('description');
222
+ expect(log.Calls).toHaveLength(0);
223
+ });
224
+ it('keeps the null-session guard for the interpreter tools (no server call)', async () => {
225
+ channel.Initialize(makeContext(log, null, null));
226
+ const result = parseResult(await channel.ApplyAgentTool('browser_DescribePage', '{}'));
227
+ expect(result.success).toBe(false);
228
+ expect(result.error).toContain('No live browser session');
229
+ expect(log.Calls).toHaveLength(0);
230
+ });
231
+ });
232
+ describe('RemoteBrowserChannel — live screencast (pushed frames)', () => {
233
+ let channel;
234
+ let log;
235
+ beforeEach(() => {
236
+ channel = new RemoteBrowserChannel();
237
+ log = { Notes: [], Calls: [] };
238
+ });
239
+ /** Minimal surface double recording the frames it was asked to render + its Streaming flag. */
240
+ function makeSurface() {
241
+ const surface = {
242
+ Streaming: false,
243
+ Interactive: false,
244
+ AudioAvailable: false,
245
+ AudioMuted: false,
246
+ HumanInput: new EventEmitter(),
247
+ AudioMutedChange: new EventEmitter(),
248
+ RenderedFrames: [],
249
+ RenderFrame(dataBase64) {
250
+ this.RenderedFrames.push(dataBase64);
251
+ },
252
+ SetCurrentUrl() { }
253
+ };
254
+ return surface;
255
+ }
256
+ /** Flushes microtasks so BindSurface's fire-and-forget startScreencast resolves. */
257
+ function flush() {
258
+ return new Promise((resolve) => setTimeout(resolve, 0));
259
+ }
260
+ it('starts the screencast and flips the surface to Streaming when the backend supports it', async () => {
261
+ channel.Initialize(makeContext(log, { StartRemoteBrowserScreencast: { Streaming: true } }));
262
+ const surface = makeSurface();
263
+ channel.BindSurface(surface);
264
+ await flush();
265
+ // The start mutation ran with the session id…
266
+ const startCall = log.Calls.find(c => c.Query.includes('StartRemoteBrowserScreencast'));
267
+ expect(startCall).toBeTruthy();
268
+ expect(startCall?.Variables).toMatchObject({ agentSessionID: 'session-1' });
269
+ // …and the surface was switched to canvas mode.
270
+ expect(surface.Streaming).toBe(true);
271
+ });
272
+ it('forwards pushed frames to the surface canvas once streaming', async () => {
273
+ channel.Initialize(makeContext(log, { StartRemoteBrowserScreencast: { Streaming: true } }));
274
+ const surface = makeSurface();
275
+ channel.BindSurface(surface);
276
+ await flush();
277
+ channel.OnScreencastFrame('AAAA');
278
+ channel.OnScreencastFrame('BBBB');
279
+ expect(surface.RenderedFrames).toEqual(['AAAA', 'BBBB']);
280
+ });
281
+ it('does NOT flip Streaming or forward frames when the backend lacks ScreenStreaming', async () => {
282
+ channel.Initialize(makeContext(log, { StartRemoteBrowserScreencast: { Streaming: false } }));
283
+ const surface = makeSurface();
284
+ channel.BindSurface(surface);
285
+ await flush();
286
+ expect(surface.Streaming).toBe(false);
287
+ channel.OnScreencastFrame('AAAA');
288
+ expect(surface.RenderedFrames).toEqual([]); // ignored — channel is not streaming
289
+ });
290
+ it('stops the screencast on UnbindSurface when it had started', async () => {
291
+ channel.Initialize(makeContext(log, { StartRemoteBrowserScreencast: { Streaming: true } }));
292
+ const surface = makeSurface();
293
+ channel.BindSurface(surface);
294
+ await flush();
295
+ channel.UnbindSurface();
296
+ await flush();
297
+ const stopCall = log.Calls.find(c => c.Query.includes('StopRemoteBrowserScreencast'));
298
+ expect(stopCall).toBeTruthy();
299
+ expect(stopCall?.Variables).toMatchObject({ agentSessionID: 'session-1' });
300
+ // After unbind, late frames are ignored (not streaming, no surface).
301
+ channel.OnScreencastFrame('CCCC');
302
+ expect(surface.RenderedFrames).toEqual([]);
303
+ });
304
+ });
305
+ describe('RemoteBrowserChannel — human takeover (relay surface input to the server)', () => {
306
+ let channel;
307
+ let log;
308
+ beforeEach(() => {
309
+ channel = new RemoteBrowserChannel();
310
+ log = { Notes: [], Calls: [] };
311
+ });
312
+ /** Minimal surface double exposing the takeover wiring: Interactive flag + HumanInput emitter. */
313
+ function makeSurface() {
314
+ const surface = {
315
+ Streaming: false,
316
+ Interactive: false,
317
+ AudioAvailable: false,
318
+ AudioMuted: false,
319
+ HumanInput: new EventEmitter(),
320
+ AudioMutedChange: new EventEmitter(),
321
+ RenderFrame() { },
322
+ SetCurrentUrl() { }
323
+ };
324
+ return surface;
325
+ }
326
+ it('enables takeover by default on bind (Interactive = true)', () => {
327
+ channel.Initialize(makeContext(log, { StartRemoteBrowserScreencast: { Streaming: true } }));
328
+ const surface = makeSurface();
329
+ channel.BindSurface(surface);
330
+ expect(surface.Interactive).toBe(true);
331
+ });
332
+ it('forwards a pointer-click HumanInput to the relay mutation with viewport coords + button', () => {
333
+ channel.Initialize(makeContext(log, { RelayRemoteBrowserHumanInput: true }));
334
+ const surface = makeSurface();
335
+ channel.BindSurface(surface);
336
+ surface.HumanInput.emit({ kind: 'pointer-click', x: 320, y: 480, button: 'left' });
337
+ const relayCall = log.Calls.find(c => c.Query.includes('RelayRemoteBrowserHumanInput'));
338
+ expect(relayCall).toBeTruthy();
339
+ expect(relayCall?.Variables).toMatchObject({
340
+ agentSessionID: 'session-1', kind: 'pointer-click', x: 320, y: 480, button: 'left', key: null
341
+ });
342
+ });
343
+ it('forwards a scroll HumanInput to the relay mutation with viewport coords + wheel deltas', () => {
344
+ channel.Initialize(makeContext(log, { RelayRemoteBrowserHumanInput: true }));
345
+ const surface = makeSurface();
346
+ channel.BindSurface(surface);
347
+ surface.HumanInput.emit({ kind: 'scroll', x: 100, y: 200, deltaX: 12, deltaY: -48 });
348
+ const relayCall = log.Calls.find(c => c.Query.includes('RelayRemoteBrowserHumanInput'));
349
+ expect(relayCall).toBeTruthy();
350
+ expect(relayCall?.Variables).toMatchObject({
351
+ agentSessionID: 'session-1', kind: 'scroll', x: 100, y: 200, button: null, key: null, deltaX: 12, deltaY: -48
352
+ });
353
+ });
354
+ it('forwards a key HumanInput with the key string (null pointer coords)', () => {
355
+ channel.Initialize(makeContext(log, { RelayRemoteBrowserHumanInput: true }));
356
+ const surface = makeSurface();
357
+ channel.BindSurface(surface);
358
+ surface.HumanInput.emit({ kind: 'key', key: 'Enter' });
359
+ const relayCall = log.Calls.find(c => c.Query.includes('RelayRemoteBrowserHumanInput'));
360
+ expect(relayCall?.Variables).toMatchObject({
361
+ agentSessionID: 'session-1', kind: 'key', x: null, y: null, button: null, key: 'Enter', modifiers: null
362
+ });
363
+ });
364
+ it('relays held modifiers as a comma-separated list (Ctrl/Cmd+A select-all)', () => {
365
+ channel.Initialize(makeContext(log, { RelayRemoteBrowserHumanInput: true }));
366
+ const surface = makeSurface();
367
+ channel.BindSurface(surface);
368
+ surface.HumanInput.emit({ kind: 'key', key: 'a', modifiers: ['Control'] });
369
+ const relayCall = log.Calls.find(c => c.Query.includes('RelayRemoteBrowserHumanInput'));
370
+ expect(relayCall?.Variables).toMatchObject({ kind: 'key', key: 'a', modifiers: 'Control' });
371
+ });
372
+ it('relays a shift-click with the modifier carried on the pointer-click', () => {
373
+ channel.Initialize(makeContext(log, { RelayRemoteBrowserHumanInput: true }));
374
+ const surface = makeSurface();
375
+ channel.BindSurface(surface);
376
+ surface.HumanInput.emit({ kind: 'pointer-click', x: 5, y: 6, button: 'left', modifiers: ['Shift'] });
377
+ const relayCall = log.Calls.find(c => c.Query.includes('RelayRemoteBrowserHumanInput'));
378
+ expect(relayCall?.Variables).toMatchObject({ kind: 'pointer-click', x: 5, y: 6, modifiers: 'Shift' });
379
+ });
380
+ it('relays a click-drag as pointer-down then pointer-up (drag text selection)', () => {
381
+ channel.Initialize(makeContext(log, { RelayRemoteBrowserHumanInput: true }));
382
+ const surface = makeSurface();
383
+ channel.BindSurface(surface);
384
+ surface.HumanInput.emit({ kind: 'pointer-down', x: 10, y: 20 });
385
+ surface.HumanInput.emit({ kind: 'pointer-up', x: 80, y: 20 });
386
+ const relayed = log.Calls
387
+ .filter(c => c.Query.includes('RelayRemoteBrowserHumanInput'))
388
+ .map(c => c.Variables.kind);
389
+ expect(relayed).toEqual(['pointer-down', 'pointer-up']);
390
+ });
391
+ it('stops forwarding after UnbindSurface (subscription torn down)', () => {
392
+ channel.Initialize(makeContext(log, { RelayRemoteBrowserHumanInput: true }));
393
+ const surface = makeSurface();
394
+ channel.BindSurface(surface);
395
+ channel.UnbindSurface();
396
+ surface.HumanInput.emit({ kind: 'pointer-move', x: 1, y: 2 });
397
+ expect(log.Calls.find(c => c.Query.includes('RelayRemoteBrowserHumanInput'))).toBeUndefined();
398
+ });
399
+ });
400
+ describe('MapToViewportCoords (display → viewport coordinate mapping)', () => {
401
+ it('maps a click on a scaled-up canvas back to viewport pixels', () => {
402
+ // Canvas internal resolution 1280x720, displayed at 640x360 (2x downscale) at offset (100, 50).
403
+ // A click at display (420, 230) → local (320, 180) → viewport (640, 360).
404
+ const rect = { left: 100, top: 50, width: 640, height: 360 };
405
+ expect(MapToViewportCoords(420, 230, rect, 1280, 720)).toEqual({ x: 640, y: 360 });
406
+ });
407
+ it('maps 1:1 when the display size equals the internal resolution', () => {
408
+ const rect = { left: 0, top: 0, width: 800, height: 600 };
409
+ expect(MapToViewportCoords(120, 90, rect, 800, 600)).toEqual({ x: 120, y: 90 });
410
+ });
411
+ it('rounds to integer viewport coordinates', () => {
412
+ const rect = { left: 0, top: 0, width: 300, height: 300 };
413
+ // 101/300 * 1000 = 336.67 → 337 ; 100/300 * 1000 = 333.33 → 333
414
+ expect(MapToViewportCoords(101, 100, rect, 1000, 1000)).toEqual({ x: 337, y: 333 });
415
+ });
416
+ it('returns null on a zero-size rect (divide-by-zero guard)', () => {
417
+ expect(MapToViewportCoords(10, 10, { left: 0, top: 0, width: 0, height: 0 }, 1280, 720)).toBeNull();
418
+ });
419
+ it('returns null on an un-sized canvas', () => {
420
+ expect(MapToViewportCoords(10, 10, { left: 0, top: 0, width: 640, height: 360 }, 0, 0)).toBeNull();
421
+ });
422
+ });
423
+ //# sourceMappingURL=remote-browser-channel.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-browser-channel.test.js","sourceRoot":"","sources":["../../src/__tests__/remote-browser-channel.test.ts"],"names":[],"mappings":"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","sourcesContent":["// The Remote Browser channel imports its standalone Angular surface component (partial-compiled\n// Angular libs require the JIT compiler in this node test environment), so load the compiler FIRST.\nimport '@angular/compiler';\nimport { describe, it, expect, beforeEach } from 'vitest';\nimport { EventEmitter } from '@angular/core';\nimport { JSONValue } from '@memberjunction/ai';\nimport { MJGlobal } from '@memberjunction/global';\nimport { BaseRealtimeChannelClient, RealtimeChannelContext } from '../lib/components/realtime/channels/base-realtime-channel-client';\nimport {\n LoadRealtimeRemoteBrowserChannel, RemoteBrowserChannel\n} from '../lib/components/realtime/remote-browser/remote-browser-channel';\nimport {\n MapToViewportCoords, RemoteBrowserHumanInputEvent, RemoteBrowserSurfaceComponent\n} from '../lib/components/realtime/remote-browser/remote-browser-surface.component';\nimport {\n MapToolToAction, REMOTE_BROWSER_TOOL_DEFINITIONS, REMOTE_BROWSER_TOOL_NAMES,\n REMOTE_BROWSER_TOOL_PREFIX, RemoteBrowserToolArgError\n} from '../lib/components/realtime/remote-browser/remote-browser-tools';\n\nLoadRealtimeRemoteBrowserChannel();\n\n/** One recorded GraphQL call the fake context received. */\ninterface ServerCall {\n Query: string;\n Variables: Record<string, JSONValue>;\n}\n\ninterface CtxLog {\n Notes: string[];\n Calls: ServerCall[];\n}\n\n/**\n * Builds a fake channel context that records `SendContextNote` notes and `ExecuteServerAction`\n * calls, and replies with a fixed server response. `sessionId` defaults to a live id so tools\n * can run; pass `null` to exercise the no-session path.\n */\nfunction makeContext(\n log: CtxLog,\n response: Record<string, JSONValue> | null,\n sessionId: string | null = 'session-1'\n): RealtimeChannelContext {\n return {\n AgentName: 'Sage',\n SendContextNote: (text: string) => log.Notes.push(text),\n RequestSave: () => undefined,\n SetFocusMode: () => undefined,\n SaveAsArtifact: async () => null,\n AgentSessionID: sessionId,\n ExecuteServerAction: async <T>(query: string, variables: Record<string, JSONValue>): Promise<T | null> => {\n log.Calls.push({ Query: query, Variables: variables });\n return response as T | null;\n }\n };\n}\n\n/** Parses a channel tool-result JSON string into a typed shape. */\nfunction parseResult(json: string): { success: boolean; currentUrl?: string; detail?: string; error?: string } {\n return JSON.parse(json) as { success: boolean; currentUrl?: string; detail?: string; error?: string };\n}\n\ndescribe('Remote Browser — MapToolToAction (tool → server action)', () => {\n it('maps browser_OpenUrl to a navigate action carrying the url', () => {\n expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.OpenUrl, { url: 'https://example.com' }))\n .toEqual({ Kind: 'navigate', Url: 'https://example.com' });\n });\n\n it('throws a model-readable error when browser_OpenUrl is missing its url', () => {\n expect(() => MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.OpenUrl, {})).toThrow(RemoteBrowserToolArgError);\n });\n\n it('maps browser_Click by selector', () => {\n expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Click, { selector: '#go' }))\n .toEqual({ Kind: 'click', Selector: '#go', X: undefined, Y: undefined });\n });\n\n it('maps browser_Click by x/y coordinates', () => {\n expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Click, { x: 12, y: 34 }))\n .toEqual({ Kind: 'click', Selector: undefined, X: 12, Y: 34 });\n });\n\n it('throws when browser_Click has neither a selector nor both coordinates', () => {\n expect(() => MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Click, { x: 12 })).toThrow(RemoteBrowserToolArgError);\n expect(() => MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Click, {})).toThrow(RemoteBrowserToolArgError);\n });\n\n it('maps browser_Type with optional selector, and allows empty text', () => {\n expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Type, { text: 'hello', selector: '#q' }))\n .toEqual({ Kind: 'type', Text: 'hello', Selector: '#q' });\n // empty string is a VALID value to type (clears / types nothing) — only a non-string is rejected\n expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Type, { text: '' }))\n .toEqual({ Kind: 'type', Text: '', Selector: undefined });\n });\n\n it('throws when browser_Type is missing text entirely', () => {\n expect(() => MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Type, {})).toThrow(RemoteBrowserToolArgError);\n });\n\n it('maps browser_Key, requiring a key', () => {\n expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Key, { key: 'Enter' })).toEqual({ Kind: 'key', Key: 'Enter' });\n expect(() => MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Key, {})).toThrow(RemoteBrowserToolArgError);\n });\n\n it('maps browser_Scroll with optional deltas + selector', () => {\n expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Scroll, { deltaY: 200 }))\n .toEqual({ Kind: 'scroll', DeltaX: undefined, DeltaY: 200, Selector: undefined });\n expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Scroll, {}))\n .toEqual({ Kind: 'scroll', DeltaX: undefined, DeltaY: undefined, Selector: undefined });\n });\n\n it('maps the argument-less navigation + read tools', () => {\n expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Back, {})).toEqual({ Kind: 'back' });\n expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Forward, {})).toEqual({ Kind: 'forward' });\n expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.GetPageText, {})).toEqual({ Kind: 'getPageText' });\n });\n\n it('maps browser_Wait with optional ms + selector', () => {\n expect(MapToolToAction(REMOTE_BROWSER_TOOL_NAMES.Wait, { ms: 500 }))\n .toEqual({ Kind: 'wait', Ms: 500, Selector: undefined });\n });\n\n it('throws on an unknown tool name', () => {\n expect(() => MapToolToAction('browser_Teleport', {})).toThrow(RemoteBrowserToolArgError);\n });\n});\n\ndescribe('RemoteBrowserChannel — plugin contract + ApplyAgentTool round-trip', () => {\n let channel: RemoteBrowserChannel;\n let log: CtxLog;\n\n beforeEach(() => {\n channel = new RemoteBrowserChannel();\n log = { Notes: [], Calls: [] };\n });\n\n it('is resolvable from the ClassFactory by its registry ClientPluginClass key', () => {\n const registration = MJGlobal.Instance.ClassFactory.GetRegistration(BaseRealtimeChannelClient, 'RealtimeRemoteBrowserChannel');\n expect(registration).toBeTruthy();\n const instance = MJGlobal.Instance.ClassFactory.CreateInstance<BaseRealtimeChannelClient>(\n BaseRealtimeChannelClient, 'RealtimeRemoteBrowserChannel'\n );\n expect(instance).toBeInstanceOf(RemoteBrowserChannel);\n });\n\n it('contributes the channel contract: name, prefix, tab chrome, tool set, surface', () => {\n expect(channel.ChannelName).toBe('Remote Browser');\n expect(channel.ToolNamePrefix).toBe(REMOTE_BROWSER_TOOL_PREFIX);\n expect(channel.TabTitle).toBe('Browser');\n expect(channel.TabIcon).toBe('fa-solid fa-globe');\n expect(channel.GetToolDefinitions()).toBe(REMOTE_BROWSER_TOOL_DEFINITIONS);\n expect(channel.GetSurfaceComponent()).toBe(RemoteBrowserSurfaceComponent);\n });\n\n it('supplies first-run onboarding details (heading, description, tips, icon)', () => {\n const details = channel.GetOnboardingDetails();\n expect(details).not.toBeNull();\n expect(details?.Heading).toBe('Remote Browser');\n expect(details?.IconClass).toBe('fa-solid fa-globe');\n expect(details?.Description?.length).toBeGreaterThan(0);\n expect((details?.Tips?.length ?? 0)).toBeGreaterThan(0);\n });\n\n it('drives the ExecuteRemoteBrowserAction mutation with flat variables and reports success', async () => {\n channel.Initialize(makeContext(log, { ExecuteRemoteBrowserAction: { Success: true, CurrentUrl: 'https://example.com', Detail: null } }));\n\n const resultJson = await channel.ApplyAgentTool('browser_OpenUrl', JSON.stringify({ url: 'https://example.com' }));\n const result = parseResult(resultJson);\n\n expect(result.success).toBe(true);\n expect(result.currentUrl).toBe('https://example.com');\n expect(log.Calls).toHaveLength(1);\n expect(log.Calls[0].Query).toContain('ExecuteRemoteBrowserAction');\n expect(log.Calls[0].Variables).toMatchObject({\n agentSessionID: 'session-1', kind: 'navigate', url: 'https://example.com',\n selector: null, x: null, y: null, text: null, key: null, deltaX: null, deltaY: null, ms: null\n });\n // PERCEPTION: the agent is told where the page went (no spoken reply requested).\n expect(log.Notes).toEqual(['[browser] current page: https://example.com']);\n });\n\n it('returns the server Detail for read tools and does NOT push a context note without a URL', async () => {\n channel.Initialize(makeContext(log, { ExecuteRemoteBrowserAction: { Success: true, CurrentUrl: null, Detail: 'Page text here' } }));\n\n const result = parseResult(await channel.ApplyAgentTool('browser_GetPageText', '{}'));\n expect(result.success).toBe(true);\n expect(result.detail).toBe('Page text here');\n expect(log.Notes).toEqual([]);\n });\n\n it('maps a server failure to a failed result with the server Detail (never throws)', async () => {\n channel.Initialize(makeContext(log, { ExecuteRemoteBrowserAction: { Success: false, CurrentUrl: null, Detail: 'Selector not found' } }));\n\n const result = parseResult(await channel.ApplyAgentTool('browser_Click', JSON.stringify({ selector: '#missing' })));\n expect(result.success).toBe(false);\n expect(result.error).toBe('Selector not found');\n });\n\n it('returns a failed result (no server call) when arguments are invalid', async () => {\n channel.Initialize(makeContext(log, { ExecuteRemoteBrowserAction: { Success: true, CurrentUrl: null, Detail: null } }));\n\n const result = parseResult(await channel.ApplyAgentTool('browser_OpenUrl', '{}'));\n expect(result.success).toBe(false);\n expect(result.error).toContain('url');\n expect(log.Calls).toHaveLength(0); // bad args never reach the server\n });\n\n it('returns a failed result when no live session is available', async () => {\n channel.Initialize(makeContext(log, null, null));\n\n const result = parseResult(await channel.ApplyAgentTool('browser_Back', '{}'));\n expect(result.success).toBe(false);\n expect(result.error).toContain('No live browser session');\n expect(log.Calls).toHaveLength(0);\n });\n\n it('maps a null server response to a failed result (best-effort transport)', async () => {\n channel.Initialize(makeContext(log, null));\n\n const result = parseResult(await channel.ApplyAgentTool('browser_Back', '{}'));\n expect(result.success).toBe(false);\n expect(result.error).toContain('no response from the server');\n });\n\n it('tolerates malformed args JSON for an argument-less tool (parses to {})', async () => {\n channel.Initialize(makeContext(log, { ExecuteRemoteBrowserAction: { Success: true, CurrentUrl: 'https://x', Detail: null } }));\n\n const result = parseResult(await channel.ApplyAgentTool('browser_Forward', 'not json'));\n expect(result.success).toBe(true);\n expect(log.Calls[0].Variables.kind).toBe('forward');\n });\n});\n\ndescribe('RemoteBrowserChannel — visual interpreter (browser_DescribePage / browser_LocateElement)', () => {\n let channel: RemoteBrowserChannel;\n let log: CtxLog;\n\n beforeEach(() => {\n channel = new RemoteBrowserChannel();\n log = { Notes: [], Calls: [] };\n });\n\n it('routes browser_DescribePage to the InterpretRemoteBrowserPage mutation and returns the description', async () => {\n channel.Initialize(makeContext(log, {\n InterpretRemoteBrowserPage: { Description: 'A login page with email and password fields.', Elements: [], Detail: null }\n }));\n\n const resultJson = await channel.ApplyAgentTool('browser_DescribePage', '{}');\n const result = JSON.parse(resultJson) as { description: string };\n\n expect(log.Calls).toHaveLength(1);\n expect(log.Calls[0].Query).toContain('InterpretRemoteBrowserPage');\n // DescribePage carries no target query.\n expect(log.Calls[0].Variables).toMatchObject({ agentSessionID: 'session-1', query: null });\n expect(result.description).toBe('A login page with email and password fields.');\n });\n\n it('routes browser_LocateElement to the interpret mutation with the query and returns found + centroid', async () => {\n channel.Initialize(makeContext(log, {\n InterpretRemoteBrowserPage: {\n Description: 'A login page.',\n Elements: [{ Label: 'Sign In button', X: 320, Y: 480, Confidence: 0.9 }],\n Detail: null\n }\n }));\n\n const resultJson = await channel.ApplyAgentTool('browser_LocateElement', JSON.stringify({ description: 'the blue Sign In button' }));\n const result = JSON.parse(resultJson) as {\n found: boolean;\n element: { label: string; x: number; y: number } | null;\n all: Array<{ Label: string; X: number; Y: number; Confidence: number }>;\n };\n\n expect(log.Calls).toHaveLength(1);\n expect(log.Calls[0].Query).toContain('InterpretRemoteBrowserPage');\n expect(log.Calls[0].Variables).toMatchObject({ agentSessionID: 'session-1', query: 'the blue Sign In button' });\n expect(result.found).toBe(true);\n expect(result.element).toEqual({ label: 'Sign In button', x: 320, y: 480 });\n expect(result.all).toHaveLength(1);\n });\n\n it('reports found:false with a null element when no element is localized', async () => {\n channel.Initialize(makeContext(log, {\n InterpretRemoteBrowserPage: { Description: 'A login page.', Elements: [], Detail: null }\n }));\n\n const resultJson = await channel.ApplyAgentTool('browser_LocateElement', JSON.stringify({ description: 'the logout link' }));\n const result = JSON.parse(resultJson) as { found: boolean; element: unknown | null };\n expect(result.found).toBe(false);\n expect(result.element).toBeNull();\n });\n\n it('rejects browser_LocateElement without a description (no server call)', async () => {\n channel.Initialize(makeContext(log, { InterpretRemoteBrowserPage: { Description: null, Elements: [], Detail: null } }));\n\n const result = parseResult(await channel.ApplyAgentTool('browser_LocateElement', '{}'));\n expect(result.success).toBe(false);\n expect(result.error).toContain('description');\n expect(log.Calls).toHaveLength(0);\n });\n\n it('keeps the null-session guard for the interpreter tools (no server call)', async () => {\n channel.Initialize(makeContext(log, null, null));\n\n const result = parseResult(await channel.ApplyAgentTool('browser_DescribePage', '{}'));\n expect(result.success).toBe(false);\n expect(result.error).toContain('No live browser session');\n expect(log.Calls).toHaveLength(0);\n });\n});\n\ndescribe('RemoteBrowserChannel — live screencast (pushed frames)', () => {\n let channel: RemoteBrowserChannel;\n let log: CtxLog;\n\n beforeEach(() => {\n channel = new RemoteBrowserChannel();\n log = { Notes: [], Calls: [] };\n });\n\n /** Minimal surface double recording the frames it was asked to render + its Streaming flag. */\n function makeSurface(): RemoteBrowserSurfaceComponent & { RenderedFrames: string[] } {\n const surface = {\n Streaming: false,\n Interactive: false,\n AudioAvailable: false,\n AudioMuted: false,\n HumanInput: new EventEmitter<RemoteBrowserHumanInputEvent>(),\n AudioMutedChange: new EventEmitter<boolean>(),\n RenderedFrames: [] as string[],\n RenderFrame(dataBase64: string) {\n this.RenderedFrames.push(dataBase64);\n },\n SetCurrentUrl() { /* unused here */ }\n };\n return surface as unknown as RemoteBrowserSurfaceComponent & { RenderedFrames: string[] };\n }\n\n /** Flushes microtasks so BindSurface's fire-and-forget startScreencast resolves. */\n function flush(): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, 0));\n }\n\n it('starts the screencast and flips the surface to Streaming when the backend supports it', async () => {\n channel.Initialize(makeContext(log, { StartRemoteBrowserScreencast: { Streaming: true } }));\n const surface = makeSurface();\n\n channel.BindSurface(surface);\n await flush();\n\n // The start mutation ran with the session id…\n const startCall = log.Calls.find(c => c.Query.includes('StartRemoteBrowserScreencast'));\n expect(startCall).toBeTruthy();\n expect(startCall?.Variables).toMatchObject({ agentSessionID: 'session-1' });\n // …and the surface was switched to canvas mode.\n expect(surface.Streaming).toBe(true);\n });\n\n it('forwards pushed frames to the surface canvas once streaming', async () => {\n channel.Initialize(makeContext(log, { StartRemoteBrowserScreencast: { Streaming: true } }));\n const surface = makeSurface();\n channel.BindSurface(surface);\n await flush();\n\n channel.OnScreencastFrame('AAAA');\n channel.OnScreencastFrame('BBBB');\n\n expect(surface.RenderedFrames).toEqual(['AAAA', 'BBBB']);\n });\n\n it('does NOT flip Streaming or forward frames when the backend lacks ScreenStreaming', async () => {\n channel.Initialize(makeContext(log, { StartRemoteBrowserScreencast: { Streaming: false } }));\n const surface = makeSurface();\n channel.BindSurface(surface);\n await flush();\n\n expect(surface.Streaming).toBe(false);\n channel.OnScreencastFrame('AAAA');\n expect(surface.RenderedFrames).toEqual([]); // ignored — channel is not streaming\n });\n\n it('stops the screencast on UnbindSurface when it had started', async () => {\n channel.Initialize(makeContext(log, { StartRemoteBrowserScreencast: { Streaming: true } }));\n const surface = makeSurface();\n channel.BindSurface(surface);\n await flush();\n\n channel.UnbindSurface();\n await flush();\n\n const stopCall = log.Calls.find(c => c.Query.includes('StopRemoteBrowserScreencast'));\n expect(stopCall).toBeTruthy();\n expect(stopCall?.Variables).toMatchObject({ agentSessionID: 'session-1' });\n // After unbind, late frames are ignored (not streaming, no surface).\n channel.OnScreencastFrame('CCCC');\n expect(surface.RenderedFrames).toEqual([]);\n });\n});\n\ndescribe('RemoteBrowserChannel — human takeover (relay surface input to the server)', () => {\n let channel: RemoteBrowserChannel;\n let log: CtxLog;\n\n beforeEach(() => {\n channel = new RemoteBrowserChannel();\n log = { Notes: [], Calls: [] };\n });\n\n /** Minimal surface double exposing the takeover wiring: Interactive flag + HumanInput emitter. */\n function makeSurface(): RemoteBrowserSurfaceComponent & { HumanInput: EventEmitter<RemoteBrowserHumanInputEvent> } {\n const surface = {\n Streaming: false,\n Interactive: false,\n AudioAvailable: false,\n AudioMuted: false,\n HumanInput: new EventEmitter<RemoteBrowserHumanInputEvent>(),\n AudioMutedChange: new EventEmitter<boolean>(),\n RenderFrame() { /* unused here */ },\n SetCurrentUrl() { /* unused here */ }\n };\n return surface as unknown as RemoteBrowserSurfaceComponent & { HumanInput: EventEmitter<RemoteBrowserHumanInputEvent> };\n }\n\n it('enables takeover by default on bind (Interactive = true)', () => {\n channel.Initialize(makeContext(log, { StartRemoteBrowserScreencast: { Streaming: true } }));\n const surface = makeSurface();\n\n channel.BindSurface(surface);\n\n expect(surface.Interactive).toBe(true);\n });\n\n it('forwards a pointer-click HumanInput to the relay mutation with viewport coords + button', () => {\n channel.Initialize(makeContext(log, { RelayRemoteBrowserHumanInput: true }));\n const surface = makeSurface();\n channel.BindSurface(surface);\n\n surface.HumanInput.emit({ kind: 'pointer-click', x: 320, y: 480, button: 'left' });\n\n const relayCall = log.Calls.find(c => c.Query.includes('RelayRemoteBrowserHumanInput'));\n expect(relayCall).toBeTruthy();\n expect(relayCall?.Variables).toMatchObject({\n agentSessionID: 'session-1', kind: 'pointer-click', x: 320, y: 480, button: 'left', key: null\n });\n });\n\n it('forwards a scroll HumanInput to the relay mutation with viewport coords + wheel deltas', () => {\n channel.Initialize(makeContext(log, { RelayRemoteBrowserHumanInput: true }));\n const surface = makeSurface();\n channel.BindSurface(surface);\n\n surface.HumanInput.emit({ kind: 'scroll', x: 100, y: 200, deltaX: 12, deltaY: -48 });\n\n const relayCall = log.Calls.find(c => c.Query.includes('RelayRemoteBrowserHumanInput'));\n expect(relayCall).toBeTruthy();\n expect(relayCall?.Variables).toMatchObject({\n agentSessionID: 'session-1', kind: 'scroll', x: 100, y: 200, button: null, key: null, deltaX: 12, deltaY: -48\n });\n });\n\n it('forwards a key HumanInput with the key string (null pointer coords)', () => {\n channel.Initialize(makeContext(log, { RelayRemoteBrowserHumanInput: true }));\n const surface = makeSurface();\n channel.BindSurface(surface);\n\n surface.HumanInput.emit({ kind: 'key', key: 'Enter' });\n\n const relayCall = log.Calls.find(c => c.Query.includes('RelayRemoteBrowserHumanInput'));\n expect(relayCall?.Variables).toMatchObject({\n agentSessionID: 'session-1', kind: 'key', x: null, y: null, button: null, key: 'Enter', modifiers: null\n });\n });\n\n it('relays held modifiers as a comma-separated list (Ctrl/Cmd+A select-all)', () => {\n channel.Initialize(makeContext(log, { RelayRemoteBrowserHumanInput: true }));\n const surface = makeSurface();\n channel.BindSurface(surface);\n\n surface.HumanInput.emit({ kind: 'key', key: 'a', modifiers: ['Control'] });\n\n const relayCall = log.Calls.find(c => c.Query.includes('RelayRemoteBrowserHumanInput'));\n expect(relayCall?.Variables).toMatchObject({ kind: 'key', key: 'a', modifiers: 'Control' });\n });\n\n it('relays a shift-click with the modifier carried on the pointer-click', () => {\n channel.Initialize(makeContext(log, { RelayRemoteBrowserHumanInput: true }));\n const surface = makeSurface();\n channel.BindSurface(surface);\n\n surface.HumanInput.emit({ kind: 'pointer-click', x: 5, y: 6, button: 'left', modifiers: ['Shift'] });\n\n const relayCall = log.Calls.find(c => c.Query.includes('RelayRemoteBrowserHumanInput'));\n expect(relayCall?.Variables).toMatchObject({ kind: 'pointer-click', x: 5, y: 6, modifiers: 'Shift' });\n });\n\n it('relays a click-drag as pointer-down then pointer-up (drag text selection)', () => {\n channel.Initialize(makeContext(log, { RelayRemoteBrowserHumanInput: true }));\n const surface = makeSurface();\n channel.BindSurface(surface);\n\n surface.HumanInput.emit({ kind: 'pointer-down', x: 10, y: 20 });\n surface.HumanInput.emit({ kind: 'pointer-up', x: 80, y: 20 });\n\n const relayed = log.Calls\n .filter(c => c.Query.includes('RelayRemoteBrowserHumanInput'))\n .map(c => c.Variables.kind);\n expect(relayed).toEqual(['pointer-down', 'pointer-up']);\n });\n\n it('stops forwarding after UnbindSurface (subscription torn down)', () => {\n channel.Initialize(makeContext(log, { RelayRemoteBrowserHumanInput: true }));\n const surface = makeSurface();\n channel.BindSurface(surface);\n channel.UnbindSurface();\n\n surface.HumanInput.emit({ kind: 'pointer-move', x: 1, y: 2 });\n\n expect(log.Calls.find(c => c.Query.includes('RelayRemoteBrowserHumanInput'))).toBeUndefined();\n });\n});\n\ndescribe('MapToViewportCoords (display → viewport coordinate mapping)', () => {\n it('maps a click on a scaled-up canvas back to viewport pixels', () => {\n // Canvas internal resolution 1280x720, displayed at 640x360 (2x downscale) at offset (100, 50).\n // A click at display (420, 230) → local (320, 180) → viewport (640, 360).\n const rect = { left: 100, top: 50, width: 640, height: 360 };\n expect(MapToViewportCoords(420, 230, rect, 1280, 720)).toEqual({ x: 640, y: 360 });\n });\n\n it('maps 1:1 when the display size equals the internal resolution', () => {\n const rect = { left: 0, top: 0, width: 800, height: 600 };\n expect(MapToViewportCoords(120, 90, rect, 800, 600)).toEqual({ x: 120, y: 90 });\n });\n\n it('rounds to integer viewport coordinates', () => {\n const rect = { left: 0, top: 0, width: 300, height: 300 };\n // 101/300 * 1000 = 336.67 → 337 ; 100/300 * 1000 = 333.33 → 333\n expect(MapToViewportCoords(101, 100, rect, 1000, 1000)).toEqual({ x: 337, y: 333 });\n });\n\n it('returns null on a zero-size rect (divide-by-zero guard)', () => {\n expect(MapToViewportCoords(10, 10, { left: 0, top: 0, width: 0, height: 0 }, 1280, 720)).toBeNull();\n });\n\n it('returns null on an un-sized canvas', () => {\n expect(MapToViewportCoords(10, 10, { left: 0, top: 0, width: 640, height: 360 }, 0, 0)).toBeNull();\n });\n});\n"]}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * @fileoverview Compile-time interface-conformance checks for the 5 default
3
+ * slot components.
4
+ *
5
+ * These tests deliberately do NOT instantiate the components (instantiating
6
+ * Angular standalone components at runtime requires `@angular/compiler` +
7
+ * a TestBed, which adds substantial machinery for very little signal).
8
+ * Instead we verify that:
9
+ *
10
+ * - Each default class is importable and is a class (`typeof === 'function'`).
11
+ * - The class satisfies its declared interface at compile time (the
12
+ * `interfaceCheck` assignments fail TypeScript if a contract drifts).
13
+ *
14
+ * That's the actual contract we care about: any consumer-supplied component
15
+ * for a given slot will be type-compatible with the widget's interaction
16
+ * because the default it replaces was compatible.
17
+ *
18
+ * Behavioral tests for slot content rendering belong in a future commit that
19
+ * sets up TestBed + the ChatSlotDirective + the chat-area component
20
+ * together — once those are in place we'll have end-to-end coverage of the
21
+ * projection path, not just the type shape.
22
+ */
23
+ import '@angular/compiler';
24
+ //# sourceMappingURL=slot-defaults.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slot-defaults.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/slot-defaults.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,mBAAmB,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @fileoverview Compile-time interface-conformance checks for the 5 default
3
+ * slot components.
4
+ *
5
+ * These tests deliberately do NOT instantiate the components (instantiating
6
+ * Angular standalone components at runtime requires `@angular/compiler` +
7
+ * a TestBed, which adds substantial machinery for very little signal).
8
+ * Instead we verify that:
9
+ *
10
+ * - Each default class is importable and is a class (`typeof === 'function'`).
11
+ * - The class satisfies its declared interface at compile time (the
12
+ * `interfaceCheck` assignments fail TypeScript if a contract drifts).
13
+ *
14
+ * That's the actual contract we care about: any consumer-supplied component
15
+ * for a given slot will be type-compatible with the widget's interaction
16
+ * because the default it replaces was compatible.
17
+ *
18
+ * Behavioral tests for slot content rendering belong in a future commit that
19
+ * sets up TestBed + the ChatSlotDirective + the chat-area component
20
+ * together — once those are in place we'll have end-to-end coverage of the
21
+ * projection path, not just the type shape.
22
+ */
23
+ import '@angular/compiler';
24
+ import { describe, it, expect } from 'vitest';
25
+ import { MJChatEmptyStateDefaultComponent } from '../lib/components/slots/mj-chat-empty-state-default.component';
26
+ import { MJChatAgentPresenceDefaultComponent } from '../lib/components/slots/mj-chat-agent-presence-default.component';
27
+ import { MJChatHeaderDefaultComponent } from '../lib/components/slots/mj-chat-header-default.component';
28
+ import { MJChatMessageExtraDefaultComponent } from '../lib/components/slots/mj-chat-message-extra-default.component';
29
+ import { MJChatDemonstrationSurfaceDefaultComponent } from '../lib/components/slots/mj-chat-demonstration-surface-default.component';
30
+ import { MJChatMessageBubbleDefaultComponent } from '../lib/components/slots/mj-chat-message-bubble-default.component';
31
+ describe('Slot default components — exports + interface conformance', () => {
32
+ it('MJChatEmptyStateDefaultComponent is exported as a class', () => {
33
+ expect(typeof MJChatEmptyStateDefaultComponent).toBe('function');
34
+ const _interfaceCheck = true;
35
+ expect(_interfaceCheck).toBe(true);
36
+ });
37
+ it('MJChatAgentPresenceDefaultComponent is exported as a class', () => {
38
+ expect(typeof MJChatAgentPresenceDefaultComponent).toBe('function');
39
+ const _interfaceCheck = true;
40
+ expect(_interfaceCheck).toBe(true);
41
+ });
42
+ it('MJChatHeaderDefaultComponent is exported as a class', () => {
43
+ expect(typeof MJChatHeaderDefaultComponent).toBe('function');
44
+ const _interfaceCheck = true;
45
+ expect(_interfaceCheck).toBe(true);
46
+ });
47
+ it('MJChatMessageExtraDefaultComponent is exported as a class', () => {
48
+ expect(typeof MJChatMessageExtraDefaultComponent).toBe('function');
49
+ const _interfaceCheck = true;
50
+ expect(_interfaceCheck).toBe(true);
51
+ });
52
+ it('MJChatDemonstrationSurfaceDefaultComponent is exported as a class', () => {
53
+ expect(typeof MJChatDemonstrationSurfaceDefaultComponent).toBe('function');
54
+ const _interfaceCheck = true;
55
+ expect(_interfaceCheck).toBe(true);
56
+ });
57
+ it('MJChatMessageBubbleDefaultComponent is exported as a class', () => {
58
+ expect(typeof MJChatMessageBubbleDefaultComponent).toBe('function');
59
+ const _interfaceCheck = true;
60
+ expect(_interfaceCheck).toBe(true);
61
+ });
62
+ });
63
+ //# sourceMappingURL=slot-defaults.test.js.map