@memberjunction/ng-conversations 6.1.0-edge.1 → 6.1.0-edge.2

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.
@@ -1,6 +1,6 @@
1
1
  import { EventEmitter } from '@angular/core';
2
2
  import { IMetadataProvider, UserInfo } from '@memberjunction/core';
3
- import { MentionSuggestion, ComposerTriggerProvider, MentionEditorComponent, MessageInputBoxComponent, PendingAttachment } from '@memberjunction/ng-composer';
3
+ import { MentionSuggestion, ComposerTriggerProvider, MentionEditorComponent, MessageInputBoxComponent, PendingAttachment, BeforeSkillsOpenedEventArgs } from '@memberjunction/ng-composer';
4
4
  import * as i0 from "@angular/core";
5
5
  /**
6
6
  * AI-aware composer: wraps the generic `<mj-message-input-box>` (from
@@ -70,6 +70,15 @@ export declare class AiComposerComponent {
70
70
  voiceRequested: EventEmitter<void>;
71
71
  voiceOptionsRequested: EventEmitter<void>;
72
72
  planModeToggle: EventEmitter<void>;
73
+ /**
74
+ * Before/After pair for the Skills button, proxied straight through from the input box. Gated on
75
+ * `EnableSkillCommands` — the button and the keystroke are two doors to the same feature, so one
76
+ * flag governs both rather than letting a composer advertise skills it will not serve.
77
+ *
78
+ * Cancel on `beforeSkillsOpened` vetoes the dropdown; `afterSkillsOpened` then does not fire.
79
+ */
80
+ beforeSkillsOpened: EventEmitter<BeforeSkillsOpenedEventArgs>;
81
+ afterSkillsOpened: EventEmitter<void>;
73
82
  onInnerValueChange(newValue: string): void;
74
83
  /** The inner mention editor — kept reachable for hosts that drive it directly (e.g. clear-after-programmatic-send). */
75
84
  get mentionEditor(): MentionEditorComponent | undefined;
@@ -104,6 +113,6 @@ export declare class AiComposerComponent {
104
113
  artifactVersionId?: string;
105
114
  }): PendingAttachment | undefined;
106
115
  static ɵfac: i0.ɵɵFactoryDeclaration<AiComposerComponent, never>;
107
- static ɵcmp: i0.ɵɵComponentDeclaration<AiComposerComponent, "mj-ai-composer", never, { "EnableAgentMentions": { "alias": "EnableAgentMentions"; "required": false; }; "EnableEntityMentions": { "alias": "EnableEntityMentions"; "required": false; }; "EnableSkillCommands": { "alias": "EnableSkillCommands"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "value": { "alias": "value"; "required": false; }; "showCharacterCount": { "alias": "showCharacterCount"; "required": false; }; "enableMentions": { "alias": "enableMentions"; "required": false; }; "Provider": { "alias": "Provider"; "required": false; }; "currentUser": { "alias": "currentUser"; "required": false; }; "rows": { "alias": "rows"; "required": false; }; "enableAttachments": { "alias": "enableAttachments"; "required": false; }; "maxAttachments": { "alias": "maxAttachments"; "required": false; }; "maxAttachmentSizeBytes": { "alias": "maxAttachmentSizeBytes"; "required": false; }; "acceptedFileTypes": { "alias": "acceptedFileTypes"; "required": false; }; "enableRealtime": { "alias": "enableRealtime"; "required": false; }; "voiceActive": { "alias": "voiceActive"; "required": false; }; "canStartRealtime": { "alias": "canStartRealtime"; "required": false; }; "enablePlanMode": { "alias": "enablePlanMode"; "required": false; }; "planModeActive": { "alias": "planModeActive"; "required": false; }; }, { "textSubmitted": "textSubmitted"; "blurred": "blurred"; "valueChange": "valueChange"; "attachmentsChanged": "attachmentsChanged"; "attachmentError": "attachmentError"; "attachmentClicked": "attachmentClicked"; "voiceRequested": "voiceRequested"; "voiceOptionsRequested": "voiceOptionsRequested"; "planModeToggle": "planModeToggle"; }, never, never, false, never>;
116
+ static ɵcmp: i0.ɵɵComponentDeclaration<AiComposerComponent, "mj-ai-composer", never, { "EnableAgentMentions": { "alias": "EnableAgentMentions"; "required": false; }; "EnableEntityMentions": { "alias": "EnableEntityMentions"; "required": false; }; "EnableSkillCommands": { "alias": "EnableSkillCommands"; "required": false; }; "placeholder": { "alias": "placeholder"; "required": false; }; "disabled": { "alias": "disabled"; "required": false; }; "value": { "alias": "value"; "required": false; }; "showCharacterCount": { "alias": "showCharacterCount"; "required": false; }; "enableMentions": { "alias": "enableMentions"; "required": false; }; "Provider": { "alias": "Provider"; "required": false; }; "currentUser": { "alias": "currentUser"; "required": false; }; "rows": { "alias": "rows"; "required": false; }; "enableAttachments": { "alias": "enableAttachments"; "required": false; }; "maxAttachments": { "alias": "maxAttachments"; "required": false; }; "maxAttachmentSizeBytes": { "alias": "maxAttachmentSizeBytes"; "required": false; }; "acceptedFileTypes": { "alias": "acceptedFileTypes"; "required": false; }; "enableRealtime": { "alias": "enableRealtime"; "required": false; }; "voiceActive": { "alias": "voiceActive"; "required": false; }; "canStartRealtime": { "alias": "canStartRealtime"; "required": false; }; "enablePlanMode": { "alias": "enablePlanMode"; "required": false; }; "planModeActive": { "alias": "planModeActive"; "required": false; }; }, { "textSubmitted": "textSubmitted"; "blurred": "blurred"; "valueChange": "valueChange"; "attachmentsChanged": "attachmentsChanged"; "attachmentError": "attachmentError"; "attachmentClicked": "attachmentClicked"; "voiceRequested": "voiceRequested"; "voiceOptionsRequested": "voiceOptionsRequested"; "planModeToggle": "planModeToggle"; "beforeSkillsOpened": "beforeSkillsOpened"; "afterSkillsOpened": "afterSkillsOpened"; }, never, never, false, never>;
108
117
  }
109
118
  //# sourceMappingURL=ai-composer.component.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-composer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/composer/ai-composer.component.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ai-composer.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/composer/ai-composer.component.ts"],"names":[],"mappings":"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"}
@@ -112,6 +112,15 @@ export class AiComposerComponent {
112
112
  voiceRequested = new EventEmitter();
113
113
  voiceOptionsRequested = new EventEmitter();
114
114
  planModeToggle = new EventEmitter();
115
+ /**
116
+ * Before/After pair for the Skills button, proxied straight through from the input box. Gated on
117
+ * `EnableSkillCommands` — the button and the keystroke are two doors to the same feature, so one
118
+ * flag governs both rather than letting a composer advertise skills it will not serve.
119
+ *
120
+ * Cancel on `beforeSkillsOpened` vetoes the dropdown; `afterSkillsOpened` then does not fire.
121
+ */
122
+ beforeSkillsOpened = new EventEmitter();
123
+ afterSkillsOpened = new EventEmitter();
115
124
  onInnerValueChange(newValue) {
116
125
  this.value = newValue;
117
126
  this.valueChange.emit(newValue);
@@ -139,7 +148,7 @@ export class AiComposerComponent {
139
148
  }
140
149
  /** Mention chip data (id/type/name + preset info) currently in the editor. */
141
150
  getMentionChipsData() {
142
- return this.inputBox?.getMentionChipsData() || [];
151
+ return this.inputBox?.GetMentionChipsData() || [];
143
152
  }
144
153
  /** Plain text with mentions encoded as JSON (`@{"type":...}`) — the persistence format. */
145
154
  getPlainTextWithJsonMentions() {
@@ -147,11 +156,11 @@ export class AiComposerComponent {
147
156
  }
148
157
  /** Pending (not yet uploaded) attachments. */
149
158
  getPendingAttachments() {
150
- return this.inputBox?.getPendingAttachments() || [];
159
+ return this.inputBox?.GetPendingAttachments() || [];
151
160
  }
152
161
  /** Open the attachment file picker programmatically. */
153
162
  openFilePicker() {
154
- this.inputBox?.openFilePicker();
163
+ this.inputBox?.OpenFilePicker();
155
164
  }
156
165
  /** Attach an artifact as a pending attachment (artifact picker flow). */
157
166
  AddArtifactAttachment(artifact) {
@@ -163,13 +172,13 @@ export class AiComposerComponent {
163
172
  } if (rf & 2) {
164
173
  let _t;
165
174
  i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.inputBox = _t.first);
166
- } }, inputs: { EnableAgentMentions: "EnableAgentMentions", EnableEntityMentions: "EnableEntityMentions", EnableSkillCommands: "EnableSkillCommands", placeholder: "placeholder", disabled: "disabled", value: "value", showCharacterCount: "showCharacterCount", enableMentions: "enableMentions", Provider: "Provider", currentUser: "currentUser", rows: "rows", enableAttachments: "enableAttachments", maxAttachments: "maxAttachments", maxAttachmentSizeBytes: "maxAttachmentSizeBytes", acceptedFileTypes: "acceptedFileTypes", enableRealtime: "enableRealtime", voiceActive: "voiceActive", canStartRealtime: "canStartRealtime", enablePlanMode: "enablePlanMode", planModeActive: "planModeActive" }, outputs: { textSubmitted: "textSubmitted", blurred: "blurred", valueChange: "valueChange", attachmentsChanged: "attachmentsChanged", attachmentError: "attachmentError", attachmentClicked: "attachmentClicked", voiceRequested: "voiceRequested", voiceOptionsRequested: "voiceOptionsRequested", planModeToggle: "planModeToggle" }, standalone: false, decls: 2, vars: 18, consts: [["inputBox", ""], [3, "textSubmitted", "blurred", "valueChange", "attachmentsChanged", "attachmentError", "attachmentClicked", "voiceRequested", "voiceOptionsRequested", "planModeToggle", "placeholder", "disabled", "value", "showCharacterCount", "enableMentions", "TriggerProviders", "Provider", "currentUser", "rows", "enableAttachments", "maxAttachments", "maxAttachmentSizeBytes", "acceptedFileTypes", "enableRealtime", "voiceActive", "canStartRealtime", "enablePlanMode", "planModeActive"]], template: function AiComposerComponent_Template(rf, ctx) { if (rf & 1) {
175
+ } }, inputs: { EnableAgentMentions: "EnableAgentMentions", EnableEntityMentions: "EnableEntityMentions", EnableSkillCommands: "EnableSkillCommands", placeholder: "placeholder", disabled: "disabled", value: "value", showCharacterCount: "showCharacterCount", enableMentions: "enableMentions", Provider: "Provider", currentUser: "currentUser", rows: "rows", enableAttachments: "enableAttachments", maxAttachments: "maxAttachments", maxAttachmentSizeBytes: "maxAttachmentSizeBytes", acceptedFileTypes: "acceptedFileTypes", enableRealtime: "enableRealtime", voiceActive: "voiceActive", canStartRealtime: "canStartRealtime", enablePlanMode: "enablePlanMode", planModeActive: "planModeActive" }, outputs: { textSubmitted: "textSubmitted", blurred: "blurred", valueChange: "valueChange", attachmentsChanged: "attachmentsChanged", attachmentError: "attachmentError", attachmentClicked: "attachmentClicked", voiceRequested: "voiceRequested", voiceOptionsRequested: "voiceOptionsRequested", planModeToggle: "planModeToggle", beforeSkillsOpened: "beforeSkillsOpened", afterSkillsOpened: "afterSkillsOpened" }, standalone: false, decls: 2, vars: 19, consts: [["inputBox", ""], [3, "TextSubmitted", "Blurred", "ValueChange", "AttachmentsChanged", "AttachmentError", "AttachmentClicked", "VoiceRequested", "VoiceOptionsRequested", "PlanModeToggle", "BeforeSkillsOpened", "AfterSkillsOpened", "Placeholder", "Disabled", "Value", "ShowCharacterCount", "EnableMentions", "TriggerProviders", "Provider", "CurrentUser", "Rows", "EnableAttachments", "MaxAttachments", "MaxAttachmentSizeBytes", "AcceptedFileTypes", "EnableRealtime", "VoiceActive", "CanStartRealtime", "EnablePlanMode", "EnableSkills", "PlanModeActive"]], template: function AiComposerComponent_Template(rf, ctx) { if (rf & 1) {
167
176
  const _r1 = i0.ɵɵgetCurrentView();
168
177
  i0.ɵɵelementStart(0, "mj-message-input-box", 1, 0);
169
- i0.ɵɵlistener("textSubmitted", function AiComposerComponent_Template_mj_message_input_box_textSubmitted_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.textSubmitted.emit($event)); })("blurred", function AiComposerComponent_Template_mj_message_input_box_blurred_0_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.blurred.emit()); })("valueChange", function AiComposerComponent_Template_mj_message_input_box_valueChange_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onInnerValueChange($event)); })("attachmentsChanged", function AiComposerComponent_Template_mj_message_input_box_attachmentsChanged_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.attachmentsChanged.emit($event)); })("attachmentError", function AiComposerComponent_Template_mj_message_input_box_attachmentError_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.attachmentError.emit($event)); })("attachmentClicked", function AiComposerComponent_Template_mj_message_input_box_attachmentClicked_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.attachmentClicked.emit($event)); })("voiceRequested", function AiComposerComponent_Template_mj_message_input_box_voiceRequested_0_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.voiceRequested.emit()); })("voiceOptionsRequested", function AiComposerComponent_Template_mj_message_input_box_voiceOptionsRequested_0_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.voiceOptionsRequested.emit()); })("planModeToggle", function AiComposerComponent_Template_mj_message_input_box_planModeToggle_0_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.planModeToggle.emit()); });
178
+ i0.ɵɵlistener("TextSubmitted", function AiComposerComponent_Template_mj_message_input_box_TextSubmitted_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.textSubmitted.emit($event)); })("Blurred", function AiComposerComponent_Template_mj_message_input_box_Blurred_0_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.blurred.emit()); })("ValueChange", function AiComposerComponent_Template_mj_message_input_box_ValueChange_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.onInnerValueChange($event)); })("AttachmentsChanged", function AiComposerComponent_Template_mj_message_input_box_AttachmentsChanged_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.attachmentsChanged.emit($event)); })("AttachmentError", function AiComposerComponent_Template_mj_message_input_box_AttachmentError_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.attachmentError.emit($event)); })("AttachmentClicked", function AiComposerComponent_Template_mj_message_input_box_AttachmentClicked_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.attachmentClicked.emit($event)); })("VoiceRequested", function AiComposerComponent_Template_mj_message_input_box_VoiceRequested_0_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.voiceRequested.emit()); })("VoiceOptionsRequested", function AiComposerComponent_Template_mj_message_input_box_VoiceOptionsRequested_0_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.voiceOptionsRequested.emit()); })("PlanModeToggle", function AiComposerComponent_Template_mj_message_input_box_PlanModeToggle_0_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.planModeToggle.emit()); })("BeforeSkillsOpened", function AiComposerComponent_Template_mj_message_input_box_BeforeSkillsOpened_0_listener($event) { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.beforeSkillsOpened.emit($event)); })("AfterSkillsOpened", function AiComposerComponent_Template_mj_message_input_box_AfterSkillsOpened_0_listener() { i0.ɵɵrestoreView(_r1); return i0.ɵɵresetView(ctx.afterSkillsOpened.emit()); });
170
179
  i0.ɵɵelementEnd();
