@memberjunction/ng-conversations 2.111.1 → 2.113.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/artifact-create-modal.component.js +1 -1
  2. package/dist/lib/components/collection/artifact-create-modal.component.js.map +1 -1
  3. package/dist/lib/components/collection/collection-artifact-card.component.d.ts +8 -7
  4. package/dist/lib/components/collection/collection-artifact-card.component.d.ts.map +1 -1
  5. package/dist/lib/components/collection/collection-artifact-card.component.js +52 -36
  6. package/dist/lib/components/collection/collection-artifact-card.component.js.map +1 -1
  7. package/dist/lib/components/collection/collection-view.component.d.ts +28 -9
  8. package/dist/lib/components/collection/collection-view.component.d.ts.map +1 -1
  9. package/dist/lib/components/collection/collection-view.component.js +124 -58
  10. package/dist/lib/components/collection/collection-view.component.js.map +1 -1
  11. package/dist/lib/components/collection/collections-full-view.component.d.ts +18 -6
  12. package/dist/lib/components/collection/collections-full-view.component.d.ts.map +1 -1
  13. package/dist/lib/components/collection/collections-full-view.component.js +82 -58
  14. package/dist/lib/components/collection/collections-full-view.component.js.map +1 -1
  15. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +37 -0
  16. package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
  17. package/dist/lib/components/conversation/conversation-chat-area.component.js +278 -162
  18. package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
  19. package/dist/lib/components/conversation/conversation-list.component.d.ts +10 -3
  20. package/dist/lib/components/conversation/conversation-list.component.d.ts.map +1 -1
  21. package/dist/lib/components/conversation/conversation-list.component.js +118 -77
  22. package/dist/lib/components/conversation/conversation-list.component.js.map +1 -1
  23. package/dist/lib/components/global-tasks/global-tasks-panel.component.d.ts +25 -0
  24. package/dist/lib/components/global-tasks/global-tasks-panel.component.d.ts.map +1 -0
  25. package/dist/lib/components/global-tasks/global-tasks-panel.component.js +206 -0
  26. package/dist/lib/components/global-tasks/global-tasks-panel.component.js.map +1 -0
  27. package/dist/lib/components/message/conversation-message-rating.component.d.ts +47 -0
  28. package/dist/lib/components/message/conversation-message-rating.component.d.ts.map +1 -0
  29. package/dist/lib/components/message/conversation-message-rating.component.js +224 -0
  30. package/dist/lib/components/message/conversation-message-rating.component.js.map +1 -0
  31. package/dist/lib/components/message/message-input-box.component.d.ts.map +1 -1
  32. package/dist/lib/components/message/message-input-box.component.js +2 -12
  33. package/dist/lib/components/message/message-input-box.component.js.map +1 -1
  34. package/dist/lib/components/message/message-input.component.d.ts +17 -2
  35. package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
  36. package/dist/lib/components/message/message-input.component.js +258 -275
  37. package/dist/lib/components/message/message-input.component.js.map +1 -1
  38. package/dist/lib/components/message/message-item.component.d.ts +31 -1
  39. package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
  40. package/dist/lib/components/message/message-item.component.js +232 -116
  41. package/dist/lib/components/message/message-item.component.js.map +1 -1
  42. package/dist/lib/components/message/message-list.component.d.ts +3 -1
  43. package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
  44. package/dist/lib/components/message/message-list.component.js +11 -2
  45. package/dist/lib/components/message/message-list.component.js.map +1 -1
  46. package/dist/lib/components/navigation/conversation-navigation.component.d.ts +7 -1
  47. package/dist/lib/components/navigation/conversation-navigation.component.d.ts.map +1 -1
  48. package/dist/lib/components/navigation/conversation-navigation.component.js +24 -14
  49. package/dist/lib/components/navigation/conversation-navigation.component.js.map +1 -1
  50. package/dist/lib/components/tasks/tasks-dropdown.component.d.ts +17 -28
  51. package/dist/lib/components/tasks/tasks-dropdown.component.d.ts.map +1 -1
  52. package/dist/lib/components/tasks/tasks-dropdown.component.js +175 -217
  53. package/dist/lib/components/tasks/tasks-dropdown.component.js.map +1 -1
  54. package/dist/lib/components/workspace/conversation-workspace.component.d.ts +14 -8
  55. package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
  56. package/dist/lib/components/workspace/conversation-workspace.component.js +103 -64
  57. package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
  58. package/dist/lib/conversations.module.d.ts +54 -52
  59. package/dist/lib/conversations.module.d.ts.map +1 -1
  60. package/dist/lib/conversations.module.js +11 -3
  61. package/dist/lib/conversations.module.js.map +1 -1
  62. package/dist/lib/models/conversation-complete-query.model.d.ts +31 -22
  63. package/dist/lib/models/conversation-complete-query.model.d.ts.map +1 -1
  64. package/dist/lib/models/conversation-complete-query.model.js +5 -2
  65. package/dist/lib/models/conversation-complete-query.model.js.map +1 -1
  66. package/dist/lib/models/lazy-artifact-info.d.ts +3 -0
  67. package/dist/lib/models/lazy-artifact-info.d.ts.map +1 -1
  68. package/dist/lib/models/lazy-artifact-info.js +7 -2
  69. package/dist/lib/models/lazy-artifact-info.js.map +1 -1
  70. package/dist/lib/services/active-tasks.service.d.ts +18 -0
  71. package/dist/lib/services/active-tasks.service.d.ts.map +1 -1
  72. package/dist/lib/services/active-tasks.service.js +53 -3
  73. package/dist/lib/services/active-tasks.service.js.map +1 -1
  74. package/dist/lib/services/agent-state.service.d.ts.map +1 -1
  75. package/dist/lib/services/agent-state.service.js +5 -5
  76. package/dist/lib/services/agent-state.service.js.map +1 -1
  77. package/dist/lib/services/artifact-permission.service.d.ts.map +1 -1
  78. package/dist/lib/services/artifact-permission.service.js +3 -1
  79. package/dist/lib/services/artifact-permission.service.js.map +1 -1
  80. package/dist/lib/services/artifact-state.service.d.ts +22 -5
  81. package/dist/lib/services/artifact-state.service.d.ts.map +1 -1
  82. package/dist/lib/services/artifact-state.service.js +118 -30
  83. package/dist/lib/services/artifact-state.service.js.map +1 -1
  84. package/dist/lib/services/artifact-use-tracking.service.d.ts +35 -0
  85. package/dist/lib/services/artifact-use-tracking.service.d.ts.map +1 -0
  86. package/dist/lib/services/artifact-use-tracking.service.js +76 -0
  87. package/dist/lib/services/artifact-use-tracking.service.js.map +1 -0
  88. package/dist/lib/services/conversation-agent.service.d.ts +30 -3
  89. package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
  90. package/dist/lib/services/conversation-agent.service.js +101 -12
  91. package/dist/lib/services/conversation-agent.service.js.map +1 -1
  92. package/dist/lib/services/conversation-state.service.d.ts +13 -0
  93. package/dist/lib/services/conversation-state.service.d.ts.map +1 -1
  94. package/dist/lib/services/conversation-state.service.js +26 -0
  95. package/dist/lib/services/conversation-state.service.js.map +1 -1
  96. package/dist/lib/services/mention-autocomplete.service.d.ts.map +1 -1
  97. package/dist/lib/services/mention-autocomplete.service.js +1 -1
  98. package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
  99. package/dist/lib/services/search.service.d.ts.map +1 -1
  100. package/dist/lib/services/search.service.js +3 -1
  101. package/dist/lib/services/search.service.js.map +1 -1
  102. package/dist/public-api.d.ts +3 -0
  103. package/dist/public-api.d.ts.map +1 -1
  104. package/dist/public-api.js +3 -0
  105. package/dist/public-api.js.map +1 -1
  106. package/package.json +14 -14
@@ -1,7 +1,8 @@
1
1
  import { Component, Input, Output, EventEmitter, ViewChild } from '@angular/core';
