@memberjunction/ng-conversations 6.1.4 → 6.2.0-edge.1

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 (424) hide show
  1. package/README.md +36 -3
  2. package/dist/lib/components/active-tasks/active-tasks-panel.component.d.ts +19 -3
  3. package/dist/lib/components/active-tasks/active-tasks-panel.component.d.ts.map +1 -1
  4. package/dist/lib/components/active-tasks/active-tasks-panel.component.js +39 -7
  5. package/dist/lib/components/active-tasks/active-tasks-panel.component.js.map +1 -1
  6. package/dist/lib/components/agent/active-agent-indicator.component.d.ts +74 -6
  7. package/dist/lib/components/agent/active-agent-indicator.component.d.ts.map +1 -1
  8. package/dist/lib/components/agent/active-agent-indicator.component.js +111 -21
  9. package/dist/lib/components/agent/active-agent-indicator.component.js.map +1 -1
  10. package/dist/lib/components/agent/agent-process-panel.component.d.ts +31 -5
  11. package/dist/lib/components/agent/agent-process-panel.component.d.ts.map +1 -1
  12. package/dist/lib/components/agent/agent-process-panel.component.js +71 -15
  13. package/dist/lib/components/agent/agent-process-panel.component.js.map +1 -1
  14. package/dist/lib/components/artifact/artifact-share-modal.component.d.ts +65 -9
  15. package/dist/lib/components/artifact/artifact-share-modal.component.d.ts.map +1 -1
  16. package/dist/lib/components/artifact/artifact-share-modal.component.js +169 -57
  17. package/dist/lib/components/artifact/artifact-share-modal.component.js.map +1 -1
  18. package/dist/lib/components/attachment/image-viewer.component.d.ts +77 -11
  19. package/dist/lib/components/attachment/image-viewer.component.d.ts.map +1 -1
  20. package/dist/lib/components/attachment/image-viewer.component.js +181 -47
  21. package/dist/lib/components/attachment/image-viewer.component.js.map +1 -1
  22. package/dist/lib/components/collection/artifact-collection-picker-modal.component.d.ts +129 -19
  23. package/dist/lib/components/collection/artifact-collection-picker-modal.component.d.ts.map +1 -1
  24. package/dist/lib/components/collection/artifact-collection-picker-modal.component.js +350 -116
  25. package/dist/lib/components/collection/artifact-collection-picker-modal.component.js.map +1 -1
  26. package/dist/lib/components/collection/artifact-create-modal.component.d.ts +60 -8
  27. package/dist/lib/components/collection/artifact-create-modal.component.d.ts.map +1 -1
  28. package/dist/lib/components/collection/artifact-create-modal.component.js +130 -46
  29. package/dist/lib/components/collection/artifact-create-modal.component.js.map +1 -1
  30. package/dist/lib/components/collection/collection-artifact-card.component.d.ts +63 -3
  31. package/dist/lib/components/collection/collection-artifact-card.component.d.ts.map +1 -1
  32. package/dist/lib/components/collection/collection-artifact-card.component.js +113 -19
  33. package/dist/lib/components/collection/collection-artifact-card.component.js.map +1 -1
  34. package/dist/lib/components/collection/collection-form-modal.component.d.ts +54 -8
  35. package/dist/lib/components/collection/collection-form-modal.component.d.ts.map +1 -1
  36. package/dist/lib/components/collection/collection-form-modal.component.js +130 -46
  37. package/dist/lib/components/collection/collection-form-modal.component.js.map +1 -1
  38. package/dist/lib/components/collection/collection-share-modal.component.d.ts +70 -10
  39. package/dist/lib/components/collection/collection-share-modal.component.d.ts.map +1 -1
  40. package/dist/lib/components/collection/collection-share-modal.component.js +175 -53
  41. package/dist/lib/components/collection/collection-share-modal.component.js.map +1 -1
  42. package/dist/lib/components/collection/collection-tree.component.d.ts +89 -9
  43. package/dist/lib/components/collection/collection-tree.component.d.ts.map +1 -1
  44. package/dist/lib/components/collection/collection-tree.component.js +220 -70
  45. package/dist/lib/components/collection/collection-tree.component.js.map +1 -1
  46. package/dist/lib/components/collection/collection-view.component.d.ts +89 -11
  47. package/dist/lib/components/collection/collection-view.component.d.ts.map +1 -1
  48. package/dist/lib/components/collection/collection-view.component.js +154 -40
  49. package/dist/lib/components/collection/collection-view.component.js.map +1 -1
  50. package/dist/lib/components/collection/collections-full-view.component.d.ts +390 -38
  51. package/dist/lib/components/collection/collections-full-view.component.d.ts.map +1 -1
  52. package/dist/lib/components/collection/collections-full-view.component.js +951 -367
  53. package/dist/lib/components/collection/collections-full-view.component.js.map +1 -1
  54. package/dist/lib/components/composer/ai-composer.component.d.ts +213 -18
  55. package/dist/lib/components/composer/ai-composer.component.d.ts.map +1 -1
  56. package/dist/lib/components/composer/ai-composer.component.js +388 -44
  57. package/dist/lib/components/composer/ai-composer.component.js.map +1 -1
  58. package/dist/lib/components/conversation/conversation-agent-picker.component.d.ts +12 -1
  59. package/dist/lib/components/conversation/conversation-agent-picker.component.d.ts.map +1 -1
  60. package/dist/lib/components/conversation/conversation-agent-picker.component.js +26 -4
  61. package/dist/lib/components/conversation/conversation-agent-picker.component.js.map +1 -1
  62. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +1090 -123
  63. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
  64. package/dist/lib/components/conversation/conversation-chat-area.component.js +2407 -731
  65. package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
  66. package/dist/lib/components/conversation/conversation-empty-state.component.d.ts +139 -19
  67. package/dist/lib/components/conversation/conversation-empty-state.component.d.ts.map +1 -1
  68. package/dist/lib/components/conversation/conversation-empty-state.component.js +296 -59
  69. package/dist/lib/components/conversation/conversation-empty-state.component.js.map +1 -1
  70. package/dist/lib/components/conversation/conversation-list.component.d.ts +579 -78
  71. package/dist/lib/components/conversation/conversation-list.component.d.ts.map +1 -1
  72. package/dist/lib/components/conversation/conversation-list.component.js +2322 -823
  73. package/dist/lib/components/conversation/conversation-list.component.js.map +1 -1
  74. package/dist/lib/components/conversation/pinned-messages-panel.component.d.ts +40 -4
  75. package/dist/lib/components/conversation/pinned-messages-panel.component.d.ts.map +1 -1
  76. package/dist/lib/components/conversation/pinned-messages-panel.component.js +72 -14
  77. package/dist/lib/components/conversation/pinned-messages-panel.component.js.map +1 -1
  78. package/dist/lib/components/dialogs/input-dialog.component.d.ts +57 -11
  79. package/dist/lib/components/dialogs/input-dialog.component.d.ts.map +1 -1
  80. package/dist/lib/components/dialogs/input-dialog.component.js +128 -16
  81. package/dist/lib/components/dialogs/input-dialog.component.js.map +1 -1
  82. package/dist/lib/components/dialogs/rating-dialog.component.d.ts +68 -10
  83. package/dist/lib/components/dialogs/rating-dialog.component.d.ts.map +1 -1
  84. package/dist/lib/components/dialogs/rating-dialog.component.js +151 -27
  85. package/dist/lib/components/dialogs/rating-dialog.component.js.map +1 -1
  86. package/dist/lib/components/export/export-modal.component.d.ts +84 -10
  87. package/dist/lib/components/export/export-modal.component.d.ts.map +1 -1
  88. package/dist/lib/components/export/export-modal.component.js +173 -49
  89. package/dist/lib/components/export/export-modal.component.js.map +1 -1
  90. package/dist/lib/components/global-tasks/global-tasks-panel.component.d.ts +29 -3
  91. package/dist/lib/components/global-tasks/global-tasks-panel.component.d.ts.map +1 -1
  92. package/dist/lib/components/global-tasks/global-tasks-panel.component.js +58 -12
  93. package/dist/lib/components/global-tasks/global-tasks-panel.component.js.map +1 -1
  94. package/dist/lib/components/library/library-full-view.component.d.ts +57 -8
  95. package/dist/lib/components/library/library-full-view.component.d.ts.map +1 -1
  96. package/dist/lib/components/library/library-full-view.component.js +119 -35
  97. package/dist/lib/components/library/library-full-view.component.js.map +1 -1
  98. package/dist/lib/components/members/members-modal.component.d.ts +76 -10
  99. package/dist/lib/components/members/members-modal.component.d.ts.map +1 -1
  100. package/dist/lib/components/members/members-modal.component.js +164 -50
  101. package/dist/lib/components/members/members-modal.component.js.map +1 -1
  102. package/dist/lib/components/message/actionable-commands.component.d.ts +37 -5
  103. package/dist/lib/components/message/actionable-commands.component.d.ts.map +1 -1
  104. package/dist/lib/components/message/actionable-commands.component.js +82 -16
  105. package/dist/lib/components/message/actionable-commands.component.js.map +1 -1
  106. package/dist/lib/components/message/conversation-message-rating.component.d.ts +46 -10
  107. package/dist/lib/components/message/conversation-message-rating.component.d.ts.map +1 -1
  108. package/dist/lib/components/message/conversation-message-rating.component.js +120 -36
  109. package/dist/lib/components/message/conversation-message-rating.component.js.map +1 -1
  110. package/dist/lib/components/message/message-input.component.d.ts +549 -74
  111. package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
  112. package/dist/lib/components/message/message-input.component.js +1250 -421
  113. package/dist/lib/components/message/message-input.component.js.map +1 -1
  114. package/dist/lib/components/message/message-item.component.d.ts +411 -29
  115. package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
  116. package/dist/lib/components/message/message-item.component.js +896 -222
  117. package/dist/lib/components/message/message-item.component.js.map +1 -1
  118. package/dist/lib/components/message/message-list.component.d.ts +288 -29
  119. package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
  120. package/dist/lib/components/message/message-list.component.js +561 -123
  121. package/dist/lib/components/message/message-list.component.js.map +1 -1
  122. package/dist/lib/components/navigation/conversation-navigation.component.d.ts +64 -5
  123. package/dist/lib/components/navigation/conversation-navigation.component.d.ts.map +1 -1
  124. package/dist/lib/components/navigation/conversation-navigation.component.js +101 -11
  125. package/dist/lib/components/navigation/conversation-navigation.component.js.map +1 -1
  126. package/dist/lib/components/notification/activity-indicator.component.d.ts +6 -2
  127. package/dist/lib/components/notification/activity-indicator.component.d.ts.map +1 -1
  128. package/dist/lib/components/notification/activity-indicator.component.js +13 -3
  129. package/dist/lib/components/notification/activity-indicator.component.js.map +1 -1
  130. package/dist/lib/components/notification/notification-badge.component.d.ts +15 -3
  131. package/dist/lib/components/notification/notification-badge.component.d.ts.map +1 -1
  132. package/dist/lib/components/notification/notification-badge.component.js +36 -8
  133. package/dist/lib/components/notification/notification-badge.component.js.map +1 -1
  134. package/dist/lib/components/overlay/chat-overlay.component.d.ts +1 -1
  135. package/dist/lib/components/overlay/chat-overlay.component.js +4 -4
  136. package/dist/lib/components/overlay/chat-overlay.component.js.map +1 -1
  137. package/dist/lib/components/project/project-form-modal.component.d.ts +93 -11
  138. package/dist/lib/components/project/project-form-modal.component.d.ts.map +1 -1
  139. package/dist/lib/components/project/project-form-modal.component.js +348 -87
  140. package/dist/lib/components/project/project-form-modal.component.js.map +1 -1
  141. package/dist/lib/components/project/project-selector.component.d.ts +78 -8
  142. package/dist/lib/components/project/project-selector.component.d.ts.map +1 -1
  143. package/dist/lib/components/project/project-selector.component.js +177 -46
  144. package/dist/lib/components/project/project-selector.component.js.map +1 -1
  145. package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.d.ts +1 -1
  146. package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.d.ts.map +1 -1
  147. package/dist/lib/components/realtime/channels/channel-onboarding-panel.component.js.map +1 -1
  148. package/dist/lib/components/realtime/channels/client-context-channel.d.ts +1 -1
  149. package/dist/lib/components/realtime/channels/client-context-channel.d.ts.map +1 -1
  150. package/dist/lib/components/realtime/channels/client-context-channel.js +1 -1
  151. package/dist/lib/components/realtime/channels/client-context-channel.js.map +1 -1
  152. package/dist/lib/components/realtime/channels/realtime-channel-pane.component.d.ts +1 -1
  153. package/dist/lib/components/realtime/channels/realtime-channel-pane.component.d.ts.map +1 -1
  154. package/dist/lib/components/realtime/channels/realtime-channel-pane.component.js +3 -0
  155. package/dist/lib/components/realtime/channels/realtime-channel-pane.component.js.map +1 -1
  156. package/dist/lib/components/realtime/media/media-channel.d.ts +2 -1
  157. package/dist/lib/components/realtime/media/media-channel.d.ts.map +1 -1
  158. package/dist/lib/components/realtime/media/media-channel.js +1 -1
  159. package/dist/lib/components/realtime/media/media-channel.js.map +1 -1
  160. package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts +2 -2
  161. package/dist/lib/components/realtime/realtime-activity-rail.component.d.ts.map +1 -1
  162. package/dist/lib/components/realtime/realtime-activity-rail.component.js +2 -2
  163. package/dist/lib/components/realtime/realtime-activity-rail.component.js.map +1 -1
  164. package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts +1 -1
  165. package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts.map +1 -1
  166. package/dist/lib/components/realtime/realtime-agent-banner.component.js.map +1 -1
  167. package/dist/lib/components/realtime/realtime-composer.component.d.ts +3 -1
  168. package/dist/lib/components/realtime/realtime-composer.component.d.ts.map +1 -1
  169. package/dist/lib/components/realtime/realtime-composer.component.js +26 -8
  170. package/dist/lib/components/realtime/realtime-composer.component.js.map +1 -1
  171. package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts +2 -2
  172. package/dist/lib/components/realtime/realtime-delegation-card.component.d.ts.map +1 -1
  173. package/dist/lib/components/realtime/realtime-delegation-card.component.js +2 -2
  174. package/dist/lib/components/realtime/realtime-delegation-card.component.js.map +1 -1
  175. package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts +4 -4
  176. package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts.map +1 -1
  177. package/dist/lib/components/realtime/realtime-session-overlay.component.js +19 -19
  178. package/dist/lib/components/realtime/realtime-session-overlay.component.js.map +1 -1
  179. package/dist/lib/components/realtime/realtime-session-state.d.ts +2 -3
  180. package/dist/lib/components/realtime/realtime-session-state.d.ts.map +1 -1
  181. package/dist/lib/components/realtime/realtime-session-state.js +3 -2
  182. package/dist/lib/components/realtime/realtime-session-state.js.map +1 -1
  183. package/dist/lib/components/realtime/realtime-session-thread.component.d.ts +17 -1
  184. package/dist/lib/components/realtime/realtime-session-thread.component.d.ts.map +1 -1
  185. package/dist/lib/components/realtime/realtime-session-thread.component.js +122 -45
  186. package/dist/lib/components/realtime/realtime-session-thread.component.js.map +1 -1
  187. package/dist/lib/components/realtime/realtime-session-timeline-card.component.d.ts +47 -31
  188. package/dist/lib/components/realtime/realtime-session-timeline-card.component.d.ts.map +1 -1
  189. package/dist/lib/components/realtime/realtime-session-timeline-card.component.js +138 -124
  190. package/dist/lib/components/realtime/realtime-session-timeline-card.component.js.map +1 -1
  191. package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts +4 -4
  192. package/dist/lib/components/realtime/realtime-surface-tabs.component.d.ts.map +1 -1
  193. package/dist/lib/components/realtime/realtime-surface-tabs.component.js +4 -4
  194. package/dist/lib/components/realtime/realtime-surface-tabs.component.js.map +1 -1
  195. package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts +2 -2
  196. package/dist/lib/components/realtime/realtime-surface-tabs.model.d.ts.map +1 -1
  197. package/dist/lib/components/realtime/realtime-surface-tabs.model.js +3 -3
  198. package/dist/lib/components/realtime/realtime-surface-tabs.model.js.map +1 -1
  199. package/dist/lib/components/realtime/realtime-ui-config.d.ts +13 -11
  200. package/dist/lib/components/realtime/realtime-ui-config.d.ts.map +1 -1
  201. package/dist/lib/components/realtime/realtime-ui-config.js +29 -25
  202. package/dist/lib/components/realtime/realtime-ui-config.js.map +1 -1
  203. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts +1 -1
  204. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts.map +1 -1
  205. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js +1 -1
  206. package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js.map +1 -1
  207. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts +4 -2
  208. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.d.ts.map +1 -1
  209. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js +7 -3
  210. package/dist/lib/components/realtime/whiteboard/whiteboard-channel.js.map +1 -1
  211. package/dist/lib/components/search/search-panel.component.d.ts +133 -22
  212. package/dist/lib/components/search/search-panel.component.d.ts.map +1 -1
  213. package/dist/lib/components/search/search-panel.component.js +312 -83
  214. package/dist/lib/components/search/search-panel.component.js.map +1 -1
  215. package/dist/lib/components/share/share-modal.component.d.ts +69 -9
  216. package/dist/lib/components/share/share-modal.component.d.ts.map +1 -1
  217. package/dist/lib/components/share/share-modal.component.js +139 -31
  218. package/dist/lib/components/share/share-modal.component.js.map +1 -1
  219. package/dist/lib/components/shared/user-picker.component.d.ts +48 -8
  220. package/dist/lib/components/shared/user-picker.component.d.ts.map +1 -1
  221. package/dist/lib/components/shared/user-picker.component.js +110 -30
  222. package/dist/lib/components/shared/user-picker.component.js.map +1 -1
  223. package/dist/lib/components/sidebar/conversation-sidebar.component.d.ts +114 -11
  224. package/dist/lib/components/sidebar/conversation-sidebar.component.d.ts.map +1 -1
  225. package/dist/lib/components/sidebar/conversation-sidebar.component.js +199 -21
  226. package/dist/lib/components/sidebar/conversation-sidebar.component.js.map +1 -1
  227. package/dist/lib/components/sidebar/routines-section.component.d.ts +20 -1
  228. package/dist/lib/components/sidebar/routines-section.component.d.ts.map +1 -1
  229. package/dist/lib/components/sidebar/routines-section.component.js +25 -5
  230. package/dist/lib/components/sidebar/routines-section.component.js.map +1 -1
  231. package/dist/lib/components/slots/slot-interfaces.d.ts +19 -0
  232. package/dist/lib/components/slots/slot-interfaces.d.ts.map +1 -1
  233. package/dist/lib/components/slots/slot-interfaces.js.map +1 -1
  234. package/dist/lib/components/task/tasks-full-view.component.d.ts +95 -13
  235. package/dist/lib/components/task/tasks-full-view.component.d.ts.map +1 -1
  236. package/dist/lib/components/task/tasks-full-view.component.js +223 -75
  237. package/dist/lib/components/task/tasks-full-view.component.js.map +1 -1
  238. package/dist/lib/components/tasks/task-widget.component.d.ts +46 -6
  239. package/dist/lib/components/tasks/task-widget.component.d.ts.map +1 -1
  240. package/dist/lib/components/tasks/task-widget.component.js +100 -16
  241. package/dist/lib/components/tasks/task-widget.component.js.map +1 -1
  242. package/dist/lib/components/tasks/tasks-dropdown.component.d.ts +64 -7
  243. package/dist/lib/components/tasks/tasks-dropdown.component.d.ts.map +1 -1
  244. package/dist/lib/components/tasks/tasks-dropdown.component.js +127 -27
  245. package/dist/lib/components/tasks/tasks-dropdown.component.js.map +1 -1
  246. package/dist/lib/components/thread/thread-panel.component.d.ts +66 -8
  247. package/dist/lib/components/thread/thread-panel.component.d.ts.map +1 -1
  248. package/dist/lib/components/thread/thread-panel.component.js +146 -36
  249. package/dist/lib/components/thread/thread-panel.component.js.map +1 -1
  250. package/dist/lib/components/toast/toast.component.d.ts +11 -1
  251. package/dist/lib/components/toast/toast.component.d.ts.map +1 -1
  252. package/dist/lib/components/toast/toast.component.js +25 -5
  253. package/dist/lib/components/toast/toast.component.js.map +1 -1
  254. package/dist/lib/components/workspace/conversation-workspace.component.d.ts +385 -42
  255. package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
  256. package/dist/lib/components/workspace/conversation-workspace.component.js +887 -291
  257. package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
  258. package/dist/lib/composer-plugins/base-conversation-mention.provider.d.ts +8 -0
  259. package/dist/lib/composer-plugins/base-conversation-mention.provider.d.ts.map +1 -1
  260. package/dist/lib/composer-plugins/base-conversation-mention.provider.js +8 -1
  261. package/dist/lib/composer-plugins/base-conversation-mention.provider.js.map +1 -1
  262. package/dist/lib/conversations.module.d.ts.map +1 -1
  263. package/dist/lib/conversations.module.js.map +1 -1
  264. package/dist/lib/directives/chat-slot.directive.d.ts +5 -1
  265. package/dist/lib/directives/chat-slot.directive.d.ts.map +1 -1
  266. package/dist/lib/directives/chat-slot.directive.js.map +1 -1
  267. package/dist/lib/directives/search-shortcut.directive.d.ts +9 -1
  268. package/dist/lib/directives/search-shortcut.directive.d.ts.map +1 -1
  269. package/dist/lib/directives/search-shortcut.directive.js +14 -4
  270. package/dist/lib/directives/search-shortcut.directive.js.map +1 -1
  271. package/dist/lib/events/chat-events.d.ts +34 -7
  272. package/dist/lib/events/chat-events.d.ts.map +1 -1
  273. package/dist/lib/events/chat-events.js +37 -7
  274. package/dist/lib/events/chat-events.js.map +1 -1
  275. package/dist/lib/models/agent-turn.model.d.ts +103 -0
  276. package/dist/lib/models/agent-turn.model.d.ts.map +1 -0
  277. package/dist/lib/models/agent-turn.model.js +12 -0
  278. package/dist/lib/models/agent-turn.model.js.map +1 -0
  279. package/dist/lib/models/collection-view.model.js.map +1 -1
  280. package/dist/lib/models/conversation-state.model.d.ts +8 -0
  281. package/dist/lib/models/conversation-state.model.d.ts.map +1 -1
  282. package/dist/lib/models/conversation-state.model.js.map +1 -1
  283. package/dist/lib/models/lazy-artifact-info.d.ts +44 -12
  284. package/dist/lib/models/lazy-artifact-info.d.ts.map +1 -1
  285. package/dist/lib/models/lazy-artifact-info.js +98 -34
  286. package/dist/lib/models/lazy-artifact-info.js.map +1 -1
  287. package/dist/lib/services/active-tasks.service.d.ts +30 -4
  288. package/dist/lib/services/active-tasks.service.d.ts.map +1 -1
  289. package/dist/lib/services/active-tasks.service.js +71 -19
  290. package/dist/lib/services/active-tasks.service.js.map +1 -1
  291. package/dist/lib/services/agent-state.service.d.ts +17 -1
  292. package/dist/lib/services/agent-state.service.d.ts.map +1 -1
  293. package/dist/lib/services/agent-state.service.js +48 -16
  294. package/dist/lib/services/agent-state.service.js.map +1 -1
  295. package/dist/lib/services/artifact-permission.service.d.ts +39 -0
  296. package/dist/lib/services/artifact-permission.service.d.ts.map +1 -1
  297. package/dist/lib/services/artifact-permission.service.js +152 -17
  298. package/dist/lib/services/artifact-permission.service.js.map +1 -1
  299. package/dist/lib/services/artifact-state.service.d.ts +58 -5
  300. package/dist/lib/services/artifact-state.service.d.ts.map +1 -1
  301. package/dist/lib/services/artifact-state.service.js +135 -35
  302. package/dist/lib/services/artifact-state.service.js.map +1 -1
  303. package/dist/lib/services/browser-realtime-media-host.d.ts +24 -0
  304. package/dist/lib/services/browser-realtime-media-host.d.ts.map +1 -0
  305. package/dist/lib/services/browser-realtime-media-host.js +95 -0
  306. package/dist/lib/services/browser-realtime-media-host.js.map +1 -0
  307. package/dist/lib/services/collection-permission.service.d.ts +28 -0
  308. package/dist/lib/services/collection-permission.service.d.ts.map +1 -1
  309. package/dist/lib/services/collection-permission.service.js +88 -32
  310. package/dist/lib/services/collection-permission.service.js.map +1 -1
  311. package/dist/lib/services/collection-state.service.d.ts +11 -1
  312. package/dist/lib/services/collection-state.service.d.ts.map +1 -1
  313. package/dist/lib/services/collection-state.service.js +25 -5
  314. package/dist/lib/services/collection-state.service.js.map +1 -1
  315. package/dist/lib/services/conversation-agent.service.d.ts +26 -4
  316. package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
  317. package/dist/lib/services/conversation-agent.service.js +57 -18
  318. package/dist/lib/services/conversation-agent.service.js.map +1 -1
  319. package/dist/lib/services/conversation-attachment.service.d.ts +41 -2
  320. package/dist/lib/services/conversation-attachment.service.d.ts.map +1 -1
  321. package/dist/lib/services/conversation-attachment.service.js +80 -13
  322. package/dist/lib/services/conversation-attachment.service.js.map +1 -1
  323. package/dist/lib/services/conversation-streaming.service.d.ts +22 -0
  324. package/dist/lib/services/conversation-streaming.service.d.ts.map +1 -1
  325. package/dist/lib/services/conversation-streaming.service.js +50 -10
  326. package/dist/lib/services/conversation-streaming.service.js.map +1 -1
  327. package/dist/lib/services/data-cache.service.d.ts +31 -0
  328. package/dist/lib/services/data-cache.service.d.ts.map +1 -1
  329. package/dist/lib/services/data-cache.service.js +71 -15
  330. package/dist/lib/services/data-cache.service.js.map +1 -1
  331. package/dist/lib/services/dialog.service.d.ts +13 -0
  332. package/dist/lib/services/dialog.service.d.ts.map +1 -1
  333. package/dist/lib/services/dialog.service.js +25 -5
  334. package/dist/lib/services/dialog.service.js.map +1 -1
  335. package/dist/lib/services/export.service.d.ts +2 -0
  336. package/dist/lib/services/export.service.d.ts.map +1 -1
  337. package/dist/lib/services/export.service.js +5 -1
  338. package/dist/lib/services/export.service.js.map +1 -1
  339. package/dist/lib/services/graphql-attachment-blob-store.d.ts +44 -0
  340. package/dist/lib/services/graphql-attachment-blob-store.d.ts.map +1 -0
  341. package/dist/lib/services/graphql-attachment-blob-store.js +113 -0
  342. package/dist/lib/services/graphql-attachment-blob-store.js.map +1 -0
  343. package/dist/lib/services/mention-autocomplete.service.d.ts +17 -141
  344. package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -1
  345. package/dist/lib/services/mention-autocomplete.service.js +15 -460
  346. package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
  347. package/dist/lib/services/mention-parser.service.d.ts +10 -0
  348. package/dist/lib/services/mention-parser.service.d.ts.map +1 -1
  349. package/dist/lib/services/mention-parser.service.js +25 -5
  350. package/dist/lib/services/mention-parser.service.js.map +1 -1
  351. package/dist/lib/services/notification.service.d.ts +54 -6
  352. package/dist/lib/services/notification.service.d.ts.map +1 -1
  353. package/dist/lib/services/notification.service.js +123 -27
  354. package/dist/lib/services/notification.service.js.map +1 -1
  355. package/dist/lib/services/realtime-audio-recorder.d.ts +4 -4
  356. package/dist/lib/services/realtime-audio-recorder.js +12 -12
  357. package/dist/lib/services/realtime-audio-recorder.js.map +1 -1
  358. package/dist/lib/services/realtime-pcm-wav.d.ts +4 -0
  359. package/dist/lib/services/realtime-pcm-wav.d.ts.map +1 -1
  360. package/dist/lib/services/realtime-pcm-wav.js +10 -2
  361. package/dist/lib/services/realtime-pcm-wav.js.map +1 -1
  362. package/dist/lib/services/realtime-session-review.service.d.ts +1 -1
  363. package/dist/lib/services/realtime-session-review.service.d.ts.map +1 -1
  364. package/dist/lib/services/realtime-session-review.service.js +1 -1
  365. package/dist/lib/services/realtime-session-review.service.js.map +1 -1
  366. package/dist/lib/services/realtime-session.service.d.ts +16 -1150
  367. package/dist/lib/services/realtime-session.service.d.ts.map +1 -1
  368. package/dist/lib/services/realtime-session.service.js +28 -2430
  369. package/dist/lib/services/realtime-session.service.js.map +1 -1
  370. package/dist/lib/services/search.service.d.ts +42 -9
  371. package/dist/lib/services/search.service.d.ts.map +1 -1
  372. package/dist/lib/services/search.service.js +103 -23
  373. package/dist/lib/services/search.service.js.map +1 -1
  374. package/dist/lib/services/toast.service.d.ts +19 -1
  375. package/dist/lib/services/toast.service.d.ts.map +1 -1
  376. package/dist/lib/services/toast.service.js +45 -9
  377. package/dist/lib/services/toast.service.js.map +1 -1
  378. package/dist/lib/services/ui-command-handler.service.d.ts +16 -2
  379. package/dist/lib/services/ui-command-handler.service.d.ts.map +1 -1
  380. package/dist/lib/services/ui-command-handler.service.js +36 -8
  381. package/dist/lib/services/ui-command-handler.service.js.map +1 -1
  382. package/dist/lib/utils/agent-artifact-summary.d.ts +2 -2
  383. package/dist/lib/utils/agent-artifact-summary.js +3 -3
  384. package/dist/lib/utils/agent-artifact-summary.js.map +1 -1
  385. package/dist/lib/utils/agent-turn-routing.d.ts +69 -0
  386. package/dist/lib/utils/agent-turn-routing.d.ts.map +1 -0
  387. package/dist/lib/utils/agent-turn-routing.js +96 -0
  388. package/dist/lib/utils/agent-turn-routing.js.map +1 -0
  389. package/dist/lib/utils/artifact-panel-action.d.ts +15 -4
  390. package/dist/lib/utils/artifact-panel-action.d.ts.map +1 -1
  391. package/dist/lib/utils/artifact-panel-action.js +10 -2
  392. package/dist/lib/utils/artifact-panel-action.js.map +1 -1
  393. package/dist/lib/utils/distinct-artifacts.d.ts +2 -0
  394. package/dist/lib/utils/distinct-artifacts.d.ts.map +1 -1
  395. package/dist/lib/utils/distinct-artifacts.js +5 -1
  396. package/dist/lib/utils/distinct-artifacts.js.map +1 -1
  397. package/dist/lib/utils/plan-mode-preference.d.ts +2 -2
  398. package/dist/lib/utils/plan-mode-preference.js +6 -6
  399. package/dist/lib/utils/plan-mode-preference.js.map +1 -1
  400. package/dist/lib/utils/realtime-session-timeline.d.ts +8 -79
  401. package/dist/lib/utils/realtime-session-timeline.d.ts.map +1 -1
  402. package/dist/lib/utils/realtime-session-timeline.js +8 -89
  403. package/dist/lib/utils/realtime-session-timeline.js.map +1 -1
  404. package/dist/public-api.d.ts +5 -2
  405. package/dist/public-api.d.ts.map +1 -1
  406. package/dist/public-api.js +9 -2
  407. package/dist/public-api.js.map +1 -1
  408. package/package.json +33 -32
  409. package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts +0 -384
  410. package/dist/lib/components/realtime/channels/base-realtime-channel-client.d.ts.map +0 -1
  411. package/dist/lib/components/realtime/channels/base-realtime-channel-client.js +0 -231
  412. package/dist/lib/components/realtime/channels/base-realtime-channel-client.js.map +0 -1
  413. package/dist/lib/services/delegation-result-parser.d.ts +0 -50
  414. package/dist/lib/services/delegation-result-parser.d.ts.map +0 -1
  415. package/dist/lib/services/delegation-result-parser.js +0 -60
  416. package/dist/lib/services/delegation-result-parser.js.map +0 -1
  417. package/dist/lib/services/narration-template.d.ts +0 -42
  418. package/dist/lib/services/narration-template.d.ts.map +0 -1
  419. package/dist/lib/services/narration-template.js +0 -73
  420. package/dist/lib/services/narration-template.js.map +0 -1
  421. package/dist/lib/services/skill-picker-narrowing.d.ts +0 -13
  422. package/dist/lib/services/skill-picker-narrowing.d.ts.map +0 -1
  423. package/dist/lib/services/skill-picker-narrowing.js +0 -13
  424. package/dist/lib/services/skill-picker-narrowing.js.map +0 -1