171
180
  } if (rf & 2) {
172
- i0.ɵɵproperty("placeholder", ctx.placeholder)("disabled", ctx.disabled)("value", ctx.value)("showCharacterCount", ctx.showCharacterCount)("enableMentions", ctx.enableMentions)("TriggerProviders", ctx.ActiveTriggerProviders)("Provider", ctx.Provider)("currentUser", ctx.currentUser)("rows", ctx.rows)("enableAttachments", ctx.enableAttachments)("maxAttachments", ctx.maxAttachments)("maxAttachmentSizeBytes", ctx.maxAttachmentSizeBytes)("acceptedFileTypes", ctx.acceptedFileTypes)("enableRealtime", ctx.enableRealtime)("voiceActive", ctx.voiceActive)("canStartRealtime", ctx.canStartRealtime)("enablePlanMode", ctx.enablePlanMode)("planModeActive", ctx.planModeActive);
181
+ i0.ɵɵproperty("Placeholder", ctx.placeholder)("Disabled", ctx.disabled)("Value", ctx.value)("ShowCharacterCount", ctx.showCharacterCount)("EnableMentions", ctx.enableMentions)("TriggerProviders", ctx.ActiveTriggerProviders)("Provider", ctx.Provider)("CurrentUser", ctx.currentUser)("Rows", ctx.rows)("EnableAttachments", ctx.enableAttachments)("MaxAttachments", ctx.maxAttachments)("MaxAttachmentSizeBytes", ctx.maxAttachmentSizeBytes)("AcceptedFileTypes", ctx.acceptedFileTypes)("EnableRealtime", ctx.enableRealtime)("VoiceActive", ctx.voiceActive)("CanStartRealtime", ctx.canStartRealtime)("EnablePlanMode", ctx.enablePlanMode)("EnableSkills", ctx.EnableSkillCommands)("PlanModeActive", ctx.planModeActive);
173
182
  } }, dependencies: [i1.MessageInputBoxComponent], encapsulation: 2 });
174
183
  }
175
184
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AiComposerComponent, [{
@@ -180,33 +189,36 @@ export class AiComposerComponent {
180
189
  template: `
181
190
  <mj-message-input-box
182
191
  #inputBox
183
- [placeholder]="placeholder"
184
- [disabled]="disabled"
185
- [value]="value"
186
- [showCharacterCount]="showCharacterCount"
187
- [enableMentions]="enableMentions"
192
+ [Placeholder]="placeholder"
193
+ [Disabled]="disabled"
194
+ [Value]="value"
195
+ [ShowCharacterCount]="showCharacterCount"
196
+ [EnableMentions]="enableMentions"
188
197
  [TriggerProviders]="ActiveTriggerProviders"
189
198
  [Provider]="Provider"
190
- [currentUser]="currentUser"
191
- [rows]="rows"
192
- [enableAttachments]="enableAttachments"
193
- [maxAttachments]="maxAttachments"
194
- [maxAttachmentSizeBytes]="maxAttachmentSizeBytes"
195
- [acceptedFileTypes]="acceptedFileTypes"
196
- [enableRealtime]="enableRealtime"
197
- [voiceActive]="voiceActive"
198
- [canStartRealtime]="canStartRealtime"
199
- [enablePlanMode]="enablePlanMode"
200
- [planModeActive]="planModeActive"
201
- (textSubmitted)="textSubmitted.emit($event)"
202
- (blurred)="blurred.emit()"
203
- (valueChange)="onInnerValueChange($event)"
204
- (attachmentsChanged)="attachmentsChanged.emit($event)"
205
- (attachmentError)="attachmentError.emit($event)"
206
- (attachmentClicked)="attachmentClicked.emit($event)"
207
- (voiceRequested)="voiceRequested.emit()"
208
- (voiceOptionsRequested)="voiceOptionsRequested.emit()"
209
- (planModeToggle)="planModeToggle.emit()">
199
+ [CurrentUser]="currentUser"
200
+ [Rows]="rows"
201
+ [EnableAttachments]="enableAttachments"
202
+ [MaxAttachments]="maxAttachments"
203
+ [MaxAttachmentSizeBytes]="maxAttachmentSizeBytes"
204
+ [AcceptedFileTypes]="acceptedFileTypes"
205
+ [EnableRealtime]="enableRealtime"
206
+ [VoiceActive]="voiceActive"
207
+ [CanStartRealtime]="canStartRealtime"
208
+ [EnablePlanMode]="enablePlanMode"
209
+ [EnableSkills]="EnableSkillCommands"
210
+ [PlanModeActive]="planModeActive"
211
+ (TextSubmitted)="textSubmitted.emit($event)"
212
+ (Blurred)="blurred.emit()"
213
+ (ValueChange)="onInnerValueChange($event)"
214
+ (AttachmentsChanged)="attachmentsChanged.emit($event)"
215
+ (AttachmentError)="attachmentError.emit($event)"
216
+ (AttachmentClicked)="attachmentClicked.emit($event)"
217
+ (VoiceRequested)="voiceRequested.emit()"
218
+ (VoiceOptionsRequested)="voiceOptionsRequested.emit()"
219
+ (PlanModeToggle)="planModeToggle.emit()"
220
+ (BeforeSkillsOpened)="beforeSkillsOpened.emit($event)"
221
+ (AfterSkillsOpened)="afterSkillsOpened.emit()">
210
222
  </mj-message-input-box>
211
223
  `
212
224
  }]
@@ -271,6 +283,10 @@ export class AiComposerComponent {
271
283
  type: Output
272
284
  }], planModeToggle: [{
273
285
  type: Output
286
+ }], beforeSkillsOpened: [{
287
+ type: Output
288
+ }], afterSkillsOpened: [{
289
+ type: Output
274
290
  }] }); })();
275
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AiComposerComponent, { className: "AiComposerComponent", filePath: "src/lib/components/composer/ai-composer.component.ts", lineNumber: 66 }); })();
291
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AiComposerComponent, { className: "AiComposerComponent", filePath: "src/lib/components/composer/ai-composer.component.ts", lineNumber: 70 }); })();
276
292
  //# sourceMappingURL=ai-composer.component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ai-composer.component.js","sourceRoot":"","sources":["../../../../src/lib/components/composer/ai-composer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAQlF,OAAO,EAAE,oBAAoB,EAAE,MAAM,+CAA+C,CAAC;AACrF,OAAO,EAAE,qBAAqB,EAAE,MAAM,gDAAgD,CAAC;AACvF,OAAO,EAAE,oBAAoB,EAAE,MAAM,+CAA+C,CAAC;;;;AAErF;;;;;;;;;;;;;;;;GAgBG;AAqCH,MAAM,OAAO,mBAAmB;IACP,QAAQ,CAA4B;IAE3D,oFAAoF;IAC5E,oBAAoB,GAAY,IAAI,CAAC;IACrC,qBAAqB,GAAY,IAAI,CAAC;IACtC,oBAAoB,GAAY,IAAI,CAAC;IACrC,uBAAuB,GAA8B,EAAE,CAAC;IAEhE,2EAA2E;IAC3E,6EAA6E;IAC5D,oBAAoB,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAClD,qBAAqB,GAAG,IAAI,qBAAqB,EAAE,CAAC;IACpD,oBAAoB,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAEnE;QACE,IAAI,CAAC,uBAAuB,EAAE,CAAC;IACjC,CAAC;IAED,uDAAuD;IACvD,IACI,mBAAmB,CAAC,KAAc;QACpC,IAAI,KAAK,KAAK,IAAI,CAAC,oBAAoB,EAAE,CAAC;YACxC,IAAI,CAAC,oBAAoB,GAAG,KAAK,CAAC;YAClC,IAAI,CAAC,uBAAuB,EAAE,CAAC;QACjC,CAAC;IACH,CAAC;IACD,IAAI,mBAAmB;QACrB,OAAO,IAAI,CAAC,oBAAoB,CAAC;IACnC,CAAC;IAED,gEAAgE;IAChE,IACI,oBAAoB,CAAC,KAAc;QACrC,IAAI,KAAK,KAAK,IAAI,CAAC,qBAAqB,EAAE,CAAC;YACzC,IAAI,CAAC,qBAAqB,GAAG,KAAK,CAAC;YACnC,IAAI,CAAC,uBAAuB,EAAE,CAAC;QACjC,CAAC;IACH,CAAC;IACD,IAAI,oBAAoB;QACtB,OAAO,IAAI,CAAC,qBAAqB,CAAC;IACpC,CAAC;IAED,gDAAgD;IAChD,IACI,mBAAmB,CAAC,KAAc;QACpC,IAAI,KAAK,KAAK,IAAI,CAAC,oBAAoB,EAAE,CAAC;YACxC,IAAI,CAAC,oBAAoB,GAAG,KAAK,CAAC;YAClC,IAAI,CAAC,uBAAuB,EAAE,CAAC;QACjC,CAAC;IACH,CAAC;IACD,IAAI,mBAAmB;QACrB,OAAO,IAAI,CAAC,oBAAoB,CAAC;IACnC,CAAC;IAED,+GAA+G;IAC/G,IAAW,sBAAsB;QAC/B,OAAO,IAAI,CAAC,uBAAuB,CAAC;IACtC,CAAC;IAEO,uBAAuB;QAC7B,MAAM,SAAS,GAA8B,EAAE,CAAC;QAChD,IAAI,IAAI,CAAC,oBAAoB;YAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QACzE,IAAI,IAAI,CAAC,qBAAqB;YAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;QAC3E,IAAI,IAAI,CAAC,oBAAoB;YAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;QACzE,IAAI,CAAC,uBAAuB,GAAG,SAAS,CAAC;IAC3C,CAAC;IAED,oFAAoF;IAC3E,WAAW,GAAW,kDAAkD,CAAC;IACzE,QAAQ,GAAY,KAAK,CAAC;IAC1B,KAAK,GAAW,EAAE,CAAC;IACnB,kBAAkB,GAAY,KAAK,CAAC;IAC7C,qEAAqE;IAC5D,cAAc,GAAY,IAAI,CAAC;IACxC,8FAA8F;IACrF,QAAQ,GAA6B,IAAI,CAAC;IAC1C,WAAW,CAAY;IACvB,IAAI,GAAW,CAAC,CAAC;IACjB,iBAAiB,GAAY,IAAI,CAAC;IAClC,cAAc,GAAW,EAAE,CAAC;IAC5B,sBAAsB,GAAW,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;IAC1D,iBAAiB,GAAW,SAAS,CAAC;IACtC,cAAc,GAAY,KAAK,CAAC;IAChC,WAAW,GAAY,KAAK,CAAC;IAC7B,gBAAgB,GAAY,IAAI,CAAC;IACjC,cAAc,GAAY,KAAK,CAAC;IAChC,cAAc,GAAY,KAAK,CAAC;IAEzC,qFAAqF;IAC3E,aAAa,GAAG,IAAI,YAAY,EAAU,CAAC;IACrD,0DAA0D;IAChD,OAAO,GAAG,IAAI,YAAY,EAAQ,CAAC;IACnC,WAAW,GAAG,IAAI,YAAY,EAAU,CAAC;IACzC,kBAAkB,GAAG,IAAI,YAAY,EAAuB,CAAC;IAC7D,eAAe,GAAG,IAAI,YAAY,EAAU,CAAC;IAC7C,iBAAiB,GAAG,IAAI,YAAY,EAAqB,CAAC;IAC1D,cAAc,GAAG,IAAI,YAAY,EAAQ,CAAC;IAC1C,qBAAqB,GAAG,IAAI,YAAY,EAAQ,CAAC;IACjD,cAAc,GAAG,IAAI,YAAY,EAAQ,CAAC;IAEpD,kBAAkB,CAAC,QAAgB;QACjC,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC;QACtB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAClC,CAAC;IAED,qFAAqF;IAErF,uHAAuH;IACvH,IAAW,aAAa;QACtB,OAAO,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC;IACtC,CAAC;IAED,gCAAgC;IAChC,0FAA0F;IACnF,aAAa,CAAC,UAA6B,EAAE,QAAiB,IAAI;QACvE,OAAO,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,KAAK,CAAC;IAClE,CAAC;IAED,kDAAkD;IAC3C,eAAe;QACpB,OAAO,IAAI,CAAC,QAAQ,EAAE,eAAe,EAAE,IAAI,KAAK,CAAC;IACnD,CAAC;IAEM,KAAK;QACV,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC;IACzB,CAAC;IAED,wDAAwD;IACjD,KAAK;QACV,IAAI,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,CAAC;IACxC,CAAC;IAED,8EAA8E;IACvE,mBAAmB;QACxB,OAAO,IAAI,CAAC,QAAQ,EAAE,mBAAmB,EAAE,IAAI,EAAE,CAAC;IACpD,CAAC;IAED,2FAA2F;IACpF,4BAA4B;QACjC,OAAO,IAAI,CAAC,QAAQ,EAAE,aAAa,EAAE,4BAA4B,EAAE,IAAI,EAAE,CAAC;IAC5E,CAAC;IAED,8CAA8C;IACvC,qBAAqB;QAC1B,OAAO,IAAI,CAAC,QAAQ,EAAE,qBAAqB,EAAE,IAAI,EAAE,CAAC;IACtD,CAAC;IAED,wDAAwD;IACjD,cAAc;QACnB,IAAI,CAAC,QAAQ,EAAE,cAAc,EAAE,CAAC;IAClC,CAAC;IAED,yEAAyE;IAClE,qBAAqB,CAAC,QAG5B;QACC,OAAO,IAAI,CAAC,QAAQ,EAAE,qBAAqB,CAAC,QAAQ,CAAC,CAAC;IACxD,CAAC;6GA/JU,mBAAmB;6DAAnB,mBAAmB;;;;;;;YAhC5B,kDA4B2C;YAAzC,AADA,AADA,AADA,AADA,AADA,AADA,AADA,AADA,0KAAiB,8BAA0B,KAAC,2IACjC,kBAAc,KAAC,yJACX,8BAA0B,KAAC,uKACpB,mCAA+B,KAAC,iKACnC,gCAA4B,KAAC,qKAC3B,kCAA8B,KAAC,yJAClC,yBAAqB,KAAC,uKACf,gCAA4B,KAAC,yJACpC,yBAAqB,KAAC;YAC1C,iBAAuB;;YAVrB,AADA,AADA,AADA,AADA,AADA,AADA,AADA,AADA,AADA,AADA,AADA,AADA,AADA,AADA,AADA,AADA,AADA,6CAA2B,0BACN,oBACN,8CAC0B,sCACR,gDACU,0BACtB,gCACM,kBACd,4CAC0B,sCACN,sDACgB,4CACV,sCACN,gCACN,0CACU,sCACJ,sCACA;;;iFAa1B,mBAAmB;cApC/B,SAAS;eAAC;gBACT,UAAU,EAAE,KAAK;gBACjB,QAAQ,EAAE,gBAAgB;gBAC1B,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BT;aACF;;kBAEE,SAAS;mBAAC,UAAU;;kBAmBpB,KAAK;;kBAYL,KAAK;;kBAYL,KAAK;;kBAyBL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBAEL,KAAK;;kBAEL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBACL,KAAK;;kBAGL,MAAM;;kBAEN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kBACN,MAAM;;kFAnGI,mBAAmB","sourcesContent":["import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';\nimport { IMetadataProvider, UserInfo } from '@memberjunction/core';\nimport { MentionSuggestion,\n ComposerTriggerProvider,\n MentionEditorComponent,\n MessageInputBoxComponent,\n PendingAttachment\n} from '@memberjunction/ng-composer';\nimport { AgentMentionProvider } from '../../composer-plugins/agent-mention.provider';\nimport { RecordMentionProvider } from '../../composer-plugins/record-mention.provider';\nimport { SkillCommandProvider } from '../../composer-plugins/skill-command.provider';\n\n/**\n * AI-aware composer: wraps the generic `<mj-message-input-box>` (from\n * `@memberjunction/ng-composer`, which ships zero AI knowledge) and builds the AI\n * trigger plugins in — '@' agent/user mentions, '#' entity/query record mentions, and\n * '/' skill commands — while proxying the input box's full surface (inputs, outputs,\n * and public methods).\n *\n * The familiar granular flags live HERE now:\n * - `EnableAgentMentions` (default true) — the '@' trigger\n * - `EnableEntityMentions` (default true) — the '#' trigger\n * - `EnableSkillCommands` (default true) — the '/' trigger\n * plus the pass-through `enableMentions` master switch.\n *\n * The wrapper always runs the inner editor in EXPLICIT provider-list mode (it owns the\n * list; it deliberately does not proxy `TriggerProviders` / `ExcludedTriggerKeys` — use\n * the raw `<mj-message-input-box>` when you need custom providers or discovery mode).\n */\n@Component({\n standalone: false,\n selector: 'mj-ai-composer',\n template: `\n <mj-message-input-box\n #inputBox\n [placeholder]=\"placeholder\"\n [disabled]=\"disabled\"\n [value]=\"value\"\n [showCharacterCount]=\"showCharacterCount\"\n [enableMentions]=\"enableMentions\"\n [TriggerProviders]=\"ActiveTriggerProviders\"\n [Provider]=\"Provider\"\n [currentUser]=\"currentUser\"\n [rows]=\"rows\"\n [enableAttachments]=\"enableAttachments\"\n [maxAttachments]=\"maxAttachments\"\n [maxAttachmentSizeBytes]=\"maxAttachmentSizeBytes\"\n [acceptedFileTypes]=\"acceptedFileTypes\"\n [enableRealtime]=\"enableRealtime\"\n [voiceActive]=\"voiceActive\"\n [canStartRealtime]=\"canStartRealtime\"\n [enablePlanMode]=\"enablePlanMode\"\n [planModeActive]=\"planModeActive\"\n (textSubmitted)=\"textSubmitted.emit($event)\"\n (blurred)=\"blurred.emit()\"\n (valueChange)=\"onInnerValueChange($event)\"\n (attachmentsChanged)=\"attachmentsChanged.emit($event)\"\n (attachmentError)=\"attachmentError.emit($event)\"\n (attachmentClicked)=\"attachmentClicked.emit($event)\"\n (voiceRequested)=\"voiceRequested.emit()\"\n (voiceOptionsRequested)=\"voiceOptionsRequested.emit()\"\n (planModeToggle)=\"planModeToggle.emit()\">\n </mj-message-input-box>\n `\n})\nexport class AiComposerComponent {\n @ViewChild('inputBox') inputBox?: MessageInputBoxComponent;\n\n // ── AI convenience flags (the wrapper's reason to exist) ─────────────────────────\n private _enableAgentMentions: boolean = true;\n private _enableEntityMentions: boolean = true;\n private _enableSkillCommands: boolean = true;\n private _activeTriggerProviders: ComposerTriggerProvider[] = [];\n\n // One instance per plugin per composer — all instances share the singleton\n // MentionAutocompleteService engine, so there is no duplicate cache warm-up.\n private readonly agentMentionProvider = new AgentMentionProvider();\n private readonly recordMentionProvider = new RecordMentionProvider();\n private readonly skillCommandProvider = new SkillCommandProvider();\n\n constructor() {\n this.rebuildTriggerProviders();\n }\n\n /** Enables the '@' trigger (agent + user mentions). */\n @Input()\n set EnableAgentMentions(value: boolean) {\n if (value !== this._enableAgentMentions) {\n this._enableAgentMentions = value;\n this.rebuildTriggerProviders();\n }\n }\n get EnableAgentMentions(): boolean {\n return this._enableAgentMentions;\n }\n\n /** Enables the '#' trigger (entity + query record mentions). */\n @Input()\n set EnableEntityMentions(value: boolean) {\n if (value !== this._enableEntityMentions) {\n this._enableEntityMentions = value;\n this.rebuildTriggerProviders();\n }\n }\n get EnableEntityMentions(): boolean {\n return this._enableEntityMentions;\n }\n\n /** Enables the '/' trigger (skill commands). */\n @Input()\n set EnableSkillCommands(value: boolean) {\n if (value !== this._enableSkillCommands) {\n this._enableSkillCommands = value;\n this.rebuildTriggerProviders();\n }\n }\n get EnableSkillCommands(): boolean {\n return this._enableSkillCommands;\n }\n\n /** The explicit provider list bound to the inner editor (stable reference; rebuilt only when a flag flips). */\n public get ActiveTriggerProviders(): ComposerTriggerProvider[] {\n return this._activeTriggerProviders;\n }\n\n private rebuildTriggerProviders(): void {\n const providers: ComposerTriggerProvider[] = [];\n if (this._enableAgentMentions) providers.push(this.agentMentionProvider);\n if (this._enableEntityMentions) providers.push(this.recordMentionProvider);\n if (this._enableSkillCommands) providers.push(this.skillCommandProvider);\n this._activeTriggerProviders = providers;\n }\n\n // ── Proxied inputs (identical names/defaults to MessageInputBoxComponent) ────────\n @Input() placeholder: string = 'Type your message to start a new conversation...';\n @Input() disabled: boolean = false;\n @Input() value: string = '';\n @Input() showCharacterCount: boolean = false;\n /** Master switch for all mention/command triggers (pass-through). */\n @Input() enableMentions: boolean = true;\n /** Optional metadata provider scoping this composer (pass-through to the trigger plugins). */\n @Input() Provider: IMetadataProvider | null = null;\n @Input() currentUser?: UserInfo;\n @Input() rows: number = 3;\n @Input() enableAttachments: boolean = true;\n @Input() maxAttachments: number = 10;\n @Input() maxAttachmentSizeBytes: number = 20 * 1024 * 1024; // 20MB\n @Input() acceptedFileTypes: string = 'image/*';\n @Input() enableRealtime: boolean = false;\n @Input() voiceActive: boolean = false;\n @Input() canStartRealtime: boolean = true;\n @Input() enablePlanMode: boolean = false;\n @Input() planModeActive: boolean = false;\n\n // ── Proxied outputs ───────────────────────────────────────────────────────────────\n @Output() textSubmitted = new EventEmitter<string>();\n /** Composer lost focus — hosts persist drafts on this. */\n @Output() blurred = new EventEmitter<void>();\n @Output() valueChange = new EventEmitter<string>();\n @Output() attachmentsChanged = new EventEmitter<PendingAttachment[]>();\n @Output() attachmentError = new EventEmitter<string>();\n @Output() attachmentClicked = new EventEmitter<PendingAttachment>();\n @Output() voiceRequested = new EventEmitter<void>();\n @Output() voiceOptionsRequested = new EventEmitter<void>();\n @Output() planModeToggle = new EventEmitter<void>();\n\n onInnerValueChange(newValue: string): void {\n this.value = newValue;\n this.valueChange.emit(newValue);\n }\n\n // ── Proxied public methods (ViewChild delegation) ─────────────────────────────────\n\n /** The inner mention editor — kept reachable for hosts that drive it directly (e.g. clear-after-programmatic-send). */\n public get mentionEditor(): MentionEditorComponent | undefined {\n return this.inputBox?.mentionEditor;\n }\n\n /** Focus the composer input. */\n /** Inserts a resolved mention chip + space (see MentionEditorComponent.InsertMention). */\n public InsertMention(suggestion: MentionSuggestion, focus: boolean = true): boolean {\n return this.inputBox?.InsertMention(suggestion, focus) ?? false;\n }\n\n /** Focus with the caret at the end of content. */\n public FocusCaretAtEnd(): boolean {\n return this.inputBox?.FocusCaretAtEnd() ?? false;\n }\n\n public focus(): void {\n this.inputBox?.focus();\n }\n\n /** Clear the editor content and pending attachments. */\n public clear(): void {\n this.inputBox?.mentionEditor?.clear();\n }\n\n /** Mention chip data (id/type/name + preset info) currently in the editor. */\n public getMentionChipsData(): Array<{ id: string; type: string; name: string; presetId?: string; presetName?: string }> {\n return this.inputBox?.getMentionChipsData() || [];\n }\n\n /** Plain text with mentions encoded as JSON (`@{\"type\":...}`) — the persistence format. */\n public getPlainTextWithJsonMentions(): string {\n return this.inputBox?.mentionEditor?.getPlainTextWithJsonMentions() || '';\n }\n\n /** Pending (not yet uploaded) attachments. */\n public getPendingAttachments(): PendingAttachment[] {\n return this.inputBox?.getPendingAttachments() || [];\n }\n\n /** Open the attachment file picker programmatically. */\n public openFilePicker(): void {\n this.inputBox?.openFilePicker();\n }\n\n /** Attach an artifact as a pending attachment (artifact picker flow). */\n public AddArtifactAttachment(artifact: {\n fileID: string; fileName: string; mimeType: string;\n sizeBytes: number; artifactVersionId?: string;\n }): PendingAttachment | undefined {\n return this.inputBox?.AddArtifactAttachment(artifact);\n }\n}\n"]}
