@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.
- package/dist/lib/components/collection/collection-view.component.d.ts +5 -1
- package/dist/lib/components/collection/collection-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-view.component.js +53 -11
- package/dist/lib/components/collection/collection-view.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +53 -13
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +455 -289
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-list.component.d.ts +9 -1
- package/dist/lib/components/conversation/conversation-list.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-list.component.js +258 -71
- package/dist/lib/components/conversation/conversation-list.component.js.map +1 -1
- package/dist/lib/components/mention/mention-dropdown.component.d.ts +6 -0
- package/dist/lib/components/mention/mention-dropdown.component.d.ts.map +1 -1
- package/dist/lib/components/mention/mention-dropdown.component.js +35 -13
- package/dist/lib/components/mention/mention-dropdown.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +49 -12
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +579 -221
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +16 -5
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +136 -64
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/message/message-list.component.d.ts +11 -5
- package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-list.component.js +48 -9
- package/dist/lib/components/message/message-list.component.js.map +1 -1
- package/dist/lib/components/navigation/conversation-navigation.component.d.ts.map +1 -1
- package/dist/lib/components/navigation/conversation-navigation.component.js +24 -9
- package/dist/lib/components/navigation/conversation-navigation.component.js.map +1 -1
- package/dist/lib/components/task/tasks-full-view.component.d.ts +47 -0
- package/dist/lib/components/task/tasks-full-view.component.d.ts.map +1 -0
- package/dist/lib/components/task/tasks-full-view.component.js +368 -0
- package/dist/lib/components/task/tasks-full-view.component.js.map +1 -0
- package/dist/lib/components/tasks/task-widget.component.d.ts +29 -0
- package/dist/lib/components/tasks/task-widget.component.d.ts.map +1 -0
- package/dist/lib/components/tasks/task-widget.component.js +385 -0
- package/dist/lib/components/tasks/task-widget.component.js.map +1 -0
- package/dist/lib/components/tasks/tasks-dropdown.component.d.ts +22 -8
- package/dist/lib/components/tasks/tasks-dropdown.component.d.ts.map +1 -1
- package/dist/lib/components/tasks/tasks-dropdown.component.js +195 -99
- package/dist/lib/components/tasks/tasks-dropdown.component.js.map +1 -1
- package/dist/lib/components/thread/thread-panel.component.d.ts +3 -1
- package/dist/lib/components/thread/thread-panel.component.d.ts.map +1 -1
- package/dist/lib/components/thread/thread-panel.component.js +14 -14
- package/dist/lib/components/thread/thread-panel.component.js.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts +43 -4
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.js +288 -46
- package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
- package/dist/lib/conversations.module.d.ts +40 -46
- package/dist/lib/conversations.module.d.ts.map +1 -1
- package/dist/lib/conversations.module.js +28 -42
- package/dist/lib/conversations.module.js.map +1 -1
- package/dist/lib/models/conversation-state.model.d.ts +29 -1
- package/dist/lib/models/conversation-state.model.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.d.ts +6 -5
- package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.js +18 -19
- package/dist/lib/services/conversation-agent.service.js.map +1 -1
- package/dist/lib/services/data-cache.service.d.ts +96 -0
- package/dist/lib/services/data-cache.service.d.ts.map +1 -0
- package/dist/lib/services/data-cache.service.js +203 -0
- package/dist/lib/services/data-cache.service.js.map +1 -0
- package/dist/public-api.d.ts +3 -6
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +3 -6
- package/dist/public-api.js.map +1 -1
- package/package.json +12 -10
- package/dist/lib/components/artifact/artifact-panel.component.d.ts +0 -22
- package/dist/lib/components/artifact/artifact-panel.component.d.ts.map +0 -1
- package/dist/lib/components/artifact/artifact-panel.component.js +0 -237
- package/dist/lib/components/artifact/artifact-panel.component.js.map +0 -1
- package/dist/lib/components/artifact/artifact-upload-modal.component.d.ts +0 -39
- package/dist/lib/components/artifact/artifact-upload-modal.component.d.ts.map +0 -1
- package/dist/lib/components/artifact/artifact-upload-modal.component.js +0 -384
- package/dist/lib/components/artifact/artifact-upload-modal.component.js.map +0 -1
- package/dist/lib/components/artifact/artifact-version-history.component.d.ts +0 -28
- package/dist/lib/components/artifact/artifact-version-history.component.d.ts.map +0 -1
- package/dist/lib/components/artifact/artifact-version-history.component.js +0 -280
- package/dist/lib/components/artifact/artifact-version-history.component.js.map +0 -1
- package/dist/lib/components/artifact/artifact-viewer-panel.component.d.ts +0 -22
- package/dist/lib/components/artifact/artifact-viewer-panel.component.d.ts.map +0 -1
- package/dist/lib/components/artifact/artifact-viewer-panel.component.js +0 -182
- package/dist/lib/components/artifact/artifact-viewer-panel.component.js.map +0 -1
- package/dist/lib/components/artifact/artifact-viewer.component.d.ts +0 -27
- package/dist/lib/components/artifact/artifact-viewer.component.d.ts.map +0 -1
- package/dist/lib/components/artifact/artifact-viewer.component.js +0 -266
- package/dist/lib/components/artifact/artifact-viewer.component.js.map +0 -1
- package/dist/lib/components/artifact/inline-artifact.component.d.ts +0 -46
- package/dist/lib/components/artifact/inline-artifact.component.d.ts.map +0 -1
- package/dist/lib/components/artifact/inline-artifact.component.js +0 -447
- package/dist/lib/components/artifact/inline-artifact.component.js.map +0 -1
- package/dist/lib/components/task/task-form-modal.component.d.ts +0 -42
- package/dist/lib/components/task/task-form-modal.component.d.ts.map +0 -1
- package/dist/lib/components/task/task-form-modal.component.js +0 -329
- package/dist/lib/components/task/task-form-modal.component.js.map +0 -1
- package/dist/lib/components/task/task-item.component.d.ts +0 -22
- package/dist/lib/components/task/task-item.component.d.ts.map +0 -1
- package/dist/lib/components/task/task-item.component.js +0 -234
- package/dist/lib/components/task/task-item.component.js.map +0 -1
- package/dist/lib/components/task/task-list.component.d.ts +0 -32
- package/dist/lib/components/task/task-list.component.d.ts.map +0 -1
- package/dist/lib/components/task/task-list.component.js +0 -290
- 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/
|
|
11
|
-
import * as i6 from "../../services/
|
|
12
|
-
import * as i7 from "../../services/mention-
|
|
13
|
-
import * as i8 from "
|
|
14
|
-
import * as i9 from "@angular/
|
|
15
|
-
import * as i10 from "
|
|
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
|
-
|
|
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
|
|
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 >
|
|
320
|
+
// Determine routing: @mention > last agent context > Sage
|
|
223
321
|
if (mentionResult.agentMention) {
|
|
224
|
-
// Direct @mention - skip
|
|
225
|
-
console.log('🎯 Direct @mention detected, bypassing
|
|
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
|
|
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
|
|
259
|
-
console.log('🤖 No agent context, using
|
|
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(
|
|
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(
|
|
417
|
+
this.activeTasks.updateStatusByConversationDetailId(conversationDetail.ID, progressText);
|
|
304
418
|
// Update the ConversationDetail message in real-time
|
|
305
419
|
try {
|
|
306
|
-
|
|
307
|
-
|
|
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
|
-
//
|
|
315
|
-
|
|
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
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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:
|
|
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
|
|
364
|
-
|
|
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
|
|
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
|
|
379
|
-
// Stage 1:
|
|
480
|
+
// Use Sage to evaluate and route
|
|
481
|
+
// Stage 1: Sage evaluates the message
|
|
380
482
|
taskId = this.activeTasks.add({
|
|
381
|
-
agentName: '
|
|
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
|
|
387
|
-
// Remove
|
|
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('⚠️
|
|
504
|
+
console.warn('⚠️ Sage failed:', result?.agentRun?.ErrorMessage);
|
|
403
505
|
return;
|
|
404
506
|
}
|
|
405
|
-
console.log('🤖
|
|
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
|
|
411
|
-
if (result.
|
|
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
|
|
532
|
+
// Stage 4: Direct chat response from Sage
|
|
416
533
|
else if (result.agentRun.FinalStep === 'Chat' && result.agentRun.Message) {
|
|
417
|
-
//
|
|
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
|
|
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
|
|
553
|
+
// Stage 5: Silent observation - but check for message content first
|
|
436
554
|
else {
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
926
|
-
this.createProgressCallback(agentResponseMessage
|
|
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
|
|
932
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
994
|
-
|
|
995
|
-
//
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
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 =
|
|
1013
|
-
version.VersionNumber =
|
|
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
|
-
|
|
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.
|
|
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: [
|
|
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.
|
|
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:
|
|
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
|