@@ -3,6 +3,7 @@ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
3
3
  import { LogStatusEx } from '@memberjunction/core';
4
4
  import { MJEnvironmentEntityExtended, ConversationEngine, UserInfoEngine, TaskGraphSubmitOperation } from '@memberjunction/core-entities';
5
5
  import { BeforeAgentTurnEventArgs, AfterAgentTurnEventArgs } from '../../events/chat-events';
6
+ import { ResolveAgentTurn, IsAgentAllowed, FindDisallowedTaskGraphAgents } from '../../utils/agent-turn-routing';
6
7
  import { GenerateAndApplyConversationName } from '../../services/conversation-naming';
7
8
  import { AIEngineBase } from '@memberjunction/ai-engine-base';
8
9
  import { ConversationUtility, agentFailureDisposition, agentFailureMessage } from '@memberjunction/ai-core-plus';
@@ -59,30 +60,198 @@ export class MessageInputComponent extends BaseAngularComponent {
59
60
  realtimeSession;
60
61
  // Default artifact type ID for JSON (when agent doesn't specify DefaultArtifactTypeID)
61
62
  JSON_ARTIFACT_TYPE_ID = 'ae674c7e-ea0d-49ea-89e4-0649f5eb20d4';
62
- conversationId;
63
- conversationName; // For task tracking display
64
- currentUser;
65
- disabled = false;
66
- placeholder = 'Type a message... (Ctrl+Enter to send)';
67
- parentMessageId; // Optional: for replying in threads
68
- enableAttachments = true; // Whether to show attachment button (based on agent modality support)
69
- enableMentions = true; // Whether to enable @-mention autocomplete (agents/users). Hosts addressing a single fixed agent (e.g. Form Builder cockpit) typically set false.
63
+ ConversationId;
64
+ /** @deprecated Use {@link ConversationId}. */
65
+ set conversationId(value) {
66
+ this.ConversationId = value;
67
+ }
68
+ /** @deprecated Use {@link ConversationId}. */
69
+ get conversationId() {
70
+ return this.ConversationId;
71
+ }
72
+ ConversationName;
73
+ /** @deprecated Use {@link ConversationName}. */
74
+ set conversationName(value) {
75
+ this.ConversationName = value;
76
+ }
77
+ /** @deprecated Use {@link ConversationName}. */
78
+ get conversationName() {
79
+ return this.ConversationName;
80
+ } // For task tracking display
81
+ CurrentUser;
82
+ /** @deprecated Use {@link CurrentUser}. */
83
+ set currentUser(value) {
84
+ this.CurrentUser = value;
85
+ }
86
+ /** @deprecated Use {@link CurrentUser}. */
87
+ get currentUser() {
88
+ return this.CurrentUser;
89
+ }
90
+ Disabled = false;
91
+ /** @deprecated Use {@link Disabled}. */
92
+ set disabled(value) {
93
+ this.Disabled = value;
94
+ }
95
+ /** @deprecated Use {@link Disabled}. */
96
+ get disabled() {
97
+ return this.Disabled;
98
+ }
99
+ Placeholder = 'Type a message... (Ctrl+Enter to send)';
100
+ /** @deprecated Use {@link Placeholder}. */
101
+ set placeholder(value) {
102
+ this.Placeholder = value;
103
+ }
104
+ /** @deprecated Use {@link Placeholder}. */
105
+ get placeholder() {
106
+ return this.Placeholder;
107
+ }
108
+ ParentMessageId;
109
+ /** @deprecated Use {@link ParentMessageId}. */
110
+ set parentMessageId(value) {
111
+ this.ParentMessageId = value;
112
+ }
113
+ /** @deprecated Use {@link ParentMessageId}. */
114
+ get parentMessageId() {
115
+ return this.ParentMessageId;
116
+ } // Optional: for replying in threads
117
+ EnableAttachments = true;
118
+ /** @deprecated Use {@link EnableAttachments}. */
119
+ set enableAttachments(value) {
120
+ this.EnableAttachments = value;
121
+ }
122
+ /** @deprecated Use {@link EnableAttachments}. */
123
+ get enableAttachments() {
124
+ return this.EnableAttachments;
125
+ } // Whether to show attachment button (based on agent modality support)
126
+ EnableMentions = true;
127
+ /** @deprecated Use {@link EnableMentions}. */
128
+ set enableMentions(value) {
129
+ this.EnableMentions = value;
130
+ }
131
+ /** @deprecated Use {@link EnableMentions}. */
132
+ get enableMentions() {
133
+ return this.EnableMentions;
134
+ } // Whether to enable @-mention autocomplete (agents/users). Hosts addressing a single fixed agent (e.g. Form Builder cockpit) typically set false.
70
135
  // Per-type caps under enableMentions (all default true) — forwarded to the AI composer's
71
136
  // EnableAgentMentions/EnableEntityMentions/EnableSkillCommands. Let a host keep '/' skill
72
137
  // commands while dropping '@' agent mentions (which would override a pinned default agent).
73
- enableAgentMentions = true;
74
- enableEntityMentions = true;
75
- enableSkillCommands = true;
76
- enablePlanMode = true; // Whether the composer shows the Plan Mode toggle. Hosts that don't expose plan-mode workflows set false.
77
- enableRealtime = true; // Whether the composer shows the realtime voice-call launcher/options. Hosts without a voice experience set false.
78
- maxAttachments = 10; // Maximum number of attachments per message
79
- maxAttachmentSizeBytes = 20 * 1024 * 1024; // Maximum size per attachment (20MB default)
80
- acceptedFileTypes = 'image/*'; // Accepted MIME types pattern
81
- artifactsByDetailId; // Pre-loaded artifact data for performance
82
- systemArtifactsByDetailId; // Pre-loaded system artifact data (Visibility='System Only')
83
- agentRunsByDetailId; // Pre-loaded agent run data for performance
84
- emptyStateMode = false; // When true, emits emptyStateSubmit instead of creating messages directly
85
- appContext = null; // Application context for AI agent awareness
138
+ EnableAgentMentions = true;
139
+ /** @deprecated Use {@link EnableAgentMentions}. */
140
+ set enableAgentMentions(value) {
141
+ this.EnableAgentMentions = value;
142
+ }
143
+ /** @deprecated Use {@link EnableAgentMentions}. */
144
+ get enableAgentMentions() {
145
+ return this.EnableAgentMentions;
146
+ }
147
+ EnableEntityMentions = true;
148
+ /** @deprecated Use {@link EnableEntityMentions}. */
149
+ set enableEntityMentions(value) {
150
+ this.EnableEntityMentions = value;
151
+ }
152
+ /** @deprecated Use {@link EnableEntityMentions}. */
153
+ get enableEntityMentions() {
154
+ return this.EnableEntityMentions;
155
+ }
156
+ EnableSkillCommands = true;
157
+ /** @deprecated Use {@link EnableSkillCommands}. */
158
+ set enableSkillCommands(value) {
159
+ this.EnableSkillCommands = value;
160
+ }
161
+ /** @deprecated Use {@link EnableSkillCommands}. */
162
+ get enableSkillCommands() {
163
+ return this.EnableSkillCommands;
164
+ }
165
+ EnablePlanMode = true;
166
+ /** @deprecated Use {@link EnablePlanMode}. */
167
+ set enablePlanMode(value) {
168
+ this.EnablePlanMode = value;
169
+ }
170
+ /** @deprecated Use {@link EnablePlanMode}. */
171
+ get enablePlanMode() {
172
+ return this.EnablePlanMode;
173
+ } // Whether the composer shows the Plan Mode toggle. Hosts that don't expose plan-mode workflows set false.
174
+ EnableRealtime = true;
175
+ /** @deprecated Use {@link EnableRealtime}. */
176
+ set enableRealtime(value) {
177
+ this.EnableRealtime = value;
178
+ }
179
+ /** @deprecated Use {@link EnableRealtime}. */
180
+ get enableRealtime() {
181
+ return this.EnableRealtime;
182
+ } // Whether the composer shows the realtime voice-call launcher/options. Hosts without a voice experience set false.
183
+ MaxAttachments = 10;
184
+ /** @deprecated Use {@link MaxAttachments}. */
185
+ set maxAttachments(value) {
186
+ this.MaxAttachments = value;
187
+ }
188
+ /** @deprecated Use {@link MaxAttachments}. */
189
+ get maxAttachments() {
190
+ return this.MaxAttachments;
191
+ } // Maximum number of attachments per message
192
+ MaxAttachmentSizeBytes = 20 * 1024 * 1024;
193
+ /** @deprecated Use {@link MaxAttachmentSizeBytes}. */
194
+ set maxAttachmentSizeBytes(value) {
195
+ this.MaxAttachmentSizeBytes = value;
196
+ }
197
+ /** @deprecated Use {@link MaxAttachmentSizeBytes}. */
198
+ get maxAttachmentSizeBytes() {
199
+ return this.MaxAttachmentSizeBytes;
200
+ } // Maximum size per attachment (20MB default)
201
+ AcceptedFileTypes = 'image/*';
202
+ /** @deprecated Use {@link AcceptedFileTypes}. */
203
+ set acceptedFileTypes(value) {
204
+ this.AcceptedFileTypes = value;
205
+ }
206
+ /** @deprecated Use {@link AcceptedFileTypes}. */
207
+ get acceptedFileTypes() {
208
+ return this.AcceptedFileTypes;
209
+ } // Accepted MIME types pattern
210
+ ArtifactsByDetailId;
211
+ /** @deprecated Use {@link ArtifactsByDetailId}. */
212
+ set artifactsByDetailId(value) {
213
+ this.ArtifactsByDetailId = value;
214
+ }
215
+ /** @deprecated Use {@link ArtifactsByDetailId}. */
216
+ get artifactsByDetailId() {
217
+ return this.ArtifactsByDetailId;
218
+ } // Pre-loaded artifact data for performance
219
+ SystemArtifactsByDetailId;
220
+ /** @deprecated Use {@link SystemArtifactsByDetailId}. */
221
+ set systemArtifactsByDetailId(value) {
222
+ this.SystemArtifactsByDetailId = value;
223
+ }
224
+ /** @deprecated Use {@link SystemArtifactsByDetailId}. */
225
+ get systemArtifactsByDetailId() {
226
+ return this.SystemArtifactsByDetailId;
227
+ } // Pre-loaded system artifact data (Visibility='System Only')
228
+ AgentRunsByDetailId;
229
+ /** @deprecated Use {@link AgentRunsByDetailId}. */
230
+ set agentRunsByDetailId(value) {
231
+ this.AgentRunsByDetailId = value;
232
+ }
233
+ /** @deprecated Use {@link AgentRunsByDetailId}. */
234
+ get agentRunsByDetailId() {
235
+ return this.AgentRunsByDetailId;
236
+ } // Pre-loaded agent run data for performance
237
+ EmptyStateMode = false;
238
+ /** @deprecated Use {@link EmptyStateMode}. */
239
+ set emptyStateMode(value) {
240
+ this.EmptyStateMode = value;
241
+ }
242
+ /** @deprecated Use {@link EmptyStateMode}. */
243
+ get emptyStateMode() {
244
+ return this.EmptyStateMode;
245
+ } // When true, emits emptyStateSubmit instead of creating messages directly
246
+ AppContext = null;
247
+ /** @deprecated Use {@link AppContext}. */
248
+ set appContext(value) {
249
+ this.AppContext = value;
250
+ }
251
+ /** @deprecated Use {@link AppContext}. */
252
+ get appContext() {
253
+ return this.AppContext;
254
+ } // Application context for AI agent awareness
86
255
  /**
87
256
  * Plan Mode toggle state — sticky PER CONVERSATION, OFF by default (no behavior change unless
88
257
  * the user turns it on). When on, the user's next message(s) in THIS conversation request Plan
@@ -99,7 +268,7 @@ export class MessageInputComponent extends BaseAngularComponent {
99
268
  * conversation's flag OFF automatically (see message-item's plan-decision handling).
100
269
  */
101
270
  get PlanModeEnabled() {
102
- return PlanModePreference.IsEnabled(this.conversationId);
271
+ return PlanModePreference.IsEnabled(this.ConversationId);
103
272
  }
104
273
  /**
105
274
  * Skill IDs the user requested via `/skill-name` mentions in the message being routed. Collected
@@ -114,7 +283,7 @@ export class MessageInputComponent extends BaseAngularComponent {
114
283
  * start of routing so the value is stable for the whole message dispatch.
115
284
  */
116
285
  collectRequestedSkillIDs() {
117
- const chipData = this.inputBox?.getMentionChipsData() || [];
286
+ const chipData = this.InputBox?.getMentionChipsData() || [];
118
287
  return chipData.filter(chip => chip.type === 'skill').map(chip => chip.id);
119
288
  }
120
289
  /**
@@ -130,7 +299,15 @@ export class MessageInputComponent extends BaseAngularComponent {
130
299
  * context and don't need Sage to route. Leave unset to preserve the
131
300
  * standard Sage-fronted UX of the main Chat app.
132
301
  */
133
- defaultAgentId = null;
302
+ DefaultAgentId = null;
303
+ /** @deprecated Use {@link DefaultAgentId}. */
304
+ set defaultAgentId(value) {
305
+ this.DefaultAgentId = value;
306
+ }
307
+ /** @deprecated Use {@link DefaultAgentId}. */
308
+ get defaultAgentId() {
309
+ return this.DefaultAgentId;
310
+ }
134
311
  /**
135
312
  * Per-conversation pinned default agent — sourced from the loaded
136
313
  * `MJConversationEntity.DefaultAgentID`. When set, this agent is used in
@@ -144,7 +321,15 @@ export class MessageInputComponent extends BaseAngularComponent {
144
321
  * 4. defaultAgentId (embedder-supplied)
145
322
  * 5. Sage fallback
146
323
  */
147
- conversationDefaultAgentId = null;
324
+ ConversationDefaultAgentId = null;
325
+ /** @deprecated Use {@link ConversationDefaultAgentId}. */
326
+ set conversationDefaultAgentId(value) {
327
+ this.ConversationDefaultAgentId = value;
328
+ }
329
+ /** @deprecated Use {@link ConversationDefaultAgentId}. */
330
+ get conversationDefaultAgentId() {
331
+ return this.ConversationDefaultAgentId;
332
+ }
148
333
  /**
149
334
  * The `MJ: AI Agent Configurations.ID` selected via the chat header's
150
335
  * mode picker (Draft / Standard / High). Applied to **non-mention**
@@ -162,14 +347,22 @@ export class MessageInputComponent extends BaseAngularComponent {
162
347
  * messages already in flight or already in history. Affects "what
163
348
  * happens next."
164
349
  */
165
- agentConfigurationPresetId = null;
350
+ AgentConfigurationPresetId = null;
351
+ /** @deprecated Use {@link AgentConfigurationPresetId}. */
352
+ set agentConfigurationPresetId(value) {
353
+ this.AgentConfigurationPresetId = value;
354
+ }
355
+ /** @deprecated Use {@link AgentConfigurationPresetId}. */
356
+ get agentConfigurationPresetId() {
357
+ return this.AgentConfigurationPresetId;
358
+ }
166
359
  // Initial message to send automatically - using getter/setter for precise control
167
360
  _initialMessage = null;
168
361
  _initialAttachments = null;
169
362
  _isComponentReady = false; // Track if component is ready to send
170
363
  /** Conversation this input has already auto-sent its pending first message for. */
171
364
  _autoSentForConversationId = null;
172
- set initialMessage(value) {
365
+ set InitialMessage(value) {
173
366
  // Handle case where an object with {text, attachments} is passed instead of just a string
174
367
  // This can happen if there's a type mismatch in the binding chain
175
368
  let actualValue = value;
@@ -183,31 +376,55 @@ export class MessageInputComponent extends BaseAngularComponent {
183
376
  this.triggerInitialSend();
184
377
  }
185
378
  }
186
- get initialMessage() {
379
+ get InitialMessage() {
187
380
  return this._initialMessage;
188
381
  }
189
- set initialAttachments(value) {
382
+ /** @deprecated Use {@link InitialMessage}. */
383
+ get initialMessage() {
384
+ return this.InitialMessage;
385
+ }
386
+ /** @deprecated Use {@link InitialMessage}. */
387
+ set initialMessage(value) {
388
+ this.InitialMessage = value;
389
+ }
390
+ set InitialAttachments(value) {
190
391
  this._initialAttachments = value;
191
392
  }
192
- get initialAttachments() {
393
+ get InitialAttachments() {
193
394
  return this._initialAttachments;
194
395
  }
396
+ /** @deprecated Use {@link InitialAttachments}. */
397
+ get initialAttachments() {
398
+ return this.InitialAttachments;
399
+ }
400
+ /** @deprecated Use {@link InitialAttachments}. */
401
+ set initialAttachments(value) {
402
+ this.InitialAttachments = value;
403
+ }
195
404
  _conversationHistory = [];
196
- get conversationHistory() {
405
+ get ConversationHistory() {
197
406
  return this._conversationHistory;
198
407
  }
199
- set conversationHistory(value) {
408
+ set ConversationHistory(value) {
200
409
  this._conversationHistory = value;
201
410
  }
411
+ /** @deprecated Use {@link ConversationHistory}. */
412
+ get conversationHistory() {
413
+ return this.ConversationHistory;
414
+ }
415
+ /** @deprecated Use {@link ConversationHistory}. */
416
+ set conversationHistory(value) {
417
+ this.ConversationHistory = value;
418
+ }
202
419
  // Message IDs that are in-progress and need streaming reconnection
203
420
  // Using getter/setter to react immediately when value changes (avoids timing issues with ngOnChanges)
204
421
  _inProgressMessageIds;
205
- set inProgressMessageIds(value) {
422
+ set InProgressMessageIds(value) {
206
423
  this._inProgressMessageIds = value;
207
424
  // React immediately when input changes (after component initialized)
208
425
  // This ensures callbacks are registered without relying on ngOnChanges timing
209
426
  if (this.streamingService && value && value.length > 0) {
210
- this.reconnectInProgressMessages();
427
+ this.ReconnectInProgressMessages();
211
428
  }
212
429
  else if (this.streamingService) {
213
430
  // Empty/undefined — e.g. this input was backgrounded by a conversation swap
@@ -217,48 +434,236 @@ export class MessageInputComponent extends BaseAngularComponent {
217
434
  this.unregisterAllCallbacks();
218
435
  }
219
436
  }
220
- get inProgressMessageIds() {
437
+ get InProgressMessageIds() {
221
438
  return this._inProgressMessageIds;
222
439
  }
440
+ /** @deprecated Use {@link InProgressMessageIds}. */
441
+ get inProgressMessageIds() {
442
+ return this.InProgressMessageIds;
443
+ }
444
+ /** @deprecated Use {@link InProgressMessageIds}. */
445
+ set inProgressMessageIds(value) {
446
+ this.InProgressMessageIds = value;
447
+ }
223
448
  /**
224
449
  * Application context for the current conversation. Threaded through from the
225
450
  * chat-area component for inclusion in the {@link beforeAgentTurn} event payload —
226
451
  * lets listeners reason about which app's chat surface is invoking the agent.
227
452
  * Optional; defaults to null for surfaces with no app context.
228
453
  */
229
- applicationId = null;
230
- messageSent = new EventEmitter();
231
- agentResponse = new EventEmitter();
454
+ ApplicationId = null;
455
+ /** @deprecated Use {@link ApplicationId}. */
456
+ set applicationId(value) {
457
+ this.ApplicationId = value;
458
+ }
459
+ /** @deprecated Use {@link ApplicationId}. */
460
+ get applicationId() {
461
+ return this.ApplicationId;
462
+ }
463
+ // ── Host rules for agent turns ──────────────────────────────────────────
464
+ // All opt-in. Left at their defaults, every message is answered exactly as before.
465
+ /**
466
+ * When a message starts an agent turn. `'Always'` (the default) answers every message, as
467
+ * before. `'MentionOnly'` answers only a message that tags an agent; any other message is
468
+ * posted with no turn at all — no reply row, no placeholder, no turn events — which is what a
469
+ * chat between several people needs.
470
+ */
471
+ AgentReplyMode = 'Always';
472
+ /**
473
+ * The agents that may answer here. Narrows the composer's '@' list, every route (a tagged
474
+ * agent, continuity, the pinned and host default agents, the conversation manager) and the
475
+ * conversation manager's delegation, including each agent step of a workflow it plans. A tagged
476
+ * agent outside the list is ignored. Null (the default) allows every agent the user can run;
477
+ * an empty list allows none.
478
+ */
479
+ AllowedAgentIDs = null;
480
+ /**
481
+ * The people the composer's '@' list offers, such as the chat's members. Null (the default)
482
+ * offers only the current user, as before.
483
+ */
484
+ MentionPeople = null;
232
485
  /**
233
- * Cancelable — fired BEFORE `agentService.processMessage()` is called for a user turn.
234
- * Listeners may set `event.Cancel = true` to halt the agent invocation (e.g., a
235
- * client-side guardrail blocking the turn). When canceled, the corresponding
236
- * {@link afterAgentTurn} event is NOT fired and the running task is cleared.
237
- * Follows MJ's established Before/After cancelable event pattern.
486
+ * The first moment of the conversation an agent turn may read. The server loads the agent's
487
+ * history from there and uses no summary of earlier messages; the run's own reads of the
488
+ * conversation (history tools, attached artifacts, the previous output it continues from)
489
+ * start there too, and so does the continuity route, which considers only replies written at
490
+ * or after it. Sent to the server only when set, so leaving it null keeps working against an
491
+ * MJAPI that predates it (one that predates it rejects a turn that sets it, rather than
492
+ * reading past the floor). Null (the default) reads the whole conversation.
238
493
  */
239
- beforeAgentTurn = new EventEmitter();
494
+ AgentHistoryFrom = null;
240
495
  /**
241
- * Fired AFTER a successful agent turn completes. Carries the agent run id and the
242
- * full agent result. Not fired when {@link beforeAgentTurn} was canceled or when
243
- * the underlying `processMessage` errored.
496
+ * Runs agent turns on the host's server instead of MJ's own path. The chat area still picks
497
+ * the agent and fires {@link BeforeAgentTurn}; it then calls the handler once per turn, before
498
+ * any reply row exists, and shows the rows the handler reports. Null (the default) runs turns
499
+ * on MJ's path.
244
500
  */
245
- afterAgentTurn = new EventEmitter();
501
+ AgentTurnHandler = null;
502
+ /**
503
+ * Whether MJ names a new conversation from its first message. True (the default) keeps
504
+ * today's behavior; a host that names its own chats turns it off.
505
+ */
506
+ AutoNameConversation = true;
507
+ MessageSent = new EventEmitter();
508
+ /**
509
+ * @deprecated Use {@link MessageSent}.
510
+ *
511
+ * The same emitter under the old binding name, so a template still binding
512
+ * (messageSent) keeps working. Must stay AFTER MessageSent: class fields
513
+ * initialise in order, and the other way round this captures undefined.
514
+ */
515
+ messageSent = this.MessageSent;
516
+ AgentResponse = new EventEmitter();
517
+ /**
518
+ * @deprecated Use {@link AgentResponse}.
519
+ *
520
+ * The same emitter under the old binding name, so a template still binding
521
+ * (agentResponse) keeps working. Must stay AFTER AgentResponse: class fields
522
+ * initialise in order, and the other way round this captures undefined.
523
+ */
524
+ agentResponse = this.AgentResponse;
525
+ /**
526
+ * Cancelable — fired once per agent turn, on every route, after routing has picked the agent
527
+ * and BEFORE any reply row exists. Listeners may set `event.Cancel = true` to stop the turn
528
+ * (nothing more is written, and {@link AfterAgentTurn} does not fire) or set
529
+ * `event.RedirectAgentId` to send it to another allowed agent. A message that starts no turn
530
+ * fires nothing. Follows MJ's established Before/After cancelable event pattern.
531
+ */
532
+ BeforeAgentTurn = new EventEmitter();
533
+ /**
534
+ * @deprecated Use {@link BeforeAgentTurn}.
535
+ *
536
+ * The same emitter under the old binding name, so a template still binding
537
+ * (beforeAgentTurn) keeps working. Must stay AFTER BeforeAgentTurn: class fields
538
+ * initialise in order, and the other way round this captures undefined.
539
+ */
540
+ beforeAgentTurn = this.BeforeAgentTurn;
541
+ /**
542
+ * Fired AFTER a successful agent turn, on every route. Carries the agent run id and the
543
+ * full agent result. Not fired when {@link BeforeAgentTurn} was canceled, when the turn
544
+ * failed, or when a host {@link AgentTurnHandler} reported no result.
545
+ */
546
+ AfterAgentTurn = new EventEmitter();
547
+ /**
548
+ * @deprecated Use {@link AfterAgentTurn}.
549
+ *
550
+ * The same emitter under the old binding name, so a template still binding
551
+ * (afterAgentTurn) keeps working. Must stay AFTER AfterAgentTurn: class fields
552
+ * initialise in order, and the other way round this captures undefined.
553
+ */
554
+ afterAgentTurn = this.AfterAgentTurn;
246
555
  // conversationId is carried on every agent-lifecycle event so the parent chat-area can drop
247
556
  // events emitted by a BACKGROUND conversation's (hidden, still-streaming) input after the user
248
557
  // has swapped conversations — preventing cross-conversation state/cache bleed. Sourced from the
249
558
  // ConversationDetail entity's ConversationID (the captured, immutable value), never this.conversationId.
250
- agentRunDetected = new EventEmitter();
251
- agentRunUpdate = new EventEmitter(); // Emits when agent run data updates during progress
252
- messageComplete = new EventEmitter(); // Emits when message completes (success or error)
253
- artifactCreated = new EventEmitter();
254
- conversationRenamed = new EventEmitter();
255
- intentCheckStarted = new EventEmitter(); // Emits when intent checking starts
256
- intentCheckCompleted = new EventEmitter(); // Emits when intent checking completes (carries conversationId so the parent can drop a background conversation's completion after a swap — symmetric with intentCheckStarted)
257
- initialMessageAutoSendStarted = new EventEmitter(); // Emitted when this input latches the pending first message for auto-send
258
- initialMessageAutoSendFailed = new EventEmitter(); // Emitted when a latched pending first message fails before messageSent
259
- emptyStateSubmit = new EventEmitter(); // Emitted when in emptyStateMode
260
- uploadStateChanged = new EventEmitter(); // Emits when attachment upload state changes
261
- inputBox;
559
+ AgentRunDetected = new EventEmitter();
560
+ /**
561
+ * @deprecated Use {@link AgentRunDetected}.
562
+ *
563
+ * The same emitter under the old binding name, so a template still binding
564
+ * (agentRunDetected) keeps working. Must stay AFTER AgentRunDetected: class fields
565
+ * initialise in order, and the other way round this captures undefined.
566
+ */
567
+ agentRunDetected = this.AgentRunDetected;
568
+ AgentRunUpdate = new EventEmitter();
569
+ /**
570
+ * @deprecated Use {@link AgentRunUpdate}.
571
+ *
572
+ * The same emitter under the old binding name, so a template still binding
573
+ * (agentRunUpdate) keeps working. Must stay AFTER AgentRunUpdate: class fields
574
+ * initialise in order, and the other way round this captures undefined.
575
+ */
576
+ agentRunUpdate = this.AgentRunUpdate; // Emits when agent run data updates during progress
577
+ MessageComplete = new EventEmitter();
578
+ /**
579
+ * @deprecated Use {@link MessageComplete}.
580
+ *
581
+ * The same emitter under the old binding name, so a template still binding
582
+ * (messageComplete) keeps working. Must stay AFTER MessageComplete: class fields
583
+ * initialise in order, and the other way round this captures undefined.
584
+ */
585
+ messageComplete = this.MessageComplete; // Emits when message completes (success or error)
586
+ ArtifactCreated = new EventEmitter();
587
+ /**
588
+ * @deprecated Use {@link ArtifactCreated}.
589
+ *
590
+ * The same emitter under the old binding name, so a template still binding
591
+ * (artifactCreated) keeps working. Must stay AFTER ArtifactCreated: class fields
592
+ * initialise in order, and the other way round this captures undefined.
593
+ */
594
+ artifactCreated = this.ArtifactCreated;
595
+ ConversationRenamed = new EventEmitter();
596
+ /**
597
+ * @deprecated Use {@link ConversationRenamed}.
598
+ *
599
+ * The same emitter under the old binding name, so a template still binding
600
+ * (conversationRenamed) keeps working. Must stay AFTER ConversationRenamed: class fields
601
+ * initialise in order, and the other way round this captures undefined.
602
+ */
603
+ conversationRenamed = this.ConversationRenamed;
604
+ IntentCheckStarted = new EventEmitter();
605
+ /**
606
+ * @deprecated Use {@link IntentCheckStarted}.
607
+ *
608
+ * The same emitter under the old binding name, so a template still binding
609
+ * (intentCheckStarted) keeps working. Must stay AFTER IntentCheckStarted: class fields
610
+ * initialise in order, and the other way round this captures undefined.
611
+ */
612
+ intentCheckStarted = this.IntentCheckStarted; // Emits when intent checking starts
613
+ IntentCheckCompleted = new EventEmitter();
614
+ /**
615
+ * @deprecated Use {@link IntentCheckCompleted}.
616
+ *
617
+ * The same emitter under the old binding name, so a template still binding
618
+ * (intentCheckCompleted) keeps working. Must stay AFTER IntentCheckCompleted: class fields
619
+ * initialise in order, and the other way round this captures undefined.
620
+ */
621
+ intentCheckCompleted = this.IntentCheckCompleted; // Emits when intent checking completes (carries conversationId so the parent can drop a background conversation's completion after a swap — symmetric with intentCheckStarted)
622
+ InitialMessageAutoSendStarted = new EventEmitter();
623
+ /**
624
+ * @deprecated Use {@link InitialMessageAutoSendStarted}.
625
+ *
626
+ * The same emitter under the old binding name, so a template still binding
627
+ * (initialMessageAutoSendStarted) keeps working. Must stay AFTER InitialMessageAutoSendStarted: class fields
628
+ * initialise in order, and the other way round this captures undefined.
629
+ */
630
+ initialMessageAutoSendStarted = this.InitialMessageAutoSendStarted; // Emitted when this input latches the pending first message for auto-send
631
+ InitialMessageAutoSendFailed = new EventEmitter();
632
+ /**
633
+ * @deprecated Use {@link InitialMessageAutoSendFailed}.
634
+ *
635
+ * The same emitter under the old binding name, so a template still binding
636
+ * (initialMessageAutoSendFailed) keeps working. Must stay AFTER InitialMessageAutoSendFailed: class fields
637
+ * initialise in order, and the other way round this captures undefined.
638
+ */
639
+ initialMessageAutoSendFailed = this.InitialMessageAutoSendFailed; // Emitted when a latched pending first message fails before messageSent
640
+ EmptyStateSubmit = new EventEmitter();
641
+ /**
642
+ * @deprecated Use {@link EmptyStateSubmit}.
643
+ *
644
+ * The same emitter under the old binding name, so a template still binding
645
+ * (emptyStateSubmit) keeps working. Must stay AFTER EmptyStateSubmit: class fields
646
+ * initialise in order, and the other way round this captures undefined.
647
+ */
648
+ emptyStateSubmit = this.EmptyStateSubmit; // Emitted when in emptyStateMode
649
+ UploadStateChanged = new EventEmitter();
650
+ /**
651
+ * @deprecated Use {@link UploadStateChanged}.
652
+ *
653
+ * The same emitter under the old binding name, so a template still binding
654
+ * (uploadStateChanged) keeps working. Must stay AFTER UploadStateChanged: class fields
655
+ * initialise in order, and the other way round this captures undefined.
656
+ */
657
+ uploadStateChanged = this.UploadStateChanged; // Emits when attachment upload state changes
658
+ InputBox;
659
+ /** @deprecated Use {@link InputBox}. */
660
+ get inputBox() {
661
+ return this.InputBox;
662
+ }
663
+ /** @deprecated Use {@link InputBox}. */
664
+ set inputBox(value) {
665
+ this.InputBox = value;
666
+ }
262
667
  _messageText = '';
263
668
  /**
264
669
  * The composer's text. An accessor pair rather than a plain field because every write reaches the
@@ -274,13 +679,21 @@ export class MessageInputComponent extends BaseAngularComponent {
274
679
  * Read is a plain field read; there is no two-way `ngModel` on this property (the template binds
275
680
  * `[value]="messageText"` one-way), so the pair is transparent to callers.
276
681
  */
277
- get messageText() {
682
+ get MessageText() {
278
683
  return this._messageText;
279
684
  }
280
- set messageText(value) {
685
+ set MessageText(value) {
281
686
  this._messageText = value;
282
687
  this.mentionedAgentId = undefined;
283
688
  }
689
+ /** @deprecated Use {@link MessageText}. */
690
+ get messageText() {
691
+ return this.MessageText;
692
+ }
693
+ /** @deprecated Use {@link MessageText}. */
694
+ set messageText(value) {
695
+ this.MessageText = value;
696
+ }
284
697
  /**
285
698
  * Prefills the composer with draft text WITHOUT sending (unlike pendingMessage,
286
699
  * which auto-sends) and focuses the input — e.g. the omnibar's '@agent' flow
@@ -292,24 +705,40 @@ export class MessageInputComponent extends BaseAngularComponent {
292
705
  * view isn't up yet, ngAfterViewInit applies it. Emits initialDraftApplied so the
293
706
  * host can clear its pending state.
294
707
  */
295
- set initialDraft(value) {
708
+ set InitialDraft(value) {
296
709
  if (value && value !== this.appliedInitialDraft) {
297
710
  this.appliedInitialDraft = value;
298
- if (this.inputBox) {
711
+ if (this.InputBox) {
299
712
  this.SetDraft(value, true);
300
- this.initialDraftApplied.emit();
713
+ this.InitialDraftApplied.emit();
301
714
  }
302
715
  else {
303
716
  this.pendingInitialDraft = value;
304
717
  }
305
718
  }
306
719
  }
307
- get initialDraft() {
720
+ get InitialDraft() {
308
721
  return this.appliedInitialDraft;
309
722
  }
723
+ /** @deprecated Use {@link InitialDraft}. */
724
+ get initialDraft() {
725
+ return this.InitialDraft;
726
+ }
727
+ /** @deprecated Use {@link InitialDraft}. */
728
+ set initialDraft(value) {
729
+ this.InitialDraft = value;
730
+ }
310
731
  appliedInitialDraft = null;
311
732
  pendingInitialDraft = null;
312
- initialDraftApplied = new EventEmitter();
733
+ InitialDraftApplied = new EventEmitter();
734
+ /**
735
+ * @deprecated Use {@link InitialDraftApplied}.
736
+ *
737
+ * The same emitter under the old binding name, so a template still binding
738
+ * (initialDraftApplied) keeps working. Must stay AFTER InitialDraftApplied: class fields
739
+ * initialise in order, and the other way round this captures undefined.
740
+ */
741
+ initialDraftApplied = this.InitialDraftApplied;
313
742
  /**
314
743
  * Live draft-state signal: fires on every composer value change with the
315
744
  * SERIALIZED content (mention pills encoded via getPlainTextWithJsonMentions,
@@ -320,12 +749,12 @@ export class MessageInputComponent extends BaseAngularComponent {
320
749
  ComposerBlurred = new EventEmitter();
321
750
  /** Handles the composer's value stream: keeps messageText in sync + emits draft state. */
322
751
  OnComposerValueChanged(value) {
323
- this.messageText = value;
752
+ this.MessageText = value;
324
753
  this.DraftStateChanged.emit(this.GetSerializedDraft());
325
754
  }
326
755
  /** Current composer content in the lossless serialized form ('' when empty). */
327
756
  GetSerializedDraft() {
328
- const serialized = this.inputBox?.getPlainTextWithJsonMentions() ?? this.messageText ?? '';
757
+ const serialized = this.InputBox?.getPlainTextWithJsonMentions() ?? this.MessageText ?? '';
329
758
  return serialized.trim().length === 0 ? '' : serialized;
330
759
  }
331
760
  /**
@@ -338,27 +767,27 @@ export class MessageInputComponent extends BaseAngularComponent {
338
767
  * @returns false while the composer view isn't mounted yet — callers may retry.
339
768
  */
340
769
  async InsertAgentMention(agentName, focus = true, clearExisting = true) {
341
- if (!this.inputBox) {
770
+ if (!this.InputBox) {
342
771
  console.log(`[Omnibar→Chat] InsertAgentMention('${agentName}'): input box not mounted yet — caller will retry`);
343
772
  return false;
344
773
  }
345
774
  if (clearExisting) {
346
775
  // Pre-addressing REPLACES any un-sent draft (tagging agent B after agent A
347
776
  // must not stack pills).
348
- this.inputBox.mentionEditor?.clear();
349
- this.messageText = '';
777
+ this.InputBox.mentionEditor?.clear();
778
+ this.MessageText = '';
350
779
  }
351
780
  try {
352
- if (!this.mentionAutocomplete.IsInitialized && this.currentUser) {
781
+ if (!this.mentionAutocomplete.IsInitialized && this.CurrentUser) {
353
782
  console.log(`[Omnibar→Chat] InsertAgentMention('${agentName}'): initializing mention autocomplete…`);
354
- await this.mentionAutocomplete.initialize(this.currentUser);
783
+ await this.mentionAutocomplete.initialize(this.CurrentUser);
355
784
  }
356
785
  const wanted = agentName.trim().toLowerCase();
357
786
  const suggestion = this.mentionAutocomplete
358
787
  .getSuggestions(agentName, false, '@')
359
788
  .find(s => s.type === 'agent' && s.name.trim().toLowerCase() === wanted);
360
789
  if (suggestion) {
361
- const inserted = this.inputBox.InsertMention(suggestion, focus);
790
+ const inserted = this.InputBox.InsertMention(suggestion, focus);
362
791
  console.log(`[Omnibar→Chat] InsertAgentMention('${agentName}'): resolved to pill (id=${suggestion.id}) — insert ${inserted ? 'OK' : 'FAILED (editor view not ready)'}`);
363
792
  if (inserted) {
364
793
  if (focus) {
@@ -385,7 +814,7 @@ export class MessageInputComponent extends BaseAngularComponent {
385
814
  scheduleFocusReassert(context) {
386
815
  for (const delay of [300, 900, 1800]) {
387
816
  setTimeout(() => {
388
- const editor = this.inputBox?.mentionEditor;
817
+ const editor = this.InputBox?.mentionEditor;
389
818
  if (editor && !editor.HasFocus) {
390
819
  const ok = editor.FocusCaretAtEnd();
391
820
  console.log(`[Omnibar→Chat] focus re-assert (+${delay}ms) for '${context}': ${ok ? 'refocused' : 'editor gone'}`);
@@ -394,18 +823,66 @@ export class MessageInputComponent extends BaseAngularComponent {
394
823
  }
395
824
  }
396
825
  SetDraft(text, focus = true) {
397
- this.messageText = text;
826
+ this.MessageText = text;
398
827
  if (focus) {
399
828
  // The composer mounts/binds on the next tick after messageText flows down.
400
- setTimeout(() => this.inputBox?.focus(), 50);
401
- }
829
+ setTimeout(() => this.InputBox?.focus(), 50);
830
+ }
831
+ }
832
+ IsSending = false;
833
+ /** @deprecated Use {@link IsSending}. */
834
+ get isSending() {
835
+ return this.IsSending;
836
+ }
837
+ /** @deprecated Use {@link IsSending}. */
838
+ set isSending(value) {
839
+ this.IsSending = value;
840
+ }
841
+ IsProcessing = false;
842
+ /** @deprecated Use {@link IsProcessing}. */
843
+ get isProcessing() {
844
+ return this.IsProcessing;
845
+ }
846
+ /** @deprecated Use {@link IsProcessing}. */
847
+ set isProcessing(value) {
848
+ this.IsProcessing = value;
849
+ } // True when waiting for agent/naming response
850
+ ProcessingMessage = 'AI is responding...';
851
+ /** @deprecated Use {@link ProcessingMessage}. */
852
+ get processingMessage() {
853
+ return this.ProcessingMessage;
854
+ }
855
+ /** @deprecated Use {@link ProcessingMessage}. */
856
+ set processingMessage(value) {
857
+ this.ProcessingMessage = value;
858
+ } // Message shown during processing
859
+ IsUploadingAttachments = false;
860
+ /** @deprecated Use {@link IsUploadingAttachments}. */
861
+ get isUploadingAttachments() {
862
+ return this.IsUploadingAttachments;
863
+ }
864
+ /** @deprecated Use {@link IsUploadingAttachments}. */
865
+ set isUploadingAttachments(value) {
866
+ this.IsUploadingAttachments = value;
867
+ } // True when uploading attachments to server
868
+ UploadingMessage = 'Uploading attachments...';
869
+ /** @deprecated Use {@link UploadingMessage}. */
870
+ get uploadingMessage() {
871
+ return this.UploadingMessage;
872
+ }
873
+ /** @deprecated Use {@link UploadingMessage}. */
874
+ set uploadingMessage(value) {
875
+ this.UploadingMessage = value;
876
+ } // Message shown during upload
877
+ ConverationManagerAgent = null;
878
+ /** @deprecated Use {@link ConverationManagerAgent}. */
879
+ get converationManagerAgent() {
880
+ return this.ConverationManagerAgent;
881
+ }
882
+ /** @deprecated Use {@link ConverationManagerAgent}. */
883
+ set converationManagerAgent(value) {
884
+ this.ConverationManagerAgent = value;
402
885
  }
403
- isSending = false;
404
- isProcessing = false; // True when waiting for agent/naming response
405
- processingMessage = 'AI is responding...'; // Message shown during processing
406
- isUploadingAttachments = false; // True when uploading attachments to server
407
- uploadingMessage = 'Uploading attachments...'; // Message shown during upload
408
- converationManagerAgent = null;
409
886
  // Track completion timestamps to prevent race conditions with late progress updates
410
887
  completionTimestamps = new Map();
411
888
  // Track registered streaming callbacks for cleanup
@@ -437,7 +914,15 @@ export class MessageInputComponent extends BaseAngularComponent {
437
914
  }
438
915
  // ── Voice session (Realtime Co-Agent) ───────────────────────────────
439
916
  /** True while a live voice session is active — drives the overlay + mic state. */
440
- voiceActive = false;
917
+ VoiceActive = false;
918
+ /** @deprecated Use {@link VoiceActive}. */
919
+ get voiceActive() {
920
+ return this.VoiceActive;
921
+ }
922
+ /** @deprecated Use {@link VoiceActive}. */
923
+ set voiceActive(value) {
924
+ this.VoiceActive = value;
925
+ }
441
926
  realtimeActiveSub;
442
927
  async ngOnInit() {
443
928
  // Bind provider-aware services to this component's provider.
@@ -449,16 +934,16 @@ export class MessageInputComponent extends BaseAngularComponent {
449
934
  this.realtimeSession.Provider = p;
450
935
  // Reflect the live voice-session Active flag into a local field for the template.
451
936
  this.realtimeActiveSub = this.realtimeSession.Active$.subscribe(active => {
452
- this.voiceActive = active;
937
+ this.VoiceActive = active;
453
938
  });
454
- this.converationManagerAgent = await this.agentService.getConversationManagerAgent();
939
+ this.ConverationManagerAgent = await this.agentService.getConversationManagerAgent();
455
940
  // Warm UserInfoEngine so the PlanModeEnabled getter has the cached settings available
456
941
  // (no-op when already loaded; failure just leaves the toggle at its default OFF).
457
942
  PlanModePreference.Warm();
458
943
  // Initialize mention autocomplete (needed for parsing mentions in messages)
459
- await this.mentionAutocomplete.initialize(this.currentUser);
944
+ await this.mentionAutocomplete.initialize(this.CurrentUser);
460
945
  // Reconnect to any in-progress messages for streaming updates (via global streaming service)
461
- this.reconnectInProgressMessages();
946
+ this.ReconnectInProgressMessages();
462
947
  }
463
948
  ngOnChanges(changes) {
464
949
  // When conversation changes, focus the input
@@ -474,7 +959,7 @@ export class MessageInputComponent extends BaseAngularComponent {
474
959
  // next tick — the composer's own view finishes mounting first
475
960
  setTimeout(() => {
476
961
  this.SetDraft(draft, true);
477
- this.initialDraftApplied.emit();
962
+ this.InitialDraftApplied.emit();
478
963
  }, 50);
479
964
  }
480
965
  // Focus input on initial load
@@ -494,23 +979,23 @@ export class MessageInputComponent extends BaseAngularComponent {
494
979
  const message = this._initialMessage;
495
980
  const attachments = this._initialAttachments;
496
981
  const hasContent = !!message || !!(attachments && attachments.length > 0);
497
- if (!hasContent || !this.conversationId || UUIDsEqual(this._autoSentForConversationId, this.conversationId)) {
982
+ if (!hasContent || !this.ConversationId || UUIDsEqual(this._autoSentForConversationId, this.ConversationId)) {
498
983
  return;
499
984
  }
500
- this._autoSentForConversationId = this.conversationId;
985
+ this._autoSentForConversationId = this.ConversationId;
501
986
  // Set pending attachments before sending
502
987
  if (attachments && attachments.length > 0) {
503
988
  this.pendingAttachments = [...attachments];
504
989
  }
505
990
  Promise.resolve().then(() => {
506
- this.initialMessageAutoSendStarted.emit({ conversationId: this.conversationId });
991
+ this.InitialMessageAutoSendStarted.emit({ conversationId: this.ConversationId });
507
992
  });
508
993
  // Use setTimeout to ensure we're outside of change detection cycle
509
994
  setTimeout(async () => {
510
- const sent = await this.sendMessageWithText(message || '');
995
+ const sent = await this.SendMessageWithText(message || '');
511
996
  if (!sent) {
512
997
  this._autoSentForConversationId = null;
513
- this.initialMessageAutoSendFailed.emit({ conversationId: this.conversationId });
998
+ this.InitialMessageAutoSendFailed.emit({ conversationId: this.ConversationId });
514
999
  }
515
1000
  }, 100);
516
1001
  }
@@ -531,14 +1016,17 @@ export class MessageInputComponent extends BaseAngularComponent {
531
1016
  * 2. per-conversation pinned default
532
1017
  * 3. embedder-supplied default
533
1018
  * 4. Sage fallback
534
- * Returns null only if Sage itself failed to load.
1019
+ * skipping any agent outside {@link AllowedAgentIDs}. {@link AgentReplyMode} does not apply:
1020
+ * starting a call is itself a choice to talk to an agent. Returns null when Sage failed to
1021
+ * load, or when the host allows none of these agents.
535
1022
  */
1023
+ ResolveCurrentAgentId() {
1024
+ const target = ResolveAgentTurn(this.agentTurnCandidates([]), { ReplyMode: 'Always', AllowedAgentIDs: this.AllowedAgentIDs }, () => true);
1025
+ return target?.AgentId ?? null;
1026
+ }
1027
+ /** @deprecated Use {@link ResolveCurrentAgentId}. */
536
1028
  resolveCurrentAgentId() {
537
- return this.findLastNonSageAgentId()
538
- ?? this.conversationDefaultAgentId
539
- ?? this.defaultAgentId
540
- ?? this.converationManagerAgent?.ID
541
- ?? null;
1029
+ return this.ResolveCurrentAgentId();
542
1030
  }
543
1031
  /**
544
1032
  * The agent the '/' skill picker should narrow to. Mirrors routing's priority: an explicit
@@ -546,12 +1034,16 @@ export class MessageInputComponent extends BaseAngularComponent {
546
1034
  * would otherwise go to ({@link resolveCurrentAgentId}). Bound to `mj-ai-composer`'s
547
1035
  * `TargetAgentId`; null = unknown, no narrowing.
548
1036
  */
549
- get pickerTargetAgentId() {
1037
+ get PickerTargetAgentId() {
550
1038
  if (this.mentionedAgentId === undefined) {
551
- const chips = this.inputBox?.getMentionChipsData() || [];
1039
+ const chips = this.InputBox?.getMentionChipsData() || [];
552
1040
  this.mentionedAgentId = chips.find(chip => chip.type === 'agent')?.id ?? null;
553
1041
  }
554
- return this.mentionedAgentId ?? this.resolveCurrentAgentId();
1042
+ return this.mentionedAgentId ?? this.ResolveCurrentAgentId();
1043
+ }
1044
+ /** @deprecated Use {@link PickerTargetAgentId}. */
1045
+ get pickerTargetAgentId() {
1046
+ return this.PickerTargetAgentId;
555
1047
  }
556
1048
  /**
557
1049
  * Memo for the first `@agent` chip in the draft, so the template-bound
@@ -582,8 +1074,12 @@ export class MessageInputComponent extends BaseAngularComponent {
582
1074
  */
583
1075
  mentionedAgentId = undefined;
584
1076
  /** True when the mic button should be enabled (have an agent + not disabled). */
1077
+ get CanStartRealtime() {
1078
+ return !this.Disabled && !this.VoiceActive && !!this.ResolveCurrentAgentId();
1079
+ }
1080
+ /** @deprecated Use {@link CanStartRealtime}. */
585
1081
  get canStartRealtime() {
586
- return !this.disabled && !this.voiceActive && !!this.resolveCurrentAgentId();
1082
+ return this.CanStartRealtime;
587
1083
  }
588
1084
  /**
589
1085
  * Display name of the agent the voice session fronts. Resolved here (this component
@@ -591,7 +1087,7 @@ export class MessageInputComponent extends BaseAngularComponent {
591
1087
  * session start so the chat-area-hosted overlay can read it from the service.
592
1088
  */
593
1089
  resolveRealtimeAgentName() {
594
- const agentId = this.resolveCurrentAgentId();
1090
+ const agentId = this.ResolveCurrentAgentId();
595
1091
  if (agentId) {
596
1092
  const match = this.mentionAutocomplete
597
1093
  .getAvailableAgents()
@@ -600,10 +1096,18 @@ export class MessageInputComponent extends BaseAngularComponent {
600
1096
  return match.Name;
601
1097
  }
602
1098
  }
603
- return this.converationManagerAgent?.Name ?? 'Sage';
1099
+ return this.ConverationManagerAgent?.Name ?? 'Sage';
604
1100
  }
605
1101
  /** True while the "Start a voice call with…" agent picker popover is open. */
606
- showRealtimeAgentPicker = false;
1102
+ ShowRealtimeAgentPicker = false;
1103
+ /** @deprecated Use {@link ShowRealtimeAgentPicker}. */
1104
+ get showRealtimeAgentPicker() {
1105
+ return this.ShowRealtimeAgentPicker;
1106
+ }
1107
+ /** @deprecated Use {@link ShowRealtimeAgentPicker}. */
1108
+ set showRealtimeAgentPicker(value) {
1109
+ this.ShowRealtimeAgentPicker = value;
1110
+ }
607
1111
  /**
608
1112
  * CDK connected-overlay positions for the voice agent picker. Preferred: open UPWARD,
609
1113
  * right edge aligned to the composer's right edge (matching the old absolute placement).
@@ -611,40 +1115,79 @@ export class MessageInputComponent extends BaseAngularComponent {
611
1115
  * body-level CDK overlay container (with `cdkConnectedOverlayPush`), it escapes the chat
612
1116
  * overlay's `overflow: hidden` border and can never clip at the top of a narrow overlay.
613
1117
  */
614
- pickerOverlayPositions = [
1118
+ PickerOverlayPositions = [
615
1119
  { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -8 },
616
1120
  { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 8 },
617
1121
  ];
1122
+ /** @deprecated Use {@link PickerOverlayPositions}. */
1123
+ get pickerOverlayPositions() {
1124
+ return this.PickerOverlayPositions;
1125
+ }
618
1126
  /**
619
1127
  * `MJ: User Settings` key persisting the user's co-agent choice for realtime calls
620
1128
  * (server-side, cross-device — never localStorage). Stored shape: `{"coAgentId":
621
1129
  * string | null}` — `null` is an explicit "Auto" choice that overwrites an older pick.
622
1130
  */
623
- static CoAgentPrefKey = 'mj.realtimeVoice.coAgent.v1';
1131
+ static coAgentPrefKey = 'mj.realtimeVoice.coAgent.v1';
624
1132
  /**
625
1133
  * The persisted co-agent preference, loaded just before the picker opens (and read by
626
1134
  * the instant-start path). `null` = no preference / explicit "Auto".
627
1135
  */
628
- voicePickerDefaultCoAgentId = null;
1136
+ VoicePickerDefaultCoAgentId = null;
1137
+ /** @deprecated Use {@link VoicePickerDefaultCoAgentId}. */
1138
+ get voicePickerDefaultCoAgentId() {
1139
+ return this.VoicePickerDefaultCoAgentId;
1140
+ }
1141
+ /** @deprecated Use {@link VoicePickerDefaultCoAgentId}. */
1142
+ set voicePickerDefaultCoAgentId(value) {
1143
+ this.VoicePickerDefaultCoAgentId = value;
1144
+ }
629
1145
  /**
630
1146
  * Agents the voice picker offers — the same cached set the @mention
631
- * autocomplete and {@link resolveRealtimeAgentName} use, so the picker can
632
- * never offer an agent the conversation couldn't otherwise route to.
633
- */
1147
+ * autocomplete and {@link resolveRealtimeAgentName} use, narrowed to
1148
+ * {@link AllowedAgentIDs}, so the picker can never offer an agent the
1149
+ * conversation couldn't otherwise route to.
1150
+ */
1151
+ get VoicePickerAgents() {
1152
+ const agents = this.mentionAutocomplete.getAvailableAgents();
1153
+ const allowed = this.AllowedAgentIDs;
1154
+ if (allowed == null) {
1155
+ return agents;
1156
+ }
1157
+ // Memoized on both inputs: the picker binds this getter, and a fresh array on every
1158
+ // change-detection pass would look like a new input each time.
1159
+ const memo = this.voicePickerAgentsMemo;
1160
+ if (memo && memo.Source === agents && memo.Allowed === allowed) {
1161
+ return memo.Result;
1162
+ }
1163
+ const result = agents.filter(agent => IsAgentAllowed(agent.ID, allowed));
1164
+ this.voicePickerAgentsMemo = { Source: agents, Allowed: allowed, Result: result };
1165
+ return result;
1166
+ }
1167
+ voicePickerAgentsMemo = null;
1168
+ /** @deprecated Use {@link VoicePickerAgents}. */
634
1169
  get voicePickerAgents() {
635
- return this.mentionAutocomplete.getAvailableAgents();
1170
+ return this.VoicePickerAgents;
636
1171
  }
637
1172
  /**
638
1173
  * The ACTIVE Realtime-type co-agent candidates — the same run-permission-filtered
639
1174
  * cached set as {@link voicePickerAgents}, narrowed to the Realtime agent type. The
640
1175
  * picker shows its co-agent selector only when more than one exists.
641
1176
  */
642
- get voicePickerCoAgents() {
1177
+ get VoicePickerCoAgents() {
643
1178
  return FilterRealtimeCoAgents(this.mentionAutocomplete.getAvailableAgents());
644
1179
  }
1180
+ /** @deprecated Use {@link VoicePickerCoAgents}. */
1181
+ get voicePickerCoAgents() {
1182
+ return this.VoicePickerCoAgents;
1183
+ }
645
1184
  /** The agent the default resolution would call — preselected in the picker. */
1185
+ get VoicePickerDefaultAgentId() {
1186
+ return this.ResolveCurrentAgentId();
1187
+ }
1188
+ /** @deprecated Use {@link VoicePickerDefaultAgentId}. */
646
1189
  get voicePickerDefaultAgentId() {
647
- return this.resolveCurrentAgentId();
1190
+ return this.VoicePickerDefaultAgentId;
648
1191
  }
649
1192
  /**
650
1193
  * Start a real-time voice session fronting the conversation's current agent.
@@ -660,18 +1203,18 @@ export class MessageInputComponent extends BaseAngularComponent {
660
1203
  * the user never chose — so show a compact agent picker instead and start
661
1204
  * with whichever agent they pick.
662
1205
  */
663
- async onStartRealtime() {
664
- if (!this.canStartRealtime) {
1206
+ async OnStartRealtime() {
1207
+ if (!this.CanStartRealtime) {
665
1208
  return;
666
1209
  }
667
1210
  // New/empty conversation (no prior agent turn): let the user choose who
668
1211
  // to call. Falls through to the immediate path if the agent cache is
669
1212
  // empty (nothing to pick from — the resolved default is the only option).
670
- if (!this.findLastNonSageAgentId() && this.voicePickerAgents.length > 0) {
1213
+ if (!this.findLastNonSageAgentId() && this.VoicePickerAgents.length > 0) {
671
1214
  await this.openRealtimeAgentPicker();
672
1215
  return;
673
1216
  }
674
- const targetAgentId = this.resolveCurrentAgentId();
1217
+ const targetAgentId = this.ResolveCurrentAgentId();
675
1218
  if (!targetAgentId) {
676
1219
  this.toastService.error('No agent available for a voice session.');
677
1220
  return;
@@ -679,6 +1222,10 @@ export class MessageInputComponent extends BaseAngularComponent {
679
1222
  const coAgentId = await this.resolveInstantCoAgentId(targetAgentId);
680
1223
  await this.startRealtimeWithAgent(targetAgentId, this.resolveRealtimeAgentName(), null, coAgentId);
681
1224
  }
1225
+ /** @deprecated Use {@link OnStartRealtime}. */
1226
+ async onStartRealtime() {
1227
+ return this.OnStartRealtime();
1228
+ }
682
1229
  /**
683
1230
  * Caret-next-to-the-phone click: open the agent/co-agent/model picker ON DEMAND, even
684
1231
  * when the conversation already has agent history (where the plain phone click
@@ -687,27 +1234,35 @@ export class MessageInputComponent extends BaseAngularComponent {
687
1234
  * choice one click away. Falls through to the instant path when there is nothing to
688
1235
  * pick from.
689
1236
  */
690
- async onRealtimeOptions() {
691
- if (!this.canStartRealtime) {
1237
+ async OnRealtimeOptions() {
1238
+ if (!this.CanStartRealtime) {
692
1239
  return;
693
1240
  }
694
- if (this.voicePickerAgents.length > 0) {
1241
+ if (this.VoicePickerAgents.length > 0) {
695
1242
  await this.openRealtimeAgentPicker();
696
1243
  return;
697
1244
  }
698
- void this.onStartRealtime();
1245
+ void this.OnStartRealtime();
1246
+ }
1247
+ /** @deprecated Use {@link OnRealtimeOptions}. */
1248
+ async onRealtimeOptions() {
1249
+ return this.OnRealtimeOptions();
699
1250
  }
700
1251
  /** Loads the persisted co-agent preference, then shows the picker (pref preselected). */
701
1252
  async openRealtimeAgentPicker() {
702
- this.voicePickerDefaultCoAgentId = await this.loadPersistedCoAgentId();
703
- this.showRealtimeAgentPicker = true;
1253
+ this.VoicePickerDefaultCoAgentId = await this.loadPersistedCoAgentId();
1254
+ this.ShowRealtimeAgentPicker = true;
704
1255
  }
705
1256
  /** User confirmed an agent (+ optional co-agent / voice model) in the voice picker — start the call. */
706
- async onRealtimeAgentPicked(pick) {
707
- this.showRealtimeAgentPicker = false;
1257
+ async OnRealtimeAgentPicked(pick) {
1258
+ this.ShowRealtimeAgentPicker = false;
708
1259
  this.persistCoAgentChoice(pick.CoAgentId);
709
1260
  await this.startRealtimeWithAgent(pick.Agent.ID, pick.Agent.Name || this.resolveRealtimeAgentName(), pick.PreferredModelId, pick.CoAgentId, BuildRealtimeConfigOverridesJson(pick.PreferredModelId, pick.PreferredVoice), pick.RecordingConsent);
710
1261
  }
1262
+ /** @deprecated Use {@link OnRealtimeAgentPicked}. */
1263
+ async onRealtimeAgentPicked(pick) {
1264
+ return this.OnRealtimeAgentPicked(pick);
1265
+ }
711
1266
  /**
712
1267
  * Reads the persisted co-agent preference from `MJ: User Settings` (via
713
1268
  * `UserInfoEngine`'s cached settings). Defensive: any failure or malformed payload
@@ -716,7 +1271,7 @@ export class MessageInputComponent extends BaseAngularComponent {
716
1271
  async loadPersistedCoAgentId() {
717
1272
  try {
718
1273
  await UserInfoEngine.Instance.Config();
719
- const raw = UserInfoEngine.Instance.GetSetting(MessageInputComponent.CoAgentPrefKey);
1274
+ const raw = UserInfoEngine.Instance.GetSetting(MessageInputComponent.coAgentPrefKey);
720
1275
  if (!raw) {
721
1276
  return null;
722
1277
  }
@@ -730,7 +1285,7 @@ export class MessageInputComponent extends BaseAngularComponent {
730
1285
  /** Persists the user's co-agent choice (including explicit "Auto" = null) cross-device. */
731
1286
  persistCoAgentChoice(coAgentId) {
732
1287
  try {
733
- UserInfoEngine.Instance.SetSettingDebounced(MessageInputComponent.CoAgentPrefKey, JSON.stringify({ coAgentId: coAgentId ?? null }));
1288
+ UserInfoEngine.Instance.SetSettingDebounced(MessageInputComponent.coAgentPrefKey, JSON.stringify({ coAgentId: coAgentId ?? null }));
734
1289
  }
735
1290
  catch (error) {
736
1291
  console.warn('[MessageInput] Failed to persist co-agent preference:', error);
@@ -749,7 +1304,7 @@ export class MessageInputComponent extends BaseAngularComponent {
749
1304
  if (!preferred) {
750
1305
  return null;
751
1306
  }
752
- const isValidCandidate = this.voicePickerCoAgents.some(a => UUIDsEqual(a.ID, preferred));
1307
+ const isValidCandidate = this.VoicePickerCoAgents.some(a => UUIDsEqual(a.ID, preferred));
753
1308
  if (!isValidCandidate) {
754
1309
  return null;
755
1310
  }
@@ -757,8 +1312,12 @@ export class MessageInputComponent extends BaseAngularComponent {
757
1312
  return PairingsAllowTarget(pairings, targetAgentId) ? preferred : null;
758
1313
  }
759
1314
  /** User dismissed the voice picker without starting a call. */
1315
+ OnRealtimeAgentPickerCancelled() {
1316
+ this.ShowRealtimeAgentPicker = false;
1317
+ }
1318
+ /** @deprecated Use {@link OnRealtimeAgentPickerCancelled}. */
760
1319
  onRealtimeAgentPickerCancelled() {
761
- this.showRealtimeAgentPicker = false;
1320
+ return this.OnRealtimeAgentPickerCancelled();
762
1321
  }
763
1322
  /**
764
1323
  * Shared session-start path for both the immediate (existing conversation)
@@ -777,11 +1336,11 @@ export class MessageInputComponent extends BaseAngularComponent {
777
1336
  */
778
1337
  async startRealtimeWithAgent(agentId, agentName, preferredModelId, coAgentId, configOverridesJson, recordingConsent) {
779
1338
  try {
780
- await this.realtimeSession.StartRealtimeSession(agentId, this.conversationId, null, agentName, preferredModelId ?? null, null, coAgentId ?? null, configOverridesJson ?? null, recordingConsent ?? null, null,
1339
+ await this.realtimeSession.StartRealtimeSession(agentId, this.ConversationId, null, agentName, preferredModelId ?? null, null, coAgentId ?? null, configOverridesJson ?? null, recordingConsent ?? null, null,
781
1340
  // App awareness: the app the session runs in + the live app-context snapshot (where the
782
1341
  // user is, what they see, capability manifest) — drives the server-side app cascade + the
783
1342
  // mint-time prompt injection, and seeds the ClientContextChannel's streaming.
784
- this.applicationId, this.appContext);
1343
+ this.ApplicationId, this.AppContext);
785
1344
  }
786
1345
  catch (error) {
787
1346
  console.error('Failed to start voice session:', error);
@@ -794,8 +1353,8 @@ export class MessageInputComponent extends BaseAngularComponent {
794
1353
  focusInput() {
795
1354
  // Use setTimeout to ensure DOM is ready
796
1355
  setTimeout(() => {
797
- if (this.inputBox) {
798
- this.inputBox.focus();
1356
+ if (this.InputBox) {
1357
+ this.InputBox.focus();
799
1358
  }
800
1359
  }, 100);
801
1360
  }
@@ -807,14 +1366,14 @@ export class MessageInputComponent extends BaseAngularComponent {
807
1366
  * 3. User returns to a conversation with in-progress messages
808
1367
  * 4. Parent component explicitly triggers reconnection
809
1368
  */
810
- reconnectInProgressMessages() {
811
- if (!this.inProgressMessageIds || this.inProgressMessageIds.length === 0) {
1369
+ ReconnectInProgressMessages() {
1370
+ if (!this.InProgressMessageIds || this.InProgressMessageIds.length === 0) {
812
1371
  return;
813
1372
  }
814
1373
  // Unregister any previously registered callbacks for this component
815
1374
  this.unregisterAllCallbacks();
816
1375
  // Register new callbacks for each in-progress message
817
- for (const messageId of this.inProgressMessageIds) {
1376
+ for (const messageId of this.InProgressMessageIds) {
818
1377
  // Create callback bound to this message ID
819
1378
  const callback = this.createMessageProgressCallback(messageId);
820
1379
  // Store reference for cleanup
@@ -823,6 +1382,10 @@ export class MessageInputComponent extends BaseAngularComponent {
823
1382
  this.streamingService.registerMessageCallback(messageId, callback);
824
1383
  }
825
1384
  }
1385
+ /** @deprecated Use {@link ReconnectInProgressMessages}. */
1386
+ reconnectInProgressMessages() {
1387
+ return this.ReconnectInProgressMessages();
1388
+ }
826
1389
  /**
827
1390
  * Create a progress callback for a specific message ID.
828
1391
  * This callback will be invoked by the streaming service when progress updates arrive.
@@ -835,7 +1398,7 @@ export class MessageInputComponent extends BaseAngularComponent {
835
1398
  return async (progress) => {
836
1399
  try {
837
1400
  // Get message from cache (single source of truth)
838
- const message = (resolvedMessage ??= await this.dataCache.getConversationDetail(messageId, this.currentUser));
1401
+ const message = (resolvedMessage ??= await this.dataCache.getConversationDetail(messageId, this.CurrentUser));
839
1402
  if (!message) {
840
1403
  console.warn(`[StreamingCallback] Message ${messageId} not found in cache`);
841
1404
  return;
@@ -857,7 +1420,7 @@ export class MessageInputComponent extends BaseAngularComponent {
857
1420
  // bubble with the server-saved final message, so no append/merge is needed here.
858
1421
  if (progress.streaming) {
859
1422
  message.Message = progress.streaming.content;
860
- this.messageSent.emit(message);
1423
+ this.MessageSent.emit(message);
861
1424
  // Keep the tasks dropdown on a stable status line rather than the growing reply text.
862
1425
  this.activeTasks.updateStatusByConversationDetailId(message.ID, 'Responding…');
863
1426
  return;
@@ -875,7 +1438,7 @@ export class MessageInputComponent extends BaseAngularComponent {
875
1438
  // Server now saves progress - client only updates in-memory and emits for UI
876
1439
  // (Prevents race condition where client's late save overwrites server's final Status)
877
1440
  // CRITICAL: Emit update to trigger UI refresh
878
- this.messageSent.emit(message);
1441
+ this.MessageSent.emit(message);
879
1442
  // CRITICAL: Update ActiveTasksService to keep the tasks dropdown in sync
880
1443
  this.activeTasks.updateStatusByConversationDetailId(message.ID, progress.message);
881
1444
  console.log(`[StreamingCallback] Updated message ${messageId}: ${progress.taskName || 'Agent'}`);
@@ -899,25 +1462,37 @@ export class MessageInputComponent extends BaseAngularComponent {
899
1462
  }
900
1463
  this.registeredCallbacks.clear();
901
1464
  }
1465
+ get CanSend() {
1466
+ return !this.Disabled && !this.IsSending && this.MessageText.trim().length > 0;
1467
+ }
1468
+ /** @deprecated Use {@link CanSend}. */
902
1469
  get canSend() {
903
- return !this.disabled && !this.isSending && this.messageText.trim().length > 0;
1470
+ return this.CanSend;
904
1471
  }
905
1472
  /**
906
1473
  * Handle attachments changed from the input box
907
1474
  */
908
- onAttachmentsChanged(attachments) {
1475
+ OnAttachmentsChanged(attachments) {
909
1476
  this.pendingAttachments = attachments;
910
1477
  }
1478
+ /** @deprecated Use {@link OnAttachmentsChanged}. */
1479
+ onAttachmentsChanged(attachments) {
1480
+ return this.OnAttachmentsChanged(attachments);
1481
+ }
911
1482
  /**
912
1483
  * Handle attachment errors from the input box
913
1484
  */
914
- onAttachmentError(error) {
1485
+ OnAttachmentError(error) {
915
1486
  this.toastService.error(error);
916
1487
  }
1488
+ /** @deprecated Use {@link OnAttachmentError}. */
1489
+ onAttachmentError(error) {
1490
+ return this.OnAttachmentError(error);
1491
+ }
917
1492
  /**
918
1493
  * Handle text submitted from the input box
919
1494
  */
920
- async onTextSubmitted(text) {
1495
+ async OnTextSubmitted(text) {
921
1496
  // Check if we have either text or attachments
922
1497
  const hasText = text && text.trim().length > 0;
923
1498
  const hasAttachments = this.pendingAttachments.length > 0;
@@ -925,14 +1500,14 @@ export class MessageInputComponent extends BaseAngularComponent {
925
1500
  return;
926
1501
  }
927
1502
  // In empty state mode, just emit the data and let parent handle conversation creation
928
- if (this.emptyStateMode) {
1503
+ if (this.EmptyStateMode) {
929
1504
  const attachmentsToEmit = [...this.pendingAttachments];
930
1505
  this.pendingAttachments = [];
931
- this.messageText = '';
932
- this.emptyStateSubmit.emit({ text: text?.trim() || '', attachments: attachmentsToEmit });
1506
+ this.MessageText = '';
1507
+ this.EmptyStateSubmit.emit({ text: text?.trim() || '', attachments: attachmentsToEmit });
933
1508
  return;
934
1509
  }
935
- this.isSending = true;
1510
+ this.IsSending = true;
936
1511
  // Store attachments locally since we'll clear them after send
937
1512
  const attachmentsToSave = [...this.pendingAttachments];
938
1513
  try {
@@ -944,12 +1519,12 @@ export class MessageInputComponent extends BaseAngularComponent {
944
1519
  // separately when building AI messages - no need to add tokens to Message field
945
1520
  if (attachmentsToSave.length > 0) {
946
1521
  // Show upload indicator for attachments
947
- this.isUploadingAttachments = true;
948
- this.uploadingMessage = `Uploading ${attachmentsToSave.length} attachment${attachmentsToSave.length > 1 ? 's' : ''}...`;
949
- this.uploadStateChanged.emit({ isUploading: true, message: this.uploadingMessage });
1522
+ this.IsUploadingAttachments = true;
1523
+ this.UploadingMessage = `Uploading ${attachmentsToSave.length} attachment${attachmentsToSave.length > 1 ? 's' : ''}...`;
1524
+ this.UploadStateChanged.emit({ isUploading: true, message: this.UploadingMessage });
950
1525
  let attachmentRejection = null;
951
1526
  try {
952
- await this.attachmentService.saveAttachments(messageDetail.ID, attachmentsToSave, this.currentUser);
1527
+ await this.attachmentService.saveAttachments(messageDetail.ID, attachmentsToSave, this.CurrentUser);
953
1528
  }
954
1529
  catch (attachmentError) {
955
1530
  console.error('Failed to save attachments:', attachmentError);
@@ -958,8 +1533,8 @@ export class MessageInputComponent extends BaseAngularComponent {
958
1533
  : 'Some attachments could not be saved';
959
1534
  }
960
1535
  finally {
961
- this.isUploadingAttachments = false;
962
- this.uploadStateChanged.emit({ isUploading: false, message: '' });
1536
+ this.IsUploadingAttachments = false;
1537
+ this.UploadStateChanged.emit({ isUploading: false, message: '' });
963
1538
  }
964
1539
  // Plan §6: when attachments are rejected, the message itself must
965
1540
  // not go through. Roll back the ConversationDetail and notify the
@@ -974,7 +1549,7 @@ export class MessageInputComponent extends BaseAngularComponent {
974
1549
  catch (rollbackErr) {
975
1550
  console.error('Failed to roll back conversation detail after attachment rejection:', rollbackErr);
976
1551
  }
977
- this.isSending = false;
1552
+ this.IsSending = false;
978
1553
  return;
979
1554
  }
980
1555
  }
@@ -990,9 +1565,13 @@ export class MessageInputComponent extends BaseAngularComponent {
990
1565
  this.handleSendError(error);
991
1566
  }
992
1567
  finally {
993
- this.isSending = false;
1568
+ this.IsSending = false;
994
1569
  }
995
1570
  }
1571
+ /** @deprecated Use {@link OnTextSubmitted}. */
1572
+ async onTextSubmitted(text) {
1573
+ return this.OnTextSubmitted(text);
1574
+ }
996
1575
  /**
997
1576
  * Toggle sticky Plan Mode for THIS conversation (or the pending-new bucket on the
998
1577
  * new-conversation composer). Writes through {@link PlanModePreference} — the
@@ -1001,12 +1580,12 @@ export class MessageInputComponent extends BaseAngularComponent {
1001
1580
  * until the user turns it off or approves a plan.
1002
1581
  */
1003
1582
  TogglePlanMode() {
1004
- PlanModePreference.Set(this.conversationId, !this.PlanModeEnabled);
1583
+ PlanModePreference.Set(this.ConversationId, !this.PlanModeEnabled);
1005
1584
  }
1006
- async onSend() {
1007
- if (!this.canSend)
1585
+ async OnSend() {
1586
+ if (!this.CanSend)
1008
1587
  return;
1009
- this.isSending = true;
1588
+ this.IsSending = true;
1010
1589
  try {
1011
1590
  const messageDetail = await this.createMessageDetail();
1012
1591
  const saved = await messageDetail.Save();
@@ -1021,9 +1600,13 @@ export class MessageInputComponent extends BaseAngularComponent {
1021
1600
  this.handleSendError(error);
1022
1601
  }
1023
1602
  finally {
1024
- this.isSending = false;
1603
+ this.IsSending = false;
1025
1604
  }
1026
1605
  }
1606
+ /** @deprecated Use {@link OnSend}. */
1607
+ async onSend() {
1608
+ return this.OnSend();
1609
+ }
1027
1610
  /**
1028
1611
  * Send a message with custom text WITHOUT modifying the visible messageText input
1029
1612
  * Used for suggested responses and initial messages from empty state.
@@ -1035,7 +1618,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1035
1618
  * `this.pendingAttachments` may not contain the attachment. Pass it in
1036
1619
  * explicitly and we merge + dedupe (by `id`) before saving.
1037
1620
  */
1038
- async sendMessageWithText(text, extraAttachments) {
1621
+ async SendMessageWithText(text, extraAttachments) {
1039
1622
  const merged = (() => {
1040
1623
  if (!extraAttachments || extraAttachments.length === 0) {
1041
1624
  return [...this.pendingAttachments];
@@ -1055,31 +1638,31 @@ export class MessageInputComponent extends BaseAngularComponent {
1055
1638
  if (!hasText && !hasAttachments) {
1056
1639
  return false;
1057
1640
  }
1058
- if (this.isSending) {
1641
+ if (this.IsSending) {
1059
1642
  return false;
1060
1643
  }
1061
- this.isSending = true;
1644
+ this.IsSending = true;
1062
1645
  const attachmentsToSave = merged;
1063
1646
  try {
1064
- const detail = await this.dataCache.createConversationDetail(this.currentUser);
1065
- detail.ConversationID = this.conversationId;
1647
+ const detail = await this.dataCache.createConversationDetail(this.CurrentUser);
1648
+ detail.ConversationID = this.ConversationId;
1066
1649
  detail.Message = text?.trim() || '';
1067
1650
  detail.Role = 'User';
1068
- detail.UserID = this.currentUser.ID; // Set the user who sent the message
1069
- if (this.parentMessageId) {
1070
- detail.ParentID = this.parentMessageId;
1651
+ detail.UserID = this.CurrentUser.ID; // Set the user who sent the message
1652
+ if (this.ParentMessageId) {
1653
+ detail.ParentID = this.ParentMessageId;
1071
1654
  }
1072
1655
  const saved = await detail.Save();
1073
1656
  if (saved) {
1074
1657
  // Save attachments if any were pending
1075
1658
  if (attachmentsToSave.length > 0) {
1076
1659
  // Show upload indicator for attachments
1077
- this.isUploadingAttachments = true;
1078
- this.uploadingMessage = `Uploading ${attachmentsToSave.length} attachment${attachmentsToSave.length > 1 ? 's' : ''}...`;
1079
- this.uploadStateChanged.emit({ isUploading: true, message: this.uploadingMessage });
1660
+ this.IsUploadingAttachments = true;
1661
+ this.UploadingMessage = `Uploading ${attachmentsToSave.length} attachment${attachmentsToSave.length > 1 ? 's' : ''}...`;
1662
+ this.UploadStateChanged.emit({ isUploading: true, message: this.UploadingMessage });
1080
1663
  let attachmentRejection = null;
1081
1664
  try {
1082
- await this.attachmentService.saveAttachments(detail.ID, attachmentsToSave, this.currentUser);
1665
+ await this.attachmentService.saveAttachments(detail.ID, attachmentsToSave, this.CurrentUser);
1083
1666
  }
1084
1667
  catch (attachmentError) {
1085
1668
  console.error('Failed to save attachments:', attachmentError);
@@ -1088,8 +1671,8 @@ export class MessageInputComponent extends BaseAngularComponent {
1088
1671
  : 'Some attachments could not be saved';
1089
1672
  }
1090
1673
  finally {
1091
- this.isUploadingAttachments = false;
1092
- this.uploadStateChanged.emit({ isUploading: false, message: '' });
1674
+ this.IsUploadingAttachments = false;
1675
+ this.UploadStateChanged.emit({ isUploading: false, message: '' });
1093
1676
  }
1094
1677
  // Plan §6: roll back the message when attachments are rejected so
1095
1678
  // the user sees the rejection clearly instead of the agent answering
@@ -1102,7 +1685,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1102
1685
  catch (rollbackErr) {
1103
1686
  console.error('Failed to roll back conversation detail after attachment rejection:', rollbackErr);
1104
1687
  }
1105
- this.isSending = false;
1688
+ this.IsSending = false;
1106
1689
  return false;
1107
1690
  }
1108
1691
  }
@@ -1112,10 +1695,10 @@ export class MessageInputComponent extends BaseAngularComponent {
1112
1695
  // The user-initiated send path (MessageInputBoxComponent.onSendClick)
1113
1696
  // calls mentionEditor.clear() — we bypass that path here, so the chips
1114
1697
  // would otherwise stay on screen after the message goes out.
1115
- this.inputBox?.mentionEditor?.clear();
1116
- this.messageSent.emit(detail);
1698
+ this.InputBox?.mentionEditor?.clear();
1699
+ this.MessageSent.emit(detail);
1117
1700
  const mentionResult = this.parseMentionsFromMessage(detail.Message);
1118
- const isFirstMessage = this.conversationHistory.length === 0;
1701
+ const isFirstMessage = this.ConversationHistory.length === 0;
1119
1702
  await this.routeMessage(detail, mentionResult, isFirstMessage);
1120
1703
  return true;
1121
1704
  }
@@ -1129,20 +1712,24 @@ export class MessageInputComponent extends BaseAngularComponent {
1129
1712
  return false;
1130
1713
  }
1131
1714
  finally {
1132
- this.isSending = false;
1715
+ this.IsSending = false;
1133
1716
  }
1134
1717
  }
1718
+ /** @deprecated Use {@link SendMessageWithText}. */
1719
+ async sendMessageWithText(text, extraAttachments) {
1720
+ return this.SendMessageWithText(text, extraAttachments);
1721
+ }
1135
1722
  /**
1136
1723
  * Creates and configures a new conversation detail message
1137
1724
  */
1138
1725
  async createMessageDetail() {
1139
- const detail = await this.dataCache.createConversationDetail(this.currentUser);
1140
- detail.ConversationID = this.conversationId;
1141
- detail.Message = this.messageText.trim();
1726
+ const detail = await this.dataCache.createConversationDetail(this.CurrentUser);
1727
+ detail.ConversationID = this.ConversationId;
1728
+ detail.Message = this.MessageText.trim();
1142
1729
  detail.Role = 'User';
1143
- detail.UserID = this.currentUser.ID; // Set the user who sent the message
1144
- if (this.parentMessageId) {
1145
- detail.ParentID = this.parentMessageId;
1730
+ detail.UserID = this.CurrentUser.ID; // Set the user who sent the message
1731
+ if (this.ParentMessageId) {
1732
+ detail.ParentID = this.ParentMessageId;
1146
1733
  }
1147
1734
  return detail;
1148
1735
  }
@@ -1150,13 +1737,13 @@ export class MessageInputComponent extends BaseAngularComponent {
1150
1737
  * Creates and configures a new conversation detail message from provided text
1151
1738
  */
1152
1739
  async createMessageDetailFromText(text) {
1153
- const detail = await this.dataCache.createConversationDetail(this.currentUser);
1154
- detail.ConversationID = this.conversationId;
1740
+ const detail = await this.dataCache.createConversationDetail(this.CurrentUser);
1741
+ detail.ConversationID = this.ConversationId;
1155
1742
  detail.Message = text;
1156
1743
  detail.Role = 'User';
1157
- detail.UserID = this.currentUser.ID; // Set the user who sent the message
1158
- if (this.parentMessageId) {
1159
- detail.ParentID = this.parentMessageId;
1744
+ detail.UserID = this.CurrentUser.ID; // Set the user who sent the message
1745
+ if (this.ParentMessageId) {
1746
+ detail.ParentID = this.ParentMessageId;
1160
1747
  }
1161
1748
  return detail;
1162
1749
  }
@@ -1164,10 +1751,10 @@ export class MessageInputComponent extends BaseAngularComponent {
1164
1751
  * Handles successful message send - routes to appropriate agent
1165
1752
  */
1166
1753
  async handleSuccessfulSend(messageDetail) {
1167
- this.messageSent.emit(messageDetail);
1168
- this.messageText = '';
1754
+ this.MessageSent.emit(messageDetail);
1755
+ this.MessageText = '';
1169
1756
  const mentionResult = this.parseMentionsFromMessage(messageDetail.Message);
1170
- const isFirstMessage = this.conversationHistory.length === 0;
1757
+ const isFirstMessage = this.ConversationHistory.length === 0;
1171
1758
  await this.routeMessage(messageDetail, mentionResult, isFirstMessage);
1172
1759
  this.refocusTextarea();
1173
1760
  }
@@ -1179,9 +1766,13 @@ export class MessageInputComponent extends BaseAngularComponent {
1179
1766
  return mentionResult;
1180
1767
  }
1181
1768
  /**
1182
- * Routes the message to the appropriate agent or Sage based on context.
1183
- * Priority: explicit @mention > prior-agent continuity > embedder-supplied
1184
- * default agent > Sage fallback.
1769
+ * Routes a saved message to the agent that answers it — or to none.
1770
+ *
1771
+ * Routing picks the agent ({@link resolveAgentTurnTarget}): a tagged agent, then (under
1772
+ * `AgentReplyMode` `'Always'`) the last agent that answered, the conversation's pinned agent,
1773
+ * the host's default agent, and the conversation manager, each only if the host allows it.
1774
+ * {@link BeforeAgentTurn} is then fired once, before any reply row exists, and the turn runs on
1775
+ * MJ's path or through the host's {@link AgentTurnHandler}.
1185
1776
  */
1186
1777
  async routeMessage(messageDetail, mentionResult, isFirstMessage) {
1187
1778
  // A Plan Mode choice made on the new-conversation composer (no conversation yet) lives in a
@@ -1199,80 +1790,123 @@ export class MessageInputComponent extends BaseAngularComponent {
1199
1790
  this._pendingRequestedSkillIDs = mentionResult.skillMentions?.length
1200
1791
  ? mentionResult.skillMentions.map(m => m.id)
1201
1792
  : this.collectRequestedSkillIDs();
1202
- // Priority 1: Direct @mention
1203
- if (mentionResult.agentMention) {
1204
- await this.handleDirectMention(messageDetail, mentionResult.agentMention, isFirstMessage);
1793
+ // Naming is about the conversation, not the turn, so it doesn't wait on (or depend on) one.
1794
+ // It runs in the background: naming can take minutes to time out.
1795
+ if (isFirstMessage && this.AutoNameConversation) {
1796
+ void this.nameConversation(messageDetail.Message, messageDetail.ConversationID)
1797
+ .catch(error => console.error('Conversation naming failed:', error));
1798
+ }
1799
+ const target = this.resolveAgentTurnTarget(mentionResult);
1800
+ if (!target) {
1801
+ await this.finishWithoutAgentTurn(messageDetail, 'NoAgent');
1205
1802
  return;
1206
1803
  }
1207
- // Priority 2: Check for previous agent with intent check
1208
- const lastAgentId = this.findLastNonSageAgentId();
1209
- if (lastAgentId) {
1210
- await this.handleAgentContinuity(messageDetail, lastAgentId, mentionResult, isFirstMessage);
1804
+ const turn = this.announceAgentTurn(messageDetail, target);
1805
+ if (!turn) {
1806
+ await this.finishWithoutAgentTurn(messageDetail, 'Declined');
1211
1807
  return;
1212
1808
  }
1213
- // Priority 3: User's per-conversation pinned default agent — sourced
1214
- // from MJConversationEntity.DefaultAgentID. Wins over the embedder's
1215
- // default because it represents an explicit user choice on this
1216
- // conversation (e.g. "always route to Research Agent for this thread").
1217
- if (this.conversationDefaultAgentId) {
1218
- await this.handleAgentContinuity(messageDetail, this.conversationDefaultAgentId, mentionResult, isFirstMessage);
1219
- return;
1809
+ await this.runAgentTurn(messageDetail, mentionResult, turn);
1810
+ }
1811
+ /** The host's rules, as the routing functions take them. */
1812
+ get agentTurnRules() {
1813
+ return { ReplyMode: this.AgentReplyMode, AllowedAgentIDs: this.AllowedAgentIDs };
1814
+ }
1815
+ /** The agent each route would use for this conversation, before the host's rules apply. */
1816
+ agentTurnCandidates(mentionedAgentIds) {
1817
+ return {
1818
+ MentionedAgentIds: mentionedAgentIds,
1819
+ ContinuityAgentId: this.findLastNonSageAgentId(),
1820
+ ConversationDefaultAgentId: this.ConversationDefaultAgentId,
1821
+ HostDefaultAgentId: this.DefaultAgentId,
1822
+ ConversationManagerAgentId: this.ConverationManagerAgent?.ID ?? null
1823
+ };
1824
+ }
1825
+ /** Picks the agent that answers this message and the route that chose it, or null for no turn. */
1826
+ resolveAgentTurnTarget(mentionResult) {
1827
+ const mentionedAgentIds = mentionResult.mentions.filter(m => m.type === 'agent').map(m => m.id);
1828
+ return ResolveAgentTurn(this.agentTurnCandidates(mentionedAgentIds), this.agentTurnRules, agentId => this.isKnownAgent(agentId));
1829
+ }
1830
+ /**
1831
+ * Fires {@link BeforeAgentTurn} for the turn routing picked — before any reply row exists —
1832
+ * and applies what its listeners decided. Returns the turn to run, or null when a listener
1833
+ * canceled it or redirected it to an agent this chat can't use.
1834
+ */
1835
+ announceAgentTurn(userMessage, target) {
1836
+ const args = new BeforeAgentTurnEventArgs(userMessage.ConversationID, userMessage.Message ?? '', this.ApplicationId, { ...target, AgentName: this.agentNameFor(target.AgentId), UserMessageId: userMessage.ID });
1837
+ this.BeforeAgentTurn.emit(args);
1838
+ if (args.Cancel) {
1839
+ LogStatusEx({ message: `Agent turn canceled by a BeforeAgentTurn listener${args.CancelReason ? `: ${args.CancelReason}` : ''}` });
1840
+ return null;
1841
+ }
1842
+ const redirectAgentId = args.RedirectAgentId;
1843
+ if (!redirectAgentId || UUIDsEqual(redirectAgentId, target.AgentId)) {
1844
+ return target;
1845
+ }
1846
+ if (!IsAgentAllowed(redirectAgentId, this.AllowedAgentIDs) || !this.isKnownAgent(redirectAgentId)) {
1847
+ this.notifyAgentTurnProblem('That agent isn\'t available in this chat, so it didn\'t answer.', 'warning');
1848
+ return null;
1849
+ }
1850
+ return { AgentId: redirectAgentId, Route: 'Redirect' };
1851
+ }
1852
+ /**
1853
+ * Settles a message that starts no agent turn — `MentionOnly` with no tagged agent, no allowed
1854
+ * agent left, or a turn declined in {@link BeforeAgentTurn}. Nothing new is written; the
1855
+ * person's own row is only confirmed Complete.
1856
+ */
1857
+ async finishWithoutAgentTurn(userMessage, reason) {
1858
+ // Under the default rules a turn always has somewhere to go, so reaching here with no agent
1859
+ // means MJ's conversation manager failed to load. Say so rather than sit silent.
1860
+ if (reason === 'NoAgent' && this.AgentReplyMode === 'Always' && this.AllowedAgentIDs == null) {
1861
+ this.notifyAgentTurnProblem('No agent is available to answer this message.', 'error');
1220
1862
  }
1221
- // Priority 4: Embedder-supplied default agent. Set by chat surfaces
1222
- // that have a specialist agent for the context (e.g. Form Builder
1223
- // cockpit). Only kicks in when nothing more explicit is present —
1224
- // @mention always wins, conversation continuity always wins. The
1225
- // intent is to skip Sage's default delegation when the embedder
1226
- // already knows what agent owns this conversation.
1227
- if (this.defaultAgentId) {
1228
- await this.handleAgentContinuity(messageDetail, this.defaultAgentId, mentionResult, isFirstMessage);
1863
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1864
+ }
1865
+ /** Runs a turn that {@link BeforeAgentTurn} let through, on the host's handler or MJ's path. */
1866
+ async runAgentTurn(userMessage, mentionResult, turn) {
1867
+ const mention = turn.Route === 'Mention' ? this.findAgentMention(mentionResult, turn.AgentId) : null;
1868
+ if (this.AgentTurnHandler) {
1869
+ await this.runHostAgentTurn(this.AgentTurnHandler, userMessage, turn, mention);
1229
1870
  return;
1230
1871
  }
1231
- // Priority 5: Check if Sage was explicitly @mentioned with a config preset
1232
- // If so, treat it like agent continuity so the config preset is preserved
1233
- if (this.converationManagerAgent?.ID) {
1234
- const sageConfigPreset = this.conversationId
1235
- ? await this.agentService.FindConfigurationPresetForAgent(this.conversationId, this.converationManagerAgent.ID)
1236
- : undefined;
1237
- if (sageConfigPreset) {
1238
- // User explicitly @mentioned Sage with a config - use the shared execution helper directly
1239
- // Pass the already-found config preset to avoid redundant history search
1240
- await this.executeRouteWithNaming(() => this.executeAgentContinuation(messageDetail, this.converationManagerAgent.ID, this.converationManagerAgent.Name || 'Sage', messageDetail.ConversationID, null, // Sage doesn't use payload continuity
1241
- null, // Sage doesn't use artifact info
1242
- sageConfigPreset // Pass the already-found config preset
1243
- ), messageDetail.Message, isFirstMessage, messageDetail.ConversationID);
1244
- return;
1245
- }
1872
+ if (mention) {
1873
+ await this.invokeAgentDirectly(userMessage, mention, userMessage.ConversationID);
1874
+ }
1875
+ else if (turn.Route === 'ConversationManager' || (turn.Route === 'Redirect' && this.isConversationManager(turn.AgentId))) {
1876
+ await this.runConversationManagerTurn(userMessage, mentionResult);
1877
+ }
1878
+ else {
1879
+ // Pinned and host defaults keep their direct call even when they name the manager, as before.
1880
+ await this.handleAgentContinuity(userMessage, turn.AgentId);
1246
1881
  }
1247
- // Priority 6: No context - use Sage with default config
1248
- await this.handleNoAgentContext(messageDetail, mentionResult, isFirstMessage);
1249
1882
  }
1250
1883
  /**
1251
- * Handles routing when user directly mentions an agent with @
1884
+ * The tagged agent's mention, with its configuration preset. A mention saved without one
1885
+ * (legacy text format) takes the preset from its chip in the composer, when there is one.
1252
1886
  */
1253
- async handleDirectMention(messageDetail, agentMention, isFirstMessage) {
1254
- // The agentMention already has configurationId from JSON parsing
1255
- // If it wasn't in JSON (legacy format), try to get from chip data
1256
- if (!agentMention.configurationId) {
1257
- const chipData = this.inputBox?.getMentionChipsData() || [];
1258
- const agentChip = chipData.find(chip => chip.id === agentMention.id && chip.type === 'agent');
1887
+ findAgentMention(mentionResult, agentId) {
1888
+ const mention = mentionResult.mentions.find(m => m.type === 'agent' && UUIDsEqual(m.id, agentId));
1889
+ if (!mention) {
1890
+ return null;
1891
+ }
1892
+ if (!mention.configurationId) {
1893
+ const chipData = this.InputBox?.getMentionChipsData() || [];
1894
+ const agentChip = chipData.find(chip => chip.id === mention.id && chip.type === 'agent');
1259
1895
  if (agentChip?.presetId) {
1260
- agentMention.configurationId = agentChip.presetId;
1896
+ mention.configurationId = agentChip.presetId;
1261
1897
  }
1262
1898
  }
1263
- if (agentMention.configurationId) {
1264
- //console.log(`🎯 Agent mention has configuration ID: ${agentMention.configurationId}`);
1265
- }
1266
- await this.executeRouteWithNaming(() => this.invokeAgentDirectly(messageDetail, agentMention, messageDetail.ConversationID), messageDetail.Message, isFirstMessage, messageDetail.ConversationID);
1899
+ return mention;
1267
1900
  }
1268
1901
  /**
1269
- * Handles routing when there's a previous non-Sage agent in the conversation.
1902
+ * Continues with an agent chosen without a mention: the last agent that answered, the pinned
1903
+ * or host default agent, or a redirect.
1270
1904
  *
1271
1905
  * LATENCY OPTIMIZATION (PR #2309 / plans/agent-latency-optimization.md — Opt #1):
1272
- * Previously, this method made a separate LLM call via checkContinuityIntent() to decide
1273
- * whether the user's new message was still directed at the previous agent or should be
1274
- * routed to Sage. That call added ~300ms of latency on every message in a conversation
1275
- * with an active agent — the single largest source of non-inference overhead on the client.
1906
+ * This used to make a separate LLM call via checkContinuityIntent() to decide whether the
1907
+ * user's new message was still directed at the previous agent or should be routed to Sage.
1908
+ * That call added ~300ms of latency on every message in a conversation with an active agent —
1909
+ * the single largest source of non-inference overhead on the client.
1276
1910
  *
1277
1911
  * The heuristic replacement is simple: if a previous non-Sage agent exists, always continue
1278
1912
  * with it. The user can @mention a different agent (or Sage) to explicitly switch. This is
@@ -1284,49 +1918,149 @@ export class MessageInputComponent extends BaseAngularComponent {
1284
1918
  * when browser-local inference is fast enough (~20-50ms) to do this without blocking the
1285
1919
  * user. See PR #2309 for the full discussion.
1286
1920
  */
1287
- async handleAgentContinuity(messageDetail, lastAgentId, mentionResult, isFirstMessage) {
1288
- // COMMENTED OUT — LLM intent check removed for latency optimization (see JSDoc above).
1289
- // const intentResult = await this.checkContinuityIntent(lastAgentId, messageDetail.Message);
1290
- //
1291
- // if (intentResult.decision === 'YES') {
1292
- // await this.executeRouteWithNaming(
1293
- // () => this.continueWithAgent(
1294
- // messageDetail,
1295
- // lastAgentId,
1296
- // this.conversationId,
1297
- // intentResult.targetArtifactVersionId
1298
- // ),
1299
- // messageDetail.Message,
1300
- // isFirstMessage
1301
- // );
1302
- // } else {
1303
- // await this.executeRouteWithNaming(
1304
- // () => this.processMessageThroughAgent(messageDetail, mentionResult),
1305
- // messageDetail.Message,
1306
- // isFirstMessage
1307
- // );
1308
- // }
1309
- // Always continue with the previous agent — user can @mention another agent to switch.
1310
- await this.executeRouteWithNaming(() => this.continueWithAgent(messageDetail, lastAgentId, messageDetail.ConversationID, undefined // artifact version targeting unavailable without intent check
1311
- ), messageDetail.Message, isFirstMessage, messageDetail.ConversationID);
1312
- }
1313
- /**
1314
- * Handles routing when there's no previous agent context
1315
- */
1316
- async handleNoAgentContext(messageDetail, mentionResult, isFirstMessage) {
1317
- await this.executeRouteWithNaming(() => this.processMessageThroughAgent(messageDetail, mentionResult), messageDetail.Message, isFirstMessage, messageDetail.ConversationID);
1318
- }
1319
- /**
1320
- * Finds the last agent ID that isn't Sage
1921
+ async handleAgentContinuity(messageDetail, agentId) {
1922
+ await this.continueWithAgent(messageDetail, agentId, messageDetail.ConversationID, undefined // artifact version targeting unavailable without intent check
1923
+ );
1924
+ }
1925
+ /**
1926
+ * The conversation manager's turn. When an earlier message @mentioned the manager with a
1927
+ * configuration preset, the preset keeps applying and the manager answers directly;
1928
+ * otherwise it evaluates the message and may delegate.
1929
+ */
1930
+ async runConversationManagerTurn(messageDetail, mentionResult) {
1931
+ const manager = this.ConverationManagerAgent;
1932
+ const managerPreset = manager?.ID && messageDetail.ConversationID
1933
+ ? await this.agentService.FindConfigurationPresetForAgent(messageDetail.ConversationID, manager.ID)
1934
+ : undefined;
1935
+ if (manager?.ID && managerPreset) {
1936
+ await this.executeAgentContinuation(messageDetail, manager.ID, manager.Name || 'Sage', messageDetail.ConversationID, null, // Sage doesn't use payload continuity
1937
+ null, // Sage doesn't use artifact info
1938
+ managerPreset // Pass the already-found config preset
1939
+ );
1940
+ return;
1941
+ }
1942
+ await this.processMessageThroughAgent(messageDetail, mentionResult);
1943
+ }
1944
+ /**
1945
+ * The newest reply from an agent other than the conversation manager — among the replies a
1946
+ * turn may read, so from {@link AgentHistoryFrom} onward when it's set.
1321
1947
  */
1322
- findLastNonSageAgentId() {
1323
- const lastAIMessage = this.conversationHistory
1948
+ findLastNonSageReply() {
1949
+ return this.ConversationHistory
1324
1950
  .slice()
1325
1951
  .reverse()
1326
1952
  .find(msg => msg.Role === 'AI' &&
1327
- msg.AgentID &&
1328
- !UUIDsEqual(msg.AgentID, this.converationManagerAgent?.ID));
1329
- return lastAIMessage?.AgentID || null;
1953
+ !!msg.AgentID &&
1954
+ !UUIDsEqual(msg.AgentID, this.ConverationManagerAgent?.ID) &&
1955
+ this.isWithinHistoryFloor(msg)) ?? null;
1956
+ }
1957
+ /**
1958
+ * Finds the last agent ID that isn't Sage (see {@link findLastNonSageReply}).
1959
+ */
1960
+ findLastNonSageAgentId() {
1961
+ return this.findLastNonSageReply()?.AgentID || null;
1962
+ }
1963
+ /** True when a row was written at or after {@link AgentHistoryFrom}; always true without one. */
1964
+ isWithinHistoryFloor(detail) {
1965
+ const floor = this.AgentHistoryFrom;
1966
+ if (!floor) {
1967
+ return true;
1968
+ }
1969
+ const createdAt = detail.__mj_CreatedAt;
1970
+ return createdAt != null && new Date(createdAt).getTime() >= floor.getTime();
1971
+ }
1972
+ /** True when the agent is in the client's agent catalog. */
1973
+ isKnownAgent(agentId) {
1974
+ return AIEngineBase.Instance.Agents.some(a => UUIDsEqual(a.ID, agentId));
1975
+ }
1976
+ /** The agent's name, when the client's agent catalog has it. */
1977
+ agentNameFor(agentId) {
1978
+ return AIEngineBase.Instance.Agents.find(a => UUIDsEqual(a.ID, agentId))?.Name ?? null;
1979
+ }
1980
+ /** The ID of the agent with this name, or null when there is none. */
1981
+ agentIdByName(agentName) {
1982
+ return AIEngineBase.Instance.Agents.find(a => a.Name === agentName)?.ID ?? null;
1983
+ }
1984
+ /** True when the agent is MJ's conversation manager. */
1985
+ isConversationManager(agentId) {
1986
+ return !!this.ConverationManagerAgent?.ID && UUIDsEqual(agentId, this.ConverationManagerAgent.ID);
1987
+ }
1988
+ /** Shows the person why an agent turn didn't happen, or didn't finish. */
1989
+ notifyAgentTurnProblem(message, style) {
1990
+ MJNotificationService.Instance?.CreateSimpleNotification(message, style, 5000);
1991
+ }
1992
+ /**
1993
+ * Refuses agents the host doesn't allow — named by the conversation manager's delegation or
1994
+ * by a workflow it planned — and settles the person's message.
1995
+ */
1996
+ async refuseDisallowedAgents(userMessage, agentNames) {
1997
+ const subject = agentNames.join(', ');
1998
+ this.notifyAgentTurnProblem(agentNames.length === 1
1999
+ ? `${subject} isn't available in this chat, so it didn't run.`
2000
+ : `${subject} aren't available in this chat, so they didn't run.`, 'warning');
2001
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
2002
+ }
2003
+ /**
2004
+ * Runs the turn through the host's {@link AgentTurnHandler} instead of MJ's path. Nothing is
2005
+ * written here: the host writes the reply rows and reports them, and each is shown like a row
2006
+ * MJ wrote — one still In-Progress is followed like any other in-progress reply.
2007
+ */
2008
+ async runHostAgentTurn(handler, userMessage, turn, mention) {
2009
+ const result = await this.callAgentTurnHandler(handler, this.buildAgentTurnRequest(userMessage, turn, mention));
2010
+ if (!result.Success) {
2011
+ this.notifyAgentTurnProblem(result.ErrorMessage || 'The agent could not answer this message.', 'error');
2012
+ }
2013
+ else {
2014
+ await this.showHostTurnRows(result.ReplyDetailIds ?? []);
2015
+ }
2016
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
2017
+ if (result.Success && result.Result) {
2018
+ this.emitAfterAgentTurn(userMessage.ConversationID, result.Result, result.AgentRunId);
2019
+ }
2020
+ }
2021
+ /** Calls the host's handler; a throw is reported as a failed turn, never swallowed. */
2022
+ async callAgentTurnHandler(handler, request) {
2023
+ try {
2024
+ return await handler(request);
2025
+ }
2026
+ catch (error) {
2027
+ console.error('AgentTurnHandler failed:', error);
2028
+ return { Success: false, ErrorMessage: error instanceof Error ? error.message : String(error) };
2029
+ }
2030
+ }
2031
+ /** What the host's handler is told about the turn. */
2032
+ buildAgentTurnRequest(userMessage, turn, mention) {
2033
+ return {
2034
+ ConversationId: userMessage.ConversationID,
2035
+ UserMessageId: userMessage.ID,
2036
+ MessageText: userMessage.Message ?? '',
2037
+ AgentId: turn.AgentId,
2038
+ AgentName: this.agentNameFor(turn.AgentId),
2039
+ Route: turn.Route,
2040
+ ApplicationId: this.ApplicationId,
2041
+ AppContext: this.AppContext,
2042
+ AgentHistoryFrom: this.AgentHistoryFrom,
2043
+ // A mention carries its own preset (or none); every other route follows the header picker.
2044
+ ConfigurationPresetId: turn.Route === 'Mention' ? (mention?.configurationId ?? null) : this.AgentConfigurationPresetId,
2045
+ RequestedSkillIDs: [...this._pendingRequestedSkillIDs],
2046
+ PlanMode: this.PlanModeEnabled
2047
+ };
2048
+ }
2049
+ /** Shows the rows a host's handler reported for its turn, oldest first. */
2050
+ async showHostTurnRows(detailIds) {
2051
+ for (const detailId of detailIds) {
2052
+ const detail = await this.dataCache.getConversationDetail(detailId, this.CurrentUser);
2053
+ if (detail) {
2054
+ this.MessageSent.emit(detail);
2055
+ }
2056
+ else {
2057
+ console.warn(`AgentTurnHandler reported conversation detail ${detailId}, but it could not be loaded`);
2058
+ }
2059
+ }
2060
+ }
2061
+ /** Fires {@link AfterAgentTurn} for a turn that succeeded. */
2062
+ emitAfterAgentTurn(conversationId, result, agentRunId) {
2063
+ this.AfterAgentTurn.emit(new AfterAgentTurnEventArgs(conversationId, agentRunId ?? result.agentRun?.ID ?? '', result));
1330
2064
  }
1331
2065
  /**
1332
2066
  * Checks if message should continue with the previous agent
@@ -1344,17 +2078,17 @@ export class MessageInputComponent extends BaseAngularComponent {
1344
2078
  };
1345
2079
  }
1346
2080
  // Emit event to show temporary "Analyzing intent..." message in conversation
1347
- this.intentCheckStarted.emit({ conversationId: this.conversationId });
2081
+ this.IntentCheckStarted.emit({ conversationId: this.ConversationId });
1348
2082
  try {
1349
2083
  // The pre-loaded artifact/agent-run maps are no longer passed: they are scoped to the
1350
2084
  // loaded transcript window, and the service now queries for this agent's artifacts so
1351
2085
  // the classifier reasons over the whole conversation. A conversation id is what it
1352
2086
  // needs instead, and without one there is nothing to query.
1353
- if (!this.conversationId) {
2087
+ if (!this.ConversationId) {
1354
2088
  console.warn('⚠️ No conversation id available for intent check');
1355
2089
  return { decision: 'UNSURE', reasoning: 'Conversation not available' };
1356
2090
  }
1357
- const intent = await this.agentService.checkAgentContinuityIntent(this.conversationId, agentId, message, this.conversationHistory);
2091
+ const intent = await this.agentService.checkAgentContinuityIntent(this.ConversationId, agentId, message, this.ConversationHistory);
1358
2092
  return intent;
1359
2093
  }
1360
2094
  catch (error) {
@@ -1363,25 +2097,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1363
2097
  }
1364
2098
  finally {
1365
2099
  // Emit event to remove temporary intent checking message
1366
- this.intentCheckCompleted.emit({ conversationId: this.conversationId });
1367
- }
1368
- }
1369
- /**
1370
- * Executes a routing function, optionally with conversation naming for first message
1371
- *
1372
- * IMPORTANT: Conversation naming runs asynchronously in the background and does NOT
1373
- * block the agent invocation. This prevents UI blocking if naming times out.
1374
- */
1375
- async executeRouteWithNaming(routeFunction, userMessage, isFirstMessage, conversationId) {
1376
- if (isFirstMessage) {
1377
- // Fire conversation naming in background (don't await)
1378
- // This prevents 2+ minute UI blocking if naming times out
1379
- this.nameConversation(userMessage, conversationId);
1380
- // Execute route immediately (don't wait for naming)
1381
- await routeFunction();
1382
- }
1383
- else {
1384
- await routeFunction();
2100
+ this.IntentCheckCompleted.emit({ conversationId: this.ConversationId });
1385
2101
  }
1386
2102
  }
1387
2103
  /**
@@ -1389,8 +2105,8 @@ export class MessageInputComponent extends BaseAngularComponent {
1389
2105
  */
1390
2106
  refocusTextarea() {
1391
2107
  setTimeout(() => {
1392
- if (this.inputBox) {
1393
- this.inputBox.focus();
2108
+ if (this.InputBox) {
2109
+ this.InputBox.focus();
1394
2110
  }
1395
2111
  }, 100);
1396
2112
  }
@@ -1450,7 +2166,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1450
2166
  // CRITICAL FIX: Emit FULL agent run object for incremental updates
1451
2167
  // This contains live timestamps, status, and other fields that change during execution
1452
2168
  if (progressAgentRun || progressAgentRunId) {
1453
- this.agentRunUpdate.emit({
2169
+ this.AgentRunUpdate.emit({
1454
2170
  conversationId: conversationDetail.ConversationID,
1455
2171
  conversationDetailId: conversationDetail.ID,
1456
2172
  agentRun: progressAgentRun,
@@ -1460,7 +2176,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1460
2176
  else if (progressAgentRunId && !capturedAgentRunId) {
1461
2177
  // Fallback: If we don't have the full object but have the ID, emit agentRunDetected
1462
2178
  // This will trigger a database query to load the agent run
1463
- this.agentRunDetected.emit({
2179
+ this.AgentRunDetected.emit({
1464
2180
  conversationId: conversationDetail.ConversationID,
1465
2181
  conversationDetailId: conversationDetail.ID,
1466
2182
  agentRunId: progressAgentRunId
@@ -1470,7 +2186,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1470
2186
  conversationDetail.Message = progressText;
1471
2187
  // Server now saves progress - client only updates in-memory and emits for UI
1472
2188
  // (Prevents race condition where client's late save overwrites server's final Status)
1473
- this.messageSent.emit(conversationDetail);
2189
+ this.MessageSent.emit(conversationDetail);
1474
2190
  }
1475
2191
  }
1476
2192
  }
@@ -1481,7 +2197,8 @@ export class MessageInputComponent extends BaseAngularComponent {
1481
2197
  }
1482
2198
  /**
1483
2199
  * Process the message through agents (multi-stage: Sage -> possible sub-agent)
1484
- * Only called when there's no @mention and no implicit agent context
2200
+ * Only called when there's no @mention and no implicit agent context.
2201
+ * {@link BeforeAgentTurn} has already fired — routing announces the turn before any row exists.
1485
2202
  */
1486
2203
  async processMessageThroughAgent(userMessage, mentionResult) {
1487
2204
  let taskId = null;
@@ -1491,7 +2208,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1491
2208
  const conversationId = userMessage.ConversationID;
1492
2209
  try {
1493
2210
  // Create AI message for Sage BEFORE invoking
1494
- conversationManagerMessage = await this.dataCache.createConversationDetail(this.currentUser);
2211
+ conversationManagerMessage = await this.dataCache.createConversationDetail(this.CurrentUser);
1495
2212
  conversationManagerMessage.ConversationID = conversationId;
1496
2213
  conversationManagerMessage.Role = 'AI';
1497
2214
  conversationManagerMessage.Message = '⏳ Starting...';
@@ -1499,11 +2216,11 @@ export class MessageInputComponent extends BaseAngularComponent {
1499
2216
  conversationManagerMessage.Status = 'In-Progress';
1500
2217
  conversationManagerMessage.HiddenToUser = false;
1501
2218
  // Use the preloaded Sage agent instead of looking it up
1502
- if (this.converationManagerAgent?.ID) {
1503
- conversationManagerMessage.AgentID = this.converationManagerAgent.ID;
2219
+ if (this.ConverationManagerAgent?.ID) {
2220
+ conversationManagerMessage.AgentID = this.ConverationManagerAgent.ID;
1504
2221
  }
1505
2222
  await conversationManagerMessage.Save();
1506
- this.messageSent.emit(conversationManagerMessage);
2223
+ this.MessageSent.emit(conversationManagerMessage);
1507
2224
  // Use Sage to evaluate and route
1508
2225
  // Stage 1: Sage evaluates the message
1509
2226
  taskId = this.activeTasks.add({
@@ -1512,39 +2229,22 @@ export class MessageInputComponent extends BaseAngularComponent {
1512
2229
  relatedMessageId: userMessage.ID,
1513
2230
  conversationDetailId: conversationManagerMessage.ID,
1514
2231
  conversationId,
1515
- conversationName: this.conversationName
2232
+ conversationName: this.ConversationName
1516
2233
  });
1517
- // ── PR 2c follow-up: Before/After cancelable event wiring ──
1518
- // Emit beforeAgentTurn so consumers can veto the turn (rate-limit, guardrail,
1519
- // confirm-dialog, etc.). Cancel propagates synchronously through the chat-area
1520
- // re-emit binding, so by the time .emit() returns, event.Cancel reflects every
1521
- // subscriber's final answer.
1522
- const beforeEvent = new BeforeAgentTurnEventArgs(conversationId, userMessage.Message ?? '', this.applicationId);
1523
- this.beforeAgentTurn.emit(beforeEvent);
1524
- if (beforeEvent.Cancel) {
1525
- // Mark the conversation-manager message as canceled + clear its task so the
1526
- // UI doesn't show a forever-pending spinner. afterAgentTurn is NOT emitted.
1527
- await this.updateConversationDetail(conversationManagerMessage, beforeEvent.CancelReason ?? '⛔ Turn canceled before agent invocation', 'Error');
1528
- await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1529
- this.cleanupCompletionTimestamp(conversationManagerMessage.ID);
1530
- if (taskId) {
1531
- this.activeTasks.remove(taskId);
1532
- taskId = null;
1533
- }
1534
- return;
1535
- }
1536
- const result = await this.agentService.processMessage(conversationId, userMessage, this.conversationHistory, conversationManagerMessage.ID, this.createProgressCallback(conversationManagerMessage, 'Sage'), this.appContext, this.PlanModeEnabled, // per-request Plan Mode toggle
1537
- this._pendingRequestedSkillIDs);
2234
+ const result = await this.agentService.ProcessMessage(conversationId, userMessage, this.ConversationHistory, conversationManagerMessage.ID, this.createProgressCallback(conversationManagerMessage, 'Sage'), this.AppContext, this.PlanModeEnabled, // per-request Plan Mode toggle
2235
+ this._pendingRequestedSkillIDs, // user-requested skills (/skill mentions)
2236
+ this.AllowedAgentIDs, // the agents the manager may delegate to
2237
+ this.AgentHistoryFrom);
1538
2238
  // Emit afterAgentTurn on the happy path only — the error/failure branch
1539
2239
  // immediately below handles its own cleanup and skips this emit.
1540
2240
  if (result && result.success) {
1541
- this.afterAgentTurn.emit(new AfterAgentTurnEventArgs(conversationId, (result.agentRun?.ID ?? ''), result));
2241
+ this.emitAfterAgentTurn(conversationId, result);
1542
2242
  }
1543
2243
  // Task will be removed automatically in markMessageComplete()
1544
2244
  // DO NOT remove here - agent may still be streaming/processing
1545
2245
  taskId = null; // Clear reference but don't remove from service
1546
2246
  if (!result || !result.success) {
1547
- await this.applyAgentFailureToDetail(conversationManagerMessage, userMessage, this.converationManagerAgent?.Name || 'Sage', result, 'failed');
2247
+ await this.applyAgentFailureToDetail(conversationManagerMessage, userMessage, this.ConverationManagerAgent?.Name || 'Sage', result, 'failed');
1548
2248
  console.warn('⚠️ Sage failed:', agentFailureMessage(result, 'Agent evaluation failed'));
1549
2249
  this.cleanupCompletionTimestamp(conversationManagerMessage.ID);
1550
2250
  return;
@@ -1576,7 +2276,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1576
2276
  // Server already created artifacts - just emit event to trigger UI reload
1577
2277
  if (result.payload && Object.keys(result.payload).length > 0) {
1578
2278
  this.emitArtifactReload(conversationManagerMessage);
1579
- this.messageSent.emit(conversationManagerMessage);
2279
+ this.MessageSent.emit(conversationManagerMessage);
1580
2280
  }
1581
2281
  await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1582
2282
  // Remove CM from active tasks
@@ -1595,7 +2295,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1595
2295
  conversationManagerMessage.HiddenToUser = false;
1596
2296
  // use update helper to ensure that if there is a race condition with more streaming updates we don't allow that to override this final message
1597
2297
  await this.updateConversationDetail(conversationManagerMessage, result.agentRun.Message, 'Complete', result);
1598
- this.messageSent.emit(conversationManagerMessage);
2298
+ this.MessageSent.emit(conversationManagerMessage);
1599
2299
  // Clean up completion timestamp after delay
1600
2300
  this.cleanupCompletionTimestamp(conversationManagerMessage.ID);
1601
2301
  }
@@ -1606,7 +2306,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1606
2306
  conversationManagerMessage.HiddenToUser = true;
1607
2307
  // use update helper to ensure that if there is a race condition with more streaming updates we don't allow that to override this final message
1608
2308
  await this.updateConversationDetail(conversationManagerMessage, conversationManagerMessage.Message, 'Complete', result);
1609
- this.messageSent.emit(conversationManagerMessage);
2309
+ this.MessageSent.emit(conversationManagerMessage);
1610
2310
  await this.handleSilentObservation(userMessage, conversationId);
1611
2311
  // Clean up completion timestamp after delay
1612
2312
  this.cleanupCompletionTimestamp(conversationManagerMessage.ID);
@@ -1666,11 +2366,18 @@ export class MessageInputComponent extends BaseAngularComponent {
1666
2366
  const taskGraph = managerResult.payload?.taskGraph;
1667
2367
  if (!taskGraph)
1668
2368
  return;
2369
+ // The host's allowed list holds for every agent step of the plan, not only for a single
2370
+ // delegation: a workflow naming an agent the chat doesn't allow is not submitted at all.
2371
+ const disallowedAgents = FindDisallowedTaskGraphAgents(Array.isArray(taskGraph.tasks) ? taskGraph.tasks : [], this.AllowedAgentIDs, agentName => this.agentIdByName(agentName));
2372
+ if (disallowedAgents.length > 0) {
2373
+ await this.refuseDisallowedAgents(userMessage, disallowedAgents);
2374
+ return;
2375
+ }
1669
2376
  const workflowName = taskGraph.workflowName || 'Workflow';
1670
2377
  const reasoning = taskGraph.reasoning || 'Executing multi-step workflow';
1671
2378
  const taskCount = Array.isArray(taskGraph.tasks) ? taskGraph.tasks.length : 0;
1672
2379
  // A message the user can watch. Progress frames from the dispatcher land against this ID.
1673
- const taskExecutionMessage = await this.dataCache.createConversationDetail(this.currentUser);
2380
+ const taskExecutionMessage = await this.dataCache.createConversationDetail(this.CurrentUser);
1674
2381
  taskExecutionMessage.ConversationID = conversationId;
1675
2382
  taskExecutionMessage.Role = 'AI';
1676
2383
  taskExecutionMessage.Message = `⏳ **${workflowName}**\n\n${reasoning}\n\nSubmitting ${taskCount} task(s)…`;
@@ -1678,7 +2385,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1678
2385
  taskExecutionMessage.Status = 'In-Progress';
1679
2386
  taskExecutionMessage.HiddenToUser = false;
1680
2387
  await taskExecutionMessage.Save();
1681
- this.messageSent.emit(taskExecutionMessage);
2388
+ this.MessageSent.emit(taskExecutionMessage);
1682
2389
  const callback = this.createMessageProgressCallback(taskExecutionMessage.ID);
1683
2390
  this.registeredCallbacks.set(taskExecutionMessage.ID, callback);
1684
2391
  this.streamingService.registerMessageCallback(taskExecutionMessage.ID, callback);
@@ -1728,7 +2435,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1728
2435
  await convoDetail.Load(convoDetail.ID);
1729
2436
  if (convoDetail.Status === 'Complete' || convoDetail.Status === 'Error') {
1730
2437
  this.markMessageComplete(convoDetail);
1731
- this.messageSent.emit(convoDetail);
2438
+ this.MessageSent.emit(convoDetail);
1732
2439
  return;
1733
2440
  }
1734
2441
  }
@@ -1755,7 +2462,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1755
2462
  await convoDetail.Save();
1756
2463
  if (convoDetail.Message === message && convoDetail.Status === status) {
1757
2464
  done = true;
1758
- this.messageSent.emit(convoDetail);
2465
+ this.MessageSent.emit(convoDetail);
1759
2466
  }
1760
2467
  else {
1761
2468
  console.warn(` ⚠️ ConversationDetail update attempt ${attempts + 1} did not persist. ${attempts + 1 < maxAttempts ? 'Retrying...' : 'Giving up.'}`);
@@ -1778,10 +2485,10 @@ export class MessageInputComponent extends BaseAngularComponent {
1778
2485
  * Direction, not on Visibility.
1779
2486
  */
1780
2487
  async loadPreviousPayloadForAgent(agentId) {
1781
- if (!this.conversationId) {
2488
+ if (!this.ConversationId) {
1782
2489
  return { payload: null, artifactInfo: null };
1783
2490
  }
1784
- const source = await this.agentService.FindLatestAgentOutputVersion(this.conversationId, agentId);
2491
+ const source = await this.agentService.FindLatestAgentOutputVersion(this.ConversationId, agentId, this.AgentHistoryFrom);
1785
2492
  if (!source || source.payload == null) {
1786
2493
  console.log(`📦 No previous payload found for agent ${agentId}`);
1787
2494
  return { payload: null, artifactInfo: null };
@@ -1809,8 +2516,14 @@ export class MessageInputComponent extends BaseAngularComponent {
1809
2516
  try {
1810
2517
  // Look up the agent to get its ID
1811
2518
  const agent = AIEngineBase.Instance.Agents.find(a => a.Name === agentName);
2519
+ // The manager routes only among the allowed agents, but its answer is model output: hold
2520
+ // the host's list here too, before any row is written for the delegate.
2521
+ if (this.AllowedAgentIDs != null && !IsAgentAllowed(agent?.ID, this.AllowedAgentIDs)) {
2522
+ await this.refuseDisallowedAgents(userMessage, [agentName]);
2523
+ return;
2524
+ }
1812
2525
  // Create AI response message BEFORE invoking agent (for duration tracking)
1813
- agentResponseMessage = await this.dataCache.createConversationDetail(this.currentUser);
2526
+ agentResponseMessage = await this.dataCache.createConversationDetail(this.CurrentUser);
1814
2527
  agentResponseMessage.ConversationID = conversationId;
1815
2528
  agentResponseMessage.Role = 'AI';
1816
2529
  agentResponseMessage.Message = '⏳ Starting...'; // Initial message
@@ -1823,7 +2536,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1823
2536
  }
1824
2537
  // Save the record to establish __mj_CreatedAt timestamp
1825
2538
  await agentResponseMessage.Save();
1826
- this.messageSent.emit(agentResponseMessage);
2539
+ this.MessageSent.emit(agentResponseMessage);
1827
2540
  // Add sub-agent to active tasks
1828
2541
  const newTaskId = this.activeTasks.add({
1829
2542
  agentName: agentName,
@@ -1831,7 +2544,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1831
2544
  relatedMessageId: userMessage.ID,
1832
2545
  conversationDetailId: agentResponseMessage.ID,
1833
2546
  conversationId,
1834
- conversationName: this.conversationName
2547
+ conversationName: this.ConversationName
1835
2548
  });
1836
2549
  // Load previous payload if agent has been invoked before
1837
2550
  const { payload: previousPayload, artifactInfo } = agent?.ID
@@ -1842,11 +2555,12 @@ export class MessageInputComponent extends BaseAngularComponent {
1842
2555
  ? await this.agentService.FindConfigurationPresetForAgent(conversationId, agent.ID)
1843
2556
  : undefined;
1844
2557
  // Invoke the sub-agent with progress callback
1845
- const subResult = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.conversationHistory, reasoning, agentResponseMessage.ID, previousPayload, // Pass previous payload for continuity
2558
+ const subResult = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.ConversationHistory, reasoning, agentResponseMessage.ID, previousPayload, // Pass previous payload for continuity
1846
2559
  this.createProgressCallback(agentResponseMessage, agentName), artifactInfo?.artifactId, artifactInfo?.versionId, configurationPresetId, // Pass configuration from previous @mention for continuity
1847
- this.appContext, // Embedder-supplied app/form context
2560
+ this.AppContext, // Embedder-supplied app/form context
1848
2561
  this.PlanModeEnabled, // per-request Plan Mode toggle
1849
- this._pendingRequestedSkillIDs);
2562
+ this._pendingRequestedSkillIDs, // user-requested skills (/skill mentions)
2563
+ this.AgentHistoryFrom);
1850
2564
  // Task will be removed automatically in markMessageComplete() when status changes to Complete/Error
1851
2565
  // DO NOT remove here - allows UI to show task during entire execution
1852
2566
  if (subResult && subResult.success) {
@@ -1860,7 +2574,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1860
2574
  // artifacts even when the result payload is empty (e.g., remote stage server).
1861
2575
  // onArtifactCreated will reload from DB and discover any artifacts that exist.
1862
2576
  this.emitArtifactReload(agentResponseMessage);
1863
- this.messageSent.emit(agentResponseMessage);
2577
+ this.MessageSent.emit(agentResponseMessage);
1864
2578
  // Mark user message as complete
1865
2579
  await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1866
2580
  }
@@ -1877,11 +2591,12 @@ export class MessageInputComponent extends BaseAngularComponent {
1877
2591
  // Update the existing agentResponseMessage to show retry status
1878
2592
  await this.updateConversationDetail(agentResponseMessage, "Retrying...", agentResponseMessage.Status);
1879
2593
  // Retry the sub-agent (reuse previously loaded payload and config from first attempt)
1880
- const retryResult = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.conversationHistory, reasoning, agentResponseMessage.ID, previousPayload, // Pass same payload as first attempt
2594
+ const retryResult = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.ConversationHistory, reasoning, agentResponseMessage.ID, previousPayload, // Pass same payload as first attempt
1881
2595
  this.createProgressCallback(agentResponseMessage, `${agentName} (retry)`), artifactInfo?.artifactId, artifactInfo?.versionId, configurationPresetId, // Pass same config as first attempt
1882
- this.appContext, // Embedder-supplied app/form context
2596
+ this.AppContext, // Embedder-supplied app/form context
1883
2597
  this.PlanModeEnabled, // per-request Plan Mode toggle
1884
- this._pendingRequestedSkillIDs);
2598
+ this._pendingRequestedSkillIDs, // user-requested skills (/skill mentions)
2599
+ this.AgentHistoryFrom);
1885
2600
  if (retryResult && retryResult.success) {
1886
2601
  // Retry succeeded - update the same message
1887
2602
  if (retryResult.agentRun.AgentID) {
@@ -1890,7 +2605,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1890
2605
  await this.updateConversationDetail(agentResponseMessage, retryResult.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete', retryResult);
1891
2606
  // Always emit artifactCreated to trigger UI reload (same as initial attempt)
1892
2607
  this.emitArtifactReload(agentResponseMessage);
1893
- this.messageSent.emit(agentResponseMessage);
2608
+ this.MessageSent.emit(agentResponseMessage);
1894
2609
  await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1895
2610
  }
1896
2611
  else {
@@ -1929,19 +2644,19 @@ export class MessageInputComponent extends BaseAngularComponent {
1929
2644
  * check if we should continue with the last agent for iterative refinement
1930
2645
  */
1931
2646
  async handleSilentObservation(userMessage, conversationId) {
1932
- // Find the last AI message (excluding Sage) in the conversation history
1933
- const lastAIMessage = this.conversationHistory
1934
- .slice()
1935
- .reverse()
1936
- .find(msg => msg.Role === 'AI' &&
1937
- msg.AgentID &&
1938
- !UUIDsEqual(msg.AgentID, this.converationManagerAgent?.ID));
2647
+ // Find the last AI message (excluding Sage) this turn may read
2648
+ const lastAIMessage = this.findLastNonSageReply();
1939
2649
  if (!lastAIMessage || !lastAIMessage.AgentID) {
1940
2650
  // No previous specialist agent - just mark user message as complete
1941
2651
  console.log('🔇 No previous specialist agent found - marking complete');
1942
2652
  await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1943
2653
  return;
1944
2654
  }
2655
+ if (!IsAgentAllowed(lastAIMessage.AgentID, this.AllowedAgentIDs)) {
2656
+ console.log('🔇 Previous specialist agent is not allowed in this chat - marking complete');
2657
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
2658
+ return;
2659
+ }
1945
2660
  // Load the agent entity to get its name
1946
2661
  const previousAgent = AIEngineBase.Instance.Agents.find(a => UUIDsEqual(a.ID, lastAIMessage.AgentID));
1947
2662
  if (!previousAgent) {
@@ -1955,7 +2670,7 @@ export class MessageInputComponent extends BaseAngularComponent {
1955
2670
  // Resolved by QUERY, not from the window's artifact maps: `lastAIMessage` can sit inside
1956
2671
  // the loaded window while its artifact does not, and this path silently degrades to a
1957
2672
  // null payload when the lookup misses.
1958
- const source = await this.agentService.FindLatestAgentOutputVersion(conversationId, lastAIMessage.AgentID);
2673
+ const source = await this.agentService.FindLatestAgentOutputVersion(conversationId, lastAIMessage.AgentID, this.AgentHistoryFrom);
1959
2674
  if (source && source.payload != null) {
1960
2675
  previousPayload = source.payload;
1961
2676
  previousArtifactInfo = {
@@ -1966,16 +2681,16 @@ export class MessageInputComponent extends BaseAngularComponent {
1966
2681
  console.log('📦 Loaded previous OUTPUT artifact as payload for continuity', previousArtifactInfo);
1967
2682
  }
1968
2683
  // Create status message showing agent continuity
1969
- const statusMessage = await this.dataCache.createConversationDetail(this.currentUser);
2684
+ const statusMessage = await this.dataCache.createConversationDetail(this.CurrentUser);
1970
2685
  statusMessage.ConversationID = conversationId;
1971
2686
  statusMessage.Role = 'AI';
1972
2687
  statusMessage.Message = `Continuing with **${agentName}** for refinement...`;
1973
2688
  statusMessage.ParentID = userMessage.ID;
1974
2689
  statusMessage.Status = 'Complete';
1975
2690
  statusMessage.HiddenToUser = false;
1976
- statusMessage.AgentID = this.converationManagerAgent?.ID || null;
2691
+ statusMessage.AgentID = this.ConverationManagerAgent?.ID || null;
1977
2692
  await statusMessage.Save();
1978
- this.messageSent.emit(statusMessage);
2693
+ this.MessageSent.emit(statusMessage);
1979
2694
  // Add agent to active tasks
1980
2695
  const taskId = this.activeTasks.add({
1981
2696
  agentName: agentName,
@@ -1983,19 +2698,20 @@ export class MessageInputComponent extends BaseAngularComponent {
1983
2698
  relatedMessageId: userMessage.ID,
1984
2699
  conversationDetailId: statusMessage.ID,
1985
2700
  conversationId,
1986
- conversationName: this.conversationName
2701
+ conversationName: this.ConversationName
1987
2702
  });
1988
2703
  try {
1989
2704
  // Invoke the agent with the previous payload
1990
- const continuityResult = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.conversationHistory, 'Continuing previous work based on user feedback', statusMessage.ID, previousPayload, this.createProgressCallback(statusMessage, agentName), previousArtifactInfo?.artifactId, previousArtifactInfo?.versionId, undefined, // configurationPresetId not used in this path
1991
- this.appContext, // Embedder-supplied app/form context
2705
+ const continuityResult = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.ConversationHistory, 'Continuing previous work based on user feedback', statusMessage.ID, previousPayload, this.createProgressCallback(statusMessage, agentName), previousArtifactInfo?.artifactId, previousArtifactInfo?.versionId, undefined, // configurationPresetId not used in this path
2706
+ this.AppContext, // Embedder-supplied app/form context
1992
2707
  this.PlanModeEnabled, // per-request Plan Mode toggle
1993
- this._pendingRequestedSkillIDs);
2708
+ this._pendingRequestedSkillIDs, // user-requested skills (/skill mentions)
2709
+ this.AgentHistoryFrom);
1994
2710
  // Remove from active tasks
1995
2711
  // Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
1996
2712
  if (continuityResult && continuityResult.success) {
1997
2713
  // Create response message
1998
- const agentResponseMessage = await this.dataCache.createConversationDetail(this.currentUser);
2714
+ const agentResponseMessage = await this.dataCache.createConversationDetail(this.CurrentUser);
1999
2715
  agentResponseMessage.ConversationID = conversationId;
2000
2716
  agentResponseMessage.Role = 'AI';
2001
2717
  agentResponseMessage.Message = continuityResult.agentRun?.Message || `✅ **${agentName}** completed refinement`;
@@ -2004,12 +2720,12 @@ export class MessageInputComponent extends BaseAngularComponent {
2004
2720
  agentResponseMessage.HiddenToUser = false;
2005
2721
  agentResponseMessage.AgentID = lastAIMessage.AgentID;
2006
2722
  await agentResponseMessage.Save();
2007
- this.messageSent.emit(agentResponseMessage);
2723
+ this.MessageSent.emit(agentResponseMessage);
2008
2724
  // Server created artifacts (handles versioning automatically) - emit event to trigger UI reload
2009
2725
  if (continuityResult.payload && Object.keys(continuityResult.payload).length > 0) {
2010
2726
  this.emitArtifactReload(agentResponseMessage);
2011
2727
  console.log('🎨 Server created artifact (versioned) from agent continuity');
2012
- this.messageSent.emit(agentResponseMessage);
2728
+ this.MessageSent.emit(agentResponseMessage);
2013
2729
  }
2014
2730
  // Mark user message as complete
2015
2731
  await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
@@ -2037,7 +2753,7 @@ export class MessageInputComponent extends BaseAngularComponent {
2037
2753
  relatedMessageId: userMessage.ID,
2038
2754
  conversationDetailId: userMessage.ID,
2039
2755
  conversationId,
2040
- conversationName: this.conversationName
2756
+ conversationName: this.ConversationName
2041
2757
  });
2042
2758
  // Declare agentResponseMessage outside try block so it's accessible in catch
2043
2759
  let agentResponseMessage = undefined;
@@ -2046,11 +2762,11 @@ export class MessageInputComponent extends BaseAngularComponent {
2046
2762
  // (No UI uses User message 'In-Progress' - only AI messages need that status)
2047
2763
  userMessage.Status = 'Complete';
2048
2764
  await userMessage.Save();
2049
- this.messageSent.emit(userMessage);
2765
+ this.MessageSent.emit(userMessage);
2050
2766
  // Look up the agent to get its ID
2051
2767
  const agent = AIEngineBase.Instance.Agents.find(a => a.Name === agentName);
2052
2768
  // Create AI response message BEFORE invoking agent (for duration tracking)
2053
- agentResponseMessage = await this.dataCache.createConversationDetail(this.currentUser);
2769
+ agentResponseMessage = await this.dataCache.createConversationDetail(this.CurrentUser);
2054
2770
  agentResponseMessage.ConversationID = conversationId;
2055
2771
  agentResponseMessage.Role = 'AI';
2056
2772
  agentResponseMessage.Message = '⏳ Starting...'; // Initial message
@@ -2063,23 +2779,25 @@ export class MessageInputComponent extends BaseAngularComponent {
2063
2779
  }
2064
2780
  // Save the record to establish __mj_CreatedAt timestamp
2065
2781
  await agentResponseMessage.Save();
2066
- this.messageSent.emit(agentResponseMessage);
2782
+ this.MessageSent.emit(agentResponseMessage);
2067
2783
  // Load previous payload if agent has been invoked before
2068
2784
  const { payload: previousPayload, artifactInfo } = agent?.ID
2069
2785
  ? await this.loadPreviousPayloadForAgent(agent.ID)
2070
2786
  : { payload: null, artifactInfo: null };
2071
2787
  // Invoke the agent directly
2072
- const result = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.conversationHistory, `User mentioned agent directly with @${agentName}`, agentResponseMessage.ID, previousPayload, // Pass previous payload for continuity
2788
+ const result = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.ConversationHistory, `User mentioned agent directly with @${agentName}`, agentResponseMessage.ID, previousPayload, // Pass previous payload for continuity
2073
2789
  this.createProgressCallback(agentResponseMessage, agentName), artifactInfo?.artifactId, artifactInfo?.versionId, agentMention.configurationId, // Pass configuration preset ID
2074
- this.appContext, // Embedder-supplied app/form context
2790
+ this.AppContext, // Embedder-supplied app/form context
2075
2791
  this.PlanModeEnabled, // per-request Plan Mode toggle
2076
- this._pendingRequestedSkillIDs);
2792
+ this._pendingRequestedSkillIDs, // user-requested skills (/skill mentions)
2793
+ this.AgentHistoryFrom);
2077
2794
  // Remove from active tasks
2078
2795
  // Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
2079
2796
  if (result && result.success) {
2080
2797
  if (result.agentRun.AgentID) {
2081
2798
  agentResponseMessage.AgentID = result.agentRun.AgentID;
2082
2799
  }
2800
+ this.emitAfterAgentTurn(conversationId, result);
2083
2801
  // Multi-stage response handling (same logic as ambient Sage)
2084
2802
  // Stage 1: Check for task graph (multi-step orchestration)
2085
2803
  if (result.payload?.taskGraph) {
@@ -2097,7 +2815,7 @@ export class MessageInputComponent extends BaseAngularComponent {
2097
2815
  // Server created artifacts - emit event to trigger UI reload
2098
2816
  if (result.payload && Object.keys(result.payload).length > 0) {
2099
2817
  this.emitArtifactReload(agentResponseMessage);
2100
- this.messageSent.emit(agentResponseMessage);
2818
+ this.MessageSent.emit(agentResponseMessage);
2101
2819
  }
2102
2820
  // Mark user message as complete
2103
2821
  await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
@@ -2127,6 +2845,11 @@ export class MessageInputComponent extends BaseAngularComponent {
2127
2845
  // Load the agent entity to get its name
2128
2846
  const agent = AIEngineBase.Instance.Agents.find(a => UUIDsEqual(a.ID, agentId));
2129
2847
  if (!agent) {
2848
+ if (!IsAgentAllowed(this.ConverationManagerAgent?.ID, this.AllowedAgentIDs)) {
2849
+ console.warn('⚠️ Could not load agent for continuation, and Sage is not allowed in this chat');
2850
+ await this.finishWithoutAgentTurn(userMessage, 'Declined');
2851
+ return;
2852
+ }
2130
2853
  console.warn('⚠️ Could not load agent for continuation - falling back to Sage');
2131
2854
  await this.processMessageThroughAgent(userMessage, { mentions: [], agentMention: null, userMentions: [], entityMentions: [], skillMentions: [] });
2132
2855
  return;
@@ -2163,8 +2886,8 @@ export class MessageInputComponent extends BaseAngularComponent {
2163
2886
  // High) and applies to all subsequent non-mention routes. A
2164
2887
  // history-derived preset still wins because it represents an
2165
2888
  // explicit per-message intent the user expressed earlier.
2166
- if (!previousConfigurationId && this.agentConfigurationPresetId) {
2167
- previousConfigurationId = this.agentConfigurationPresetId;
2889
+ if (!previousConfigurationId && this.AgentConfigurationPresetId) {
2890
+ previousConfigurationId = this.AgentConfigurationPresetId;
2168
2891
  }
2169
2892
  // Fall back to searching through all agent messages for an artifact
2170
2893
  // This ensures payload continuity even after clarifying exchanges without artifacts
@@ -2173,7 +2896,7 @@ export class MessageInputComponent extends BaseAngularComponent {
2173
2896
  // used to walk is the loaded window, so the artifact it is looking for is exactly the
2174
2897
  // one most likely to be missing from it.
2175
2898
  if (!previousPayload) {
2176
- const source = await this.agentService.FindLatestAgentOutputVersion(conversationId, agentId);
2899
+ const source = await this.agentService.FindLatestAgentOutputVersion(conversationId, agentId, this.AgentHistoryFrom);
2177
2900
  if (source && source.payload != null) {
2178
2901
  previousPayload = source.payload;
2179
2902
  previousArtifactInfo = {
@@ -2210,7 +2933,7 @@ export class MessageInputComponent extends BaseAngularComponent {
2210
2933
  relatedMessageId: userMessage.ID,
2211
2934
  conversationDetailId: userMessage.ID,
2212
2935
  conversationId,
2213
- conversationName: this.conversationName
2936
+ conversationName: this.ConversationName
2214
2937
  });
2215
2938
  // Declare agentResponseMessage outside try block so it's accessible in catch
2216
2939
  let agentResponseMessage = undefined;
@@ -2219,9 +2942,9 @@ export class MessageInputComponent extends BaseAngularComponent {
2219
2942
  // (No UI uses User message 'In-Progress' - only AI messages need that status)
2220
2943
  userMessage.Status = 'Complete';
2221
2944
  await userMessage.Save();
2222
- this.messageSent.emit(userMessage);
2945
+ this.MessageSent.emit(userMessage);
2223
2946
  // Create AI response message BEFORE invoking agent (for duration tracking)
2224
- agentResponseMessage = await this.dataCache.createConversationDetail(this.currentUser);
2947
+ agentResponseMessage = await this.dataCache.createConversationDetail(this.CurrentUser);
2225
2948
  agentResponseMessage.ConversationID = conversationId;
2226
2949
  agentResponseMessage.Role = 'AI';
2227
2950
  agentResponseMessage.Message = '⏳ Starting...'; // Initial message
@@ -2231,26 +2954,28 @@ export class MessageInputComponent extends BaseAngularComponent {
2231
2954
  agentResponseMessage.AgentID = agentId;
2232
2955
  // Save the record to establish __mj_CreatedAt timestamp
2233
2956
  await agentResponseMessage.Save();
2234
- this.messageSent.emit(agentResponseMessage);
2957
+ this.MessageSent.emit(agentResponseMessage);
2235
2958
  // Invoke the agent directly (continuation) with previous payload if available.
2236
2959
  // `this.appContext` is forwarded so direct-routed sub-agents (e.g. Form
2237
2960
  // Builder via [defaultAgentId]) see the embedder's ActiveForm/Schema/
2238
2961
  // OverrideID block in their prompt — same flow Sage gets via
2239
2962
  // `processMessage`.
2240
- const result = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.conversationHistory, 'Continuing previous conversation with user', agentResponseMessage.ID, previousPayload, // Pass previous OUTPUT artifact payload for continuity
2963
+ const result = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.ConversationHistory, 'Continuing previous conversation with user', agentResponseMessage.ID, previousPayload, // Pass previous OUTPUT artifact payload for continuity
2241
2964
  this.createProgressCallback(agentResponseMessage, agentName), previousArtifactInfo?.artifactId, previousArtifactInfo?.versionId, configurationId, // Pass configuration for continuity
2242
- this.appContext, // Embedder-supplied app/form context
2965
+ this.AppContext, // Embedder-supplied app/form context
2243
2966
  this.PlanModeEnabled, // per-request Plan Mode toggle
2244
- this._pendingRequestedSkillIDs);
2967
+ this._pendingRequestedSkillIDs, // user-requested skills (/skill mentions)
2968
+ this.AgentHistoryFrom);
2245
2969
  // Remove from active tasks
2246
2970
  // Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
2247
2971
  if (result && result.success) {
2972
+ this.emitAfterAgentTurn(conversationId, result);
2248
2973
  // Update the response message with agent result
2249
2974
  await this.updateConversationDetail(agentResponseMessage, result.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete', result);
2250
2975
  // Server created artifacts (handles versioning) - emit event to trigger UI reload
2251
2976
  if (result.payload && Object.keys(result.payload).length > 0) {
2252
2977
  this.emitArtifactReload(agentResponseMessage);
2253
- this.messageSent.emit(agentResponseMessage);
2978
+ this.MessageSent.emit(agentResponseMessage);
2254
2979
  }
2255
2980
  // Mark user message as complete
2256
2981
  await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
@@ -2284,11 +3009,11 @@ export class MessageInputComponent extends BaseAngularComponent {
2284
3009
  ConversationId: conversationId,
2285
3010
  MessageText: plainTextMessage,
2286
3011
  Provider: this.ProviderToUse,
2287
- CurrentUser: this.currentUser
3012
+ CurrentUser: this.CurrentUser
2288
3013
  });
2289
3014
  if (result) {
2290
3015
  // Emit event for animation in conversation list
2291
- this.conversationRenamed.emit({
3016
+ this.ConversationRenamed.emit({
2292
3017
  conversationId,
2293
3018
  name: result.Name,
2294
3019
  description: result.Description
@@ -2352,7 +3077,7 @@ export class MessageInputComponent extends BaseAngularComponent {
2352
3077
  if (detail.Status === 'Complete' || detail.Status === 'Error') {
2353
3078
  this.stopInFlightDetailWatch(detail.ID);
2354
3079
  this.markMessageComplete(detail);
2355
- this.messageSent.emit(detail);
3080
+ this.MessageSent.emit(detail);
2356
3081
  return;
2357
3082
  }
2358
3083
  }
@@ -2444,7 +3169,7 @@ export class MessageInputComponent extends BaseAngularComponent {
2444
3169
  LogStatusEx({ message: `[MarkComplete] No task registered against completed message ${conversationDetail.ID} — expected for any message that did not start the turn`, verboseOnly: true });
2445
3170
  }
2446
3171
  // Emit completion event to parent so it can refresh agent run data
2447
- this.messageComplete.emit({
3172
+ this.MessageComplete.emit({
2448
3173
  conversationId: conversationDetail.ConversationID,
2449
3174
  conversationDetailId: conversationDetail.ID,
2450
3175
  agentId: conversationDetail.AgentID || undefined
@@ -2458,7 +3183,7 @@ export class MessageInputComponent extends BaseAngularComponent {
2458
3183
  * detail entity's immutable ConversationID, never this.conversationId.
2459
3184
  */
2460
3185
  emitArtifactReload(detail) {
2461
- this.artifactCreated.emit({
3186
+ this.ArtifactCreated.emit({
2462
3187
  conversationId: detail.ConversationID,
2463
3188
  artifactId: '',
2464
3189
  versionId: '',
@@ -2481,8 +3206,8 @@ export class MessageInputComponent extends BaseAngularComponent {
2481
3206
  i0.ɵɵviewQuery(_c0, 5);
2482
3207
  } if (rf & 2) {
2483
3208
  let _t;
2484
- i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.inputBox = _t.first);
2485
- } }, inputs: { conversationId: "conversationId", conversationName: "conversationName", currentUser: "currentUser", disabled: "disabled", placeholder: "placeholder", parentMessageId: "parentMessageId", enableAttachments: "enableAttachments", enableMentions: "enableMentions", enableAgentMentions: "enableAgentMentions", enableEntityMentions: "enableEntityMentions", enableSkillCommands: "enableSkillCommands", enablePlanMode: "enablePlanMode", enableRealtime: "enableRealtime", maxAttachments: "maxAttachments", maxAttachmentSizeBytes: "maxAttachmentSizeBytes", acceptedFileTypes: "acceptedFileTypes", artifactsByDetailId: "artifactsByDetailId", systemArtifactsByDetailId: "systemArtifactsByDetailId", agentRunsByDetailId: "agentRunsByDetailId", emptyStateMode: "emptyStateMode", appContext: "appContext", defaultAgentId: "defaultAgentId", conversationDefaultAgentId: "conversationDefaultAgentId", agentConfigurationPresetId: "agentConfigurationPresetId", initialMessage: "initialMessage", initialAttachments: "initialAttachments", conversationHistory: "conversationHistory", inProgressMessageIds: "inProgressMessageIds", applicationId: "applicationId", initialDraft: "initialDraft" }, outputs: { messageSent: "messageSent", agentResponse: "agentResponse", beforeAgentTurn: "beforeAgentTurn", afterAgentTurn: "afterAgentTurn", agentRunDetected: "agentRunDetected", agentRunUpdate: "agentRunUpdate", messageComplete: "messageComplete", artifactCreated: "artifactCreated", conversationRenamed: "conversationRenamed", intentCheckStarted: "intentCheckStarted", intentCheckCompleted: "intentCheckCompleted", initialMessageAutoSendStarted: "initialMessageAutoSendStarted", initialMessageAutoSendFailed: "initialMessageAutoSendFailed", emptyStateSubmit: "emptyStateSubmit", uploadStateChanged: "uploadStateChanged", initialDraftApplied: "initialDraftApplied", DraftStateChanged: "DraftStateChanged", ComposerBlurred: "ComposerBlurred" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 6, vars: 27, consts: [["pickerOrigin", "cdkOverlayOrigin"], ["inputBox", ""], ["cdkOverlayOrigin", "", 1, "message-input-wrapper"], [1, "processing-indicator"], [3, "valueChange", "blurred", "textSubmitted", "attachmentsChanged", "attachmentError", "voiceRequested", "voiceOptionsRequested", "planModeToggle", "placeholder", "disabled", "showCharacterCount", "enableMentions", "EnableAgentMentions", "EnableEntityMentions", "EnableSkillCommands", "TargetAgentId", "enableAttachments", "maxAttachments", "maxAttachmentSizeBytes", "acceptedFileTypes", "currentUser", "Provider", "rows", "enableRealtime", "voiceActive", "canStartRealtime", "enablePlanMode", "planModeActive", "value"], ["cdkConnectedOverlay", "", "cdkConnectedOverlayBackdropClass", "cdk-overlay-transparent-backdrop", 3, "backdropClick", "detach", "cdkConnectedOverlayOrigin", "cdkConnectedOverlayOpen", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPush", "cdkConnectedOverlayHasBackdrop"], [1, "fas", "fa-circle-notch", "fa-spin"], [3, "AgentPicked", "Cancelled", "Provider", "CurrentUser", "Agents", "DefaultAgentId", "CoAgents", "DefaultCoAgentId"]], template: function MessageInputComponent_Template(rf, ctx) { if (rf & 1) {
3209
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.InputBox = _t.first);
3210
+ } }, inputs: { ConversationId: "ConversationId", conversationId: "conversationId", ConversationName: "ConversationName", conversationName: "conversationName", CurrentUser: "CurrentUser", currentUser: "currentUser", Disabled: "Disabled", disabled: "disabled", Placeholder: "Placeholder", placeholder: "placeholder", ParentMessageId: "ParentMessageId", parentMessageId: "parentMessageId", EnableAttachments: "EnableAttachments", enableAttachments: "enableAttachments", EnableMentions: "EnableMentions", enableMentions: "enableMentions", EnableAgentMentions: "EnableAgentMentions", enableAgentMentions: "enableAgentMentions", EnableEntityMentions: "EnableEntityMentions", enableEntityMentions: "enableEntityMentions", EnableSkillCommands: "EnableSkillCommands", enableSkillCommands: "enableSkillCommands", EnablePlanMode: "EnablePlanMode", enablePlanMode: "enablePlanMode", EnableRealtime: "EnableRealtime", enableRealtime: "enableRealtime", MaxAttachments: "MaxAttachments", maxAttachments: "maxAttachments", MaxAttachmentSizeBytes: "MaxAttachmentSizeBytes", maxAttachmentSizeBytes: "maxAttachmentSizeBytes", AcceptedFileTypes: "AcceptedFileTypes", acceptedFileTypes: "acceptedFileTypes", ArtifactsByDetailId: "ArtifactsByDetailId", artifactsByDetailId: "artifactsByDetailId", SystemArtifactsByDetailId: "SystemArtifactsByDetailId", systemArtifactsByDetailId: "systemArtifactsByDetailId", AgentRunsByDetailId: "AgentRunsByDetailId", agentRunsByDetailId: "agentRunsByDetailId", EmptyStateMode: "EmptyStateMode", emptyStateMode: "emptyStateMode", AppContext: "AppContext", appContext: "appContext", DefaultAgentId: "DefaultAgentId", defaultAgentId: "defaultAgentId", ConversationDefaultAgentId: "ConversationDefaultAgentId", conversationDefaultAgentId: "conversationDefaultAgentId", AgentConfigurationPresetId: "AgentConfigurationPresetId", agentConfigurationPresetId: "agentConfigurationPresetId", InitialMessage: "InitialMessage", initialMessage: "initialMessage", InitialAttachments: "InitialAttachments", initialAttachments: "initialAttachments", ConversationHistory: "ConversationHistory", conversationHistory: "conversationHistory", InProgressMessageIds: "InProgressMessageIds", inProgressMessageIds: "inProgressMessageIds", ApplicationId: "ApplicationId", applicationId: "applicationId", AgentReplyMode: "AgentReplyMode", AllowedAgentIDs: "AllowedAgentIDs", MentionPeople: "MentionPeople", AgentHistoryFrom: "AgentHistoryFrom", AgentTurnHandler: "AgentTurnHandler", AutoNameConversation: "AutoNameConversation", InitialDraft: "InitialDraft", initialDraft: "initialDraft" }, outputs: { MessageSent: "MessageSent", messageSent: "messageSent", AgentResponse: "AgentResponse", agentResponse: "agentResponse", BeforeAgentTurn: "BeforeAgentTurn", beforeAgentTurn: "beforeAgentTurn", AfterAgentTurn: "AfterAgentTurn", afterAgentTurn: "afterAgentTurn", AgentRunDetected: "AgentRunDetected", agentRunDetected: "agentRunDetected", AgentRunUpdate: "AgentRunUpdate", agentRunUpdate: "agentRunUpdate", MessageComplete: "MessageComplete", messageComplete: "messageComplete", ArtifactCreated: "ArtifactCreated", artifactCreated: "artifactCreated", ConversationRenamed: "ConversationRenamed", conversationRenamed: "conversationRenamed", IntentCheckStarted: "IntentCheckStarted", intentCheckStarted: "intentCheckStarted", IntentCheckCompleted: "IntentCheckCompleted", intentCheckCompleted: "intentCheckCompleted", InitialMessageAutoSendStarted: "InitialMessageAutoSendStarted", initialMessageAutoSendStarted: "initialMessageAutoSendStarted", InitialMessageAutoSendFailed: "InitialMessageAutoSendFailed", initialMessageAutoSendFailed: "initialMessageAutoSendFailed", EmptyStateSubmit: "EmptyStateSubmit", emptyStateSubmit: "emptyStateSubmit", UploadStateChanged: "UploadStateChanged", uploadStateChanged: "uploadStateChanged", InitialDraftApplied: "InitialDraftApplied", initialDraftApplied: "initialDraftApplied", DraftStateChanged: "DraftStateChanged", ComposerBlurred: "ComposerBlurred" }, standalone: false, features: [i0.ɵɵInheritDefinitionFeature, i0.ɵɵNgOnChangesFeature], decls: 6, vars: 29, consts: [["pickerOrigin", "cdkOverlayOrigin"], ["inputBox", ""], ["cdkOverlayOrigin", "", 1, "message-input-wrapper"], [1, "processing-indicator"], [3, "valueChange", "blurred", "textSubmitted", "attachmentsChanged", "attachmentError", "voiceRequested", "voiceOptionsRequested", "planModeToggle", "placeholder", "disabled", "showCharacterCount", "enableMentions", "EnableAgentMentions", "EnableEntityMentions", "EnableSkillCommands", "TargetAgentId", "AllowedAgentIDs", "MentionPeople", "enableAttachments", "maxAttachments", "maxAttachmentSizeBytes", "acceptedFileTypes", "currentUser", "Provider", "rows", "enableRealtime", "voiceActive", "canStartRealtime", "enablePlanMode", "planModeActive", "value"], ["cdkConnectedOverlay", "", "cdkConnectedOverlayBackdropClass", "cdk-overlay-transparent-backdrop", 3, "backdropClick", "detach", "cdkConnectedOverlayOrigin", "cdkConnectedOverlayOpen", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPush", "cdkConnectedOverlayHasBackdrop"], [1, "fas", "fa-circle-notch", "fa-spin"], [3, "AgentPicked", "Cancelled", "Provider", "CurrentUser", "Agents", "DefaultAgentId", "CoAgents", "DefaultCoAgentId"]], template: function MessageInputComponent_Template(rf, ctx) { if (rf & 1) {
2486
3211
  i0.ɵɵelementStart(0, "div", 2, 0);
2487
3212
  i0.ɵɵconditionalCreate(2, MessageInputComponent_Conditional_2_Template, 4, 1, "div", 3);
2488
3213
  i0.ɵɵelementStart(3, "mj-ai-composer", 4, 1);
@@ -2496,107 +3221,211 @@ export class MessageInputComponent extends BaseAngularComponent {
2496
3221
  i0.ɵɵadvance(2);
2497
3222
  i0.ɵɵconditional(ctx.isProcessing ? 2 : -1);
2498
3223
  i0.ɵɵadvance();
2499
- i0.ɵɵproperty("placeholder", ctx.placeholder)("disabled", ctx.disabled || ctx.isProcessing)("showCharacterCount", false)("enableMentions", ctx.enableMentions)("EnableAgentMentions", ctx.enableAgentMentions)("EnableEntityMentions", ctx.enableEntityMentions)("EnableSkillCommands", ctx.enableSkillCommands)("TargetAgentId", ctx.pickerTargetAgentId)("enableAttachments", ctx.enableAttachments)("maxAttachments", ctx.maxAttachments)("maxAttachmentSizeBytes", ctx.maxAttachmentSizeBytes)("acceptedFileTypes", ctx.acceptedFileTypes)("currentUser", ctx.currentUser)("Provider", ctx.Provider)("rows", 3)("enableRealtime", ctx.enableRealtime)("voiceActive", ctx.voiceActive)("canStartRealtime", ctx.canStartRealtime)("enablePlanMode", ctx.enablePlanMode)("planModeActive", ctx.PlanModeEnabled)("value", ctx.messageText);
3224
+ i0.ɵɵproperty("placeholder", ctx.placeholder)("disabled", ctx.disabled || ctx.isProcessing)("showCharacterCount", false)("enableMentions", ctx.enableMentions)("EnableAgentMentions", ctx.enableAgentMentions)("EnableEntityMentions", ctx.enableEntityMentions)("EnableSkillCommands", ctx.enableSkillCommands)("TargetAgentId", ctx.pickerTargetAgentId)("AllowedAgentIDs", ctx.AllowedAgentIDs)("MentionPeople", ctx.MentionPeople)("enableAttachments", ctx.enableAttachments)("maxAttachments", ctx.maxAttachments)("maxAttachmentSizeBytes", ctx.maxAttachmentSizeBytes)("acceptedFileTypes", ctx.acceptedFileTypes)("currentUser", ctx.currentUser)("Provider", ctx.Provider)("rows", 3)("enableRealtime", ctx.enableRealtime)("voiceActive", ctx.voiceActive)("canStartRealtime", ctx.canStartRealtime)("enablePlanMode", ctx.enablePlanMode)("planModeActive", ctx.PlanModeEnabled)("value", ctx.messageText);
2500
3225
  i0.ɵɵadvance(2);
2501
3226
  i0.ɵɵproperty("cdkConnectedOverlayOrigin", pickerOrigin_r3)("cdkConnectedOverlayOpen", ctx.showRealtimeAgentPicker)("cdkConnectedOverlayPositions", ctx.pickerOverlayPositions)("cdkConnectedOverlayPush", true)("cdkConnectedOverlayHasBackdrop", true);
2502
3227
  } }, dependencies: [i11.CdkConnectedOverlay, i11.CdkOverlayOrigin, i12.RealtimeAgentPickerComponent, i13.AiComposerComponent], styles: [".message-input-wrapper[_ngcontent-%COMP%] {\n position: relative;\n width: 100%;\n}\n\n\n\n\n.processing-indicator[_ngcontent-%COMP%] {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1.25rem;\n background: color-mix(in srgb, var(--mj-bg-surface-card) 95%, transparent);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-sm);\n z-index: 10;\n pointer-events: none;\n}\n.processing-indicator[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: var(--mj-brand-primary);\n}\n.processing-indicator[_ngcontent-%COMP%] span[_ngcontent-%COMP%] {\n font-size: 0.9rem;\n color: var(--mj-text-primary);\n}"] });
2503
3228
  }
2504
3229
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MessageInputComponent, [{
2505
3230
  type: Component,
2506
- args: [{ standalone: false, selector: 'mj-message-input', template: "<div class=\"message-input-wrapper\" cdkOverlayOrigin #pickerOrigin=\"cdkOverlayOrigin\">\n <!-- Processing Indicator Overlay -->\n @if (isProcessing) {\n <div class=\"processing-indicator\">\n <i class=\"fas fa-circle-notch fa-spin\"></i>\n <span>{{ processingMessage }}</span>\n </div>\n }\n\n <!-- AI composer (mic + Plan Mode buttons live in its in-composer toolbar): wraps the\n generic mj-message-input-box with the '@' agent / '#' record / '/' skill trigger\n plugins built in \u2014 all three on by default, preserving the standard chat behavior. -->\n <mj-ai-composer\n #inputBox\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled || isProcessing\"\n [showCharacterCount]=\"false\"\n [enableMentions]=\"enableMentions\"\n [EnableAgentMentions]=\"enableAgentMentions\"\n [EnableEntityMentions]=\"enableEntityMentions\"\n [EnableSkillCommands]=\"enableSkillCommands\"\n [TargetAgentId]=\"pickerTargetAgentId\"\n [enableAttachments]=\"enableAttachments\"\n [maxAttachments]=\"maxAttachments\"\n [maxAttachmentSizeBytes]=\"maxAttachmentSizeBytes\"\n [acceptedFileTypes]=\"acceptedFileTypes\"\n [currentUser]=\"currentUser\"\n [Provider]=\"Provider\"\n [rows]=\"3\"\n [enableRealtime]=\"enableRealtime\"\n [voiceActive]=\"voiceActive\"\n [canStartRealtime]=\"canStartRealtime\"\n [enablePlanMode]=\"enablePlanMode\"\n [planModeActive]=\"PlanModeEnabled\"\n [value]=\"messageText\"\n (valueChange)=\"OnComposerValueChanged($event)\"\n (blurred)=\"ComposerBlurred.emit()\"\n (textSubmitted)=\"onTextSubmitted($event)\"\n (attachmentsChanged)=\"onAttachmentsChanged($event)\"\n (attachmentError)=\"onAttachmentError($event)\"\n (voiceRequested)=\"onStartRealtime()\"\n (voiceOptionsRequested)=\"onRealtimeOptions()\"\n (planModeToggle)=\"TogglePlanMode()\">\n </mj-ai-composer>\n\n <!-- Voice agent/co-agent/model picker \u2014 shown when starting a call on a conversation\n with NO prior agent participation, or on demand via the caret next to the phone\n button (any conversation). Rendered in a body-level CDK overlay anchored above the\n composer so it POPS OUT of the chat overlay's clipping border instead of being cut\n off at the top of a narrow (~390px) overlay. -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"pickerOrigin\"\n [cdkConnectedOverlayOpen]=\"showRealtimeAgentPicker\"\n [cdkConnectedOverlayPositions]=\"pickerOverlayPositions\"\n [cdkConnectedOverlayPush]=\"true\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n (backdropClick)=\"onRealtimeAgentPickerCancelled()\"\n (detach)=\"onRealtimeAgentPickerCancelled()\">\n <mj-realtime-agent-picker\n [Provider]=\"ProviderToUse\"\n [CurrentUser]=\"currentUser\"\n [Agents]=\"voicePickerAgents\"\n [DefaultAgentId]=\"voicePickerDefaultAgentId\"\n [CoAgents]=\"voicePickerCoAgents\"\n [DefaultCoAgentId]=\"voicePickerDefaultCoAgentId\"\n (AgentPicked)=\"onRealtimeAgentPicked($event)\"\n (Cancelled)=\"onRealtimeAgentPickerCancelled()\">\n </mj-realtime-agent-picker>\n </ng-template>\n</div>\n\n<!-- The voice \"call mode\" overlay is hosted by <mj-conversation-chat-area> (it fills the\n whole conversation panel in place); this component only owns the trigger wiring. -->\n", styles: [".message-input-wrapper {\n position: relative;\n width: 100%;\n}\n\n/* Plan Mode moved into the in-composer toolbar (message-input-box) as a toggle icon button\n next to the attachment/voice buttons \u2014 see .plan-mode-button-icon there. */\n\n.processing-indicator {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1.25rem;\n background: color-mix(in srgb, var(--mj-bg-surface-card) 95%, transparent);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-sm);\n z-index: 10;\n pointer-events: none;\n}\n.processing-indicator i {\n color: var(--mj-brand-primary);\n}\n.processing-indicator span {\n font-size: 0.9rem;\n color: var(--mj-text-primary);\n}\n"] }]
2507
- }], () => [{ type: i1.DialogService }, { type: i2.ToastService }, { type: i3.ConversationAgentService }, { type: i4.DataCacheService }, { type: i5.ActiveTasksService }, { type: i6.ConversationStreamingService }, { type: i7.MentionParserService }, { type: i8.ConversationAttachmentService }, { type: i9.ConversationBridgeService }, { type: i10.RealtimeSessionService }], { conversationId: [{
3231
+ args: [{ standalone: false, selector: 'mj-message-input', template: "<div class=\"message-input-wrapper\" cdkOverlayOrigin #pickerOrigin=\"cdkOverlayOrigin\">\n <!-- Processing Indicator Overlay -->\n @if (isProcessing) {\n <div class=\"processing-indicator\">\n <i class=\"fas fa-circle-notch fa-spin\"></i>\n <span>{{ processingMessage }}</span>\n </div>\n }\n\n <!-- AI composer (mic + Plan Mode buttons live in its in-composer toolbar): wraps the\n generic mj-message-input-box with the '@' agent / '#' record / '/' skill trigger\n plugins built in \u2014 all three on by default, preserving the standard chat behavior. -->\n <mj-ai-composer\n #inputBox\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled || isProcessing\"\n [showCharacterCount]=\"false\"\n [enableMentions]=\"enableMentions\"\n [EnableAgentMentions]=\"enableAgentMentions\"\n [EnableEntityMentions]=\"enableEntityMentions\"\n [EnableSkillCommands]=\"enableSkillCommands\"\n [TargetAgentId]=\"pickerTargetAgentId\"\n [AllowedAgentIDs]=\"AllowedAgentIDs\"\n [MentionPeople]=\"MentionPeople\"\n [enableAttachments]=\"enableAttachments\"\n [maxAttachments]=\"maxAttachments\"\n [maxAttachmentSizeBytes]=\"maxAttachmentSizeBytes\"\n [acceptedFileTypes]=\"acceptedFileTypes\"\n [currentUser]=\"currentUser\"\n [Provider]=\"Provider\"\n [rows]=\"3\"\n [enableRealtime]=\"enableRealtime\"\n [voiceActive]=\"voiceActive\"\n [canStartRealtime]=\"canStartRealtime\"\n [enablePlanMode]=\"enablePlanMode\"\n [planModeActive]=\"PlanModeEnabled\"\n [value]=\"messageText\"\n (valueChange)=\"OnComposerValueChanged($event)\"\n (blurred)=\"ComposerBlurred.emit()\"\n (textSubmitted)=\"onTextSubmitted($event)\"\n (attachmentsChanged)=\"onAttachmentsChanged($event)\"\n (attachmentError)=\"onAttachmentError($event)\"\n (voiceRequested)=\"onStartRealtime()\"\n (voiceOptionsRequested)=\"onRealtimeOptions()\"\n (planModeToggle)=\"TogglePlanMode()\">\n </mj-ai-composer>\n\n <!-- Voice agent/co-agent/model picker \u2014 shown when starting a call on a conversation\n with NO prior agent participation, or on demand via the caret next to the phone\n button (any conversation). Rendered in a body-level CDK overlay anchored above the\n composer so it POPS OUT of the chat overlay's clipping border instead of being cut\n off at the top of a narrow (~390px) overlay. -->\n <ng-template\n cdkConnectedOverlay\n [cdkConnectedOverlayOrigin]=\"pickerOrigin\"\n [cdkConnectedOverlayOpen]=\"showRealtimeAgentPicker\"\n [cdkConnectedOverlayPositions]=\"pickerOverlayPositions\"\n [cdkConnectedOverlayPush]=\"true\"\n [cdkConnectedOverlayHasBackdrop]=\"true\"\n cdkConnectedOverlayBackdropClass=\"cdk-overlay-transparent-backdrop\"\n (backdropClick)=\"onRealtimeAgentPickerCancelled()\"\n (detach)=\"onRealtimeAgentPickerCancelled()\">\n <mj-realtime-agent-picker\n [Provider]=\"ProviderToUse\"\n [CurrentUser]=\"currentUser\"\n [Agents]=\"voicePickerAgents\"\n [DefaultAgentId]=\"voicePickerDefaultAgentId\"\n [CoAgents]=\"voicePickerCoAgents\"\n [DefaultCoAgentId]=\"voicePickerDefaultCoAgentId\"\n (AgentPicked)=\"onRealtimeAgentPicked($event)\"\n (Cancelled)=\"onRealtimeAgentPickerCancelled()\">\n </mj-realtime-agent-picker>\n </ng-template>\n</div>\n\n<!-- The voice \"call mode\" overlay is hosted by <mj-conversation-chat-area> (it fills the\n whole conversation panel in place); this component only owns the trigger wiring. -->\n", styles: [".message-input-wrapper {\n position: relative;\n width: 100%;\n}\n\n/* Plan Mode moved into the in-composer toolbar (message-input-box) as a toggle icon button\n next to the attachment/voice buttons \u2014 see .plan-mode-button-icon there. */\n\n.processing-indicator {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1.25rem;\n background: color-mix(in srgb, var(--mj-bg-surface-card) 95%, transparent);\n border-radius: 8px;\n box-shadow: var(--mj-shadow-sm);\n z-index: 10;\n pointer-events: none;\n}\n.processing-indicator i {\n color: var(--mj-brand-primary);\n}\n.processing-indicator span {\n font-size: 0.9rem;\n color: var(--mj-text-primary);\n}\n"] }]
3232
+ }], () => [{ type: i1.DialogService }, { type: i2.ToastService }, { type: i3.ConversationAgentService }, { type: i4.DataCacheService }, { type: i5.ActiveTasksService }, { type: i6.ConversationStreamingService }, { type: i7.MentionParserService }, { type: i8.ConversationAttachmentService }, { type: i9.ConversationBridgeService }, { type: i10.RealtimeSessionService }], { ConversationId: [{
3233
+ type: Input
3234
+ }], conversationId: [{
3235
+ type: Input
3236
+ }], ConversationName: [{
2508
3237
  type: Input
2509
3238
  }], conversationName: [{
2510
3239
  type: Input
3240
+ }], CurrentUser: [{
3241
+ type: Input
2511
3242
  }], currentUser: [{
2512
3243
  type: Input
3244
+ }], Disabled: [{
3245
+ type: Input
2513
3246
  }], disabled: [{
2514
3247
  type: Input
3248
+ }], Placeholder: [{
3249
+ type: Input
2515
3250
  }], placeholder: [{
2516
3251
  type: Input
3252
+ }], ParentMessageId: [{
3253
+ type: Input
2517
3254
  }], parentMessageId: [{
2518
3255
  type: Input
3256
+ }], EnableAttachments: [{
3257
+ type: Input
2519
3258
  }], enableAttachments: [{
2520
3259
  type: Input
3260
+ }], EnableMentions: [{
3261
+ type: Input
2521
3262
  }], enableMentions: [{
2522
3263
  type: Input
3264
+ }], EnableAgentMentions: [{
3265
+ type: Input
2523
3266
  }], enableAgentMentions: [{
2524
3267
  type: Input
3268
+ }], EnableEntityMentions: [{
3269
+ type: Input
2525
3270
  }], enableEntityMentions: [{
2526
3271
  type: Input
3272
+ }], EnableSkillCommands: [{
3273
+ type: Input
2527
3274
  }], enableSkillCommands: [{
2528
3275
  type: Input
3276
+ }], EnablePlanMode: [{
3277
+ type: Input
2529
3278
  }], enablePlanMode: [{
2530
3279
  type: Input
3280
+ }], EnableRealtime: [{
3281
+ type: Input
2531
3282
  }], enableRealtime: [{
2532
3283
  type: Input
3284
+ }], MaxAttachments: [{
3285
+ type: Input
2533
3286
  }], maxAttachments: [{
2534
3287
  type: Input
3288
+ }], MaxAttachmentSizeBytes: [{
3289
+ type: Input
2535
3290
  }], maxAttachmentSizeBytes: [{
2536
3291
  type: Input
3292
+ }], AcceptedFileTypes: [{
3293
+ type: Input
2537
3294
  }], acceptedFileTypes: [{
2538
3295
  type: Input
3296
+ }], ArtifactsByDetailId: [{
3297
+ type: Input
2539
3298
  }], artifactsByDetailId: [{
2540
3299
  type: Input
3300
+ }], SystemArtifactsByDetailId: [{
3301
+ type: Input
2541
3302
  }], systemArtifactsByDetailId: [{
2542
3303
  type: Input
3304
+ }], AgentRunsByDetailId: [{
3305
+ type: Input
2543
3306
  }], agentRunsByDetailId: [{
2544
3307
  type: Input
3308
+ }], EmptyStateMode: [{
3309
+ type: Input
2545
3310
  }], emptyStateMode: [{
2546
3311
  type: Input
3312
+ }], AppContext: [{
3313
+ type: Input
2547
3314
  }], appContext: [{
2548
3315
  type: Input
3316
+ }], DefaultAgentId: [{
3317
+ type: Input
2549
3318
  }], defaultAgentId: [{
2550
3319
  type: Input
3320
+ }], ConversationDefaultAgentId: [{
3321
+ type: Input
2551
3322
  }], conversationDefaultAgentId: [{
2552
3323
  type: Input
3324
+ }], AgentConfigurationPresetId: [{
3325
+ type: Input
2553
3326
  }], agentConfigurationPresetId: [{
2554
3327
  type: Input
3328
+ }], InitialMessage: [{
3329
+ type: Input
2555
3330
  }], initialMessage: [{
2556
3331
  type: Input
3332
+ }], InitialAttachments: [{
3333
+ type: Input
2557
3334
  }], initialAttachments: [{
2558
3335
  type: Input
3336
+ }], ConversationHistory: [{
3337
+ type: Input
2559
3338
  }], conversationHistory: [{
2560
3339
  type: Input
3340
+ }], InProgressMessageIds: [{
3341
+ type: Input
2561
3342
  }], inProgressMessageIds: [{
2562
3343
  type: Input
3344
+ }], ApplicationId: [{
3345
+ type: Input
2563
3346
  }], applicationId: [{
2564
3347
  type: Input
3348
+ }], AgentReplyMode: [{
3349
+ type: Input
3350
+ }], AllowedAgentIDs: [{
3351
+ type: Input
3352
+ }], MentionPeople: [{
3353
+ type: Input
3354
+ }], AgentHistoryFrom: [{
3355
+ type: Input
3356
+ }], AgentTurnHandler: [{
3357
+ type: Input
3358
+ }], AutoNameConversation: [{
3359
+ type: Input
3360
+ }], MessageSent: [{
3361
+ type: Output
2565
3362
  }], messageSent: [{
2566
3363
  type: Output
3364
+ }], AgentResponse: [{
3365
+ type: Output
2567
3366
  }], agentResponse: [{
2568
3367
  type: Output
3368
+ }], BeforeAgentTurn: [{
3369
+ type: Output
2569
3370
  }], beforeAgentTurn: [{
2570
3371
  type: Output
3372
+ }], AfterAgentTurn: [{
3373
+ type: Output
2571
3374
  }], afterAgentTurn: [{
2572
3375
  type: Output
3376
+ }], AgentRunDetected: [{
3377
+ type: Output
2573
3378
  }], agentRunDetected: [{
2574
3379
  type: Output
3380
+ }], AgentRunUpdate: [{
3381
+ type: Output
2575
3382
  }], agentRunUpdate: [{
2576
3383
  type: Output
3384
+ }], MessageComplete: [{
3385
+ type: Output
2577
3386
  }], messageComplete: [{
2578
3387
  type: Output
3388
+ }], ArtifactCreated: [{
3389
+ type: Output
2579
3390
  }], artifactCreated: [{
2580
3391
  type: Output
3392
+ }], ConversationRenamed: [{
3393
+ type: Output
2581
3394
  }], conversationRenamed: [{
2582
3395
  type: Output
3396
+ }], IntentCheckStarted: [{
3397
+ type: Output
2583
3398
  }], intentCheckStarted: [{
2584
3399
  type: Output
3400
+ }], IntentCheckCompleted: [{
3401
+ type: Output
2585
3402
  }], intentCheckCompleted: [{
2586
3403
  type: Output
3404
+ }], InitialMessageAutoSendStarted: [{
3405
+ type: Output
2587
3406
  }], initialMessageAutoSendStarted: [{
2588
3407
  type: Output
3408
+ }], InitialMessageAutoSendFailed: [{
3409
+ type: Output
2589
3410
  }], initialMessageAutoSendFailed: [{
2590
3411
  type: Output
3412
+ }], EmptyStateSubmit: [{
3413
+ type: Output
2591
3414
  }], emptyStateSubmit: [{
2592
3415
  type: Output
3416
+ }], UploadStateChanged: [{
3417
+ type: Output
2593
3418
  }], uploadStateChanged: [{
2594
3419
  type: Output
2595
- }], inputBox: [{
3420
+ }], InputBox: [{
2596
3421
  type: ViewChild,
2597
3422
  args: ['inputBox']
3423
+ }], InitialDraft: [{
3424
+ type: Input
2598
3425
  }], initialDraft: [{
2599
3426
  type: Input
3427
+ }], InitialDraftApplied: [{
3428
+ type: Output
2600
3429
  }], initialDraftApplied: [{
2601
3430
  type: Output
2602
3431
  }], DraftStateChanged: [{
@@ -2604,5 +3433,5 @@ export class MessageInputComponent extends BaseAngularComponent {
2604
3433
  }], ComposerBlurred: [{
2605
3434
  type: Output
2606
3435
  }] }); })();
2607
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MessageInputComponent, { className: "MessageInputComponent", filePath: "src/lib/components/message/message-input.component.ts", lineNumber: 45 }); })();
3436
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MessageInputComponent, { className: "MessageInputComponent", filePath: "src/lib/components/message/message-input.component.ts", lineNumber: 48 }); })();
2608
3437
  //# sourceMappingURL=message-input.component.js.map