1
+ {"version":3,"file":"ai-composer.component.js","sourceRoot":"","sources":["../../../../src/lib/components/composer/ai-composer.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, Input, Output, ViewChild } from '@angular/core';\nimport { IMetadataProvider, UserInfo } from '@memberjunction/core';\nimport { MentionSuggestion,\n ComposerTriggerProvider,\n MentionEditorComponent,\n MessageInputBoxComponent,\n PendingAttachment,\n BeforeSkillsOpenedEventArgs\n} from '@memberjunction/ng-composer';\nimport { AgentMentionProvider } from '../../composer-plugins/agent-mention.provider';\nimport { RecordMentionProvider } from '../../composer-plugins/record-mention.provider';\nimport { SkillCommandProvider } from '../../composer-plugins/skill-command.provider';\n\n/**\n * AI-aware composer: wraps the generic `<mj-message-input-box>` (from\n * `@memberjunction/ng-composer`, which ships zero AI knowledge) and builds the AI\n * trigger plugins in — '@' agent/user mentions, '#' entity/query record mentions, and\n * '/' skill commands — while proxying the input box's full surface (inputs, outputs,\n * and public methods).\n *\n * The familiar granular flags live HERE now:\n * - `EnableAgentMentions` (default true) — the '@' trigger\n * - `EnableEntityMentions` (default true) — the '#' trigger\n * - `EnableSkillCommands` (default true) — the '/' trigger\n * plus the pass-through `enableMentions` master switch.\n *\n * The wrapper always runs the inner editor in EXPLICIT provider-list mode (it owns the\n * list; it deliberately does not proxy `TriggerProviders` / `ExcludedTriggerKeys` — use\n * the raw `<mj-message-input-box>` when you need custom providers or discovery mode).\n */\n@Component({\n standalone: false,\n selector: 'mj-ai-composer',\n template: `\n <mj-message-input-box\n #inputBox\n [Placeholder]=\"placeholder\"\n [Disabled]=\"disabled\"\n [Value]=\"value\"\n [ShowCharacterCount]=\"showCharacterCount\"\n [EnableMentions]=\"enableMentions\"\n [TriggerProviders]=\"ActiveTriggerProviders\"\n [Provider]=\"Provider\"\n [CurrentUser]=\"currentUser\"\n [Rows]=\"rows\"\n [EnableAttachments]=\"enableAttachments\"\n [MaxAttachments]=\"maxAttachments\"\n [MaxAttachmentSizeBytes]=\"maxAttachmentSizeBytes\"\n [AcceptedFileTypes]=\"acceptedFileTypes\"\n [EnableRealtime]=\"enableRealtime\"\n [VoiceActive]=\"voiceActive\"\n [CanStartRealtime]=\"canStartRealtime\"\n [EnablePlanMode]=\"enablePlanMode\"\n [EnableSkills]=\"EnableSkillCommands\"\n [PlanModeActive]=\"planModeActive\"\n (TextSubmitted)=\"textSubmitted.emit($event)\"\n (Blurred)=\"blurred.emit()\"\n (ValueChange)=\"onInnerValueChange($event)\"\n (AttachmentsChanged)=\"attachmentsChanged.emit($event)\"\n (AttachmentError)=\"attachmentError.emit($event)\"\n (AttachmentClicked)=\"attachmentClicked.emit($event)\"\n (VoiceRequested)=\"voiceRequested.emit()\"\n (VoiceOptionsRequested)=\"voiceOptionsRequested.emit()\"\n (PlanModeToggle)=\"planModeToggle.emit()\"\n (BeforeSkillsOpened)=\"beforeSkillsOpened.emit($event)\"\n (AfterSkillsOpened)=\"afterSkillsOpened.emit()\">\n </mj-message-input-box>\n `\n})\nexport class AiComposerComponent {\n @ViewChild('inputBox') inputBox?: MessageInputBoxComponent;\n\n // ── AI convenience flags (the wrapper's reason to exist) ─────────────────────────\n private _enableAgentMentions: boolean = true;\n private _enableEntityMentions: boolean = true;\n private _enableSkillCommands: boolean = true;\n private _activeTriggerProviders: ComposerTriggerProvider[] = [];\n\n // One instance per plugin per composer — all instances share the singleton\n // MentionAutocompleteService engine, so there is no duplicate cache warm-up.\n private readonly agentMentionProvider = new AgentMentionProvider();\n private readonly recordMentionProvider = new RecordMentionProvider();\n private readonly skillCommandProvider = new SkillCommandProvider();\n\n constructor() {\n this.rebuildTriggerProviders();\n }\n\n /** Enables the '@' trigger (agent + user mentions). */\n @Input()\n set EnableAgentMentions(value: boolean) {\n if (value !== this._enableAgentMentions) {\n this._enableAgentMentions = value;\n this.rebuildTriggerProviders();\n }\n }\n get EnableAgentMentions(): boolean {\n return this._enableAgentMentions;\n }\n\n /** Enables the '#' trigger (entity + query record mentions). */\n @Input()\n set EnableEntityMentions(value: boolean) {\n if (value !== this._enableEntityMentions) {\n this._enableEntityMentions = value;\n this.rebuildTriggerProviders();\n }\n }\n get EnableEntityMentions(): boolean {\n return this._enableEntityMentions;\n }\n\n /** Enables the '/' trigger (skill commands). */\n @Input()\n set EnableSkillCommands(value: boolean) {\n if (value !== this._enableSkillCommands) {\n this._enableSkillCommands = value;\n this.rebuildTriggerProviders();\n }\n }\n get EnableSkillCommands(): boolean {\n return this._enableSkillCommands;\n }\n\n /** The explicit provider list bound to the inner editor (stable reference; rebuilt only when a flag flips). */\n public get ActiveTriggerProviders(): ComposerTriggerProvider[] {\n return this._activeTriggerProviders;\n }\n\n private rebuildTriggerProviders(): void {\n const providers: ComposerTriggerProvider[] = [];\n if (this._enableAgentMentions) providers.push(this.agentMentionProvider);\n if (this._enableEntityMentions) providers.push(this.recordMentionProvider);\n if (this._enableSkillCommands) providers.push(this.skillCommandProvider);\n this._activeTriggerProviders = providers;\n }\n\n // ── Proxied inputs (identical names/defaults to MessageInputBoxComponent) ────────\n @Input() placeholder: string = 'Type your message to start a new conversation...';\n @Input() disabled: boolean = false;\n @Input() value: string = '';\n @Input() showCharacterCount: boolean = false;\n /** Master switch for all mention/command triggers (pass-through). */\n @Input() enableMentions: boolean = true;\n /** Optional metadata provider scoping this composer (pass-through to the trigger plugins). */\n @Input() Provider: IMetadataProvider | null = null;\n @Input() currentUser?: UserInfo;\n @Input() rows: number = 3;\n @Input() enableAttachments: boolean = true;\n @Input() maxAttachments: number = 10;\n @Input() maxAttachmentSizeBytes: number = 20 * 1024 * 1024; // 20MB\n @Input() acceptedFileTypes: string = 'image/*';\n @Input() enableRealtime: boolean = false;\n @Input() voiceActive: boolean = false;\n @Input() canStartRealtime: boolean = true;\n @Input() enablePlanMode: boolean = false;\n @Input() planModeActive: boolean = false;\n\n // ── Proxied outputs ───────────────────────────────────────────────────────────────\n @Output() textSubmitted = new EventEmitter<string>();\n /** Composer lost focus — hosts persist drafts on this. */\n @Output() blurred = new EventEmitter<void>();\n @Output() valueChange = new EventEmitter<string>();\n @Output() attachmentsChanged = new EventEmitter<PendingAttachment[]>();\n @Output() attachmentError = new EventEmitter<string>();\n @Output() attachmentClicked = new EventEmitter<PendingAttachment>();\n @Output() voiceRequested = new EventEmitter<void>();\n @Output() voiceOptionsRequested = new EventEmitter<void>();\n @Output() planModeToggle = new EventEmitter<void>();\n /**\n * Before/After pair for the Skills button, proxied straight through from the input box. Gated on\n * `EnableSkillCommands` — the button and the keystroke are two doors to the same feature, so one\n * flag governs both rather than letting a composer advertise skills it will not serve.\n *\n * Cancel on `beforeSkillsOpened` vetoes the dropdown; `afterSkillsOpened` then does not fire.\n */\n @Output() beforeSkillsOpened = new EventEmitter<BeforeSkillsOpenedEventArgs>();\n @Output() afterSkillsOpened = new EventEmitter<void>();\n\n onInnerValueChange(newValue: string): void {\n this.value = newValue;\n this.valueChange.emit(newValue);\n }\n\n // ── Proxied public methods (ViewChild delegation) ─────────────────────────────────\n\n /** The inner mention editor — kept reachable for hosts that drive it directly (e.g. clear-after-programmatic-send). */\n public get mentionEditor(): MentionEditorComponent | undefined {\n return this.inputBox?.mentionEditor;\n }\n\n /** Focus the composer input. */\n /** Inserts a resolved mention chip + space (see MentionEditorComponent.InsertMention). */\n public InsertMention(suggestion: MentionSuggestion, focus: boolean = true): boolean {\n return this.inputBox?.InsertMention(suggestion, focus) ?? false;\n }\n\n /** Focus with the caret at the end of content. */\n public FocusCaretAtEnd(): boolean {\n return this.inputBox?.FocusCaretAtEnd() ?? false;\n }\n\n public focus(): void {\n this.inputBox?.focus();\n }\n\n /** Clear the editor content and pending attachments. */\n public clear(): void {\n this.inputBox?.mentionEditor?.clear();\n }\n\n /** Mention chip data (id/type/name + preset info) currently in the editor. */\n public getMentionChipsData(): Array<{ id: string; type: string; name: string; presetId?: string; presetName?: string }> {\n return this.inputBox?.GetMentionChipsData() || [];\n }\n\n /** Plain text with mentions encoded as JSON (`@{\"type\":...}`) — the persistence format. */\n public getPlainTextWithJsonMentions(): string {\n return this.inputBox?.mentionEditor?.getPlainTextWithJsonMentions() || '';\n }\n\n /** Pending (not yet uploaded) attachments. */\n public getPendingAttachments(): PendingAttachment[] {\n return this.inputBox?.GetPendingAttachments() || [];\n }\n\n /** Open the attachment file picker programmatically. */\n public openFilePicker(): void {\n this.inputBox?.OpenFilePicker();\n }\n\n /** Attach an artifact as a pending attachment (artifact picker flow). */\n public AddArtifactAttachment(artifact: {\n fileID: string; fileName: string; mimeType: string;\n sizeBytes: number; artifactVersionId?: string;\n }): PendingAttachment | undefined {\n return this.inputBox?.AddArtifactAttachment(artifact);\n }\n}\n"]}
@@ -25,9 +25,41 @@ export declare class SaveAsWorkflowRequestedEventArgs {
25
25
  readonly Spec: TaskGraphSpec;
26
26
  /** The parent task the graph ran as, when it was dispatched. Absent for a folded graph. */
27
27
  readonly ParentTaskID: string | null;
28
+ /**
29
+ * What to call it, as typed in the card.
30
+ *
31
+ * Asked here rather than in a dialog (ratified answer ④): one field and a button keeps the
32
+ * capture at two seconds. A saved workflow gets NO trigger from this path — it runs on
33
+ * demand until someone gives it a schedule — because the moment of saving is about capture,
34
+ * not scheduling.
35
+ */
36
+ readonly Name: string;
37
+ /**
38
+ * True when the user asked to continue in the editor rather than just save.
39
+ *
40
+ * A secondary route on purpose: making the editor mandatory turns a two-second capture into
41
+ * a task, which is the friction that stops good one-off plans becoming reusable.
42
+ */
43
+ readonly OpenInEditor: boolean;
28
44
  constructor(Spec: TaskGraphSpec,
29
45
  /** The parent task the graph ran as, when it was dispatched. Absent for a folded graph. */
30
- ParentTaskID: string | null);
46
+ ParentTaskID: string | null,
47
+ /**
48
+ * What to call it, as typed in the card.
49
+ *
50
+ * Asked here rather than in a dialog (ratified answer ④): one field and a button keeps the
51
+ * capture at two seconds. A saved workflow gets NO trigger from this path — it runs on
52
+ * demand until someone gives it a schedule — because the moment of saving is about capture,
53
+ * not scheduling.
54
+ */
55
+ Name: string,
56
+ /**
57
+ * True when the user asked to continue in the editor rather than just save.
58
+ *
59
+ * A secondary route on purpose: making the editor mandatory turns a two-second capture into
60
+ * a task, which is the friction that stops good one-off plans becoming reusable.
61
+ */
62
+ OpenInEditor?: boolean);
31
63
  }
