@memberjunction/ng-conversations 5.36.0 → 5.38.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/README.md +2 -2
- package/dist/lib/components/conversation/conversation-agent-picker.component.d.ts +47 -0
- package/dist/lib/components/conversation/conversation-agent-picker.component.d.ts.map +1 -0
- package/dist/lib/components/conversation/conversation-agent-picker.component.js +253 -0
- package/dist/lib/components/conversation/conversation-agent-picker.component.js.map +1 -0
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts +201 -4
- package/dist/lib/components/conversation/conversation-chat-area.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js +776 -263
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-empty-state.component.d.ts +2 -1
- package/dist/lib/components/conversation/conversation-empty-state.component.d.ts.map +1 -1
- package/dist/lib/components/conversation/conversation-empty-state.component.js +6 -3
- package/dist/lib/components/conversation/conversation-empty-state.component.js.map +1 -1
- package/dist/lib/components/conversation/conversation-mode-picker.component.d.ts +57 -0
- package/dist/lib/components/conversation/conversation-mode-picker.component.d.ts.map +1 -0
- package/dist/lib/components/conversation/conversation-mode-picker.component.js +252 -0
- package/dist/lib/components/conversation/conversation-mode-picker.component.js.map +1 -0
- package/dist/lib/components/dialogs/rating-dialog.component.d.ts +31 -0
- package/dist/lib/components/dialogs/rating-dialog.component.d.ts.map +1 -0
- package/dist/lib/components/dialogs/rating-dialog.component.js +290 -0
- package/dist/lib/components/dialogs/rating-dialog.component.js.map +1 -0
- package/dist/lib/components/mention/mention-editor.component.d.ts +1 -1
- package/dist/lib/components/mention/mention-editor.component.d.ts.map +1 -1
- package/dist/lib/components/mention/mention-editor.component.js +1 -0
- package/dist/lib/components/mention/mention-editor.component.js.map +1 -1
- package/dist/lib/components/message/conversation-message-rating.component.d.ts +43 -18
- package/dist/lib/components/message/conversation-message-rating.component.d.ts.map +1 -1
- package/dist/lib/components/message/conversation-message-rating.component.js +235 -193
- package/dist/lib/components/message/conversation-message-rating.component.js.map +1 -1
- package/dist/lib/components/message/message-input-box.component.d.ts +1 -1
- package/dist/lib/components/message/message-input-box.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input-box.component.js +1 -1
- package/dist/lib/components/message/message-input-box.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +58 -4
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +135 -21
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/message-item.component.d.ts +0 -13
- package/dist/lib/components/message/message-item.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-item.component.js +126 -152
- package/dist/lib/components/message/message-item.component.js.map +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.d.ts +88 -5
- package/dist/lib/components/overlay/chat-overlay.component.d.ts.map +1 -1
- package/dist/lib/components/overlay/chat-overlay.component.js +296 -54
- package/dist/lib/components/overlay/chat-overlay.component.js.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts +11 -0
- package/dist/lib/components/workspace/conversation-workspace.component.d.ts.map +1 -1
- package/dist/lib/components/workspace/conversation-workspace.component.js +13 -3
- package/dist/lib/components/workspace/conversation-workspace.component.js.map +1 -1
- package/dist/lib/conversations.module.d.ts +59 -57
- package/dist/lib/conversations.module.d.ts.map +1 -1
- package/dist/lib/conversations.module.js +12 -4
- package/dist/lib/conversations.module.js.map +1 -1
- package/dist/lib/models/conversation-state.model.d.ts +0 -27
- package/dist/lib/models/conversation-state.model.d.ts.map +1 -1
- package/dist/lib/models/conversation-state.model.js.map +1 -1
- package/dist/lib/services/conversation-agent.service.d.ts +1 -9
- package/dist/lib/services/conversation-agent.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-agent.service.js +22 -121
- package/dist/lib/services/conversation-agent.service.js.map +1 -1
- package/dist/lib/services/conversation-attachment.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-attachment.service.js +5 -26
- package/dist/lib/services/conversation-attachment.service.js.map +1 -1
- package/dist/lib/services/conversation-bridge.service.d.ts +13 -0
- package/dist/lib/services/conversation-bridge.service.d.ts.map +1 -1
- package/dist/lib/services/conversation-bridge.service.js +15 -0
- package/dist/lib/services/conversation-bridge.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 +0 -1
- package/dist/lib/services/data-cache.service.js.map +1 -1
- package/dist/lib/services/dialog.service.d.ts +24 -0
- package/dist/lib/services/dialog.service.d.ts.map +1 -1
- package/dist/lib/services/dialog.service.js +45 -0
- package/dist/lib/services/dialog.service.js.map +1 -1
- package/package.json +23 -22
- package/dist/lib/components/message/suggested-responses.component.d.ts +0 -55
- package/dist/lib/components/message/suggested-responses.component.d.ts.map +0 -1
- package/dist/lib/components/message/suggested-responses.component.js +0 -207
- package/dist/lib/components/message/suggested-responses.component.js.map +0 -1
package/README.md
CHANGED
|
@@ -113,9 +113,9 @@ A floating chat panel (bottom-right corner) that wraps the chat area for persist
|
|
|
113
113
|
The overlay is generic — it raises events for navigation and tool execution. The consuming application (e.g., MJExplorer) handles those events with app-specific logic like `NavigationService.OpenEntityRecord()`.
|
|
114
114
|
|
|
115
115
|
**Related packages:**
|
|
116
|
-
- [`@memberjunction/ai-agent-client`](
|
|
116
|
+
- [`@memberjunction/ai-agent-client`](../../../AI/AgentsClient/README.md) — Core agent SDK (framework-agnostic, GraphQL transport, tool registry)
|
|
117
117
|
- [`@memberjunction/ng-agent-client`](../agent-client/README.md) — Angular wrapper for the agent SDK
|
|
118
|
-
- [`@memberjunction/core-entities`](../../../MJCoreEntities/
|
|
118
|
+
- [`@memberjunction/core-entities`](../../../MJCoreEntities/readme.md) — ConversationEngine for centralized conversation data
|
|
119
119
|
|
|
120
120
|
### Message Components
|
|
121
121
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { EventEmitter, OnInit } from '@angular/core';
|
|
2
|
+
import type { UserInfo } from '@memberjunction/core';
|
|
3
|
+
import type { MJAIAgentEntityExtended } from '@memberjunction/ai-core-plus';
|
|
4
|
+
import type { MJConversationEntity } from '@memberjunction/core-entities';
|
|
5
|
+
import * as i0 from "@angular/core";
|
|
6
|
+
/**
|
|
7
|
+
* Header widget that lets a user pin a default AI agent on the active
|
|
8
|
+
* conversation. The pinned agent (saved to
|
|
9
|
+
* `MJConversationEntity.DefaultAgentID`) takes precedence over the
|
|
10
|
+
* embedder-supplied default in `MessageInputComponent.routeMessage()` —
|
|
11
|
+
* non-mention messages route to it instead of Sage.
|
|
12
|
+
*
|
|
13
|
+
* Eligible agents are top-level, Active, non-Sub-Agent rows from
|
|
14
|
+
* `AIEngineBase.Instance.Agents`. The widget renders as a compact button
|
|
15
|
+
* showing the current pin (or "Auto" when nothing is pinned). Clicking
|
|
16
|
+
* opens an inline list with a "Clear" option to remove the pin.
|
|
17
|
+
*
|
|
18
|
+
* The host controls visibility via `<mj-conversation-chat-area>`'s
|
|
19
|
+
* `[showAgentPicker]` input (default true). Surfaces that don't want this
|
|
20
|
+
* UX (e.g. agent-less embed) set it to false.
|
|
21
|
+
*/
|
|
22
|
+
export declare class ConversationAgentPickerComponent implements OnInit {
|
|
23
|
+
/** The conversation whose `DefaultAgentID` this widget edits. */
|
|
24
|
+
Conversation: MJConversationEntity | null;
|
|
25
|
+
/** Required for the Save() audit context. */
|
|
26
|
+
CurrentUser: UserInfo | null;
|
|
27
|
+
/** Disable the picker (e.g. read-only conversation). */
|
|
28
|
+
Disabled: boolean;
|
|
29
|
+
/** Emitted after a successful save with the new (or null) agent ID. */
|
|
30
|
+
AgentChanged: EventEmitter<string | null>;
|
|
31
|
+
IsOpen: boolean;
|
|
32
|
+
EligibleAgents: MJAIAgentEntityExtended[];
|
|
33
|
+
private readonly cdr;
|
|
34
|
+
private readonly notifications;
|
|
35
|
+
ngOnInit(): Promise<void>;
|
|
36
|
+
get CurrentAgentLabel(): string;
|
|
37
|
+
get ButtonTitle(): string;
|
|
38
|
+
IconClassFor(agent: MJAIAgentEntityExtended): string;
|
|
39
|
+
IsSelected(agent: MJAIAgentEntityExtended): boolean;
|
|
40
|
+
Toggle(event: MouseEvent): void;
|
|
41
|
+
PickAgent(agent: MJAIAgentEntityExtended | null): Promise<void>;
|
|
42
|
+
/** Close the menu when the user clicks outside the picker. */
|
|
43
|
+
OnDocumentClick(_event: MouseEvent): void;
|
|
44
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<ConversationAgentPickerComponent, never>;
|
|
45
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ConversationAgentPickerComponent, "mj-conversation-agent-picker", never, { "Conversation": { "alias": "Conversation"; "required": false; }; "CurrentUser": { "alias": "CurrentUser"; "required": false; }; "Disabled": { "alias": "Disabled"; "required": false; }; }, { "AgentChanged": "AgentChanged"; }, never, never, false, never>;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=conversation-agent-picker.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-agent-picker.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/conversation/conversation-agent-picker.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAIH,YAAY,EAGZ,MAAM,EAGT,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AAC5E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;;AAI1E;;;;;;;;;;;;;;;GAeG;AACH,qBAyFa,gCAAiC,YAAW,MAAM;IAC3D,iEAAiE;IACxD,YAAY,EAAE,oBAAoB,GAAG,IAAI,CAAQ;IAE1D,6CAA6C;IACpC,WAAW,EAAE,QAAQ,GAAG,IAAI,CAAQ;IAE7C,wDAAwD;IAC/C,QAAQ,EAAE,OAAO,CAAS;IAEnC,uEAAuE;IAC7D,YAAY,8BAAqC;IAEpD,MAAM,UAAS;IACf,cAAc,EAAE,uBAAuB,EAAE,CAAM;IAEtD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA6B;IACjD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAiC;IAElD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBtC,IAAW,iBAAiB,IAAI,MAAM,CAUrC;IAED,IAAW,WAAW,IAAI,MAAM,CAI/B;IAEM,YAAY,CAAC,KAAK,EAAE,uBAAuB,GAAG,MAAM;IAIpD,UAAU,CAAC,KAAK,EAAE,uBAAuB,GAAG,OAAO;IAKnD,MAAM,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAOzB,SAAS,CAAC,KAAK,EAAE,uBAAuB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IA6B5E,8DAA8D;IAEvD,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;yCAvGvC,gCAAgC;2CAAhC,gCAAgC;CA6G5C"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, HostListener, Input, Output, inject, } from '@angular/core';
|
|
2
|
+
import { LogError } from '@memberjunction/core';
|
|
3
|
+
import { AIEngineBase } from '@memberjunction/ai-engine-base';
|
|
4
|
+
import { MJNotificationService } from '@memberjunction/ng-notifications';
|
|
5
|
+
import { UUIDsEqual } from '@memberjunction/global';
|
|
6
|
+
import * as i0 from "@angular/core";
|
|
7
|
+
const _forTrack0 = ($index, $item) => $item.ID;
|
|
8
|
+
function ConversationAgentPickerComponent_Conditional_6_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
9
|
+
i0.ɵɵelementStart(0, "div", 10);
|
|
10
|
+
i0.ɵɵtext(1, "No agents available");
|
|
11
|
+
i0.ɵɵelementEnd();
|
|
12
|
+
} }
|
|
13
|
+
function ConversationAgentPickerComponent_Conditional_6_Conditional_8_For_1_Template(rf, ctx) { if (rf & 1) {
|
|
14
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
15
|
+
i0.ɵɵelementStart(0, "button", 12);
|
|
16
|
+
i0.ɵɵlistener("click", function ConversationAgentPickerComponent_Conditional_6_Conditional_8_For_1_Template_button_click_0_listener() { const agent_r4 = i0.ɵɵrestoreView(_r3).$implicit; const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.PickAgent(agent_r4)); });
|
|
17
|
+
i0.ɵɵelement(1, "i");
|
|
18
|
+
i0.ɵɵelementStart(2, "span");
|
|
19
|
+
i0.ɵɵtext(3);
|
|
20
|
+
i0.ɵɵelementEnd()();
|
|
21
|
+
} if (rf & 2) {
|
|
22
|
+
const agent_r4 = ctx.$implicit;
|
|
23
|
+
const ctx_r1 = i0.ɵɵnextContext(3);
|
|
24
|
+
i0.ɵɵclassProp("mj-cv-agent-picker__item--selected", ctx_r1.IsSelected(agent_r4));
|
|
25
|
+
i0.ɵɵproperty("title", agent_r4.Description || agent_r4.Name);
|
|
26
|
+
i0.ɵɵadvance();
|
|
27
|
+
i0.ɵɵclassMap(ctx_r1.IconClassFor(agent_r4));
|
|
28
|
+
i0.ɵɵadvance(2);
|
|
29
|
+
i0.ɵɵtextInterpolate(agent_r4.Name);
|
|
30
|
+
} }
|
|
31
|
+
function ConversationAgentPickerComponent_Conditional_6_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
32
|
+
i0.ɵɵrepeaterCreate(0, ConversationAgentPickerComponent_Conditional_6_Conditional_8_For_1_Template, 4, 6, "button", 11, _forTrack0);
|
|
33
|
+
} if (rf & 2) {
|
|
34
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
35
|
+
i0.ɵɵrepeater(ctx_r1.EligibleAgents);
|
|
36
|
+
} }
|
|
37
|
+
function ConversationAgentPickerComponent_Conditional_6_Template(rf, ctx) { if (rf & 1) {
|
|
38
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
39
|
+
i0.ɵɵelementStart(0, "div", 6);
|
|
40
|
+
i0.ɵɵlistener("click", function ConversationAgentPickerComponent_Conditional_6_Template_div_click_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
41
|
+
i0.ɵɵelementStart(1, "div", 7);
|
|
42
|
+
i0.ɵɵtext(2, "Default agent for this conversation");
|
|
43
|
+
i0.ɵɵelementEnd();
|
|
44
|
+
i0.ɵɵelementStart(3, "button", 8);
|
|
45
|
+
i0.ɵɵlistener("click", function ConversationAgentPickerComponent_Conditional_6_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.PickAgent(null)); });
|
|
46
|
+
i0.ɵɵelement(4, "i", 9);
|
|
47
|
+
i0.ɵɵelementStart(5, "span");
|
|
48
|
+
i0.ɵɵtext(6, "Auto (use default)");
|
|
49
|
+
i0.ɵɵelementEnd()();
|
|
50
|
+
i0.ɵɵconditionalCreate(7, ConversationAgentPickerComponent_Conditional_6_Conditional_7_Template, 2, 0, "div", 10)(8, ConversationAgentPickerComponent_Conditional_6_Conditional_8_Template, 2, 0);
|
|
51
|
+
i0.ɵɵelementEnd();
|
|
52
|
+
} if (rf & 2) {
|
|
53
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
54
|
+
i0.ɵɵadvance(3);
|
|
55
|
+
i0.ɵɵclassProp("mj-cv-agent-picker__item--selected", !(ctx_r1.Conversation == null ? null : ctx_r1.Conversation.DefaultAgentID));
|
|
56
|
+
i0.ɵɵadvance(4);
|
|
57
|
+
i0.ɵɵconditional(ctx_r1.EligibleAgents.length === 0 ? 7 : 8);
|
|
58
|
+
} }
|
|
59
|
+
/**
|
|
60
|
+
* Header widget that lets a user pin a default AI agent on the active
|
|
61
|
+
* conversation. The pinned agent (saved to
|
|
62
|
+
* `MJConversationEntity.DefaultAgentID`) takes precedence over the
|
|
63
|
+
* embedder-supplied default in `MessageInputComponent.routeMessage()` —
|
|
64
|
+
* non-mention messages route to it instead of Sage.
|
|
65
|
+
*
|
|
66
|
+
* Eligible agents are top-level, Active, non-Sub-Agent rows from
|
|
67
|
+
* `AIEngineBase.Instance.Agents`. The widget renders as a compact button
|
|
68
|
+
* showing the current pin (or "Auto" when nothing is pinned). Clicking
|
|
69
|
+
* opens an inline list with a "Clear" option to remove the pin.
|
|
70
|
+
*
|
|
71
|
+
* The host controls visibility via `<mj-conversation-chat-area>`'s
|
|
72
|
+
* `[showAgentPicker]` input (default true). Surfaces that don't want this
|
|
73
|
+
* UX (e.g. agent-less embed) set it to false.
|
|
74
|
+
*/
|
|
75
|
+
export class ConversationAgentPickerComponent {
|
|
76
|
+
/** The conversation whose `DefaultAgentID` this widget edits. */
|
|
77
|
+
Conversation = null;
|
|
78
|
+
/** Required for the Save() audit context. */
|
|
79
|
+
CurrentUser = null;
|
|
80
|
+
/** Disable the picker (e.g. read-only conversation). */
|
|
81
|
+
Disabled = false;
|
|
82
|
+
/** Emitted after a successful save with the new (or null) agent ID. */
|
|
83
|
+
AgentChanged = new EventEmitter();
|
|
84
|
+
IsOpen = false;
|
|
85
|
+
EligibleAgents = [];
|
|
86
|
+
cdr = inject(ChangeDetectorRef);
|
|
87
|
+
notifications = inject(MJNotificationService);
|
|
88
|
+
async ngOnInit() {
|
|
89
|
+
try {
|
|
90
|
+
// AIEngineBase is the live, cached agent catalog. Ensure it's
|
|
91
|
+
// loaded — most app shells warm it up at boot, but the picker
|
|
92
|
+
// could mount before that completes.
|
|
93
|
+
await AIEngineBase.Instance.Config(false);
|
|
94
|
+
this.EligibleAgents = (AIEngineBase.Instance.Agents ?? [])
|
|
95
|
+
.filter(a => !a.ParentID &&
|
|
96
|
+
a.Status === 'Active' &&
|
|
97
|
+
a.InvocationMode !== 'Sub-Agent')
|
|
98
|
+
.sort((a, b) => (a.Name ?? '').localeCompare(b.Name ?? ''));
|
|
99
|
+
this.cdr.markForCheck();
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
LogError(`ConversationAgentPicker.ngOnInit: ${err instanceof Error ? err.message : String(err)}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
get CurrentAgentLabel() {
|
|
106
|
+
const id = this.Conversation?.DefaultAgentID;
|
|
107
|
+
if (!id)
|
|
108
|
+
return 'Auto';
|
|
109
|
+
const match = this.EligibleAgents.find(a => UUIDsEqual(a.ID, id));
|
|
110
|
+
// If the pinned agent is no longer eligible (deactivated, demoted to
|
|
111
|
+
// sub-agent), still surface its name from the cache so the user can
|
|
112
|
+
// see what was pinned even though they can't re-pin it.
|
|
113
|
+
const fromCache = match
|
|
114
|
+
?? (AIEngineBase.Instance.Agents ?? []).find(a => UUIDsEqual(a.ID, id));
|
|
115
|
+
return fromCache?.Name ?? 'Pinned agent';
|
|
116
|
+
}
|
|
117
|
+
get ButtonTitle() {
|
|
118
|
+
return this.Conversation?.DefaultAgentID
|
|
119
|
+
? `This conversation routes to ${this.CurrentAgentLabel}. Click to change.`
|
|
120
|
+
: 'No agent pinned — messages route via the standard rules. Click to pin an agent.';
|
|
121
|
+
}
|
|
122
|
+
IconClassFor(agent) {
|
|
123
|
+
return agent.IconClass?.trim() || 'fa-solid fa-robot';
|
|
124
|
+
}
|
|
125
|
+
IsSelected(agent) {
|
|
126
|
+
return !!this.Conversation?.DefaultAgentID
|
|
127
|
+
&& UUIDsEqual(agent.ID, this.Conversation.DefaultAgentID);
|
|
128
|
+
}
|
|
129
|
+
Toggle(event) {
|
|
130
|
+
event.stopPropagation();
|
|
131
|
+
if (this.Disabled || !this.Conversation)
|
|
132
|
+
return;
|
|
133
|
+
this.IsOpen = !this.IsOpen;
|
|
134
|
+
this.cdr.markForCheck();
|
|
135
|
+
}
|
|
136
|
+
async PickAgent(agent) {
|
|
137
|
+
this.IsOpen = false;
|
|
138
|
+
const conv = this.Conversation;
|
|
139
|
+
if (!conv)
|
|
140
|
+
return;
|
|
141
|
+
const newId = agent?.ID ?? null;
|
|
142
|
+
if (conv.DefaultAgentID === newId) {
|
|
143
|
+
this.cdr.markForCheck();
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const prev = conv.DefaultAgentID;
|
|
147
|
+
conv.DefaultAgentID = newId;
|
|
148
|
+
try {
|
|
149
|
+
const saved = await conv.Save();
|
|
150
|
+
if (!saved) {
|
|
151
|
+
conv.DefaultAgentID = prev;
|
|
152
|
+
this.notifications.CreateSimpleNotification(conv.LatestResult?.CompleteMessage ?? 'Failed to update conversation default agent.', 'error', 4500);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
this.AgentChanged.emit(newId);
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
conv.DefaultAgentID = prev;
|
|
159
|
+
LogError(`ConversationAgentPicker.PickAgent: ${err instanceof Error ? err.message : String(err)}`);
|
|
160
|
+
}
|
|
161
|
+
finally {
|
|
162
|
+
this.cdr.markForCheck();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/** Close the menu when the user clicks outside the picker. */
|
|
166
|
+
OnDocumentClick(_event) {
|
|
167
|
+
if (this.IsOpen) {
|
|
168
|
+
this.IsOpen = false;
|
|
169
|
+
this.cdr.markForCheck();
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
static ɵfac = function ConversationAgentPickerComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || ConversationAgentPickerComponent)(); };
|
|
173
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: ConversationAgentPickerComponent, selectors: [["mj-conversation-agent-picker"]], hostBindings: function ConversationAgentPickerComponent_HostBindings(rf, ctx) { if (rf & 1) {
|
|
174
|
+
i0.ɵɵlistener("click", function ConversationAgentPickerComponent_click_HostBindingHandler($event) { return ctx.OnDocumentClick($event); }, i0.ɵɵresolveDocument);
|
|
175
|
+
} }, inputs: { Conversation: "Conversation", CurrentUser: "CurrentUser", Disabled: "Disabled" }, outputs: { AgentChanged: "AgentChanged" }, standalone: false, decls: 7, vars: 6, consts: [[1, "mj-cv-agent-picker"], ["type", "button", 1, "mj-cv-agent-picker__trigger", 3, "click", "title", "disabled"], [1, "fa-solid", "fa-robot"], [1, "mj-cv-agent-picker__label"], [1, "fa-solid", "fa-caret-down", "mj-cv-agent-picker__caret"], [1, "mj-cv-agent-picker__menu"], [1, "mj-cv-agent-picker__menu", 3, "click"], [1, "mj-cv-agent-picker__menu-header"], ["type", "button", 1, "mj-cv-agent-picker__item", 3, "click"], [1, "fa-solid", "fa-wand-magic-sparkles"], [1, "mj-cv-agent-picker__empty"], ["type", "button", 1, "mj-cv-agent-picker__item", 3, "mj-cv-agent-picker__item--selected", "title"], ["type", "button", 1, "mj-cv-agent-picker__item", 3, "click", "title"]], template: function ConversationAgentPickerComponent_Template(rf, ctx) { if (rf & 1) {
|
|
176
|
+
i0.ɵɵelementStart(0, "div", 0)(1, "button", 1);
|
|
177
|
+
i0.ɵɵlistener("click", function ConversationAgentPickerComponent_Template_button_click_1_listener($event) { return ctx.Toggle($event); });
|
|
178
|
+
i0.ɵɵelement(2, "i", 2);
|
|
179
|
+
i0.ɵɵelementStart(3, "span", 3);
|
|
180
|
+
i0.ɵɵtext(4);
|
|
181
|
+
i0.ɵɵelementEnd();
|
|
182
|
+
i0.ɵɵelement(5, "i", 4);
|
|
183
|
+
i0.ɵɵelementEnd();
|
|
184
|
+
i0.ɵɵconditionalCreate(6, ConversationAgentPickerComponent_Conditional_6_Template, 9, 3, "div", 5);
|
|
185
|
+
i0.ɵɵelementEnd();
|
|
186
|
+
} if (rf & 2) {
|
|
187
|
+
i0.ɵɵclassProp("mj-cv-agent-picker--open", ctx.IsOpen);
|
|
188
|
+
i0.ɵɵadvance();
|
|
189
|
+
i0.ɵɵproperty("title", ctx.ButtonTitle)("disabled", ctx.Disabled || !ctx.Conversation);
|
|
190
|
+
i0.ɵɵadvance(3);
|
|
191
|
+
i0.ɵɵtextInterpolate(ctx.CurrentAgentLabel);
|
|
192
|
+
i0.ɵɵadvance(2);
|
|
193
|
+
i0.ɵɵconditional(ctx.IsOpen ? 6 : -1);
|
|
194
|
+
} }, styles: ["\n\n\n\n .mj-cv-agent-picker[_ngcontent-%COMP%] { position: relative; display: inline-flex; align-items: center; }\n .mj-cv-agent-picker__trigger[_ngcontent-%COMP%] {\n display: inline-flex; align-items: center; gap: 6px;\n padding: 6px 12px; min-height: 32px; box-sizing: border-box;\n background: var(--mj-bg-surface-card); border: 1px solid var(--mj-border-default);\n border-radius: 4px; cursor: pointer;\n color: var(--mj-text-primary); font-size: 0.8125rem; line-height: 1.5;\n transition: background 120ms, border-color 120ms;\n }\n .mj-cv-agent-picker__trigger[_ngcontent-%COMP%]:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover); border-color: var(--mj-border-strong);\n }\n .mj-cv-agent-picker__trigger[_ngcontent-%COMP%]:disabled { opacity: 0.5; cursor: not-allowed; }\n .mj-cv-agent-picker--open[_ngcontent-%COMP%] .mj-cv-agent-picker__trigger[_ngcontent-%COMP%] { border-color: var(--mj-border-focus); }\n .mj-cv-agent-picker__label[_ngcontent-%COMP%] { max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .mj-cv-agent-picker__caret[_ngcontent-%COMP%] { font-size: 10px; opacity: 0.7; }\n .mj-cv-agent-picker__menu[_ngcontent-%COMP%] {\n position: absolute; top: calc(100% + 4px); right: 0; z-index: 50;\n min-width: 220px; max-height: 320px; overflow-y: auto;\n background: var(--mj-bg-surface-elevated); border: 1px solid var(--mj-border-default);\n border-radius: 6px; box-shadow: 0 4px 16px rgba(0,0,0,0.12);\n padding: 4px 0;\n }\n .mj-cv-agent-picker__menu-header[_ngcontent-%COMP%] {\n padding: 6px 12px 4px; font-size: 11px; text-transform: uppercase;\n letter-spacing: 0.04em; color: var(--mj-text-muted);\n }\n .mj-cv-agent-picker__item[_ngcontent-%COMP%] {\n display: flex; align-items: center; gap: 8px; width: 100%;\n padding: 6px 12px; background: transparent; border: none; cursor: pointer;\n color: var(--mj-text-primary); font-size: 13px; text-align: left;\n }\n .mj-cv-agent-picker__item[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { width: 14px; text-align: center; color: var(--mj-text-secondary); }\n .mj-cv-agent-picker__item[_ngcontent-%COMP%]:hover { background: var(--mj-bg-surface-hover); }\n .mj-cv-agent-picker__item--selected[_ngcontent-%COMP%] { background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent); }\n .mj-cv-agent-picker__item--selected[_ngcontent-%COMP%] i[_ngcontent-%COMP%] { color: var(--mj-brand-primary); }\n .mj-cv-agent-picker__empty[_ngcontent-%COMP%] { padding: 8px 12px; font-size: 12px; color: var(--mj-text-muted); }"], changeDetection: 0 });
|
|
195
|
+
}
|
|
196
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(ConversationAgentPickerComponent, [{
|
|
197
|
+
type: Component,
|
|
198
|
+
args: [{ standalone: false, selector: 'mj-conversation-agent-picker', changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
199
|
+
<div class="mj-cv-agent-picker" [class.mj-cv-agent-picker--open]="IsOpen">
|
|
200
|
+
<button
|
|
201
|
+
type="button"
|
|
202
|
+
class="mj-cv-agent-picker__trigger"
|
|
203
|
+
[title]="ButtonTitle"
|
|
204
|
+
[disabled]="Disabled || !Conversation"
|
|
205
|
+
(click)="Toggle($event)">
|
|
206
|
+
<i class="fa-solid fa-robot"></i>
|
|
207
|
+
<span class="mj-cv-agent-picker__label">{{ CurrentAgentLabel }}</span>
|
|
208
|
+
<i class="fa-solid fa-caret-down mj-cv-agent-picker__caret"></i>
|
|
209
|
+
</button>
|
|
210
|
+
@if (IsOpen) {
|
|
211
|
+
<div class="mj-cv-agent-picker__menu" (click)="$event.stopPropagation()">
|
|
212
|
+
<div class="mj-cv-agent-picker__menu-header">Default agent for this conversation</div>
|
|
213
|
+
<button
|
|
214
|
+
type="button"
|
|
215
|
+
class="mj-cv-agent-picker__item"
|
|
216
|
+
[class.mj-cv-agent-picker__item--selected]="!Conversation?.DefaultAgentID"
|
|
217
|
+
(click)="PickAgent(null)">
|
|
218
|
+
<i class="fa-solid fa-wand-magic-sparkles"></i>
|
|
219
|
+
<span>Auto (use default)</span>
|
|
220
|
+
</button>
|
|
221
|
+
@if (EligibleAgents.length === 0) {
|
|
222
|
+
<div class="mj-cv-agent-picker__empty">No agents available</div>
|
|
223
|
+
} @else {
|
|
224
|
+
@for (agent of EligibleAgents; track agent.ID) {
|
|
225
|
+
<button
|
|
226
|
+
type="button"
|
|
227
|
+
class="mj-cv-agent-picker__item"
|
|
228
|
+
[class.mj-cv-agent-picker__item--selected]="IsSelected(agent)"
|
|
229
|
+
[title]="agent.Description || agent.Name"
|
|
230
|
+
(click)="PickAgent(agent)">
|
|
231
|
+
<i [class]="IconClassFor(agent)"></i>
|
|
232
|
+
<span>{{ agent.Name }}</span>
|
|
233
|
+
</button>
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
</div>
|
|
237
|
+
}
|
|
238
|
+
</div>
|
|
239
|
+
`, styles: ["\n /* Match mjButton size=\"sm\" so the trigger lines up with neighboring\n Export / Share buttons: min-height 32px, 6px/12px padding,\n 0.8125rem font. Same border-radius as the other chrome buttons. */\n .mj-cv-agent-picker { position: relative; display: inline-flex; align-items: center; }\n .mj-cv-agent-picker__trigger {\n display: inline-flex; align-items: center; gap: 6px;\n padding: 6px 12px; min-height: 32px; box-sizing: border-box;\n background: var(--mj-bg-surface-card); border: 1px solid var(--mj-border-default);\n border-radius: 4px; cursor: pointer;\n color: var(--mj-text-primary); font-size: 0.8125rem; line-height: 1.5;\n transition: background 120ms, border-color 120ms;\n }\n .mj-cv-agent-picker__trigger:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover); border-color: var(--mj-border-strong);\n }\n .mj-cv-agent-picker__trigger:disabled { opacity: 0.5; cursor: not-allowed; }\n .mj-cv-agent-picker--open .mj-cv-agent-picker__trigger { border-color: var(--mj-border-focus); }\n .mj-cv-agent-picker__label { max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .mj-cv-agent-picker__caret { font-size: 10px; opacity: 0.7; }\n .mj-cv-agent-picker__menu {\n position: absolute; top: calc(100% + 4px); right: 0; z-index: 50;\n min-width: 220px; max-height: 320px; overflow-y: auto;\n background: var(--mj-bg-surface-elevated); border: 1px solid var(--mj-border-default);\n border-radius: 6px; box-shadow: 0 4px 16px rgba(0,0,0,0.12);\n padding: 4px 0;\n }\n .mj-cv-agent-picker__menu-header {\n padding: 6px 12px 4px; font-size: 11px; text-transform: uppercase;\n letter-spacing: 0.04em; color: var(--mj-text-muted);\n }\n .mj-cv-agent-picker__item {\n display: flex; align-items: center; gap: 8px; width: 100%;\n padding: 6px 12px; background: transparent; border: none; cursor: pointer;\n color: var(--mj-text-primary); font-size: 13px; text-align: left;\n }\n .mj-cv-agent-picker__item i { width: 14px; text-align: center; color: var(--mj-text-secondary); }\n .mj-cv-agent-picker__item:hover { background: var(--mj-bg-surface-hover); }\n .mj-cv-agent-picker__item--selected { background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent); }\n .mj-cv-agent-picker__item--selected i { color: var(--mj-brand-primary); }\n .mj-cv-agent-picker__empty { padding: 8px 12px; font-size: 12px; color: var(--mj-text-muted); }\n "] }]
|
|
240
|
+
}], null, { Conversation: [{
|
|
241
|
+
type: Input
|
|
242
|
+
}], CurrentUser: [{
|
|
243
|
+
type: Input
|
|
244
|
+
}], Disabled: [{
|
|
245
|
+
type: Input
|
|
246
|
+
}], AgentChanged: [{
|
|
247
|
+
type: Output
|
|
248
|
+
}], OnDocumentClick: [{
|
|
249
|
+
type: HostListener,
|
|
250
|
+
args: ['document:click', ['$event']]
|
|
251
|
+
}] }); })();
|
|
252
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(ConversationAgentPickerComponent, { className: "ConversationAgentPickerComponent", filePath: "src/lib/components/conversation/conversation-agent-picker.component.ts", lineNumber: 125 }); })();
|
|
253
|
+
//# sourceMappingURL=conversation-agent-picker.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-agent-picker.component.js","sourceRoot":"","sources":["../../../../src/lib/components/conversation/conversation-agent-picker.component.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy,\n ChangeDetectorRef,\n Component,\n EventEmitter,\n HostListener,\n Input,\n OnInit,\n Output,\n inject,\n} from '@angular/core';\nimport { LogError } from '@memberjunction/core';\nimport type { UserInfo } from '@memberjunction/core';\nimport { AIEngineBase } from '@memberjunction/ai-engine-base';\nimport type { MJAIAgentEntityExtended } from '@memberjunction/ai-core-plus';\nimport type { MJConversationEntity } from '@memberjunction/core-entities';\nimport { MJNotificationService } from '@memberjunction/ng-notifications';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Header widget that lets a user pin a default AI agent on the active\n * conversation. The pinned agent (saved to\n * `MJConversationEntity.DefaultAgentID`) takes precedence over the\n * embedder-supplied default in `MessageInputComponent.routeMessage()` —\n * non-mention messages route to it instead of Sage.\n *\n * Eligible agents are top-level, Active, non-Sub-Agent rows from\n * `AIEngineBase.Instance.Agents`. The widget renders as a compact button\n * showing the current pin (or \"Auto\" when nothing is pinned). Clicking\n * opens an inline list with a \"Clear\" option to remove the pin.\n *\n * The host controls visibility via `<mj-conversation-chat-area>`'s\n * `[showAgentPicker]` input (default true). Surfaces that don't want this\n * UX (e.g. agent-less embed) set it to false.\n */\n@Component({\n standalone: false,\n selector: 'mj-conversation-agent-picker',\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <div class=\"mj-cv-agent-picker\" [class.mj-cv-agent-picker--open]=\"IsOpen\">\n <button\n type=\"button\"\n class=\"mj-cv-agent-picker__trigger\"\n [title]=\"ButtonTitle\"\n [disabled]=\"Disabled || !Conversation\"\n (click)=\"Toggle($event)\">\n <i class=\"fa-solid fa-robot\"></i>\n <span class=\"mj-cv-agent-picker__label\">{{ CurrentAgentLabel }}</span>\n <i class=\"fa-solid fa-caret-down mj-cv-agent-picker__caret\"></i>\n </button>\n @if (IsOpen) {\n <div class=\"mj-cv-agent-picker__menu\" (click)=\"$event.stopPropagation()\">\n <div class=\"mj-cv-agent-picker__menu-header\">Default agent for this conversation</div>\n <button\n type=\"button\"\n class=\"mj-cv-agent-picker__item\"\n [class.mj-cv-agent-picker__item--selected]=\"!Conversation?.DefaultAgentID\"\n (click)=\"PickAgent(null)\">\n <i class=\"fa-solid fa-wand-magic-sparkles\"></i>\n <span>Auto (use default)</span>\n </button>\n @if (EligibleAgents.length === 0) {\n <div class=\"mj-cv-agent-picker__empty\">No agents available</div>\n } @else {\n @for (agent of EligibleAgents; track agent.ID) {\n <button\n type=\"button\"\n class=\"mj-cv-agent-picker__item\"\n [class.mj-cv-agent-picker__item--selected]=\"IsSelected(agent)\"\n [title]=\"agent.Description || agent.Name\"\n (click)=\"PickAgent(agent)\">\n <i [class]=\"IconClassFor(agent)\"></i>\n <span>{{ agent.Name }}</span>\n </button>\n }\n }\n </div>\n }\n </div>\n `,\n styles: [`\n /* Match mjButton size=\"sm\" so the trigger lines up with neighboring\n Export / Share buttons: min-height 32px, 6px/12px padding,\n 0.8125rem font. Same border-radius as the other chrome buttons. */\n .mj-cv-agent-picker { position: relative; display: inline-flex; align-items: center; }\n .mj-cv-agent-picker__trigger {\n display: inline-flex; align-items: center; gap: 6px;\n padding: 6px 12px; min-height: 32px; box-sizing: border-box;\n background: var(--mj-bg-surface-card); border: 1px solid var(--mj-border-default);\n border-radius: 4px; cursor: pointer;\n color: var(--mj-text-primary); font-size: 0.8125rem; line-height: 1.5;\n transition: background 120ms, border-color 120ms;\n }\n .mj-cv-agent-picker__trigger:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover); border-color: var(--mj-border-strong);\n }\n .mj-cv-agent-picker__trigger:disabled { opacity: 0.5; cursor: not-allowed; }\n .mj-cv-agent-picker--open .mj-cv-agent-picker__trigger { border-color: var(--mj-border-focus); }\n .mj-cv-agent-picker__label { max-width: 160px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n .mj-cv-agent-picker__caret { font-size: 10px; opacity: 0.7; }\n .mj-cv-agent-picker__menu {\n position: absolute; top: calc(100% + 4px); right: 0; z-index: 50;\n min-width: 220px; max-height: 320px; overflow-y: auto;\n background: var(--mj-bg-surface-elevated); border: 1px solid var(--mj-border-default);\n border-radius: 6px; box-shadow: 0 4px 16px rgba(0,0,0,0.12);\n padding: 4px 0;\n }\n .mj-cv-agent-picker__menu-header {\n padding: 6px 12px 4px; font-size: 11px; text-transform: uppercase;\n letter-spacing: 0.04em; color: var(--mj-text-muted);\n }\n .mj-cv-agent-picker__item {\n display: flex; align-items: center; gap: 8px; width: 100%;\n padding: 6px 12px; background: transparent; border: none; cursor: pointer;\n color: var(--mj-text-primary); font-size: 13px; text-align: left;\n }\n .mj-cv-agent-picker__item i { width: 14px; text-align: center; color: var(--mj-text-secondary); }\n .mj-cv-agent-picker__item:hover { background: var(--mj-bg-surface-hover); }\n .mj-cv-agent-picker__item--selected { background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent); }\n .mj-cv-agent-picker__item--selected i { color: var(--mj-brand-primary); }\n .mj-cv-agent-picker__empty { padding: 8px 12px; font-size: 12px; color: var(--mj-text-muted); }\n `]\n})\nexport class ConversationAgentPickerComponent implements OnInit {\n /** The conversation whose `DefaultAgentID` this widget edits. */\n @Input() Conversation: MJConversationEntity | null = null;\n\n /** Required for the Save() audit context. */\n @Input() CurrentUser: UserInfo | null = null;\n\n /** Disable the picker (e.g. read-only conversation). */\n @Input() Disabled: boolean = false;\n\n /** Emitted after a successful save with the new (or null) agent ID. */\n @Output() AgentChanged = new EventEmitter<string | null>();\n\n public IsOpen = false;\n public EligibleAgents: MJAIAgentEntityExtended[] = [];\n\n private readonly cdr = inject(ChangeDetectorRef);\n private readonly notifications = inject(MJNotificationService);\n\n public async ngOnInit(): Promise<void> {\n try {\n // AIEngineBase is the live, cached agent catalog. Ensure it's\n // loaded — most app shells warm it up at boot, but the picker\n // could mount before that completes.\n await AIEngineBase.Instance.Config(false);\n this.EligibleAgents = (AIEngineBase.Instance.Agents ?? [])\n .filter(a =>\n !a.ParentID &&\n a.Status === 'Active' &&\n a.InvocationMode !== 'Sub-Agent'\n )\n .sort((a, b) => (a.Name ?? '').localeCompare(b.Name ?? ''));\n this.cdr.markForCheck();\n } catch (err) {\n LogError(`ConversationAgentPicker.ngOnInit: ${err instanceof Error ? err.message : String(err)}`);\n }\n }\n\n public get CurrentAgentLabel(): string {\n const id = this.Conversation?.DefaultAgentID;\n if (!id) return 'Auto';\n const match = this.EligibleAgents.find(a => UUIDsEqual(a.ID, id));\n // If the pinned agent is no longer eligible (deactivated, demoted to\n // sub-agent), still surface its name from the cache so the user can\n // see what was pinned even though they can't re-pin it.\n const fromCache = match\n ?? (AIEngineBase.Instance.Agents ?? []).find(a => UUIDsEqual(a.ID, id));\n return fromCache?.Name ?? 'Pinned agent';\n }\n\n public get ButtonTitle(): string {\n return this.Conversation?.DefaultAgentID\n ? `This conversation routes to ${this.CurrentAgentLabel}. Click to change.`\n : 'No agent pinned — messages route via the standard rules. Click to pin an agent.';\n }\n\n public IconClassFor(agent: MJAIAgentEntityExtended): string {\n return agent.IconClass?.trim() || 'fa-solid fa-robot';\n }\n\n public IsSelected(agent: MJAIAgentEntityExtended): boolean {\n return !!this.Conversation?.DefaultAgentID\n && UUIDsEqual(agent.ID, this.Conversation.DefaultAgentID);\n }\n\n public Toggle(event: MouseEvent): void {\n event.stopPropagation();\n if (this.Disabled || !this.Conversation) return;\n this.IsOpen = !this.IsOpen;\n this.cdr.markForCheck();\n }\n\n public async PickAgent(agent: MJAIAgentEntityExtended | null): Promise<void> {\n this.IsOpen = false;\n const conv = this.Conversation;\n if (!conv) return;\n const newId = agent?.ID ?? null;\n if (conv.DefaultAgentID === newId) {\n this.cdr.markForCheck();\n return;\n }\n const prev = conv.DefaultAgentID;\n conv.DefaultAgentID = newId;\n try {\n const saved = await conv.Save();\n if (!saved) {\n conv.DefaultAgentID = prev;\n this.notifications.CreateSimpleNotification(\n conv.LatestResult?.CompleteMessage ?? 'Failed to update conversation default agent.',\n 'error', 4500);\n return;\n }\n this.AgentChanged.emit(newId);\n } catch (err) {\n conv.DefaultAgentID = prev;\n LogError(`ConversationAgentPicker.PickAgent: ${err instanceof Error ? err.message : String(err)}`);\n } finally {\n this.cdr.markForCheck();\n }\n }\n\n /** Close the menu when the user clicks outside the picker. */\n @HostListener('document:click', ['$event'])\n public OnDocumentClick(_event: MouseEvent): void {\n if (this.IsOpen) {\n this.IsOpen = false;\n this.cdr.markForCheck();\n }\n }\n}\n"]}
|
|
@@ -3,6 +3,7 @@ import { BaseAngularComponent } from '@memberjunction/ng-base-types';
|
|
|
3
3
|
import { UserInfo, CompositeKey, DataSnapshot } from '@memberjunction/core';
|
|
4
4
|
import { MJConversationEntity, MJConversationDetailEntity, MJAIAgentRunEntity, MJArtifactEntity, MJTaskEntity, RatingJSON } from '@memberjunction/core-entities';
|
|
5
5
|
import { MJAIAgentRunEntityExtended } from "@memberjunction/ai-core-plus";
|
|
6
|
+
import { UICommandHandlerService } from '../../services/ui-command-handler.service';
|
|
6
7
|
import { AgentStateService } from '../../services/agent-state.service';
|
|
7
8
|
import { ConversationAgentService } from '../../services/conversation-agent.service';
|
|
8
9
|
import { ActiveTasksService } from '../../services/active-tasks.service';
|
|
@@ -13,7 +14,7 @@ import { MJResourcePermissionShareAdapter, ResourceShareContext } from '@memberj
|
|
|
13
14
|
import { MessageAttachment } from '../message/message-item.component';
|
|
14
15
|
import { LazyArtifactInfo } from '../../models/lazy-artifact-info';
|
|
15
16
|
import { PendingAttachment } from '../mention/mention-editor.component';
|
|
16
|
-
import { NavigationRequest, AnalyzeArtifactService } from '@memberjunction/ng-artifacts';
|
|
17
|
+
import { NavigationRequest, AnalyzeArtifactService, InteractiveFormApplyService } from '@memberjunction/ng-artifacts';
|
|
17
18
|
import { TestFeedbackDialogData, TestFeedbackDialogResult } from '@memberjunction/ng-testing';
|
|
18
19
|
import { DialogService as ConversationsDialogService } from '../../services/dialog.service';
|
|
19
20
|
import { Subject } from 'rxjs';
|
|
@@ -32,6 +33,8 @@ export declare class ConversationChatAreaComponent extends BaseAngularComponent
|
|
|
32
33
|
private confirmDialog;
|
|
33
34
|
private bridge;
|
|
34
35
|
private analyzeArtifactService;
|
|
36
|
+
private uiCommandHandler;
|
|
37
|
+
private interactiveFormApplyService;
|
|
35
38
|
environmentId: string;
|
|
36
39
|
currentUser: UserInfo;
|
|
37
40
|
private _conversationId;
|
|
@@ -39,6 +42,30 @@ export declare class ConversationChatAreaComponent extends BaseAngularComponent
|
|
|
39
42
|
get conversationId(): string | null;
|
|
40
43
|
conversation: MJConversationEntity | null;
|
|
41
44
|
threadId: string | null;
|
|
45
|
+
/**
|
|
46
|
+
* When true, render the normal message-list + message-input layout even
|
|
47
|
+
* before a conversation exists, instead of the centered empty-state
|
|
48
|
+
* welcome card. Lets host pages (e.g. Form Builder cockpit) put the chat
|
|
49
|
+
* header + mode picker front-and-center on first open and let the user
|
|
50
|
+
* pick a mode before typing. The first send still routes through
|
|
51
|
+
* MessageInputComponent and triggers conversationCreated as usual.
|
|
52
|
+
*/
|
|
53
|
+
suppressNewConversationEmptyState: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Host-level cap for @-mention autocomplete (agents and users).
|
|
56
|
+
* Defaults true. Hosts addressing a single fixed agent (e.g. Form Builder
|
|
57
|
+
* cockpit pinned to the Form Builder agent) should set false so the user
|
|
58
|
+
* can't accidentally redirect a turn to a different agent.
|
|
59
|
+
*/
|
|
60
|
+
allowMentions: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Host-level cap for attachments. Defaults true. When false, the host
|
|
63
|
+
* disables attachments regardless of agent modality support — useful for
|
|
64
|
+
* surfaces where attachments don't make sense (cockpit text-only flows).
|
|
65
|
+
* When true (default), attachment availability still depends on the
|
|
66
|
+
* agent's modality support, computed at runtime.
|
|
67
|
+
*/
|
|
68
|
+
allowAttachments: boolean;
|
|
42
69
|
private _isNewConversation;
|
|
43
70
|
set isNewConversation(value: boolean);
|
|
44
71
|
get isNewConversation(): boolean;
|
|
@@ -60,6 +87,103 @@ export declare class ConversationChatAreaComponent extends BaseAngularComponent
|
|
|
60
87
|
showArtifactIndicator: boolean;
|
|
61
88
|
/** Application context snapshot for AI agent awareness. Included in agent execution data. */
|
|
62
89
|
appContext: Record<string, unknown> | null;
|
|
90
|
+
/**
|
|
91
|
+
* Optional default agent ID for the conversation. Forwarded to
|
|
92
|
+
* `<mj-message-input>` as its `[defaultAgentId]` so the first message
|
|
93
|
+
* routes directly to this agent instead of Sage. See
|
|
94
|
+
* `MessageInputComponent.routeMessage` priority rules — explicit
|
|
95
|
+
* @mention and prior-agent continuity still take precedence.
|
|
96
|
+
*
|
|
97
|
+
* Embedded chat surfaces (Form Builder cockpit, future domain chats)
|
|
98
|
+
* set this to the specialist agent's ID; the main Chat app leaves it
|
|
99
|
+
* unset to preserve the Sage-fronted UX.
|
|
100
|
+
*/
|
|
101
|
+
defaultAgentId: string | null;
|
|
102
|
+
/**
|
|
103
|
+
* Scope to apply when this surface CREATES a new conversation. Forwarded
|
|
104
|
+
* to `ConversationEngine.CreateConversation` so the new row's
|
|
105
|
+
* `ApplicationScope` column is stamped correctly. Embedded surfaces
|
|
106
|
+
* (e.g. the Form Builder cockpit) set this to `'Application'` so their
|
|
107
|
+
* conversations don't pollute the main Chat app list. Main Chat leaves
|
|
108
|
+
* it as the default `'Global'`. Has no effect on existing conversations.
|
|
109
|
+
*/
|
|
110
|
+
applicationScope: 'Global' | 'Application' | 'Both';
|
|
111
|
+
/**
|
|
112
|
+
* Application ID to bind a newly-created conversation to. REQUIRED when
|
|
113
|
+
* `applicationScope` is 'Application' or 'Both' (DB CHECK constraint
|
|
114
|
+
* enforces it). Used by embedded chat surfaces to scope their
|
|
115
|
+
* conversations to their owning Application.
|
|
116
|
+
*/
|
|
117
|
+
applicationId: string | null;
|
|
118
|
+
/**
|
|
119
|
+
* "What is this conversation about?" — the Entity ID this conversation
|
|
120
|
+
* references. Forwarded to `ConversationEngine.CreateConversation` so
|
|
121
|
+
* the new row's `LinkedEntityID` is stamped at creation time. Paired
|
|
122
|
+
* with {@link linkedRecordId} (DB CHECK requires both populated or both
|
|
123
|
+
* null). Form Builder cockpit passes the MJ: Components entity ID;
|
|
124
|
+
* Component Studio's AI panel does the same. Surfaces use this to
|
|
125
|
+
* later list "prior conversations about THIS form/component."
|
|
126
|
+
* Has no effect on existing conversations.
|
|
127
|
+
*/
|
|
128
|
+
linkedEntityId: string | null;
|
|
129
|
+
/**
|
|
130
|
+
* Primary key of the linked record, serialized as a string. Used with
|
|
131
|
+
* {@link linkedEntityId}. Form Builder cockpit passes the active
|
|
132
|
+
* form's ComponentID; Component Studio's AI panel passes the
|
|
133
|
+
* currently-selected component's ID.
|
|
134
|
+
*/
|
|
135
|
+
linkedRecordId: string | null;
|
|
136
|
+
/**
|
|
137
|
+
* Whether the conversation header should render the per-conversation
|
|
138
|
+
* agent picker. Default true. The picker lets a user pin a default
|
|
139
|
+
* agent on the active conversation (saved to
|
|
140
|
+
* `MJConversationEntity.DefaultAgentID`), so non-mention messages route
|
|
141
|
+
* to that agent instead of through Sage. Surfaces with no meaningful
|
|
142
|
+
* agent-choice UX can set this to false to hide the widget.
|
|
143
|
+
*/
|
|
144
|
+
showAgentPicker: boolean;
|
|
145
|
+
/**
|
|
146
|
+
* Whether the chat header should render the per-agent mode/quality
|
|
147
|
+
* picker (Draft / Standard / High, etc.). Default true. The picker
|
|
148
|
+
* auto-hides when the bound agent has fewer than 2 configured
|
|
149
|
+
* presets, so embedders rarely need to set this explicitly — turn
|
|
150
|
+
* off only when the surface should never expose model-tier choice
|
|
151
|
+
* (kiosks, specialty embeds).
|
|
152
|
+
*/
|
|
153
|
+
showAgentModePicker: boolean;
|
|
154
|
+
/**
|
|
155
|
+
* The mode/preset picker's selected configuration ID, forwarded to
|
|
156
|
+
* `<mj-message-input>` so non-mention routes apply it on the next
|
|
157
|
+
* send. Past messages are NOT retroactively re-routed — the picker
|
|
158
|
+
* only affects subsequent requests. Updated when the user picks a
|
|
159
|
+
* row in the mode picker; the picker itself persists the choice
|
|
160
|
+
* per-user, per-agent via UserInfoEngine.
|
|
161
|
+
*/
|
|
162
|
+
ActiveAgentConfigurationPresetId: string | null;
|
|
163
|
+
/**
|
|
164
|
+
* Agent the mode picker should target. Mirrors the routing precedence
|
|
165
|
+
* minus message-history continuity (the picker is persistent UI; it
|
|
166
|
+
* shouldn't flip as the user scrolls history).
|
|
167
|
+
*
|
|
168
|
+
* Order: conversation-pinned default → embedder default → Sage.
|
|
169
|
+
*/
|
|
170
|
+
/**
|
|
171
|
+
* True when the chat header should render even before a conversation
|
|
172
|
+
* row exists. Currently means: the embedder has enabled the mode
|
|
173
|
+
* picker AND we resolved a target agent for it (so there's actually
|
|
174
|
+
* something to put in the header). Lets surfaces like the Form
|
|
175
|
+
* Builder cockpit show the mode picker on top of the empty-state
|
|
176
|
+
* instead of waiting for the first message to create a conversation.
|
|
177
|
+
*/
|
|
178
|
+
get HasPreConversationHeader(): boolean;
|
|
179
|
+
get ModePickerTargetAgentId(): string | null;
|
|
180
|
+
/**
|
|
181
|
+
* Mode picker emitted a new selection. Store it; the next message's
|
|
182
|
+
* route picks it up via `<mj-message-input>`'s
|
|
183
|
+
* `[agentConfigurationPresetId]` binding. Past messages stay routed
|
|
184
|
+
* as they were — the change is forward-only.
|
|
185
|
+
*/
|
|
186
|
+
OnAgentModePresetChanged(presetId: string | null): void;
|
|
63
187
|
/** Greeting message shown in the empty state when no conversation is active */
|
|
64
188
|
emptyStateGreeting: string;
|
|
65
189
|
showSidebarToggle: boolean;
|
|
@@ -188,7 +312,17 @@ export declare class ConversationChatAreaComponent extends BaseAngularComponent
|
|
|
188
312
|
acceptedFileTypes: string;
|
|
189
313
|
private conversationManagerAgent;
|
|
190
314
|
private engine;
|
|
191
|
-
constructor(agentStateService: AgentStateService, conversationAgentService: ConversationAgentService, activeTasks: ActiveTasksService, cdr: ChangeDetectorRef, mentionAutocompleteService: MentionAutocompleteService, artifactPermissionService: ArtifactPermissionService, attachmentService: ConversationAttachmentService, streamingService: ConversationStreamingService, confirmDialog: ConversationsDialogService, bridge: ConversationBridgeService, analyzeArtifactService: AnalyzeArtifactService);
|
|
315
|
+
constructor(agentStateService: AgentStateService, conversationAgentService: ConversationAgentService, activeTasks: ActiveTasksService, cdr: ChangeDetectorRef, mentionAutocompleteService: MentionAutocompleteService, artifactPermissionService: ArtifactPermissionService, attachmentService: ConversationAttachmentService, streamingService: ConversationStreamingService, confirmDialog: ConversationsDialogService, bridge: ConversationBridgeService, analyzeArtifactService: AnalyzeArtifactService, uiCommandHandler: UICommandHandlerService, interactiveFormApplyService: InteractiveFormApplyService);
|
|
316
|
+
/**
|
|
317
|
+
* Apply a form-role artifact's spec as an EntityFormOverride for the
|
|
318
|
+
* current user. The service handles the Create-vs-Modify decision (based
|
|
319
|
+
* on whether an Active override already exists), confirms via dialog,
|
|
320
|
+
* and surfaces success/failure via notification.
|
|
321
|
+
*/
|
|
322
|
+
OnApplyFormRequested(event: {
|
|
323
|
+
spec: unknown;
|
|
324
|
+
entityName: string;
|
|
325
|
+
}): Promise<void>;
|
|
192
326
|
ngOnInit(): Promise<void>;
|
|
193
327
|
/**
|
|
194
328
|
* Initializes attachment support by checking if the conversation manager agent (Sage)
|
|
@@ -588,7 +722,70 @@ export declare class ConversationChatAreaComponent extends BaseAngularComponent
|
|
|
588
722
|
OnAnalyzeArtifact(event: {
|
|
589
723
|
artifactId: string;
|
|
590
724
|
snapshot: DataSnapshot;
|
|
591
|
-
}): Promise<
|
|
725
|
+
}): Promise<PendingAttachment | null>;
|
|
726
|
+
/**
|
|
727
|
+
* Handle a `client:capture-data-snapshot` actionable command emitted by an
|
|
728
|
+
* analysis-class agent that needs the user's current view of an artifact to
|
|
729
|
+
* answer accurately but has no Data Snapshot artifact attached.
|
|
730
|
+
*
|
|
731
|
+
* Flow:
|
|
732
|
+
* 1. Resolve the target artifact — `command.artifactId` if provided,
|
|
733
|
+
* otherwise the most-recent output artifact on the conversation.
|
|
734
|
+
* 2. Open the artifact viewer panel for it (mounts the viewer plugin if not
|
|
735
|
+
* already mounted).
|
|
736
|
+
* 3. Poll until the viewer can produce a snapshot via
|
|
737
|
+
* `GetCurrentStateSnapshot()`, with a short timeout.
|
|
738
|
+
* 4. Reuse the existing `OnAnalyzeArtifact` flow to persist the snapshot
|
|
739
|
+
* as a Data Snapshot artifact + attach it as a chip on the message input.
|
|
740
|
+
* 5. If `command.followupMessage` is provided, replace the prefill and
|
|
741
|
+
* auto-send so the agent immediately re-runs with the snapshot attached.
|
|
742
|
+
* Otherwise, leave the chip + prefill in place for the user to send manually.
|
|
743
|
+
*
|
|
744
|
+
* Soft-fails — logs a warning and stops on any unrecoverable error rather
|
|
745
|
+
* than throwing. The user's conversation state isn't disrupted.
|
|
746
|
+
*/
|
|
747
|
+
private handleCaptureDataSnapshotCommand;
|
|
748
|
+
/**
|
|
749
|
+
* Poll `artifactViewerComponent.GetCurrentStateSnapshot()` for the LIVE
|
|
750
|
+
* data snapshot. The React component inside the viewer plugin needs several
|
|
751
|
+
* render cycles after `selectedArtifactId` changes before its inner data
|
|
752
|
+
* fetches run and its `getCurrentDataState()` becomes callable via
|
|
753
|
+
* `callbacks.RegisterMethod('getCurrentDataState', ...)`.
|
|
754
|
+
*
|
|
755
|
+
* `GetCurrentStateSnapshot()` returns three distinct shapes:
|
|
756
|
+
* - **Live**: a populated DataSnapshot with `tables[]` whose rows are filled.
|
|
757
|
+
* - **Fallback**: an empty placeholder with only `title` + `interpretation`
|
|
758
|
+
* ("No live data was captured — the component either has no data-fetching
|
|
759
|
+
* hooks or has not yet run its queries"). This fires when the React
|
|
760
|
+
* component hasn't yet registered `getCurrentDataState()`.
|
|
761
|
+
* - **Schema-only**: a structured snapshot with real `tables`/`columns` and
|
|
762
|
+
* metadata (e.g. `totalAvailableRowCount`) but `rows: []`. This is common
|
|
763
|
+
* for query-backed / server-paged components whose data load hasn't
|
|
764
|
+
* completed (or whose visible page is empty) at the moment of capture.
|
|
765
|
+
*
|
|
766
|
+
* We must accept ONLY a snapshot that actually carries rows — a schema-only
|
|
767
|
+
* or placeholder snapshot defeats the point of the pipeline (the analysis
|
|
768
|
+
* agent receives an empty table). So we key "live" on `rows.length`, not just
|
|
769
|
+
* `tables.length`, and keep polling so async/paged data has time to load.
|
|
770
|
+
* Only after timeout do we return the last available row-less snapshot (any
|
|
771
|
+
* structure is better than nothing, but the user will see an empty table in
|
|
772
|
+
* the resulting artifact).
|
|
773
|
+
*/
|
|
774
|
+
private waitForViewerSnapshot;
|
|
775
|
+
/**
|
|
776
|
+
* Find the most-recent Component artifact attached as `Output` to this
|
|
777
|
+
* conversation. Used when a `client:capture-data-snapshot` command arrives
|
|
778
|
+
* without an explicit `artifactId`.
|
|
779
|
+
*
|
|
780
|
+
* Filtering to Component-typed artifacts is intentional even though the
|
|
781
|
+
* command type itself is artifact-generic: the downstream
|
|
782
|
+
* `waitForViewerSnapshot` polling waits for `tables[]` to populate (the
|
|
783
|
+
* shape Components produce via React `getCurrentDataState()`). Falling back
|
|
784
|
+
* to a non-Component artifact would 10s-timeout to a placeholder snapshot.
|
|
785
|
+
* When other artifact types need a usable fallback, generalize the polling
|
|
786
|
+
* first, then drop the filter here.
|
|
787
|
+
*/
|
|
788
|
+
private findMostRecentComponentArtifactId;
|
|
592
789
|
/**
|
|
593
790
|
* Handle close of artifact share modal
|
|
594
791
|
*/
|
|
@@ -643,6 +840,6 @@ export declare class ConversationChatAreaComponent extends BaseAngularComponent
|
|
|
643
840
|
*/
|
|
644
841
|
onIntentCheckCompleted(): void;
|
|
645
842
|
static ɵfac: i0.ɵɵFactoryDeclaration<ConversationChatAreaComponent, never>;
|
|
646
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<ConversationChatAreaComponent, "mj-conversation-chat-area", never, { "environmentId": { "alias": "environmentId"; "required": false; }; "currentUser": { "alias": "currentUser"; "required": false; }; "conversationId": { "alias": "conversationId"; "required": false; }; "conversation": { "alias": "conversation"; "required": false; }; "threadId": { "alias": "threadId"; "required": false; }; "isNewConversation": { "alias": "isNewConversation"; "required": false; }; "pendingMessage": { "alias": "pendingMessage"; "required": false; }; "pendingAttachments": { "alias": "pendingAttachments"; "required": false; }; "pendingArtifactId": { "alias": "pendingArtifactId"; "required": false; }; "pendingArtifactVersionNumber": { "alias": "pendingArtifactVersionNumber"; "required": false; }; "overlayMode": { "alias": "overlayMode"; "required": false; }; "showExportButton": { "alias": "showExportButton"; "required": false; }; "showShareButton": { "alias": "showShareButton"; "required": false; }; "showArtifactIndicator": { "alias": "showArtifactIndicator"; "required": false; }; "appContext": { "alias": "appContext"; "required": false; }; "emptyStateGreeting": { "alias": "emptyStateGreeting"; "required": false; }; "showSidebarToggle": { "alias": "showSidebarToggle"; "required": false; }; }, { "conversationRenamed": "conversationRenamed"; "openEntityRecord": "openEntityRecord"; "navigationRequest": "navigationRequest"; "taskClicked": "taskClicked"; "artifactLinkClicked": "artifactLinkClicked"; "sidebarToggleClicked": "sidebarToggleClicked"; "conversationCreated": "conversationCreated"; "threadOpened": "threadOpened"; "threadClosed": "threadClosed"; "pendingArtifactConsumed": "pendingArtifactConsumed"; "pendingMessageConsumed": "pendingMessageConsumed"; "pendingMessageRequested": "pendingMessageRequested"; }, never, never, false, never>;
|
|
843
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<ConversationChatAreaComponent, "mj-conversation-chat-area", never, { "environmentId": { "alias": "environmentId"; "required": false; }; "currentUser": { "alias": "currentUser"; "required": false; }; "conversationId": { "alias": "conversationId"; "required": false; }; "conversation": { "alias": "conversation"; "required": false; }; "threadId": { "alias": "threadId"; "required": false; }; "suppressNewConversationEmptyState": { "alias": "suppressNewConversationEmptyState"; "required": false; }; "allowMentions": { "alias": "allowMentions"; "required": false; }; "allowAttachments": { "alias": "allowAttachments"; "required": false; }; "isNewConversation": { "alias": "isNewConversation"; "required": false; }; "pendingMessage": { "alias": "pendingMessage"; "required": false; }; "pendingAttachments": { "alias": "pendingAttachments"; "required": false; }; "pendingArtifactId": { "alias": "pendingArtifactId"; "required": false; }; "pendingArtifactVersionNumber": { "alias": "pendingArtifactVersionNumber"; "required": false; }; "overlayMode": { "alias": "overlayMode"; "required": false; }; "showExportButton": { "alias": "showExportButton"; "required": false; }; "showShareButton": { "alias": "showShareButton"; "required": false; }; "showArtifactIndicator": { "alias": "showArtifactIndicator"; "required": false; }; "appContext": { "alias": "appContext"; "required": false; }; "defaultAgentId": { "alias": "defaultAgentId"; "required": false; }; "applicationScope": { "alias": "applicationScope"; "required": false; }; "applicationId": { "alias": "applicationId"; "required": false; }; "linkedEntityId": { "alias": "linkedEntityId"; "required": false; }; "linkedRecordId": { "alias": "linkedRecordId"; "required": false; }; "showAgentPicker": { "alias": "showAgentPicker"; "required": false; }; "showAgentModePicker": { "alias": "showAgentModePicker"; "required": false; }; "emptyStateGreeting": { "alias": "emptyStateGreeting"; "required": false; }; "showSidebarToggle": { "alias": "showSidebarToggle"; "required": false; }; }, { "conversationRenamed": "conversationRenamed"; "openEntityRecord": "openEntityRecord"; "navigationRequest": "navigationRequest"; "taskClicked": "taskClicked"; "artifactLinkClicked": "artifactLinkClicked"; "sidebarToggleClicked": "sidebarToggleClicked"; "conversationCreated": "conversationCreated"; "threadOpened": "threadOpened"; "threadClosed": "threadClosed"; "pendingArtifactConsumed": "pendingArtifactConsumed"; "pendingMessageConsumed": "pendingMessageConsumed"; "pendingMessageRequested": "pendingMessageRequested"; }, never, never, false, never>;
|
|
647
844
|
}
|
|
648
845
|
//# sourceMappingURL=conversation-chat-area.component.d.ts.map
|