2
- import { Metadata, RunView } from '@memberjunction/core';
2
+ import { Metadata } from '@memberjunction/core';
3
3
  import { GraphQLDataProvider, GraphQLAIClient } from '@memberjunction/graphql-dataprovider';
4
4
  import { AIEngineBase } from '@memberjunction/ai-engine-base';
5
+ import { MJNotificationService } from '@memberjunction/ng-notifications';
5
6
  import * as i0 from "@angular/core";
6
7
  import * as i1 from "../../services/dialog.service";
7
8
  import * as i2 from "../../services/toast.service";
@@ -37,12 +38,16 @@ export class MessageInputComponent {
37
38
  // Default artifact type ID for JSON (when agent doesn't specify DefaultArtifactTypeID)
38
39
  JSON_ARTIFACT_TYPE_ID = 'ae674c7e-ea0d-49ea-89e4-0649f5eb20d4';
39
40
  conversationId;
41
+ conversationName; // For task tracking display
40
42
  currentUser;
41
43
  disabled = false;
42
44
  placeholder = 'Type a message... (Ctrl+Enter to send)';
43
45
  parentMessageId; // Optional: for replying in threads
44
46
  conversationHistory = []; // For agent context
45
47
  initialMessage = null; // Message to send automatically when component initializes
48
+ artifactsByDetailId; // Pre-loaded artifact data for performance
49
+ agentRunsByDetailId; // Pre-loaded agent run data for performance
50
+ inProgressMessageIds; // Message IDs that are in-progress and need streaming reconnection
46
51
  messageSent = new EventEmitter();
47
52
  agentResponse = new EventEmitter();
48
53
  agentRunDetected = new EventEmitter();
@@ -80,12 +85,18 @@ export class MessageInputComponent {
80
85
  await this.mentionAutocomplete.initialize(this.currentUser);
81
86
  // Subscribe to PubSub for task progress updates
82
87
  this.subscribeToPushStatus();
88
+ // Reconnect to any in-progress messages for streaming updates
89
+ this.reconnectInProgressMessages();
83
90
  }
84
91
  ngOnChanges(changes) {
85
92
  // When conversation changes, focus the input
86
93
  if (changes['conversationId'] && !changes['conversationId'].firstChange) {
87
94
  this.focusInput();
88
95
  }
96
+ // When in-progress message IDs change (switching conversations), reconnect
97
+ if (changes['inProgressMessageIds']) {
98
+ this.reconnectInProgressMessages();
99
+ }
89
100
  }
90
101
  ngAfterViewInit() {
91
102
  // Focus input on initial load
@@ -115,6 +126,24 @@ export class MessageInputComponent {
115
126
  }
116
127
  }, 100);
117
128
  }
129
+ /**
130
+ * Reconnect to in-progress messages for streaming updates
131
+ * This is called when:
132
+ * 1. Component initializes (ngOnInit)
133
+ * 2. Conversation changes (ngOnChanges)
134
+ * 3. User returns to a conversation with in-progress messages
135
+ */
136
+ reconnectInProgressMessages() {
137
+ if (!this.inProgressMessageIds || this.inProgressMessageIds.length === 0) {
138
+ return;
139
+ }
140
+ console.log(`🔌 Reconnecting to ${this.inProgressMessageIds.length} in-progress messages for streaming updates`);
141
+ // Register all in-progress message IDs to receive PubSub updates
142
+ for (const messageId of this.inProgressMessageIds) {
143
+ this.activeTaskExecutionMessageIds.add(messageId);
144
+ }
145
+ console.log(`✅ Registered ${this.activeTaskExecutionMessageIds.size} messages for streaming updates`);
146
+ }
118
147
  /**
119
148
  * Subscribe to PubSub for real-time task orchestration progress updates
120
149
  */