32
64
  export declare class WorkflowPlanCardComponent {
33
65
  /** The plan. Null renders nothing — a card with no graph has nothing to say. */
@@ -48,6 +80,14 @@ export declare class WorkflowPlanCardComponent {
48
80
  Expanded: boolean;
49
81
  SaveAsWorkflowRequested: EventEmitter<SaveAsWorkflowRequestedEventArgs>;
50
82
  ExpandedChange: EventEmitter<boolean>;
83
+ /**
84
+ * The name to save under, seeded from the plan and editable in place.
85
+ *
86
+ * Held rather than read off the DOM so the empty-name guard is testable without a browser.
87
+ */
88
+ SaveName: string;
89
+ /** True once the user has typed, so the seeded name stops being overwritten by the plan's. */
90
+ private nameTouched;
51
91
  get StepCount(): number;
52
92
  get TempIds(): string[];
53
93
  /** One line of progress, or the plan's own reasoning when nothing is running yet. */
@@ -60,7 +100,17 @@ export declare class WorkflowPlanCardComponent {
60
100
  */
61
101
  get CanSave(): boolean;
62
102
  ToggleExpanded(): void;
63
- RequestSave(): void;
103
+ /**
104
+ * The name the field shows: what the user typed, else the plan's own name.
105
+ *
106
+ * Seeded rather than left blank because the plan already has a perfectly good name, and making
107
+ * someone invent one is the difference between saving and not bothering.
108
+ */
109
+ get EffectiveName(): string;
110
+ OnNameChanged(value: string): void;
111
+ /** A workflow with no name is not saveable — there would be nothing to find it by later. */
112
+ get CanCommit(): boolean;
113
+ RequestSave(openInEditor?: boolean): void;
64
114
  static ɵfac: i0.ɵɵFactoryDeclaration<WorkflowPlanCardComponent, never>;
65
115
  static ɵcmp: i0.ɵɵComponentDeclaration<WorkflowPlanCardComponent, "mj-workflow-plan-card", never, { "Spec": { "alias": "Spec"; "required": false; }; "RuntimeStatus": { "alias": "RuntimeStatus"; "required": false; }; "ParentTaskID": { "alias": "ParentTaskID"; "required": false; }; "AllowSaveAsWorkflow": { "alias": "AllowSaveAsWorkflow"; "required": false; }; "Expanded": { "alias": "Expanded"; "required": false; }; }, { "SaveAsWorkflowRequested": "SaveAsWorkflowRequested"; "ExpandedChange": "ExpandedChange"; }, never, never, false, never>;
66
116
  }
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-plan-card.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/message/workflow-plan-card.component.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAa,YAAY,EAAiB,MAAM,eAAe,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;;AAGnF,8EAA8E;AAC9E,qBAAa,gCAAgC;aAErB,IAAI,EAAE,aAAa;IACnC,2FAA2F;aAC3E,YAAY,EAAE,MAAM,GAAG,IAAI;gBAF3B,IAAI,EAAE,aAAa;IACnC,2FAA2F;IAC3E,YAAY,EAAE,MAAM,GAAG,IAAI;CAElD;AAED,qBAMa,yBAAyB;IAClC,gFAAgF;IAChE,IAAI,EAAE,aAAa,GAAG,IAAI,CAAQ;IAElD,yFAAyF;IACzE,aAAa,EAAE,sBAAsB,GAAG,IAAI,CAAQ;IAEpE,kFAAkF;IAClE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEnD;;;;;;OAMG;IACa,mBAAmB,EAAE,OAAO,CAAQ;IAEpD,4FAA4F;IAC5E,QAAQ,EAAE,OAAO,CAAS;IAEzB,uBAAuB,iDAAwD;IAC/E,cAAc,wBAA+B;IAE9D,IAAW,SAAS,IAAI,MAAM,CAE7B;IAED,IAAW,OAAO,IAAI,MAAM,EAAE,CAE7B;IAED,qFAAqF;IACrF,IAAW,WAAW,IAAI,MAAM,CAG/B;IAED,4EAA4E;IAC5E,IAAW,UAAU,IAAI,OAAO,CAE/B;IAED;;;OAGG;IACH,IAAW,OAAO,IAAI,OAAO,CAE5B;IAEM,cAAc,IAAI,IAAI;IAKtB,WAAW,IAAI,IAAI;yCAzDjB,yBAAyB;2CAAzB,yBAAyB;CA8DrC"}
1
+ {"version":3,"file":"workflow-plan-card.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/message/workflow-plan-card.component.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAa,YAAY,EAAiB,MAAM,eAAe,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;;AAGnF,8EAA8E;AAC9E,qBAAa,gCAAgC;aAErB,IAAI,EAAE,aAAa;IACnC,2FAA2F;aAC3E,YAAY,EAAE,MAAM,GAAG,IAAI;IAC3C;;;;;;;OAOG;aACa,IAAI,EAAE,MAAM;IAC5B;;;;;OAKG;aACa,YAAY,EAAE,OAAO;gBAlBrB,IAAI,EAAE,aAAa;IACnC,2FAA2F;IAC3E,YAAY,EAAE,MAAM,GAAG,IAAI;IAC3C;;;;;;;OAOG;IACa,IAAI,EAAE,MAAM;IAC5B;;;;;OAKG;IACa,YAAY,GAAE,OAAe;CAEpD;AAED,qBAMa,yBAAyB;IAClC,gFAAgF;IAChE,IAAI,EAAE,aAAa,GAAG,IAAI,CAAQ;IAElD,yFAAyF;IACzE,aAAa,EAAE,sBAAsB,GAAG,IAAI,CAAQ;IAEpE,kFAAkF;IAClE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEnD;;;;;;OAMG;IACa,mBAAmB,EAAE,OAAO,CAAQ;IAEpD,4FAA4F;IAC5E,QAAQ,EAAE,OAAO,CAAS;IAEzB,uBAAuB,iDAAwD;IAC/E,cAAc,wBAA+B;IAE9D;;;;OAIG;IACI,QAAQ,SAAM;IAErB,8FAA8F;IAC9F,OAAO,CAAC,WAAW,CAAS;IAE5B,IAAW,SAAS,IAAI,MAAM,CAE7B;IAED,IAAW,OAAO,IAAI,MAAM,EAAE,CAE7B;IAED,qFAAqF;IACrF,IAAW,WAAW,IAAI,MAAM,CAG/B;IAED,4EAA4E;IAC5E,IAAW,UAAU,IAAI,OAAO,CAE/B;IAED;;;OAGG;IACH,IAAW,OAAO,IAAI,OAAO,CAE5B;IAEM,cAAc,IAAI,IAAI;IAK7B;;;;;OAKG;IACH,IAAW,aAAa,IAAI,MAAM,CAEjC;IAEM,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAKzC,4FAA4F;IAC5F,IAAW,SAAS,IAAI,OAAO,CAE9B;IAEM,WAAW,CAAC,YAAY,GAAE,OAAe,GAAG,IAAI;yCAvF9C,yBAAyB;2CAAzB,yBAAyB;CAmGrC"}
@@ -19,23 +19,42 @@
19
19
  import { Component, EventEmitter, Input, Output } from '@angular/core';
20
20
  import { IsRuntimeSettled, SummarizeRuntime } from '@memberjunction/ng-task-graph-editor';
21
21
  import * as i0 from "@angular/core";
22
- import * as i1 from "@memberjunction/ng-task-graph-editor";
23
- import * as i2 from "@memberjunction/ng-ui-components";
22
+ import * as i1 from "@angular/forms";
23
+ import * as i2 from "@memberjunction/ng-task-graph-editor";
24
+ import * as i3 from "@memberjunction/ng-ui-components";
24
25
  function WorkflowPlanCardComponent_Conditional_0_Conditional_7_Template(rf, ctx) { if (rf & 1) {
25
26
  i0.ɵɵelement(0, "i", 6);
26
27
  } }
27
28
  function WorkflowPlanCardComponent_Conditional_0_Conditional_8_Conditional_2_Template(rf, ctx) { if (rf & 1) {
28
29
  const _r3 = i0.ɵɵgetCurrentView();
29
- i0.ɵɵelementStart(0, "div", 9)(1, "button", 10);
30
- i0.ɵɵlistener("click", function WorkflowPlanCardComponent_Conditional_0_Conditional_8_Conditional_2_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.RequestSave()); });
31
- i0.ɵɵelement(2, "i", 11);
32
- i0.ɵɵtext(3, " Save this approach as a Workflow ");
30
+ i0.ɵɵelementStart(0, "div", 9)(1, "input", 10);
31
+ i0.ɵɵlistener("ngModelChange", function WorkflowPlanCardComponent_Conditional_0_Conditional_8_Conditional_2_Template_input_ngModelChange_1_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.OnNameChanged($event)); });
32
+ i0.ɵɵelementEnd();
33
+ i0.ɵɵelementStart(2, "button", 11);
34
+ i0.ɵɵlistener("click", function WorkflowPlanCardComponent_Conditional_0_Conditional_8_Conditional_2_Template_button_click_2_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.RequestSave(false)); });
35
+ i0.ɵɵelement(3, "i", 12);
36
+ i0.ɵɵtext(4, " Save as Workflow ");
37
+ i0.ɵɵelementEnd();
38
+ i0.ɵɵelementStart(5, "button", 13);
39
+ i0.ɵɵlistener("click", function WorkflowPlanCardComponent_Conditional_0_Conditional_8_Conditional_2_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.RequestSave(true)); });
40
+ i0.ɵɵtext(6, " Open in editor ");
33
41
  i0.ɵɵelementEnd()();
42
+ i0.ɵɵelementStart(7, "p", 14);
43
+ i0.ɵɵtext(8, "Saved workflows run on demand until you give them a schedule.");
44
+ i0.ɵɵelementEnd();
45
+ } if (rf & 2) {
46
+ const ctx_r1 = i0.ɵɵnextContext(3);
47
+ i0.ɵɵadvance();
48
+ i0.ɵɵproperty("ngModel", ctx_r1.EffectiveName);
49
+ i0.ɵɵadvance();
50
+ i0.ɵɵproperty("disabled", !ctx_r1.CanCommit);
51
+ i0.ɵɵadvance(3);
52
+ i0.ɵɵproperty("disabled", !ctx_r1.CanCommit);
34
53
  } }
