@memberjunction/ng-conversations 2.104.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (226) hide show
  1. package/README.md +197 -0
  2. package/dist/lib/components/active-tasks/active-tasks-panel.component.d.ts +20 -0
  3. package/dist/lib/components/active-tasks/active-tasks-panel.component.d.ts.map +1 -0
  4. package/dist/lib/components/active-tasks/active-tasks-panel.component.js +125 -0
  5. package/dist/lib/components/active-tasks/active-tasks-panel.component.js.map +1 -0
  6. package/dist/lib/components/agent/active-agent-indicator.component.d.ts +48 -0
  7. package/dist/lib/components/agent/active-agent-indicator.component.d.ts.map +1 -0
  8. package/dist/lib/components/agent/active-agent-indicator.component.js +199 -0
  9. package/dist/lib/components/agent/active-agent-indicator.component.js.map +1 -0
  10. package/dist/lib/components/agent/agent-process-panel.component.d.ts +30 -0
  11. package/dist/lib/components/agent/agent-process-panel.component.d.ts.map +1 -0
  12. package/dist/lib/components/agent/agent-process-panel.component.js +333 -0
  13. package/dist/lib/components/agent/agent-process-panel.component.js.map +1 -0
  14. package/dist/lib/components/artifact/artifact-panel.component.d.ts +22 -0
  15. package/dist/lib/components/artifact/artifact-panel.component.d.ts.map +1 -0
  16. package/dist/lib/components/artifact/artifact-panel.component.js +237 -0
  17. package/dist/lib/components/artifact/artifact-panel.component.js.map +1 -0
  18. package/dist/lib/components/artifact/artifact-upload-modal.component.d.ts +39 -0
  19. package/dist/lib/components/artifact/artifact-upload-modal.component.d.ts.map +1 -0
  20. package/dist/lib/components/artifact/artifact-upload-modal.component.js +384 -0
  21. package/dist/lib/components/artifact/artifact-upload-modal.component.js.map +1 -0
  22. package/dist/lib/components/artifact/artifact-version-history.component.d.ts +28 -0
  23. package/dist/lib/components/artifact/artifact-version-history.component.d.ts.map +1 -0
  24. package/dist/lib/components/artifact/artifact-version-history.component.js +280 -0
  25. package/dist/lib/components/artifact/artifact-version-history.component.js.map +1 -0
  26. package/dist/lib/components/artifact/artifact-viewer-panel.component.d.ts +22 -0
  27. package/dist/lib/components/artifact/artifact-viewer-panel.component.d.ts.map +1 -0
  28. package/dist/lib/components/artifact/artifact-viewer-panel.component.js +182 -0
  29. package/dist/lib/components/artifact/artifact-viewer-panel.component.js.map +1 -0
  30. package/dist/lib/components/artifact/artifact-viewer.component.d.ts +27 -0
  31. package/dist/lib/components/artifact/artifact-viewer.component.d.ts.map +1 -0
  32. package/dist/lib/components/artifact/artifact-viewer.component.js +266 -0
  33. package/dist/lib/components/artifact/artifact-viewer.component.js.map +1 -0
  34. package/dist/lib/components/artifact/inline-artifact.component.d.ts +46 -0
  35. package/dist/lib/components/artifact/inline-artifact.component.d.ts.map +1 -0
  36. package/dist/lib/components/artifact/inline-artifact.component.js +447 -0
  37. package/dist/lib/components/artifact/inline-artifact.component.js.map +1 -0
  38. package/dist/lib/components/collection/collection-artifact-card.component.d.ts +18 -0
  39. package/dist/lib/components/collection/collection-artifact-card.component.d.ts.map +1 -0
  40. package/dist/lib/components/collection/collection-artifact-card.component.js +147 -0
  41. package/dist/lib/components/collection/collection-artifact-card.component.js.map +1 -0
  42. package/dist/lib/components/collection/collection-form-modal.component.d.ts +33 -0
  43. package/dist/lib/components/collection/collection-form-modal.component.d.ts.map +1 -0
  44. package/dist/lib/components/collection/collection-form-modal.component.js +245 -0
  45. package/dist/lib/components/collection/collection-form-modal.component.js.map +1 -0
  46. package/dist/lib/components/collection/collection-tree.component.d.ts +42 -0
  47. package/dist/lib/components/collection/collection-tree.component.d.ts.map +1 -0
  48. package/dist/lib/components/collection/collection-tree.component.js +482 -0
  49. package/dist/lib/components/collection/collection-tree.component.js.map +1 -0
  50. package/dist/lib/components/collection/collection-view.component.d.ts +31 -0
  51. package/dist/lib/components/collection/collection-view.component.d.ts.map +1 -0
  52. package/dist/lib/components/collection/collection-view.component.js +254 -0
  53. package/dist/lib/components/collection/collection-view.component.js.map +1 -0
  54. package/dist/lib/components/collection/collections-full-view.component.d.ts +55 -0
  55. package/dist/lib/components/collection/collections-full-view.component.d.ts.map +1 -0
  56. package/dist/lib/components/collection/collections-full-view.component.js +578 -0
  57. package/dist/lib/components/collection/collections-full-view.component.js.map +1 -0
  58. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +160 -0
  59. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -0
  60. package/dist/lib/components/conversation/conversation-chat-area.component.js +891 -0
  61. package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -0
  62. package/dist/lib/components/conversation/conversation-list.component.d.ts +29 -0
  63. package/dist/lib/components/conversation/conversation-list.component.d.ts.map +1 -0
  64. package/dist/lib/components/conversation/conversation-list.component.js +255 -0
  65. package/dist/lib/components/conversation/conversation-list.component.js.map +1 -0
  66. package/dist/lib/components/dialogs/input-dialog.component.d.ts +17 -0
  67. package/dist/lib/components/dialogs/input-dialog.component.d.ts.map +1 -0
  68. package/dist/lib/components/dialogs/input-dialog.component.js +122 -0
  69. package/dist/lib/components/dialogs/input-dialog.component.js.map +1 -0
  70. package/dist/lib/components/export/export-modal.component.d.ts +37 -0
  71. package/dist/lib/components/export/export-modal.component.d.ts.map +1 -0
  72. package/dist/lib/components/export/export-modal.component.js +414 -0
  73. package/dist/lib/components/export/export-modal.component.js.map +1 -0
  74. package/dist/lib/components/library/library-full-view.component.d.ts +36 -0
  75. package/dist/lib/components/library/library-full-view.component.d.ts.map +1 -0
  76. package/dist/lib/components/library/library-full-view.component.js +270 -0
  77. package/dist/lib/components/library/library-full-view.component.js.map +1 -0
  78. package/dist/lib/components/members/members-modal.component.d.ts +42 -0
  79. package/dist/lib/components/members/members-modal.component.d.ts.map +1 -0
  80. package/dist/lib/components/members/members-modal.component.js +352 -0
  81. package/dist/lib/components/members/members-modal.component.js.map +1 -0
  82. package/dist/lib/components/mention/mention-dropdown.component.d.ts +44 -0
  83. package/dist/lib/components/mention/mention-dropdown.component.d.ts.map +1 -0
  84. package/dist/lib/components/mention/mention-dropdown.component.js +194 -0
  85. package/dist/lib/components/mention/mention-dropdown.component.js.map +1 -0
  86. package/dist/lib/components/message/message-input.component.d.ts +137 -0
  87. package/dist/lib/components/message/message-input.component.d.ts.map +1 -0
  88. package/dist/lib/components/message/message-input.component.js +1159 -0
  89. package/dist/lib/components/message/message-input.component.js.map +1 -0
  90. package/dist/lib/components/message/message-item.component.d.ts +140 -0
  91. package/dist/lib/components/message/message-item.component.d.ts.map +1 -0
  92. package/dist/lib/components/message/message-item.component.js +817 -0
  93. package/dist/lib/components/message/message-item.component.js.map +1 -0
  94. package/dist/lib/components/message/message-list.component.d.ts +77 -0
  95. package/dist/lib/components/message/message-list.component.d.ts.map +1 -0
  96. package/dist/lib/components/message/message-list.component.js +316 -0
  97. package/dist/lib/components/message/message-list.component.js.map +1 -0
  98. package/dist/lib/components/navigation/conversation-navigation.component.d.ts +13 -0
  99. package/dist/lib/components/navigation/conversation-navigation.component.d.ts.map +1 -0
  100. package/dist/lib/components/navigation/conversation-navigation.component.js +88 -0
  101. package/dist/lib/components/navigation/conversation-navigation.component.js.map +1 -0
  102. package/dist/lib/components/notification/activity-indicator.component.d.ts +11 -0
  103. package/dist/lib/components/notification/activity-indicator.component.d.ts.map +1 -0
  104. package/dist/lib/components/notification/activity-indicator.component.js +56 -0
  105. package/dist/lib/components/notification/activity-indicator.component.js.map +1 -0
  106. package/dist/lib/components/notification/notification-badge.component.d.ts +27 -0
  107. package/dist/lib/components/notification/notification-badge.component.d.ts.map +1 -0
  108. package/dist/lib/components/notification/notification-badge.component.js +160 -0
  109. package/dist/lib/components/notification/notification-badge.component.js.map +1 -0
  110. package/dist/lib/components/project/project-form-modal.component.d.ts +34 -0
  111. package/dist/lib/components/project/project-form-modal.component.d.ts.map +1 -0
  112. package/dist/lib/components/project/project-form-modal.component.js +357 -0
  113. package/dist/lib/components/project/project-form-modal.component.js.map +1 -0
  114. package/dist/lib/components/project/project-selector.component.d.ts +36 -0
  115. package/dist/lib/components/project/project-selector.component.d.ts.map +1 -0
  116. package/dist/lib/components/project/project-selector.component.js +317 -0
  117. package/dist/lib/components/project/project-selector.component.js.map +1 -0
  118. package/dist/lib/components/search/search-panel.component.d.ts +120 -0
  119. package/dist/lib/components/search/search-panel.component.d.ts.map +1 -0
  120. package/dist/lib/components/search/search-panel.component.js +714 -0
  121. package/dist/lib/components/search/search-panel.component.js.map +1 -0
  122. package/dist/lib/components/share/share-modal.component.d.ts +46 -0
  123. package/dist/lib/components/share/share-modal.component.d.ts.map +1 -0
  124. package/dist/lib/components/share/share-modal.component.js +431 -0
  125. package/dist/lib/components/share/share-modal.component.js.map +1 -0
  126. package/dist/lib/components/sidebar/conversation-sidebar.component.d.ts +18 -0
  127. package/dist/lib/components/sidebar/conversation-sidebar.component.d.ts.map +1 -0
  128. package/dist/lib/components/sidebar/conversation-sidebar.component.js +81 -0
  129. package/dist/lib/components/sidebar/conversation-sidebar.component.js.map +1 -0
  130. package/dist/lib/components/task/task-form-modal.component.d.ts +42 -0
  131. package/dist/lib/components/task/task-form-modal.component.d.ts.map +1 -0
  132. package/dist/lib/components/task/task-form-modal.component.js +329 -0
  133. package/dist/lib/components/task/task-form-modal.component.js.map +1 -0
  134. package/dist/lib/components/task/task-item.component.d.ts +22 -0
  135. package/dist/lib/components/task/task-item.component.d.ts.map +1 -0
  136. package/dist/lib/components/task/task-item.component.js +234 -0
  137. package/dist/lib/components/task/task-item.component.js.map +1 -0
  138. package/dist/lib/components/task/task-list.component.d.ts +32 -0
  139. package/dist/lib/components/task/task-list.component.d.ts.map +1 -0
  140. package/dist/lib/components/task/task-list.component.js +290 -0
  141. package/dist/lib/components/task/task-list.component.js.map +1 -0
  142. package/dist/lib/components/tasks/tasks-dropdown.component.d.ts +27 -0
  143. package/dist/lib/components/tasks/tasks-dropdown.component.d.ts.map +1 -0
  144. package/dist/lib/components/tasks/tasks-dropdown.component.js +254 -0
  145. package/dist/lib/components/tasks/tasks-dropdown.component.js.map +1 -0
  146. package/dist/lib/components/thread/thread-panel.component.d.ts +65 -0
  147. package/dist/lib/components/thread/thread-panel.component.d.ts.map +1 -0
  148. package/dist/lib/components/thread/thread-panel.component.js +325 -0
  149. package/dist/lib/components/thread/thread-panel.component.js.map +1 -0
  150. package/dist/lib/components/toast/toast.component.d.ts +26 -0
  151. package/dist/lib/components/toast/toast.component.d.ts.map +1 -0
  152. package/dist/lib/components/toast/toast.component.js +108 -0
  153. package/dist/lib/components/toast/toast.component.js.map +1 -0
  154. package/dist/lib/components/workspace/conversation-workspace.component.d.ts +75 -0
  155. package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -0
  156. package/dist/lib/components/workspace/conversation-workspace.component.js +299 -0
  157. package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -0
  158. package/dist/lib/conversations.module.d.ts +62 -0
  159. package/dist/lib/conversations.module.d.ts.map +1 -0
  160. package/dist/lib/conversations.module.js +248 -0
  161. package/dist/lib/conversations.module.js.map +1 -0
  162. package/dist/lib/directives/search-shortcut.directive.d.ts +17 -0
  163. package/dist/lib/directives/search-shortcut.directive.d.ts.map +1 -0
  164. package/dist/lib/directives/search-shortcut.directive.js +39 -0
  165. package/dist/lib/directives/search-shortcut.directive.js.map +1 -0
  166. package/dist/lib/models/conversation-state.model.d.ts +72 -0
  167. package/dist/lib/models/conversation-state.model.d.ts.map +1 -0
  168. package/dist/lib/models/conversation-state.model.js +2 -0
  169. package/dist/lib/models/conversation-state.model.js.map +1 -0
  170. package/dist/lib/models/notification.model.d.ts +89 -0
  171. package/dist/lib/models/notification.model.d.ts.map +1 -0
  172. package/dist/lib/models/notification.model.js +11 -0
  173. package/dist/lib/models/notification.model.js.map +1 -0
  174. package/dist/lib/services/active-tasks.service.d.ts +65 -0
  175. package/dist/lib/services/active-tasks.service.d.ts.map +1 -0
  176. package/dist/lib/services/active-tasks.service.js +95 -0
  177. package/dist/lib/services/active-tasks.service.js.map +1 -0
  178. package/dist/lib/services/agent-state.service.d.ts +78 -0
  179. package/dist/lib/services/agent-state.service.d.ts.map +1 -0
  180. package/dist/lib/services/agent-state.service.js +213 -0
  181. package/dist/lib/services/agent-state.service.js.map +1 -0
  182. package/dist/lib/services/artifact-state.service.d.ts +114 -0
  183. package/dist/lib/services/artifact-state.service.d.ts.map +1 -0
  184. package/dist/lib/services/artifact-state.service.js +288 -0
  185. package/dist/lib/services/artifact-state.service.js.map +1 -0
  186. package/dist/lib/services/conversation-agent.service.d.ts +79 -0
  187. package/dist/lib/services/conversation-agent.service.d.ts.map +1 -0
  188. package/dist/lib/services/conversation-agent.service.js +259 -0
  189. package/dist/lib/services/conversation-agent.service.js.map +1 -0
  190. package/dist/lib/services/conversation-state.service.d.ts +122 -0
  191. package/dist/lib/services/conversation-state.service.d.ts.map +1 -0
  192. package/dist/lib/services/conversation-state.service.js +255 -0
  193. package/dist/lib/services/conversation-state.service.js.map +1 -0
  194. package/dist/lib/services/dialog.service.d.ts +54 -0
  195. package/dist/lib/services/dialog.service.d.ts.map +1 -0
  196. package/dist/lib/services/dialog.service.js +157 -0
  197. package/dist/lib/services/dialog.service.js.map +1 -0
  198. package/dist/lib/services/export.service.d.ts +25 -0
  199. package/dist/lib/services/export.service.d.ts.map +1 -0
  200. package/dist/lib/services/export.service.js +237 -0
  201. package/dist/lib/services/export.service.js.map +1 -0
  202. package/dist/lib/services/mention-autocomplete.service.d.ts +59 -0
  203. package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -0
  204. package/dist/lib/services/mention-autocomplete.service.js +160 -0
  205. package/dist/lib/services/mention-autocomplete.service.js.map +1 -0
  206. package/dist/lib/services/mention-parser.service.d.ts +46 -0
  207. package/dist/lib/services/mention-parser.service.d.ts.map +1 -0
  208. package/dist/lib/services/mention-parser.service.js +156 -0
  209. package/dist/lib/services/mention-parser.service.js.map +1 -0
  210. package/dist/lib/services/notification.service.d.ts +108 -0
  211. package/dist/lib/services/notification.service.d.ts.map +1 -0
  212. package/dist/lib/services/notification.service.js +431 -0
  213. package/dist/lib/services/notification.service.js.map +1 -0
  214. package/dist/lib/services/search.service.d.ts +144 -0
  215. package/dist/lib/services/search.service.d.ts.map +1 -0
  216. package/dist/lib/services/search.service.js +370 -0
  217. package/dist/lib/services/search.service.js.map +1 -0
  218. package/dist/lib/services/toast.service.d.ts +46 -0
  219. package/dist/lib/services/toast.service.d.ts.map +1 -0
  220. package/dist/lib/services/toast.service.js +76 -0
  221. package/dist/lib/services/toast.service.js.map +1 -0
  222. package/dist/public-api.d.ts +42 -0
  223. package/dist/public-api.d.ts.map +1 -0
  224. package/dist/public-api.js +49 -0
  225. package/dist/public-api.js.map +1 -0
  226. package/package.json +54 -0