@@ -366,13 +395,18 @@ export class MessageInputComponent {
366
395
  */
367
396
  async handleAgentContinuity(messageDetail, lastAgentId, mentionResult, isFirstMessage) {
368
397
  console.log('🔍 Previous agent found, checking continuity intent...');
369
- const intent = await this.checkContinuityIntent(lastAgentId, messageDetail.Message);
370
- if (intent === 'YES') {
371
- console.log('✅ Intent check: YES - continuing with previous agent');
372
- await this.executeRouteWithNaming(() => this.continueWithAgent(messageDetail, lastAgentId, this.conversationId), messageDetail.Message, isFirstMessage);
398
+ const intentResult = await this.checkContinuityIntent(lastAgentId, messageDetail.Message);
399
+ if (intentResult.decision === 'YES') {
400
+ console.log('✅ Intent check: YES - continuing with previous agent', {
401
+ reasoning: intentResult.reasoning,
402
+ targetArtifactVersionId: intentResult.targetArtifactVersionId
403
+ });
404
+ await this.executeRouteWithNaming(() => this.continueWithAgent(messageDetail, lastAgentId, this.conversationId, intentResult.targetArtifactVersionId), messageDetail.Message, isFirstMessage);
373
405
  }
374
406
  else {
375
- console.log(`🤖 Intent check: ${intent} - routing through Sage for evaluation`);
407
+ console.log(`🤖 Intent check: ${intentResult.decision} - routing through Sage for evaluation`, {
408
+ reasoning: intentResult.reasoning
409
+ });
376
410
  await this.executeRouteWithNaming(() => this.processMessageThroughAgent(messageDetail, mentionResult), messageDetail.Message, isFirstMessage);
377
411
  }
378
412
  }
@@ -403,12 +437,20 @@ export class MessageInputComponent {
403
437
  // Emit event to show temporary "Analyzing intent..." message in conversation
404
438
  this.intentCheckStarted.emit();
405
439
  try {
406
- const intent = await this.agentService.checkAgentContinuityIntent(agentId, message, this.conversationHistory);
440
+ // Build context from pre-loaded maps (if available)
441
+ if (!this.artifactsByDetailId || !this.agentRunsByDetailId) {
442
+ console.warn('⚠️ Artifact/agent run context not available for intent check');
443
+ return { decision: 'UNSURE', reasoning: 'Context not available' };
444
+ }
445
+ const intent = await this.agentService.checkAgentContinuityIntent(agentId, message, this.conversationHistory, {
446
+ artifactsByDetailId: this.artifactsByDetailId,
447
+ agentRunsByDetailId: this.agentRunsByDetailId
448
+ });
407
449
  return intent;
408
450
  }
409
451
  catch (error) {
410
452
  console.error('❌ Intent check failed, defaulting to UNSURE:', error);
411
- return 'UNSURE';
453
+ return { decision: 'UNSURE', reasoning: 'Intent check failed with error' };
412
454
  }
413
455
  finally {
414
456
  // Emit event to remove temporary intent checking message
@@ -481,12 +523,6 @@ export class MessageInputComponent {
481
523
  return async (progress) => {
482
524
  let progressAgentRun = progress.metadata?.agentRun;
483
525
  const progressAgentRunId = progressAgentRun?.ID || progress.metadata?.agentRunId;
484
- // if (!progressAgentRun && progressAgentRunId) {
485
- // // load the full agent run object from the database if we only have the ID
486
- // const md = new Metadata();
487
- // progressAgentRun = await md.GetEntityObject<AIAgentRunEntity>("MJ: AI Agent Runs");
488
- // await progressAgentRun.Load(progressAgentRunId);
489
- // }
490
526
  // Capture the agent run ID from the first progress message
491
527
  if (!capturedAgentRunId && progressAgentRunId) {
492
528
  capturedAgentRunId = progressAgentRunId;
@@ -581,25 +617,20 @@ export class MessageInputComponent {
581
617
  agentName: 'Sage',
582
618
  status: 'Evaluating message...',
583
619
  relatedMessageId: userMessage.ID,
584
- conversationDetailId: conversationManagerMessage.ID
620
+ conversationDetailId: conversationManagerMessage.ID,
621
+ conversationId: this.conversationId,
622
+ conversationName: this.conversationName
585
623
  });
586
624
  const result = await this.agentService.processMessage(conversationId, userMessage, this.conversationHistory, conversationManagerMessage.ID, this.createProgressCallback(conversationManagerMessage, 'Sage'));
587
- // Remove Sage from active tasks
588
- if (taskId) {
589
- this.activeTasks.remove(taskId);
590
- taskId = null;
591
- }
625
+ // Task will be removed automatically in markMessageComplete()
626
+ // DO NOT remove here - agent may still be streaming/processing
627
+ taskId = null; // Clear reference but don't remove from service
592
628
  if (!result || !result.success) {
593
- // Evaluation failed - mark as complete to stop progress updates
594
- this.markMessageComplete(conversationManagerMessage);
595
- conversationManagerMessage.Status = 'Error';
596
- conversationManagerMessage.Message = `❌ Evaluation failed`;
597
- conversationManagerMessage.Error = result?.agentRun?.ErrorMessage || 'Agent evaluation failed';
598
- await conversationManagerMessage.Save();
599
- this.messageSent.emit(conversationManagerMessage);
600
- userMessage.Status = 'Complete';
601
- await userMessage.Save();
602
- this.messageSent.emit(userMessage);
629
+ // Evaluation failed - use updateConversationDetail to ensure task cleanup
630
+ const errorMsg = result?.agentRun?.ErrorMessage || 'Agent evaluation failed';
631
+ conversationManagerMessage.Error = errorMsg;
632
+ await this.updateConversationDetail(conversationManagerMessage, `❌ Evaluation failed`, 'Error');
633
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
603
634
  console.warn('⚠️ Sage failed:', result?.agentRun?.ErrorMessage);
604
635
  // Clean up completion timestamp
605
636
  this.cleanupCompletionTimestamp(conversationManagerMessage.ID);
@@ -619,7 +650,7 @@ export class MessageInputComponent {
619
650
  await this.handleTaskGraphExecution(userMessage, result, this.conversationId, conversationManagerMessage);
620
651
  // Remove CM from active tasks
621
652
  if (taskId) {
622
- this.activeTasks.remove(taskId);
653
+ // Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
623
654
  }
624
655
  }
625
656
  // Stage 3: Check for sub-agent invocation (single-step delegation)
@@ -628,7 +659,7 @@ export class MessageInputComponent {
628
659
  await this.handleSubAgentInvocation(userMessage, result, this.conversationId, conversationManagerMessage);
629
660
  // Remove CM from active tasks
630
661
  if (taskId) {
631
- this.activeTasks.remove(taskId);
662
+ // Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
632
663
  }
633
664
  }
634
665
  // Stage 4: Direct chat response from Sage
@@ -651,12 +682,10 @@ export class MessageInputComponent {
651
682
  console.log('🎨 Server created artifact, UI will reload to show it');
652
683
  this.messageSent.emit(conversationManagerMessage);
653
684
  }
654
- userMessage.Status = 'Complete';
655
- await userMessage.Save();
656
- this.messageSent.emit(userMessage);
685
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
657
686
  // Remove CM from active tasks
658
687
  if (taskId) {
659
- this.activeTasks.remove(taskId);
688
+ // Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
660
689
  }
661
690
  // Clean up completion timestamp after delay
662
691
  this.cleanupCompletionTimestamp(conversationManagerMessage.ID);
@@ -690,7 +719,7 @@ export class MessageInputComponent {
690
719
  }
691
720
  // Remove CM from active tasks
692
721
  if (taskId) {
693
- this.activeTasks.remove(taskId);
722
+ // Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
694
723
  }
695
724
  }
696
725
  }
@@ -698,23 +727,17 @@ export class MessageInputComponent {
698
727
  console.error('❌ Error processing message through agents:', error);
699
728
  // Update conversationManagerMessage status to Error
700
729
  if (conversationManagerMessage && conversationManagerMessage.ID) {
701
- // Mark as complete to stop progress updates
702
- this.markMessageComplete(conversationManagerMessage);
703
- conversationManagerMessage.Status = 'Error';
704
- conversationManagerMessage.Message = `❌ Error: ${String(error)}`;
730
+ // Use updateConversationDetail to ensure task cleanup
705
731
  conversationManagerMessage.Error = String(error);
706
- await conversationManagerMessage.Save();
707
- this.messageSent.emit(conversationManagerMessage);
732
+ await this.updateConversationDetail(conversationManagerMessage, `❌ Error: ${String(error)}`, 'Error');
708
733
  // Clean up completion timestamp
709
734
  this.cleanupCompletionTimestamp(conversationManagerMessage.ID);
710
735
  }
711
736
  // Mark user message as complete
712
- userMessage.Status = 'Complete';
713
- await userMessage.Save();
714
- this.messageSent.emit(userMessage);
737
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
715
738
  // Clean up active task
716
739
  if (taskId) {
717
- this.activeTasks.remove(taskId);
740
+ // Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
718
741
  }
719
742
  }
720
743
  }
@@ -731,7 +754,6 @@ export class MessageInputComponent {
731
754
  reasoning,
732
755
  taskCount
733
756
  });
734
- const md = new Metadata();
735
757
  // Deduplicate tasks by tempId (LLM sometimes returns duplicates)
736
758
  const seenTempIds = new Set();
737
759
  const uniqueTasks = taskGraph.tasks.filter((task) => {
@@ -818,18 +840,14 @@ export class MessageInputComponent {
818
840
  // ExecuteGQL returns data directly (not wrapped in {data, errors})
819
841
  if (result?.ExecuteTaskGraph?.success) {
820
842
  console.log('✅ Task graph execution completed successfully');
821
- taskExecutionMessage.Message = `✅ **${workflowName}** completed successfully`;
822
- taskExecutionMessage.Status = 'Complete';
843
+ await this.updateConversationDetail(taskExecutionMessage, `✅ **${workflowName}** completed successfully`, 'Complete');
823
844
  }
824
845
  else {
825
846
  const errorMsg = result?.ExecuteTaskGraph?.errorMessage || 'Unknown error';
826
847
  console.error('❌ Task graph execution failed:', errorMsg);
827
- taskExecutionMessage.Message = `❌ **${workflowName}** failed: ${errorMsg}`;
828
- taskExecutionMessage.Status = 'Error';
829
848
  taskExecutionMessage.Error = errorMsg;
849
+ await this.updateConversationDetail(taskExecutionMessage, `❌ **${workflowName}** failed: ${errorMsg}`, 'Error');
830
850
  }
831
- await taskExecutionMessage.Save();
832
- this.messageSent.emit(taskExecutionMessage);
833
851
  // Trigger artifact reload for this message
834
852
  // Artifacts were created on server during task execution and linked to this message
835
853
  // This event triggers the parent component to reload artifacts from the database
@@ -842,18 +860,15 @@ export class MessageInputComponent {
842
860
  });
843
861
  // Unregister from real-time updates (task complete)
844
862
  this.activeTaskExecutionMessageIds.delete(taskExecutionMessage.ID);
863
+ // Mark agent response message as complete (removes task from active tasks)
864
+ await this.updateConversationDetail(conversationManagerMessage, conversationManagerMessage.Message, 'Complete');
845
865
  // Mark user message as complete
846
- userMessage.Status = 'Complete';
847
- await userMessage.Save();
848
- this.messageSent.emit(userMessage);
866
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
849
867
  }
850
868
  catch (error) {
851
869
  console.error('❌ Error executing task graph:', error);
852
- taskExecutionMessage.Message = `❌ **${workflowName}** - Error: ${String(error)}`;
853
- taskExecutionMessage.Status = 'Error';
854
870
  taskExecutionMessage.Error = String(error);
855
- await taskExecutionMessage.Save();
856
- this.messageSent.emit(taskExecutionMessage);
871
+ await this.updateConversationDetail(taskExecutionMessage, `❌ **${workflowName}** - Error: ${String(error)}`, 'Error');
857
872
  // Trigger artifact reload even on error - partial artifacts may have been created
858
873
  this.artifactCreated.emit({
859
874
  artifactId: '',
@@ -864,19 +879,23 @@ export class MessageInputComponent {
864
879
  });
865
880
  // Unregister from real-time updates (task failed)
866
881
  this.activeTaskExecutionMessageIds.delete(taskExecutionMessage.ID);
867
- userMessage.Status = 'Complete';
868
- await userMessage.Save();
869
- this.messageSent.emit(userMessage);
882
+ // Mark agent response message as complete (removes task from active tasks)
883
+ conversationManagerMessage.Error = String(error);
884
+ await this.updateConversationDetail(conversationManagerMessage, conversationManagerMessage.Message, 'Error');
885
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
870
886
  }
871
887
  }
872
888
  async updateConversationDetail(convoDetail, message, status, suggestedResponses) {
873
- if (convoDetail.Status === 'Complete' || convoDetail.Status === 'Error') {
874
- return; // Do not update completed or errored messages
875
- }
876
- // Mark as completing BEFORE updating if status is Complete or Error
889
+ // Mark as completing FIRST if status is Complete or Error
890
+ // This ensures task cleanup happens even if we return early due to guard clause
877
891
  if (status === 'Complete' || status === 'Error') {
878
892
  this.markMessageComplete(convoDetail);
879
893
  }
894
+ // Guard clause: Don't re-save if already complete/errored (prevents duplicate saves)
895
+ // Task has already been removed by markMessageComplete() above
896
+ if (convoDetail.Status === 'Complete' || convoDetail.Status === 'Error') {
897
+ return; // Already complete, no need to save again
898
+ }
880
899
  const maxAttempts = 2;
881
900
  let attempts = 0, done = false;
882
901
  while (attempts < maxAttempts && !done) {
@@ -928,16 +947,18 @@ export class MessageInputComponent {
928
947
  agentName: agentName,
929
948
  status: 'Starting...',
930
949
  relatedMessageId: userMessage.ID,
931
- conversationDetailId: agentResponseMessage.ID
950
+ conversationDetailId: agentResponseMessage.ID,
951
+ conversationId: this.conversationId,
952
+ conversationName: this.conversationName
932
953
  });
933
954
  // Invoke agent with task's input payload
934
955
  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
935
956
  this.createProgressCallback(agentResponseMessage, agentName));
936
- // Remove from active tasks
937
- this.activeTasks.remove(newTaskId);
957
+ // Task will be removed automatically in markMessageComplete() when status changes to Complete/Error
958
+ // DO NOT remove here - allows UI to show task during entire execution
938
959
  if (agentResult && agentResult.success) {
939
960
  // Update message with result
940
- await this.updateConversationDetail(agentResponseMessage, agentResult.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete');
961
+ await this.updateConversationDetail(agentResponseMessage, agentResult.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete', agentResult.suggestedResponses);
941
962
  // Server created artifacts - emit event to trigger UI reload
942
963
  if (agentResult.payload && Object.keys(agentResult.payload).length > 0) {
943
964
  this.artifactCreated.emit({
@@ -954,22 +975,15 @@ export class MessageInputComponent {
954
975
  else {
955
976
  // Handle failure
956
977
  const errorMsg = agentResult?.agentRun?.ErrorMessage || 'Agent execution failed';
957
- agentResponseMessage.Message = `❌ **${agentName}** failed: ${errorMsg}`;
958
- agentResponseMessage.Status = 'Error';
959
978
  agentResponseMessage.Error = errorMsg;
960
- await agentResponseMessage.Save();
961
- this.messageSent.emit(agentResponseMessage);
979
+ await this.updateConversationDetail(agentResponseMessage, `❌ **${agentName}** failed: ${errorMsg}`, 'Error');
962
980
  }
963
981
  // Mark user message as complete
964
- userMessage.Status = 'Complete';
965
- await userMessage.Save();
966
- this.messageSent.emit(userMessage);
982
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
967
983
  }
968
984
  catch (error) {
969
985
  console.error('❌ Error in single task execution:', error);
970
- userMessage.Status = 'Complete';
971
- await userMessage.Save();
972
- this.messageSent.emit(userMessage);
986
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
973
987
  }
974
988
  }
975
989
  /**
@@ -1004,20 +1018,22 @@ export class MessageInputComponent {
1004
1018
  agentName: agentName,
1005
1019
  status: 'Starting...',
1006
1020
  relatedMessageId: userMessage.ID,
1007
- conversationDetailId: agentResponseMessage.ID
1021
+ conversationDetailId: agentResponseMessage.ID,
1022
+ conversationId: this.conversationId,
1023
+ conversationName: this.conversationName
1008
1024
  });
1009
1025
  // Invoke the sub-agent with progress callback
1010
1026
  const subResult = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.conversationHistory, reasoning, agentResponseMessage.ID, undefined, // no payload for initial invocation
1011
1027
  this.createProgressCallback(agentResponseMessage, agentName));
1012
- // Remove from active tasks
1013
- this.activeTasks.remove(newTaskId);
1028
+ // Task will be removed automatically in markMessageComplete() when status changes to Complete/Error
1029
+ // DO NOT remove here - allows UI to show task during entire execution
1014
1030
  if (subResult && subResult.success) {
1015
1031
  // Update the response message with agent result
1016
1032
  // Store the agent ID for display
1017
1033
  if (subResult.agentRun.AgentID) {
1018
1034
  agentResponseMessage.AgentID = subResult.agentRun.AgentID;
1019
1035
  }
1020
- await this.updateConversationDetail(agentResponseMessage, subResult.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete');
1036
+ await this.updateConversationDetail(agentResponseMessage, subResult.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete', subResult.suggestedResponses);
1021
1037
  // Server created artifacts - emit event to trigger UI reload
1022
1038
  if (subResult.payload && Object.keys(subResult.payload).length > 0) {
1023
1039
  this.artifactCreated.emit({
@@ -1032,9 +1048,7 @@ export class MessageInputComponent {
1032
1048
  this.messageSent.emit(agentResponseMessage);
1033
1049
  }
1034
1050
  // Mark user message as complete
1035
- userMessage.Status = 'Complete';
1036
- await userMessage.Save();
1037
- this.messageSent.emit(userMessage);
1051
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1038
1052
  }
1039
1053
  else {
1040
1054
  // Sub-agent failed - attempt auto-retry once
@@ -1062,33 +1076,21 @@ export class MessageInputComponent {
1062
1076
  });
1063
1077
  this.messageSent.emit(agentResponseMessage);
1064
1078
  }
1065
- userMessage.Status = 'Complete';
1066
- await userMessage.Save();
1067
- this.messageSent.emit(userMessage);
1079
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1068
1080
  }
1069
1081
  else {
1070
1082
  // Retry also failed - show error with manual retry option
1071
- conversationManagerMessage.Status = 'Error';
1072
- conversationManagerMessage.Message = `❌ **${agentName}** failed after retry\n\n${retryResult?.agentRun?.ErrorMessage || 'Unknown error'}`;
1073
1083
  conversationManagerMessage.Error = retryResult?.agentRun?.ErrorMessage || null;
1074
- await conversationManagerMessage.Save();
1075
- this.messageSent.emit(conversationManagerMessage);
1076
- userMessage.Status = 'Complete'; // Don't mark user message as error
1077
- await userMessage.Save();
1078
- this.messageSent.emit(userMessage);
1084
+ await this.updateConversationDetail(conversationManagerMessage, `❌ **${agentName}** failed after retry\n\n${retryResult?.agentRun?.ErrorMessage || 'Unknown error'}`, 'Error');
1085
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1079
1086
  }
1080
1087
  }
1081
1088
  }
1082
1089
  catch (error) {
1083
1090
  console.error(`❌ Error invoking sub-agent ${agentName}:`, error);
1084
- conversationManagerMessage.Status = 'Error';
1085
- conversationManagerMessage.Message = `❌ **${agentName}** encountered an error\n\n${String(error)}`;
1086
1091
  conversationManagerMessage.Error = String(error);
1087
- await conversationManagerMessage.Save();
1088
- this.messageSent.emit(conversationManagerMessage);
1089
- userMessage.Status = 'Complete'; // Don't mark user message as error
1090
- await userMessage.Save();
1091
- this.messageSent.emit(userMessage);
1092
+ await this.updateConversationDetail(conversationManagerMessage, `❌ **${agentName}** encountered an error\n\n${String(error)}`, 'Error');
1093
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1092
1094
  }
1093
1095
  }
1094
1096
  /**
@@ -1106,59 +1108,40 @@ export class MessageInputComponent {
1106
1108
  if (!lastAIMessage || !lastAIMessage.AgentID) {
1107
1109
  // No previous specialist agent - just mark user message as complete
1108
1110
  console.log('🔇 No previous specialist agent found - marking complete');
1109
- userMessage.Status = 'Complete';
1110
- await userMessage.Save();
1111
- this.messageSent.emit(userMessage);
1111
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1112
1112
  return;
1113
1113
  }
1114
1114
  // Load the agent entity to get its name
1115
- const rv = new RunView();
1116
- const agentResult = await rv.RunView({
1117
- EntityName: 'AI Agents',
1118
- ExtraFilter: `ID='${lastAIMessage.AgentID}'`,
1119
- ResultType: 'entity_object'
1120
- }, this.currentUser);
1121
- if (!agentResult.Success || !agentResult.Results || agentResult.Results.length === 0) {
1115
+ const previousAgent = AIEngineBase.Instance.Agents.find(a => a.ID === lastAIMessage.AgentID);
1116
+ if (!previousAgent) {
1122
1117
  console.warn('⚠️ Could not load previous agent - marking complete');
1123
- userMessage.Status = 'Complete';
1124
- await userMessage.Save();
1125
- this.messageSent.emit(userMessage);
1118
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1126
1119
  return;
1127
1120
  }
1128
- const previousAgent = agentResult.Results[0];
1129
1121
  const agentName = previousAgent.Name || 'Agent';
1130
1122
  console.log(`🔄 Agent continuity: Continuing with ${agentName} (AgentID: ${lastAIMessage.AgentID})`);
1131
- // Load the OUTPUT artifact from the last agent message
1132
- const artifactResult = await rv.RunView({
1133
- EntityName: 'MJ: Conversation Detail Artifacts',
1134
- ExtraFilter: `ConversationDetailID='${lastAIMessage.ID}' AND Direction='Output'`,
1135
- ResultType: 'entity_object'
1136
- }, this.currentUser);
1137
1123
  let previousPayload = null;
1138
1124
  let previousArtifactInfo = null;
1139
- if (artifactResult.Success && artifactResult.Results && artifactResult.Results.length > 0) {
1140
- // Load the artifact version content
1141
- const junctionRecord = artifactResult.Results[0];
1142
- const versionResult = await rv.RunView({
1143
- EntityName: 'MJ: Artifact Versions',
1144
- ExtraFilter: `ID='${junctionRecord.ArtifactVersionID}'`,
1145
- ResultType: 'entity_object'
1146
- }, this.currentUser);
1147
- if (versionResult.Success && versionResult.Results && versionResult.Results.length > 0) {
1148
- const version = versionResult.Results[0];
1149
- if (version.Content) {
1150
- try {
1125
+ // Use pre-loaded artifact data (no DB queries!)
1126
+ if (this.artifactsByDetailId) {
1127
+ const artifacts = this.artifactsByDetailId.get(lastAIMessage.ID);
1128
+ if (artifacts && artifacts.length > 0) {
1129
+ try {
1130
+ // Use the first artifact (should only be one OUTPUT per message)
1131
+ const artifact = artifacts[0];
1132
+ const version = await artifact.getVersion();
1133
+ if (version.Content) {
1151
1134
  previousPayload = JSON.parse(version.Content);
1152
1135
  previousArtifactInfo = {
1153
- artifactId: version.ArtifactID,
1154
- versionId: version.ID,
1155
- versionNumber: version.VersionNumber || 1
1136
+ artifactId: artifact.artifactId,
1137
+ versionId: artifact.artifactVersionId,
1138
+ versionNumber: artifact.versionNumber
1156
1139
  };
1157
1140
  console.log('📦 Loaded previous OUTPUT artifact as payload for continuity', previousArtifactInfo);
1158
1141
  }
1159
- catch (error) {
1160
- console.warn('⚠️ Could not parse previous artifact content:', error);
1161
- }
1142
+ }
1143
+ catch (error) {
1144
+ console.warn('⚠️ Could not parse previous artifact content:', error);
1162
1145
  }
1163
1146
  }
1164
1147
  }
@@ -1178,13 +1161,15 @@ export class MessageInputComponent {
1178
1161
  agentName: agentName,
1179
1162
  status: 'Processing refinement...',
1180
1163
  relatedMessageId: userMessage.ID,
1181
- conversationDetailId: statusMessage.ID
1164
+ conversationDetailId: statusMessage.ID,
1165
+ conversationId: this.conversationId,
1166
+ conversationName: this.conversationName
1182
1167
  });
1183
1168
  try {
1184
1169
  // Invoke the agent with the previous payload
1185
1170
  const continuityResult = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.conversationHistory, 'Continuing previous work based on user feedback', statusMessage.ID, previousPayload, this.createProgressCallback(statusMessage, agentName), previousArtifactInfo?.artifactId, previousArtifactInfo?.versionId);
1186
1171
  // Remove from active tasks
1187
- this.activeTasks.remove(taskId);
1172
+ // Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
1188
1173
  if (continuityResult && continuityResult.success) {
1189
1174
  // Create response message
1190
1175
  const agentResponseMessage = await this.dataCache.createConversationDetail(this.currentUser);
@@ -1210,33 +1195,21 @@ export class MessageInputComponent {
1210
1195
  this.messageSent.emit(agentResponseMessage);
1211
1196
  }
1212
1197
  // Mark user message as complete
1213
- userMessage.Status = 'Complete';
1214
- await userMessage.Save();
1215
- this.messageSent.emit(userMessage);
1198
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1216
1199
  }
1217
1200
  else {
1218
1201
  // Agent failed
1219
- statusMessage.Status = 'Error';
1220
- statusMessage.Message = `❌ **${agentName}** failed during refinement\n\n${continuityResult?.agentRun?.ErrorMessage || 'Unknown error'}`;
1221
1202
  statusMessage.Error = continuityResult?.agentRun?.ErrorMessage || null;
1222
- await statusMessage.Save();
1223
- this.messageSent.emit(statusMessage);
1224
- userMessage.Status = 'Complete';
1225
- await userMessage.Save();
1226
- this.messageSent.emit(userMessage);
1203
+ await this.updateConversationDetail(statusMessage, `❌ **${agentName}** failed during refinement\n\n${continuityResult?.agentRun?.ErrorMessage || 'Unknown error'}`, 'Error');
1204
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1227
1205
  }
1228
1206
  }
1229
1207
  catch (error) {
1230
1208
  console.error(`❌ Error in agent continuity with ${agentName}:`, error);
1231
- this.activeTasks.remove(taskId);
1232
- statusMessage.Status = 'Error';
1233
- statusMessage.Message = `❌ **${agentName}** encountered an error\n\n${String(error)}`;
1209
+ // Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
1234
1210
  statusMessage.Error = String(error);
1235
- await statusMessage.Save();
1236
- this.messageSent.emit(statusMessage);
1237
- userMessage.Status = 'Complete';
1238
- await userMessage.Save();
1239
- this.messageSent.emit(userMessage);
1211
+ await this.updateConversationDetail(statusMessage, `❌ **${agentName}** encountered an error\n\n${String(error)}`, 'Error');
1212
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1240
1213
  }
1241
1214
  }
1242
1215
  /**
@@ -1250,8 +1223,12 @@ export class MessageInputComponent {
1250
1223
  agentName: agentName,
1251
1224
  status: 'Processing...',
1252
1225
  relatedMessageId: userMessage.ID,
1253
- conversationDetailId: userMessage.ID
1226
+ conversationDetailId: userMessage.ID,
1227
+ conversationId: this.conversationId,
1228
+ conversationName: this.conversationName
1254
1229
  });
1230
+ // Declare agentResponseMessage outside try block so it's accessible in catch
1231
+ let agentResponseMessage = undefined;
1255
1232
  try {
1256
1233
  // Update user message status to In-Progress
1257
1234
  userMessage.Status = 'In-Progress';
@@ -1260,7 +1237,7 @@ export class MessageInputComponent {
1260
1237
  // Look up the agent to get its ID
1261
1238
  const agent = AIEngineBase.Instance.Agents.find(a => a.Name === agentName);
1262
1239
  // Create AI response message BEFORE invoking agent (for duration tracking)
1263
- const agentResponseMessage = await this.dataCache.createConversationDetail(this.currentUser);
1240
+ agentResponseMessage = await this.dataCache.createConversationDetail(this.currentUser);
1264
1241
  agentResponseMessage.ConversationID = conversationId;
1265
1242
  agentResponseMessage.Role = 'AI';
1266
1243
  agentResponseMessage.Message = '⏳ Starting...'; // Initial message
@@ -1278,7 +1255,7 @@ export class MessageInputComponent {
1278
1255
  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
1279
1256
  this.createProgressCallback(agentResponseMessage, agentName));
1280
1257
  // Remove from active tasks
1281
- this.activeTasks.remove(taskId);
1258
+ // Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
1282
1259
  if (result && result.success) {
1283
1260
  if (result.agentRun.AgentID) {
1284
1261
  agentResponseMessage.AgentID = result.agentRun.AgentID;
@@ -1296,7 +1273,7 @@ export class MessageInputComponent {
1296
1273
  }
1297
1274
  // Stage 3: Normal chat response
1298
1275
  else {
1299
- await this.updateConversationDetail(agentResponseMessage, result.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete');
1276
+ await this.updateConversationDetail(agentResponseMessage, result.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete', result.suggestedResponses);
1300
1277
  // Server created artifacts - emit event to trigger UI reload
1301
1278
  if (result.payload && Object.keys(result.payload).length > 0) {
1302
1279
  this.artifactCreated.emit({
@@ -1309,103 +1286,100 @@ export class MessageInputComponent {
1309
1286
  this.messageSent.emit(agentResponseMessage);
1310
1287
  }
1311
1288
  // Mark user message as complete
1312
- userMessage.Status = 'Complete';
1313
- await userMessage.Save();
1314
- this.messageSent.emit(userMessage);
1289
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1315
1290
  }
1316
1291
  }
1317
1292
  else {
1318
- // Agent failed - create error message
1319
- const errorMessage = await this.dataCache.createConversationDetail(this.currentUser);
1320
- errorMessage.ConversationID = conversationId;
1321
- errorMessage.Role = 'AI';
1322
- errorMessage.Message = `❌ **@${agentName}** failed\n\n${result?.agentRun?.ErrorMessage || 'Unknown error'}`;
1323
- errorMessage.ParentID = userMessage.ID;
1324
- errorMessage.Status = 'Error';
1325
- errorMessage.Error = result?.agentRun?.ErrorMessage || null;
1326
- errorMessage.HiddenToUser = false;
1327
- await errorMessage.Save();
1328
- this.messageSent.emit(errorMessage);
1329
- userMessage.Status = 'Complete';
1330
- await userMessage.Save();
1331
- this.messageSent.emit(userMessage);
1293
+ // Agent failed - update the existing message instead of creating a new one
1294
+ agentResponseMessage.Error = result?.agentRun?.ErrorMessage || null;
1295
+ await this.updateConversationDetail(agentResponseMessage, `❌ **@${agentName}** failed\n\n${result?.agentRun?.ErrorMessage || 'Unknown error'}`, 'Error');
1296
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1332
1297
  }
1333
1298
  }
1334
1299
  catch (error) {
1335
1300
  console.error(`❌ Error invoking mentioned agent ${agentName}:`, error);
1336
- this.activeTasks.remove(taskId);
1337
- const errorMessage = await this.dataCache.createConversationDetail(this.currentUser);
1338
- errorMessage.ConversationID = conversationId;
1339
- errorMessage.Role = 'AI';
1340
- errorMessage.Message = `❌ **@${agentName}** encountered an error\n\n${String(error)}`;
1341
- errorMessage.ParentID = userMessage.ID;
1342
- errorMessage.Status = 'Error';
1343
- errorMessage.Error = String(error);
1344
- errorMessage.HiddenToUser = false;
1345
- await errorMessage.Save();
1346
- this.messageSent.emit(errorMessage);
1347
- userMessage.Status = 'Complete';
1348
- await userMessage.Save();
1349
- this.messageSent.emit(userMessage);
1301
+ // Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
1302
+ // Update the existing agent response message if it was created
1303
+ if (agentResponseMessage) {
1304
+ agentResponseMessage.Error = String(error);
1305
+ await this.updateConversationDetail(agentResponseMessage, `❌ **@${agentName}** encountered an error\n\n${String(error)}`, 'Error');
1306
+ }
1307
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1350
1308
  }
1351
1309
  }
1352
1310
  /**
1353
1311
  * Continue with the same agent from previous message (implicit continuation)
1354
1312
  * Bypasses Sage - no status messages
1313
+ *
1314
+ * @param targetArtifactVersionId Optional specific artifact version to use as payload (from intent check)
1355
1315
  */
1356
- async continueWithAgent(userMessage, agentId, conversationId) {
1316
+ async continueWithAgent(userMessage, agentId, conversationId, targetArtifactVersionId) {
1357
1317
  // Load the agent entity to get its name
1358
- const rv = new RunView();
1359
- const agentResult = await rv.RunView({
1360
- EntityName: 'AI Agents',
1361
- ExtraFilter: `ID='${agentId}'`,
1362
- ResultType: 'entity_object'
1363
- }, this.currentUser);
1364
- if (!agentResult.Success || !agentResult.Results || agentResult.Results.length === 0) {
1318
+ const agent = AIEngineBase.Instance.Agents.find(a => a.ID === agentId);
1319
+ if (!agent) {
1365
1320
  console.warn('⚠️ Could not load agent for continuation - falling back to Sage');
1366
1321
  await this.processMessageThroughAgent(userMessage, { mentions: [], agentMention: null, userMentions: [] });
1367
1322
  return;
1368
1323
  }
1369
- const agent = agentResult.Results[0];
1370
1324
  const agentName = agent.Name || 'Agent';
1371
- // Find the last AI message from this same agent to get the previous OUTPUT artifact
1372
- const lastAIMessage = this.conversationHistory
1373
- .slice()
1374
- .reverse()
1375
- .find(msg => msg.Role === 'AI' && msg.AgentID === agentId);
1376
1325
  let previousPayload = null;
1377
1326
  let previousArtifactInfo = null;
1378
- if (lastAIMessage) {
1379
- // Load the OUTPUT artifact from the last agent message
1380
- const artifactResult = await rv.RunView({
1381
- EntityName: 'MJ: Conversation Detail Artifacts',
1382
- ExtraFilter: `ConversationDetailID='${lastAIMessage.ID}' AND Direction='Output'`,
1383
- ResultType: 'entity_object'
1384
- }, this.currentUser);
1385
- if (artifactResult.Success && artifactResult.Results && artifactResult.Results.length > 0) {
1386
- // Load the artifact version content
1387
- const junctionRecord = artifactResult.Results[0];
1388
- const versionResult = await rv.RunView({
1389
- EntityName: 'MJ: Artifact Versions',
1390
- ExtraFilter: `ID='${junctionRecord.ArtifactVersionID}'`,
1391
- ResultType: 'entity_object'
1392
- }, this.currentUser);
1393
- if (versionResult.Success && versionResult.Results && versionResult.Results.length > 0) {
1394
- const version = versionResult.Results[0];
1395
- if (version.Content) {
1396
- try {
1327
+ // Use targetArtifactVersionId if specified (from intent check)
1328
+ if (targetArtifactVersionId && this.artifactsByDetailId) {
1329
+ console.log('🎯 Using target artifact version from intent check:', targetArtifactVersionId);
1330
+ // Find the artifact in pre-loaded data (O(n) search across all messages)
1331
+ for (const [detailId, artifacts] of this.artifactsByDetailId.entries()) {
1332
+ const targetArtifact = artifacts.find(a => a.artifactVersionId === targetArtifactVersionId);
1333
+ if (targetArtifact) {
1334
+ try {
1335
+ // Lazy load the full version entity to get Content
1336
+ const version = await targetArtifact.getVersion();
1337
+ if (version.Content) {
1397
1338
  previousPayload = JSON.parse(version.Content);
1398
1339
  previousArtifactInfo = {
1399
- artifactId: version.ArtifactID,
1400
- versionId: version.ID,
1401
- versionNumber: version.VersionNumber || 1
1340
+ artifactId: targetArtifact.artifactId,
1341
+ versionId: targetArtifact.artifactVersionId,
1342
+ versionNumber: targetArtifact.versionNumber
1402
1343
  };
1403
- console.log('📦 Loaded previous OUTPUT artifact as payload for continuation', previousArtifactInfo);
1344
+ console.log('📦 Loaded target artifact version as payload', previousArtifactInfo);
1404
1345
  }
1405
- catch (error) {
1406
- console.warn('⚠️ Could not parse previous artifact content:', error);
1346
+ }
1347
+ catch (error) {
1348
+ console.warn('⚠️ Could not load target artifact version:', error);
1349
+ }
1350
+ break;
1351
+ }
1352
+ }
1353
+ }
1354
+ // Fall back to most recent artifact if no target specified or target not found
1355
+ if (!previousPayload) {
1356
+ console.log('📦 Using most recent artifact from last agent message');
1357
+ // Find the last AI message from this same agent
1358
+ const lastAIMessage = this.conversationHistory
1359
+ .slice()
1360
+ .reverse()
1361
+ .find(msg => msg.Role === 'AI' && msg.AgentID === agentId);
1362
+ if (lastAIMessage && this.artifactsByDetailId) {
1363
+ // Get artifacts from pre-loaded data (no DB query!)
1364
+ const artifacts = this.artifactsByDetailId.get(lastAIMessage.ID);
1365
+ if (artifacts && artifacts.length > 0) {
1366
+ try {
1367
+ // Use the first artifact (should only be one OUTPUT per message)
1368
+ const artifact = artifacts[0];
1369
+ const version = await artifact.getVersion();
1370
+ if (version.Content) {
1371
+ previousPayload = JSON.parse(version.Content);
1372
+ previousArtifactInfo = {
1373
+ artifactId: artifact.artifactId,
1374
+ versionId: artifact.artifactVersionId,
1375
+ versionNumber: artifact.versionNumber
1376
+ };
1377
+ console.log('📦 Loaded most recent artifact as payload', previousArtifactInfo);
1407
1378
  }
1408
1379
  }
1380
+ catch (error) {
1381
+ console.warn('⚠️ Could not parse artifact content:', error);
1382
+ }
1409
1383
  }
1410
1384
  }
1411
1385
  }
@@ -1414,15 +1388,19 @@ export class MessageInputComponent {
1414
1388
  agentName: agentName,
1415
1389
  status: 'Processing...',
1416
1390
  relatedMessageId: userMessage.ID,
1417
- conversationDetailId: userMessage.ID
1391
+ conversationDetailId: userMessage.ID,
1392
+ conversationId: this.conversationId,
1393
+ conversationName: this.conversationName
1418
1394
  });
1395
+ // Declare agentResponseMessage outside try block so it's accessible in catch
1396
+ let agentResponseMessage = undefined;
1419
1397
  try {
1420
1398
  // Update user message status to In-Progress
1421
1399
  userMessage.Status = 'In-Progress';
1422
1400
  await userMessage.Save();
1423
1401
  this.messageSent.emit(userMessage);
1424
1402
  // Create AI response message BEFORE invoking agent (for duration tracking)
1425
- const agentResponseMessage = await this.dataCache.createConversationDetail(this.currentUser);
1403
+ agentResponseMessage = await this.dataCache.createConversationDetail(this.currentUser);
1426
1404
  agentResponseMessage.ConversationID = conversationId;
1427
1405
  agentResponseMessage.Role = 'AI';
1428
1406
  agentResponseMessage.Message = '⏳ Starting...'; // Initial message
@@ -1437,10 +1415,10 @@ export class MessageInputComponent {
1437
1415
  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
1438
1416
  this.createProgressCallback(agentResponseMessage, agentName), previousArtifactInfo?.artifactId, previousArtifactInfo?.versionId);
1439
1417
  // Remove from active tasks
1440
- this.activeTasks.remove(taskId);
1418
+ // Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
1441
1419
  if (result && result.success) {
1442
1420
  // Update the response message with agent result
1443
- await this.updateConversationDetail(agentResponseMessage, result.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete');
1421
+ await this.updateConversationDetail(agentResponseMessage, result.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete', result.suggestedResponses);
1444
1422
  // Server created artifacts (handles versioning) - emit event to trigger UI reload
1445
1423
  if (result.payload && Object.keys(result.payload).length > 0) {
1446
1424
  this.artifactCreated.emit({
@@ -1453,43 +1431,24 @@ export class MessageInputComponent {
1453
1431
  this.messageSent.emit(agentResponseMessage);
1454
1432
  }
1455
1433
  // Mark user message as complete
1456
- userMessage.Status = 'Complete';
1457
- await userMessage.Save();
1458
- this.messageSent.emit(userMessage);
1434
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1459
1435
  }
1460
1436
  else {
1461
- // Agent failed - create error message
1462
- const errorMessage = await this.dataCache.createConversationDetail(this.currentUser);
1463
- errorMessage.ConversationID = conversationId;
1464
- errorMessage.Role = 'AI';
1465
- errorMessage.Message = `❌ **${agentName}** failed\n\n${result?.agentRun?.ErrorMessage || 'Unknown error'}`;
1466
- errorMessage.ParentID = userMessage.ID;
1467
- errorMessage.Status = 'Error';
1468
- errorMessage.Error = result?.agentRun?.ErrorMessage || null;
1469
- errorMessage.HiddenToUser = false;
1470
- await errorMessage.Save();
1471
- this.messageSent.emit(errorMessage);
1472
- userMessage.Status = 'Complete';
1473
- await userMessage.Save();
1474
- this.messageSent.emit(userMessage);
1437
+ // Agent failed - update the existing message instead of creating a new one
1438
+ agentResponseMessage.Error = result?.agentRun?.ErrorMessage || null;
1439
+ await this.updateConversationDetail(agentResponseMessage, `❌ **${agentName}** failed\n\n${result?.agentRun?.ErrorMessage || 'Unknown error'}`, 'Error');
1440
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1475
1441
  }
1476
1442
  }
1477
1443
  catch (error) {
1478
1444
  console.error(`❌ Error continuing with agent ${agentName}:`, error);
1479
- this.activeTasks.remove(taskId);
1480
- const errorMessage = await this.dataCache.createConversationDetail(this.currentUser);
1481
- errorMessage.ConversationID = conversationId;
1482
- errorMessage.Role = 'AI';
1483
- errorMessage.Message = `❌ **${agentName}** encountered an error\n\n${String(error)}`;
1484
- errorMessage.ParentID = userMessage.ID;
1485
- errorMessage.Status = 'Error';
1486
- errorMessage.Error = String(error);
1487
- errorMessage.HiddenToUser = false;
1488
- await errorMessage.Save();
1489
- this.messageSent.emit(errorMessage);
1490
- userMessage.Status = 'Complete';
1491
- await userMessage.Save();
1492
- this.messageSent.emit(userMessage);
1445
+ // Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
1446
+ // Update the existing agent response message if it was created
1447
+ if (agentResponseMessage) {
1448
+ agentResponseMessage.Error = String(error);
1449
+ await this.updateConversationDetail(agentResponseMessage, `❌ **${agentName}** encountered an error\n\n${String(error)}`, 'Error');
1450
+ }
1451
+ await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
1493
1452
  }
1494
1453
  }
1495
1454
  /**
@@ -1553,6 +1512,22 @@ export class MessageInputComponent {
1553
1512
  markMessageComplete(conversationDetail) {
1554
1513
  const now = Date.now();
1555
1514
  this.completionTimestamps.set(conversationDetail.ID, now);
1515
+ // Remove task from active tasks if it exists
1516
+ const task = this.activeTasks.getByConversationDetailId(conversationDetail.ID);
1517
+ if (task) {
1518
+ console.log(`✅ Task found for message ${conversationDetail.ID} - removing from active tasks:`, {
1519
+ taskId: task.id,
1520
+ agentName: task.agentName,
1521
+ conversationId: task.conversationId,
1522
+ conversationName: task.conversationName
1523
+ });
1524
+ this.activeTasks.remove(task.id);
1525
+ // Show completion notification
1526
+ MJNotificationService.Instance?.CreateSimpleNotification(`${task.agentName} completed in ${task.conversationName || 'conversation'}`, 'success', 3000);
1527
+ }
1528
+ else {
1529
+ console.warn(`⚠️ No task found for completed message ${conversationDetail.ID} - task may have been removed prematurely or not added`);
1530
+ }
1556
1531
  // Emit completion event to parent so it can refresh agent run data
1557
1532
  this.messageComplete.emit({
1558
1533
  conversationDetailId: conversationDetail.ID,
@@ -1574,7 +1549,7 @@ export class MessageInputComponent {
1574
1549
  } if (rf & 2) {
1575
1550
  let _t;
1576
1551
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.inputBox = _t.first);
1577
- } }, inputs: { conversationId: "conversationId", currentUser: "currentUser", disabled: "disabled", placeholder: "placeholder", parentMessageId: "parentMessageId", conversationHistory: "conversationHistory", initialMessage: "initialMessage" }, outputs: { messageSent: "messageSent", agentResponse: "agentResponse", agentRunDetected: "agentRunDetected", agentRunUpdate: "agentRunUpdate", messageComplete: "messageComplete", artifactCreated: "artifactCreated", conversationRenamed: "conversationRenamed", intentCheckStarted: "intentCheckStarted", intentCheckCompleted: "intentCheckCompleted" }, features: [i0.ɵɵNgOnChangesFeature], decls: 4, vars: 8, consts: [["inputBox", ""], [1, "message-input-wrapper"], ["class", "processing-indicator", 4, "ngIf"], [3, "valueChange", "textSubmitted", "placeholder", "disabled", "showCharacterCount", "enableMentions", "currentUser", "rows", "value"], [1, "processing-indicator"], [1, "fas", "fa-circle-notch", "fa-spin"]], template: function MessageInputComponent_Template(rf, ctx) { if (rf & 1) {
1552
+ } }, inputs: { conversationId: "conversationId", conversationName: "conversationName", currentUser: "currentUser", disabled: "disabled", placeholder: "placeholder", parentMessageId: "parentMessageId", conversationHistory: "conversationHistory", initialMessage: "initialMessage", artifactsByDetailId: "artifactsByDetailId", agentRunsByDetailId: "agentRunsByDetailId", inProgressMessageIds: "inProgressMessageIds" }, outputs: { messageSent: "messageSent", agentResponse: "agentResponse", agentRunDetected: "agentRunDetected", agentRunUpdate: "agentRunUpdate", messageComplete: "messageComplete", artifactCreated: "artifactCreated", conversationRenamed: "conversationRenamed", intentCheckStarted: "intentCheckStarted", intentCheckCompleted: "intentCheckCompleted" }, features: [i0.ɵɵNgOnChangesFeature], decls: 4, vars: 8, consts: [["inputBox", ""], [1, "message-input-wrapper"], ["class", "processing-indicator", 4, "ngIf"], [3, "valueChange", "textSubmitted", "placeholder", "disabled", "showCharacterCount", "enableMentions", "currentUser", "rows", "value"], [1, "processing-indicator"], [1, "fas", "fa-circle-notch", "fa-spin"]], template: function MessageInputComponent_Template(rf, ctx) { if (rf & 1) {
1578
1553
  const _r1 = i0.ɵɵgetCurrentView();
1579
1554
  i0.ɵɵelementStart(0, "div", 1);
1580
1555
  i0.ɵɵtemplate(1, MessageInputComponent_div_1_Template, 4, 1, "div", 2);
@@ -1595,6 +1570,8 @@ export class MessageInputComponent {
1595
1570
  args: [{ selector: 'mj-message-input', template: "<div class=\"message-input-wrapper\">\n <!-- Processing Indicator Overlay -->\n <div class=\"processing-indicator\" *ngIf=\"isProcessing\">\n <i class=\"fas fa-circle-notch fa-spin\"></i>\n <span>{{ processingMessage }}</span>\n </div>\n\n <!-- Message Input Box -->\n <mj-message-input-box\n #inputBox\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled || isProcessing\"\n [showCharacterCount]=\"false\"\n [enableMentions]=\"true\"\n [currentUser]=\"currentUser\"\n [rows]=\"3\"\n [(value)]=\"messageText\"\n (textSubmitted)=\"onTextSubmitted($event)\">\n </mj-message-input-box>\n</div>", styles: [".message-input-wrapper {\n position: relative;\n width: 100%;\n}\n\n.processing-indicator {\n position: absolute;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n display: flex;\n align-items: center;\n gap: 0.5rem;\n padding: 0.75rem 1.25rem;\n background: rgba(255, 255, 255, 0.95);\n border-radius: 8px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);\n z-index: 10;\n pointer-events: none;\n\n i {\n color: var(--primary-color, #007bff);\n }\n\n span {\n font-size: 0.9rem;\n color: var(--text-primary, #333);\n }\n}"] }]
1596
1571
  }], () => [{ type: i1.DialogService }, { type: i2.ToastService }, { type: i3.ConversationAgentService }, { type: i4.ConversationStateService }, { type: i5.DataCacheService }, { type: i6.ActiveTasksService }, { type: i7.MentionParserService }, { type: i8.MentionAutocompleteService }], { conversationId: [{
1597
1572
  type: Input
1573
+ }], conversationName: [{
1574
+ type: Input
1598
1575
  }], currentUser: [{
1599
1576
  type: Input
1600
1577
  }], disabled: [{
@@ -1607,6 +1584,12 @@ export class MessageInputComponent {
1607
1584
  type: Input
1608
1585
  }], initialMessage: [{
1609
1586
  type: Input
1587
+ }], artifactsByDetailId: [{
1588
+ type: Input
1589
+ }], agentRunsByDetailId: [{
1590
+ type: Input
1591
+ }], inProgressMessageIds: [{
1592
+ type: Input
1610
1593
  }], messageSent: [{
1611
1594
  type: Output
1612
1595
  }], agentResponse: [{
@@ -1629,5 +1612,5 @@ export class MessageInputComponent {
1629
1612
  type: ViewChild,
1630
1613
  args: ['inputBox']
1631
1614
  }] }); })();
1632
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MessageInputComponent, { className: "MessageInputComponent", filePath: "src/lib/components/message/message-input.component.ts", lineNumber: 23 }); })();
1615
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MessageInputComponent, { className: "MessageInputComponent", filePath: "src/lib/components/message/message-input.component.ts", lineNumber: 25 }); })();
1633
1616
  //# sourceMappingURL=message-input.component.js.map