35
54
  function WorkflowPlanCardComponent_Conditional_0_Conditional_8_Template(rf, ctx) { if (rf & 1) {
36
55
  i0.ɵɵelementStart(0, "div", 7);
37
56
  i0.ɵɵelement(1, "mj-task-graph-editor", 8);
38
- i0.ɵɵconditionalCreate(2, WorkflowPlanCardComponent_Conditional_0_Conditional_8_Conditional_2_Template, 4, 0, "div", 9);
57
+ i0.ɵɵconditionalCreate(2, WorkflowPlanCardComponent_Conditional_0_Conditional_8_Conditional_2_Template, 9, 3);
39
58
  i0.ɵɵelementEnd();
40
59
  } if (rf & 2) {
41
60
  const ctx_r1 = i0.ɵɵnextContext(2);
@@ -79,11 +98,31 @@ function WorkflowPlanCardComponent_Conditional_0_Template(rf, ctx) { if (rf & 1)
79
98
  export class SaveAsWorkflowRequestedEventArgs {
80
99
  Spec;
81
100
  ParentTaskID;
101
+ Name;
102
+ OpenInEditor;
82
103
  constructor(Spec,
83
104
  /** The parent task the graph ran as, when it was dispatched. Absent for a folded graph. */
84
- ParentTaskID) {
105
+ ParentTaskID,
106
+ /**
107
+ * What to call it, as typed in the card.
108
+ *
109
+ * Asked here rather than in a dialog (ratified answer ④): one field and a button keeps the
110
+ * capture at two seconds. A saved workflow gets NO trigger from this path — it runs on
111
+ * demand until someone gives it a schedule — because the moment of saving is about capture,
112
+ * not scheduling.
113
+ */
114
+ Name,
115
+ /**
116
+ * True when the user asked to continue in the editor rather than just save.
117
+ *
118
+ * A secondary route on purpose: making the editor mandatory turns a two-second capture into
119
+ * a task, which is the friction that stops good one-off plans becoming reusable.
120
+ */
121
+ OpenInEditor = false) {
85
122
  this.Spec = Spec;
86
123
  this.ParentTaskID = ParentTaskID;
124
+ this.Name = Name;
125
+ this.OpenInEditor = OpenInEditor;
87
126
  }
88
127
  }
89
128
  export class WorkflowPlanCardComponent {
@@ -105,6 +144,14 @@ export class WorkflowPlanCardComponent {
105
144
  Expanded = false;
106
145
  SaveAsWorkflowRequested = new EventEmitter();
107
146
  ExpandedChange = new EventEmitter();
147
+ /**
148
+ * The name to save under, seeded from the plan and editable in place.
149
+ *
150
+ * Held rather than read off the DOM so the empty-name guard is testable without a browser.
151
+ */
152
+ SaveName = '';
153
+ /** True once the user has typed, so the seeded name stops being overwritten by the plan's. */
154
+ nameTouched = false;
108
155
  get StepCount() {
109
156
  return this.Spec?.tasks?.length ?? 0;
110
157
  }
@@ -132,21 +179,38 @@ export class WorkflowPlanCardComponent {
132
179
  this.Expanded = !this.Expanded;
133
180
  this.ExpandedChange.emit(this.Expanded);
134
181
  }
135
- RequestSave() {
136
- if (this.Spec && this.CanSave) {
137
- this.SaveAsWorkflowRequested.emit(new SaveAsWorkflowRequestedEventArgs(this.Spec, this.ParentTaskID));
182
+ /**
183
+ * The name the field shows: what the user typed, else the plan's own name.
184
+ *
185
+ * Seeded rather than left blank because the plan already has a perfectly good name, and making
186
+ * someone invent one is the difference between saving and not bothering.
187
+ */
188
+ get EffectiveName() {
189
+ return this.nameTouched ? this.SaveName : this.SaveName || (this.Spec?.workflowName ?? '');
190
+ }
191
+ OnNameChanged(value) {
192
+ this.nameTouched = true;
193
+ this.SaveName = value;
194
+ }
195
+ /** A workflow with no name is not saveable — there would be nothing to find it by later. */
196
+ get CanCommit() {
197
+ return this.CanSave && this.EffectiveName.trim().length > 0;
198
+ }
199
+ RequestSave(openInEditor = false) {
200
+ if (this.Spec && this.CanCommit) {
201
+ this.SaveAsWorkflowRequested.emit(new SaveAsWorkflowRequestedEventArgs(this.Spec, this.ParentTaskID, this.EffectiveName.trim(), openInEditor));
138
202
  }
139
203
  }
140
204
  static ɵfac = function WorkflowPlanCardComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || WorkflowPlanCardComponent)(); };
141
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: WorkflowPlanCardComponent, selectors: [["mj-workflow-plan-card"]], inputs: { Spec: "Spec", RuntimeStatus: "RuntimeStatus", ParentTaskID: "ParentTaskID", AllowSaveAsWorkflow: "AllowSaveAsWorkflow", Expanded: "Expanded" }, outputs: { SaveAsWorkflowRequested: "SaveAsWorkflowRequested", ExpandedChange: "ExpandedChange" }, standalone: false, decls: 1, vars: 1, consts: [[1, "mj-wpc", 3, "mj-wpc--expanded"], [1, "mj-wpc"], ["type", "button", 1, "mj-wpc__header", 3, "click"], [1, "fa-solid"], [1, "mj-wpc__title"], [1, "mj-wpc__summary"], ["aria-label", "Finished", 1, "fa-solid", "fa-circle-check", "mj-wpc__done"], [1, "mj-wpc__body"], [1, "mj-wpc__canvas", 3, "Spec", "RuntimeStatus", "ReadOnly", "ShowPalette", "ShowToolbar", "ShowMinimap"], [1, "mj-wpc__actions"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click"], [1, "fa-solid", "fa-floppy-disk"]], template: function WorkflowPlanCardComponent_Template(rf, ctx) { if (rf & 1) {
205
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: WorkflowPlanCardComponent, selectors: [["mj-workflow-plan-card"]], inputs: { Spec: "Spec", RuntimeStatus: "RuntimeStatus", ParentTaskID: "ParentTaskID", AllowSaveAsWorkflow: "AllowSaveAsWorkflow", Expanded: "Expanded" }, outputs: { SaveAsWorkflowRequested: "SaveAsWorkflowRequested", ExpandedChange: "ExpandedChange" }, standalone: false, decls: 1, vars: 1, consts: [[1, "mj-wpc", 3, "mj-wpc--expanded"], [1, "mj-wpc"], ["type", "button", 1, "mj-wpc__header", 3, "click"], [1, "fa-solid"], [1, "mj-wpc__title"], [1, "mj-wpc__summary"], ["aria-label", "Finished", 1, "fa-solid", "fa-circle-check", "mj-wpc__done"], [1, "mj-wpc__body"], [1, "mj-wpc__canvas", 3, "Spec", "RuntimeStatus", "ReadOnly", "ShowPalette", "ShowToolbar", "ShowMinimap"], [1, "mj-wpc__actions"], ["type", "text", "aria-label", "Workflow name", 1, "mj-wpc__name", 3, "ngModelChange", "ngModel"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click", "disabled"], [1, "fa-solid", "fa-floppy-disk"], ["mjButton", "", "variant", "secondary", "size", "sm", 3, "click", "disabled"], [1, "mj-wpc__note"]], template: function WorkflowPlanCardComponent_Template(rf, ctx) { if (rf & 1) {
142
206
  i0.ɵɵconditionalCreate(0, WorkflowPlanCardComponent_Conditional_0_Template, 9, 11, "div", 0);
143
207
  } if (rf & 2) {
144
208
  i0.ɵɵconditional(ctx.Spec ? 0 : -1);
145
- } }, dependencies: [i1.TaskGraphEditorComponent, i2.MJButtonDirective], styles: ["\n\n.mj-wpc[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-subtle);\n border-radius: var(--mj-radius-md, 6px);\n background: var(--mj-bg-surface);\n overflow: hidden;\n}\n\n.mj-wpc__header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: var(--mj-space-sm, 0.5rem);\n width: 100%;\n padding: var(--mj-space-sm, 0.5rem) var(--mj-space-md, 1rem);\n background: transparent;\n border: none;\n cursor: pointer;\n text-align: left;\n color: var(--mj-text-primary);\n}\n\n.mj-wpc__title[_ngcontent-%COMP%] {\n font-weight: 600;\n}\n\n.mj-wpc__summary[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n font-size: var(--mj-font-size-sm, 0.8125rem);\n color: var(--mj-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mj-wpc__done[_ngcontent-%COMP%] {\n color: var(--mj-color-success);\n}\n\n.mj-wpc__body[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n\n\n.mj-wpc__canvas[_ngcontent-%COMP%] {\n height: 320px;\n min-height: 0;\n}\n\n.mj-wpc__actions[_ngcontent-%COMP%] {\n display: flex;\n justify-content: flex-start;\n padding: var(--mj-space-sm, 0.5rem) var(--mj-space-md, 1rem);\n border-top: 1px solid var(--mj-border-subtle);\n}"] });
209
+ } }, dependencies: [i1.DefaultValueAccessor, i1.NgControlStatus, i1.NgModel, i2.TaskGraphEditorComponent, i3.MJButtonDirective], styles: ["\n\n.mj-wpc[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-subtle);\n border-radius: var(--mj-radius-md, 6px);\n background: var(--mj-bg-surface);\n overflow: hidden;\n}\n\n.mj-wpc__header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: var(--mj-space-sm, 0.5rem);\n width: 100%;\n padding: var(--mj-space-sm, 0.5rem) var(--mj-space-md, 1rem);\n background: transparent;\n border: none;\n cursor: pointer;\n text-align: left;\n color: var(--mj-text-primary);\n}\n\n.mj-wpc__title[_ngcontent-%COMP%] {\n font-weight: 600;\n}\n\n.mj-wpc__summary[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n font-size: var(--mj-font-size-sm, 0.8125rem);\n color: var(--mj-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mj-wpc__done[_ngcontent-%COMP%] {\n color: var(--mj-color-success);\n}\n\n.mj-wpc__body[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n\n\n.mj-wpc__canvas[_ngcontent-%COMP%] {\n height: 320px;\n min-height: 0;\n}\n\n.mj-wpc__actions[_ngcontent-%COMP%] {\n display: flex;\n justify-content: flex-start;\n padding: var(--mj-space-sm, 0.5rem) var(--mj-space-md, 1rem);\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n\n\n.mj-wpc__name[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n border: 1px solid var(--mj-border-strong);\n border-radius: var(--mj-radius-md);\n padding: var(--mj-space-2) var(--mj-space-3);\n font: inherit;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n}\n\n.mj-wpc__name[_ngcontent-%COMP%]:focus-visible {\n outline: 2px solid var(--mj-color-primary);\n outline-offset: -1px;\n}\n\n\n\n.mj-wpc__note[_ngcontent-%COMP%] {\n margin-top: var(--mj-space-2);\n font-size: var(--mj-font-size-xs);\n color: var(--mj-text-tertiary);\n}"] });
146
210
  }
147
211
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(WorkflowPlanCardComponent, [{
148
212
  type: Component,
149
- args: [{ standalone: false, selector: 'mj-workflow-plan-card', template: "@if (Spec) {\n <div class=\"mj-wpc\" [class.mj-wpc--expanded]=\"Expanded\">\n <button type=\"button\" class=\"mj-wpc__header\" (click)=\"ToggleExpanded()\"\n [attr.aria-expanded]=\"Expanded\">\n <i class=\"fa-solid\" [class.fa-chevron-right]=\"!Expanded\" [class.fa-chevron-down]=\"Expanded\"></i>\n <span class=\"mj-wpc__title\">{{ Spec.workflowName }}</span>\n <span class=\"mj-wpc__summary\">{{ SummaryLine }}</span>\n @if (IsFinished) {\n <i class=\"fa-solid fa-circle-check mj-wpc__done\" aria-label=\"Finished\"></i>\n }\n </button>\n\n @if (Expanded) {\n <div class=\"mj-wpc__body\">\n <!-- The SAME component the editor uses, read-only. A simpler chat-only renderer would be a\n second thing that can disagree with the canvas about what a graph means. -->\n <mj-task-graph-editor\n class=\"mj-wpc__canvas\"\n [Spec]=\"Spec\"\n [RuntimeStatus]=\"RuntimeStatus\"\n [ReadOnly]=\"true\"\n [ShowPalette]=\"false\"\n [ShowToolbar]=\"false\"\n [ShowMinimap]=\"false\">\n </mj-task-graph-editor>\n\n @if (CanSave) {\n <div class=\"mj-wpc__actions\">\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"RequestSave()\">\n <i class=\"fa-solid fa-floppy-disk\"></i> Save this approach as a Workflow\n </button>\n </div>\n }\n </div>\n }\n </div>\n}\n", styles: ["/* Design tokens only \u2014 no hardcoded colors. */\n.mj-wpc {\n border: 1px solid var(--mj-border-subtle);\n border-radius: var(--mj-radius-md, 6px);\n background: var(--mj-bg-surface);\n overflow: hidden;\n}\n\n.mj-wpc__header {\n display: flex;\n align-items: center;\n gap: var(--mj-space-sm, 0.5rem);\n width: 100%;\n padding: var(--mj-space-sm, 0.5rem) var(--mj-space-md, 1rem);\n background: transparent;\n border: none;\n cursor: pointer;\n text-align: left;\n color: var(--mj-text-primary);\n}\n\n.mj-wpc__title {\n font-weight: 600;\n}\n\n.mj-wpc__summary {\n flex: 1 1 auto;\n font-size: var(--mj-font-size-sm, 0.8125rem);\n color: var(--mj-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mj-wpc__done {\n color: var(--mj-color-success);\n}\n\n.mj-wpc__body {\n display: flex;\n flex-direction: column;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n/* A plan card sits inline in a conversation \u2014 a bounded height keeps it from dominating the thread. */\n.mj-wpc__canvas {\n height: 320px;\n min-height: 0;\n}\n\n.mj-wpc__actions {\n display: flex;\n justify-content: flex-start;\n padding: var(--mj-space-sm, 0.5rem) var(--mj-space-md, 1rem);\n border-top: 1px solid var(--mj-border-subtle);\n}\n"] }]
213
+ args: [{ standalone: false, selector: 'mj-workflow-plan-card', template: "@if (Spec) {\n <div class=\"mj-wpc\" [class.mj-wpc--expanded]=\"Expanded\">\n <button type=\"button\" class=\"mj-wpc__header\" (click)=\"ToggleExpanded()\"\n [attr.aria-expanded]=\"Expanded\">\n <i class=\"fa-solid\" [class.fa-chevron-right]=\"!Expanded\" [class.fa-chevron-down]=\"Expanded\"></i>\n <span class=\"mj-wpc__title\">{{ Spec.workflowName }}</span>\n <span class=\"mj-wpc__summary\">{{ SummaryLine }}</span>\n @if (IsFinished) {\n <i class=\"fa-solid fa-circle-check mj-wpc__done\" aria-label=\"Finished\"></i>\n }\n </button>\n\n @if (Expanded) {\n <div class=\"mj-wpc__body\">\n <!-- The SAME component the editor uses, read-only. A simpler chat-only renderer would be a\n second thing that can disagree with the canvas about what a graph means. -->\n <mj-task-graph-editor\n class=\"mj-wpc__canvas\"\n [Spec]=\"Spec\"\n [RuntimeStatus]=\"RuntimeStatus\"\n [ReadOnly]=\"true\"\n [ShowPalette]=\"false\"\n [ShowToolbar]=\"false\"\n [ShowMinimap]=\"false\">\n </mj-task-graph-editor>\n\n @if (CanSave) {\n <!-- Name it inline, then offer the editor (ratified answer \u2463). No trigger is requested\n here: saving is capture, not scheduling. -->\n <div class=\"mj-wpc__actions\">\n <input\n class=\"mj-wpc__name\"\n type=\"text\"\n aria-label=\"Workflow name\"\n [ngModel]=\"EffectiveName\"\n (ngModelChange)=\"OnNameChanged($event)\" />\n <button mjButton variant=\"primary\" size=\"sm\" [disabled]=\"!CanCommit\" (click)=\"RequestSave(false)\">\n <i class=\"fa-solid fa-floppy-disk\"></i> Save as Workflow\n </button>\n <button mjButton variant=\"secondary\" size=\"sm\" [disabled]=\"!CanCommit\" (click)=\"RequestSave(true)\">\n Open in editor\n </button>\n </div>\n <p class=\"mj-wpc__note\">Saved workflows run on demand until you give them a schedule.</p>\n }\n </div>\n }\n </div>\n}\n", styles: ["/* Design tokens only \u2014 no hardcoded colors. */\n.mj-wpc {\n border: 1px solid var(--mj-border-subtle);\n border-radius: var(--mj-radius-md, 6px);\n background: var(--mj-bg-surface);\n overflow: hidden;\n}\n\n.mj-wpc__header {\n display: flex;\n align-items: center;\n gap: var(--mj-space-sm, 0.5rem);\n width: 100%;\n padding: var(--mj-space-sm, 0.5rem) var(--mj-space-md, 1rem);\n background: transparent;\n border: none;\n cursor: pointer;\n text-align: left;\n color: var(--mj-text-primary);\n}\n\n.mj-wpc__title {\n font-weight: 600;\n}\n\n.mj-wpc__summary {\n flex: 1 1 auto;\n font-size: var(--mj-font-size-sm, 0.8125rem);\n color: var(--mj-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mj-wpc__done {\n color: var(--mj-color-success);\n}\n\n.mj-wpc__body {\n display: flex;\n flex-direction: column;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n/* A plan card sits inline in a conversation \u2014 a bounded height keeps it from dominating the thread. */\n.mj-wpc__canvas {\n height: 320px;\n min-height: 0;\n}\n\n.mj-wpc__actions {\n display: flex;\n justify-content: flex-start;\n padding: var(--mj-space-sm, 0.5rem) var(--mj-space-md, 1rem);\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n/* Name it inline (\u2463): one field and a button, so capture stays a two-second act. */\n.mj-wpc__name {\n flex: 1;\n min-width: 0;\n border: 1px solid var(--mj-border-strong);\n border-radius: var(--mj-radius-md);\n padding: var(--mj-space-2) var(--mj-space-3);\n font: inherit;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n}\n\n.mj-wpc__name:focus-visible {\n outline: 2px solid var(--mj-color-primary);\n outline-offset: -1px;\n}\n\n/* States the default plainly, so nobody goes looking for a schedule field that is not there. */\n.mj-wpc__note {\n margin-top: var(--mj-space-2);\n font-size: var(--mj-font-size-xs);\n color: var(--mj-text-tertiary);\n}\n"] }]
150
214
  }], null, { Spec: [{
151
215
  type: Input
152
216
  }], RuntimeStatus: [{
@@ -162,5 +226,5 @@ export class WorkflowPlanCardComponent {
162
226
  }], ExpandedChange: [{
163
227
  type: Output
164
228
  }] }); })();