@@ -0,0 +1,1159 @@
1
+ import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
2
+ import { Metadata, RunView } from '@memberjunction/core';
3
+ import { GraphQLAIClient } from '@memberjunction/graphql-dataprovider';
4
+ import { AIEngineBase } from '@memberjunction/ai-engine-base';
5
+ import * as i0 from "@angular/core";
6
+ import * as i1 from "../../services/dialog.service";
7
+ import * as i2 from "../../services/toast.service";
8
+ import * as i3 from "../../services/conversation-agent.service";
9
+ import * as i4 from "../../services/conversation-state.service";
10
+ import * as i5 from "../../services/active-tasks.service";
11
+ import * as i6 from "../../services/mention-autocomplete.service";
12
+ import * as i7 from "../../services/mention-parser.service";
13
+ import * as i8 from "@angular/common";
14
+ import * as i9 from "@angular/forms";
15
+ import * as i10 from "../mention/mention-dropdown.component";
16
+ const _c0 = ["messageTextarea"];
17
+ function MessageInputComponent_div_6_Template(rf, ctx) { if (rf & 1) {
18
+ i0.ɵɵelementStart(0, "div", 10);
19
+ i0.ɵɵelement(1, "i", 11);
20
+ i0.ɵɵelementStart(2, "span");
21
+ i0.ɵɵtext(3, "AI is responding...");
22
+ i0.ɵɵelementEnd()();
23
+ } }
24
+ export class MessageInputComponent {
25
+ dialogService;
26
+ toastService;
27
+ agentService;
28
+ conversationState;
29
+ activeTasks;
30
+ mentionAutocomplete;
31
+ mentionParser;
32
+ conversationId;
33
+ currentUser;
34
+ disabled = false;
35
+ placeholder = 'Type a message... (Ctrl+Enter to send)';
36
+ parentMessageId; // Optional: for replying in threads
37
+ conversationHistory = []; // For agent context
38
+ messageSent = new EventEmitter();
39
+ agentResponse = new EventEmitter();
40
+ agentRunDetected = new EventEmitter();
41
+ artifactCreated = new EventEmitter();
42
+ conversationRenamed = new EventEmitter();
43
+ messageTextarea;
44
+ messageText = '';
45
+ isSending = false;
46
+ isProcessing = false; // True when waiting for agent/naming response
47
+ converationManagerAgent = null;
48
+ // Mention autocomplete state
49
+ showMentionDropdown = false;
50
+ mentionSuggestions = [];
51
+ mentionDropdownPosition = { top: 0, left: 0 };
52
+ mentionDropdownShowAbove = false; // Controls transform direction
53
+ mentionStartIndex = -1;
54
+ mentionQuery = '';
55
+ constructor(dialogService, toastService, agentService, conversationState, activeTasks, mentionAutocomplete, mentionParser) {
56
+ this.dialogService = dialogService;
57
+ this.toastService = toastService;
58
+ this.agentService = agentService;
59
+ this.conversationState = conversationState;
60
+ this.activeTasks = activeTasks;
61
+ this.mentionAutocomplete = mentionAutocomplete;
62
+ this.mentionParser = mentionParser;
63
+ }
64
+ async ngOnInit() {
65
+ this.converationManagerAgent = await this.agentService.getConversationManagerAgent();
66
+ // Initialize mention autocomplete
67
+ await this.mentionAutocomplete.initialize(this.currentUser);
68
+ }
69
+ get canSend() {
70
+ return !this.disabled && !this.isSending && this.messageText.trim().length > 0;
71
+ }
72
+ /**
73
+ * Handle input events to detect @ mentions
74
+ */
75
+ onInput(event) {
76
+ const textarea = event.target;
77
+ const cursorPos = textarea.selectionStart;
78
+ const text = textarea.value;
79
+ // Check if we're typing after an @ symbol
80
+ const textBeforeCursor = text.substring(0, cursorPos);
81
+ const mentionMatch = textBeforeCursor.match(/@(\S*)$/);
82
+ if (mentionMatch) {
83
+ // We found an @ mention being typed
84
+ this.mentionStartIndex = cursorPos - mentionMatch[0].length;
85
+ this.mentionQuery = mentionMatch[1] || '';
86
+ console.log('[MentionInput] Detected @mention:', this.mentionQuery);
87
+ // Get suggestions
88
+ this.mentionSuggestions = this.mentionAutocomplete.getSuggestions(this.mentionQuery);
89
+ console.log('[MentionInput] Got suggestions:', this.mentionSuggestions.length, this.mentionSuggestions);
90
+ // Calculate dropdown position
91
+ this.calculateDropdownPosition(textarea);
92
+ // Show dropdown if we have suggestions OR to show empty state
93
+ this.showMentionDropdown = true;
94
+ console.log('[MentionInput] Showing dropdown:', this.showMentionDropdown);
95
+ }
96
+ else {
97
+ // No @ mention, close dropdown
98
+ this.closeMentionDropdown();
99
+ }
100
+ }
101
+ /**
102
+ * Handle keydown events in the textarea
103
+ * - Enter alone: Send message (unless dropdown is open)
104
+ * - Shift+Enter: Add new line
105
+ * - Arrow keys, Tab, Escape: Handle mention dropdown if open
106
+ */
107
+ onKeyDown(event) {
108
+ // If mention dropdown is open, let it handle certain keys
109
+ if (this.showMentionDropdown) {
110
+ if (['ArrowDown', 'ArrowUp', 'Enter', 'Tab', 'Escape'].includes(event.key)) {
111
+ // These keys are handled by the dropdown component
112
+ return;
113
+ }
114
+ }
115
+ // Regular key handling
116
+ if (event.key === 'Enter' && !event.shiftKey) {
117
+ // Prevent default behavior (adding newline)
118
+ event.preventDefault();
119
+ // Send the message
120
+ this.onSend();
121
+ }
122
+ // If Shift+Enter, allow default behavior (add newline)
123
+ }
124
+ /**
125
+ * Calculate position for mention dropdown
126
+ * Keeps dropdown anchored to textarea edge regardless of content size
127
+ */
128
+ calculateDropdownPosition(textarea) {
129
+ const rect = textarea.getBoundingClientRect();
130
+ const container = textarea.closest('.message-input-container');
131
+ const containerRect = container?.getBoundingClientRect();
132
+ if (!containerRect) {
133
+ // Fallback to absolute positioning
134
+ this.mentionDropdownPosition = {
135
+ top: rect.bottom + window.scrollY + 4,
136
+ left: rect.left + window.scrollX
137
+ };
138
+ return;
139
+ }
140
+ // Check if there's enough space below the textarea
141
+ const spaceBelow = window.innerHeight - rect.bottom;
142
+ const spaceAbove = rect.top;
143
+ this.mentionDropdownShowAbove = spaceBelow < 200 && spaceAbove > spaceBelow;
144
+ // Position relative to the container
145
+ // Always anchor to the textarea edge so dropdown stays in place as content changes
146
+ if (this.mentionDropdownShowAbove) {
147
+ // Show above the textarea - anchor to the TOP of the textarea
148
+ // CSS transform will make it grow upward from this anchor point
149
+ this.mentionDropdownPosition = {
150
+ top: rect.top - containerRect.top - 4, // Anchor just above textarea
151
+ left: rect.left - containerRect.left
152
+ };
153
+ }
154
+ else {
155
+ // Show below the textarea (default) - anchor to the BOTTOM of the textarea
156
+ this.mentionDropdownPosition = {
157
+ top: rect.bottom - containerRect.top + 4,
158
+ left: rect.left - containerRect.left
159
+ };
160
+ }
161
+ }
162
+ /**
163
+ * Handle mention suggestion selection
164
+ */
165
+ onMentionSelected(suggestion) {
166
+ if (this.mentionStartIndex === -1)
167
+ return;
168
+ const textarea = this.messageTextarea.nativeElement;
169
+ const cursorPos = textarea.selectionStart;
170
+ // Replace the @mention text with the selected name
171
+ const beforeMention = this.messageText.substring(0, this.mentionStartIndex);
172
+ const afterMention = this.messageText.substring(cursorPos);
173
+ // If name has spaces, wrap in quotes
174
+ const mentionText = suggestion.displayName.includes(' ')
175
+ ? `@"${suggestion.displayName}" `
176
+ : `@${suggestion.displayName} `;
177
+ this.messageText = beforeMention + mentionText + afterMention;
178
+ // Close dropdown
179
+ this.closeMentionDropdown();
180
+ // Set cursor position after the mention
181
+ const newCursorPos = beforeMention.length + mentionText.length;
182
+ setTimeout(() => {
183
+ textarea.selectionStart = newCursorPos;
184
+ textarea.selectionEnd = newCursorPos;
185
+ textarea.focus();
186
+ }, 0);
187
+ }
188
+ /**
189
+ * Close mention dropdown
190
+ */
191
+ closeMentionDropdown() {
192
+ this.showMentionDropdown = false;
193
+ this.mentionSuggestions = [];
194
+ this.mentionStartIndex = -1;
195
+ this.mentionQuery = '';
196
+ }
197
+ async onSend() {
198
+ if (!this.canSend)
199
+ return;
200
+ this.isSending = true;
201
+ try {
202
+ const md = new Metadata();
203
+ const detail = await md.GetEntityObject('Conversation Details', this.currentUser);
204
+ detail.ConversationID = this.conversationId;
205
+ detail.Message = this.messageText.trim();
206
+ detail.Role = 'User';
207
+ // Parse mentions from message (not stored, used for routing only)
208
+ const mentionResult = this.mentionParser.parseMentions(detail.Message, this.mentionAutocomplete.getAvailableAgents(), this.mentionAutocomplete.getAvailableUsers());
209
+ console.log('[MentionInput] Parsing message for routing:', detail.Message);
210
+ console.log('[MentionInput] Found mentions:', mentionResult);
211
+ console.log('[MentionInput] Agent mention:', mentionResult.agentMention);
212
+ // Set ParentID if this is a thread reply
213
+ if (this.parentMessageId) {
214
+ detail.ParentID = this.parentMessageId;
215
+ }
216
+ const saved = await detail.Save();
217
+ if (saved) {
218
+ this.messageSent.emit(detail);
219
+ this.messageText = '';
220
+ // Check if this is the first message in the conversation
221
+ const isFirstMessage = this.conversationHistory.length === 0;
222
+ // Determine routing: @mention > last agent context > Conversation Manager
223
+ if (mentionResult.agentMention) {
224
+ // Direct @mention - skip Conversation Manager, invoke agent directly
225
+ console.log('🎯 Direct @mention detected, bypassing Conversation Manager');
226
+ if (isFirstMessage) {
227
+ Promise.all([
228
+ this.invokeAgentDirectly(detail, mentionResult.agentMention, this.conversationId),
229
+ this.nameConversation(detail.Message)
230
+ ]);
231
+ }
232
+ else {
233
+ this.invokeAgentDirectly(detail, mentionResult.agentMention, this.conversationId);
234
+ }
235
+ }
236
+ else {
237
+ // Check if user is replying to an agent (implicit continuation)
238
+ const lastAIMessage = this.conversationHistory
239
+ .slice()
240
+ .reverse()
241
+ .find(msg => msg.Role === 'AI' &&
242
+ msg.AgentID &&
243
+ msg.AgentID !== this.converationManagerAgent?.ID);
244
+ if (lastAIMessage && lastAIMessage.AgentID) {
245
+ // Continue with same agent - skip Conversation Manager
246
+ console.log('🔄 Implicit continuation detected, continuing with last agent');
247
+ if (isFirstMessage) {
248
+ Promise.all([
249
+ this.continueWithAgent(detail, lastAIMessage.AgentID, this.conversationId),
250
+ this.nameConversation(detail.Message)
251
+ ]);
252
+ }
253
+ else {
254
+ this.continueWithAgent(detail, lastAIMessage.AgentID, this.conversationId);
255
+ }
256
+ }
257
+ else {
258
+ // No context - use Conversation Manager
259
+ console.log('🤖 No agent context, using Conversation Manager');
260
+ if (isFirstMessage) {
261
+ Promise.all([
262
+ this.processMessageThroughAgent(detail, mentionResult),
263
+ this.nameConversation(detail.Message)
264
+ ]);
265
+ }
266
+ else {
267
+ this.processMessageThroughAgent(detail, mentionResult);
268
+ }
269
+ }
270
+ }
271
+ // Focus back on textarea
272
+ setTimeout(() => {
273
+ if (this.messageTextarea && this.messageTextarea.nativeElement) {
274
+ this.messageTextarea.nativeElement.focus();
275
+ }
276
+ }, 100);
277
+ }
278
+ else {
279
+ console.error('Failed to send message:', detail.LatestResult?.Message);
280
+ this.toastService.error('Failed to send message. Please try again.');
281
+ }
282
+ }
283
+ catch (error) {
284
+ console.error('Error sending message:', error);
285
+ this.toastService.error('Error sending message. Please try again.');
286
+ }
287
+ finally {
288
+ this.isSending = false;
289
+ }
290
+ }
291
+ /**
292
+ * Create a progress callback for agent execution
293
+ * This callback updates both the active task and the ConversationDetail message
294
+ * IMPORTANT: Filters by agentRunId to prevent cross-contamination when multiple agents run in parallel
295
+ */
296
+ createProgressCallback(conversationDetailId, agentName) {
297
+ return async (progress) => {
298
+ // Extract agentRunId from progress metadata
299
+ const progressAgentRunId = progress.metadata?.agentRunId;
300
+ // Format progress message with visual indicator
301
+ const progressText = progress.message;
302
+ // Update the active task with progress details (if it exists)
303
+ this.activeTasks.updateStatusByConversationDetailId(conversationDetailId, progressText);
304
+ // Update the ConversationDetail message in real-time
305
+ try {
306
+ const md = new Metadata();
307
+ const conversationDetail = await md.GetEntityObject('Conversation Details', this.currentUser);
308
+ if (await conversationDetail.Load(conversationDetailId)) {
309
+ // Skip progress updates if message is already complete
310
+ if (conversationDetail.Status === 'Complete') {
311
+ console.log(`[${agentName}] Skipping progress update - message already complete`);
312
+ return;
313
+ }
314
+ // CRITICAL: Only update if agentRunIds match (prevents cross-contamination)
315
+ const storedAgentRunId = conversationDetail.AgentRunID;
316
+ // If AgentRunID not yet set, set it from the first progress update
317
+ if (!storedAgentRunId && progressAgentRunId) {
318
+ conversationDetail.AgentRunID = progressAgentRunId;
319
+ console.log(`[${agentName}] Setting AgentRunID from progress update:`, progressAgentRunId);
320
+ // Emit event to parent so it can load the agent run into the map immediately
321
+ this.agentRunDetected.emit({
322
+ conversationDetailId,
323
+ agentRunId: progressAgentRunId
324
+ });
325
+ }
326
+ // If we have both IDs, verify they match before updating
327
+ else if (progressAgentRunId && storedAgentRunId) {
328
+ if (progressAgentRunId !== storedAgentRunId) {
329
+ console.log(`[${agentName}] Skipping progress update - agentRunId mismatch:`, {
330
+ progressAgentRunId,
331
+ storedAgentRunId,
332
+ conversationDetailId
333
+ });
334
+ return; // Skip this update - it's for a different agent run
335
+ }
336
+ }
337
+ conversationDetail.Message = progressText;
338
+ await conversationDetail.Save();
339
+ // Emit update to trigger UI refresh
340
+ this.messageSent.emit(conversationDetail);
341
+ }
342
+ }
343
+ catch (error) {
344
+ console.warn('Failed to save progress update to ConversationDetail:', error);
345
+ }
346
+ console.log(`[${agentName}] Progress: ${progress.step} - ${progress.message} (${progress.percentage}%)`, {
347
+ agentRunId: progressAgentRunId,
348
+ conversationDetailId
349
+ });
350
+ };
351
+ }
352
+ /**
353
+ * Process the message through agents (multi-stage: Conversation Manager -> possible sub-agent)
354
+ * Only called when there's no @mention and no implicit agent context
355
+ */
356
+ async processMessageThroughAgent(userMessage, mentionResult) {
357
+ let taskId = null;
358
+ let conversationManagerMessage = null;
359
+ // CRITICAL: Capture conversationId from user message at start
360
+ // This prevents race condition when user switches conversations during async processing
361
+ const conversationId = userMessage.ConversationID;
362
+ try {
363
+ // Create AI message for Conversation Manager BEFORE invoking
364
+ const md = new Metadata();
365
+ conversationManagerMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
366
+ conversationManagerMessage.ConversationID = conversationId;
367
+ conversationManagerMessage.Role = 'AI';
368
+ conversationManagerMessage.Message = '⏳ Starting...';
369
+ conversationManagerMessage.ParentID = userMessage.ID;
370
+ conversationManagerMessage.Status = 'In-Progress';
371
+ conversationManagerMessage.HiddenToUser = false;
372
+ // Use the preloaded Conversation Manager agent instead of looking it up
373
+ if (this.converationManagerAgent?.ID) {
374
+ conversationManagerMessage.AgentID = this.converationManagerAgent.ID;
375
+ }
376
+ await conversationManagerMessage.Save();
377
+ this.messageSent.emit(conversationManagerMessage);
378
+ // Use Conversation Manager to evaluate and route
379
+ // Stage 1: Conversation Manager evaluates the message
380
+ taskId = this.activeTasks.add({
381
+ agentName: 'Conversation Manager',
382
+ status: 'Evaluating message...',
383
+ relatedMessageId: userMessage.ID,
384
+ conversationDetailId: conversationManagerMessage.ID
385
+ });
386
+ const result = await this.agentService.processMessage(conversationId, userMessage, this.conversationHistory, conversationManagerMessage.ID, this.createProgressCallback(conversationManagerMessage.ID, 'Conversation Manager'));
387
+ // Remove Conversation Manager from active tasks
388
+ if (taskId) {
389
+ this.activeTasks.remove(taskId);
390
+ taskId = null;
391
+ }
392
+ if (!result || !result.success) {
393
+ // Evaluation failed
394
+ conversationManagerMessage.Status = 'Error';
395
+ conversationManagerMessage.Message = `❌ Evaluation failed`;
396
+ conversationManagerMessage.Error = result?.agentRun?.ErrorMessage || 'Agent evaluation failed';
397
+ await conversationManagerMessage.Save();
398
+ this.messageSent.emit(conversationManagerMessage);
399
+ userMessage.Status = 'Complete';
400
+ await userMessage.Save();
401
+ this.messageSent.emit(userMessage);
402
+ console.warn('⚠️ Conversation Manager failed:', result?.agentRun?.ErrorMessage);
403
+ return;
404
+ }
405
+ console.log('🤖 Conversation Manager Response:', {
406
+ finalStep: result.agentRun.FinalStep,
407
+ hasPayload: !!result.payload,
408
+ hasMessage: !!result.agentRun.Message
409
+ });
410
+ // Stage 2: Check for sub-agent invocation
411
+ if (result.agentRun.FinalStep === 'Success' && result.payload?.invokeAgent) {
412
+ // Reuse the existing conversationManagerMessage instead of creating new ones
413
+ await this.handleSubAgentInvocation(userMessage, result, this.conversationId, conversationManagerMessage);
414
+ }
415
+ // Stage 3: Direct chat response from Conversation Manager
416
+ else if (result.agentRun.FinalStep === 'Chat' && result.agentRun.Message) {
417
+ // Update the existing message with the response
418
+ conversationManagerMessage.Message = result.agentRun.Message;
419
+ conversationManagerMessage.Status = 'Complete';
420
+ if (result.agentRun.ID) {
421
+ conversationManagerMessage.AgentRunID = result.agentRun.ID;
422
+ }
423
+ await conversationManagerMessage.Save();
424
+ this.messageSent.emit(conversationManagerMessage);
425
+ // Handle artifacts if any
426
+ if (result.payload && Object.keys(result.payload).length > 0) {
427
+ await this.createArtifactFromPayload(result.payload, conversationManagerMessage, result.agentRun.AgentID);
428
+ console.log('🎨 Artifact created and linked to Conversation Manager message');
429
+ this.messageSent.emit(conversationManagerMessage);
430
+ }
431
+ userMessage.Status = 'Complete';
432
+ await userMessage.Save();
433
+ this.messageSent.emit(userMessage);
434
+ }
435
+ // Stage 4: Silent observation - check for agent continuity
436
+ else {
437
+ console.log('🔇 Conversation Manager chose to observe silently');
438
+ // Hide the Conversation Manager message
439
+ conversationManagerMessage.HiddenToUser = true;
440
+ conversationManagerMessage.Status = 'Complete';
441
+ await conversationManagerMessage.Save();
442
+ this.messageSent.emit(conversationManagerMessage);
443
+ await this.handleSilentObservation(userMessage, this.conversationId);
444
+ }
445
+ }
446
+ catch (error) {
447
+ console.error('❌ Error processing message through agents:', error);
448
+ // Update conversationManagerMessage status to Error
449
+ if (conversationManagerMessage && conversationManagerMessage.ID) {
450
+ conversationManagerMessage.Status = 'Error';
451
+ conversationManagerMessage.Message = `❌ Error: ${String(error)}`;
452
+ conversationManagerMessage.Error = String(error);
453
+ await conversationManagerMessage.Save();
454
+ this.messageSent.emit(conversationManagerMessage);
455
+ }
456
+ // Mark user message as complete
457
+ userMessage.Status = 'Complete';
458
+ await userMessage.Save();
459
+ this.messageSent.emit(userMessage);
460
+ // Clean up active task
461
+ if (taskId) {
462
+ this.activeTasks.remove(taskId);
463
+ }
464
+ }
465
+ }
466
+ /**
467
+ * Handle sub-agent invocation based on Conversation Manager's payload
468
+ * Reuses the existing conversationManagerMessage to avoid creating multiple records
469
+ */
470
+ async handleSubAgentInvocation(userMessage, managerResult, conversationId, conversationManagerMessage) {
471
+ const payload = managerResult.payload;
472
+ const agentName = payload.invokeAgent;
473
+ const reasoning = payload.reasoning || 'Delegating to specialist agent';
474
+ console.log(`👉 Sub-agent invocation requested: ${agentName}`, { reasoning });
475
+ // Update the existing Conversation Manager message to show delegation
476
+ conversationManagerMessage.Message = `👉 **${agentName}** will handle this request...`;
477
+ conversationManagerMessage.Status = 'Complete';
478
+ // Keep AgentID as Conversation Manager (already set)
479
+ if (managerResult.agentRun.ID) {
480
+ conversationManagerMessage.AgentRunID = managerResult.agentRun.ID;
481
+ }
482
+ await conversationManagerMessage.Save();
483
+ this.messageSent.emit(conversationManagerMessage);
484
+ // Now create a NEW message for the sub-agent execution
485
+ try {
486
+ // Look up the agent to get its ID
487
+ const agent = AIEngineBase.Instance.Agents.find(a => a.Name === agentName);
488
+ // Create AI response message BEFORE invoking agent (for duration tracking)
489
+ const md = new Metadata();
490
+ const agentResponseMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
491
+ agentResponseMessage.ConversationID = conversationId;
492
+ agentResponseMessage.Role = 'AI';
493
+ agentResponseMessage.Message = '⏳ Starting...'; // Initial message
494
+ agentResponseMessage.ParentID = conversationManagerMessage.ID; // Thread under delegation message
495
+ agentResponseMessage.Status = 'In-Progress';
496
+ agentResponseMessage.HiddenToUser = false;
497
+ // Set AgentID immediately for proper attribution
498
+ if (agent?.ID) {
499
+ agentResponseMessage.AgentID = agent.ID;
500
+ }
501
+ // Save the record to establish __mj_CreatedAt timestamp
502
+ await agentResponseMessage.Save();
503
+ this.messageSent.emit(agentResponseMessage);
504
+ // Add sub-agent to active tasks
505
+ const newTaskId = this.activeTasks.add({
506
+ agentName: agentName,
507
+ status: 'Starting...',
508
+ relatedMessageId: userMessage.ID,
509
+ conversationDetailId: agentResponseMessage.ID
510
+ });
511
+ // Invoke the sub-agent with progress callback
512
+ const subResult = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.conversationHistory, reasoning, agentResponseMessage.ID, undefined, // no payload for initial invocation
513
+ this.createProgressCallback(agentResponseMessage.ID, agentName));
514
+ // Remove from active tasks
515
+ this.activeTasks.remove(newTaskId);
516
+ if (subResult && subResult.success) {
517
+ // Update the response message with agent result
518
+ agentResponseMessage.Message = subResult.agentRun?.Message || `✅ **${agentName}** completed`;
519
+ agentResponseMessage.Status = 'Complete';
520
+ // Store the agent ID and AgentRunID for display and tracking
521
+ if (subResult.agentRun.AgentID) {
522
+ agentResponseMessage.AgentID = subResult.agentRun.AgentID;
523
+ }
524
+ if (subResult.agentRun.ID) {
525
+ agentResponseMessage.AgentRunID = subResult.agentRun.ID;
526
+ }
527
+ // Save updates - this sets __mj_UpdatedAt for duration calculation
528
+ await agentResponseMessage.Save();
529
+ this.messageSent.emit(agentResponseMessage);
530
+ // Handle artifacts from sub-agent if any
531
+ if (subResult.payload && Object.keys(subResult.payload).length > 0) {
532
+ await this.createArtifactFromPayload(subResult.payload, agentResponseMessage, subResult.agentRun.AgentID);
533
+ console.log('🎨 Artifact created and linked to sub-agent message:', agentResponseMessage.ID);
534
+ // Re-emit to trigger artifact display
535
+ this.messageSent.emit(agentResponseMessage);
536
+ }
537
+ // Mark user message as complete
538
+ userMessage.Status = 'Complete';
539
+ await userMessage.Save();
540
+ this.messageSent.emit(userMessage);
541
+ }
542
+ else {
543
+ // Sub-agent failed - attempt auto-retry once
544
+ console.log(`⚠️ ${agentName} failed, attempting auto-retry...`);
545
+ // Update delegation message to show retry
546
+ conversationManagerMessage.Message = `👉 **${agentName}** will handle this request...\n\n⚠️ First attempt failed, retrying...`;
547
+ await conversationManagerMessage.Save();
548
+ this.messageSent.emit(conversationManagerMessage);
549
+ // Update the existing agentResponseMessage to show retry status
550
+ agentResponseMessage.Message = 'Retrying...';
551
+ await agentResponseMessage.Save();
552
+ this.messageSent.emit(agentResponseMessage);
553
+ // Retry the sub-agent
554
+ const retryResult = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.conversationHistory, reasoning, agentResponseMessage.ID, undefined, // no payload for retry
555
+ this.createProgressCallback(agentResponseMessage.ID, `${agentName} (retry)`));
556
+ if (retryResult && retryResult.success) {
557
+ // Retry succeeded - update the same message
558
+ agentResponseMessage.Message = retryResult.agentRun?.Message || `✅ **${agentName}** completed`;
559
+ agentResponseMessage.Status = 'Complete';
560
+ if (retryResult.agentRun.AgentID) {
561
+ agentResponseMessage.AgentID = retryResult.agentRun.AgentID;
562
+ }
563
+ if (retryResult.agentRun.ID) {
564
+ agentResponseMessage.AgentRunID = retryResult.agentRun.ID;
565
+ }
566
+ // Save updates - maintains original CreatedAt for accurate duration
567
+ await agentResponseMessage.Save();
568
+ this.messageSent.emit(agentResponseMessage);
569
+ // Handle artifacts
570
+ if (retryResult.payload && Object.keys(retryResult.payload).length > 0) {
571
+ await this.createArtifactFromPayload(retryResult.payload, agentResponseMessage, retryResult.agentRun.AgentID);
572
+ this.messageSent.emit(agentResponseMessage);
573
+ }
574
+ userMessage.Status = 'Complete';
575
+ await userMessage.Save();
576
+ this.messageSent.emit(userMessage);
577
+ }
578
+ else {
579
+ // Retry also failed - show error with manual retry option
580
+ conversationManagerMessage.Status = 'Error';
581
+ conversationManagerMessage.Message = `❌ **${agentName}** failed after retry\n\n${retryResult?.agentRun?.ErrorMessage || 'Unknown error'}`;
582
+ conversationManagerMessage.Error = retryResult?.agentRun?.ErrorMessage || null;
583
+ await conversationManagerMessage.Save();
584
+ this.messageSent.emit(conversationManagerMessage);
585
+ userMessage.Status = 'Complete'; // Don't mark user message as error
586
+ await userMessage.Save();
587
+ this.messageSent.emit(userMessage);
588
+ }
589
+ }
590
+ }
591
+ catch (error) {
592
+ console.error(`❌ Error invoking sub-agent ${agentName}:`, error);
593
+ conversationManagerMessage.Status = 'Error';
594
+ conversationManagerMessage.Message = `❌ **${agentName}** encountered an error\n\n${String(error)}`;
595
+ conversationManagerMessage.Error = String(error);
596
+ await conversationManagerMessage.Save();
597
+ this.messageSent.emit(conversationManagerMessage);
598
+ userMessage.Status = 'Complete'; // Don't mark user message as error
599
+ await userMessage.Save();
600
+ this.messageSent.emit(userMessage);
601
+ }
602
+ }
603
+ /**
604
+ * Handle silent observation - when Conversation Manager stays silent,
605
+ * check if we should continue with the last agent for iterative refinement
606
+ */
607
+ async handleSilentObservation(userMessage, conversationId) {
608
+ // Find the last AI message (excluding Conversation Manager) in the conversation history
609
+ const lastAIMessage = this.conversationHistory
610
+ .slice()
611
+ .reverse()
612
+ .find(msg => msg.Role === 'AI' &&
613
+ msg.AgentID &&
614
+ msg.AgentID !== this.converationManagerAgent?.ID);
615
+ if (!lastAIMessage || !lastAIMessage.AgentID) {
616
+ // No previous specialist agent - just mark user message as complete
617
+ console.log('🔇 No previous specialist agent found - marking complete');
618
+ userMessage.Status = 'Complete';
619
+ await userMessage.Save();
620
+ this.messageSent.emit(userMessage);
621
+ return;
622
+ }
623
+ // Load the agent entity to get its name
624
+ const md = new Metadata();
625
+ const rv = new RunView();
626
+ const agentResult = await rv.RunView({
627
+ EntityName: 'AI Agents',
628
+ ExtraFilter: `ID='${lastAIMessage.AgentID}'`,
629
+ ResultType: 'entity_object'
630
+ }, this.currentUser);
631
+ if (!agentResult.Success || !agentResult.Results || agentResult.Results.length === 0) {
632
+ console.warn('⚠️ Could not load previous agent - marking complete');
633
+ userMessage.Status = 'Complete';
634
+ await userMessage.Save();
635
+ this.messageSent.emit(userMessage);
636
+ return;
637
+ }
638
+ const previousAgent = agentResult.Results[0];
639
+ const agentName = previousAgent.Name || 'Agent';
640
+ console.log(`🔄 Agent continuity: Continuing with ${agentName} (AgentID: ${lastAIMessage.AgentID})`);
641
+ // Load the OUTPUT artifact from the last agent message
642
+ const artifactResult = await rv.RunView({
643
+ EntityName: 'MJ: Conversation Detail Artifacts',
644
+ ExtraFilter: `ConversationDetailID='${lastAIMessage.ID}' AND Direction='Output'`,
645
+ ResultType: 'entity_object'
646
+ }, this.currentUser);
647
+ let previousPayload = null;
648
+ if (artifactResult.Success && artifactResult.Results && artifactResult.Results.length > 0) {
649
+ // Load the artifact version content
650
+ const junctionRecord = artifactResult.Results[0];
651
+ const versionResult = await rv.RunView({
652
+ EntityName: 'MJ: Artifact Versions',
653
+ ExtraFilter: `ID='${junctionRecord.ArtifactVersionID}'`,
654
+ ResultType: 'entity_object'
655
+ }, this.currentUser);
656
+ if (versionResult.Success && versionResult.Results && versionResult.Results.length > 0) {
657
+ const version = versionResult.Results[0];
658
+ if (version.Content) {
659
+ try {
660
+ previousPayload = JSON.parse(version.Content);
661
+ console.log('📦 Loaded previous OUTPUT artifact as payload for continuity');
662
+ }
663
+ catch (error) {
664
+ console.warn('⚠️ Could not parse previous artifact content:', error);
665
+ }
666
+ }
667
+ }
668
+ }
669
+ // Create status message showing agent continuity
670
+ const statusMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
671
+ statusMessage.ConversationID = conversationId;
672
+ statusMessage.Role = 'AI';
673
+ statusMessage.Message = `Continuing with **${agentName}** for refinement...`;
674
+ statusMessage.ParentID = userMessage.ID;
675
+ statusMessage.Status = 'Complete';
676
+ statusMessage.HiddenToUser = false;
677
+ statusMessage.AgentID = this.converationManagerAgent?.ID || null;
678
+ await statusMessage.Save();
679
+ this.messageSent.emit(statusMessage);
680
+ // Add agent to active tasks
681
+ const taskId = this.activeTasks.add({
682
+ agentName: agentName,
683
+ status: 'Processing refinement...',
684
+ relatedMessageId: userMessage.ID,
685
+ conversationDetailId: statusMessage.ID
686
+ });
687
+ try {
688
+ // Invoke the agent with the previous payload
689
+ const continuityResult = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.conversationHistory, 'Continuing previous work based on user feedback', statusMessage.ID, previousPayload, this.createProgressCallback(statusMessage.ID, agentName));
690
+ // Remove from active tasks
691
+ this.activeTasks.remove(taskId);
692
+ if (continuityResult && continuityResult.success) {
693
+ // Create response message
694
+ const agentResponseMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
695
+ agentResponseMessage.ConversationID = conversationId;
696
+ agentResponseMessage.Role = 'AI';
697
+ agentResponseMessage.Message = continuityResult.agentRun?.Message || `✅ **${agentName}** completed refinement`;
698
+ agentResponseMessage.ParentID = statusMessage.ID;
699
+ agentResponseMessage.Status = 'Complete';
700
+ agentResponseMessage.HiddenToUser = false;
701
+ agentResponseMessage.AgentID = lastAIMessage.AgentID;
702
+ await agentResponseMessage.Save();
703
+ this.messageSent.emit(agentResponseMessage);
704
+ // Handle artifacts from agent if any
705
+ if (continuityResult.payload && Object.keys(continuityResult.payload).length > 0) {
706
+ await this.createArtifactFromPayload(continuityResult.payload, agentResponseMessage, lastAIMessage.AgentID);
707
+ console.log('🎨 Artifact created from agent continuity');
708
+ this.messageSent.emit(agentResponseMessage);
709
+ }
710
+ // Mark user message as complete
711
+ userMessage.Status = 'Complete';
712
+ await userMessage.Save();
713
+ this.messageSent.emit(userMessage);
714
+ }
715
+ else {
716
+ // Agent failed
717
+ statusMessage.Status = 'Error';
718
+ statusMessage.Message = `❌ **${agentName}** failed during refinement\n\n${continuityResult?.agentRun?.ErrorMessage || 'Unknown error'}`;
719
+ statusMessage.Error = continuityResult?.agentRun?.ErrorMessage || null;
720
+ await statusMessage.Save();
721
+ this.messageSent.emit(statusMessage);
722
+ userMessage.Status = 'Complete';
723
+ await userMessage.Save();
724
+ this.messageSent.emit(userMessage);
725
+ }
726
+ }
727
+ catch (error) {
728
+ console.error(`❌ Error in agent continuity with ${agentName}:`, error);
729
+ this.activeTasks.remove(taskId);
730
+ statusMessage.Status = 'Error';
731
+ statusMessage.Message = `❌ **${agentName}** encountered an error\n\n${String(error)}`;
732
+ statusMessage.Error = String(error);
733
+ await statusMessage.Save();
734
+ this.messageSent.emit(statusMessage);
735
+ userMessage.Status = 'Complete';
736
+ await userMessage.Save();
737
+ this.messageSent.emit(userMessage);
738
+ }
739
+ }
740
+ /**
741
+ * Handle agent response - create AI message from agent result
742
+ */
743
+ async handleAgentResponse(userMessage, result, conversationId) {
744
+ if (!result.agentRun.Message)
745
+ return;
746
+ const md = new Metadata();
747
+ const agentMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
748
+ agentMessage.ConversationID = conversationId;
749
+ agentMessage.Message = result.agentRun.Message;
750
+ agentMessage.Role = 'AI';
751
+ agentMessage.Status = 'Complete';
752
+ agentMessage.AgentID = this.converationManagerAgent?.ID || null; // Default to Conversation Manager
753
+ // Populate denormalized AgentID for fast lookup
754
+ if (result.agentRun.AgentID) {
755
+ agentMessage.AgentID = result.agentRun.AgentID;
756
+ console.log(`✅ Set AgentID=${result.agentRun.AgentID} for message from agent`);
757
+ }
758
+ else {
759
+ console.warn('⚠️ AgentID not found in agentRun result');
760
+ }
761
+ const saved = await agentMessage.Save();
762
+ if (saved) {
763
+ console.log('💾 Agent response saved');
764
+ // If agent returned a payload, create an artifact version linked to this message
765
+ if (result.payload && Object.keys(result.payload).length > 0) {
766
+ await this.createArtifactFromPayload(result.payload, agentMessage, result.agentRun.AgentID);
767
+ console.log('🎨 Artifact created and linked to conversation detail:', agentMessage.ID);
768
+ }
769
+ this.agentResponse.emit({
770
+ message: agentMessage,
771
+ agentResult: result
772
+ });
773
+ // Mark user message as complete
774
+ userMessage.Status = 'Complete';
775
+ await userMessage.Save();
776
+ this.messageSent.emit(userMessage);
777
+ }
778
+ }
779
+ /**
780
+ * Invoke an agent directly when mentioned with @ symbol
781
+ * Bypasses Conversation Manager completely - no status messages
782
+ */
783
+ async invokeAgentDirectly(userMessage, agentMention, conversationId) {
784
+ const agentName = agentMention.name;
785
+ // Add agent to active tasks
786
+ const taskId = this.activeTasks.add({
787
+ agentName: agentName,
788
+ status: 'Processing...',
789
+ relatedMessageId: userMessage.ID,
790
+ conversationDetailId: userMessage.ID
791
+ });
792
+ try {
793
+ // Update user message status to In-Progress
794
+ userMessage.Status = 'In-Progress';
795
+ await userMessage.Save();
796
+ this.messageSent.emit(userMessage);
797
+ // Look up the agent to get its ID
798
+ const agent = AIEngineBase.Instance.Agents.find(a => a.Name === agentName);
799
+ // Create AI response message BEFORE invoking agent (for duration tracking)
800
+ const md = new Metadata();
801
+ const agentResponseMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
802
+ agentResponseMessage.ConversationID = conversationId;
803
+ agentResponseMessage.Role = 'AI';
804
+ agentResponseMessage.Message = '⏳ Starting...'; // Initial message
805
+ agentResponseMessage.ParentID = userMessage.ID;
806
+ agentResponseMessage.Status = 'In-Progress';
807
+ agentResponseMessage.HiddenToUser = false;
808
+ // Set AgentID immediately for proper attribution
809
+ if (agent?.ID) {
810
+ agentResponseMessage.AgentID = agent.ID;
811
+ }
812
+ // Save the record to establish __mj_CreatedAt timestamp
813
+ await agentResponseMessage.Save();
814
+ this.messageSent.emit(agentResponseMessage);
815
+ // Invoke the agent directly
816
+ const result = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.conversationHistory, `User mentioned agent directly with @${agentName}`, agentResponseMessage.ID, undefined, // no payload for direct mention
817
+ this.createProgressCallback(agentResponseMessage.ID, agentName));
818
+ // Remove from active tasks
819
+ this.activeTasks.remove(taskId);
820
+ if (result && result.success) {
821
+ // Update the response message with agent result
822
+ agentResponseMessage.Message = result.agentRun?.Message || `✅ **${agentName}** completed`;
823
+ agentResponseMessage.Status = 'Complete';
824
+ if (result.agentRun.AgentID) {
825
+ agentResponseMessage.AgentID = result.agentRun.AgentID;
826
+ }
827
+ if (result.agentRun.ID) {
828
+ agentResponseMessage.AgentRunID = result.agentRun.ID;
829
+ }
830
+ // Save updates - this sets __mj_UpdatedAt for duration calculation
831
+ await agentResponseMessage.Save();
832
+ this.messageSent.emit(agentResponseMessage);
833
+ // Handle artifacts
834
+ if (result.payload && Object.keys(result.payload).length > 0) {
835
+ await this.createArtifactFromPayload(result.payload, agentResponseMessage, result.agentRun.AgentID);
836
+ this.messageSent.emit(agentResponseMessage);
837
+ }
838
+ // Mark user message as complete
839
+ userMessage.Status = 'Complete';
840
+ await userMessage.Save();
841
+ this.messageSent.emit(userMessage);
842
+ }
843
+ else {
844
+ // Agent failed - create error message
845
+ const md = new Metadata();
846
+ const errorMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
847
+ errorMessage.ConversationID = conversationId;
848
+ errorMessage.Role = 'AI';
849
+ errorMessage.Message = `❌ **@${agentName}** failed\n\n${result?.agentRun?.ErrorMessage || 'Unknown error'}`;
850
+ errorMessage.ParentID = userMessage.ID;
851
+ errorMessage.Status = 'Error';
852
+ errorMessage.Error = result?.agentRun?.ErrorMessage || null;
853
+ errorMessage.HiddenToUser = false;
854
+ await errorMessage.Save();
855
+ this.messageSent.emit(errorMessage);
856
+ userMessage.Status = 'Complete';
857
+ await userMessage.Save();
858
+ this.messageSent.emit(userMessage);
859
+ }
860
+ }
861
+ catch (error) {
862
+ console.error(`❌ Error invoking mentioned agent ${agentName}:`, error);
863
+ this.activeTasks.remove(taskId);
864
+ const md = new Metadata();
865
+ const errorMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
866
+ errorMessage.ConversationID = conversationId;
867
+ errorMessage.Role = 'AI';
868
+ errorMessage.Message = `❌ **@${agentName}** encountered an error\n\n${String(error)}`;
869
+ errorMessage.ParentID = userMessage.ID;
870
+ errorMessage.Status = 'Error';
871
+ errorMessage.Error = String(error);
872
+ errorMessage.HiddenToUser = false;
873
+ await errorMessage.Save();
874
+ this.messageSent.emit(errorMessage);
875
+ userMessage.Status = 'Complete';
876
+ await userMessage.Save();
877
+ this.messageSent.emit(userMessage);
878
+ }
879
+ }
880
+ /**
881
+ * Continue with the same agent from previous message (implicit continuation)
882
+ * Bypasses Conversation Manager - no status messages
883
+ */
884
+ async continueWithAgent(userMessage, agentId, conversationId) {
885
+ // Load the agent entity to get its name
886
+ const md = new Metadata();
887
+ const rv = new RunView();
888
+ const agentResult = await rv.RunView({
889
+ EntityName: 'AI Agents',
890
+ ExtraFilter: `ID='${agentId}'`,
891
+ ResultType: 'entity_object'
892
+ }, this.currentUser);
893
+ if (!agentResult.Success || !agentResult.Results || agentResult.Results.length === 0) {
894
+ console.warn('⚠️ Could not load agent for continuation - falling back to Conversation Manager');
895
+ await this.processMessageThroughAgent(userMessage, { mentions: [], agentMention: null, userMentions: [] });
896
+ return;
897
+ }
898
+ const agent = agentResult.Results[0];
899
+ const agentName = agent.Name || 'Agent';
900
+ // Add agent to active tasks
901
+ const taskId = this.activeTasks.add({
902
+ agentName: agentName,
903
+ status: 'Processing...',
904
+ relatedMessageId: userMessage.ID,
905
+ conversationDetailId: userMessage.ID
906
+ });
907
+ try {
908
+ // Update user message status to In-Progress
909
+ userMessage.Status = 'In-Progress';
910
+ await userMessage.Save();
911
+ this.messageSent.emit(userMessage);
912
+ // Create AI response message BEFORE invoking agent (for duration tracking)
913
+ const agentResponseMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
914
+ agentResponseMessage.ConversationID = conversationId;
915
+ agentResponseMessage.Role = 'AI';
916
+ agentResponseMessage.Message = '⏳ Starting...'; // Initial message
917
+ agentResponseMessage.ParentID = userMessage.ID;
918
+ agentResponseMessage.Status = 'In-Progress';
919
+ agentResponseMessage.HiddenToUser = false;
920
+ agentResponseMessage.AgentID = agentId;
921
+ // Save the record to establish __mj_CreatedAt timestamp
922
+ await agentResponseMessage.Save();
923
+ this.messageSent.emit(agentResponseMessage);
924
+ // Invoke the agent directly (continuation)
925
+ const result = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.conversationHistory, 'Continuing previous conversation with user', agentResponseMessage.ID, undefined, // no payload for continuation
926
+ this.createProgressCallback(agentResponseMessage.ID, agentName));
927
+ // Remove from active tasks
928
+ this.activeTasks.remove(taskId);
929
+ if (result && result.success) {
930
+ // Update the response message with agent result
931
+ agentResponseMessage.Message = result.agentRun?.Message || `✅ **${agentName}** completed`;
932
+ agentResponseMessage.Status = 'Complete';
933
+ if (result.agentRun.ID) {
934
+ agentResponseMessage.AgentRunID = result.agentRun.ID;
935
+ }
936
+ // Save updates - this sets __mj_UpdatedAt for duration calculation
937
+ await agentResponseMessage.Save();
938
+ this.messageSent.emit(agentResponseMessage);
939
+ // Handle artifacts
940
+ if (result.payload && Object.keys(result.payload).length > 0) {
941
+ await this.createArtifactFromPayload(result.payload, agentResponseMessage, agentId);
942
+ this.messageSent.emit(agentResponseMessage);
943
+ }
944
+ // Mark user message as complete
945
+ userMessage.Status = 'Complete';
946
+ await userMessage.Save();
947
+ this.messageSent.emit(userMessage);
948
+ }
949
+ else {
950
+ // Agent failed - create error message
951
+ const errorMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
952
+ errorMessage.ConversationID = conversationId;
953
+ errorMessage.Role = 'AI';
954
+ errorMessage.Message = `❌ **${agentName}** failed\n\n${result?.agentRun?.ErrorMessage || 'Unknown error'}`;
955
+ errorMessage.ParentID = userMessage.ID;
956
+ errorMessage.Status = 'Error';
957
+ errorMessage.Error = result?.agentRun?.ErrorMessage || null;
958
+ errorMessage.HiddenToUser = false;
959
+ await errorMessage.Save();
960
+ this.messageSent.emit(errorMessage);
961
+ userMessage.Status = 'Complete';
962
+ await userMessage.Save();
963
+ this.messageSent.emit(userMessage);
964
+ }
965
+ }
966
+ catch (error) {
967
+ console.error(`❌ Error continuing with agent ${agentName}:`, error);
968
+ this.activeTasks.remove(taskId);
969
+ const errorMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
970
+ errorMessage.ConversationID = conversationId;
971
+ errorMessage.Role = 'AI';
972
+ errorMessage.Message = `❌ **${agentName}** encountered an error\n\n${String(error)}`;
973
+ errorMessage.ParentID = userMessage.ID;
974
+ errorMessage.Status = 'Error';
975
+ errorMessage.Error = String(error);
976
+ errorMessage.HiddenToUser = false;
977
+ await errorMessage.Save();
978
+ this.messageSent.emit(errorMessage);
979
+ userMessage.Status = 'Complete';
980
+ await userMessage.Save();
981
+ this.messageSent.emit(userMessage);
982
+ }
983
+ }
984
+ /**
985
+ * Creates an artifact from an agent's payload and links it to the conversation detail
986
+ * @param payload The agent's payload object
987
+ * @param message The conversation detail message to link to
988
+ * @param agentId The ID of the agent that produced the payload
989
+ */
990
+ async createArtifactFromPayload(payload, message, agentId) {
991
+ try {
992
+ const md = new Metadata();
993
+ // Create Artifact header
994
+ const artifact = await md.GetEntityObject('MJ: Artifacts', this.currentUser);
995
+ // Generate artifact name based on agent name and timestamp
996
+ const agentName = agentId
997
+ ? AIEngineBase.Instance?.Agents?.find(a => a.ID === agentId)?.Name || 'Agent'
998
+ : 'Agent';
999
+ artifact.Name = `${agentName} Payload - ${new Date().toLocaleString()}`;
1000
+ artifact.Description = `Payload returned by ${agentName}`;
1001
+ // Use JSON artifact type (hardcoded ID from migration)
1002
+ artifact.TypeID = 'ae674c7e-ea0d-49ea-89e4-0649f5eb20d4'; // JSON type
1003
+ artifact.UserID = this.currentUser.ID;
1004
+ artifact.EnvironmentID = this.currentUser.EnvironmentID || 'F51358F3-9447-4176-B313-BF8025FD8D09';
1005
+ const artifactSaved = await artifact.Save();
1006
+ if (!artifactSaved) {
1007
+ console.error('Failed to save artifact');
1008
+ return;
1009
+ }
1010
+ // Create Artifact Version with content
1011
+ const version = await md.GetEntityObject('MJ: Artifact Versions', this.currentUser);
1012
+ version.ArtifactID = artifact.ID;
1013
+ version.VersionNumber = 1;
1014
+ version.Content = JSON.stringify(payload, null, 2);
1015
+ version.UserID = this.currentUser.ID;
1016
+ const versionSaved = await version.Save();
1017
+ if (!versionSaved) {
1018
+ console.error('Failed to save artifact version');
1019
+ return;
1020
+ }
1021
+ // Create M2M relationship using ConversationDetailArtifact junction table
1022
+ const junction = await md.GetEntityObject('MJ: Conversation Detail Artifacts', this.currentUser);
1023
+ junction.ConversationDetailID = message.ID;
1024
+ junction.ArtifactVersionID = version.ID;
1025
+ junction.Direction = 'Output'; // This artifact was produced as output from the agent
1026
+ const junctionSaved = await junction.Save();
1027
+ if (!junctionSaved) {
1028
+ console.error('Failed to create artifact-message association');
1029
+ }
1030
+ this.artifactCreated.emit({ artifactId: artifact.ID, versionId: version.ID, conversationDetailId: message.ID, name: artifact.Name });
1031
+ }
1032
+ catch (error) {
1033
+ console.error('Error creating artifact from payload:', error);
1034
+ }
1035
+ }
1036
+ /**
1037
+ * Name the conversation based on the first message using GraphQL AI client
1038
+ */
1039
+ async nameConversation(message) {
1040
+ try {
1041
+ console.log('🏷️ Naming conversation based on first message...');
1042
+ // Load the Name Conversation prompt to get its ID
1043
+ await AIEngineBase.Instance.Config(false);
1044
+ const p = AIEngineBase.Instance.Prompts.find(pr => pr.Name === 'Name Conversation');
1045
+ if (!p) {
1046
+ console.warn('⚠️ Name Conversation prompt not found');
1047
+ return;
1048
+ }
1049
+ const promptId = p.ID;
1050
+ // Use GraphQL AI client to run the prompt (same client as agent)
1051
+ const provider = Metadata.Provider;
1052
+ if (!provider) {
1053
+ console.warn('⚠️ GraphQLDataProvider not available');
1054
+ return;
1055
+ }
1056
+ const aiClient = new GraphQLAIClient(provider);
1057
+ const result = await aiClient.RunAIPrompt({
1058
+ promptId: promptId,
1059
+ messages: [{ role: 'user', content: message }],
1060
+ });
1061
+ if (result && result.success && (result.parsedResult || result.output)) {
1062
+ // Use parsedResult if available, otherwise parse output
1063
+ const parsed = result.parsedResult ||
1064
+ (result.output ? JSON.parse(result.output) : null);
1065
+ if (parsed) {
1066
+ const { name, description } = parsed;
1067
+ if (name) {
1068
+ console.log('✅ Generated conversation name:', { name, description });
1069
+ // Update the conversation name and description in database AND state immediately
1070
+ await this.conversationState.saveConversation(this.conversationId, { Name: name, Description: description || '' }, this.currentUser);
1071
+ console.log('💾 Conversation name updated in database and UI');
1072
+ // Emit event for animation in conversation list
1073
+ this.conversationRenamed.emit({
1074
+ conversationId: this.conversationId,
1075
+ name: name,
1076
+ description: description || ''
1077
+ });
1078
+ }
1079
+ }
1080
+ }
1081
+ else {
1082
+ console.warn('⚠️ Failed to generate conversation name');
1083
+ }
1084
+ }
1085
+ catch (error) {
1086
+ console.error('❌ Error naming conversation:', error);
1087
+ // Don't show error to user - naming failures should be silent
1088
+ }
1089
+ }
1090
+ static ɵfac = function MessageInputComponent_Factory(t) { return new (t || MessageInputComponent)(i0.ɵɵdirectiveInject(i1.DialogService), i0.ɵɵdirectiveInject(i2.ToastService), i0.ɵɵdirectiveInject(i3.ConversationAgentService), i0.ɵɵdirectiveInject(i4.ConversationStateService), i0.ɵɵdirectiveInject(i5.ActiveTasksService), i0.ɵɵdirectiveInject(i6.MentionAutocompleteService), i0.ɵɵdirectiveInject(i7.MentionParserService)); };
1091
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MessageInputComponent, selectors: [["mj-message-input"]], viewQuery: function MessageInputComponent_Query(rf, ctx) { if (rf & 1) {
1092
+ i0.ɵɵviewQuery(_c0, 5);
1093
+ } if (rf & 2) {
1094
+ let _t;
1095
+ i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.messageTextarea = _t.first);
1096
+ } }, inputs: { conversationId: "conversationId", currentUser: "currentUser", disabled: "disabled", placeholder: "placeholder", parentMessageId: "parentMessageId", conversationHistory: "conversationHistory" }, outputs: { messageSent: "messageSent", agentResponse: "agentResponse", agentRunDetected: "agentRunDetected", artifactCreated: "artifactCreated", conversationRenamed: "conversationRenamed" }, decls: 11, vars: 11, consts: [["messageTextarea", ""], [1, "message-input-container"], ["rows", "3", 1, "message-input", 3, "ngModelChange", "keydown", "input", "ngModel", "placeholder", "disabled"], [3, "suggestionSelected", "closed", "suggestions", "position", "visible", "showAbove"], [1, "input-actions"], ["class", "processing-indicator", 4, "ngIf"], ["title", "Attach file (coming soon)", 1, "btn-attach", 3, "disabled"], [1, "fas", "fa-paperclip"], [1, "btn-send", 3, "click", "disabled", "title"], [1, "fas", "fa-paper-plane"], [1, "processing-indicator"], [1, "fas", "fa-circle-notch", "fa-spin"]], template: function MessageInputComponent_Template(rf, ctx) { if (rf & 1) {
1097
+ const _r1 = i0.ɵɵgetCurrentView();
1098
+ i0.ɵɵelementStart(0, "div", 1)(1, "textarea", 2, 0);
1099
+ i0.ɵɵtwoWayListener("ngModelChange", function MessageInputComponent_Template_textarea_ngModelChange_1_listener($event) { i0.ɵɵrestoreView(_r1); i0.ɵɵtwoWayBindingSet(ctx.messageText, $event) || (ctx.messageText = $event); return i0.ɵɵresetView($event); });
1100
+ i0.ɵɵlistener("keydown", function MessageInputComponent_Template_textarea_keydown_1_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onKeyDown($event)); })("input", function MessageInputComponent_Template_textarea_input_1_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onInput($event)); });
1101
+ i0.ɵɵtext(3, " ");
1102
+ i0.ɵɵelementEnd();
1103
+ i0.ɵɵelementStart(4, "mj-mention-dropdown", 3);
1104
+ i0.ɵɵlistener("suggestionSelected", function MessageInputComponent_Template_mj_mention_dropdown_suggestionSelected_4_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onMentionSelected($event)); })("closed", function MessageInputComponent_Template_mj_mention_dropdown_closed_4_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.closeMentionDropdown()); });
1105
+ i0.ɵɵelementEnd();
1106
+ i0.ɵɵelementStart(5, "div", 4);
1107
+ i0.ɵɵtemplate(6, MessageInputComponent_div_6_Template, 4, 0, "div", 5);
1108
+ i0.ɵɵelementStart(7, "button", 6);
1109
+ i0.ɵɵelement(8, "i", 7);
1110
+ i0.ɵɵelementEnd();
1111
+ i0.ɵɵelementStart(9, "button", 8);
1112
+ i0.ɵɵlistener("click", function MessageInputComponent_Template_button_click_9_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onSend()); });
1113
+ i0.ɵɵelement(10, "i", 9);
1114
+ i0.ɵɵelementEnd()()();
1115
+ } if (rf & 2) {
1116
+ i0.ɵɵadvance();
1117
+ i0.ɵɵtwoWayProperty("ngModel", ctx.messageText);
1118
+ i0.ɵɵproperty("placeholder", ctx.placeholder)("disabled", ctx.disabled || ctx.isSending);
1119
+ i0.ɵɵadvance(3);
1120
+ i0.ɵɵproperty("suggestions", ctx.mentionSuggestions)("position", ctx.mentionDropdownPosition)("visible", ctx.showMentionDropdown)("showAbove", ctx.mentionDropdownShowAbove);
1121
+ i0.ɵɵadvance(2);
1122
+ i0.ɵɵproperty("ngIf", ctx.isProcessing);
1123
+ i0.ɵɵadvance();
1124
+ i0.ɵɵproperty("disabled", ctx.disabled);
1125
+ i0.ɵɵadvance(2);
1126
+ i0.ɵɵproperty("disabled", !ctx.canSend)("title", ctx.isSending ? "Sending..." : "Send message");
1127
+ } }, dependencies: [i8.NgIf, i9.DefaultValueAccessor, i9.NgControlStatus, i9.NgModel, i10.MentionDropdownComponent], styles: [".message-input-container[_ngcontent-%COMP%] {\n position: relative;\n padding: 16px 24px;\n border-top: 1px solid #D9D9D9;\n background: #FFF;\n}\n\n.message-input-wrapper[_ngcontent-%COMP%] {\n border: 2px solid #D9D9D9;\n border-radius: 8px;\n padding: 12px;\n transition: border-color 0.2s, box-shadow 0.2s;\n background: #FFF;\n}\n\n.message-input-wrapper[_ngcontent-%COMP%]:focus-within {\n border-color: #0076B6;\n box-shadow: 0 0 0 3px rgba(0, 118, 182, 0.1);\n}\n\n.message-input[_ngcontent-%COMP%] {\n width: 100%;\n padding: 0;\n border: none;\n resize: none;\n font-family: inherit;\n font-size: 14px;\n min-height: 40px;\n max-height: 200px;\n line-height: 1.5;\n}\n\n.message-input[_ngcontent-%COMP%]:focus {\n outline: none;\n}\n\n.message-input[_ngcontent-%COMP%]:disabled {\n background: #F4F4F4;\n cursor: not-allowed;\n}\n.input-actions[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-top: 12px;\n}\n.btn-attach[_ngcontent-%COMP%] {\n padding: 8px 16px;\n background: transparent;\n border: 1px solid #D9D9D9;\n border-radius: 6px;\n cursor: pointer;\n color: #333;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.btn-attach[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #F4F4F4;\n border-color: #AAA;\n}\n.btn-attach[_ngcontent-%COMP%]:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.btn-send[_ngcontent-%COMP%] {\n width: 40px;\n height: 40px;\n background: #3B82F6;\n color: white;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n flex-shrink: 0;\n}\n.btn-send[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: #2563EB;\n}\n.btn-send[_ngcontent-%COMP%]:disabled {\n background: #D9D9D9;\n color: #AAA;\n cursor: not-allowed;\n}\n.btn-send[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 16px;\n}\n.processing-indicator[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: #6B7280;\n margin-right: auto;\n}\n.processing-indicator[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n color: #0076B6;\n}"] });
1128
+ }
1129
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MessageInputComponent, [{
1130
+ type: Component,
1131
+ args: [{ selector: 'mj-message-input', template: "<div class=\"message-input-container\">\n <textarea\n #messageTextarea\n class=\"message-input\"\n [(ngModel)]=\"messageText\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled || isSending\"\n (keydown)=\"onKeyDown($event)\"\n (input)=\"onInput($event)\"\n rows=\"3\">\n </textarea>\n\n <!-- Mention Autocomplete Dropdown -->\n <mj-mention-dropdown\n [suggestions]=\"mentionSuggestions\"\n [position]=\"mentionDropdownPosition\"\n [visible]=\"showMentionDropdown\"\n [showAbove]=\"mentionDropdownShowAbove\"\n (suggestionSelected)=\"onMentionSelected($event)\"\n (closed)=\"closeMentionDropdown()\">\n </mj-mention-dropdown>\n\n <div class=\"input-actions\">\n <div class=\"processing-indicator\" *ngIf=\"isProcessing\">\n <i class=\"fas fa-circle-notch fa-spin\"></i>\n <span>AI is responding...</span>\n </div>\n <button\n class=\"btn-attach\"\n [disabled]=\"disabled\"\n title=\"Attach file (coming soon)\">\n <i class=\"fas fa-paperclip\"></i>\n </button>\n <button\n class=\"btn-send\"\n [disabled]=\"!canSend\"\n (click)=\"onSend()\"\n [title]=\"isSending ? 'Sending...' : 'Send message'\">\n <i class=\"fas fa-paper-plane\"></i>\n </button>\n </div>\n</div>", styles: [".message-input-container {\n position: relative;\n padding: 16px 24px;\n border-top: 1px solid #D9D9D9;\n background: #FFF;\n}\n\n.message-input-wrapper {\n border: 2px solid #D9D9D9;\n border-radius: 8px;\n padding: 12px;\n transition: border-color 0.2s, box-shadow 0.2s;\n background: #FFF;\n}\n\n.message-input-wrapper:focus-within {\n border-color: #0076B6;\n box-shadow: 0 0 0 3px rgba(0, 118, 182, 0.1);\n}\n\n.message-input {\n width: 100%;\n padding: 0;\n border: none;\n resize: none;\n font-family: inherit;\n font-size: 14px;\n min-height: 40px;\n max-height: 200px;\n line-height: 1.5;\n}\n\n.message-input:focus {\n outline: none;\n}\n\n.message-input:disabled {\n background: #F4F4F4;\n cursor: not-allowed;\n}\n.input-actions {\n display: flex;\n justify-content: space-between;\n align-items: center;\n margin-top: 12px;\n}\n.btn-attach {\n padding: 8px 16px;\n background: transparent;\n border: 1px solid #D9D9D9;\n border-radius: 6px;\n cursor: pointer;\n color: #333;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.btn-attach:hover:not(:disabled) {\n background: #F4F4F4;\n border-color: #AAA;\n}\n.btn-attach:disabled {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.btn-send {\n width: 40px;\n height: 40px;\n background: #3B82F6;\n color: white;\n border: none;\n border-radius: 8px;\n cursor: pointer;\n display: flex;\n align-items: center;\n justify-content: center;\n transition: background 0.2s;\n flex-shrink: 0;\n}\n.btn-send:hover:not(:disabled) {\n background: #2563EB;\n}\n.btn-send:disabled {\n background: #D9D9D9;\n color: #AAA;\n cursor: not-allowed;\n}\n.btn-send i {\n font-size: 16px;\n}\n.processing-indicator {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n color: #6B7280;\n margin-right: auto;\n}\n.processing-indicator i {\n color: #0076B6;\n}"] }]
1132
+ }], () => [{ type: i1.DialogService }, { type: i2.ToastService }, { type: i3.ConversationAgentService }, { type: i4.ConversationStateService }, { type: i5.ActiveTasksService }, { type: i6.MentionAutocompleteService }, { type: i7.MentionParserService }], { conversationId: [{
1133
+ type: Input
1134
+ }], currentUser: [{
1135
+ type: Input
1136
+ }], disabled: [{
1137
+ type: Input
1138
+ }], placeholder: [{
1139
+ type: Input
1140
+ }], parentMessageId: [{
1141
+ type: Input
1142
+ }], conversationHistory: [{
1143
+ type: Input
1144
+ }], messageSent: [{
1145
+ type: Output
1146
+ }], agentResponse: [{
1147
+ type: Output
1148
+ }], agentRunDetected: [{
1149
+ type: Output
1150
+ }], artifactCreated: [{
1151
+ type: Output
1152
+ }], conversationRenamed: [{
1153
+ type: Output
1154
+ }], messageTextarea: [{
1155
+ type: ViewChild,
1156
+ args: ['messageTextarea']
1157
+ }] }); })();
1158
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MessageInputComponent, { className: "MessageInputComponent", filePath: "src/lib/components/message/message-input.component.ts", lineNumber: 21 }); })();
1159
+ //# sourceMappingURL=message-input.component.js.map