@memberjunction/ng-conversations 6.2.0-edge.5 → 6.2.0-edge.6
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/attachment/image-viewer.component.d.ts +3 -1
- package/dist/lib/components/attachment/image-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/attachment/image-viewer.component.js +5 -1
- package/dist/lib/components/attachment/image-viewer.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +13 -6
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +74 -28
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +60 -20
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +282 -381
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +16 -1
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +180 -144
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/message/message-list.component.d.ts +5 -0
- package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-list.component.js +53 -38
- package/dist/lib/components/message/message-list.component.js.map +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.d.ts +19 -0
- package/dist/lib/components/overlay/chat-overlay.component.d.ts.map +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.js +125 -13
- package/dist/lib/components/overlay/chat-overlay.component.js.map +1 -1
- package/dist/lib/components/project/project-form-modal.component.js +2 -2
- package/dist/lib/components/project/project-form-modal.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts +6 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-agent-banner.component.js +110 -82
- package/dist/lib/components/realtime/realtime-agent-banner.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-call-failure-notice.d.ts +13 -0
- package/dist/lib/components/realtime/realtime-call-failure-notice.d.ts.map +1 -0
- package/dist/lib/components/realtime/realtime-call-failure-notice.js +21 -0
- package/dist/lib/components/realtime/realtime-call-failure-notice.js.map +1 -0
- package/dist/lib/components/realtime/realtime-composer.component.js +114 -102
- package/dist/lib/components/realtime/realtime-composer.component.js.map +1 -1
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts +21 -2
- package/dist/lib/components/realtime/realtime-session-overlay.component.d.ts.map +1 -1
- package/dist/lib/components/realtime/realtime-session-overlay.component.js +113 -76
- package/dist/lib/components/realtime/realtime-session-overlay.component.js.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.d.ts.map +1 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js +7 -1
- package/dist/lib/components/realtime/remote-browser/remote-browser-channel.js.map +1 -1
- package/dist/lib/services/conversation-agent.service.d.ts +37 -12
- package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.js +51 -27
- package/dist/lib/services/conversation-agent.service.js.map +1 -1
- package/dist/lib/services/conversation-detail-window.store.d.ts +5 -0
- package/dist/lib/services/conversation-detail-window.store.d.ts.map +1 -1
- package/dist/lib/services/conversation-detail-window.store.js +52 -5
- package/dist/lib/services/conversation-detail-window.store.js.map +1 -1
- package/dist/lib/services/realtime-session.service.d.ts.map +1 -1
- package/dist/lib/services/realtime-session.service.js +2 -1
- package/dist/lib/services/realtime-session.service.js.map +1 -1
- package/dist/lib/utils/agent-failure-display.d.ts +14 -0
- package/dist/lib/utils/agent-failure-display.d.ts.map +1 -0
- package/dist/lib/utils/agent-failure-display.js +26 -0
- package/dist/lib/utils/agent-failure-display.js.map +1 -0
- package/dist/lib/utils/message-list-order.d.ts +13 -0
- package/dist/lib/utils/message-list-order.d.ts.map +1 -0
- package/dist/lib/utils/message-list-order.js +31 -0
- package/dist/lib/utils/message-list-order.js.map +1 -0
- package/dist/lib/utils/pin-state.d.ts +11 -0
- package/dist/lib/utils/pin-state.d.ts.map +1 -0
- package/dist/lib/utils/pin-state.js +9 -0
- package/dist/lib/utils/pin-state.js.map +1 -0
- package/dist/public-api.d.ts +1 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +1 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +36 -36
|
@@ -8,7 +8,7 @@ import { ApplyRoutingDecision, ArtifactVersionForTurn, RunRoutingDecision, Shoul
|
|
|
8
8
|
import { BuildRecentTurns, BuildRoutingArtifactVersions, CanAskRoutingDecision, CollectRoutingParticipants, IsAgentAllowed } from '@memberjunction/ai-core-plus';
|
|
9
9
|
import { GenerateAndApplyConversationName } from '../../services/conversation-naming';
|
|
10
10
|
import { AIEngineBase } from '@memberjunction/ai-engine-base';
|
|
11
|
-
import { ConversationUtility, agentFailureDisposition, agentFailureMessage } from '@memberjunction/ai-core-plus';
|
|
11
|
+
import { ConversationUtility, agentFailureDisposition, agentFailureMessage, ContinuationAnnouncementText } from '@memberjunction/ai-core-plus';
|
|
12
12
|
import { MentionAutocompleteService } from '../../services/mention-autocomplete.service';
|
|
13
13
|
import { PlanModePreference } from '../../utils/plan-mode-preference';
|
|
14
14
|
import { MJNotificationService } from '@memberjunction/ng-notifications';
|
|
@@ -824,7 +824,7 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
824
824
|
*/
|
|
825
825
|
async InsertAgentMention(agentName, focus = true, clearExisting = true) {
|
|
826
826
|
if (!this.InputBox) {
|
|
827
|
-
|
|
827
|
+
LogStatusEx({ message: `[Omnibar→Chat] InsertAgentMention('${agentName}'): input box not mounted yet — caller will retry`, verboseOnly: true });
|
|
828
828
|
return false;
|
|
829
829
|
}
|
|
830
830
|
if (clearExisting) {
|
|
@@ -835,7 +835,7 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
835
835
|
}
|
|
836
836
|
try {
|
|
837
837
|
if (!this.mentionAutocomplete.IsInitialized && this.CurrentUser) {
|
|
838
|
-
|
|
838
|
+
LogStatusEx({ message: `[Omnibar→Chat] InsertAgentMention('${agentName}'): initializing mention autocomplete…`, verboseOnly: true });
|
|
839
839
|
await this.mentionAutocomplete.initialize(this.CurrentUser);
|
|
840
840
|
}
|
|
841
841
|
const wanted = agentName.trim().toLowerCase();
|
|
@@ -844,7 +844,7 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
844
844
|
.find(s => s.type === 'agent' && s.name.trim().toLowerCase() === wanted);
|
|
845
845
|
if (suggestion) {
|
|
846
846
|
const inserted = this.InputBox.InsertMention(suggestion, focus);
|
|
847
|
-
|
|
847
|
+
LogStatusEx({ message: `[Omnibar→Chat] InsertAgentMention('${agentName}'): resolved to pill (id=${suggestion.id}) — insert ${inserted ? 'OK' : 'FAILED (editor view not ready)'}`, verboseOnly: true });
|
|
848
848
|
if (inserted) {
|
|
849
849
|
if (focus) {
|
|
850
850
|
this.scheduleFocusReassert(agentName);
|
|
@@ -873,7 +873,7 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
873
873
|
const editor = this.InputBox?.mentionEditor;
|
|
874
874
|
if (editor && !editor.HasFocus) {
|
|
875
875
|
const ok = editor.FocusCaretAtEnd();
|
|
876
|
-
|
|
876
|
+
LogStatusEx({ message: `[Omnibar→Chat] focus re-assert (+${delay}ms) for '${context}': ${ok ? 'refocused' : 'editor gone'}`, verboseOnly: true });
|
|
877
877
|
}
|
|
878
878
|
}, delay);
|
|
879
879
|
}
|
|
@@ -1515,13 +1515,13 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
1515
1515
|
}
|
|
1516
1516
|
// Skip if already complete or errored
|
|
1517
1517
|
if (message.Status === 'Complete' || message.Status === 'Error') {
|
|
1518
|
-
|
|
1518
|
+
LogStatusEx({ message: `[StreamingCallback] Message ${messageId} is ${message.Status}, ignoring progress update`, verboseOnly: true });
|
|
1519
1519
|
return;
|
|
1520
1520
|
}
|
|
1521
1521
|
// Check if message was marked as completed (prevents race condition)
|
|
1522
1522
|
const completionTime = this.completionTimestamps.get(messageId);
|
|
1523
1523
|
if (completionTime) {
|
|
1524
|
-
|
|
1524
|
+
LogStatusEx({ message: `[StreamingCallback] Message ${messageId} marked complete at ${new Date(completionTime).toISOString()}, ignoring late progress update`, verboseOnly: true });
|
|
1525
1525
|
return;
|
|
1526
1526
|
}
|
|
1527
1527
|
// Streamed final-response content: the service accumulates deltas, so
|
|
@@ -1557,7 +1557,7 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
1557
1557
|
this.MessageSent.emit(message);
|
|
1558
1558
|
// CRITICAL: Update ActiveTasksService to keep the tasks dropdown in sync
|
|
1559
1559
|
this.activeTasks.updateStatusByConversationDetailId(message.ID, progress.message);
|
|
1560
|
-
|
|
1560
|
+
LogStatusEx({ message: `[StreamingCallback] Updated message ${messageId}: ${progress.taskName || 'Agent'}`, verboseOnly: true });
|
|
1561
1561
|
}
|
|
1562
1562
|
catch (error) {
|
|
1563
1563
|
console.error(`[StreamingCallback] Error updating message ${messageId}:`, error);
|
|
@@ -1572,7 +1572,7 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
1572
1572
|
if (this.registeredCallbacks.size === 0) {
|
|
1573
1573
|
return;
|
|
1574
1574
|
}
|
|
1575
|
-
|
|
1575
|
+
LogStatusEx({ message: `🧹 Unregistering ${this.registeredCallbacks.size} message callbacks`, verboseOnly: true });
|
|
1576
1576
|
for (const [messageId, callback] of this.registeredCallbacks) {
|
|
1577
1577
|
this.streamingService.unregisterMessageCallback(messageId, callback);
|
|
1578
1578
|
}
|
|
@@ -1910,18 +1910,6 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
1910
1910
|
return undefined;
|
|
1911
1911
|
}
|
|
1912
1912
|
}
|
|
1913
|
-
/**
|
|
1914
|
-
* A new AI row of an agent turn: in `conversationId`, on the turn's branch `branchId` (null is
|
|
1915
|
-
* Main), visible. The caller sets Message, ParentID, Status and AgentID, then saves it.
|
|
1916
|
-
*/
|
|
1917
|
-
async createTurnRow(conversationId, branchId) {
|
|
1918
|
-
const row = await this.dataCache.createConversationDetail(this.CurrentUser);
|
|
1919
|
-
row.ConversationID = conversationId;
|
|
1920
|
-
row.BranchID = branchId;
|
|
1921
|
-
row.Role = 'AI';
|
|
1922
|
-
row.HiddenToUser = false;
|
|
1923
|
-
return row;
|
|
1924
|
-
}
|
|
1925
1913
|
/**
|
|
1926
1914
|
* Creates and configures a new conversation detail message on `branchId` (null is Main)
|
|
1927
1915
|
*/
|
|
@@ -2398,7 +2386,7 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
2398
2386
|
// Form responses always continue with the agent that requested the form
|
|
2399
2387
|
// Don't show "Analyzing intent..." message for this obvious case
|
|
2400
2388
|
if (ConversationUtility.ContainsFormResponse(message)) {
|
|
2401
|
-
|
|
2389
|
+
LogStatusEx({ message: '✅ Form response detected, skipping intent check UI (fast path)', verboseOnly: true });
|
|
2402
2390
|
return {
|
|
2403
2391
|
decision: 'YES',
|
|
2404
2392
|
reasoning: 'User submitted a form response to the previous agent'
|
|
@@ -2530,143 +2518,76 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
2530
2518
|
* @param branchId The turn's branch (null is Main).
|
|
2531
2519
|
*/
|
|
2532
2520
|
async processMessageThroughAgent(userMessage, mentionResult, branchId) {
|
|
2533
|
-
let taskId = null;
|
|
2534
2521
|
let conversationManagerMessage = null;
|
|
2535
2522
|
// CRITICAL: Capture conversationId from user message at start
|
|
2536
2523
|
// This prevents race condition when user switches conversations during async processing
|
|
2537
2524
|
const conversationId = userMessage.ConversationID;
|
|
2538
2525
|
try {
|
|
2539
|
-
//
|
|
2540
|
-
conversationManagerMessage = await this.
|
|
2541
|
-
conversationManagerMessage.Message = '⏳ Starting...';
|
|
2542
|
-
conversationManagerMessage.ParentID = userMessage.ID;
|
|
2543
|
-
conversationManagerMessage.Status = 'In-Progress';
|
|
2544
|
-
// Use the preloaded Sage agent instead of looking it up
|
|
2545
|
-
if (this.ConverationManagerAgent?.ID) {
|
|
2546
|
-
conversationManagerMessage.AgentID = this.ConverationManagerAgent.ID;
|
|
2547
|
-
}
|
|
2548
|
-
await conversationManagerMessage.Save();
|
|
2549
|
-
this.MessageSent.emit(conversationManagerMessage);
|
|
2526
|
+
// Sage's reply shows as a local placeholder until the server creates the row and names it.
|
|
2527
|
+
conversationManagerMessage = await this.createReplyPlaceholder(conversationId, branchId, userMessage.ID, this.ConverationManagerAgent?.ID ?? null);
|
|
2550
2528
|
// Use Sage to evaluate and route
|
|
2551
2529
|
// Stage 1: Sage evaluates the message
|
|
2552
|
-
|
|
2530
|
+
const result = await this.agentService.ProcessMessage(conversationId, userMessage, this.ConversationHistory, this.adoptReplyRow(conversationManagerMessage, {
|
|
2553
2531
|
agentName: 'Sage',
|
|
2554
2532
|
status: 'Evaluating message...',
|
|
2555
2533
|
relatedMessageId: userMessage.ID,
|
|
2556
|
-
conversationDetailId: conversationManagerMessage.ID,
|
|
2557
2534
|
conversationId,
|
|
2558
2535
|
conversationName: this.ConversationName
|
|
2559
|
-
})
|
|
2560
|
-
const result = await this.agentService.ProcessMessage(conversationId, userMessage, this.ConversationHistory, conversationManagerMessage.ID, this.createProgressCallback(conversationManagerMessage, 'Sage'), this.AppContext, this.PlanModeEnabled, // per-request Plan Mode toggle
|
|
2536
|
+
}), this.createProgressCallback(conversationManagerMessage, 'Sage'), this.AppContext, this.PlanModeEnabled, // per-request Plan Mode toggle
|
|
2561
2537
|
this._pendingRequestedSkillIDs, // user-requested skills (/skill mentions)
|
|
2562
2538
|
this.AllowedAgentIDs, // the agents the manager may delegate to
|
|
2563
|
-
this.AgentHistoryFrom
|
|
2539
|
+
this.AgentHistoryFrom, // the first moment of the conversation the run may read
|
|
2540
|
+
branchId);
|
|
2564
2541
|
// Emit afterAgentTurn on the happy path only — the error/failure branch
|
|
2565
2542
|
// immediately below handles its own cleanup and skips this emit.
|
|
2566
2543
|
if (result && result.success) {
|
|
2567
2544
|
this.emitAfterAgentTurn(conversationId, result);
|
|
2568
2545
|
}
|
|
2569
|
-
// Task will be removed automatically in markMessageComplete()
|
|
2570
|
-
// DO NOT remove here - agent may still be streaming/processing
|
|
2571
|
-
taskId = null; // Clear reference but don't remove from service
|
|
2572
2546
|
if (!result || !result.success) {
|
|
2573
2547
|
await this.applyAgentFailureToDetail(conversationManagerMessage, userMessage, this.ConverationManagerAgent?.Name || 'Sage', result, 'failed');
|
|
2574
2548
|
console.warn('⚠️ Sage failed:', agentFailureMessage(result, 'Agent evaluation failed'));
|
|
2575
|
-
this.cleanupCompletionTimestamp(conversationManagerMessage.ID);
|
|
2576
2549
|
return;
|
|
2577
2550
|
}
|
|
2578
|
-
//
|
|
2579
|
-
|
|
2580
|
-
|
|
2581
|
-
// Remove CM from active tasks
|
|
2582
|
-
if (taskId) {
|
|
2583
|
-
// Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
|
|
2584
|
-
}
|
|
2585
|
-
}
|
|
2586
|
-
// Stage 3: Check for sub-agent invocation (single-step delegation)
|
|
2587
|
-
else if (result.agentRun.FinalStep === 'Success' && result.payload?.invokeAgent) {
|
|
2588
|
-
// Reuse the existing conversationManagerMessage instead of creating new ones
|
|
2589
|
-
await this.handleSubAgentInvocation(userMessage, result, conversationId, branchId, conversationManagerMessage);
|
|
2590
|
-
// Remove CM from active tasks
|
|
2591
|
-
if (taskId) {
|
|
2592
|
-
// Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
|
|
2593
|
-
}
|
|
2594
|
-
}
|
|
2595
|
-
// Stage 4: Direct chat response from Agent
|
|
2596
|
-
else if (result.agentRun.FinalStep === 'Chat' && result.agentRun.Message) {
|
|
2597
|
-
// Normal chat response
|
|
2598
|
-
// use update helper to ensure that if there is a race condition with more streaming updates we don't allow that to override this final message
|
|
2599
|
-
// Note: updateConversationDetail will call markMessageComplete() for us
|
|
2600
|
-
await this.updateConversationDetail(conversationManagerMessage, result.agentRun.Message, 'Complete', result);
|
|
2601
|
-
// Handle artifacts if any (but NOT task graphs - those are intermediate work products)
|
|
2602
|
-
// Server already created artifacts - just emit event to trigger UI reload
|
|
2603
|
-
if (result.payload && Object.keys(result.payload).length > 0) {
|
|
2604
|
-
this.emitArtifactReload(conversationManagerMessage);
|
|
2605
|
-
this.MessageSent.emit(conversationManagerMessage);
|
|
2606
|
-
}
|
|
2607
|
-
await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
|
|
2608
|
-
// Remove CM from active tasks
|
|
2609
|
-
if (taskId) {
|
|
2610
|
-
// Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
|
|
2611
|
-
}
|
|
2612
|
-
// Clean up completion timestamp after delay
|
|
2613
|
-
this.cleanupCompletionTimestamp(conversationManagerMessage.ID);
|
|
2614
|
-
}
|
|
2615
|
-
// Stage 5: Silent observation - but check for message content first
|
|
2616
|
-
else {
|
|
2617
|
-
// Check if there's a message to display even without payload/taskGraph
|
|
2618
|
-
if (result.agentRun.Message) {
|
|
2619
|
-
// Mark message as completing BEFORE setting final content
|
|
2620
|
-
this.markMessageComplete(conversationManagerMessage);
|
|
2621
|
-
conversationManagerMessage.HiddenToUser = false;
|
|
2622
|
-
// use update helper to ensure that if there is a race condition with more streaming updates we don't allow that to override this final message
|
|
2623
|
-
await this.updateConversationDetail(conversationManagerMessage, result.agentRun.Message, 'Complete', result);
|
|
2624
|
-
this.MessageSent.emit(conversationManagerMessage);
|
|
2625
|
-
// Clean up completion timestamp after delay
|
|
2626
|
-
this.cleanupCompletionTimestamp(conversationManagerMessage.ID);
|
|
2627
|
-
}
|
|
2628
|
-
else {
|
|
2629
|
-
// Mark message as completing
|
|
2630
|
-
this.markMessageComplete(conversationManagerMessage);
|
|
2631
|
-
// Hide the Sage message
|
|
2632
|
-
conversationManagerMessage.HiddenToUser = true;
|
|
2633
|
-
// use update helper to ensure that if there is a race condition with more streaming updates we don't allow that to override this final message
|
|
2634
|
-
await this.updateConversationDetail(conversationManagerMessage, conversationManagerMessage.Message, 'Complete', result);
|
|
2635
|
-
this.MessageSent.emit(conversationManagerMessage);
|
|
2636
|
-
await this.handleSilentObservation(userMessage, conversationId, branchId);
|
|
2637
|
-
// Clean up completion timestamp after delay
|
|
2638
|
-
this.cleanupCompletionTimestamp(conversationManagerMessage.ID);
|
|
2639
|
-
}
|
|
2640
|
-
// Remove CM from active tasks
|
|
2641
|
-
if (taskId) {
|
|
2642
|
-
// Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
|
|
2643
|
-
}
|
|
2644
|
-
}
|
|
2551
|
+
// The server wrote Sage's reply; show it as the server left it before routing on.
|
|
2552
|
+
await this.settleReply(conversationManagerMessage, '❌ Sage could not start.');
|
|
2553
|
+
await this.routeConversationManagerResult(userMessage, result, conversationId, branchId, conversationManagerMessage);
|
|
2645
2554
|
}
|
|
2646
2555
|
catch (error) {
|
|
2647
2556
|
console.error('❌ Error processing message through agents:', error);
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
// Use updateConversationDetail to ensure task cleanup
|
|
2651
|
-
conversationManagerMessage.Error = String(error);
|
|
2652
|
-
await this.updateConversationDetail(conversationManagerMessage, `❌ Error: ${String(error)}`, 'Error');
|
|
2653
|
-
// Clean up completion timestamp
|
|
2654
|
-
this.cleanupCompletionTimestamp(conversationManagerMessage.ID);
|
|
2557
|
+
if (conversationManagerMessage) {
|
|
2558
|
+
await this.settleReply(conversationManagerMessage, `❌ Error: ${String(error)}`);
|
|
2655
2559
|
}
|
|
2656
2560
|
// Mark user message as complete
|
|
2657
2561
|
await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
|
|
2658
|
-
// Clean up active task
|
|
2659
|
-
if (taskId) {
|
|
2660
|
-
// Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
|
|
2661
|
-
}
|
|
2662
2562
|
}
|
|
2663
2563
|
}
|
|
2664
|
-
/**
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2564
|
+
/**
|
|
2565
|
+
* Acts on the conversation manager's settled reply: a workflow, a delegation, a chat answer, or
|
|
2566
|
+
* silence (its reply is hidden and the last agent may continue).
|
|
2567
|
+
*/
|
|
2568
|
+
async routeConversationManagerResult(userMessage, result, conversationId, branchId, managerReply) {
|
|
2569
|
+
// Stage 2: Check for task graph (multi-step orchestration)
|
|
2570
|
+
if (result.payload?.taskGraph) {
|
|
2571
|
+
await this.handleTaskGraphExecution(userMessage, result, conversationId, branchId, managerReply);
|
|
2572
|
+
}
|
|
2573
|
+
// Stage 3: Check for sub-agent invocation (single-step delegation)
|
|
2574
|
+
else if (result.agentRun.FinalStep === 'Success' && result.payload?.invokeAgent) {
|
|
2575
|
+
await this.handleSubAgentInvocation(userMessage, result, conversationId, branchId, managerReply);
|
|
2576
|
+
}
|
|
2577
|
+
// Stage 4: Direct chat response from Agent
|
|
2578
|
+
else if (result.agentRun.FinalStep === 'Chat' && result.agentRun.Message) {
|
|
2579
|
+
// Handle artifacts if any (but NOT task graphs - those are intermediate work products)
|
|
2580
|
+
// Server already created artifacts - just emit event to trigger UI reload
|
|
2581
|
+
if (result.payload && Object.keys(result.payload).length > 0) {
|
|
2582
|
+
this.emitArtifactReload(managerReply);
|
|
2583
|
+
this.MessageSent.emit(managerReply);
|
|
2584
|
+
}
|
|
2585
|
+
await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
|
|
2586
|
+
}
|
|
2587
|
+
// Stage 5: Silent observation — Sage said nothing, so its reply is hidden and the last agent may continue
|
|
2588
|
+
else if (!result.agentRun.Message) {
|
|
2589
|
+
await this.hideReply(managerReply);
|
|
2590
|
+
await this.handleSilentObservation(userMessage, conversationId, branchId);
|
|
2670
2591
|
}
|
|
2671
2592
|
}
|
|
2672
2593
|
/**
|
|
@@ -2702,49 +2623,59 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
2702
2623
|
return;
|
|
2703
2624
|
}
|
|
2704
2625
|
const workflowName = taskGraph.workflowName || 'Workflow';
|
|
2705
|
-
const reasoning = taskGraph.reasoning || 'Executing multi-step workflow';
|
|
2706
|
-
const taskCount = Array.isArray(taskGraph.tasks) ? taskGraph.tasks.length : 0;
|
|
2707
|
-
// A message the user can watch. Progress frames from the dispatcher land against this ID.
|
|
2708
|
-
const taskExecutionMessage = await this.createTurnRow(conversationId, branchId);
|
|
2709
|
-
taskExecutionMessage.Message = `⏳ **${workflowName}**\n\n${reasoning}\n\nSubmitting ${taskCount} task(s)…`;
|
|
2710
|
-
taskExecutionMessage.ParentID = conversationManagerMessage.ID;
|
|
2711
|
-
taskExecutionMessage.Status = 'In-Progress';
|
|
2712
|
-
await taskExecutionMessage.Save();
|
|
2713
|
-
this.MessageSent.emit(taskExecutionMessage);
|
|
2714
|
-
const callback = this.createMessageProgressCallback(taskExecutionMessage.ID);
|
|
2715
|
-
this.registeredCallbacks.set(taskExecutionMessage.ID, callback);
|
|
2716
|
-
this.streamingService.registerMessageCallback(taskExecutionMessage.ID, callback);
|
|
2717
2626
|
try {
|
|
2718
2627
|
// `TaskGraph.Submit` is a Remote Operation, not a bespoke mutation: the same call site is
|
|
2719
2628
|
// reachable from MCP, an Action wrapper, and this UI. `Execute` marshals over the generic
|
|
2720
2629
|
// `ExecuteRemoteOperation` transport, so there is no hand-written GraphQL document here.
|
|
2721
|
-
|
|
2630
|
+
//
|
|
2631
|
+
// The server writes the workflow's status bubble under the reply that planned it (only the
|
|
2632
|
+
// server writes agent replies) and binds the graph to it, so progress frames land on it.
|
|
2633
|
+
const input = {
|
|
2722
2634
|
spec: taskGraph,
|
|
2723
2635
|
environmentID: MJEnvironmentEntityExtended.DefaultEnvironmentID,
|
|
2724
|
-
conversationDetailID:
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
await this.
|
|
2731
|
-
}
|
|
2732
|
-
|
|
2733
|
-
|
|
2636
|
+
conversationDetailID: conversationManagerMessage.ID,
|
|
2637
|
+
postStatusMessage: true,
|
|
2638
|
+
};
|
|
2639
|
+
const result = await new TaskGraphSubmitOperation().Execute(input);
|
|
2640
|
+
const output = result.Output;
|
|
2641
|
+
if (output?.statusConversationDetailID) {
|
|
2642
|
+
await this.showWorkflowStatus(output.statusConversationDetailID);
|
|
2643
|
+
}
|
|
2644
|
+
if (!result.Success || !output?.success) {
|
|
2645
|
+
// The bubble says it was rejected; the reason (it may quote the plan) is shown only to this person.
|
|
2646
|
+
const errorMsg = output?.errorMessage || result.ErrorMessage || 'Unknown error';
|
|
2734
2647
|
console.error('Task graph submission rejected:', errorMsg);
|
|
2735
|
-
|
|
2736
|
-
await this.updateConversationDetail(taskExecutionMessage, `❌ **${workflowName}** rejected: ${errorMsg}`, 'Error');
|
|
2737
|
-
this.releaseProgressCallback(taskExecutionMessage.ID);
|
|
2648
|
+
this.notifyAgentTurnProblem(`${workflowName} was rejected: ${errorMsg}`, 'error');
|
|
2738
2649
|
}
|
|
2739
2650
|
}
|
|
2740
2651
|
catch (error) {
|
|
2741
2652
|
const msg = error instanceof Error ? error.message : String(error);
|
|
2742
2653
|
console.error('Task graph submission failed:', error);
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2654
|
+
this.notifyAgentTurnProblem(`${workflowName} failed to submit: ${msg}`, 'error');
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
/**
|
|
2658
|
+
* Shows the workflow status bubble the server wrote, and follows the dispatcher's progress frames on
|
|
2659
|
+
* it while it runs, as the chat followed its own status row.
|
|
2660
|
+
*/
|
|
2661
|
+
async showWorkflowStatus(statusId) {
|
|
2662
|
+
const status = await this.dataCache.getConversationDetail(statusId, this.CurrentUser);
|
|
2663
|
+
if (!status) {
|
|
2664
|
+
console.warn(`Workflow status ${statusId} could not be loaded`);
|
|
2665
|
+
return;
|
|
2666
|
+
}
|
|
2667
|
+
this.MessageSent.emit(status);
|
|
2668
|
+
if (status.Status === 'In-Progress') {
|
|
2669
|
+
const callback = this.createMessageProgressCallback(status.ID);
|
|
2670
|
+
this.registeredCallbacks.set(status.ID, callback);
|
|
2671
|
+
this.streamingService.registerMessageCallback(status.ID, callback);
|
|
2746
2672
|
}
|
|
2747
2673
|
}
|
|
2674
|
+
/**
|
|
2675
|
+
* Saves the person's own message with its text and status (in MJ's chat, marking it Complete once its turn is
|
|
2676
|
+
* over). Never used for an agent's reply: only the server writes those, and the chat shows them with
|
|
2677
|
+
* {@link settleReply}.
|
|
2678
|
+
*/
|
|
2748
2679
|
async updateConversationDetail(convoDetail, message, status, result) {
|
|
2749
2680
|
// Mark as completing FIRST if status is Complete or Error
|
|
2750
2681
|
// This ensures task cleanup happens even if we return early due to guard clause
|
|
@@ -2815,10 +2746,10 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
2815
2746
|
}
|
|
2816
2747
|
const source = await this.agentService.FindLatestAgentOutputVersion(this.ConversationId, agentId, this.AgentHistoryFrom, branchId);
|
|
2817
2748
|
if (!source || source.payload == null) {
|
|
2818
|
-
|
|
2749
|
+
LogStatusEx({ message: `📦 No previous payload found for agent ${agentId}`, verboseOnly: true });
|
|
2819
2750
|
return { payload: null, artifactInfo: null };
|
|
2820
2751
|
}
|
|
2821
|
-
|
|
2752
|
+
LogStatusEx({ message: `📦 Loaded previous payload for agent ${agentId} from artifact version ${source.versionId}`, verboseOnly: true });
|
|
2822
2753
|
return {
|
|
2823
2754
|
payload: source.payload,
|
|
2824
2755
|
artifactInfo: {
|
|
@@ -2829,47 +2760,25 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
2829
2760
|
};
|
|
2830
2761
|
}
|
|
2831
2762
|
/**
|
|
2832
|
-
* Handle sub-agent invocation based on Sage's payload
|
|
2833
|
-
*
|
|
2763
|
+
* Handle sub-agent invocation based on Sage's payload: the delegate answers the user's message
|
|
2764
|
+
* with its own reply, which the server creates. A failed first attempt is retried once; its
|
|
2765
|
+
* reply is hidden when the retry takes over.
|
|
2834
2766
|
*
|
|
2835
2767
|
* @param branchId The turn's branch (null is Main).
|
|
2836
2768
|
*/
|
|
2837
|
-
async handleSubAgentInvocation(userMessage, managerResult, conversationId, branchId,
|
|
2769
|
+
async handleSubAgentInvocation(userMessage, managerResult, conversationId, branchId, delegatingReply) {
|
|
2838
2770
|
const payload = managerResult.payload;
|
|
2839
2771
|
const agentName = payload.invokeAgent;
|
|
2840
2772
|
const reasoning = payload.reasoning || 'Delegating to specialist agent';
|
|
2841
|
-
|
|
2773
|
+
const agent = AIEngineBase.Instance.ReadableAgents.find(a => a.Name === agentName);
|
|
2774
|
+
// The manager routes only among the allowed agents, but its answer is model output: hold
|
|
2775
|
+
// the host's list here too, before any run starts for the delegate.
|
|
2776
|
+
if (this.AllowedAgentIDs != null && !IsAgentAllowed(agent?.ID, this.AllowedAgentIDs)) {
|
|
2777
|
+
await this.refuseDisallowedAgents(userMessage, [agentName]);
|
|
2778
|
+
return;
|
|
2779
|
+
}
|
|
2842
2780
|
let agentResponseMessage = null;
|
|
2843
2781
|
try {
|
|
2844
|
-
// Look up the agent to get its ID
|
|
2845
|
-
const agent = AIEngineBase.Instance.ReadableAgents.find(a => a.Name === agentName);
|
|
2846
|
-
// The manager routes only among the allowed agents, but its answer is model output: hold
|
|
2847
|
-
// the host's list here too, before any row is written for the delegate.
|
|
2848
|
-
if (this.AllowedAgentIDs != null && !IsAgentAllowed(agent?.ID, this.AllowedAgentIDs)) {
|
|
2849
|
-
await this.refuseDisallowedAgents(userMessage, [agentName]);
|
|
2850
|
-
return;
|
|
2851
|
-
}
|
|
2852
|
-
// Create AI response message BEFORE invoking agent (for duration tracking)
|
|
2853
|
-
agentResponseMessage = await this.createTurnRow(conversationId, branchId);
|
|
2854
|
-
agentResponseMessage.Message = '⏳ Starting...'; // Initial message
|
|
2855
|
-
agentResponseMessage.ParentID = conversationManagerMessage.ID; // Thread under delegation message
|
|
2856
|
-
agentResponseMessage.Status = 'In-Progress';
|
|
2857
|
-
// Set AgentID immediately for proper attribution
|
|
2858
|
-
if (agent?.ID) {
|
|
2859
|
-
agentResponseMessage.AgentID = agent.ID;
|
|
2860
|
-
}
|
|
2861
|
-
// Save the record to establish __mj_CreatedAt timestamp
|
|
2862
|
-
await agentResponseMessage.Save();
|
|
2863
|
-
this.MessageSent.emit(agentResponseMessage);
|
|
2864
|
-
// Add sub-agent to active tasks
|
|
2865
|
-
const newTaskId = this.activeTasks.add({
|
|
2866
|
-
agentName: agentName,
|
|
2867
|
-
status: 'Starting...',
|
|
2868
|
-
relatedMessageId: userMessage.ID,
|
|
2869
|
-
conversationDetailId: agentResponseMessage.ID,
|
|
2870
|
-
conversationId,
|
|
2871
|
-
conversationName: this.ConversationName
|
|
2872
|
-
});
|
|
2873
2782
|
// Load previous payload if agent has been invoked before
|
|
2874
2783
|
const { payload: previousPayload, artifactInfo } = agent?.ID
|
|
2875
2784
|
? await this.loadPreviousPayloadForAgent(agent.ID, branchId)
|
|
@@ -2878,70 +2787,44 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
2878
2787
|
const configurationPresetId = agent?.ID
|
|
2879
2788
|
? await this.agentService.FindConfigurationPresetForAgent(conversationId, agent.ID, branchId)
|
|
2880
2789
|
: undefined;
|
|
2881
|
-
//
|
|
2882
|
-
const
|
|
2883
|
-
|
|
2884
|
-
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
// Update the response message with agent result
|
|
2892
|
-
// Store the agent ID for display
|
|
2893
|
-
if (subResult.agentRun.AgentID) {
|
|
2894
|
-
agentResponseMessage.AgentID = subResult.agentRun.AgentID;
|
|
2895
|
-
}
|
|
2896
|
-
await this.updateConversationDetail(agentResponseMessage, subResult.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete', subResult);
|
|
2897
|
-
// Always emit artifactCreated to trigger UI reload — the server may have created
|
|
2898
|
-
// artifacts even when the result payload is empty (e.g., remote stage server).
|
|
2899
|
-
// onArtifactCreated will reload from DB and discover any artifacts that exist.
|
|
2900
|
-
this.emitArtifactReload(agentResponseMessage);
|
|
2901
|
-
this.MessageSent.emit(agentResponseMessage);
|
|
2902
|
-
// Mark user message as complete
|
|
2903
|
-
await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
|
|
2904
|
-
}
|
|
2905
|
-
else {
|
|
2906
|
-
// A post-ACK disconnect means the first run may still be executing on this
|
|
2907
|
-
// detail — do not start a second run on the same conversationDetailId.
|
|
2908
|
-
if (agentFailureDisposition(subResult).status === 'In-Progress') {
|
|
2909
|
-
await this.applyAgentFailureToDetail(agentResponseMessage, userMessage, agentName, subResult);
|
|
2910
|
-
return;
|
|
2911
|
-
}
|
|
2912
|
-
// Sub-agent failed - attempt auto-retry once
|
|
2913
|
-
console.log(`⚠️ ${agentName} failed, attempting auto-retry...`);
|
|
2914
|
-
await this.updateConversationDetail(conversationManagerMessage, `👉 **${agentName}** will handle this request...\n\n⚠️ First attempt failed, retrying...`, conversationManagerMessage.Status);
|
|
2915
|
-
// Update the existing agentResponseMessage to show retry status
|
|
2916
|
-
await this.updateConversationDetail(agentResponseMessage, "Retrying...", agentResponseMessage.Status);
|
|
2917
|
-
// Retry the sub-agent (reuse previously loaded payload and config from first attempt)
|
|
2918
|
-
const retryResult = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.ConversationHistory, reasoning, agentResponseMessage.ID, previousPayload, // Pass same payload as first attempt
|
|
2919
|
-
this.createProgressCallback(agentResponseMessage, `${agentName} (retry)`), artifactInfo?.artifactId, artifactInfo?.versionId, configurationPresetId, // Pass same config as first attempt
|
|
2790
|
+
// The delegate's reply threads under the delegating reply, as it always has; the server checks and sets it.
|
|
2791
|
+
const threading = {
|
|
2792
|
+
ReplyParentId: delegatingReply.ID,
|
|
2793
|
+
...(branchId ? { ReplyBranchId: branchId } : {}),
|
|
2794
|
+
};
|
|
2795
|
+
const invoke = async (label) => {
|
|
2796
|
+
const reply = await this.createReplyPlaceholder(conversationId, branchId, delegatingReply.ID, agent?.ID ?? null);
|
|
2797
|
+
agentResponseMessage = reply;
|
|
2798
|
+
const result = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.ConversationHistory, reasoning, this.adoptReplyRow(reply, { agentName, status: 'Starting...', relatedMessageId: userMessage.ID, conversationId, conversationName: this.ConversationName }), previousPayload, // Pass previous payload for continuity
|
|
2799
|
+
this.createProgressCallback(reply, label), artifactInfo?.artifactId, artifactInfo?.versionId, configurationPresetId, // Pass configuration from previous @mention for continuity
|
|
2920
2800
|
this.AppContext, // Embedder-supplied app/form context
|
|
2921
2801
|
this.PlanModeEnabled, // per-request Plan Mode toggle
|
|
2922
2802
|
this._pendingRequestedSkillIDs, // user-requested skills (/skill mentions)
|
|
2923
|
-
this.AgentHistoryFrom
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2803
|
+
this.AgentHistoryFrom, // the first moment of the conversation the run may read
|
|
2804
|
+
threading);
|
|
2805
|
+
return { reply, result };
|
|
2806
|
+
};
|
|
2807
|
+
const first = await invoke(agentName);
|
|
2808
|
+
if (first.result && first.result.success) {
|
|
2809
|
+
await this.showDelegateReply(first.reply, userMessage);
|
|
2810
|
+
return;
|
|
2811
|
+
}
|
|
2812
|
+
// A post-ACK disconnect means the first run may still be executing — do not start a second run.
|
|
2813
|
+
if (agentFailureDisposition(first.result).status === 'In-Progress') {
|
|
2814
|
+
await this.applyAgentFailureToDetail(first.reply, userMessage, agentName, first.result);
|
|
2815
|
+
return;
|
|
2816
|
+
}
|
|
2817
|
+
// Sub-agent failed - attempt auto-retry once. The failed reply stays the server's record of
|
|
2818
|
+
// the attempt; it is hidden so the retry's reply takes its place on screen.
|
|
2819
|
+
LogStatusEx({ message: `⚠️ ${agentName} failed, attempting auto-retry...`, verboseOnly: true });
|
|
2820
|
+
await this.settleReply(first.reply, `❌ **${agentName}** failed`);
|
|
2821
|
+
await this.hideReply(first.reply);
|
|
2822
|
+
const retry = await invoke(`${agentName} (retry)`);
|
|
2823
|
+
if (retry.result && retry.result.success) {
|
|
2824
|
+
await this.showDelegateReply(retry.reply, userMessage);
|
|
2825
|
+
}
|
|
2826
|
+
else {
|
|
2827
|
+
await this.applyAgentFailureToDetail(retry.reply, userMessage, agentName, retry.result, 'failed after retry');
|
|
2945
2828
|
}
|
|
2946
2829
|
}
|
|
2947
2830
|
catch (error) {
|
|
@@ -2956,13 +2839,19 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
2956
2839
|
else {
|
|
2957
2840
|
await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
|
|
2958
2841
|
}
|
|
2959
|
-
const catchDisposition = agentFailureDisposition(catchResult);
|
|
2960
|
-
if (catchDisposition.status === 'Error') {
|
|
2961
|
-
conversationManagerMessage.Error = catchDisposition.message;
|
|
2962
|
-
await this.updateConversationDetail(conversationManagerMessage, `❌ **${agentName}** encountered an error\n\n${catchDisposition.message}`, 'Error');
|
|
2963
|
-
}
|
|
2964
2842
|
}
|
|
2965
2843
|
}
|
|
2844
|
+
/**
|
|
2845
|
+
* Shows a delegate's successful reply as the server wrote it, reloads its artifacts (the server
|
|
2846
|
+
* may have created some even when the result payload is empty, e.g. a remote stage server), and
|
|
2847
|
+
* completes the user's message.
|
|
2848
|
+
*/
|
|
2849
|
+
async showDelegateReply(reply, userMessage) {
|
|
2850
|
+
await this.settleReply(reply, '❌ The agent could not start.');
|
|
2851
|
+
this.emitArtifactReload(reply);
|
|
2852
|
+
this.MessageSent.emit(reply);
|
|
2853
|
+
await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
|
|
2854
|
+
}
|
|
2966
2855
|
/**
|
|
2967
2856
|
* Handle silent observation - when Sage stays silent,
|
|
2968
2857
|
* check if we should continue with the last agent for iterative refinement
|
|
@@ -2974,12 +2863,12 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
2974
2863
|
const lastAIMessage = this.findLastNonSageReply();
|
|
2975
2864
|
if (!lastAIMessage || !lastAIMessage.AgentID) {
|
|
2976
2865
|
// No previous specialist agent - just mark user message as complete
|
|
2977
|
-
|
|
2866
|
+
LogStatusEx({ message: '🔇 No previous specialist agent found - marking complete', verboseOnly: true });
|
|
2978
2867
|
await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
|
|
2979
2868
|
return;
|
|
2980
2869
|
}
|
|
2981
2870
|
if (!IsAgentAllowed(lastAIMessage.AgentID, this.AllowedAgentIDs)) {
|
|
2982
|
-
|
|
2871
|
+
LogStatusEx({ message: '🔇 Previous specialist agent is not allowed in this chat - marking complete', verboseOnly: true });
|
|
2983
2872
|
await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
|
|
2984
2873
|
return;
|
|
2985
2874
|
}
|
|
@@ -3004,60 +2893,39 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
3004
2893
|
versionId: source.versionId,
|
|
3005
2894
|
versionNumber: source.versionNumber
|
|
3006
2895
|
};
|
|
3007
|
-
|
|
3008
|
-
}
|
|
3009
|
-
//
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
await
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
conversationDetailId: statusMessage.ID,
|
|
3023
|
-
conversationId,
|
|
3024
|
-
conversationName: this.ConversationName
|
|
3025
|
-
});
|
|
2896
|
+
LogStatusEx({ message: '📦 Loaded previous OUTPUT artifact as payload for continuity', verboseOnly: true, additionalArgs: [previousArtifactInfo] });
|
|
2897
|
+
}
|
|
2898
|
+
// The manager's line "Continuing with X for refinement..." and the continuing agent's reply, threaded under it,
|
|
2899
|
+
// both written by the server. Until it acknowledges the run they show as local placeholders.
|
|
2900
|
+
const managerId = this.ConverationManagerAgent?.ID ?? null;
|
|
2901
|
+
const announcement = managerId
|
|
2902
|
+
? await this.createReplyPlaceholder(conversationId, branchId, userMessage.ID, managerId, { Message: ContinuationAnnouncementText(agentName), Status: 'Complete' })
|
|
2903
|
+
: undefined;
|
|
2904
|
+
const reply = await this.createReplyPlaceholder(conversationId, branchId, announcement?.ID ?? userMessage.ID, lastAIMessage.AgentID);
|
|
2905
|
+
const threading = managerId || branchId
|
|
2906
|
+
? {
|
|
2907
|
+
...(managerId ? { AnnouncedByAgentId: managerId } : {}),
|
|
2908
|
+
...(branchId ? { ReplyBranchId: branchId } : {}),
|
|
2909
|
+
}
|
|
2910
|
+
: null;
|
|
3026
2911
|
try {
|
|
3027
2912
|
// Invoke the agent with the previous payload
|
|
3028
|
-
const continuityResult = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.ConversationHistory, 'Continuing previous work based on user feedback',
|
|
2913
|
+
const continuityResult = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.ConversationHistory, 'Continuing previous work based on user feedback', this.adoptReplyRow(reply, { agentName, status: 'Processing refinement...', relatedMessageId: userMessage.ID, conversationId, conversationName: this.ConversationName }, announcement), previousPayload, this.createProgressCallback(reply, agentName), previousArtifactInfo?.artifactId, previousArtifactInfo?.versionId, undefined, // configurationPresetId not used in this path
|
|
3029
2914
|
this.AppContext, // Embedder-supplied app/form context
|
|
3030
2915
|
this.PlanModeEnabled, // per-request Plan Mode toggle
|
|
3031
2916
|
this._pendingRequestedSkillIDs, // user-requested skills (/skill mentions)
|
|
3032
|
-
this.AgentHistoryFrom
|
|
3033
|
-
|
|
3034
|
-
// Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
|
|
2917
|
+
this.AgentHistoryFrom, // the first moment of the conversation the run may read
|
|
2918
|
+
threading);
|
|
3035
2919
|
if (continuityResult && continuityResult.success) {
|
|
3036
|
-
|
|
3037
|
-
const agentResponseMessage = await this.createTurnRow(conversationId, branchId);
|
|
3038
|
-
agentResponseMessage.Message = continuityResult.agentRun?.Message || `✅ **${agentName}** completed refinement`;
|
|
3039
|
-
agentResponseMessage.ParentID = statusMessage.ID;
|
|
3040
|
-
agentResponseMessage.Status = 'Complete';
|
|
3041
|
-
agentResponseMessage.AgentID = lastAIMessage.AgentID;
|
|
3042
|
-
await agentResponseMessage.Save();
|
|
3043
|
-
this.MessageSent.emit(agentResponseMessage);
|
|
3044
|
-
// Server created artifacts (handles versioning automatically) - emit event to trigger UI reload
|
|
3045
|
-
if (continuityResult.payload && Object.keys(continuityResult.payload).length > 0) {
|
|
3046
|
-
this.emitArtifactReload(agentResponseMessage);
|
|
3047
|
-
console.log('🎨 Server created artifact (versioned) from agent continuity');
|
|
3048
|
-
this.MessageSent.emit(agentResponseMessage);
|
|
3049
|
-
}
|
|
3050
|
-
// Mark user message as complete
|
|
3051
|
-
await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
|
|
2920
|
+
await this.showDelegateReply(reply, userMessage);
|
|
3052
2921
|
}
|
|
3053
2922
|
else {
|
|
3054
|
-
await this.applyAgentFailureToDetail(
|
|
2923
|
+
await this.applyAgentFailureToDetail(reply, userMessage, agentName, continuityResult, 'failed during refinement');
|
|
3055
2924
|
}
|
|
3056
2925
|
}
|
|
3057
2926
|
catch (error) {
|
|
3058
2927
|
console.error(`❌ Error in agent continuity with ${agentName}:`, error);
|
|
3059
|
-
|
|
3060
|
-
await this.applyAgentFailureToDetail(statusMessage, userMessage, agentName, { success: false, errorMessage: error instanceof Error ? error.message : String(error) }, 'encountered an error');
|
|
2928
|
+
await this.applyAgentFailureToDetail(reply, userMessage, agentName, { success: false, errorMessage: error instanceof Error ? error.message : String(error) }, 'encountered an error');
|
|
3061
2929
|
}
|
|
3062
2930
|
}
|
|
3063
2931
|
/**
|
|
@@ -3068,15 +2936,6 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
3068
2936
|
*/
|
|
3069
2937
|
async invokeAgentDirectly(userMessage, agentMention, conversationId, branchId) {
|
|
3070
2938
|
const agentName = agentMention.name;
|
|
3071
|
-
// Add agent to active tasks
|
|
3072
|
-
const taskId = this.activeTasks.add({
|
|
3073
|
-
agentName: agentName,
|
|
3074
|
-
status: 'Processing...',
|
|
3075
|
-
relatedMessageId: userMessage.ID,
|
|
3076
|
-
conversationDetailId: userMessage.ID,
|
|
3077
|
-
conversationId,
|
|
3078
|
-
conversationName: this.ConversationName
|
|
3079
|
-
});
|
|
3080
2939
|
// Declare agentResponseMessage outside try block so it's accessible in catch
|
|
3081
2940
|
let agentResponseMessage = undefined;
|
|
3082
2941
|
try {
|
|
@@ -3087,50 +2946,36 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
3087
2946
|
this.MessageSent.emit(userMessage);
|
|
3088
2947
|
// Look up the agent to get its ID
|
|
3089
2948
|
const agent = AIEngineBase.Instance.ReadableAgents.find(a => a.Name === agentName);
|
|
3090
|
-
//
|
|
3091
|
-
agentResponseMessage = await this.
|
|
3092
|
-
agentResponseMessage.Message = '⏳ Starting...'; // Initial message
|
|
3093
|
-
agentResponseMessage.ParentID = userMessage.ID;
|
|
3094
|
-
agentResponseMessage.Status = 'In-Progress';
|
|
3095
|
-
// Set AgentID immediately for proper attribution
|
|
3096
|
-
if (agent?.ID) {
|
|
3097
|
-
agentResponseMessage.AgentID = agent.ID;
|
|
3098
|
-
}
|
|
3099
|
-
// Save the record to establish __mj_CreatedAt timestamp
|
|
3100
|
-
await agentResponseMessage.Save();
|
|
3101
|
-
this.MessageSent.emit(agentResponseMessage);
|
|
2949
|
+
// The reply shows as a local placeholder until the server creates the row and names it.
|
|
2950
|
+
agentResponseMessage = await this.createReplyPlaceholder(conversationId, branchId, userMessage.ID, agent?.ID ?? null);
|
|
3102
2951
|
// Load previous payload if agent has been invoked before
|
|
3103
2952
|
const { payload: previousPayload, artifactInfo } = agent?.ID
|
|
3104
2953
|
? await this.loadPreviousPayloadForAgent(agent.ID, branchId)
|
|
3105
2954
|
: { payload: null, artifactInfo: null };
|
|
3106
2955
|
// Invoke the agent directly
|
|
3107
|
-
const result = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.ConversationHistory, `User mentioned agent directly with @${agentName}`, agentResponseMessage.ID, previousPayload, // Pass previous payload for continuity
|
|
2956
|
+
const result = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.ConversationHistory, `User mentioned agent directly with @${agentName}`, this.adoptReplyRow(agentResponseMessage, { agentName, status: 'Processing...', relatedMessageId: userMessage.ID, conversationId, conversationName: this.ConversationName }), previousPayload, // Pass previous payload for continuity
|
|
3108
2957
|
this.createProgressCallback(agentResponseMessage, agentName), artifactInfo?.artifactId, artifactInfo?.versionId, agentMention.configurationId, // Pass configuration preset ID
|
|
3109
2958
|
this.AppContext, // Embedder-supplied app/form context
|
|
3110
2959
|
this.PlanModeEnabled, // per-request Plan Mode toggle
|
|
3111
2960
|
this._pendingRequestedSkillIDs, // user-requested skills (/skill mentions)
|
|
3112
|
-
this.AgentHistoryFrom
|
|
3113
|
-
|
|
3114
|
-
// Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
|
|
2961
|
+
this.AgentHistoryFrom, // the first moment of the conversation the run may read
|
|
2962
|
+
branchId ? { ReplyBranchId: branchId } : null);
|
|
3115
2963
|
if (result && result.success) {
|
|
3116
|
-
if (result.agentRun.AgentID) {
|
|
3117
|
-
agentResponseMessage.AgentID = result.agentRun.AgentID;
|
|
3118
|
-
}
|
|
3119
2964
|
this.emitAfterAgentTurn(conversationId, result);
|
|
2965
|
+
await this.settleReply(agentResponseMessage, `❌ **${agentName}** could not start.`);
|
|
3120
2966
|
// Multi-stage response handling (same logic as ambient Sage)
|
|
3121
2967
|
// Stage 1: Check for task graph (multi-step orchestration)
|
|
3122
2968
|
if (result.payload?.taskGraph) {
|
|
3123
|
-
|
|
2969
|
+
LogStatusEx({ message: '📋 Task graph detected from @mention, starting task orchestration', verboseOnly: true });
|
|
3124
2970
|
await this.handleTaskGraphExecution(userMessage, result, conversationId, branchId, agentResponseMessage);
|
|
3125
2971
|
}
|
|
3126
2972
|
// Stage 2: Check for sub-agent invocation (single-step delegation)
|
|
3127
2973
|
else if (result.agentRun.FinalStep === 'Success' && result.payload?.invokeAgent) {
|
|
3128
|
-
|
|
2974
|
+
LogStatusEx({ message: '🎯 Sub-agent invocation detected from @mention', verboseOnly: true });
|
|
3129
2975
|
await this.handleSubAgentInvocation(userMessage, result, conversationId, branchId, agentResponseMessage);
|
|
3130
2976
|
}
|
|
3131
2977
|
// Stage 3: Normal chat response
|
|
3132
2978
|
else {
|
|
3133
|
-
await this.updateConversationDetail(agentResponseMessage, result.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete', result);
|
|
3134
2979
|
// Server created artifacts - emit event to trigger UI reload
|
|
3135
2980
|
if (result.payload && Object.keys(result.payload).length > 0) {
|
|
3136
2981
|
this.emitArtifactReload(agentResponseMessage);
|
|
@@ -3191,7 +3036,7 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
3191
3036
|
versionId: target.versionId,
|
|
3192
3037
|
versionNumber: target.versionNumber
|
|
3193
3038
|
};
|
|
3194
|
-
|
|
3039
|
+
LogStatusEx({ message: '📦 Loaded target artifact version as payload', verboseOnly: true, additionalArgs: [previousArtifactInfo] });
|
|
3195
3040
|
}
|
|
3196
3041
|
else {
|
|
3197
3042
|
console.warn('⚠️ Could not load target artifact version:', targetArtifactVersionId);
|
|
@@ -3224,10 +3069,10 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
3224
3069
|
versionId: source.versionId,
|
|
3225
3070
|
versionNumber: source.versionNumber
|
|
3226
3071
|
};
|
|
3227
|
-
|
|
3072
|
+
LogStatusEx({ message: '📦 Loaded artifact as payload for continuation', verboseOnly: true, additionalArgs: [previousArtifactInfo] });
|
|
3228
3073
|
}
|
|
3229
3074
|
else {
|
|
3230
|
-
|
|
3075
|
+
LogStatusEx({ message: `📦 No artifact found for agent ${agentId} in this conversation`, verboseOnly: true });
|
|
3231
3076
|
}
|
|
3232
3077
|
}
|
|
3233
3078
|
// Execute the agent with the gathered context
|
|
@@ -3247,15 +3092,6 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
3247
3092
|
* @param configurationId Optional configuration preset ID to use
|
|
3248
3093
|
*/
|
|
3249
3094
|
async executeAgentContinuation(userMessage, agentId, agentName, conversationId, branchId, previousPayload, previousArtifactInfo, configurationId) {
|
|
3250
|
-
// Add agent to active tasks
|
|
3251
|
-
const taskId = this.activeTasks.add({
|
|
3252
|
-
agentName: agentName,
|
|
3253
|
-
status: 'Processing...',
|
|
3254
|
-
relatedMessageId: userMessage.ID,
|
|
3255
|
-
conversationDetailId: userMessage.ID,
|
|
3256
|
-
conversationId,
|
|
3257
|
-
conversationName: this.ConversationName
|
|
3258
|
-
});
|
|
3259
3095
|
// Declare agentResponseMessage outside try block so it's accessible in catch
|
|
3260
3096
|
let agentResponseMessage = undefined;
|
|
3261
3097
|
try {
|
|
@@ -3264,32 +3100,23 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
3264
3100
|
userMessage.Status = 'Complete';
|
|
3265
3101
|
await userMessage.Save();
|
|
3266
3102
|
this.MessageSent.emit(userMessage);
|
|
3267
|
-
//
|
|
3268
|
-
agentResponseMessage = await this.
|
|
3269
|
-
agentResponseMessage.Message = '⏳ Starting...'; // Initial message
|
|
3270
|
-
agentResponseMessage.ParentID = userMessage.ID;
|
|
3271
|
-
agentResponseMessage.Status = 'In-Progress';
|
|
3272
|
-
agentResponseMessage.AgentID = agentId;
|
|
3273
|
-
// Save the record to establish __mj_CreatedAt timestamp
|
|
3274
|
-
await agentResponseMessage.Save();
|
|
3275
|
-
this.MessageSent.emit(agentResponseMessage);
|
|
3103
|
+
// The reply shows as a local placeholder until the server creates the row and names it.
|
|
3104
|
+
agentResponseMessage = await this.createReplyPlaceholder(conversationId, branchId, userMessage.ID, agentId);
|
|
3276
3105
|
// Invoke the agent directly (continuation) with previous payload if available.
|
|
3277
3106
|
// `this.appContext` is forwarded so direct-routed sub-agents (e.g. Form
|
|
3278
3107
|
// Builder via [defaultAgentId]) see the embedder's ActiveForm/Schema/
|
|
3279
3108
|
// OverrideID block in their prompt — same flow Sage gets via
|
|
3280
3109
|
// `processMessage`.
|
|
3281
|
-
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
|
|
3110
|
+
const result = await this.agentService.invokeSubAgent(agentName, conversationId, userMessage, this.ConversationHistory, 'Continuing previous conversation with user', this.adoptReplyRow(agentResponseMessage, { agentName, status: 'Processing...', relatedMessageId: userMessage.ID, conversationId, conversationName: this.ConversationName }), previousPayload, // Pass previous OUTPUT artifact payload for continuity
|
|
3282
3111
|
this.createProgressCallback(agentResponseMessage, agentName), previousArtifactInfo?.artifactId, previousArtifactInfo?.versionId, configurationId, // Pass configuration for continuity
|
|
3283
3112
|
this.AppContext, // Embedder-supplied app/form context
|
|
3284
3113
|
this.PlanModeEnabled, // per-request Plan Mode toggle
|
|
3285
3114
|
this._pendingRequestedSkillIDs, // user-requested skills (/skill mentions)
|
|
3286
|
-
this.AgentHistoryFrom
|
|
3287
|
-
|
|
3288
|
-
// Task removed in markMessageComplete() - this.activeTasks.remove(taskId);
|
|
3115
|
+
this.AgentHistoryFrom, // the first moment of the conversation the run may read
|
|
3116
|
+
branchId ? { ReplyBranchId: branchId } : null);
|
|
3289
3117
|
if (result && result.success) {
|
|
3290
3118
|
this.emitAfterAgentTurn(conversationId, result);
|
|
3291
|
-
|
|
3292
|
-
await this.updateConversationDetail(agentResponseMessage, result.agentRun?.Message || `✅ **${agentName}** completed`, 'Complete', result);
|
|
3119
|
+
await this.settleReply(agentResponseMessage, `❌ **${agentName}** could not start.`);
|
|
3293
3120
|
// Server created artifacts (handles versioning) - emit event to trigger UI reload
|
|
3294
3121
|
if (result.payload && Object.keys(result.payload).length > 0) {
|
|
3295
3122
|
this.emitArtifactReload(agentResponseMessage);
|
|
@@ -3336,39 +3163,113 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
3336
3163
|
name: result.Name,
|
|
3337
3164
|
description: result.Description
|
|
3338
3165
|
});
|
|
3339
|
-
|
|
3166
|
+
LogStatusEx({ message: `✅ Conversation renamed to: "${result.Name}"`, verboseOnly: true });
|
|
3340
3167
|
}
|
|
3341
3168
|
}
|
|
3342
3169
|
/**
|
|
3343
|
-
*
|
|
3170
|
+
* An unsaved, local "Starting…" reply, shown from the moment the turn starts until the server
|
|
3171
|
+
* acknowledges the run and names the reply row it created. The chat never writes an agent's
|
|
3172
|
+
* reply: only the server does (A19). The placeholder is never saved; {@link adoptReplyRow}
|
|
3173
|
+
* turns it into the server's row.
|
|
3174
|
+
*/
|
|
3175
|
+
async createReplyPlaceholder(conversationId, branchId, parentId, agentId, line = { Message: '⏳ Starting...', Status: 'In-Progress' }) {
|
|
3176
|
+
const placeholder = await this.dataCache.createConversationDetail(this.CurrentUser);
|
|
3177
|
+
placeholder.ConversationID = conversationId;
|
|
3178
|
+
placeholder.BranchID = branchId; // the server writes the reply on its message's branch; the placeholder shows there too
|
|
3179
|
+
placeholder.Role = 'AI';
|
|
3180
|
+
placeholder.Message = line.Message;
|
|
3181
|
+
placeholder.ParentID = parentId;
|
|
3182
|
+
placeholder.Status = line.Status;
|
|
3183
|
+
placeholder.HiddenToUser = false;
|
|
3184
|
+
placeholder.AgentID = agentId;
|
|
3185
|
+
this.MessageSent.emit(placeholder);
|
|
3186
|
+
return placeholder;
|
|
3187
|
+
}
|
|
3188
|
+
/**
|
|
3189
|
+
* The acknowledgement callback for a placeholder's run. When the server accepts the run it names
|
|
3190
|
+
* the reply row it created: the placeholder takes that row's ID and creation time (so the bubble's
|
|
3191
|
+
* timer starts when the server's row did), and the turn's active task is keyed by that ID, which
|
|
3192
|
+
* is what its progress and completion carry. Runs synchronously, before any progress is routed.
|
|
3193
|
+
*/
|
|
3194
|
+
adoptReplyRow(placeholder, task, announcement) {
|
|
3195
|
+
return (reply) => {
|
|
3196
|
+
if (announcement && reply.AnnouncementDetailId) {
|
|
3197
|
+
this.adoptRow(announcement, reply.AnnouncementDetailId, reply.CreatedAt);
|
|
3198
|
+
}
|
|
3199
|
+
this.adoptRow(placeholder, reply.ConversationDetailId, reply.CreatedAt);
|
|
3200
|
+
this.activeTasks.add({ ...task, conversationDetailId: reply.ConversationDetailId });
|
|
3201
|
+
};
|
|
3202
|
+
}
|
|
3203
|
+
/** Makes a local placeholder the server's row: its ID and creation time. Synchronous, so nothing routes in between. */
|
|
3204
|
+
adoptRow(placeholder, id, createdAt) {
|
|
3205
|
+
void placeholder.LoadFromData({ ...placeholder.GetAll(), ID: id, __mj_CreatedAt: createdAt }, true);
|
|
3206
|
+
this.MessageSent.emit(placeholder);
|
|
3207
|
+
}
|
|
3208
|
+
/**
|
|
3209
|
+
* Shows a reply as the server left it once its run has returned. The server wrote the reply's
|
|
3210
|
+
* text and status (the chat no longer saves it), so the row is reloaded. A row still In-Progress
|
|
3211
|
+
* is watched until the server settles it. A placeholder the server never acknowledged has no row:
|
|
3212
|
+
* it is marked failed on screen only, with `failureText`, and never saved.
|
|
3213
|
+
*/
|
|
3214
|
+
async settleReply(reply, failureText) {
|
|
3215
|
+
if (!reply.IsSaved) {
|
|
3216
|
+
this.markMessageComplete(reply);
|
|
3217
|
+
reply.Status = 'Error';
|
|
3218
|
+
reply.Message = failureText;
|
|
3219
|
+
this.MessageSent.emit(reply);
|
|
3220
|
+
this.cleanupCompletionTimestamp(reply.ID);
|
|
3221
|
+
return;
|
|
3222
|
+
}
|
|
3223
|
+
await reply.Load(reply.ID);
|
|
3224
|
+
if (reply.Status === 'In-Progress') {
|
|
3225
|
+
this.MessageSent.emit(reply);
|
|
3226
|
+
this.startInFlightDetailWatch(reply);
|
|
3227
|
+
return;
|
|
3228
|
+
}
|
|
3229
|
+
this.markMessageComplete(reply);
|
|
3230
|
+
this.MessageSent.emit(reply);
|
|
3231
|
+
this.cleanupCompletionTimestamp(reply.ID);
|
|
3232
|
+
}
|
|
3233
|
+
/**
|
|
3234
|
+
* Hides a reply from the transcript (bookkeeping a person may still write; what the reply says
|
|
3235
|
+
* stays the server's). Used for a manager's silent turn and for a delegate's failed first attempt
|
|
3236
|
+
* once its retry takes over.
|
|
3237
|
+
*/
|
|
3238
|
+
async hideReply(reply) {
|
|
3239
|
+
reply.HiddenToUser = true;
|
|
3240
|
+
if (reply.IsSaved && !(await reply.Save())) {
|
|
3241
|
+
console.warn(`Could not hide reply ${reply.ID}:`, reply.LatestResult?.CompleteMessage);
|
|
3242
|
+
}
|
|
3243
|
+
this.MessageSent.emit(reply);
|
|
3244
|
+
}
|
|
3245
|
+
/**
|
|
3246
|
+
* Shows an agent failure on the reply bubble.
|
|
3344
3247
|
*
|
|
3345
|
-
*
|
|
3346
|
-
* the
|
|
3347
|
-
*
|
|
3348
|
-
*
|
|
3349
|
-
*
|
|
3350
|
-
* renders it. The GraphQLAIClient stall reconciler covers the wait inside
|
|
3351
|
-
* invokeSubAgent; once that returns, the watch observes the detail until the
|
|
3352
|
-
* server writes a terminal status (MaxTimePerRun).
|
|
3248
|
+
* The server writes the reply, failures included (the chat never saves it), so this reloads the
|
|
3249
|
+
* row the server wrote. A dropped HTTP/WebSocket path after the server ACKed the run may leave the
|
|
3250
|
+
* run still executing: the bubble then says so, on screen only, and {@link startInFlightDetailWatch}
|
|
3251
|
+
* follows the row until the server writes a terminal status. A placeholder the server never
|
|
3252
|
+
* acknowledged shows the failure on screen only.
|
|
3353
3253
|
*
|
|
3354
|
-
* Always completes the user message — the user turn finished regardless of
|
|
3355
|
-
* what the agent is doing.
|
|
3254
|
+
* Always completes the user message — the user turn finished regardless of what the agent is doing.
|
|
3356
3255
|
*/
|
|
3357
3256
|
async applyAgentFailureToDetail(agentResponseMessage, userMessage, agentName, result, failedVerb = 'failed') {
|
|
3358
3257
|
const disposition = agentFailureDisposition(result);
|
|
3359
|
-
if (disposition.status === 'In-Progress') {
|
|
3360
|
-
await
|
|
3361
|
-
// Skip the watch if the reload-before-write guard already found a
|
|
3362
|
-
// terminal server status (Complete/Error) — starting it would race the
|
|
3363
|
-
// just-completed bubble.
|
|
3258
|
+
if (disposition.status === 'In-Progress' && agentResponseMessage.IsSaved) {
|
|
3259
|
+
await agentResponseMessage.Load(agentResponseMessage.ID);
|
|
3364
3260
|
if (agentResponseMessage.Status === 'In-Progress') {
|
|
3261
|
+
agentResponseMessage.Message = `⏳ **${agentName}** is still running on the server.\n\n${disposition.message}`;
|
|
3262
|
+
this.MessageSent.emit(agentResponseMessage);
|
|
3365
3263
|
this.startInFlightDetailWatch(agentResponseMessage);
|
|
3366
3264
|
}
|
|
3367
|
-
|
|
3368
|
-
|
|
3265
|
+
else {
|
|
3266
|
+
this.markMessageComplete(agentResponseMessage);
|
|
3267
|
+
this.MessageSent.emit(agentResponseMessage);
|
|
3268
|
+
}
|
|
3269
|
+
}
|
|
3270
|
+
else {
|
|
3271
|
+
await this.settleReply(agentResponseMessage, `❌ **${agentName}** ${failedVerb}\n\n${disposition.message}`);
|
|
3369
3272
|
}
|
|
3370
|
-
agentResponseMessage.Error = disposition.message;
|
|
3371
|
-
await this.updateConversationDetail(agentResponseMessage, `❌ **${agentName}** ${failedVerb}\n\n${disposition.message}`, 'Error');
|
|
3372
3273
|
await this.updateConversationDetail(userMessage, userMessage.Message, 'Complete');
|
|
3373
3274
|
}
|
|
3374
3275
|
startInFlightDetailWatch(detail) {
|
|
@@ -3773,5 +3674,5 @@ export class MessageInputComponent extends BaseAngularComponent {
|
|
|
3773
3674
|
}], ComposerBlurred: [{
|
|
3774
3675
|
type: Output
|
|
3775
3676
|
}] }); })();
|
|
3776
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MessageInputComponent, { className: "MessageInputComponent", filePath: "src/lib/components/message/message-input.component.ts", lineNumber:
|
|
3677
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MessageInputComponent, { className: "MessageInputComponent", filePath: "src/lib/components/message/message-input.component.ts", lineNumber: 78 }); })();
|
|
3777
3678
|
//# sourceMappingURL=message-input.component.js.map
|