165
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(WorkflowPlanCardComponent, { className: "WorkflowPlanCardComponent", filePath: "src/lib/components/message/workflow-plan-card.component.ts", lineNumber: 39 }); })();
229
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(WorkflowPlanCardComponent, { className: "WorkflowPlanCardComponent", filePath: "src/lib/components/message/workflow-plan-card.component.ts", lineNumber: 55 }); })();
166
230
  //# sourceMappingURL=workflow-plan-card.component.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-plan-card.component.js","sourceRoot":"","sources":["../../../../src/lib/components/message/workflow-plan-card.component.ts","../../../../src/lib/components/message/workflow-plan-card.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview The plan card in a conversation: what an agent decomposed, and what became of it.\n *\n * **The moment this exists for.** An agent breaks a request into steps, the work runs, and it was\n * good. Today that is where it ends — the decomposition was ephemeral, so the next person who wants\n * the same thing asks an agent to invent it again. This card is where a one-off agent plan becomes\n * reusable organizational automation (D17).\n *\n * Renders through the *same* `TaskGraphSpec` component the editor uses, in `ReadOnly` mode. That is\n * deliberate: a second, simpler renderer for chat would be a second thing that can disagree with the\n * canvas about what a graph means, and the whole program has been about removing exactly that.\n *\n * Save is **intent only**. This is a `widgets`-layer component in a conversation; it does not\n * persist agents. The host converts and writes through `AgentSpecSync`, which keeps the one place\n * that writes an agent the one place that writes an agent.\n *\n * @module @memberjunction/ng-conversations\n */\nimport { Component, EventEmitter, Input, Output } from '@angular/core';\nimport type { TaskGraphSpec } from '@memberjunction/ai-core-plus';\nimport type { TaskGraphRuntimeStatus } from '@memberjunction/ng-task-graph-editor';\nimport { IsRuntimeSettled, SummarizeRuntime } from '@memberjunction/ng-task-graph-editor';\n\n/** Fired when the user asks to promote this plan into a reusable workflow. */\nexport class SaveAsWorkflowRequestedEventArgs {\n constructor(\n public readonly Spec: TaskGraphSpec,\n /** The parent task the graph ran as, when it was dispatched. Absent for a folded graph. */\n public readonly ParentTaskID: string | null,\n ) {}\n}\n\n@Component({\n standalone: false,\n selector: 'mj-workflow-plan-card',\n templateUrl: './workflow-plan-card.component.html',\n styleUrls: ['./workflow-plan-card.component.css'],\n})\nexport class WorkflowPlanCardComponent {\n /** The plan. Null renders nothing — a card with no graph has nothing to say. */\n @Input() public Spec: TaskGraphSpec | null = null;\n\n /** Live per-step state. Absent means the card shows the plan without a progress line. */\n @Input() public RuntimeStatus: TaskGraphRuntimeStatus | null = null;\n\n /** Handle for status/cancel, when the graph was dispatched rather than folded. */\n @Input() public ParentTaskID: string | null = null;\n\n /**\n * Whether to offer Save as Workflow.\n *\n * Off while work is still running: offering to save a plan whose shape may yet change — a\n * retry, a failure routing down a recovery branch — invites saving something that never\n * actually happened.\n */\n @Input() public AllowSaveAsWorkflow: boolean = true;\n\n /** Starts collapsed. A plan card sits inline in a conversation and must not dominate it. */\n @Input() public Expanded: boolean = false;\n\n @Output() public SaveAsWorkflowRequested = new EventEmitter<SaveAsWorkflowRequestedEventArgs>();\n @Output() public ExpandedChange = new EventEmitter<boolean>();\n\n public get StepCount(): number {\n return this.Spec?.tasks?.length ?? 0;\n }\n\n public get TempIds(): string[] {\n return (this.Spec?.tasks ?? []).map((t) => t.tempId);\n }\n\n /** One line of progress, or the plan's own reasoning when nothing is running yet. */\n public get SummaryLine(): string {\n if (this.RuntimeStatus) return SummarizeRuntime(this.RuntimeStatus, this.TempIds);\n return this.Spec?.reasoning || `${this.StepCount} step${this.StepCount === 1 ? '' : 's'}`;\n }\n\n /** True once every step has reached a state nothing will move it out of. */\n public get IsFinished(): boolean {\n return !!this.RuntimeStatus && IsRuntimeSettled(this.RuntimeStatus, this.TempIds);\n }\n\n /**\n * Save is offered only once the work has settled — or when there is no runtime at all, which is\n * the design-time case (a plan being previewed before it runs).\n */\n public get CanSave(): boolean {\n return this.AllowSaveAsWorkflow && !!this.Spec && (!this.RuntimeStatus || this.IsFinished);\n }\n\n public ToggleExpanded(): void {\n this.Expanded = !this.Expanded;\n this.ExpandedChange.emit(this.Expanded);\n }\n\n public RequestSave(): void {\n if (this.Spec && this.CanSave) {\n this.SaveAsWorkflowRequested.emit(new SaveAsWorkflowRequestedEventArgs(this.Spec, this.ParentTaskID));\n }\n }\n}\n","@if (Spec) {\n <div class=\"mj-wpc\" [class.mj-wpc--expanded]=\"Expanded\">\n <button type=\"button\" class=\"mj-wpc__header\" (click)=\"ToggleExpanded()\"\n [attr.aria-expanded]=\"Expanded\">\n <i class=\"fa-solid\" [class.fa-chevron-right]=\"!Expanded\" [class.fa-chevron-down]=\"Expanded\"></i>\n <span class=\"mj-wpc__title\">{{ Spec.workflowName }}</span>\n <span class=\"mj-wpc__summary\">{{ SummaryLine }}</span>\n @if (IsFinished) {\n <i class=\"fa-solid fa-circle-check mj-wpc__done\" aria-label=\"Finished\"></i>\n }\n </button>\n\n @if (Expanded) {\n <div class=\"mj-wpc__body\">\n <!-- The SAME component the editor uses, read-only. A simpler chat-only renderer would be a\n second thing that can disagree with the canvas about what a graph means. -->\n <mj-task-graph-editor\n class=\"mj-wpc__canvas\"\n [Spec]=\"Spec\"\n [RuntimeStatus]=\"RuntimeStatus\"\n [ReadOnly]=\"true\"\n [ShowPalette]=\"false\"\n [ShowToolbar]=\"false\"\n [ShowMinimap]=\"false\">\n </mj-task-graph-editor>\n\n @if (CanSave) {\n <div class=\"mj-wpc__actions\">\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"RequestSave()\">\n <i class=\"fa-solid fa-floppy-disk\"></i> Save this approach as a Workflow\n </button>\n </div>\n }\n </div>\n }\n </div>\n}\n"]}
