@memberjunction/ng-conversations 2.106.0 → 2.108.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/artifact-collection-picker-modal.component.d.ts +67 -0
- package/dist/lib/components/collection/artifact-collection-picker-modal.component.d.ts.map +1 -0
- package/dist/lib/components/collection/artifact-collection-picker-modal.component.js +725 -0
- package/dist/lib/components/collection/artifact-collection-picker-modal.component.js.map +1 -0
- package/dist/lib/components/collection/artifact-create-modal.component.d.ts +39 -0
- package/dist/lib/components/collection/artifact-create-modal.component.d.ts.map +1 -0
- package/dist/lib/components/collection/artifact-create-modal.component.js +351 -0
- package/dist/lib/components/collection/artifact-create-modal.component.js.map +1 -0
- package/dist/lib/components/collection/collection-form-modal.component.d.ts +3 -1
- package/dist/lib/components/collection/collection-form-modal.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-form-modal.component.js +60 -10
- package/dist/lib/components/collection/collection-form-modal.component.js.map +1 -1
- package/dist/lib/components/collection/collection-share-modal.component.d.ts +43 -0
- package/dist/lib/components/collection/collection-share-modal.component.d.ts.map +1 -0
- package/dist/lib/components/collection/collection-share-modal.component.js +728 -0
- package/dist/lib/components/collection/collection-share-modal.component.js.map +1 -0
- package/dist/lib/components/collection/collection-tree.component.d.ts +8 -1
- package/dist/lib/components/collection/collection-tree.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-tree.component.js +217 -115
- package/dist/lib/components/collection/collection-tree.component.js.map +1 -1
- package/dist/lib/components/collection/collection-view.component.d.ts +2 -1
- package/dist/lib/components/collection/collection-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collection-view.component.js +52 -34
- package/dist/lib/components/collection/collection-view.component.js.map +1 -1
- package/dist/lib/components/collection/collections-full-view.component.d.ts +45 -9
- package/dist/lib/components/collection/collections-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/collection/collections-full-view.component.js +586 -220
- package/dist/lib/components/collection/collections-full-view.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +53 -20
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +365 -250
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +71 -1
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +303 -104
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +39 -5
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +259 -137
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/message/message-list.component.d.ts +8 -6
- package/dist/lib/components/message/message-list.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-list.component.js +52 -9
- package/dist/lib/components/message/message-list.component.js.map +1 -1
- package/dist/lib/components/message/suggested-responses.component.d.ts +55 -0
- package/dist/lib/components/message/suggested-responses.component.d.ts.map +1 -0
- package/dist/lib/components/message/suggested-responses.component.js +207 -0
- package/dist/lib/components/message/suggested-responses.component.js.map +1 -0
- package/dist/lib/components/search/search-panel.component.d.ts.map +1 -1
- package/dist/lib/components/search/search-panel.component.js +245 -113
- package/dist/lib/components/search/search-panel.component.js.map +1 -1
- package/dist/lib/components/shared/user-picker.component.d.ts +29 -0
- package/dist/lib/components/shared/user-picker.component.d.ts.map +1 -0
- package/dist/lib/components/shared/user-picker.component.js +229 -0
- package/dist/lib/components/shared/user-picker.component.js.map +1 -0
- package/dist/lib/components/tasks/tasks-dropdown.component.d.ts +7 -1
- package/dist/lib/components/tasks/tasks-dropdown.component.d.ts.map +1 -1
- package/dist/lib/components/tasks/tasks-dropdown.component.js +36 -6
- package/dist/lib/components/tasks/tasks-dropdown.component.js.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts +19 -2
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.js +167 -58
- package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
- package/dist/lib/conversations.module.d.ts +52 -47
- package/dist/lib/conversations.module.d.ts.map +1 -1
- package/dist/lib/conversations.module.js +27 -4
- package/dist/lib/conversations.module.js.map +1 -1
- package/dist/lib/models/conversation-complete-query.model.d.ts +75 -0
- package/dist/lib/models/conversation-complete-query.model.d.ts.map +1 -0
- package/dist/lib/models/conversation-complete-query.model.js +19 -0
- package/dist/lib/models/conversation-complete-query.model.js.map +1 -0
- package/dist/lib/models/conversation-state.model.d.ts +27 -0
- package/dist/lib/models/conversation-state.model.d.ts.map +1 -1
- package/dist/lib/models/lazy-artifact-info.d.ts +68 -0
- package/dist/lib/models/lazy-artifact-info.d.ts.map +1 -0
- package/dist/lib/models/lazy-artifact-info.js +150 -0
- package/dist/lib/models/lazy-artifact-info.js.map +1 -0
- package/dist/lib/services/agent-state.service.d.ts.map +1 -1
- package/dist/lib/services/agent-state.service.js +5 -0
- package/dist/lib/services/agent-state.service.js.map +1 -1
- package/dist/lib/services/artifact-state.service.d.ts.map +1 -1
- package/dist/lib/services/artifact-state.service.js +14 -9
- package/dist/lib/services/artifact-state.service.js.map +1 -1
- package/dist/lib/services/collection-permission.service.d.ts +96 -0
- package/dist/lib/services/collection-permission.service.d.ts.map +1 -0
- package/dist/lib/services/collection-permission.service.js +303 -0
- package/dist/lib/services/collection-permission.service.js.map +1 -0
- package/dist/lib/services/collection-state.service.d.ts +34 -0
- package/dist/lib/services/collection-state.service.d.ts.map +1 -0
- package/dist/lib/services/collection-state.service.js +50 -0
- package/dist/lib/services/collection-state.service.js.map +1 -0
- package/dist/lib/services/conversation-agent.service.d.ts +20 -4
- package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.js +179 -17
- package/dist/lib/services/conversation-agent.service.js.map +1 -1
- package/dist/lib/services/data-cache.service.d.ts.map +1 -1
- package/dist/lib/services/data-cache.service.js +5 -0
- package/dist/lib/services/data-cache.service.js.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.js +1 -1
- package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
- package/dist/lib/services/mention-parser.service.d.ts.map +1 -1
- package/dist/lib/services/mention-parser.service.js +0 -5
- package/dist/lib/services/mention-parser.service.js.map +1 -1
- package/dist/lib/services/search.service.d.ts +26 -3
- package/dist/lib/services/search.service.d.ts.map +1 -1
- package/dist/lib/services/search.service.js +172 -12
- package/dist/lib/services/search.service.js.map +1 -1
- package/dist/public-api.d.ts +4 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +4 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +12 -12
|
@@ -19,8 +19,12 @@ function MessageInputComponent_div_6_Template(rf, ctx) { if (rf & 1) {
|
|
|
19
19
|
i0.ɵɵelementStart(0, "div", 10);
|
|
20
20
|
i0.ɵɵelement(1, "i", 11);
|
|
21
21
|
i0.ɵɵelementStart(2, "span");
|
|
22
|
-
i0.ɵɵtext(3
|
|
22
|
+
i0.ɵɵtext(3);
|
|
23
23
|
i0.ɵɵelementEnd()();
|
|
24
|
+
} if (rf & 2) {
|
|
25
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
26
|
+
i0.ɵɵadvance(3);
|
|
27
|
+
i0.ɵɵtextInterpolate(ctx_r1.processingMessage);
|
|
24
28
|
} }
|
|
25
29
|
export class MessageInputComponent {
|
|
26
30
|
dialogService;
|
|
@@ -48,6 +52,7 @@ export class MessageInputComponent {
|
|
|
48
52
|
messageText = '';
|
|
49
53
|
isSending = false;
|
|
50
54
|
isProcessing = false; // True when waiting for agent/naming response
|
|
55
|
+
processingMessage = 'AI is responding...'; // Message shown during processing
|
|
51
56
|
converationManagerAgent = null;
|
|
52
57
|
// Mention autocomplete state
|
|
53
58
|
showMentionDropdown = false;
|
|
@@ -60,6 +65,8 @@ export class MessageInputComponent {
|
|
|
60
65
|
pushStatusSubscription;
|
|
61
66
|
// Track active task execution message IDs for real-time updates
|
|
62
67
|
activeTaskExecutionMessageIds = new Set();
|
|
68
|
+
// Track completion timestamps to prevent race conditions with late progress updates
|
|
69
|
+
completionTimestamps = new Map();
|
|
63
70
|
constructor(dialogService, toastService, agentService, conversationState, dataCache, activeTasks, mentionAutocomplete, mentionParser) {
|
|
64
71
|
this.dialogService = dialogService;
|
|
65
72
|
this.toastService = toastService;
|
|
@@ -297,95 +304,210 @@ export class MessageInputComponent {
|
|
|
297
304
|
if (!this.canSend)
|
|
298
305
|
return;
|
|
299
306
|
this.isSending = true;
|
|
307
|
+
try {
|
|
308
|
+
const messageDetail = await this.createMessageDetail();
|
|
309
|
+
const saved = await messageDetail.Save();
|
|
310
|
+
if (saved) {
|
|
311
|
+
await this.handleSuccessfulSend(messageDetail);
|
|
312
|
+
}
|
|
313
|
+
else {
|
|
314
|
+
this.handleSendFailure(messageDetail);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
this.handleSendError(error);
|
|
319
|
+
}
|
|
320
|
+
finally {
|
|
321
|
+
this.isSending = false;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Send a message with custom text WITHOUT modifying the visible messageText input
|
|
326
|
+
* Used for suggested responses - sends message silently without affecting user's current input
|
|
327
|
+
*/
|
|
328
|
+
async sendMessageWithText(text) {
|
|
329
|
+
if (!text || !text.trim()) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
if (this.isSending) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
this.isSending = true;
|
|
300
336
|
try {
|
|
301
337
|
const detail = await this.dataCache.createConversationDetail(this.currentUser);
|
|
302
338
|
detail.ConversationID = this.conversationId;
|
|
303
|
-
detail.Message =
|
|
339
|
+
detail.Message = text.trim();
|
|
304
340
|
detail.Role = 'User';
|
|
305
|
-
// Parse mentions from message (not stored, used for routing only)
|
|
306
|
-
const mentionResult = this.mentionParser.parseMentions(detail.Message, this.mentionAutocomplete.getAvailableAgents(), this.mentionAutocomplete.getAvailableUsers());
|
|
307
|
-
console.log('[MentionInput] Parsing message for routing:', detail.Message);
|
|
308
|
-
console.log('[MentionInput] Found mentions:', mentionResult);
|
|
309
|
-
console.log('[MentionInput] Agent mention:', mentionResult.agentMention);
|
|
310
|
-
// Set ParentID if this is a thread reply
|
|
311
341
|
if (this.parentMessageId) {
|
|
312
342
|
detail.ParentID = this.parentMessageId;
|
|
313
343
|
}
|
|
314
344
|
const saved = await detail.Save();
|
|
315
345
|
if (saved) {
|
|
316
346
|
this.messageSent.emit(detail);
|
|
317
|
-
|
|
318
|
-
// Check if this is the first message in the conversation
|
|
347
|
+
const mentionResult = this.parseMentionsFromMessage(detail.Message);
|
|
319
348
|
const isFirstMessage = this.conversationHistory.length === 0;
|
|
320
|
-
|
|
321
|
-
if (mentionResult.agentMention) {
|
|
322
|
-
// Direct @mention - skip Sage, invoke agent directly
|
|
323
|
-
console.log('🎯 Direct @mention detected, bypassing Sage');
|
|
324
|
-
if (isFirstMessage) {
|
|
325
|
-
Promise.all([
|
|
326
|
-
this.invokeAgentDirectly(detail, mentionResult.agentMention, this.conversationId),
|
|
327
|
-
this.nameConversation(detail.Message)
|
|
328
|
-
]);
|
|
329
|
-
}
|
|
330
|
-
else {
|
|
331
|
-
this.invokeAgentDirectly(detail, mentionResult.agentMention, this.conversationId);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
else {
|
|
335
|
-
// Check if user is replying to an agent (implicit continuation)
|
|
336
|
-
const lastAIMessage = this.conversationHistory
|
|
337
|
-
.slice()
|
|
338
|
-
.reverse()
|
|
339
|
-
.find(msg => msg.Role === 'AI' &&
|
|
340
|
-
msg.AgentID &&
|
|
341
|
-
msg.AgentID !== this.converationManagerAgent?.ID);
|
|
342
|
-
if (lastAIMessage && lastAIMessage.AgentID) {
|
|
343
|
-
// Continue with same agent - skip Sage
|
|
344
|
-
console.log('🔄 Implicit continuation detected, continuing with last agent');
|
|
345
|
-
if (isFirstMessage) {
|
|
346
|
-
Promise.all([
|
|
347
|
-
this.continueWithAgent(detail, lastAIMessage.AgentID, this.conversationId),
|
|
348
|
-
this.nameConversation(detail.Message)
|
|
349
|
-
]);
|
|
350
|
-
}
|
|
351
|
-
else {
|
|
352
|
-
this.continueWithAgent(detail, lastAIMessage.AgentID, this.conversationId);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
else {
|
|
356
|
-
// No context - use Sage
|
|
357
|
-
console.log('🤖 No agent context, using Sage');
|
|
358
|
-
if (isFirstMessage) {
|
|
359
|
-
Promise.all([
|
|
360
|
-
this.processMessageThroughAgent(detail, mentionResult),
|
|
361
|
-
this.nameConversation(detail.Message)
|
|
362
|
-
]);
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
this.processMessageThroughAgent(detail, mentionResult);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
// Focus back on textarea
|
|
370
|
-
setTimeout(() => {
|
|
371
|
-
if (this.messageTextarea && this.messageTextarea.nativeElement) {
|
|
372
|
-
this.messageTextarea.nativeElement.focus();
|
|
373
|
-
}
|
|
374
|
-
}, 100);
|
|
349
|
+
await this.routeMessage(detail, mentionResult, isFirstMessage);
|
|
375
350
|
}
|
|
376
351
|
else {
|
|
377
|
-
|
|
378
|
-
this.toastService.error('Failed to send message. Please try again.');
|
|
352
|
+
this.handleSendFailure(detail);
|
|
379
353
|
}
|
|
380
354
|
}
|
|
381
355
|
catch (error) {
|
|
382
|
-
|
|
383
|
-
this.toastService.error('Error sending message. Please try again.');
|
|
356
|
+
this.handleSendError(error);
|
|
384
357
|
}
|
|
385
358
|
finally {
|
|
386
359
|
this.isSending = false;
|
|
387
360
|
}
|
|
388
361
|
}
|
|
362
|
+
/**
|
|
363
|
+
* Creates and configures a new conversation detail message
|
|
364
|
+
*/
|
|
365
|
+
async createMessageDetail() {
|
|
366
|
+
const detail = await this.dataCache.createConversationDetail(this.currentUser);
|
|
367
|
+
detail.ConversationID = this.conversationId;
|
|
368
|
+
detail.Message = this.messageText.trim();
|
|
369
|
+
detail.Role = 'User';
|
|
370
|
+
if (this.parentMessageId) {
|
|
371
|
+
detail.ParentID = this.parentMessageId;
|
|
372
|
+
}
|
|
373
|
+
return detail;
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Handles successful message send - routes to appropriate agent
|
|
377
|
+
*/
|
|
378
|
+
async handleSuccessfulSend(messageDetail) {
|
|
379
|
+
this.messageSent.emit(messageDetail);
|
|
380
|
+
this.messageText = '';
|
|
381
|
+
const mentionResult = this.parseMentionsFromMessage(messageDetail.Message);
|
|
382
|
+
const isFirstMessage = this.conversationHistory.length === 0;
|
|
383
|
+
await this.routeMessage(messageDetail, mentionResult, isFirstMessage);
|
|
384
|
+
this.refocusTextarea();
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Parses mentions from the message for routing decisions
|
|
388
|
+
*/
|
|
389
|
+
parseMentionsFromMessage(message) {
|
|
390
|
+
const mentionResult = this.mentionParser.parseMentions(message, this.mentionAutocomplete.getAvailableAgents(), this.mentionAutocomplete.getAvailableUsers());
|
|
391
|
+
return mentionResult;
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Routes the message to the appropriate agent or Sage based on context
|
|
395
|
+
* Priority: @mention > intent check > Sage
|
|
396
|
+
*/
|
|
397
|
+
async routeMessage(messageDetail, mentionResult, isFirstMessage) {
|
|
398
|
+
// Priority 1: Direct @mention
|
|
399
|
+
if (mentionResult.agentMention) {
|
|
400
|
+
await this.handleDirectMention(messageDetail, mentionResult.agentMention, isFirstMessage);
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
// Priority 2: Check for previous agent with intent check
|
|
404
|
+
const lastAgentId = this.findLastNonSageAgentId();
|
|
405
|
+
if (lastAgentId) {
|
|
406
|
+
await this.handleAgentContinuity(messageDetail, lastAgentId, mentionResult, isFirstMessage);
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
// Priority 3: No context - use Sage
|
|
410
|
+
await this.handleNoAgentContext(messageDetail, mentionResult, isFirstMessage);
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Handles routing when user directly mentions an agent with @
|
|
414
|
+
*/
|
|
415
|
+
async handleDirectMention(messageDetail, agentMention, isFirstMessage) {
|
|
416
|
+
console.log('🎯 Direct @mention detected, bypassing Sage');
|
|
417
|
+
await this.executeRouteWithNaming(() => this.invokeAgentDirectly(messageDetail, agentMention, this.conversationId), messageDetail.Message, isFirstMessage);
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Handles routing when there's a previous agent - checks intent first
|
|
421
|
+
*/
|
|
422
|
+
async handleAgentContinuity(messageDetail, lastAgentId, mentionResult, isFirstMessage) {
|
|
423
|
+
console.log('🔍 Previous agent found, checking continuity intent...');
|
|
424
|
+
const intent = await this.checkContinuityIntent(lastAgentId, messageDetail.Message);
|
|
425
|
+
if (intent === 'YES') {
|
|
426
|
+
console.log('✅ Intent check: YES - continuing with previous agent');
|
|
427
|
+
await this.executeRouteWithNaming(() => this.continueWithAgent(messageDetail, lastAgentId, this.conversationId), messageDetail.Message, isFirstMessage);
|
|
428
|
+
}
|
|
429
|
+
else {
|
|
430
|
+
console.log(`🤖 Intent check: ${intent} - routing through Sage for evaluation`);
|
|
431
|
+
await this.executeRouteWithNaming(() => this.processMessageThroughAgent(messageDetail, mentionResult), messageDetail.Message, isFirstMessage);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Handles routing when there's no previous agent context
|
|
436
|
+
*/
|
|
437
|
+
async handleNoAgentContext(messageDetail, mentionResult, isFirstMessage) {
|
|
438
|
+
console.log('🤖 No agent context, using Sage');
|
|
439
|
+
await this.executeRouteWithNaming(() => this.processMessageThroughAgent(messageDetail, mentionResult), messageDetail.Message, isFirstMessage);
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Finds the last agent ID that isn't Sage
|
|
443
|
+
*/
|
|
444
|
+
findLastNonSageAgentId() {
|
|
445
|
+
const lastAIMessage = this.conversationHistory
|
|
446
|
+
.slice()
|
|
447
|
+
.reverse()
|
|
448
|
+
.find(msg => msg.Role === 'AI' &&
|
|
449
|
+
msg.AgentID &&
|
|
450
|
+
msg.AgentID !== this.converationManagerAgent?.ID);
|
|
451
|
+
return lastAIMessage?.AgentID || null;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Checks if message should continue with the previous agent
|
|
455
|
+
* Shows UI indicator during check
|
|
456
|
+
*/
|
|
457
|
+
async checkContinuityIntent(agentId, message) {
|
|
458
|
+
this.processingMessage = 'Analyzing intent...';
|
|
459
|
+
this.isProcessing = true;
|
|
460
|
+
try {
|
|
461
|
+
const intent = await this.agentService.checkAgentContinuityIntent(agentId, message, this.conversationHistory);
|
|
462
|
+
return intent;
|
|
463
|
+
}
|
|
464
|
+
catch (error) {
|
|
465
|
+
console.error('❌ Intent check failed, defaulting to UNSURE:', error);
|
|
466
|
+
return 'UNSURE';
|
|
467
|
+
}
|
|
468
|
+
finally {
|
|
469
|
+
this.processingMessage = 'AI is responding...';
|
|
470
|
+
this.isProcessing = false;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Executes a routing function, optionally with conversation naming for first message
|
|
475
|
+
*/
|
|
476
|
+
async executeRouteWithNaming(routeFunction, userMessage, isFirstMessage) {
|
|
477
|
+
if (isFirstMessage) {
|
|
478
|
+
await Promise.all([
|
|
479
|
+
routeFunction(),
|
|
480
|
+
this.nameConversation(userMessage)
|
|
481
|
+
]);
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
await routeFunction();
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
/**
|
|
488
|
+
* Returns focus to the message textarea
|
|
489
|
+
*/
|
|
490
|
+
refocusTextarea() {
|
|
491
|
+
setTimeout(() => {
|
|
492
|
+
if (this.messageTextarea?.nativeElement) {
|
|
493
|
+
this.messageTextarea.nativeElement.focus();
|
|
494
|
+
}
|
|
495
|
+
}, 100);
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* Handles message send failure
|
|
499
|
+
*/
|
|
500
|
+
handleSendFailure(messageDetail) {
|
|
501
|
+
console.error('Failed to send message:', messageDetail.LatestResult?.Message);
|
|
502
|
+
this.toastService.error('Failed to send message. Please try again.');
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Handles message send error
|
|
506
|
+
*/
|
|
507
|
+
handleSendError(error) {
|
|
508
|
+
console.error('Error sending message:', error);
|
|
509
|
+
this.toastService.error('Error sending message. Please try again.');
|
|
510
|
+
}
|
|
389
511
|
/**
|
|
390
512
|
* Safe save for ConversationDetail - prevents overwrites of completed/errored messages
|
|
391
513
|
* Use this ONLY in progress update paths to prevent race conditions
|
|
@@ -408,9 +530,23 @@ export class MessageInputComponent {
|
|
|
408
530
|
* IMPORTANT: Filters by agentRunId to prevent cross-contamination when multiple agents run in parallel
|
|
409
531
|
*/
|
|
410
532
|
createProgressCallback(conversationDetail, agentName) {
|
|
533
|
+
// Use closure to capture the agent run ID from the first progress message
|
|
534
|
+
// This allows us to filter out progress messages from other concurrent agents
|
|
535
|
+
let capturedAgentRunId = null;
|
|
411
536
|
return async (progress) => {
|
|
412
537
|
// Extract agentRunId from progress metadata
|
|
413
538
|
const progressAgentRunId = progress.metadata?.agentRunId;
|
|
539
|
+
// Capture the agent run ID from the first progress message
|
|
540
|
+
if (!capturedAgentRunId && progressAgentRunId) {
|
|
541
|
+
capturedAgentRunId = progressAgentRunId;
|
|
542
|
+
console.log(`[${agentName}] 📌 Captured agent run ID: ${capturedAgentRunId} for conversation detail: ${conversationDetail.ID}`);
|
|
543
|
+
}
|
|
544
|
+
// Filter out progress messages from other concurrent agents
|
|
545
|
+
// This prevents cross-contamination when multiple agents run in parallel
|
|
546
|
+
if (capturedAgentRunId && progressAgentRunId && progressAgentRunId !== capturedAgentRunId) {
|
|
547
|
+
console.log(`[${agentName}] 🚫 Ignoring progress from different agent run (expected: ${capturedAgentRunId}, got: ${progressAgentRunId})`);
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
414
550
|
// Format progress message with visual indicator
|
|
415
551
|
const progressText = progress.message;
|
|
416
552
|
// Update the active task with progress details (if it exists)
|
|
@@ -419,10 +555,16 @@ export class MessageInputComponent {
|
|
|
419
555
|
try {
|
|
420
556
|
if (conversationDetail) {
|
|
421
557
|
console.log(`[${agentName}] Got conversation detail from cache - Status: ${conversationDetail.Status}, ID: ${conversationDetail.ID}`);
|
|
422
|
-
//
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
558
|
+
// Check 1: Skip if message is already complete or errored
|
|
559
|
+
if (conversationDetail.Status === 'Complete' || conversationDetail.Status === 'Error') {
|
|
560
|
+
console.log(`[${agentName}] ⛔ Skipping progress update - message status is ${conversationDetail.Status}`);
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
// Check 2: Skip if message was marked as completed (prevents race condition)
|
|
564
|
+
// Once a message is marked complete, we reject ALL further progress updates
|
|
565
|
+
const completionTime = this.completionTimestamps.get(conversationDetail.ID);
|
|
566
|
+
if (completionTime) {
|
|
567
|
+
console.log(`[${agentName}] ⛔ Skipping progress update - message was marked complete at ${completionTime}`);
|
|
426
568
|
return;
|
|
427
569
|
}
|
|
428
570
|
// Emit agentRunId if we have it (for parent to track)
|
|
@@ -492,7 +634,8 @@ export class MessageInputComponent {
|
|
|
492
634
|
taskId = null;
|
|
493
635
|
}
|
|
494
636
|
if (!result || !result.success) {
|
|
495
|
-
// Evaluation failed
|
|
637
|
+
// Evaluation failed - mark as complete to stop progress updates
|
|
638
|
+
this.markMessageComplete(conversationManagerMessage);
|
|
496
639
|
conversationManagerMessage.Status = 'Error';
|
|
497
640
|
conversationManagerMessage.Message = `❌ Evaluation failed`;
|
|
498
641
|
conversationManagerMessage.Error = result?.agentRun?.ErrorMessage || 'Agent evaluation failed';
|
|
@@ -502,6 +645,8 @@ export class MessageInputComponent {
|
|
|
502
645
|
await userMessage.Save();
|
|
503
646
|
this.messageSent.emit(userMessage);
|
|
504
647
|
console.warn('⚠️ Sage failed:', result?.agentRun?.ErrorMessage);
|
|
648
|
+
// Clean up completion timestamp
|
|
649
|
+
this.cleanupCompletionTimestamp(conversationManagerMessage.ID);
|
|
505
650
|
return;
|
|
506
651
|
}
|
|
507
652
|
console.log('🤖 Sage Response:', {
|
|
@@ -509,7 +654,8 @@ export class MessageInputComponent {
|
|
|
509
654
|
hasPayload: !!result.payload,
|
|
510
655
|
hasMessage: !!result.agentRun.Message,
|
|
511
656
|
payloadKeys: result.payload ? Object.keys(result.payload) : [],
|
|
512
|
-
payload: result.payload // Full payload for debugging
|
|
657
|
+
payload: result.payload, // Full payload for debugging,
|
|
658
|
+
suggestedResponses: result.suggestedResponses
|
|
513
659
|
});
|
|
514
660
|
// Stage 2: Check for task graph (multi-step orchestration)
|
|
515
661
|
if (result.payload?.taskGraph) {
|
|
@@ -531,11 +677,11 @@ export class MessageInputComponent {
|
|
|
531
677
|
}
|
|
532
678
|
// Stage 4: Direct chat response from Sage
|
|
533
679
|
else if (result.agentRun.FinalStep === 'Chat' && result.agentRun.Message) {
|
|
680
|
+
// Mark message as completing BEFORE setting final content (prevents race condition)
|
|
681
|
+
this.markMessageComplete(conversationManagerMessage);
|
|
534
682
|
// Normal chat response
|
|
535
|
-
|
|
536
|
-
conversationManagerMessage.
|
|
537
|
-
await conversationManagerMessage.Save();
|
|
538
|
-
this.messageSent.emit(conversationManagerMessage);
|
|
683
|
+
// 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
|
|
684
|
+
await this.updateConversationDetail(conversationManagerMessage, result.agentRun.Message, 'Complete', result.suggestedResponses);
|
|
539
685
|
// Handle artifacts if any (but NOT task graphs - those are intermediate work products)
|
|
540
686
|
if (result.payload && Object.keys(result.payload).length > 0) {
|
|
541
687
|
await this.createArtifactFromPayload(result.payload, conversationManagerMessage, result.agentRun.AgentID);
|
|
@@ -549,26 +695,35 @@ export class MessageInputComponent {
|
|
|
549
695
|
if (taskId) {
|
|
550
696
|
this.activeTasks.remove(taskId);
|
|
551
697
|
}
|
|
698
|
+
// Clean up completion timestamp after delay
|
|
699
|
+
this.cleanupCompletionTimestamp(conversationManagerMessage.ID);
|
|
552
700
|
}
|
|
553
701
|
// Stage 5: Silent observation - but check for message content first
|
|
554
702
|
else {
|
|
555
703
|
// Check if there's a message to display even without payload/taskGraph
|
|
556
704
|
if (result.agentRun.Message) {
|
|
557
705
|
console.log('💬 Sage provided a message without payload');
|
|
558
|
-
|
|
559
|
-
conversationManagerMessage
|
|
706
|
+
// Mark message as completing BEFORE setting final content
|
|
707
|
+
this.markMessageComplete(conversationManagerMessage);
|
|
560
708
|
conversationManagerMessage.HiddenToUser = false;
|
|
561
|
-
|
|
709
|
+
// 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
|
|
710
|
+
await this.updateConversationDetail(conversationManagerMessage, result.agentRun.Message, 'Complete', result.suggestedResponses);
|
|
562
711
|
this.messageSent.emit(conversationManagerMessage);
|
|
712
|
+
// Clean up completion timestamp after delay
|
|
713
|
+
this.cleanupCompletionTimestamp(conversationManagerMessage.ID);
|
|
563
714
|
}
|
|
564
715
|
else {
|
|
565
716
|
console.log('🔇 Sage chose to observe silently');
|
|
717
|
+
// Mark message as completing
|
|
718
|
+
this.markMessageComplete(conversationManagerMessage);
|
|
566
719
|
// Hide the Sage message
|
|
567
720
|
conversationManagerMessage.HiddenToUser = true;
|
|
568
|
-
|
|
569
|
-
await conversationManagerMessage.
|
|
721
|
+
// 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
|
|
722
|
+
await this.updateConversationDetail(conversationManagerMessage, conversationManagerMessage.Message, 'Complete');
|
|
570
723
|
this.messageSent.emit(conversationManagerMessage);
|
|
571
724
|
await this.handleSilentObservation(userMessage, this.conversationId);
|
|
725
|
+
// Clean up completion timestamp after delay
|
|
726
|
+
this.cleanupCompletionTimestamp(conversationManagerMessage.ID);
|
|
572
727
|
}
|
|
573
728
|
// Remove CM from active tasks
|
|
574
729
|
if (taskId) {
|
|
@@ -580,11 +735,15 @@ export class MessageInputComponent {
|
|
|
580
735
|
console.error('❌ Error processing message through agents:', error);
|
|
581
736
|
// Update conversationManagerMessage status to Error
|
|
582
737
|
if (conversationManagerMessage && conversationManagerMessage.ID) {
|
|
738
|
+
// Mark as complete to stop progress updates
|
|
739
|
+
this.markMessageComplete(conversationManagerMessage);
|
|
583
740
|
conversationManagerMessage.Status = 'Error';
|
|
584
741
|
conversationManagerMessage.Message = `❌ Error: ${String(error)}`;
|
|
585
742
|
conversationManagerMessage.Error = String(error);
|
|
586
743
|
await conversationManagerMessage.Save();
|
|
587
744
|
this.messageSent.emit(conversationManagerMessage);
|
|
745
|
+
// Clean up completion timestamp
|
|
746
|
+
this.cleanupCompletionTimestamp(conversationManagerMessage.ID);
|
|
588
747
|
}
|
|
589
748
|
// Mark user message as complete
|
|
590
749
|
userMessage.Status = 'Complete';
|
|
@@ -685,27 +844,20 @@ export class MessageInputComponent {
|
|
|
685
844
|
};
|
|
686
845
|
const result = await GraphQLDataProvider.Instance.ExecuteGQL(mutation, variables);
|
|
687
846
|
console.log('📊 ExecuteTaskGraph result:', {
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
847
|
+
hasExecuteTaskGraph: !!result?.ExecuteTaskGraph,
|
|
848
|
+
success: result?.ExecuteTaskGraph?.success,
|
|
849
|
+
resultsCount: result?.ExecuteTaskGraph?.results?.length,
|
|
850
|
+
result: result
|
|
692
851
|
});
|
|
693
852
|
// Step 4: Update task execution message with results
|
|
694
|
-
//
|
|
695
|
-
if (result?.
|
|
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) {
|
|
853
|
+
// ExecuteGQL returns data directly (not wrapped in {data, errors})
|
|
854
|
+
if (result?.ExecuteTaskGraph?.success) {
|
|
703
855
|
console.log('✅ Task graph execution completed successfully');
|
|
704
856
|
taskExecutionMessage.Message = `✅ **${workflowName}** completed successfully`;
|
|
705
857
|
taskExecutionMessage.Status = 'Complete';
|
|
706
858
|
}
|
|
707
859
|
else {
|
|
708
|
-
const errorMsg = result?.
|
|
860
|
+
const errorMsg = result?.ExecuteTaskGraph?.errorMessage || 'Unknown error';
|
|
709
861
|
console.error('❌ Task graph execution failed:', errorMsg);
|
|
710
862
|
taskExecutionMessage.Message = `❌ **${workflowName}** failed: ${errorMsg}`;
|
|
711
863
|
taskExecutionMessage.Status = 'Error';
|
|
@@ -713,6 +865,16 @@ export class MessageInputComponent {
|
|
|
713
865
|
}
|
|
714
866
|
await taskExecutionMessage.Save();
|
|
715
867
|
this.messageSent.emit(taskExecutionMessage);
|
|
868
|
+
// Trigger artifact reload for this message
|
|
869
|
+
// Artifacts were created on server during task execution and linked to this message
|
|
870
|
+
// This event triggers the parent component to reload artifacts from the database
|
|
871
|
+
this.artifactCreated.emit({
|
|
872
|
+
artifactId: '', // Placeholder - reload will fetch actual artifacts from DB
|
|
873
|
+
versionId: '',
|
|
874
|
+
versionNumber: 1,
|
|
875
|
+
conversationDetailId: taskExecutionMessage.ID,
|
|
876
|
+
name: ''
|
|
877
|
+
});
|
|
716
878
|
// Unregister from real-time updates (task complete)
|
|
717
879
|
this.activeTaskExecutionMessageIds.delete(taskExecutionMessage.ID);
|
|
718
880
|
// Mark user message as complete
|
|
@@ -727,6 +889,14 @@ export class MessageInputComponent {
|
|
|
727
889
|
taskExecutionMessage.Error = String(error);
|
|
728
890
|
await taskExecutionMessage.Save();
|
|
729
891
|
this.messageSent.emit(taskExecutionMessage);
|
|
892
|
+
// Trigger artifact reload even on error - partial artifacts may have been created
|
|
893
|
+
this.artifactCreated.emit({
|
|
894
|
+
artifactId: '',
|
|
895
|
+
versionId: '',
|
|
896
|
+
versionNumber: 1,
|
|
897
|
+
conversationDetailId: taskExecutionMessage.ID,
|
|
898
|
+
name: ''
|
|
899
|
+
});
|
|
730
900
|
// Unregister from real-time updates (task failed)
|
|
731
901
|
this.activeTaskExecutionMessageIds.delete(taskExecutionMessage.ID);
|
|
732
902
|
userMessage.Status = 'Complete';
|
|
@@ -734,15 +904,22 @@ export class MessageInputComponent {
|
|
|
734
904
|
this.messageSent.emit(userMessage);
|
|
735
905
|
}
|
|
736
906
|
}
|
|
737
|
-
async updateConversationDetail(convoDetail, message, status) {
|
|
907
|
+
async updateConversationDetail(convoDetail, message, status, suggestedResponses) {
|
|
738
908
|
if (convoDetail.Status === 'Complete' || convoDetail.Status === 'Error') {
|
|
739
909
|
return; // Do not update completed or errored messages
|
|
740
910
|
}
|
|
911
|
+
// Mark as completing BEFORE updating if status is Complete or Error
|
|
912
|
+
if (status === 'Complete' || status === 'Error') {
|
|
913
|
+
this.markMessageComplete(convoDetail);
|
|
914
|
+
}
|
|
741
915
|
const maxAttempts = 2;
|
|
742
916
|
let attempts = 0, done = false;
|
|
743
917
|
while (attempts < maxAttempts && !done) {
|
|
744
918
|
convoDetail.Message = message;
|
|
745
919
|
convoDetail.Status = status;
|
|
920
|
+
if (suggestedResponses !== undefined) {
|
|
921
|
+
convoDetail.SuggestedResponses = JSON.stringify(suggestedResponses);
|
|
922
|
+
}
|
|
746
923
|
await convoDetail.Save();
|
|
747
924
|
if (convoDetail.Message === message && convoDetail.Status === status) {
|
|
748
925
|
done = true;
|
|
@@ -753,6 +930,10 @@ export class MessageInputComponent {
|
|
|
753
930
|
}
|
|
754
931
|
attempts++;
|
|
755
932
|
}
|
|
933
|
+
// Clean up completion timestamp after delay
|
|
934
|
+
if (status === 'Complete' || status === 'Error') {
|
|
935
|
+
this.cleanupCompletionTimestamp(convoDetail.ID);
|
|
936
|
+
}
|
|
756
937
|
}
|
|
757
938
|
/**
|
|
758
939
|
* Handle single task execution from task graph using direct agent execution
|
|
@@ -1445,13 +1626,30 @@ export class MessageInputComponent {
|
|
|
1445
1626
|
// Don't show error to user - naming failures should be silent
|
|
1446
1627
|
}
|
|
1447
1628
|
}
|
|
1629
|
+
/**
|
|
1630
|
+
* Marks a conversation detail as complete and records timestamp to prevent race conditions
|
|
1631
|
+
*/
|
|
1632
|
+
markMessageComplete(conversationDetail) {
|
|
1633
|
+
const now = Date.now();
|
|
1634
|
+
this.completionTimestamps.set(conversationDetail.ID, now);
|
|
1635
|
+
console.log(`🏁 Marked message ${conversationDetail.ID} as complete at ${now}`);
|
|
1636
|
+
}
|
|
1637
|
+
/**
|
|
1638
|
+
* Cleans up completion timestamps for completed messages (prevents memory leak)
|
|
1639
|
+
*/
|
|
1640
|
+
cleanupCompletionTimestamp(conversationDetailId) {
|
|
1641
|
+
// Keep timestamp for a short period to catch any late progress updates
|
|
1642
|
+
setTimeout(() => {
|
|
1643
|
+
this.completionTimestamps.delete(conversationDetailId);
|
|
1644
|
+
}, 5000); // 5 seconds should be more than enough
|
|
1645
|
+
}
|
|
1448
1646
|
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)); };
|
|
1449
1647
|
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MessageInputComponent, selectors: [["mj-message-input"]], viewQuery: function MessageInputComponent_Query(rf, ctx) { if (rf & 1) {
|
|
1450
1648
|
i0.ɵɵviewQuery(_c0, 5);
|
|
1451
1649
|
} if (rf & 2) {
|
|
1452
1650
|
let _t;
|
|
1453
1651
|
i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.messageTextarea = _t.first);
|
|
1454
|
-
} }, inputs: { conversationId: "conversationId", currentUser: "currentUser", disabled: "disabled", placeholder: "placeholder", parentMessageId: "parentMessageId", conversationHistory: "conversationHistory" }, outputs: { messageSent: "messageSent", agentResponse: "agentResponse", agentRunDetected: "agentRunDetected", artifactCreated: "artifactCreated", conversationRenamed: "conversationRenamed" }, decls: 11, vars:
|
|
1652
|
+
} }, inputs: { conversationId: "conversationId", currentUser: "currentUser", disabled: "disabled", placeholder: "placeholder", parentMessageId: "parentMessageId", conversationHistory: "conversationHistory" }, outputs: { messageSent: "messageSent", agentResponse: "agentResponse", agentRunDetected: "agentRunDetected", artifactCreated: "artifactCreated", conversationRenamed: "conversationRenamed" }, decls: 11, vars: 13, consts: [["messageTextarea", ""], [1, "message-input-container"], ["rows", "3", 1, "message-input", 3, "ngModelChange", "keydown", "input", "ngModel", "placeholder", "disabled"], [3, "suggestionSelected", "closed", "suggestions", "position", "visible", "showAbove"], [1, "input-actions"], ["class", "processing-indicator", 4, "ngIf"], ["title", "Attach file (coming soon)", 1, "btn-attach", 3, "disabled"], [1, "fas", "fa-paperclip"], [1, "btn-send", 3, "click", "disabled", "title"], [1, "fas", "fa-paper-plane"], [1, "processing-indicator"], [1, "fas", "fa-circle-notch", "fa-spin"]], template: function MessageInputComponent_Template(rf, ctx) { if (rf & 1) {
|
|
1455
1653
|
const _r1 = i0.ɵɵgetCurrentView();
|
|
1456
1654
|
i0.ɵɵelementStart(0, "div", 1)(1, "textarea", 2, 0);
|
|
1457
1655
|
i0.ɵɵtwoWayListener("ngModelChange", function MessageInputComponent_Template_textarea_ngModelChange_1_listener($event) { i0.ɵɵrestoreView(_r1); i0.ɵɵtwoWayBindingSet(ctx.messageText, $event) || (ctx.messageText = $event); return i0.ɵɵresetView($event); });
|
|
@@ -1462,7 +1660,7 @@ export class MessageInputComponent {
|
|
|
1462
1660
|
i0.ɵɵlistener("suggestionSelected", function MessageInputComponent_Template_mj_mention_dropdown_suggestionSelected_4_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onMentionSelected($event)); })("closed", function MessageInputComponent_Template_mj_mention_dropdown_closed_4_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.closeMentionDropdown()); });
|
|
1463
1661
|
i0.ɵɵelementEnd();
|
|
1464
1662
|
i0.ɵɵelementStart(5, "div", 4);
|
|
1465
|
-
i0.ɵɵtemplate(6, MessageInputComponent_div_6_Template, 4,
|
|
1663
|
+
i0.ɵɵtemplate(6, MessageInputComponent_div_6_Template, 4, 1, "div", 5);
|
|
1466
1664
|
i0.ɵɵelementStart(7, "button", 6);
|
|
1467
1665
|
i0.ɵɵelement(8, "i", 7);
|
|
1468
1666
|
i0.ɵɵelementEnd();
|
|
@@ -1472,8 +1670,9 @@ export class MessageInputComponent {
|
|
|
1472
1670
|
i0.ɵɵelementEnd()()();
|
|
1473
1671
|
} if (rf & 2) {
|
|
1474
1672
|
i0.ɵɵadvance();
|
|
1673
|
+
i0.ɵɵclassProp("intent-checking", ctx.isProcessing);
|
|
1475
1674
|
i0.ɵɵtwoWayProperty("ngModel", ctx.messageText);
|
|
1476
|
-
i0.ɵɵproperty("placeholder", ctx.placeholder)("disabled", ctx.disabled || ctx.
|
|
1675
|
+
i0.ɵɵproperty("placeholder", ctx.placeholder)("disabled", ctx.disabled || ctx.isProcessing);
|
|
1477
1676
|
i0.ɵɵadvance(3);
|
|
1478
1677
|
i0.ɵɵproperty("suggestions", ctx.mentionSuggestions)("position", ctx.mentionDropdownPosition)("visible", ctx.showMentionDropdown)("showAbove", ctx.mentionDropdownShowAbove);
|
|
1479
1678
|
i0.ɵɵadvance(2);
|
|
@@ -1482,11 +1681,11 @@ export class MessageInputComponent {
|
|
|
1482
1681
|
i0.ɵɵproperty("disabled", ctx.disabled);
|
|
1483
1682
|
i0.ɵɵadvance(2);
|
|
1484
1683
|
i0.ɵɵproperty("disabled", !ctx.canSend)("title", ctx.isSending ? "Sending..." : "Send message");
|
|
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:
|
|
1684
|
+
} }, 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: transparent;\n cursor: wait;\n}\n\n//[_ngcontent-%COMP%] Subtle[_ngcontent-%COMP%] visual[_ngcontent-%COMP%] feedback[_ngcontent-%COMP%] when[_ngcontent-%COMP%] checking[_ngcontent-%COMP%] intent[_ngcontent-%COMP%] (no[_ngcontent-%COMP%] ugly[_ngcontent-%COMP%] gray[_ngcontent-%COMP%] background)\n.message-input.intent-checking[_ngcontent-%COMP%] {\n opacity: 0.6;\n transition: opacity 0.2s;\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}"] });
|
|
1486
1685
|
}
|
|
1487
1686
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MessageInputComponent, [{
|
|
1488
1687
|
type: Component,
|
|
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 ||
|
|
1688
|
+
args: [{ selector: 'mj-message-input', template: "<div class=\"message-input-container\">\n <textarea\n #messageTextarea\n class=\"message-input\"\n [class.intent-checking]=\"isProcessing\"\n [(ngModel)]=\"messageText\"\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled || isProcessing\"\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>{{ processingMessage }}</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: transparent;\n cursor: wait;\n}\n\n// Subtle visual feedback when checking intent (no ugly gray background)\n.message-input.intent-checking {\n opacity: 0.6;\n transition: opacity 0.2s;\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}"] }]
|
|
1490
1689
|
}], () => [{ 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: [{
|
|
1491
1690
|
type: Input
|
|
1492
1691
|
}], currentUser: [{
|