@memberjunction/ng-conversations 2.104.0 → 2.105.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 (106) hide show
  1. package/dist/lib/components/collection/collection-view.component.d.ts +5 -1
  2. package/dist/lib/components/collection/collection-view.component.d.ts.map +1 -1
  3. package/dist/lib/components/collection/collection-view.component.js +53 -11
  4. package/dist/lib/components/collection/collection-view.component.js.map +1 -1
  5. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +53 -13
  6. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
  7. package/dist/lib/components/conversation/conversation-chat-area.component.js +455 -289
  8. package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
  9. package/dist/lib/components/conversation/conversation-list.component.d.ts +9 -1
  10. package/dist/lib/components/conversation/conversation-list.component.d.ts.map +1 -1
  11. package/dist/lib/components/conversation/conversation-list.component.js +258 -71
  12. package/dist/lib/components/conversation/conversation-list.component.js.map +1 -1
  13. package/dist/lib/components/mention/mention-dropdown.component.d.ts +6 -0
  14. package/dist/lib/components/mention/mention-dropdown.component.d.ts.map +1 -1
  15. package/dist/lib/components/mention/mention-dropdown.component.js +35 -13
  16. package/dist/lib/components/mention/mention-dropdown.component.js.map +1 -1
  17. package/dist/lib/components/message/message-input.component.d.ts +49 -12
  18. package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
  19. package/dist/lib/components/message/message-input.component.js +579 -221
  20. package/dist/lib/components/message/message-input.component.js.map +1 -1
  21. package/dist/lib/components/message/message-item.component.d.ts +16 -5
  22. package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
  23. package/dist/lib/components/message/message-item.component.js +136 -64
  24. package/dist/lib/components/message/message-item.component.js.map +1 -1
  25. package/dist/lib/components/message/message-list.component.d.ts +11 -5
  26. package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
  27. package/dist/lib/components/message/message-list.component.js +48 -9
  28. package/dist/lib/components/message/message-list.component.js.map +1 -1
  29. package/dist/lib/components/navigation/conversation-navigation.component.d.ts.map +1 -1
  30. package/dist/lib/components/navigation/conversation-navigation.component.js +24 -9
  31. package/dist/lib/components/navigation/conversation-navigation.component.js.map +1 -1
  32. package/dist/lib/components/task/tasks-full-view.component.d.ts +47 -0
  33. package/dist/lib/components/task/tasks-full-view.component.d.ts.map +1 -0
  34. package/dist/lib/components/task/tasks-full-view.component.js +368 -0
  35. package/dist/lib/components/task/tasks-full-view.component.js.map +1 -0
  36. package/dist/lib/components/tasks/task-widget.component.d.ts +29 -0
  37. package/dist/lib/components/tasks/task-widget.component.d.ts.map +1 -0
  38. package/dist/lib/components/tasks/task-widget.component.js +385 -0
  39. package/dist/lib/components/tasks/task-widget.component.js.map +1 -0
  40. package/dist/lib/components/tasks/tasks-dropdown.component.d.ts +22 -8
  41. package/dist/lib/components/tasks/tasks-dropdown.component.d.ts.map +1 -1
  42. package/dist/lib/components/tasks/tasks-dropdown.component.js +195 -99
  43. package/dist/lib/components/tasks/tasks-dropdown.component.js.map +1 -1
  44. package/dist/lib/components/thread/thread-panel.component.d.ts +3 -1
  45. package/dist/lib/components/thread/thread-panel.component.d.ts.map +1 -1
  46. package/dist/lib/components/thread/thread-panel.component.js +14 -14
  47. package/dist/lib/components/thread/thread-panel.component.js.map +1 -1
  48. package/dist/lib/components/workspace/conversation-workspace.component.d.ts +43 -4
  49. package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
  50. package/dist/lib/components/workspace/conversation-workspace.component.js +288 -46
  51. package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
  52. package/dist/lib/conversations.module.d.ts +40 -46
  53. package/dist/lib/conversations.module.d.ts.map +1 -1
  54. package/dist/lib/conversations.module.js +28 -42
  55. package/dist/lib/conversations.module.js.map +1 -1
  56. package/dist/lib/models/conversation-state.model.d.ts +29 -1
  57. package/dist/lib/models/conversation-state.model.d.ts.map +1 -1
  58. package/dist/lib/services/conversation-agent.service.d.ts +6 -5
  59. package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
  60. package/dist/lib/services/conversation-agent.service.js +18 -19
  61. package/dist/lib/services/conversation-agent.service.js.map +1 -1
  62. package/dist/lib/services/data-cache.service.d.ts +96 -0
  63. package/dist/lib/services/data-cache.service.d.ts.map +1 -0
  64. package/dist/lib/services/data-cache.service.js +203 -0
  65. package/dist/lib/services/data-cache.service.js.map +1 -0
  66. package/dist/public-api.d.ts +3 -6
  67. package/dist/public-api.d.ts.map +1 -1
  68. package/dist/public-api.js +3 -6
  69. package/dist/public-api.js.map +1 -1
  70. package/package.json +12 -10
  71. package/dist/lib/components/artifact/artifact-panel.component.d.ts +0 -22
  72. package/dist/lib/components/artifact/artifact-panel.component.d.ts.map +0 -1
  73. package/dist/lib/components/artifact/artifact-panel.component.js +0 -237
  74. package/dist/lib/components/artifact/artifact-panel.component.js.map +0 -1
  75. package/dist/lib/components/artifact/artifact-upload-modal.component.d.ts +0 -39
  76. package/dist/lib/components/artifact/artifact-upload-modal.component.d.ts.map +0 -1
  77. package/dist/lib/components/artifact/artifact-upload-modal.component.js +0 -384
  78. package/dist/lib/components/artifact/artifact-upload-modal.component.js.map +0 -1
  79. package/dist/lib/components/artifact/artifact-version-history.component.d.ts +0 -28
  80. package/dist/lib/components/artifact/artifact-version-history.component.d.ts.map +0 -1
  81. package/dist/lib/components/artifact/artifact-version-history.component.js +0 -280
  82. package/dist/lib/components/artifact/artifact-version-history.component.js.map +0 -1
  83. package/dist/lib/components/artifact/artifact-viewer-panel.component.d.ts +0 -22
  84. package/dist/lib/components/artifact/artifact-viewer-panel.component.d.ts.map +0 -1
  85. package/dist/lib/components/artifact/artifact-viewer-panel.component.js +0 -182
  86. package/dist/lib/components/artifact/artifact-viewer-panel.component.js.map +0 -1
  87. package/dist/lib/components/artifact/artifact-viewer.component.d.ts +0 -27
  88. package/dist/lib/components/artifact/artifact-viewer.component.d.ts.map +0 -1
  89. package/dist/lib/components/artifact/artifact-viewer.component.js +0 -266
  90. package/dist/lib/components/artifact/artifact-viewer.component.js.map +0 -1
  91. package/dist/lib/components/artifact/inline-artifact.component.d.ts +0 -46
  92. package/dist/lib/components/artifact/inline-artifact.component.d.ts.map +0 -1
  93. package/dist/lib/components/artifact/inline-artifact.component.js +0 -447
  94. package/dist/lib/components/artifact/inline-artifact.component.js.map +0 -1
  95. package/dist/lib/components/task/task-form-modal.component.d.ts +0 -42
  96. package/dist/lib/components/task/task-form-modal.component.d.ts.map +0 -1
  97. package/dist/lib/components/task/task-form-modal.component.js +0 -329
  98. package/dist/lib/components/task/task-form-modal.component.js.map +0 -1
  99. package/dist/lib/components/task/task-item.component.d.ts +0 -22
  100. package/dist/lib/components/task/task-item.component.d.ts.map +0 -1
  101. package/dist/lib/components/task/task-item.component.js +0 -234
  102. package/dist/lib/components/task/task-item.component.js.map +0 -1
  103. package/dist/lib/components/task/task-list.component.d.ts +0 -32
  104. package/dist/lib/components/task/task-list.component.d.ts.map +0 -1
  105. package/dist/lib/components/task/task-list.component.js +0 -290
  106. package/dist/lib/components/task/task-list.component.js.map +0 -1
@@ -1,18 +1,19 @@
1
1
  import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