1
+ {"version":3,"file":"workflow-plan-card.component.js","sourceRoot":"","sources":["../../../../src/lib/components/message/workflow-plan-card.component.ts","../../../../src/lib/components/message/workflow-plan-card.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview The plan card in a conversation: what an agent decomposed, and what became of it.\n *\n * **The moment this exists for.** An agent breaks a request into steps, the work runs, and it was\n * good. Today that is where it ends — the decomposition was ephemeral, so the next person who wants\n * the same thing asks an agent to invent it again. This card is where a one-off agent plan becomes\n * reusable organizational automation (D17).\n *\n * Renders through the *same* `TaskGraphSpec` component the editor uses, in `ReadOnly` mode. That is\n * deliberate: a second, simpler renderer for chat would be a second thing that can disagree with the\n * canvas about what a graph means, and the whole program has been about removing exactly that.\n *\n * Save is **intent only**. This is a `widgets`-layer component in a conversation; it does not\n * persist agents. The host converts and writes through `AgentSpecSync`, which keeps the one place\n * that writes an agent the one place that writes an agent.\n *\n * @module @memberjunction/ng-conversations\n */\nimport { Component, EventEmitter, Input, Output } from '@angular/core';\nimport type { TaskGraphSpec } from '@memberjunction/ai-core-plus';\nimport type { TaskGraphRuntimeStatus } from '@memberjunction/ng-task-graph-editor';\nimport { IsRuntimeSettled, SummarizeRuntime } from '@memberjunction/ng-task-graph-editor';\n\n/** Fired when the user asks to promote this plan into a reusable workflow. */\nexport class SaveAsWorkflowRequestedEventArgs {\n constructor(\n public readonly Spec: TaskGraphSpec,\n /** The parent task the graph ran as, when it was dispatched. Absent for a folded graph. */\n public readonly ParentTaskID: string | null,\n /**\n * What to call it, as typed in the card.\n *\n * Asked here rather than in a dialog (ratified answer ④): one field and a button keeps the\n * capture at two seconds. A saved workflow gets NO trigger from this path — it runs on\n * demand until someone gives it a schedule — because the moment of saving is about capture,\n * not scheduling.\n */\n public readonly Name: string,\n /**\n * True when the user asked to continue in the editor rather than just save.\n *\n * A secondary route on purpose: making the editor mandatory turns a two-second capture into\n * a task, which is the friction that stops good one-off plans becoming reusable.\n */\n public readonly OpenInEditor: boolean = false,\n ) {}\n}\n\n@Component({\n standalone: false,\n selector: 'mj-workflow-plan-card',\n templateUrl: './workflow-plan-card.component.html',\n styleUrls: ['./workflow-plan-card.component.css'],\n})\nexport class WorkflowPlanCardComponent {\n /** The plan. Null renders nothing — a card with no graph has nothing to say. */\n @Input() public Spec: TaskGraphSpec | null = null;\n\n /** Live per-step state. Absent means the card shows the plan without a progress line. */\n @Input() public RuntimeStatus: TaskGraphRuntimeStatus | null = null;\n\n /** Handle for status/cancel, when the graph was dispatched rather than folded. */\n @Input() public ParentTaskID: string | null = null;\n\n /**\n * Whether to offer Save as Workflow.\n *\n * Off while work is still running: offering to save a plan whose shape may yet change — a\n * retry, a failure routing down a recovery branch — invites saving something that never\n * actually happened.\n */\n @Input() public AllowSaveAsWorkflow: boolean = true;\n\n /** Starts collapsed. A plan card sits inline in a conversation and must not dominate it. */\n @Input() public Expanded: boolean = false;\n\n @Output() public SaveAsWorkflowRequested = new EventEmitter<SaveAsWorkflowRequestedEventArgs>();\n @Output() public ExpandedChange = new EventEmitter<boolean>();\n\n /**\n * The name to save under, seeded from the plan and editable in place.\n *\n * Held rather than read off the DOM so the empty-name guard is testable without a browser.\n */\n public SaveName = '';\n\n /** True once the user has typed, so the seeded name stops being overwritten by the plan's. */\n private nameTouched = false;\n\n public get StepCount(): number {\n return this.Spec?.tasks?.length ?? 0;\n }\n\n public get TempIds(): string[] {\n return (this.Spec?.tasks ?? []).map((t) => t.tempId);\n }\n\n /** One line of progress, or the plan's own reasoning when nothing is running yet. */\n public get SummaryLine(): string {\n if (this.RuntimeStatus) return SummarizeRuntime(this.RuntimeStatus, this.TempIds);\n return this.Spec?.reasoning || `${this.StepCount} step${this.StepCount === 1 ? '' : 's'}`;\n }\n\n /** True once every step has reached a state nothing will move it out of. */\n public get IsFinished(): boolean {\n return !!this.RuntimeStatus && IsRuntimeSettled(this.RuntimeStatus, this.TempIds);\n }\n\n /**\n * Save is offered only once the work has settled — or when there is no runtime at all, which is\n * the design-time case (a plan being previewed before it runs).\n */\n public get CanSave(): boolean {\n return this.AllowSaveAsWorkflow && !!this.Spec && (!this.RuntimeStatus || this.IsFinished);\n }\n\n public ToggleExpanded(): void {\n this.Expanded = !this.Expanded;\n this.ExpandedChange.emit(this.Expanded);\n }\n\n /**\n * The name the field shows: what the user typed, else the plan's own name.\n *\n * Seeded rather than left blank because the plan already has a perfectly good name, and making\n * someone invent one is the difference between saving and not bothering.\n */\n public get EffectiveName(): string {\n return this.nameTouched ? this.SaveName : this.SaveName || (this.Spec?.workflowName ?? '');\n }\n\n public OnNameChanged(value: string): void {\n this.nameTouched = true;\n this.SaveName = value;\n }\n\n /** A workflow with no name is not saveable — there would be nothing to find it by later. */\n public get CanCommit(): boolean {\n return this.CanSave && this.EffectiveName.trim().length > 0;\n }\n\n public RequestSave(openInEditor: boolean = false): void {\n if (this.Spec && this.CanCommit) {\n this.SaveAsWorkflowRequested.emit(\n new SaveAsWorkflowRequestedEventArgs(\n this.Spec,\n this.ParentTaskID,\n this.EffectiveName.trim(),\n openInEditor,\n ),\n );\n }\n }\n}\n","@if (Spec) {\n <div class=\"mj-wpc\" [class.mj-wpc--expanded]=\"Expanded\">\n <button type=\"button\" class=\"mj-wpc__header\" (click)=\"ToggleExpanded()\"\n [attr.aria-expanded]=\"Expanded\">\n <i class=\"fa-solid\" [class.fa-chevron-right]=\"!Expanded\" [class.fa-chevron-down]=\"Expanded\"></i>\n <span class=\"mj-wpc__title\">{{ Spec.workflowName }}</span>\n <span class=\"mj-wpc__summary\">{{ SummaryLine }}</span>\n @if (IsFinished) {\n <i class=\"fa-solid fa-circle-check mj-wpc__done\" aria-label=\"Finished\"></i>\n }\n </button>\n\n @if (Expanded) {\n <div class=\"mj-wpc__body\">\n <!-- The SAME component the editor uses, read-only. A simpler chat-only renderer would be a\n second thing that can disagree with the canvas about what a graph means. -->\n <mj-task-graph-editor\n class=\"mj-wpc__canvas\"\n [Spec]=\"Spec\"\n [RuntimeStatus]=\"RuntimeStatus\"\n [ReadOnly]=\"true\"\n [ShowPalette]=\"false\"\n [ShowToolbar]=\"false\"\n [ShowMinimap]=\"false\">\n </mj-task-graph-editor>\n\n @if (CanSave) {\n <!-- Name it inline, then offer the editor (ratified answer ④). No trigger is requested\n here: saving is capture, not scheduling. -->\n <div class=\"mj-wpc__actions\">\n <input\n class=\"mj-wpc__name\"\n type=\"text\"\n aria-label=\"Workflow name\"\n [ngModel]=\"EffectiveName\"\n (ngModelChange)=\"OnNameChanged($event)\" />\n <button mjButton variant=\"primary\" size=\"sm\" [disabled]=\"!CanCommit\" (click)=\"RequestSave(false)\">\n <i class=\"fa-solid fa-floppy-disk\"></i> Save as Workflow\n </button>\n <button mjButton variant=\"secondary\" size=\"sm\" [disabled]=\"!CanCommit\" (click)=\"RequestSave(true)\">\n Open in editor\n </button>\n </div>\n <p class=\"mj-wpc__note\">Saved workflows run on demand until you give them a schedule.</p>\n }\n </div>\n }\n </div>\n}\n"]}
@@ -109,9 +109,14 @@ export declare function BuildRealtimeModelOptions(models: ReadonlyArray<VoiceMod
109
109
  /**
110
110
  * Builds the `configOverridesJson` payload for the `StartRealtimeClientSession` mint from
111
111
  * what the (authorization-gated) pickers chose — an explicit realtime model preference and/or a
112
- * per-provider voice. The envelope (`{"realtime":{"modelPreference":"<id>","voice":{"providers":
113
- * {"openai":{"voice":"<v>"}}}}}`) is the pinned server contract, merged into the effective config
114
- * server-side (highest precedence).
112
+ * voice. The envelope (`{"realtime":{"modelPreference":"<id>","voice":{"default":{"voice":"<v>"}}}}`)
113
+ * is the pinned server contract, merged into the effective config server-side (highest precedence).
114
+ *
115
+ * The voice is filed PROVIDER-AGNOSTICALLY: the browser has a model id, never a vendor `DriverClass`
116
+ * (`GetRealtimeModelVoices` does not return one), so naming a provider here would be a guess that
117
+ * silently voided the setting for every non-OpenAI vendor. The server files it onto whichever driver
118
+ * it resolves. Kept in lockstep with the server-side `BuildRealtimeOverridesJson` in
119
+ * `@memberjunction/ai-agents` by `realtime-convergence-drift.test.ts`. See issue #3530.
115
120
  *
116
121
  * @param preferredModelId The explicit `MJ: AI Models` id, or null/empty for none.
117
122
  * @param preferredVoice The provider-native voice id (e.g. `echo`), or null/empty for none.
@@ -1 +1 @@
1
- {"version":3,"file":"realtime-pairing.d.ts","sourceRoot":"","sources":["../../../src/lib/services/realtime-pairing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAIzD;;;;;;;;;GASG;AACH,MAAM,WAAW,sBAAsB;IACrC,0BAA0B;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,aAAa,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mFAAmF;IACnF,SAAS,EAAE,OAAO,CAAC;IACnB,iDAAiD;IACjD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,wEAAwE;AACxE,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC,CA2B3H;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,SAAS,sBAAsB,EAAE,GAAG,sBAAsB,EAAE,CASlG;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,SAAS,iBAAiB,EACpE,MAAM,EAAE,SAAS,CAAC,EAAE,EACpB,QAAQ,EAAE,SAAS,sBAAsB,EAAE,GAC1C,CAAC,EAAE,CAmBL;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,SAAS,sBAAsB,EAAE,GAAG,MAAM,GAAG,IAAI,CAOhG;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,SAAS,sBAAsB,EAAE,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAQlI;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CAEnG;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,kCAAkC;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,kCAAkC;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,kDAAkD;IAClD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,aAAa,CAAC,mBAAmB,CAAC,GAAG,mBAAmB,EAAE,CAK3G;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gCAAgC,CAC9C,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC3C,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACzC,MAAM,GAAG,IAAI,CAef"}
1
+ {"version":3,"file":"realtime-pairing.d.ts","sourceRoot":"","sources":["../../../src/lib/services/realtime-pairing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAIzD;;;;;;;;;GASG;AACH,MAAM,WAAW,sBAAsB;IACrC,0BAA0B;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,yDAAyD;IACzD,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,aAAa,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,mFAAmF;IACnF,SAAS,EAAE,OAAO,CAAC;IACnB,iDAAiD;IACjD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,wEAAwE;AACxE,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;CACZ;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,EAAE,CAAC,CA2B3H;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,SAAS,sBAAsB,EAAE,GAAG,sBAAsB,EAAE,CASlG;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,SAAS,iBAAiB,EACpE,MAAM,EAAE,SAAS,CAAC,EAAE,EACpB,QAAQ,EAAE,SAAS,sBAAsB,EAAE,GAC1C,CAAC,EAAE,CAmBL;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,SAAS,sBAAsB,EAAE,GAAG,MAAM,GAAG,IAAI,CAOhG;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,SAAS,sBAAsB,EAAE,EAAE,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAQlI;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CAEnG;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,kCAAkC;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,kCAAkC;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,kDAAkD;IAClD,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,aAAa,CAAC,mBAAmB,CAAC,GAAG,mBAAmB,EAAE,CAK3G;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,gCAAgC,CAC9C,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC3C,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACzC,MAAM,GAAG,IAAI,CAcf"}
@@ -133,9 +133,14 @@ export function BuildRealtimeModelOptions(models) {
133
133
  /**
134
134
  * Builds the `configOverridesJson` payload for the `StartRealtimeClientSession` mint from
135
135
  * what the (authorization-gated) pickers chose — an explicit realtime model preference and/or a
136
- * per-provider voice. The envelope (`{"realtime":{"modelPreference":"<id>","voice":{"providers":
137
- * {"openai":{"voice":"<v>"}}}}}`) is the pinned server contract, merged into the effective config
138
- * server-side (highest precedence).
136
+ * voice. The envelope (`{"realtime":{"modelPreference":"<id>","voice":{"default":{"voice":"<v>"}}}}`)
137
+ * is the pinned server contract, merged into the effective config server-side (highest precedence).
138
+ *
139
+ * The voice is filed PROVIDER-AGNOSTICALLY: the browser has a model id, never a vendor `DriverClass`
140
+ * (`GetRealtimeModelVoices` does not return one), so naming a provider here would be a guess that
141
+ * silently voided the setting for every non-OpenAI vendor. The server files it onto whichever driver
142
+ * it resolves. Kept in lockstep with the server-side `BuildRealtimeOverridesJson` in
143
+ * `@memberjunction/ai-agents` by `realtime-convergence-drift.test.ts`. See issue #3530.
139
144
  *
140
145
  * @param preferredModelId The explicit `MJ: AI Models` id, or null/empty for none.
141
146
  * @param preferredVoice The provider-native voice id (e.g. `echo`), or null/empty for none.
@@ -152,8 +157,7 @@ export function BuildRealtimeConfigOverridesJson(preferredModelId, preferredVoic
152
157
  realtime.modelPreference = modelId;
153
158
  }
154
159
  if (voice.length > 0) {
155
- // Per-provider voice shape matches GetProviderVoiceSettings; `openai` is the realtime provider today.
156
- realtime.voice = { providers: { openai: { voice } } };
160
+ realtime.voice = { default: { voice } };
157
161
  }
158
162
  return JSON.stringify({ realtime });
159
163
  }
@@ -1 +1 @@
1
- {"version":3,"file":"realtime-pairing.js","sourceRoot":"","sources":["../../../src/lib/services/realtime-pairing.ts"],"names":[],"mappings":"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","sourcesContent":["import { IMetadataProvider } from '@memberjunction/core';\nimport { NormalizeUUID, UUIDsEqual } from '@memberjunction/global';\nimport { AIEngineBase } from '@memberjunction/ai-engine-base';\n\n/**\n * The narrow read-only projection of an `MJ: AI Agent Co Agents` row the voice UI consumes\n * (loaded via {@link LoadCoAgentPairings} from {@link AIEngineBase}'s cached `AgentCoAgents`;\n * only **Active** rows of relationship **Type `'CoAgent'`** with a specific agent target —\n * type-level rows express the resolution-chain default, not a target restriction, and\n * reserved relationship types are ignored until their features ship). Pairing semantics: a\n * co-agent with ZERO such rows is **universal** (it can front any target agent — the\n * zero-config default); a co-agent WITH rows may front exactly the listed targets, with the\n * `IsDefault` row as its preselected target.\n */\nexport interface RealtimePairedAgentRow {\n /** The pairing row id. */\n ID: string;\n /** The Realtime-type co-agent the pairing belongs to. */\n CoAgentID: string;\n /** The target agent this co-agent may front. */\n TargetAgentID: string;\n /** Denormalized target-agent display name (from the base view). */\n TargetAgent: string | null;\n /** When true, this target is the co-agent's default (at most one per co-agent). */\n IsDefault: boolean;\n /** Display/list ordering of the pairing rows. */\n Sequence: number | null;\n}\n\n/** Minimal agent shape the pure pairing/co-agent helpers operate on. */\nexport interface RealtimeAgentLike {\n ID: string;\n}\n\n/**\n * Loads the pairing rows for one co-agent from {@link AIEngineBase}'s cached\n * `MJ: AI Agent Co Agents` metadata (provider-scoped engine instance, lazy `Config`),\n * in `Sequence` order. The engine's BaseEntity-event reactivity keeps the cache fresh —\n * no per-call RunView round-trip.\n *\n * Tolerant by design: any failure degrades to an EMPTY list — i.e. \"no pairing\n * constraint\". The client gate is pure disclosure; the server independently validates\n * the co-agent/target pairing at session mint.\n */\nexport async function LoadCoAgentPairings(provider: IMetadataProvider, coAgentId: string): Promise<RealtimePairedAgentRow[]> {\n const id = coAgentId?.trim() ?? '';\n if (id.length === 0) {\n return [];\n }\n try {\n const engine = AIEngineBase.GetProviderInstance<AIEngineBase>(provider, AIEngineBase) as AIEngineBase;\n await engine.Config(false, undefined, provider);\n const rows = (engine.AgentCoAgents ?? [])\n .filter(p =>\n p.Type === 'CoAgent' &&\n p.Status === 'Active' &&\n p.TargetAgentID != null &&\n UUIDsEqual(p.CoAgentID, id))\n .map<RealtimePairedAgentRow>(p => ({\n ID: p.ID,\n CoAgentID: p.CoAgentID,\n TargetAgentID: p.TargetAgentID!,\n TargetAgent: p.TargetAgent ?? null,\n IsDefault: p.IsDefault,\n Sequence: p.Sequence ?? null\n }));\n return SortPairings(rows);\n } catch (error) {\n console.warn('[RealtimePairing] Co-agent pairing lookup unavailable — treating co-agent as universal:', error);\n return [];\n }\n}\n\n/**\n * Returns the pairing rows in canonical display order: `Sequence` ascending (null/missing\n * sequences last), ties broken by target-agent name. Pure; never mutates the input.\n */\nexport function SortPairings(pairings: readonly RealtimePairedAgentRow[]): RealtimePairedAgentRow[] {\n return [...pairings].sort((a, b) => {\n const aSeq = a.Sequence ?? Number.MAX_SAFE_INTEGER;\n const bSeq = b.Sequence ?? Number.MAX_SAFE_INTEGER;\n if (aSeq !== bSeq) {\n return aSeq - bSeq;\n }\n return (a.TargetAgent ?? '').localeCompare(b.TargetAgent ?? '');\n });\n}\n\n/**\n * Applies the pairing constraint to a candidate target-agent list:\n * - ZERO pairing rows → the co-agent is universal; the candidates are returned unchanged\n * (today's flow untouched).\n * - One or more rows → only candidates that appear among the pairing targets are\n * returned, ordered by the pairings' `Sequence`. Paired targets the user can't see /\n * run (absent from `agents`) are silently dropped.\n *\n * Pure and UUID-casing safe (`NormalizeUUID` keys).\n */\nexport function ConstrainTargetsToPairings<T extends RealtimeAgentLike>(\n agents: readonly T[],\n pairings: readonly RealtimePairedAgentRow[]\n): T[] {\n if (!pairings || pairings.length === 0) {\n return [...agents];\n }\n const agentsById = new Map<string, T>();\n for (const agent of agents) {\n agentsById.set(NormalizeUUID(agent.ID), agent);\n }\n const constrained: T[] = [];\n const seen = new Set<string>();\n for (const pairing of SortPairings(pairings)) {\n const key = NormalizeUUID(pairing.TargetAgentID);\n const agent = agentsById.get(key);\n if (agent && !seen.has(key)) {\n seen.add(key);\n constrained.push(agent);\n }\n }\n return constrained;\n}\n\n/**\n * The co-agent's default target from its pairing rows — the first `IsDefault` row in\n * `Sequence` order (server enforces at most one, but the helper stays deterministic\n * regardless). `null` when there are no rows or none is marked default.\n */\nexport function DefaultPairedTargetId(pairings: readonly RealtimePairedAgentRow[]): string | null {\n for (const pairing of SortPairings(pairings ?? [])) {\n if (pairing.IsDefault) {\n return pairing.TargetAgentID;\n }\n }\n return null;\n}\n\n/**\n * Whether the pairing rows permit `targetAgentId` as a target. ZERO rows means the\n * co-agent is universal (always allowed); otherwise the target must appear among the\n * rows. Missing/empty target id is never allowed against a constrained co-agent.\n */\nexport function PairingsAllowTarget(pairings: readonly RealtimePairedAgentRow[], targetAgentId: string | null | undefined): boolean {\n if (!pairings || pairings.length === 0) {\n return true;\n }\n if (!targetAgentId) {\n return false;\n }\n return pairings.some(p => UUIDsEqual(p.TargetAgentID, targetAgentId));\n}\n\n/**\n * Filters an agent list down to ACTIVE Realtime-type **co-agent** candidates. The input\n * is expected to already be the run-permission-filtered active-agent set the host uses\n * for routing/@mentions, so this only applies the agent-TYPE cut (trim +\n * case-insensitive match on the denormalized `Type` name).\n */\nexport function FilterRealtimeCoAgents<T extends { Type: string | null }>(agents: readonly T[]): T[] {\n return agents.filter(a => (a.Type ?? '').trim().toLowerCase() === 'realtime');\n}\n\n/**\n * A selectable realtime model option in the voice picker (narrow read-only projection of\n * `MJ: AI Models` — only the fields the dropdown renders).\n */\nexport interface RealtimeModelOption {\n /** The `MJ: AI Models` row id. */\n ID: string;\n /** The model's display name. */\n Name: string;\n}\n\n/**\n * The minimal `MJ: AI Models` row shape {@link BuildRealtimeModelOptions} consumes —\n * structurally satisfied by `AIEngineBase`'s cached `Models` entities.\n */\nexport interface VoiceModelCandidate {\n /** The `MJ: AI Models` row id. */\n ID: string;\n /** The model's display name. */\n Name: string;\n /** Denormalized model-type name (e.g. 'LLM', 'Realtime'). */\n AIModelType: string | null;\n /** Whether the model is active and selectable. */\n IsActive: boolean;\n}\n\n/**\n * Builds the \"Voice model\" dropdown options from a cached model list: ACTIVE models whose\n * type is `Realtime` (trim + case-insensitive — SQL-collation parity with the previous\n * `IsActive = 1 AND AIModelType = 'Realtime'` filter), projected to {@link RealtimeModelOption}\n * and sorted by Name. Pure; never mutates the input.\n */\nexport function BuildRealtimeModelOptions(models: ReadonlyArray<VoiceModelCandidate>): RealtimeModelOption[] {\n return models\n .filter(m => m.IsActive && (m.AIModelType ?? '').trim().toLowerCase() === 'realtime')\n .map<RealtimeModelOption>(m => ({ ID: m.ID, Name: m.Name }))\n .sort((a, b) => (a.Name ?? '').localeCompare(b.Name ?? ''));\n}\n\n/**\n * Builds the `configOverridesJson` payload for the `StartRealtimeClientSession` mint from\n * what the (authorization-gated) pickers chose — an explicit realtime model preference and/or a\n * per-provider voice. The envelope (`{\"realtime\":{\"modelPreference\":\"<id>\",\"voice\":{\"providers\":\n * {\"openai\":{\"voice\":\"<v>\"}}}}}`) is the pinned server contract, merged into the effective config\n * server-side (highest precedence).\n *\n * @param preferredModelId The explicit `MJ: AI Models` id, or null/empty for none.\n * @param preferredVoice The provider-native voice id (e.g. `echo`), or null/empty for none.\n * @returns The JSON string, or `null` when nothing was overridden (keeps the mint identical to default).\n */\nexport function BuildRealtimeConfigOverridesJson(\n preferredModelId: string | null | undefined,\n preferredVoice?: string | null | undefined,\n): string | null {\n const modelId = preferredModelId?.trim() ?? '';\n const voice = preferredVoice?.trim() ?? '';\n if (modelId.length === 0 && voice.length === 0) {\n return null;\n }\n const realtime: { modelPreference?: string; voice?: { providers: Record<string, { voice: string }> } } = {};\n if (modelId.length > 0) {\n realtime.modelPreference = modelId;\n }\n if (voice.length > 0) {\n // Per-provider voice shape matches GetProviderVoiceSettings; `openai` is the realtime provider today.\n realtime.voice = { providers: { openai: { voice } } };\n }\n return JSON.stringify({ realtime });\n}\n"]}
1
+ {"version":3,"file":"realtime-pairing.js","sourceRoot":"","sources":["../../../src/lib/services/realtime-pairing.ts"],"names":[],"mappings":"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","sourcesContent":["import { IMetadataProvider } from '@memberjunction/core';\nimport { NormalizeUUID, UUIDsEqual } from '@memberjunction/global';\nimport { AIEngineBase } from '@memberjunction/ai-engine-base';\n\n/**\n * The narrow read-only projection of an `MJ: AI Agent Co Agents` row the voice UI consumes\n * (loaded via {@link LoadCoAgentPairings} from {@link AIEngineBase}'s cached `AgentCoAgents`;\n * only **Active** rows of relationship **Type `'CoAgent'`** with a specific agent target —\n * type-level rows express the resolution-chain default, not a target restriction, and\n * reserved relationship types are ignored until their features ship). Pairing semantics: a\n * co-agent with ZERO such rows is **universal** (it can front any target agent — the\n * zero-config default); a co-agent WITH rows may front exactly the listed targets, with the\n * `IsDefault` row as its preselected target.\n */\nexport interface RealtimePairedAgentRow {\n /** The pairing row id. */\n ID: string;\n /** The Realtime-type co-agent the pairing belongs to. */\n CoAgentID: string;\n /** The target agent this co-agent may front. */\n TargetAgentID: string;\n /** Denormalized target-agent display name (from the base view). */\n TargetAgent: string | null;\n /** When true, this target is the co-agent's default (at most one per co-agent). */\n IsDefault: boolean;\n /** Display/list ordering of the pairing rows. */\n Sequence: number | null;\n}\n\n/** Minimal agent shape the pure pairing/co-agent helpers operate on. */\nexport interface RealtimeAgentLike {\n ID: string;\n}\n\n/**\n * Loads the pairing rows for one co-agent from {@link AIEngineBase}'s cached\n * `MJ: AI Agent Co Agents` metadata (provider-scoped engine instance, lazy `Config`),\n * in `Sequence` order. The engine's BaseEntity-event reactivity keeps the cache fresh —\n * no per-call RunView round-trip.\n *\n * Tolerant by design: any failure degrades to an EMPTY list — i.e. \"no pairing\n * constraint\". The client gate is pure disclosure; the server independently validates\n * the co-agent/target pairing at session mint.\n */\nexport async function LoadCoAgentPairings(provider: IMetadataProvider, coAgentId: string): Promise<RealtimePairedAgentRow[]> {\n const id = coAgentId?.trim() ?? '';\n if (id.length === 0) {\n return [];\n }\n try {\n const engine = AIEngineBase.GetProviderInstance<AIEngineBase>(provider, AIEngineBase) as AIEngineBase;\n await engine.Config(false, undefined, provider);\n const rows = (engine.AgentCoAgents ?? [])\n .filter(p =>\n p.Type === 'CoAgent' &&\n p.Status === 'Active' &&\n p.TargetAgentID != null &&\n UUIDsEqual(p.CoAgentID, id))\n .map<RealtimePairedAgentRow>(p => ({\n ID: p.ID,\n CoAgentID: p.CoAgentID,\n TargetAgentID: p.TargetAgentID!,\n TargetAgent: p.TargetAgent ?? null,\n IsDefault: p.IsDefault,\n Sequence: p.Sequence ?? null\n }));\n return SortPairings(rows);\n } catch (error) {\n console.warn('[RealtimePairing] Co-agent pairing lookup unavailable — treating co-agent as universal:', error);\n return [];\n }\n}\n\n/**\n * Returns the pairing rows in canonical display order: `Sequence` ascending (null/missing\n * sequences last), ties broken by target-agent name. Pure; never mutates the input.\n */\nexport function SortPairings(pairings: readonly RealtimePairedAgentRow[]): RealtimePairedAgentRow[] {\n return [...pairings].sort((a, b) => {\n const aSeq = a.Sequence ?? Number.MAX_SAFE_INTEGER;\n const bSeq = b.Sequence ?? Number.MAX_SAFE_INTEGER;\n if (aSeq !== bSeq) {\n return aSeq - bSeq;\n }\n return (a.TargetAgent ?? '').localeCompare(b.TargetAgent ?? '');\n });\n}\n\n/**\n * Applies the pairing constraint to a candidate target-agent list:\n * - ZERO pairing rows → the co-agent is universal; the candidates are returned unchanged\n * (today's flow untouched).\n * - One or more rows → only candidates that appear among the pairing targets are\n * returned, ordered by the pairings' `Sequence`. Paired targets the user can't see /\n * run (absent from `agents`) are silently dropped.\n *\n * Pure and UUID-casing safe (`NormalizeUUID` keys).\n */\nexport function ConstrainTargetsToPairings<T extends RealtimeAgentLike>(\n agents: readonly T[],\n pairings: readonly RealtimePairedAgentRow[]\n): T[] {\n if (!pairings || pairings.length === 0) {\n return [...agents];\n }\n const agentsById = new Map<string, T>();\n for (const agent of agents) {\n agentsById.set(NormalizeUUID(agent.ID), agent);\n }\n const constrained: T[] = [];\n const seen = new Set<string>();\n for (const pairing of SortPairings(pairings)) {\n const key = NormalizeUUID(pairing.TargetAgentID);\n const agent = agentsById.get(key);\n if (agent && !seen.has(key)) {\n seen.add(key);\n constrained.push(agent);\n }\n }\n return constrained;\n}\n\n/**\n * The co-agent's default target from its pairing rows — the first `IsDefault` row in\n * `Sequence` order (server enforces at most one, but the helper stays deterministic\n * regardless). `null` when there are no rows or none is marked default.\n */\nexport function DefaultPairedTargetId(pairings: readonly RealtimePairedAgentRow[]): string | null {\n for (const pairing of SortPairings(pairings ?? [])) {\n if (pairing.IsDefault) {\n return pairing.TargetAgentID;\n }\n }\n return null;\n}\n\n/**\n * Whether the pairing rows permit `targetAgentId` as a target. ZERO rows means the\n * co-agent is universal (always allowed); otherwise the target must appear among the\n * rows. Missing/empty target id is never allowed against a constrained co-agent.\n */\nexport function PairingsAllowTarget(pairings: readonly RealtimePairedAgentRow[], targetAgentId: string | null | undefined): boolean {\n if (!pairings || pairings.length === 0) {\n return true;\n }\n if (!targetAgentId) {\n return false;\n }\n return pairings.some(p => UUIDsEqual(p.TargetAgentID, targetAgentId));\n}\n\n/**\n * Filters an agent list down to ACTIVE Realtime-type **co-agent** candidates. The input\n * is expected to already be the run-permission-filtered active-agent set the host uses\n * for routing/@mentions, so this only applies the agent-TYPE cut (trim +\n * case-insensitive match on the denormalized `Type` name).\n */\nexport function FilterRealtimeCoAgents<T extends { Type: string | null }>(agents: readonly T[]): T[] {\n return agents.filter(a => (a.Type ?? '').trim().toLowerCase() === 'realtime');\n}\n\n/**\n * A selectable realtime model option in the voice picker (narrow read-only projection of\n * `MJ: AI Models` — only the fields the dropdown renders).\n */\nexport interface RealtimeModelOption {\n /** The `MJ: AI Models` row id. */\n ID: string;\n /** The model's display name. */\n Name: string;\n}\n\n/**\n * The minimal `MJ: AI Models` row shape {@link BuildRealtimeModelOptions} consumes —\n * structurally satisfied by `AIEngineBase`'s cached `Models` entities.\n */\nexport interface VoiceModelCandidate {\n /** The `MJ: AI Models` row id. */\n ID: string;\n /** The model's display name. */\n Name: string;\n /** Denormalized model-type name (e.g. 'LLM', 'Realtime'). */\n AIModelType: string | null;\n /** Whether the model is active and selectable. */\n IsActive: boolean;\n}\n\n/**\n * Builds the \"Voice model\" dropdown options from a cached model list: ACTIVE models whose\n * type is `Realtime` (trim + case-insensitive — SQL-collation parity with the previous\n * `IsActive = 1 AND AIModelType = 'Realtime'` filter), projected to {@link RealtimeModelOption}\n * and sorted by Name. Pure; never mutates the input.\n */\nexport function BuildRealtimeModelOptions(models: ReadonlyArray<VoiceModelCandidate>): RealtimeModelOption[] {\n return models\n .filter(m => m.IsActive && (m.AIModelType ?? '').trim().toLowerCase() === 'realtime')\n .map<RealtimeModelOption>(m => ({ ID: m.ID, Name: m.Name }))\n .sort((a, b) => (a.Name ?? '').localeCompare(b.Name ?? ''));\n}\n\n/**\n * Builds the `configOverridesJson` payload for the `StartRealtimeClientSession` mint from\n * what the (authorization-gated) pickers chose — an explicit realtime model preference and/or a\n * voice. The envelope (`{\"realtime\":{\"modelPreference\":\"<id>\",\"voice\":{\"default\":{\"voice\":\"<v>\"}}}}`)\n * is the pinned server contract, merged into the effective config server-side (highest precedence).\n *\n * The voice is filed PROVIDER-AGNOSTICALLY: the browser has a model id, never a vendor `DriverClass`\n * (`GetRealtimeModelVoices` does not return one), so naming a provider here would be a guess that\n * silently voided the setting for every non-OpenAI vendor. The server files it onto whichever driver\n * it resolves. Kept in lockstep with the server-side `BuildRealtimeOverridesJson` in\n * `@memberjunction/ai-agents` by `realtime-convergence-drift.test.ts`. See issue #3530.\n *\n * @param preferredModelId The explicit `MJ: AI Models` id, or null/empty for none.\n * @param preferredVoice The provider-native voice id (e.g. `echo`), or null/empty for none.\n * @returns The JSON string, or `null` when nothing was overridden (keeps the mint identical to default).\n */\nexport function BuildRealtimeConfigOverridesJson(\n preferredModelId: string | null | undefined,\n preferredVoice?: string | null | undefined,\n): string | null {\n const modelId = preferredModelId?.trim() ?? '';\n const voice = preferredVoice?.trim() ?? '';\n if (modelId.length === 0 && voice.length === 0) {\n return null;\n }\n const realtime: { modelPreference?: string; voice?: { default: { voice: string } } } = {};\n if (modelId.length > 0) {\n realtime.modelPreference = modelId;\n }\n if (voice.length > 0) {\n realtime.voice = { default: { voice } };\n }\n return JSON.stringify({ realtime });\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@memberjunction/ng-conversations",
3
- "version": "6.1.0-edge.1",
3
+ "version": "6.1.0-edge.2",
4
4
  "description": "MemberJunction: Conversation, Collection, and Artifact management components for any Angular application",
5
5
  "main": "./dist/public-api.js",
6
6
  "typings": "./dist/public-api.d.ts",
@@ -15,7 +15,7 @@
15
15
  "@angular/compiler-cli": "21.1.3",
16
16
  "sass": "^1.97.3",
17
17
  "vitest": "^4.0.18",
18
- "@memberjunction/ng-test-utils": "6.1.0-edge.1"
18
+ "@memberjunction/ng-test-utils": "6.1.0-edge.2"
19
19
  },
20
20
  "peerDependencies": {
21
21
  "@angular/common": "^21.1.3",
@@ -26,35 +26,35 @@
26
26
  "dependencies": {
27
27
  "@angular/animations": "21.1.3",
28
28
  "@angular/cdk": "21.1.3",
29
- "@memberjunction/ai": "6.1.0-edge.1",
30
- "@memberjunction/ai-agent-client": "6.1.0-edge.1",
31
- "@memberjunction/ai-core-plus": "6.1.0-edge.1",
32
- "@memberjunction/ai-engine-base": "6.1.0-edge.1",
33
- "@memberjunction/ai-realtime-client": "6.1.0-edge.1",
34
- "@memberjunction/conversations-runtime": "6.1.0-edge.1",
35
- "@memberjunction/core": "6.1.0-edge.1",
36
- "@memberjunction/core-entities": "6.1.0-edge.1",
37
- "@memberjunction/global": "6.1.0-edge.1",
38
- "@memberjunction/graphql-dataprovider": "6.1.0-edge.1",
39
- "@memberjunction/interactive-component-types": "6.1.0-edge.1",
40
- "@memberjunction/ng-agent-client": "6.1.0-edge.1",
41
- "@memberjunction/ng-artifacts": "6.1.0-edge.1",
42
- "@memberjunction/ng-base-types": "6.1.0-edge.1",
43
- "@memberjunction/ng-code-editor": "6.1.0-edge.1",
44
- "@memberjunction/ng-composer": "6.1.0-edge.1",
45
- "@memberjunction/ng-container-directives": "6.1.0-edge.1",
46
- "@memberjunction/ng-forms": "6.1.0-edge.1",
47
- "@memberjunction/ng-markdown": "6.1.0-edge.1",
48
- "@memberjunction/ng-media-player": "6.1.0-edge.1",
49
- "@memberjunction/ng-notifications": "6.1.0-edge.1",
50
- "@memberjunction/ng-resource-permissions": "6.1.0-edge.1",
51
- "@memberjunction/ng-shared-generic": "6.1.0-edge.1",
52
- "@memberjunction/ng-task-graph-editor": "6.1.0-edge.1",
53
- "@memberjunction/ng-tasks": "6.1.0-edge.1",
54
- "@memberjunction/ng-testing": "6.1.0-edge.1",
55
- "@memberjunction/ng-ui-components": "6.1.0-edge.1",
56
- "@memberjunction/ng-user-routines": "6.1.0-edge.1",
57
- "@memberjunction/ng-whiteboard": "6.1.0-edge.1",
29
+ "@memberjunction/ai": "6.1.0-edge.2",
30
+ "@memberjunction/ai-agent-client": "6.1.0-edge.2",
31
+ "@memberjunction/ai-core-plus": "6.1.0-edge.2",
32
+ "@memberjunction/ai-engine-base": "6.1.0-edge.2",
33
+ "@memberjunction/ai-realtime-client": "6.1.0-edge.2",
34
+ "@memberjunction/conversations-runtime": "6.1.0-edge.2",
35
+ "@memberjunction/core": "6.1.0-edge.2",
36
+ "@memberjunction/core-entities": "6.1.0-edge.2",
37
+ "@memberjunction/global": "6.1.0-edge.2",
38
+ "@memberjunction/graphql-dataprovider": "6.1.0-edge.2",
39
+ "@memberjunction/interactive-component-types": "6.1.0-edge.2",
40
+ "@memberjunction/ng-agent-client": "6.1.0-edge.2",
41
+ "@memberjunction/ng-artifacts": "6.1.0-edge.2",
42
+ "@memberjunction/ng-base-types": "6.1.0-edge.2",
43
+ "@memberjunction/ng-code-editor": "6.1.0-edge.2",
44
+ "@memberjunction/ng-composer": "6.1.0-edge.2",
45
+ "@memberjunction/ng-container-directives": "6.1.0-edge.2",
46
+ "@memberjunction/ng-forms": "6.1.0-edge.2",
47
+ "@memberjunction/ng-markdown": "6.1.0-edge.2",
48
+ "@memberjunction/ng-media-player": "6.1.0-edge.2",
49
+ "@memberjunction/ng-notifications": "6.1.0-edge.2",
50
+ "@memberjunction/ng-resource-permissions": "6.1.0-edge.2",
51
+ "@memberjunction/ng-shared-generic": "6.1.0-edge.2",
52
+ "@memberjunction/ng-task-graph-editor": "6.1.0-edge.2",
53
+ "@memberjunction/ng-tasks": "6.1.0-edge.2",
54
+ "@memberjunction/ng-testing": "6.1.0-edge.2",
55
+ "@memberjunction/ng-ui-components": "6.1.0-edge.2",
56
+ "@memberjunction/ng-user-routines": "6.1.0-edge.2",
57
+ "@memberjunction/ng-whiteboard": "6.1.0-edge.2",
58
58
  "angular-split": "^20.0.0",
59
59
  "rxjs": "^7.8.2",
60
60
  "tslib": "^2.8.1"