@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,524 @@
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 { RegisterClass } from '@memberjunction/global';
8
+ import { BaseRealtimeChannelClient } from '../channels/base-realtime-channel-client';
9
+ import { RemoteBrowserSurfaceComponent } from './remote-browser-surface.component';
10
+ import { MapToolToAction, REMOTE_BROWSER_TOOL_DEFINITIONS, REMOTE_BROWSER_TOOL_NAMES, REMOTE_BROWSER_TOOL_PREFIX, RemoteBrowserToolArgError, } from './remote-browser-tools';
11
+ import { MediaSourceAudioSink, RemoteBrowserAudioPlayer } from './remote-browser-audio-player';
12
+ /**
13
+ * GraphQL mutation that drives the SERVER-hosted browser. The channel awaits it for every
14
+ * `browser_*` tool call and feeds the result back to the model.
15
+ */
16
+ const EXECUTE_REMOTE_BROWSER_ACTION_MUTATION = `
17
+ mutation ExecuteRemoteBrowserAction(
18
+ $agentSessionID: String!, $kind: String!, $url: String, $selector: String,
19
+ $x: Float, $y: Float, $text: String, $key: String,
20
+ $deltaX: Float, $deltaY: Float, $ms: Float
21
+ ) {
22
+ ExecuteRemoteBrowserAction(
23
+ agentSessionID: $agentSessionID, kind: $kind, url: $url, selector: $selector,
24
+ x: $x, y: $y, text: $text, key: $key,
25
+ deltaX: $deltaX, deltaY: $deltaY, ms: $ms
26
+ ) {
27
+ Success
28
+ CurrentUrl
29
+ Detail
30
+ }
31
+ }
32
+ `;
33
+ /**
34
+ * GraphQL mutation that asks the server to start PUSHING live CDP screencast frames for the session.
35
+ * Returns `Streaming: true` when the backend supports `ScreenStreaming` and the stream started; the
36
+ * channel then drives the surface's canvas. `Streaming: false` (capability absent / start failed) leaves
37
+ * the surface on its 700ms snapshot poll fallback.
38
+ */
39
+ const START_SCREENCAST_MUTATION = `
40
+ mutation StartRemoteBrowserScreencast($agentSessionID: String!) {
41
+ StartRemoteBrowserScreencast(agentSessionID: $agentSessionID) {
42
+ Streaming
43
+ }
44
+ }
45
+ `;
46
+ /** GraphQL mutation that stops the server-pushed screencast (best-effort teardown). */
47
+ const STOP_SCREENCAST_MUTATION = `
48
+ mutation StopRemoteBrowserScreencast($agentSessionID: String!) {
49
+ StopRemoteBrowserScreencast(agentSessionID: $agentSessionID)
50
+ }
51
+ `;
52
+ /**
53
+ * GraphQL mutation that asks the server to start PUSHING live tab-audio chunks for the session — so a
54
+ * co-agent demoing a video/audio site is HEARD. Returns `Streaming: true` when the backend can capture
55
+ * audio (v1 gates by backend implementation) and the stream started; `Streaming: false` means no audio
56
+ * (capability absent / start failed) and the client simply plays nothing.
57
+ */
58
+ const START_AUDIO_STREAM_MUTATION = `
59
+ mutation StartRemoteBrowserAudioStream($agentSessionID: String!) {
60
+ StartRemoteBrowserAudioStream(agentSessionID: $agentSessionID) {
61
+ Streaming
62
+ }
63
+ }
64
+ `;
65
+ /** GraphQL mutation that stops the server-pushed tab-audio stream (best-effort teardown). */
66
+ const STOP_AUDIO_STREAM_MUTATION = `
67
+ mutation StopRemoteBrowserAudioStream($agentSessionID: String!) {
68
+ StopRemoteBrowserAudioStream(agentSessionID: $agentSessionID)
69
+ }
70
+ `;
71
+ /**
72
+ * HUMAN-TAKEOVER mutation — relays ONE pointer/keyboard input the user performed on the live canvas into the
73
+ * server-hosted browser (Collaborative control). Best-effort: the server returns `false` when the backend
74
+ * lacks `HumanTakeover` or no live browser exists; the channel doesn't act on the boolean.
75
+ */
76
+ const RELAY_HUMAN_INPUT_MUTATION = `
77
+ mutation RelayRemoteBrowserHumanInput(
78
+ $agentSessionID: String!, $kind: String!, $x: Float, $y: Float, $button: String, $key: String,
79
+ $deltaX: Float, $deltaY: Float, $modifiers: String
80
+ ) {
81
+ RelayRemoteBrowserHumanInput(
82
+ agentSessionID: $agentSessionID, kind: $kind, x: $x, y: $y, button: $button, key: $key,
83
+ deltaX: $deltaX, deltaY: $deltaY, modifiers: $modifiers
84
+ )
85
+ }
86
+ `;
87
+ /** GraphQL query the surface fetcher runs — the live screenshot + URL of the server browser. */
88
+ const REMOTE_BROWSER_SNAPSHOT_QUERY = `
89
+ query RemoteBrowserSnapshot($agentSessionID: String!) {
90
+ RemoteBrowserSnapshot(agentSessionID: $agentSessionID) {
91
+ ScreenshotBase64
92
+ CurrentUrl
93
+ }
94
+ }
95
+ `;
96
+ /**
97
+ * VISION-QUERY mutation (separate from {@link EXECUTE_REMOTE_BROWSER_ACTION_MUTATION} — this OBSERVES the page,
98
+ * it does not drive it). The server captures the live screenshot and runs a fast vision model over it, so the
99
+ * non-omnimodal voice agent can "see" the page: a text description and/or localized elements with pixel
100
+ * centroids. Backs both `browser_DescribePage` (no target) and `browser_LocateElement` (a target description).
101
+ */
102
+ const INTERPRET_PAGE_MUTATION = `
103
+ mutation InterpretRemoteBrowserPage($agentSessionID: String!, $query: String) {
104
+ InterpretRemoteBrowserPage(agentSessionID: $agentSessionID, query: $query) {
105
+ Description
106
+ Elements {
107
+ Label
108
+ X
109
+ Y
110
+ Confidence
111
+ }
112
+ Detail
113
+ }
114
+ }
115
+ `;
116
+ /**
117
+ * The REMOTE BROWSER as a pluggable interactive channel — a {@link BaseRealtimeChannelClient}
118
+ * resolved from the `MJ: AI Agent Channels` registry row whose `ClientPluginClass` is
119
+ * `'RealtimeRemoteBrowserChannel'`. One instance per session (ClassFactory-created at session
120
+ * start).
121
+ *
122
+ * Topology (CLIENT-DIRECT, like the whiteboard — the browser just happens to live on the
123
+ * server):
124
+ * - **Action**: the `browser_*` client-executed tool set ({@link REMOTE_BROWSER_TOOL_DEFINITIONS}).
125
+ * {@link ApplyAgentTool} maps a tool call → a normalized {@link RemoteBrowserAction}
126
+ * ({@link MapToolToAction}) and awaits the `ExecuteRemoteBrowserAction` GraphQL mutation
127
+ * (via {@link RealtimeChannelContext.ExecuteServerAction}) to drive the server browser,
128
+ * returning a concise JSON result for the model. Never throws — argument and transport
129
+ * failures map to a `{ success: false, error }` payload.
130
+ * - **Perception**: after a successful action, a `[browser]` context note carries the new
131
+ * page URL so the agent perceives where the page went; the surface independently polls a
132
+ * live screenshot.
133
+ * - **Surface**: {@link RemoteBrowserSurfaceComponent}, created dynamically in the channel
134
+ * tab; the plugin hands it the session's provider + id in {@link BindSurface} so it can
135
+ * poll `RemoteBrowserSnapshot`. HUMAN TAKEOVER is enabled by default (Collaborative): the
136
+ * user can click/type into the live canvas and those events relay to the server browser via
137
+ * {@link RELAY_HUMAN_INPUT_MUTATION} (server-gated on the backend's `HumanTakeover` capability).
138
+ *
139
+ * The channel keeps NO client-side state of record — the browser's state lives entirely on
140
+ * the server — so {@link SerializeState} / {@link RestoreState} use the base no-op behavior.
141
+ */
142
+ let RemoteBrowserChannel = class RemoteBrowserChannel extends BaseRealtimeChannelClient {
143
+ /** The live bound surface, when the channel tab's pane is instantiated. */
144
+ surface = null;
145
+ /** Subscription to the bound surface's `HumanInput` output, torn down on unbind/dispose. */
146
+ humanInputSub = null;
147
+ /**
148
+ * Whether the server confirmed it is PUSHING screencast frames for this session (`ScreenStreaming`
149
+ * supported). When `true` the surface renders pushed frames on its canvas and skips its poll, and
150
+ * {@link OnScreencastFrame} forwards each pushed frame to it. When `false` the surface keeps polling.
151
+ */
152
+ streaming = false;
153
+ /**
154
+ * Whether the server confirmed it is PUSHING tab-audio chunks for this session (the backend can capture
155
+ * audio). When `true` {@link OnAudioChunk} feeds each pushed chunk to {@link audioPlayer}; when `false`
156
+ * no audio plays.
157
+ */
158
+ audioStreaming = false;
159
+ /** The client-side audio player fed pushed chunks while {@link audioStreaming}; `null` until started. */
160
+ audioPlayer = null;
161
+ /** Subscription to the bound surface's `AudioMutedChange` output (the speaker toggle), torn down on unbind. */
162
+ audioMutedSub = null;
163
+ get ChannelName() {
164
+ return 'Remote Browser';
165
+ }
166
+ get ToolNamePrefix() {
167
+ return REMOTE_BROWSER_TOOL_PREFIX;
168
+ }
169
+ get TabTitle() {
170
+ return 'Browser';
171
+ }
172
+ get TabIcon() {
173
+ return 'fa-solid fa-globe';
174
+ }
175
+ GetToolDefinitions() {
176
+ return REMOTE_BROWSER_TOOL_DEFINITIONS;
177
+ }
178
+ GetSurfaceComponent() {
179
+ return RemoteBrowserSurfaceComponent;
180
+ }
181
+ /** First-run intro shown the first time the user opens the Browser tab (once per user). */
182
+ GetOnboardingDetails() {
183
+ return {
184
+ Heading: 'Remote Browser',
185
+ Description: 'The agent drives a live web browser on the server and you can watch it work right here. ' +
186
+ 'You can also grab the wheel: click and type directly on the live page and the agent picks ' +
187
+ 'up right where you left off.',
188
+ Tips: [
189
+ 'Ask the agent to look something up, open a site or fill in a form — it controls the page.',
190
+ "Click on the live view to take over, then click and type to drive it yourself — the \"You're driving\" badge shows when takeover is active.",
191
+ 'The current page URL is shown so you always know where the agent has navigated.',
192
+ ],
193
+ IconClass: 'fa-solid fa-globe',
194
+ };
195
+ }
196
+ /**
197
+ * Wires the dynamically-created surface: hands it a snapshot fetcher closing over the
198
+ * channel context (session id + provider live there), so the surface stays transport-
199
+ * agnostic. Set BEFORE the surface's first change detection (the pane binds synchronously),
200
+ * so its `ngOnInit` poll has the fetcher.
201
+ *
202
+ * Also asks the server to start a live screencast. When the backend supports `ScreenStreaming` the
203
+ * server starts PUSHING frames and reports `Streaming: true`; we flip the surface to canvas mode (its
204
+ * poll is then skipped) and {@link OnScreencastFrame} paints each pushed frame. When the backend lacks
205
+ * the capability (`Streaming: false`) the surface keeps the snapshot poll already running — graceful
206
+ * fallback, no further action.
207
+ *
208
+ * HUMAN TAKEOVER is enabled BY DEFAULT (Collaborative): the surface is flipped `Interactive = true` and
209
+ * its `HumanInput` output subscribed, forwarding each pointer/keyboard event to the server via
210
+ * {@link RELAY_HUMAN_INPUT_MUTATION}. Takeover only takes effect on the canvas (screencast) path; the
211
+ * `<img>` poll fallback stays view-only. The server gates the actual routing on the backend's
212
+ * `HumanTakeover` capability, so this is safe to enable unconditionally.
213
+ */
214
+ BindSurface(instance) {
215
+ this.surface = instance;
216
+ instance.Fetch = () => this.fetchSnapshot();
217
+ instance.Interactive = true;
218
+ this.humanInputSub = instance.HumanInput.subscribe((input) => this.relayHumanInput(input));
219
+ this.audioMutedSub = instance.AudioMutedChange.subscribe((muted) => this.audioPlayer?.SetMuted(muted));
220
+ void this.startScreencast(instance);
221
+ void this.startAudioStream(instance);
222
+ }
223
+ UnbindSurface() {
224
+ this.humanInputSub?.unsubscribe();
225
+ this.humanInputSub = null;
226
+ this.audioMutedSub?.unsubscribe();
227
+ this.audioMutedSub = null;
228
+ void this.stopScreencast();
229
+ void this.stopAudioStream();
230
+ this.surface = null;
231
+ }
232
+ Dispose() {
233
+ this.humanInputSub?.unsubscribe();
234
+ this.humanInputSub = null;
235
+ this.audioMutedSub?.unsubscribe();
236
+ this.audioMutedSub = null;
237
+ void this.stopScreencast();
238
+ void this.stopAudioStream();
239
+ super.Dispose();
240
+ }
241
+ /**
242
+ * Forwards one PUSHED screencast frame to the bound surface's canvas. Called by the session service
243
+ * when a `RemoteBrowserScreencastFrame` arrives on the push-status stream for THIS session. No-op when
244
+ * the channel isn't streaming or has no bound surface (e.g. the tab pane is collapsed).
245
+ *
246
+ * @param dataBase64 The frame image as raw base64 JPEG (no `data:` prefix).
247
+ */
248
+ OnScreencastFrame(dataBase64) {
249
+ if (this.streaming) {
250
+ this.surface?.RenderFrame(dataBase64);
251
+ }
252
+ }
253
+ /**
254
+ * Feeds one PUSHED tab-audio chunk to the client-side audio player. Called by the session service when a
255
+ * `RemoteBrowserAudioChunk` arrives on the push-status stream for THIS session. No-op when the channel
256
+ * isn't audio-streaming or the player has been torn down.
257
+ *
258
+ * @param chunk The pushed audio chunk (base64 webm-opus + codec/seq metadata).
259
+ */
260
+ OnAudioChunk(chunk) {
261
+ if (this.audioStreaming) {
262
+ this.audioPlayer?.Enqueue(chunk);
263
+ }
264
+ }
265
+ /**
266
+ * Asks the server to start the live screencast and, on success, flips the surface to canvas mode so it
267
+ * stops polling and starts painting pushed frames. Best-effort: a `null`/`Streaming: false` result
268
+ * (capability absent or transport failure) leaves the surface on its poll fallback.
269
+ */
270
+ async startScreencast(instance) {
271
+ const sessionId = this.Context?.AgentSessionID;
272
+ if (!sessionId) {
273
+ return;
274
+ }
275
+ const data = await this.Context?.ExecuteServerAction(START_SCREENCAST_MUTATION, { agentSessionID: sessionId });
276
+ if (data?.StartRemoteBrowserScreencast?.Streaming === true) {
277
+ this.streaming = true;
278
+ instance.Streaming = true;
279
+ }
280
+ }
281
+ /**
282
+ * Asks the server to start the live tab-audio stream and, on success, spins up the client-side
283
+ * {@link RemoteBrowserAudioPlayer} (speaker ON by default — the call is the activating user gesture) so
284
+ * pushed chunks play. Best-effort: a `null`/`Streaming: false` result (no audio capability or transport
285
+ * failure) leaves the channel silent with no player.
286
+ *
287
+ * @param instance The bound surface, flipped to show the speaker toggle when audio starts.
288
+ */
289
+ async startAudioStream(instance) {
290
+ const sessionId = this.Context?.AgentSessionID;
291
+ if (!sessionId) {
292
+ return;
293
+ }
294
+ const data = await this.Context?.ExecuteServerAction(START_AUDIO_STREAM_MUTATION, { agentSessionID: sessionId });
295
+ if (data?.StartRemoteBrowserAudioStream?.Streaming === true) {
296
+ this.audioStreaming = true;
297
+ this.audioPlayer = new RemoteBrowserAudioPlayer(new MediaSourceAudioSink());
298
+ // Speaker defaults ON; reflect that on the surface so the toggle renders un-muted.
299
+ instance.AudioAvailable = true;
300
+ instance.AudioMuted = false;
301
+ }
302
+ }
303
+ /** Asks the server to stop the tab-audio stream (best-effort), disposes the player, and clears state. */
304
+ async stopAudioStream() {
305
+ const wasStreaming = this.audioStreaming;
306
+ this.audioStreaming = false;
307
+ this.audioPlayer?.Dispose();
308
+ this.audioPlayer = null;
309
+ const sessionId = this.Context?.AgentSessionID;
310
+ if (!wasStreaming || !sessionId) {
311
+ return;
312
+ }
313
+ await this.Context?.ExecuteServerAction(STOP_AUDIO_STREAM_MUTATION, { agentSessionID: sessionId });
314
+ }
315
+ /**
316
+ * Relays ONE human-takeover input from the surface to the server's {@link RELAY_HUMAN_INPUT_MUTATION}.
317
+ * Best-effort and fire-and-forget: never throws, and the boolean result is ignored (the server already
318
+ * gates routing on the backend's `HumanTakeover` capability). No-op when no live session id exists.
319
+ *
320
+ * @param input The flattened pointer/keyboard input the user performed on the live canvas.
321
+ */
322
+ relayHumanInput(input) {
323
+ const sessionId = this.Context?.AgentSessionID;
324
+ if (!sessionId) {
325
+ return;
326
+ }
327
+ void this.Context?.ExecuteServerAction(RELAY_HUMAN_INPUT_MUTATION, {
328
+ agentSessionID: sessionId,
329
+ kind: input.kind,
330
+ x: input.x ?? null,
331
+ y: input.y ?? null,
332
+ button: input.button ?? null,
333
+ key: input.key ?? null,
334
+ deltaX: input.deltaX ?? null,
335
+ deltaY: input.deltaY ?? null,
336
+ // Server expects a comma-separated modifier list (e.g. "Shift,Control"); null when none held.
337
+ modifiers: input.modifiers && input.modifiers.length > 0 ? input.modifiers.join(',') : null,
338
+ });
339
+ }
340
+ /** Asks the server to stop the screencast (best-effort) and clears the streaming flag. */
341
+ async stopScreencast() {
342
+ const wasStreaming = this.streaming;
343
+ this.streaming = false;
344
+ const sessionId = this.Context?.AgentSessionID;
345
+ if (!wasStreaming || !sessionId) {
346
+ return;
347
+ }
348
+ await this.Context?.ExecuteServerAction(STOP_SCREENCAST_MUTATION, { agentSessionID: sessionId });
349
+ }
350
+ /**
351
+ * Fetches one live snapshot of the server browser for the surface — the PERCEPTION poll.
352
+ * Best-effort by contract ({@link RemoteBrowserSnapshotFetcher}): returns `null` when no
353
+ * session is live or the query fails (the surface keeps its last good frame).
354
+ */
355
+ async fetchSnapshot() {
356
+ const sessionId = this.Context?.AgentSessionID;
357
+ if (!sessionId) {
358
+ return null;
359
+ }
360
+ const data = await this.Context?.ExecuteServerAction(REMOTE_BROWSER_SNAPSHOT_QUERY, { agentSessionID: sessionId });
361
+ return data?.RemoteBrowserSnapshot ?? null;
362
+ }
363
+ /**
364
+ * Executes ONE `browser_*` tool call: maps it to a server action and awaits the
365
+ * `ExecuteRemoteBrowserAction` mutation, then returns a concise JSON result the model reads.
366
+ * On success it also feeds the new page URL to the agent as a `[browser]` context note. Never
367
+ * throws — bad arguments, no live session, and server failures all become a failed-result
368
+ * payload so the model can narrate the problem.
369
+ */
370
+ async ApplyAgentTool(toolName, argsJson) {
371
+ // VISION-QUERY branch: the two interpreter tools OBSERVE the page (run a vision model over a screenshot)
372
+ // rather than drive it, so they route to the separate interpret mutation, not MapToolToAction.
373
+ if (toolName === REMOTE_BROWSER_TOOL_NAMES.DescribePage) {
374
+ return this.interpretPage(undefined);
375
+ }
376
+ if (toolName === REMOTE_BROWSER_TOOL_NAMES.LocateElement) {
377
+ const description = this.asArgString(this.parseArgs(argsJson)['description']);
378
+ if (!description) {
379
+ return this.fail('browser_LocateElement requires a "description" of the element to find.');
380
+ }
381
+ return this.interpretPage(description);
382
+ }
383
+ let action;
384
+ try {
385
+ action = MapToolToAction(toolName, this.parseArgs(argsJson));
386
+ }
387
+ catch (error) {
388
+ const message = error instanceof RemoteBrowserToolArgError ? error.message : 'Invalid Remote Browser tool arguments.';
389
+ return this.fail(message);
390
+ }
391
+ const sessionId = this.Context?.AgentSessionID;
392
+ if (!sessionId) {
393
+ // Diagnostic: distinguishes "channel never Initialized (no Context)" from "session id not yet
394
+ // set on the service" — the two distinct causes of a null id at tool-execution time.
395
+ console.warn('[RemoteBrowser] browser tool invoked but AgentSessionID is null', {
396
+ tool: toolName,
397
+ hasContext: !!this.Context,
398
+ agentSessionID: this.Context?.AgentSessionID ?? null,
399
+ });
400
+ return this.fail('No live browser session is available yet — the realtime session may still be connecting; try again in a moment.');
401
+ }
402
+ const data = await this.Context?.ExecuteServerAction(EXECUTE_REMOTE_BROWSER_ACTION_MUTATION, this.buildVariables(sessionId, action));
403
+ const result = data?.ExecuteRemoteBrowserAction ?? null;
404
+ if (!result) {
405
+ return this.fail('The browser action could not be executed (no response from the server).');
406
+ }
407
+ if (!result.Success) {
408
+ return this.fail(result.Detail ?? 'The browser action failed.');
409
+ }
410
+ if (result.CurrentUrl) {
411
+ // PERCEPTION: tell the agent where the page is now (background — no spoken reply).
412
+ this.Context?.SendContextNote(`[browser] current page: ${result.CurrentUrl}`);
413
+ // In streaming mode the surface's snapshot poll (which carries the URL) is stopped, so push the new
414
+ // URL to the live-view bar directly — otherwise it stays stuck on "No page loaded yet".
415
+ this.surface?.SetCurrentUrl(result.CurrentUrl);
416
+ }
417
+ return this.ok(result.CurrentUrl, result.Detail);
418
+ }
419
+ /** Builds the flat mutation variables from a normalized action (omitted fields ride as null). */
420
+ buildVariables(sessionId, action) {
421
+ return {
422
+ agentSessionID: sessionId,
423
+ kind: action.Kind,
424
+ url: action.Url ?? null,
425
+ selector: action.Selector ?? null,
426
+ x: action.X ?? null,
427
+ y: action.Y ?? null,
428
+ text: action.Text ?? null,
429
+ key: action.Key ?? null,
430
+ deltaX: action.DeltaX ?? null,
431
+ deltaY: action.DeltaY ?? null,
432
+ ms: action.Ms ?? null,
433
+ };
434
+ }
435
+ /**
436
+ * Runs the VISION-QUERY path for the `browser_DescribePage` / `browser_LocateElement` tools: awaits the
437
+ * `InterpretRemoteBrowserPage` mutation (server captures a screenshot + runs a fast vision model) and returns
438
+ * a concise JSON string the model reads. For a describe (no `query`) it returns `{ description }`; for a locate
439
+ * it returns `{ found, element, all }` so the agent can `browser_Click` the centroid. Never throws — a null
440
+ * session, no response, or a server detail-only result all map to a result string the model can narrate.
441
+ *
442
+ * @param query The visual target to locate, or `undefined` for a plain page description.
443
+ * @returns A JSON string describing the interpretation for the model.
444
+ */
445
+ async interpretPage(query) {
446
+ const sessionId = this.Context?.AgentSessionID;
447
+ if (!sessionId) {
448
+ console.warn('[RemoteBrowser] interpret tool invoked but AgentSessionID is null', {
449
+ hasContext: !!this.Context,
450
+ agentSessionID: this.Context?.AgentSessionID ?? null,
451
+ });
452
+ return this.fail('No live browser session is available yet — the realtime session may still be connecting; try again in a moment.');
453
+ }
454
+ const data = await this.Context?.ExecuteServerAction(INTERPRET_PAGE_MUTATION, {
455
+ agentSessionID: sessionId,
456
+ query: query ?? null,
457
+ });
458
+ const result = data?.InterpretRemoteBrowserPage ?? null;
459
+ if (!result) {
460
+ return this.fail('The page could not be interpreted (no response from the server).');
461
+ }
462
+ if (query === undefined) {
463
+ // DescribePage — hand back the description (or the server's detail note when nothing was interpreted).
464
+ return JSON.stringify({ description: result.Description ?? result.Detail ?? null });
465
+ }
466
+ // LocateElement — surface whether anything matched, the best match, and all candidates.
467
+ const elements = result.Elements ?? [];
468
+ const best = elements[0];
469
+ return JSON.stringify({
470
+ found: elements.length > 0,
471
+ element: best ? { label: best.Label, x: best.X, y: best.Y } : null,
472
+ all: elements,
473
+ ...(result.Detail ? { detail: result.Detail } : {}),
474
+ });
475
+ }
476
+ /** Coerces a parsed tool-arg to a non-empty string, or `undefined` when absent / wrong-typed. */
477
+ asArgString(value) {
478
+ return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;
479
+ }
480
+ /** Parses the tool-args JSON into a plain object; returns `{}` for empty/malformed input. */
481
+ parseArgs(argsJson) {
482
+ const trimmed = argsJson?.trim();
483
+ if (!trimmed) {
484
+ return {};
485
+ }
486
+ try {
487
+ const parsed = JSON.parse(trimmed);
488
+ return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
489
+ }
490
+ catch {
491
+ return {};
492
+ }
493
+ }
494
+ /** Builds a successful tool-result JSON string for the model. */
495
+ ok(currentUrl, detail) {
496
+ const result = { success: true };
497
+ if (currentUrl) {
498
+ result.currentUrl = currentUrl;
499
+ }
500
+ if (detail) {
501
+ result.detail = detail;
502
+ }
503
+ return JSON.stringify(result);
504
+ }
505
+ /** Builds a failed tool-result JSON string for the model. */
506
+ fail(error) {
507
+ const result = { success: false, error };
508
+ return JSON.stringify(result);
509
+ }
510
+ };
511
+ RemoteBrowserChannel = __decorate([
512
+ RegisterClass(BaseRealtimeChannelClient, 'RealtimeRemoteBrowserChannel')
513
+ ], RemoteBrowserChannel);
514
+ export { RemoteBrowserChannel };
515
+ /**
516
+ * Tree-shaking prevention: the Remote Browser channel is resolved dynamically through the
517
+ * ClassFactory (by the registry row's `ClientPluginClass` key), so this static call is what
518
+ * keeps its `@RegisterClass` side effect from being eliminated by the bundler. Called from
519
+ * `conversations.module.ts` alongside {@link LoadRealtimeWhiteboardChannel}.
520
+ */
521
+ export function LoadRealtimeRemoteBrowserChannel() {
522
+ // intentional no-op — the import side effect performs the registration
523
+ }
524
+ //# sourceMappingURL=remote-browser-channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-browser-channel.js","sourceRoot":"","sources":["../../../../../src/lib/components/realtime/remote-browser/remote-browser-channel.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Type } from '@angular/core';\nimport type { Subscription } from 'rxjs';\nimport { RegisterClass } from '@memberjunction/global';\nimport { JSONValue, RealtimeToolDefinition } from '@memberjunction/ai';\nimport { BaseRealtimeChannelClient, ChannelOnboardingDetails } from '../channels/base-realtime-channel-client';\nimport { RemoteBrowserHumanInputEvent, RemoteBrowserSnapshotView, RemoteBrowserSurfaceComponent } from './remote-browser-surface.component';\nimport {\n MapToolToAction,\n REMOTE_BROWSER_TOOL_DEFINITIONS,\n REMOTE_BROWSER_TOOL_NAMES,\n REMOTE_BROWSER_TOOL_PREFIX,\n RemoteBrowserAction,\n RemoteBrowserToolArgError,\n} from './remote-browser-tools';\nimport { MediaSourceAudioSink, RemoteBrowserAudioChunkInput, RemoteBrowserAudioPlayer } from './remote-browser-audio-player';\n\n/**\n * GraphQL mutation that drives the SERVER-hosted browser. The channel awaits it for every\n * `browser_*` tool call and feeds the result back to the model.\n */\nconst EXECUTE_REMOTE_BROWSER_ACTION_MUTATION = `\n mutation ExecuteRemoteBrowserAction(\n $agentSessionID: String!, $kind: String!, $url: String, $selector: String,\n $x: Float, $y: Float, $text: String, $key: String,\n $deltaX: Float, $deltaY: Float, $ms: Float\n ) {\n ExecuteRemoteBrowserAction(\n agentSessionID: $agentSessionID, kind: $kind, url: $url, selector: $selector,\n x: $x, y: $y, text: $text, key: $key,\n deltaX: $deltaX, deltaY: $deltaY, ms: $ms\n ) {\n Success\n CurrentUrl\n Detail\n }\n }\n`;\n\n/** The narrow projection of the `ExecuteRemoteBrowserAction` mutation payload the channel reads. */\ninterface ExecuteRemoteBrowserActionResult {\n ExecuteRemoteBrowserAction: {\n Success: boolean;\n CurrentUrl: string | null;\n Detail: string | null;\n } | null;\n}\n\n/**\n * GraphQL mutation that asks the server to start PUSHING live CDP screencast frames for the session.\n * Returns `Streaming: true` when the backend supports `ScreenStreaming` and the stream started; the\n * channel then drives the surface's canvas. `Streaming: false` (capability absent / start failed) leaves\n * the surface on its 700ms snapshot poll fallback.\n */\nconst START_SCREENCAST_MUTATION = `\n mutation StartRemoteBrowserScreencast($agentSessionID: String!) {\n StartRemoteBrowserScreencast(agentSessionID: $agentSessionID) {\n Streaming\n }\n }\n`;\n\n/** GraphQL mutation that stops the server-pushed screencast (best-effort teardown). */\nconst STOP_SCREENCAST_MUTATION = `\n mutation StopRemoteBrowserScreencast($agentSessionID: String!) {\n StopRemoteBrowserScreencast(agentSessionID: $agentSessionID)\n }\n`;\n\n/**\n * GraphQL mutation that asks the server to start PUSHING live tab-audio chunks for the session — so a\n * co-agent demoing a video/audio site is HEARD. Returns `Streaming: true` when the backend can capture\n * audio (v1 gates by backend implementation) and the stream started; `Streaming: false` means no audio\n * (capability absent / start failed) and the client simply plays nothing.\n */\nconst START_AUDIO_STREAM_MUTATION = `\n mutation StartRemoteBrowserAudioStream($agentSessionID: String!) {\n StartRemoteBrowserAudioStream(agentSessionID: $agentSessionID) {\n Streaming\n }\n }\n`;\n\n/** GraphQL mutation that stops the server-pushed tab-audio stream (best-effort teardown). */\nconst STOP_AUDIO_STREAM_MUTATION = `\n mutation StopRemoteBrowserAudioStream($agentSessionID: String!) {\n StopRemoteBrowserAudioStream(agentSessionID: $agentSessionID)\n }\n`;\n\n/** The narrow projection of the `StartRemoteBrowserAudioStream` mutation payload the channel reads. */\ninterface StartRemoteBrowserAudioStreamResult {\n StartRemoteBrowserAudioStream: {\n Streaming: boolean;\n } | null;\n}\n\n/**\n * HUMAN-TAKEOVER mutation — relays ONE pointer/keyboard input the user performed on the live canvas into the\n * server-hosted browser (Collaborative control). Best-effort: the server returns `false` when the backend\n * lacks `HumanTakeover` or no live browser exists; the channel doesn't act on the boolean.\n */\nconst RELAY_HUMAN_INPUT_MUTATION = `\n mutation RelayRemoteBrowserHumanInput(\n $agentSessionID: String!, $kind: String!, $x: Float, $y: Float, $button: String, $key: String,\n $deltaX: Float, $deltaY: Float, $modifiers: String\n ) {\n RelayRemoteBrowserHumanInput(\n agentSessionID: $agentSessionID, kind: $kind, x: $x, y: $y, button: $button, key: $key,\n deltaX: $deltaX, deltaY: $deltaY, modifiers: $modifiers\n )\n }\n`;\n\n/** The narrow projection of the `StartRemoteBrowserScreencast` mutation payload the channel reads. */\ninterface StartRemoteBrowserScreencastResult {\n StartRemoteBrowserScreencast: {\n Streaming: boolean;\n } | null;\n}\n\n/** GraphQL query the surface fetcher runs — the live screenshot + URL of the server browser. */\nconst REMOTE_BROWSER_SNAPSHOT_QUERY = `\n query RemoteBrowserSnapshot($agentSessionID: String!) {\n RemoteBrowserSnapshot(agentSessionID: $agentSessionID) {\n ScreenshotBase64\n CurrentUrl\n }\n }\n`;\n\n/** The narrow projection of the `RemoteBrowserSnapshot` query payload the channel reads. */\ninterface RemoteBrowserSnapshotResult {\n RemoteBrowserSnapshot: RemoteBrowserSnapshotView | null;\n}\n\n/**\n * VISION-QUERY mutation (separate from {@link EXECUTE_REMOTE_BROWSER_ACTION_MUTATION} — this OBSERVES the page,\n * it does not drive it). The server captures the live screenshot and runs a fast vision model over it, so the\n * non-omnimodal voice agent can \"see\" the page: a text description and/or localized elements with pixel\n * centroids. Backs both `browser_DescribePage` (no target) and `browser_LocateElement` (a target description).\n */\nconst INTERPRET_PAGE_MUTATION = `\n mutation InterpretRemoteBrowserPage($agentSessionID: String!, $query: String) {\n InterpretRemoteBrowserPage(agentSessionID: $agentSessionID, query: $query) {\n Description\n Elements {\n Label\n X\n Y\n Confidence\n }\n Detail\n }\n }\n`;\n\n/** One element localized by the visual interpreter — label + pixel centroid + confidence. */\ninterface RemoteBrowserInterpretedElement {\n Label: string;\n X: number;\n Y: number;\n Confidence: number;\n}\n\n/** The narrow projection of the `InterpretRemoteBrowserPage` mutation payload the channel reads. */\ninterface InterpretRemoteBrowserPageResult {\n InterpretRemoteBrowserPage: {\n Description: string | null;\n Elements: RemoteBrowserInterpretedElement[];\n Detail: string | null;\n } | null;\n}\n\n/** The result payload (serialized to JSON) the channel returns to the model per tool call. */\ninterface RemoteBrowserToolResult {\n success: boolean;\n /** The page URL after the action, when the server reported one. */\n currentUrl?: string;\n /** Human-readable detail (e.g. extracted page text for `browser_GetPageText`, or an error). */\n detail?: string;\n /** Error description when `success` is false. */\n error?: string;\n}\n\n/**\n * The REMOTE BROWSER as a pluggable interactive channel — a {@link BaseRealtimeChannelClient}\n * resolved from the `MJ: AI Agent Channels` registry row whose `ClientPluginClass` is\n * `'RealtimeRemoteBrowserChannel'`. One instance per session (ClassFactory-created at session\n * start).\n *\n * Topology (CLIENT-DIRECT, like the whiteboard — the browser just happens to live on the\n * server):\n * - **Action**: the `browser_*` client-executed tool set ({@link REMOTE_BROWSER_TOOL_DEFINITIONS}).\n * {@link ApplyAgentTool} maps a tool call → a normalized {@link RemoteBrowserAction}\n * ({@link MapToolToAction}) and awaits the `ExecuteRemoteBrowserAction` GraphQL mutation\n * (via {@link RealtimeChannelContext.ExecuteServerAction}) to drive the server browser,\n * returning a concise JSON result for the model. Never throws — argument and transport\n * failures map to a `{ success: false, error }` payload.\n * - **Perception**: after a successful action, a `[browser]` context note carries the new\n * page URL so the agent perceives where the page went; the surface independently polls a\n * live screenshot.\n * - **Surface**: {@link RemoteBrowserSurfaceComponent}, created dynamically in the channel\n * tab; the plugin hands it the session's provider + id in {@link BindSurface} so it can\n * poll `RemoteBrowserSnapshot`. HUMAN TAKEOVER is enabled by default (Collaborative): the\n * user can click/type into the live canvas and those events relay to the server browser via\n * {@link RELAY_HUMAN_INPUT_MUTATION} (server-gated on the backend's `HumanTakeover` capability).\n *\n * The channel keeps NO client-side state of record — the browser's state lives entirely on\n * the server — so {@link SerializeState} / {@link RestoreState} use the base no-op behavior.\n */\n@RegisterClass(BaseRealtimeChannelClient, 'RealtimeRemoteBrowserChannel')\nexport class RemoteBrowserChannel extends BaseRealtimeChannelClient<RemoteBrowserSurfaceComponent> {\n /** The live bound surface, when the channel tab's pane is instantiated. */\n private surface: RemoteBrowserSurfaceComponent | null = null;\n\n /** Subscription to the bound surface's `HumanInput` output, torn down on unbind/dispose. */\n private humanInputSub: Subscription | null = null;\n\n /**\n * Whether the server confirmed it is PUSHING screencast frames for this session (`ScreenStreaming`\n * supported). When `true` the surface renders pushed frames on its canvas and skips its poll, and\n * {@link OnScreencastFrame} forwards each pushed frame to it. When `false` the surface keeps polling.\n */\n private streaming = false;\n\n /**\n * Whether the server confirmed it is PUSHING tab-audio chunks for this session (the backend can capture\n * audio). When `true` {@link OnAudioChunk} feeds each pushed chunk to {@link audioPlayer}; when `false`\n * no audio plays.\n */\n private audioStreaming = false;\n\n /** The client-side audio player fed pushed chunks while {@link audioStreaming}; `null` until started. */\n private audioPlayer: RemoteBrowserAudioPlayer | null = null;\n\n /** Subscription to the bound surface's `AudioMutedChange` output (the speaker toggle), torn down on unbind. */\n private audioMutedSub: Subscription | null = null;\n\n public get ChannelName(): string {\n return 'Remote Browser';\n }\n\n public get ToolNamePrefix(): string {\n return REMOTE_BROWSER_TOOL_PREFIX;\n }\n\n public get TabTitle(): string {\n return 'Browser';\n }\n\n public get TabIcon(): string {\n return 'fa-solid fa-globe';\n }\n\n public GetToolDefinitions(): RealtimeToolDefinition[] {\n return REMOTE_BROWSER_TOOL_DEFINITIONS;\n }\n\n public override GetSurfaceComponent(): Type<RemoteBrowserSurfaceComponent> {\n return RemoteBrowserSurfaceComponent;\n }\n\n /** First-run intro shown the first time the user opens the Browser tab (once per user). */\n public override GetOnboardingDetails(): ChannelOnboardingDetails {\n return {\n Heading: 'Remote Browser',\n Description:\n 'The agent drives a live web browser on the server and you can watch it work right here. ' +\n 'You can also grab the wheel: click and type directly on the live page and the agent picks ' +\n 'up right where you left off.',\n Tips: [\n 'Ask the agent to look something up, open a site or fill in a form — it controls the page.',\n \"Click on the live view to take over, then click and type to drive it yourself — the \\\"You're driving\\\" badge shows when takeover is active.\",\n 'The current page URL is shown so you always know where the agent has navigated.',\n ],\n IconClass: 'fa-solid fa-globe',\n };\n }\n\n /**\n * Wires the dynamically-created surface: hands it a snapshot fetcher closing over the\n * channel context (session id + provider live there), so the surface stays transport-\n * agnostic. Set BEFORE the surface's first change detection (the pane binds synchronously),\n * so its `ngOnInit` poll has the fetcher.\n *\n * Also asks the server to start a live screencast. When the backend supports `ScreenStreaming` the\n * server starts PUSHING frames and reports `Streaming: true`; we flip the surface to canvas mode (its\n * poll is then skipped) and {@link OnScreencastFrame} paints each pushed frame. When the backend lacks\n * the capability (`Streaming: false`) the surface keeps the snapshot poll already running — graceful\n * fallback, no further action.\n *\n * HUMAN TAKEOVER is enabled BY DEFAULT (Collaborative): the surface is flipped `Interactive = true` and\n * its `HumanInput` output subscribed, forwarding each pointer/keyboard event to the server via\n * {@link RELAY_HUMAN_INPUT_MUTATION}. Takeover only takes effect on the canvas (screencast) path; the\n * `<img>` poll fallback stays view-only. The server gates the actual routing on the backend's\n * `HumanTakeover` capability, so this is safe to enable unconditionally.\n */\n public BindSurface(instance: RemoteBrowserSurfaceComponent): void {\n this.surface = instance;\n instance.Fetch = () => this.fetchSnapshot();\n instance.Interactive = true;\n this.humanInputSub = instance.HumanInput.subscribe((input) => this.relayHumanInput(input));\n this.audioMutedSub = instance.AudioMutedChange.subscribe((muted) => this.audioPlayer?.SetMuted(muted));\n void this.startScreencast(instance);\n void this.startAudioStream(instance);\n }\n\n public override UnbindSurface(): void {\n this.humanInputSub?.unsubscribe();\n this.humanInputSub = null;\n this.audioMutedSub?.unsubscribe();\n this.audioMutedSub = null;\n void this.stopScreencast();\n void this.stopAudioStream();\n this.surface = null;\n }\n\n public override Dispose(): void {\n this.humanInputSub?.unsubscribe();\n this.humanInputSub = null;\n this.audioMutedSub?.unsubscribe();\n this.audioMutedSub = null;\n void this.stopScreencast();\n void this.stopAudioStream();\n super.Dispose();\n }\n\n /**\n * Forwards one PUSHED screencast frame to the bound surface's canvas. Called by the session service\n * when a `RemoteBrowserScreencastFrame` arrives on the push-status stream for THIS session. No-op when\n * the channel isn't streaming or has no bound surface (e.g. the tab pane is collapsed).\n *\n * @param dataBase64 The frame image as raw base64 JPEG (no `data:` prefix).\n */\n public OnScreencastFrame(dataBase64: string): void {\n if (this.streaming) {\n this.surface?.RenderFrame(dataBase64);\n }\n }\n\n /**\n * Feeds one PUSHED tab-audio chunk to the client-side audio player. Called by the session service when a\n * `RemoteBrowserAudioChunk` arrives on the push-status stream for THIS session. No-op when the channel\n * isn't audio-streaming or the player has been torn down.\n *\n * @param chunk The pushed audio chunk (base64 webm-opus + codec/seq metadata).\n */\n public OnAudioChunk(chunk: RemoteBrowserAudioChunkInput): void {\n if (this.audioStreaming) {\n this.audioPlayer?.Enqueue(chunk);\n }\n }\n\n /**\n * Asks the server to start the live screencast and, on success, flips the surface to canvas mode so it\n * stops polling and starts painting pushed frames. Best-effort: a `null`/`Streaming: false` result\n * (capability absent or transport failure) leaves the surface on its poll fallback.\n */\n private async startScreencast(instance: RemoteBrowserSurfaceComponent): Promise<void> {\n const sessionId = this.Context?.AgentSessionID;\n if (!sessionId) {\n return;\n }\n const data = await this.Context?.ExecuteServerAction<StartRemoteBrowserScreencastResult>(START_SCREENCAST_MUTATION, { agentSessionID: sessionId });\n if (data?.StartRemoteBrowserScreencast?.Streaming === true) {\n this.streaming = true;\n instance.Streaming = true;\n }\n }\n\n /**\n * Asks the server to start the live tab-audio stream and, on success, spins up the client-side\n * {@link RemoteBrowserAudioPlayer} (speaker ON by default — the call is the activating user gesture) so\n * pushed chunks play. Best-effort: a `null`/`Streaming: false` result (no audio capability or transport\n * failure) leaves the channel silent with no player.\n *\n * @param instance The bound surface, flipped to show the speaker toggle when audio starts.\n */\n private async startAudioStream(instance: RemoteBrowserSurfaceComponent): Promise<void> {\n const sessionId = this.Context?.AgentSessionID;\n if (!sessionId) {\n return;\n }\n const data = await this.Context?.ExecuteServerAction<StartRemoteBrowserAudioStreamResult>(START_AUDIO_STREAM_MUTATION, { agentSessionID: sessionId });\n if (data?.StartRemoteBrowserAudioStream?.Streaming === true) {\n this.audioStreaming = true;\n this.audioPlayer = new RemoteBrowserAudioPlayer(new MediaSourceAudioSink());\n // Speaker defaults ON; reflect that on the surface so the toggle renders un-muted.\n instance.AudioAvailable = true;\n instance.AudioMuted = false;\n }\n }\n\n /** Asks the server to stop the tab-audio stream (best-effort), disposes the player, and clears state. */\n private async stopAudioStream(): Promise<void> {\n const wasStreaming = this.audioStreaming;\n this.audioStreaming = false;\n this.audioPlayer?.Dispose();\n this.audioPlayer = null;\n const sessionId = this.Context?.AgentSessionID;\n if (!wasStreaming || !sessionId) {\n return;\n }\n await this.Context?.ExecuteServerAction(STOP_AUDIO_STREAM_MUTATION, { agentSessionID: sessionId });\n }\n\n /**\n * Relays ONE human-takeover input from the surface to the server's {@link RELAY_HUMAN_INPUT_MUTATION}.\n * Best-effort and fire-and-forget: never throws, and the boolean result is ignored (the server already\n * gates routing on the backend's `HumanTakeover` capability). No-op when no live session id exists.\n *\n * @param input The flattened pointer/keyboard input the user performed on the live canvas.\n */\n private relayHumanInput(input: RemoteBrowserHumanInputEvent): void {\n const sessionId = this.Context?.AgentSessionID;\n if (!sessionId) {\n return;\n }\n void this.Context?.ExecuteServerAction(RELAY_HUMAN_INPUT_MUTATION, {\n agentSessionID: sessionId,\n kind: input.kind,\n x: input.x ?? null,\n y: input.y ?? null,\n button: input.button ?? null,\n key: input.key ?? null,\n deltaX: input.deltaX ?? null,\n deltaY: input.deltaY ?? null,\n // Server expects a comma-separated modifier list (e.g. \"Shift,Control\"); null when none held.\n modifiers: input.modifiers && input.modifiers.length > 0 ? input.modifiers.join(',') : null,\n });\n }\n\n /** Asks the server to stop the screencast (best-effort) and clears the streaming flag. */\n private async stopScreencast(): Promise<void> {\n const wasStreaming = this.streaming;\n this.streaming = false;\n const sessionId = this.Context?.AgentSessionID;\n if (!wasStreaming || !sessionId) {\n return;\n }\n await this.Context?.ExecuteServerAction(STOP_SCREENCAST_MUTATION, { agentSessionID: sessionId });\n }\n\n /**\n * Fetches one live snapshot of the server browser for the surface — the PERCEPTION poll.\n * Best-effort by contract ({@link RemoteBrowserSnapshotFetcher}): returns `null` when no\n * session is live or the query fails (the surface keeps its last good frame).\n */\n private async fetchSnapshot(): Promise<RemoteBrowserSnapshotView | null> {\n const sessionId = this.Context?.AgentSessionID;\n if (!sessionId) {\n return null;\n }\n const data = await this.Context?.ExecuteServerAction<RemoteBrowserSnapshotResult>(REMOTE_BROWSER_SNAPSHOT_QUERY, { agentSessionID: sessionId });\n return data?.RemoteBrowserSnapshot ?? null;\n }\n\n /**\n * Executes ONE `browser_*` tool call: maps it to a server action and awaits the\n * `ExecuteRemoteBrowserAction` mutation, then returns a concise JSON result the model reads.\n * On success it also feeds the new page URL to the agent as a `[browser]` context note. Never\n * throws — bad arguments, no live session, and server failures all become a failed-result\n * payload so the model can narrate the problem.\n */\n public async ApplyAgentTool(toolName: string, argsJson: string): Promise<string> {\n // VISION-QUERY branch: the two interpreter tools OBSERVE the page (run a vision model over a screenshot)\n // rather than drive it, so they route to the separate interpret mutation, not MapToolToAction.\n if (toolName === REMOTE_BROWSER_TOOL_NAMES.DescribePage) {\n return this.interpretPage(undefined);\n }\n if (toolName === REMOTE_BROWSER_TOOL_NAMES.LocateElement) {\n const description = this.asArgString(this.parseArgs(argsJson)['description']);\n if (!description) {\n return this.fail('browser_LocateElement requires a \"description\" of the element to find.');\n }\n return this.interpretPage(description);\n }\n\n let action: RemoteBrowserAction;\n try {\n action = MapToolToAction(toolName, this.parseArgs(argsJson));\n } catch (error) {\n const message = error instanceof RemoteBrowserToolArgError ? error.message : 'Invalid Remote Browser tool arguments.';\n return this.fail(message);\n }\n\n const sessionId = this.Context?.AgentSessionID;\n if (!sessionId) {\n // Diagnostic: distinguishes \"channel never Initialized (no Context)\" from \"session id not yet\n // set on the service\" — the two distinct causes of a null id at tool-execution time.\n console.warn('[RemoteBrowser] browser tool invoked but AgentSessionID is null', {\n tool: toolName,\n hasContext: !!this.Context,\n agentSessionID: this.Context?.AgentSessionID ?? null,\n });\n return this.fail('No live browser session is available yet — the realtime session may still be connecting; try again in a moment.');\n }\n\n const data = await this.Context?.ExecuteServerAction<ExecuteRemoteBrowserActionResult>(\n EXECUTE_REMOTE_BROWSER_ACTION_MUTATION,\n this.buildVariables(sessionId, action),\n );\n const result = data?.ExecuteRemoteBrowserAction ?? null;\n if (!result) {\n return this.fail('The browser action could not be executed (no response from the server).');\n }\n if (!result.Success) {\n return this.fail(result.Detail ?? 'The browser action failed.');\n }\n\n if (result.CurrentUrl) {\n // PERCEPTION: tell the agent where the page is now (background — no spoken reply).\n this.Context?.SendContextNote(`[browser] current page: ${result.CurrentUrl}`);\n // In streaming mode the surface's snapshot poll (which carries the URL) is stopped, so push the new\n // URL to the live-view bar directly — otherwise it stays stuck on \"No page loaded yet\".\n this.surface?.SetCurrentUrl(result.CurrentUrl);\n }\n return this.ok(result.CurrentUrl, result.Detail);\n }\n\n /** Builds the flat mutation variables from a normalized action (omitted fields ride as null). */\n private buildVariables(sessionId: string, action: RemoteBrowserAction): Record<string, JSONValue> {\n return {\n agentSessionID: sessionId,\n kind: action.Kind,\n url: action.Url ?? null,\n selector: action.Selector ?? null,\n x: action.X ?? null,\n y: action.Y ?? null,\n text: action.Text ?? null,\n key: action.Key ?? null,\n deltaX: action.DeltaX ?? null,\n deltaY: action.DeltaY ?? null,\n ms: action.Ms ?? null,\n };\n }\n\n /**\n * Runs the VISION-QUERY path for the `browser_DescribePage` / `browser_LocateElement` tools: awaits the\n * `InterpretRemoteBrowserPage` mutation (server captures a screenshot + runs a fast vision model) and returns\n * a concise JSON string the model reads. For a describe (no `query`) it returns `{ description }`; for a locate\n * it returns `{ found, element, all }` so the agent can `browser_Click` the centroid. Never throws — a null\n * session, no response, or a server detail-only result all map to a result string the model can narrate.\n *\n * @param query The visual target to locate, or `undefined` for a plain page description.\n * @returns A JSON string describing the interpretation for the model.\n */\n private async interpretPage(query: string | undefined): Promise<string> {\n const sessionId = this.Context?.AgentSessionID;\n if (!sessionId) {\n console.warn('[RemoteBrowser] interpret tool invoked but AgentSessionID is null', {\n hasContext: !!this.Context,\n agentSessionID: this.Context?.AgentSessionID ?? null,\n });\n return this.fail('No live browser session is available yet — the realtime session may still be connecting; try again in a moment.');\n }\n\n const data = await this.Context?.ExecuteServerAction<InterpretRemoteBrowserPageResult>(INTERPRET_PAGE_MUTATION, {\n agentSessionID: sessionId,\n query: query ?? null,\n });\n const result = data?.InterpretRemoteBrowserPage ?? null;\n if (!result) {\n return this.fail('The page could not be interpreted (no response from the server).');\n }\n\n if (query === undefined) {\n // DescribePage — hand back the description (or the server's detail note when nothing was interpreted).\n return JSON.stringify({ description: result.Description ?? result.Detail ?? null });\n }\n\n // LocateElement — surface whether anything matched, the best match, and all candidates.\n const elements = result.Elements ?? [];\n const best = elements[0];\n return JSON.stringify({\n found: elements.length > 0,\n element: best ? { label: best.Label, x: best.X, y: best.Y } : null,\n all: elements,\n ...(result.Detail ? { detail: result.Detail } : {}),\n });\n }\n\n /** Coerces a parsed tool-arg to a non-empty string, or `undefined` when absent / wrong-typed. */\n private asArgString(value: JSONValue | undefined): string | undefined {\n return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;\n }\n\n /** Parses the tool-args JSON into a plain object; returns `{}` for empty/malformed input. */\n private parseArgs(argsJson: string): Record<string, JSONValue> {\n const trimmed = argsJson?.trim();\n if (!trimmed) {\n return {};\n }\n try {\n const parsed: unknown = JSON.parse(trimmed);\n return parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed) ? (parsed as Record<string, JSONValue>) : {};\n } catch {\n return {};\n }\n }\n\n /** Builds a successful tool-result JSON string for the model. */\n private ok(currentUrl: string | null, detail: string | null): string {\n const result: RemoteBrowserToolResult = { success: true };\n if (currentUrl) {\n result.currentUrl = currentUrl;\n }\n if (detail) {\n result.detail = detail;\n }\n return JSON.stringify(result);\n }\n\n /** Builds a failed tool-result JSON string for the model. */\n private fail(error: string): string {\n const result: RemoteBrowserToolResult = { success: false, error };\n return JSON.stringify(result);\n }\n}\n\n/**\n * Tree-shaking prevention: the Remote Browser channel is resolved dynamically through the\n * ClassFactory (by the registry row's `ClientPluginClass` key), so this static call is what\n * keeps its `@RegisterClass` side effect from being eliminated by the bundler. Called from\n * `conversations.module.ts` alongside {@link LoadRealtimeWhiteboardChannel}.\n */\nexport function LoadRealtimeRemoteBrowserChannel(): void {\n // intentional no-op — the import side effect performs the registration\n}\n"]}