2
2
  import { Metadata, RunView } from '@memberjunction/core';
3
- import { GraphQLAIClient } from '@memberjunction/graphql-dataprovider';
3
+ import { GraphQLDataProvider, GraphQLAIClient } from '@memberjunction/graphql-dataprovider';
4
4
  import { AIEngineBase } from '@memberjunction/ai-engine-base';
5
5
  import * as i0 from "@angular/core";
6
6
  import * as i1 from "../../services/dialog.service";
7
7
  import * as i2 from "../../services/toast.service";
8
8
  import * as i3 from "../../services/conversation-agent.service";
9
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";
10
+ import * as i5 from "../../services/data-cache.service";
11
+ import * as i6 from "../../services/active-tasks.service";
12
+ import * as i7 from "../../services/mention-autocomplete.service";
13
+ import * as i8 from "../../services/mention-parser.service";
14
+ import * as i9 from "@angular/common";
15
+ import * as i10 from "@angular/forms";
16
+ import * as i11 from "../mention/mention-dropdown.component";
16
17
  const _c0 = ["messageTextarea"];
17
18
  function MessageInputComponent_div_6_Template(rf, ctx) { if (rf & 1) {
18
19
  i0.ɵɵelementStart(0, "div", 10);
@@ -26,9 +27,12 @@ export class MessageInputComponent {
26
27
  toastService;
27
28
  agentService;
28
29
  conversationState;
30
+ dataCache;
29
31
  activeTasks;
30
32
  mentionAutocomplete;
31
33
  mentionParser;
34
+ // Default artifact type ID for JSON (when agent doesn't specify DefaultArtifactTypeID)
35
+ JSON_ARTIFACT_TYPE_ID = 'ae674c7e-ea0d-49ea-89e4-0649f5eb20d4';
32
36
  conversationId;
33
37
  currentUser;
34
38
  disabled = false;
@@ -52,11 +56,16 @@ export class MessageInputComponent {
52
56
  mentionDropdownShowAbove = false; // Controls transform direction
53
57
  mentionStartIndex = -1;
54
58
  mentionQuery = '';
55
- constructor(dialogService, toastService, agentService, conversationState, activeTasks, mentionAutocomplete, mentionParser) {
59
+ // PubSub subscription for task progress updates
60
+ pushStatusSubscription;
61
+ // Track active task execution message IDs for real-time updates
62
+ activeTaskExecutionMessageIds = new Set();
63
+ constructor(dialogService, toastService, agentService, conversationState, dataCache, activeTasks, mentionAutocomplete, mentionParser) {
56
64
  this.dialogService = dialogService;
57
65
  this.toastService = toastService;
58
66
  this.agentService = agentService;
59
67
  this.conversationState = conversationState;
68
+ this.dataCache = dataCache;
60
69
  this.activeTasks = activeTasks;
61
70
  this.mentionAutocomplete = mentionAutocomplete;
62
71
  this.mentionParser = mentionParser;
@@ -65,6 +74,96 @@ export class MessageInputComponent {
65
74
  this.converationManagerAgent = await this.agentService.getConversationManagerAgent();
66
75
  // Initialize mention autocomplete
67
76
  await this.mentionAutocomplete.initialize(this.currentUser);
77
+ // Subscribe to PubSub for task progress updates
78
+ this.subscribeToPushStatus();
79
+ }
80
+ ngOnDestroy() {
81
+ // Clean up PubSub subscription
82
+ if (this.pushStatusSubscription) {
83
+ this.pushStatusSubscription.unsubscribe();
84
+ }
85
+ }
86
+ /**
87
+ * Subscribe to PubSub for real-time task orchestration progress updates
88
+ */
89
+ subscribeToPushStatus() {
90
+ const dataProvider = GraphQLDataProvider.Instance;
91
+ this.pushStatusSubscription = dataProvider.PushStatusUpdates().subscribe((status) => {
92
+ if (!status || !status.message)
93
+ return;
94
+ try {
95
+ const statusObj = JSON.parse(status.message);
96
+ // Filter for TaskOrchestrator messages
97
+ if (statusObj.resolver === 'TaskOrchestrator') {
98
+ this.handleTaskProgress(statusObj);
99
+ }
100
+ }
101
+ catch (error) {
102
+ console.error('Error parsing push status update:', error);
103
+ }
104
+ });
105
+ }
106
+ /**
107
+ * Handle task progress updates from PubSub
108
+ */
109
+ async handleTaskProgress(statusObj) {
110
+ if (statusObj.type === 'TaskProgress') {
111
+ // High-level task progress
112
+ const { taskName, message, percentComplete } = statusObj.data;
113
+ console.log(`[Task Progress] ${taskName}: ${message} (${percentComplete}%)`);
114
+ // Update any active task execution messages
115
+ await this.updateTaskExecutionMessages(taskName, message, percentComplete);
116
+ }
117
+ else if (statusObj.type === 'AgentProgress') {
118
+ // Detailed agent progress (shown as smaller sub-text)
119
+ const { taskName, agentStep, agentMessage } = statusObj.data;
120
+ console.log(`[Agent Progress] ${taskName} → ${agentStep}: ${agentMessage}`);
121
+ // Update with agent details
122
+ await this.updateTaskExecutionMessages(taskName, `${agentStep}: ${agentMessage}`, undefined, true);
123
+ }
124
+ }
125
+ /**
126
+ * Update task execution messages in real-time based on progress updates
127
+ */
128
+ async updateTaskExecutionMessages(taskName, progressMessage, percentComplete, isAgentDetail = false) {
129
+ // Update all active task execution messages using the cache
130
+ for (const messageId of this.activeTaskExecutionMessageIds) {
131
+ try {
132
+ // Get message from cache (single source of truth)
133
+ const message = await this.dataCache.getConversationDetail(messageId, this.currentUser);
134
+ if (!message) {
135
+ console.warn(`Task execution message ${messageId} not found in cache`);
136
+ continue;
137
+ }
138
+ // Skip if already complete
139
+ if (message.Status === 'Complete' || message.Status === 'Error') {
140
+ continue;
141
+ }
142
+ // Build progress message
143
+ let updatedMessage = message.Message || '';
144
+ if (isAgentDetail) {
145
+ // Agent details shown as sub-text
146
+ updatedMessage = `⏳ **${taskName}**\n\n_${progressMessage}_`;
147
+ }
148
+ else if (percentComplete != null) {
149
+ updatedMessage = `⏳ **${taskName}** (${percentComplete}%)\n\n${progressMessage}`;
150
+ }
151
+ else {
152
+ updatedMessage = `⏳ **${taskName}**\n\n${progressMessage}`;
153
+ }
154
+ message.Message = updatedMessage;
155
+ // Use safe save to prevent race conditions with completion
156
+ const saved = await this.safeSaveConversationDetail(message, `TaskProgress:${taskName}`);
157
+ if (saved) {
158
+ this.messageSent.emit(message);
159
+ // Also update the ActiveTasksService to keep the tasks dropdown in sync
160
+ this.activeTasks.updateStatusByConversationDetailId(message.ID, progressMessage);
161
+ }
162
+ }
163
+ catch (error) {
164
+ console.error('Error updating task execution message:', error);
165
+ }
166
+ }
68
167
  }
69
168
  get canSend() {
70
169
  return !this.disabled && !this.isSending && this.messageText.trim().length > 0;
@@ -199,8 +298,7 @@ export class MessageInputComponent {
199
298
  return;
200
299
  this.isSending = true;
201
300
  try {
202
- const md = new Metadata();
203
- const detail = await md.GetEntityObject('Conversation Details', this.currentUser);
301
+ const detail = await this.dataCache.createConversationDetail(this.currentUser);
204
302
  detail.ConversationID = this.conversationId;
205
303
  detail.Message = this.messageText.trim();
206
304
  detail.Role = 'User';
@@ -219,10 +317,10 @@ export class MessageInputComponent {
219
317
  this.messageText = '';
220
318
  // Check if this is the first message in the conversation
221
319
  const isFirstMessage = this.conversationHistory.length === 0;
222
- // Determine routing: @mention > last agent context > Conversation Manager
320
+ // Determine routing: @mention > last agent context > Sage
223
321
  if (mentionResult.agentMention) {
224
- // Direct @mention - skip Conversation Manager, invoke agent directly
225
- console.log('🎯 Direct @mention detected, bypassing Conversation Manager');
322
+ // Direct @mention - skip Sage, invoke agent directly
323
+ console.log('🎯 Direct @mention detected, bypassing Sage');
226
324
  if (isFirstMessage) {
227
325
  Promise.all([
228
326
  this.invokeAgentDirectly(detail, mentionResult.agentMention, this.conversationId),
@@ -242,7 +340,7 @@ export class MessageInputComponent {
242
340
  msg.AgentID &&
243
341
  msg.AgentID !== this.converationManagerAgent?.ID);
244
342
  if (lastAIMessage && lastAIMessage.AgentID) {
245
- // Continue with same agent - skip Conversation Manager
343
+ // Continue with same agent - skip Sage
246
344
  console.log('🔄 Implicit continuation detected, continuing with last agent');
247
345
  if (isFirstMessage) {
248
346
  Promise.all([
@@ -255,8 +353,8 @@ export class MessageInputComponent {
255
353
  }
256
354
  }
257
355
  else {
258
- // No context - use Conversation Manager
259
- console.log('🤖 No agent context, using Conversation Manager');
356
+ // No context - use Sage
357
+ console.log('🤖 No agent context, using Sage');
260
358
  if (isFirstMessage) {
261
359
  Promise.all([
262
360
  this.processMessageThroughAgent(detail, mentionResult),
@@ -288,56 +386,61 @@ export class MessageInputComponent {
288
386
  this.isSending = false;
289
387
  }
290
388
  }
389
+ /**
390
+ * Safe save for ConversationDetail - prevents overwrites of completed/errored messages
391
+ * Use this ONLY in progress update paths to prevent race conditions
392
+ * @param detail The conversation detail to save
393
+ * @param context Description of who is saving (for logging)
394
+ * @returns true if saved, false if blocked
395
+ */
396
+ async safeSaveConversationDetail(detail, context) {
397
+ // Never modify completed or errored messages
398
+ if (detail.Status === 'Complete' || detail.Status === 'Error') {
399
+ console.log(`[${context}] 🛡️ Blocked save - message is ${detail.Status}`);
400
+ return false;
401
+ }
402
+ await detail.Save();
403
+ return true;
404
+ }
291
405
  /**
292
406
  * Create a progress callback for agent execution
293
407
  * This callback updates both the active task and the ConversationDetail message
294
408
  * IMPORTANT: Filters by agentRunId to prevent cross-contamination when multiple agents run in parallel
295
409
  */
296
- createProgressCallback(conversationDetailId, agentName) {
410
+ createProgressCallback(conversationDetail, agentName) {
297
411
  return async (progress) => {
298
412
  // Extract agentRunId from progress metadata
299
413
  const progressAgentRunId = progress.metadata?.agentRunId;
300
414
  // Format progress message with visual indicator
301
415
  const progressText = progress.message;
302
416
  // Update the active task with progress details (if it exists)
303
- this.activeTasks.updateStatusByConversationDetailId(conversationDetailId, progressText);
417
+ this.activeTasks.updateStatusByConversationDetailId(conversationDetail.ID, progressText);
304
418
  // Update the ConversationDetail message in real-time
305
419
  try {
306
- const md = new Metadata();
307
- const conversationDetail = await md.GetEntityObject('Conversation Details', this.currentUser);
308
- if (await conversationDetail.Load(conversationDetailId)) {
420
+ if (conversationDetail) {
421
+ console.log(`[${agentName}] Got conversation detail from cache - Status: ${conversationDetail.Status}, ID: ${conversationDetail.ID}`);
309
422
  // Skip progress updates if message is already complete
423
+ // Since we're using the cached instance, this check sees the ACTUAL current state
310
424
  if (conversationDetail.Status === 'Complete') {
311
- console.log(`[${agentName}] Skipping progress update - message already complete`);
425
+ console.log(`[${agentName}] Skipping progress update - message already complete`);
312
426
  return;
313
427
  }
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
428
+ // Emit agentRunId if we have it (for parent to track)
429
+ if (progressAgentRunId) {
321
430
  this.agentRunDetected.emit({
322
- conversationDetailId,
431
+ conversationDetailId: conversationDetail.ID,
323
432
  agentRunId: progressAgentRunId
324
433
  });
325
434
  }
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
435
+ if (conversationDetail.Status === 'In-Progress') {
436
+ conversationDetail.Message = progressText;
437
+ // Use safe save to prevent race conditions with completion
438
+ const saved = await this.safeSaveConversationDetail(conversationDetail, `Progress:${agentName}`);
439
+ if (saved) {
440
+ // Emit update to trigger UI refresh
441
+ this.messageSent.emit(conversationDetail);
335
442
  }
336
443
  }
337
- conversationDetail.Message = progressText;
338
- await conversationDetail.Save();
339
- // Emit update to trigger UI refresh
340
- this.messageSent.emit(conversationDetail);
341
444
  }
342
445
  }
343
446
  catch (error) {
@@ -345,12 +448,12 @@ export class MessageInputComponent {
345
448
  }
346
449
  console.log(`[${agentName}] Progress: ${progress.step} - ${progress.message} (${progress.percentage}%)`, {
347
450
  agentRunId: progressAgentRunId,
348
- conversationDetailId
451
+ conversationDetailId: conversationDetail.ID
349
452
  });
350
453
  };
351
454
  }
352
455
  /**
353
- * Process the message through agents (multi-stage: Conversation Manager -> possible sub-agent)
456
+ * Process the message through agents (multi-stage: Sage -> possible sub-agent)
354
457
  * Only called when there's no @mention and no implicit agent context
355
458
  */
356
459
  async processMessageThroughAgent(userMessage, mentionResult) {
@@ -360,31 +463,30 @@ export class MessageInputComponent {
360
463
  // This prevents race condition when user switches conversations during async processing
361
464
  const conversationId = userMessage.ConversationID;
362
465
  try {
363
- // Create AI message for Conversation Manager BEFORE invoking
364
- const md = new Metadata();
365
- conversationManagerMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
466
+ // Create AI message for Sage BEFORE invoking
467
+ conversationManagerMessage = await this.dataCache.createConversationDetail(this.currentUser);
366
468
  conversationManagerMessage.ConversationID = conversationId;
367
469
  conversationManagerMessage.Role = 'AI';
368
470
  conversationManagerMessage.Message = '⏳ Starting...';
369
471
  conversationManagerMessage.ParentID = userMessage.ID;
370
472
  conversationManagerMessage.Status = 'In-Progress';
371
473
  conversationManagerMessage.HiddenToUser = false;
372
- // Use the preloaded Conversation Manager agent instead of looking it up
474
+ // Use the preloaded Sage agent instead of looking it up
373
475
  if (this.converationManagerAgent?.ID) {
374
476
  conversationManagerMessage.AgentID = this.converationManagerAgent.ID;
375
477
  }
376
478
  await conversationManagerMessage.Save();
377
479
  this.messageSent.emit(conversationManagerMessage);
378
- // Use Conversation Manager to evaluate and route
379
- // Stage 1: Conversation Manager evaluates the message
480
+ // Use Sage to evaluate and route
481
+ // Stage 1: Sage evaluates the message
380
482
  taskId = this.activeTasks.add({
381
- agentName: 'Conversation Manager',
483
+ agentName: 'Sage',
382
484
  status: 'Evaluating message...',
383
485
  relatedMessageId: userMessage.ID,
384
486
  conversationDetailId: conversationManagerMessage.ID
385
487
  });
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
488
+ const result = await this.agentService.processMessage(conversationId, userMessage, this.conversationHistory, conversationManagerMessage.ID, this.createProgressCallback(conversationManagerMessage, 'Sage'));
489
+ // Remove Sage from active tasks
388
490
  if (taskId) {
389
491
  this.activeTasks.remove(taskId);
390
492
  taskId = null;
@@ -399,48 +501,79 @@ export class MessageInputComponent {
399
501
  userMessage.Status = 'Complete';
400
502
  await userMessage.Save();
401
503
  this.messageSent.emit(userMessage);
402
- console.warn('⚠️ Conversation Manager failed:', result?.agentRun?.ErrorMessage);
504
+ console.warn('⚠️ Sage failed:', result?.agentRun?.ErrorMessage);
403
505
  return;
404
506
  }
405
- console.log('🤖 Conversation Manager Response:', {
507
+ console.log('🤖 Sage Response:', {
406
508
  finalStep: result.agentRun.FinalStep,
407
509
  hasPayload: !!result.payload,
408
- hasMessage: !!result.agentRun.Message
510
+ hasMessage: !!result.agentRun.Message,
511
+ payloadKeys: result.payload ? Object.keys(result.payload) : [],
512
+ payload: result.payload // Full payload for debugging
409
513
  });
410
- // Stage 2: Check for sub-agent invocation
411
- if (result.agentRun.FinalStep === 'Success' && result.payload?.invokeAgent) {
514
+ // Stage 2: Check for task graph (multi-step orchestration)
515
+ if (result.payload?.taskGraph) {
516
+ console.log('📋 Task graph detected, starting task orchestration');
517
+ await this.handleTaskGraphExecution(userMessage, result, this.conversationId, conversationManagerMessage);
518
+ // Remove CM from active tasks
519
+ if (taskId) {
520
+ this.activeTasks.remove(taskId);
521
+ }
522
+ }
523
+ // Stage 3: Check for sub-agent invocation (single-step delegation)
524
+ else if (result.agentRun.FinalStep === 'Success' && result.payload?.invokeAgent) {
412
525
  // Reuse the existing conversationManagerMessage instead of creating new ones
413
526
  await this.handleSubAgentInvocation(userMessage, result, this.conversationId, conversationManagerMessage);
527
+ // Remove CM from active tasks
528
+ if (taskId) {
529
+ this.activeTasks.remove(taskId);
530
+ }
414
531
  }
415
- // Stage 3: Direct chat response from Conversation Manager
532
+ // Stage 4: Direct chat response from Sage
416
533
  else if (result.agentRun.FinalStep === 'Chat' && result.agentRun.Message) {
417
- // Update the existing message with the response
534
+ // Normal chat response
418
535
  conversationManagerMessage.Message = result.agentRun.Message;
419
536
  conversationManagerMessage.Status = 'Complete';
420
- if (result.agentRun.ID) {
421
- conversationManagerMessage.AgentRunID = result.agentRun.ID;
422
- }
423
537
  await conversationManagerMessage.Save();
424
538
  this.messageSent.emit(conversationManagerMessage);
425
- // Handle artifacts if any
539
+ // Handle artifacts if any (but NOT task graphs - those are intermediate work products)
426
540
  if (result.payload && Object.keys(result.payload).length > 0) {
427
541
  await this.createArtifactFromPayload(result.payload, conversationManagerMessage, result.agentRun.AgentID);
428
- console.log('🎨 Artifact created and linked to Conversation Manager message');
542
+ console.log('🎨 Artifact created and linked to Sage message');
429
543
  this.messageSent.emit(conversationManagerMessage);
430
544
  }
431
545
  userMessage.Status = 'Complete';
432
546
  await userMessage.Save();
433
547
  this.messageSent.emit(userMessage);
548
+ // Remove CM from active tasks
549
+ if (taskId) {
550
+ this.activeTasks.remove(taskId);
551
+ }
434
552
  }
435
- // Stage 4: Silent observation - check for agent continuity
553
+ // Stage 5: Silent observation - but check for message content first
436
554
  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);
555
+ // Check if there's a message to display even without payload/taskGraph
556
+ if (result.agentRun.Message) {
557
+ console.log('💬 Sage provided a message without payload');
558
+ conversationManagerMessage.Message = result.agentRun.Message;
559
+ conversationManagerMessage.Status = 'Complete';
560
+ conversationManagerMessage.HiddenToUser = false;
561
+ await conversationManagerMessage.Save();
562
+ this.messageSent.emit(conversationManagerMessage);
563
+ }
564
+ else {
565
+ console.log('🔇 Sage chose to observe silently');
566
+ // Hide the Sage message
567
+ conversationManagerMessage.HiddenToUser = true;
568
+ conversationManagerMessage.Status = 'Complete';
569
+ await conversationManagerMessage.Save();
570
+ this.messageSent.emit(conversationManagerMessage);
571
+ await this.handleSilentObservation(userMessage, this.conversationId);
572
+ }
573
+ // Remove CM from active tasks
574
+ if (taskId) {
575
+ this.activeTasks.remove(taskId);
576
+ }
444
577
  }
445
578
  }
446
579
  catch (error) {
@@ -464,30 +597,243 @@ export class MessageInputComponent {
464
597
  }
465
598
  }
466
599
  /**
467
- * Handle sub-agent invocation based on Conversation Manager's payload
600
+ * Handle task graph execution based on Sage's payload
601
+ * Creates tasks and orchestrates their execution
602
+ */
603
+ async handleTaskGraphExecution(userMessage, managerResult, conversationId, conversationManagerMessage) {
604
+ const taskGraph = managerResult.payload.taskGraph;
605
+ const workflowName = taskGraph.workflowName || 'Workflow';
606
+ const reasoning = taskGraph.reasoning || 'Executing multi-step workflow';
607
+ const taskCount = taskGraph.tasks?.length || 0;
608
+ console.log(`📋 Task graph execution requested: ${workflowName}`, {
609
+ reasoning,
610
+ taskCount
611
+ });
612
+ const md = new Metadata();
613
+ // Deduplicate tasks by tempId (LLM sometimes returns duplicates)
614
+ const seenTempIds = new Set();
615
+ const uniqueTasks = taskGraph.tasks.filter((task) => {
616
+ if (seenTempIds.has(task.tempId)) {
617
+ console.warn(`⚠️ Duplicate tempId detected on client, filtering: ${task.tempId} (${task.name})`);
618
+ return false;
619
+ }
620
+ seenTempIds.add(task.tempId);
621
+ return true;
622
+ });
623
+ const uniqueTaskCount = uniqueTasks.length;
624
+ console.log(`Filtered to ${uniqueTaskCount} unique tasks (${taskCount - uniqueTaskCount} duplicates removed)`);
625
+ const isSingleTask = uniqueTaskCount === 1;
626
+ // If single task, use direct agent execution (existing pattern with great PubSub support)
627
+ if (isSingleTask) {
628
+ const task = uniqueTasks[0];
629
+ const agentName = task.agentName;
630
+ // Update CM message
631
+ const delegationMessage = `👉 Delegating to **${agentName}**`;
632
+ await this.updateConversationDetail(conversationManagerMessage, delegationMessage, 'Complete');
633
+ // Execute single agent directly using existing pattern
634
+ await this.handleSingleTaskExecution(userMessage, task, agentName, conversationId, conversationManagerMessage);
635
+ return;
636
+ }
637
+ // Multi-step workflow - use server-side task orchestration
638
+ console.log(`📋 Multi-step workflow detected (${uniqueTaskCount} tasks), using task orchestration`);
639
+ // Update CM message with task summary (use unique tasks only)
640
+ const taskSummary = uniqueTasks.map((t) => `• ${t.name}`).join('\n');
641
+ await this.updateConversationDetail(conversationManagerMessage, `📋 Setting up multi-step workflow...\n\n**${workflowName}**\n${taskSummary}`, 'Complete');
642
+ // Step 2: Create new ConversationDetail for task execution updates
643
+ const taskExecutionMessage = await this.dataCache.createConversationDetail(this.currentUser);
644
+ taskExecutionMessage.ConversationID = conversationId;
645
+ taskExecutionMessage.Role = 'AI';
646
+ taskExecutionMessage.Message = '⏳ Starting workflow execution...';
647
+ taskExecutionMessage.ParentID = conversationManagerMessage.ID; // Thread under delegation message
648
+ taskExecutionMessage.Status = 'In-Progress';
649
+ taskExecutionMessage.HiddenToUser = false;
650
+ // No AgentID for now - this represents the task orchestration system
651
+ await taskExecutionMessage.Save();
652
+ this.messageSent.emit(taskExecutionMessage);
653
+ // Register for real-time updates via PubSub
654
+ this.activeTaskExecutionMessageIds.add(taskExecutionMessage.ID);
655
+ try {
656
+ // Get environment ID from user
657
+ const environmentId = this.currentUser.EnvironmentID || 'F51358F3-9447-4176-B313-BF8025FD8D09';
658
+ // Get session ID for PubSub
659
+ const sessionId = GraphQLDataProvider.Instance.sessionId || '';
660
+ // Step 3: Call ExecuteTaskGraph mutation (links to taskExecutionMessage)
661
+ const mutation = `
662
+ mutation ExecuteTaskGraph($taskGraphJson: String!, $conversationDetailId: String!, $environmentId: String!, $sessionId: String!) {
663
+ ExecuteTaskGraph(
664
+ taskGraphJson: $taskGraphJson
665
+ conversationDetailId: $conversationDetailId
666
+ environmentId: $environmentId
667
+ sessionId: $sessionId
668
+ ) {
669
+ success
670
+ errorMessage
671
+ results {
672
+ taskId
673
+ success
674
+ output
675
+ error
676
+ }
677
+ }
678
+ }
679
+ `;
680
+ const variables = {
681
+ taskGraphJson: JSON.stringify(taskGraph),
682
+ conversationDetailId: taskExecutionMessage.ID, // Link tasks to execution message, not CM message
683
+ environmentId: environmentId,
684
+ sessionId: sessionId
685
+ };
686
+ const result = await GraphQLDataProvider.Instance.ExecuteGQL(mutation, variables);
687
+ console.log('📊 ExecuteTaskGraph result:', {
688
+ hasData: !!result?.data,
689
+ hasErrors: !!result?.errors,
690
+ data: result?.data,
691
+ errors: result?.errors
692
+ });
693
+ // Step 4: Update task execution message with results
694
+ // Check for GraphQL errors first
695
+ if (result?.errors && result.errors.length > 0) {
696
+ const errorMsg = result.errors.map((e) => e.message).join(', ');
697
+ console.error('❌ GraphQL errors:', result.errors);
698
+ taskExecutionMessage.Message = `❌ **${workflowName}** failed: ${errorMsg}`;
699
+ taskExecutionMessage.Status = 'Error';
700
+ taskExecutionMessage.Error = errorMsg;
701
+ }
702
+ else if (result?.data?.ExecuteTaskGraph?.success) {
703
+ console.log('✅ Task graph execution completed successfully');
704
+ taskExecutionMessage.Message = `✅ **${workflowName}** completed successfully`;
705
+ taskExecutionMessage.Status = 'Complete';
706
+ }
707
+ else {
708
+ const errorMsg = result?.data?.ExecuteTaskGraph?.errorMessage || 'Unknown error';
709
+ console.error('❌ Task graph execution failed:', errorMsg);
710
+ taskExecutionMessage.Message = `❌ **${workflowName}** failed: ${errorMsg}`;
711
+ taskExecutionMessage.Status = 'Error';
712
+ taskExecutionMessage.Error = errorMsg;
713
+ }
714
+ await taskExecutionMessage.Save();
715
+ this.messageSent.emit(taskExecutionMessage);
716
+ // Unregister from real-time updates (task complete)
717
+ this.activeTaskExecutionMessageIds.delete(taskExecutionMessage.ID);
718
+ // Mark user message as complete
719
+ userMessage.Status = 'Complete';
720
+ await userMessage.Save();
721
+ this.messageSent.emit(userMessage);
722
+ }
723
+ catch (error) {
724
+ console.error('❌ Error executing task graph:', error);
725
+ taskExecutionMessage.Message = `❌ **${workflowName}** - Error: ${String(error)}`;
726
+ taskExecutionMessage.Status = 'Error';
727
+ taskExecutionMessage.Error = String(error);
728
+ await taskExecutionMessage.Save();
729
+ this.messageSent.emit(taskExecutionMessage);
730
+ // Unregister from real-time updates (task failed)
731
+ this.activeTaskExecutionMessageIds.delete(taskExecutionMessage.ID);
732
+ userMessage.Status = 'Complete';
733
+ await userMessage.Save();
734
+ this.messageSent.emit(userMessage);
735
+ }
736
+ }
737
+ async updateConversationDetail(convoDetail, message, status) {
738
+ if (convoDetail.Status === 'Complete' || convoDetail.Status === 'Error') {
739
+ return; // Do not update completed or errored messages
740
+ }
741
+ const maxAttempts = 2;
742
+ let attempts = 0, done = false;
743
+ while (attempts < maxAttempts && !done) {
744
+ convoDetail.Message = message;
745
+ convoDetail.Status = status;
746
+ await convoDetail.Save();
747
+ if (convoDetail.Message === message && convoDetail.Status === status) {
748
+ done = true;
749
+ this.messageSent.emit(convoDetail);
750
+ }
751
+ else {
752
+ console.warn(` ⚠️ ConversationDetail update attempt ${attempts + 1} did not persist. ${attempts + 1 < maxAttempts ? 'Retrying...' : 'Giving up.'}`);
753
+ }
754
+ attempts++;
755
+ }
756
+ }
757
+ /**
758
+ * Handle single task execution from task graph using direct agent execution
759
+ * Uses the existing agent execution pattern with PubSub support
760
+ */
761
+ async handleSingleTaskExecution(userMessage, task, // Task definition from taskGraph
762
+ agentName, conversationId, conversationManagerMessage) {
763
+ try {
764
+ // Look up the agent
765
+ const agent = AIEngineBase.Instance.Agents.find(a => a.Name === agentName);
766
+ if (!agent) {
767
+ throw new Error(`Agent not found: ${agentName}`);
768
+ }
769
+ // Create AI response message for the agent execution
770
+ const agentResponseMessage = await this.dataCache.createConversationDetail(this.currentUser);
771
+ agentResponseMessage.ConversationID = conversationId;
772
+ agentResponseMessage.Role = 'AI';
773
+ agentResponseMessage.Message = '⏳ Starting...';
774
+ agentResponseMessage.ParentID = conversationManagerMessage.ID; // Thread under delegation
775
+ agentResponseMessage.Status = 'In-Progress';
776
+ agentResponseMessage.HiddenToUser = false;
777
+ agentResponseMessage.AgentID = agent.ID;
778
+ await agentResponseMessage.Save();
779
+ this.messageSent.emit(agentResponseMessage);
780
+ // Add to active tasks
781
+ const newTaskId = this.activeTasks.add({
782
+ agentName: agentName,
783
+ status: 'Starting...',
784
+ relatedMessageId: userMessage.ID,
785
+ conversationDetailId: agentResponseMessage.ID
786
+ });
787
+ // Invoke agent with task's input payload
788
+ const agentResult = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.conversationHistory, task.description || task.name, agentResponseMessage.ID, task.inputPayload, // Pass the task's input payload
789
+ this.createProgressCallback(agentResponseMessage, agentName));
790
+ // Remove from active tasks
791
+ this.activeTasks.remove(newTaskId);
792
+ if (agentResult && agentResult.success) {
793
+ // Update message with result
794
+ await this.updateConversationDetail(agentResponseMessage, agentResult.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete');
795
+ // Handle artifacts
796
+ if (agentResult.payload && Object.keys(agentResult.payload).length > 0) {
797
+ await this.createArtifactFromPayload(agentResult.payload, agentResponseMessage, agentResult.agentRun.AgentID);
798
+ console.log('🎨 Artifact created from single task execution');
799
+ this.messageSent.emit(agentResponseMessage);
800
+ }
801
+ }
802
+ else {
803
+ // Handle failure
804
+ const errorMsg = agentResult?.agentRun?.ErrorMessage || 'Agent execution failed';
805
+ agentResponseMessage.Message = `❌ **${agentName}** failed: ${errorMsg}`;
806
+ agentResponseMessage.Status = 'Error';
807
+ agentResponseMessage.Error = errorMsg;
808
+ await agentResponseMessage.Save();
809
+ this.messageSent.emit(agentResponseMessage);
810
+ }
811
+ // Mark user message as complete
812
+ userMessage.Status = 'Complete';
813
+ await userMessage.Save();
814
+ this.messageSent.emit(userMessage);
815
+ }
816
+ catch (error) {
817
+ console.error('❌ Error in single task execution:', error);
818
+ userMessage.Status = 'Complete';
819
+ await userMessage.Save();
820
+ this.messageSent.emit(userMessage);
821
+ }
822
+ }
823
+ /**
824
+ * Handle sub-agent invocation based on Sage's payload
468
825
  * Reuses the existing conversationManagerMessage to avoid creating multiple records
469
826
  */
470
827
  async handleSubAgentInvocation(userMessage, managerResult, conversationId, conversationManagerMessage) {
471
828
  const payload = managerResult.payload;
472
829
  const agentName = payload.invokeAgent;
473
830
  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
831
  // Now create a NEW message for the sub-agent execution
485
832
  try {
486
833
  // Look up the agent to get its ID
487
834
  const agent = AIEngineBase.Instance.Agents.find(a => a.Name === agentName);
488
835
  // 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);
836
+ const agentResponseMessage = await this.dataCache.createConversationDetail(this.currentUser);
491
837
  agentResponseMessage.ConversationID = conversationId;
492
838
  agentResponseMessage.Role = 'AI';
493
839
  agentResponseMessage.Message = '⏳ Starting...'; // Initial message
@@ -510,23 +856,16 @@ export class MessageInputComponent {
510
856
  });
511
857
  // Invoke the sub-agent with progress callback
512
858
  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));
859
+ this.createProgressCallback(agentResponseMessage, agentName));
514
860
  // Remove from active tasks
515
861
  this.activeTasks.remove(newTaskId);
516
862
  if (subResult && subResult.success) {
517
863
  // 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
864
+ // Store the agent ID for display
521
865
  if (subResult.agentRun.AgentID) {
522
866
  agentResponseMessage.AgentID = subResult.agentRun.AgentID;
523
867
  }
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);
868
+ await this.updateConversationDetail(agentResponseMessage, subResult.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete');
530
869
  // Handle artifacts from sub-agent if any
531
870
  if (subResult.payload && Object.keys(subResult.payload).length > 0) {
532
871
  await this.createArtifactFromPayload(subResult.payload, agentResponseMessage, subResult.agentRun.AgentID);
@@ -542,30 +881,18 @@ export class MessageInputComponent {
542
881
  else {
543
882
  // Sub-agent failed - attempt auto-retry once
544
883
  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);
884
+ await this.updateConversationDetail(conversationManagerMessage, `👉 **${agentName}** will handle this request...\n\n⚠️ First attempt failed, retrying...`, conversationManagerMessage.Status);
549
885
  // Update the existing agentResponseMessage to show retry status
550
- agentResponseMessage.Message = 'Retrying...';
551
- await agentResponseMessage.Save();
552
- this.messageSent.emit(agentResponseMessage);
886
+ await this.updateConversationDetail(agentResponseMessage, "Retrying...", agentResponseMessage.Status);
553
887
  // Retry the sub-agent
554
888
  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)`));
889
+ this.createProgressCallback(agentResponseMessage, `${agentName} (retry)`));
556
890
  if (retryResult && retryResult.success) {
557
891
  // Retry succeeded - update the same message
558
- agentResponseMessage.Message = retryResult.agentRun?.Message || `✅ **${agentName}** completed`;
559
- agentResponseMessage.Status = 'Complete';
560
892
  if (retryResult.agentRun.AgentID) {
561
893
  agentResponseMessage.AgentID = retryResult.agentRun.AgentID;
562
894
  }
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);
895
+ await this.updateConversationDetail(agentResponseMessage, retryResult.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete');
569
896
  // Handle artifacts
570
897
  if (retryResult.payload && Object.keys(retryResult.payload).length > 0) {
571
898
  await this.createArtifactFromPayload(retryResult.payload, agentResponseMessage, retryResult.agentRun.AgentID);
@@ -601,11 +928,11 @@ export class MessageInputComponent {
601
928
  }
602
929
  }
603
930
  /**
604
- * Handle silent observation - when Conversation Manager stays silent,
931
+ * Handle silent observation - when Sage stays silent,
605
932
  * check if we should continue with the last agent for iterative refinement
606
933
  */
607
934
  async handleSilentObservation(userMessage, conversationId) {
608
- // Find the last AI message (excluding Conversation Manager) in the conversation history
935
+ // Find the last AI message (excluding Sage) in the conversation history
609
936
  const lastAIMessage = this.conversationHistory
610
937
  .slice()
611
938
  .reverse()
@@ -621,7 +948,6 @@ export class MessageInputComponent {
621
948
  return;
622
949
  }
623
950
  // Load the agent entity to get its name
624
- const md = new Metadata();
625
951
  const rv = new RunView();
626
952
  const agentResult = await rv.RunView({
627
953
  EntityName: 'AI Agents',
@@ -645,6 +971,7 @@ export class MessageInputComponent {
645
971
  ResultType: 'entity_object'
646
972
  }, this.currentUser);
647
973
  let previousPayload = null;
974
+ let previousArtifactInfo = null;
648
975
  if (artifactResult.Success && artifactResult.Results && artifactResult.Results.length > 0) {
649
976
  // Load the artifact version content
650
977
  const junctionRecord = artifactResult.Results[0];
@@ -658,7 +985,12 @@ export class MessageInputComponent {
658
985
  if (version.Content) {
659
986
  try {
660
987
  previousPayload = JSON.parse(version.Content);
661
- console.log('📦 Loaded previous OUTPUT artifact as payload for continuity');
988
+ previousArtifactInfo = {
989
+ artifactId: version.ArtifactID,
990
+ versionId: version.ID,
991
+ versionNumber: version.VersionNumber || 1
992
+ };
993
+ console.log('📦 Loaded previous OUTPUT artifact as payload for continuity', previousArtifactInfo);
662
994
  }
663
995
  catch (error) {
664
996
  console.warn('⚠️ Could not parse previous artifact content:', error);
@@ -667,7 +999,7 @@ export class MessageInputComponent {
667
999
  }
668
1000
  }
669
1001
  // Create status message showing agent continuity
670
- const statusMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
1002
+ const statusMessage = await this.dataCache.createConversationDetail(this.currentUser);
671
1003
  statusMessage.ConversationID = conversationId;
672
1004
  statusMessage.Role = 'AI';
673
1005
  statusMessage.Message = `Continuing with **${agentName}** for refinement...`;
@@ -686,12 +1018,12 @@ export class MessageInputComponent {
686
1018
  });
687
1019
  try {
688
1020
  // 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));
1021
+ 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));
690
1022
  // Remove from active tasks
691
1023
  this.activeTasks.remove(taskId);
692
1024
  if (continuityResult && continuityResult.success) {
693
1025
  // Create response message
694
- const agentResponseMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
1026
+ const agentResponseMessage = await this.dataCache.createConversationDetail(this.currentUser);
695
1027
  agentResponseMessage.ConversationID = conversationId;
696
1028
  agentResponseMessage.Role = 'AI';
697
1029
  agentResponseMessage.Message = continuityResult.agentRun?.Message || `✅ **${agentName}** completed refinement`;
@@ -701,9 +1033,10 @@ export class MessageInputComponent {
701
1033
  agentResponseMessage.AgentID = lastAIMessage.AgentID;
702
1034
  await agentResponseMessage.Save();
703
1035
  this.messageSent.emit(agentResponseMessage);
704
- // Handle artifacts from agent if any
1036
+ // Handle artifacts from agent if any - create new version if continuing same agent
705
1037
  if (continuityResult.payload && Object.keys(continuityResult.payload).length > 0) {
706
- await this.createArtifactFromPayload(continuityResult.payload, agentResponseMessage, lastAIMessage.AgentID);
1038
+ await this.createArtifactFromPayload(continuityResult.payload, agentResponseMessage, lastAIMessage.AgentID, previousArtifactInfo // Pass artifact info to create new version
1039
+ );
707
1040
  console.log('🎨 Artifact created from agent continuity');
708
1041
  this.messageSent.emit(agentResponseMessage);
709
1042
  }
@@ -737,48 +1070,9 @@ export class MessageInputComponent {
737
1070
  this.messageSent.emit(userMessage);
738
1071
  }
739
1072
  }
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
1073
  /**
780
1074
  * Invoke an agent directly when mentioned with @ symbol
781
- * Bypasses Conversation Manager completely - no status messages
1075
+ * Bypasses Sage completely - no status messages
782
1076
  */
783
1077
  async invokeAgentDirectly(userMessage, agentMention, conversationId) {
784
1078
  const agentName = agentMention.name;
@@ -797,8 +1091,7 @@ export class MessageInputComponent {
797
1091
  // Look up the agent to get its ID
798
1092
  const agent = AIEngineBase.Instance.Agents.find(a => a.Name === agentName);
799
1093
  // 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);
1094
+ const agentResponseMessage = await this.dataCache.createConversationDetail(this.currentUser);
802
1095
  agentResponseMessage.ConversationID = conversationId;
803
1096
  agentResponseMessage.Role = 'AI';
804
1097
  agentResponseMessage.Message = '⏳ Starting...'; // Initial message
@@ -814,22 +1107,14 @@ export class MessageInputComponent {
814
1107
  this.messageSent.emit(agentResponseMessage);
815
1108
  // Invoke the agent directly
816
1109
  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));
1110
+ this.createProgressCallback(agentResponseMessage, agentName));
818
1111
  // Remove from active tasks
819
1112
  this.activeTasks.remove(taskId);
820
1113
  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
1114
  if (result.agentRun.AgentID) {
825
1115
  agentResponseMessage.AgentID = result.agentRun.AgentID;
826
1116
  }
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);
1117
+ await this.updateConversationDetail(agentResponseMessage, result.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete');
833
1118
  // Handle artifacts
834
1119
  if (result.payload && Object.keys(result.payload).length > 0) {
835
1120
  await this.createArtifactFromPayload(result.payload, agentResponseMessage, result.agentRun.AgentID);
@@ -842,8 +1127,7 @@ export class MessageInputComponent {
842
1127
  }
843
1128
  else {
844
1129
  // Agent failed - create error message
845
- const md = new Metadata();
846
- const errorMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
1130
+ const errorMessage = await this.dataCache.createConversationDetail(this.currentUser);
847
1131
  errorMessage.ConversationID = conversationId;
848
1132
  errorMessage.Role = 'AI';
849
1133
  errorMessage.Message = `❌ **@${agentName}** failed\n\n${result?.agentRun?.ErrorMessage || 'Unknown error'}`;
@@ -861,8 +1145,7 @@ export class MessageInputComponent {
861
1145
  catch (error) {
862
1146
  console.error(`❌ Error invoking mentioned agent ${agentName}:`, error);
863
1147
  this.activeTasks.remove(taskId);
864
- const md = new Metadata();
865
- const errorMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
1148
+ const errorMessage = await this.dataCache.createConversationDetail(this.currentUser);
866
1149
  errorMessage.ConversationID = conversationId;
867
1150
  errorMessage.Role = 'AI';
868
1151
  errorMessage.Message = `❌ **@${agentName}** encountered an error\n\n${String(error)}`;
@@ -879,11 +1162,10 @@ export class MessageInputComponent {
879
1162
  }
880
1163
  /**
881
1164
  * Continue with the same agent from previous message (implicit continuation)
882
- * Bypasses Conversation Manager - no status messages
1165
+ * Bypasses Sage - no status messages
883
1166
  */
884
1167
  async continueWithAgent(userMessage, agentId, conversationId) {
885
1168
  // Load the agent entity to get its name
886
- const md = new Metadata();
887
1169
  const rv = new RunView();
888
1170
  const agentResult = await rv.RunView({
889
1171
  EntityName: 'AI Agents',
@@ -891,12 +1173,53 @@ export class MessageInputComponent {
891
1173
  ResultType: 'entity_object'
892
1174
  }, this.currentUser);
893
1175
  if (!agentResult.Success || !agentResult.Results || agentResult.Results.length === 0) {
894
- console.warn('⚠️ Could not load agent for continuation - falling back to Conversation Manager');
1176
+ console.warn('⚠️ Could not load agent for continuation - falling back to Sage');
895
1177
  await this.processMessageThroughAgent(userMessage, { mentions: [], agentMention: null, userMentions: [] });
896
1178
  return;
897
1179
  }
898
1180
  const agent = agentResult.Results[0];
899
1181
  const agentName = agent.Name || 'Agent';
1182
+ // Find the last AI message from this same agent to get the previous OUTPUT artifact
1183
+ const lastAIMessage = this.conversationHistory
1184
+ .slice()
1185
+ .reverse()
1186
+ .find(msg => msg.Role === 'AI' && msg.AgentID === agentId);
1187
+ let previousPayload = null;
1188
+ let previousArtifactInfo = null;
1189
+ if (lastAIMessage) {
1190
+ // Load the OUTPUT artifact from the last agent message
1191
+ const artifactResult = await rv.RunView({
1192
+ EntityName: 'MJ: Conversation Detail Artifacts',
1193
+ ExtraFilter: `ConversationDetailID='${lastAIMessage.ID}' AND Direction='Output'`,
1194
+ ResultType: 'entity_object'
1195
+ }, this.currentUser);
1196
+ if (artifactResult.Success && artifactResult.Results && artifactResult.Results.length > 0) {
1197
+ // Load the artifact version content
1198
+ const junctionRecord = artifactResult.Results[0];
1199
+ const versionResult = await rv.RunView({
1200
+ EntityName: 'MJ: Artifact Versions',
1201
+ ExtraFilter: `ID='${junctionRecord.ArtifactVersionID}'`,
1202
+ ResultType: 'entity_object'
1203
+ }, this.currentUser);
1204
+ if (versionResult.Success && versionResult.Results && versionResult.Results.length > 0) {
1205
+ const version = versionResult.Results[0];
1206
+ if (version.Content) {
1207
+ try {
1208
+ previousPayload = JSON.parse(version.Content);
1209
+ previousArtifactInfo = {
1210
+ artifactId: version.ArtifactID,
1211
+ versionId: version.ID,
1212
+ versionNumber: version.VersionNumber || 1
1213
+ };
1214
+ console.log('📦 Loaded previous OUTPUT artifact as payload for continuation', previousArtifactInfo);
1215
+ }
1216
+ catch (error) {
1217
+ console.warn('⚠️ Could not parse previous artifact content:', error);
1218
+ }
1219
+ }
1220
+ }
1221
+ }
1222
+ }
900
1223
  // Add agent to active tasks
901
1224
  const taskId = this.activeTasks.add({
902
1225
  agentName: agentName,
@@ -910,7 +1233,7 @@ export class MessageInputComponent {
910
1233
  await userMessage.Save();
911
1234
  this.messageSent.emit(userMessage);
912
1235
  // Create AI response message BEFORE invoking agent (for duration tracking)
913
- const agentResponseMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
1236
+ const agentResponseMessage = await this.dataCache.createConversationDetail(this.currentUser);
914
1237
  agentResponseMessage.ConversationID = conversationId;
915
1238
  agentResponseMessage.Role = 'AI';
916
1239
  agentResponseMessage.Message = '⏳ Starting...'; // Initial message
@@ -921,24 +1244,18 @@ export class MessageInputComponent {
921
1244
  // Save the record to establish __mj_CreatedAt timestamp
922
1245
  await agentResponseMessage.Save();
923
1246
  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));
1247
+ // Invoke the agent directly (continuation) with previous payload if available
1248
+ 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
1249
+ this.createProgressCallback(agentResponseMessage, agentName));
927
1250
  // Remove from active tasks
928
1251
  this.activeTasks.remove(taskId);
929
1252
  if (result && result.success) {
930
1253
  // 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
1254
+ await this.updateConversationDetail(agentResponseMessage, result.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete');
1255
+ // Handle artifacts - create new version if continuing with same agent and artifact
940
1256
  if (result.payload && Object.keys(result.payload).length > 0) {
941
- await this.createArtifactFromPayload(result.payload, agentResponseMessage, agentId);
1257
+ await this.createArtifactFromPayload(result.payload, agentResponseMessage, agentId, previousArtifactInfo // Pass artifact info to create new version instead of new artifact
1258
+ );
942
1259
  this.messageSent.emit(agentResponseMessage);
943
1260
  }
944
1261
  // Mark user message as complete
@@ -948,7 +1265,7 @@ export class MessageInputComponent {
948
1265
  }
949
1266
  else {
950
1267
  // Agent failed - create error message
951
- const errorMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
1268
+ const errorMessage = await this.dataCache.createConversationDetail(this.currentUser);
952
1269
  errorMessage.ConversationID = conversationId;
953
1270
  errorMessage.Role = 'AI';
954
1271
  errorMessage.Message = `❌ **${agentName}** failed\n\n${result?.agentRun?.ErrorMessage || 'Unknown error'}`;
@@ -966,7 +1283,7 @@ export class MessageInputComponent {
966
1283
  catch (error) {
967
1284
  console.error(`❌ Error continuing with agent ${agentName}:`, error);
968
1285
  this.activeTasks.remove(taskId);
969
- const errorMessage = await md.GetEntityObject('Conversation Details', this.currentUser);
1286
+ const errorMessage = await this.dataCache.createConversationDetail(this.currentUser);
970
1287
  errorMessage.ConversationID = conversationId;
971
1288
  errorMessage.Role = 'AI';
972
1289
  errorMessage.Message = `❌ **${agentName}** encountered an error\n\n${String(error)}`;
@@ -983,34 +1300,52 @@ export class MessageInputComponent {
983
1300
  }
984
1301
  /**
985
1302
  * Creates an artifact from an agent's payload and links it to the conversation detail
1303
+ * If previousArtifactInfo is provided, creates a new version of the existing artifact
1304
+ * Otherwise, creates a new artifact with version 1
986
1305
  * @param payload The agent's payload object
987
1306
  * @param message The conversation detail message to link to
988
1307
  * @param agentId The ID of the agent that produced the payload
1308
+ * @param previousArtifactInfo Optional info about previous artifact to create new version
989
1309
  */
990
- async createArtifactFromPayload(payload, message, agentId) {
1310
+ async createArtifactFromPayload(payload, message, agentId, previousArtifactInfo) {
991
1311
  try {
992
1312
  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;
1313
+ let artifactId;
1314
+ let newVersionNumber;
1315
+ // If we have previous artifact info, we're creating a new version of existing artifact
1316
+ if (previousArtifactInfo) {
1317
+ artifactId = previousArtifactInfo.artifactId;
1318
+ newVersionNumber = previousArtifactInfo.versionNumber + 1;
1319
+ console.log(`📦 Creating version ${newVersionNumber} of existing artifact ${artifactId}`);
1320
+ }
1321
+ else {
1322
+ // Create new Artifact header
1323
+ const artifact = await md.GetEntityObject('MJ: Artifacts', this.currentUser);
1324
+ // Look up agent to get name and default artifact type
1325
+ const agent = agentId
1326
+ ? AIEngineBase.Instance?.Agents?.find(a => a.ID === agentId)
1327
+ : null;
1328
+ const agentName = agent?.Name || 'Agent';
1329
+ artifact.Name = `${agentName} Payload - ${new Date().toLocaleString()}`;
1330
+ artifact.Description = `Payload returned by ${agentName}`;
1331
+ // Use agent's DefaultArtifactTypeID if available, otherwise fall back to JSON
1332
+ const defaultArtifactTypeId = agent?.DefaultArtifactTypeID;
1333
+ artifact.TypeID = defaultArtifactTypeId || this.JSON_ARTIFACT_TYPE_ID;
1334
+ artifact.UserID = this.currentUser.ID;
1335
+ artifact.EnvironmentID = this.currentUser.EnvironmentID || 'F51358F3-9447-4176-B313-BF8025FD8D09';
1336
+ const artifactSaved = await artifact.Save();
1337
+ if (!artifactSaved) {
1338
+ console.error('Failed to save artifact');
1339
+ return;
1340
+ }
1341
+ artifactId = artifact.ID;
1342
+ newVersionNumber = 1;
1343
+ console.log(`📦 Creating new artifact ${artifactId} with version 1`);
1009
1344
  }
1010
1345
  // Create Artifact Version with content
1011
1346
  const version = await md.GetEntityObject('MJ: Artifact Versions', this.currentUser);
1012
- version.ArtifactID = artifact.ID;
1013
- version.VersionNumber = 1;
1347
+ version.ArtifactID = artifactId;
1348
+ version.VersionNumber = newVersionNumber;
1014
1349
  version.Content = JSON.stringify(payload, null, 2);
1015
1350
  version.UserID = this.currentUser.ID;
1016
1351
  const versionSaved = await version.Save();
@@ -1027,7 +1362,30 @@ export class MessageInputComponent {
1027
1362
  if (!junctionSaved) {
1028
1363
  console.error('Failed to create artifact-message association');
1029
1364
  }
1030
- this.artifactCreated.emit({ artifactId: artifact.ID, versionId: version.ID, conversationDetailId: message.ID, name: artifact.Name });
1365
+ // Emit with artifact name (load from DB if versioning existing artifact)
1366
+ let artifactName;
1367
+ if (previousArtifactInfo) {
1368
+ const artifactEntity = await md.GetEntityObject('MJ: Artifacts', this.currentUser);
1369
+ if (await artifactEntity.Load(artifactId)) {
1370
+ artifactName = artifactEntity.Name || 'Artifact';
1371
+ }
1372
+ else {
1373
+ artifactName = 'Artifact';
1374
+ }
1375
+ }
1376
+ else {
1377
+ const agentName = agentId
1378
+ ? AIEngineBase.Instance?.Agents?.find(a => a.ID === agentId)?.Name || 'Agent'
1379
+ : 'Agent';
1380
+ artifactName = `${agentName} Payload - ${new Date().toLocaleString()}`;
1381
+ }
1382
+ this.artifactCreated.emit({
1383
+ artifactId,
1384
+ versionId: version.ID,
1385
+ versionNumber: newVersionNumber,
1386
+ conversationDetailId: message.ID,
1387
+ name: artifactName
1388
+ });
1031
1389
  }
1032
1390
  catch (error) {
1033
1391
  console.error('Error creating artifact from payload:', error);
@@ -1087,7 +1445,7 @@ export class MessageInputComponent {
1087
1445
  // Don't show error to user - naming failures should be silent
1088
1446
  }
1089
1447
  }
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)); };
1448
+ 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.DataCacheService), i0.ɵɵdirectiveInject(i6.ActiveTasksService), i0.ɵɵdirectiveInject(i7.MentionAutocompleteService), i0.ɵɵdirectiveInject(i8.MentionParserService)); };
1091
1449
  static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MessageInputComponent, selectors: [["mj-message-input"]], viewQuery: function MessageInputComponent_Query(rf, ctx) { if (rf & 1) {
1092
1450
  i0.ɵɵviewQuery(_c0, 5);
1093
1451
  } if (rf & 2) {
@@ -1124,12 +1482,12 @@ export class MessageInputComponent {
1124
1482
  i0.ɵɵproperty("disabled", ctx.disabled);
1125
1483
  i0.ɵɵadvance(2);
1126
1484
  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}"] });
1485
+ } }, dependencies: [i9.NgIf, i10.DefaultValueAccessor, i10.NgControlStatus, i10.NgModel, i11.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
1486
  }
1129
1487
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MessageInputComponent, [{
1130
1488
  type: Component,
1131
1489
  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: [{
1490
+ }], () => [{ type: i1.DialogService }, { type: i2.ToastService }, { type: i3.ConversationAgentService }, { type: i4.ConversationStateService }, { type: i5.DataCacheService }, { type: i6.ActiveTasksService }, { type: i7.MentionAutocompleteService }, { type: i8.MentionParserService }], { conversationId: [{
1133
1491
  type: Input
1134
1492
  }], currentUser: [{
1135
1493
  type: Input
@@ -1155,5 +1513,5 @@ export class MessageInputComponent {
1155
1513
  type: ViewChild,
1156
1514
  args: ['messageTextarea']
1157
1515
  }] }); })();
1158
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MessageInputComponent, { className: "MessageInputComponent", filePath: "src/lib/components/message/message-input.component.ts", lineNumber: 21 }); })();
1516
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MessageInputComponent, { className: "MessageInputComponent", filePath: "src/lib/components/message/message-input.component.ts", lineNumber: 23 }); })();
1159
1517
  //# sourceMappingURL=message-input.component.js.map