@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.
- package/dist/lib/components/composer/ai-composer.component.d.ts +11 -2
- package/dist/lib/components/composer/ai-composer.component.d.ts.map +1 -1
- package/dist/lib/components/composer/ai-composer.component.js +48 -32
- package/dist/lib/components/composer/ai-composer.component.js.map +1 -1
- package/dist/lib/components/message/workflow-plan-card.component.d.ts +52 -2
- package/dist/lib/components/message/workflow-plan-card.component.d.ts.map +1 -1
- package/dist/lib/components/message/workflow-plan-card.component.js +79 -15
- package/dist/lib/components/message/workflow-plan-card.component.js.map +1 -1
- package/dist/lib/services/realtime-pairing.d.ts +8 -3
- package/dist/lib/services/realtime-pairing.d.ts.map +1 -1
- package/dist/lib/services/realtime-pairing.js +9 -5
- package/dist/lib/services/realtime-pairing.js.map +1 -1
- package/package.json +31 -31
|
@@ -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":"AAAA,OAAO,EAAa,YAAY,EAA4B,MAAM,eAAe,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACnE,OAAO,EAAE,iBAAiB,EACxB,uBAAuB,EACvB,sBAAsB,EACtB,wBAAwB,EACxB,iBAAiB,
|
|
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?.
|
|
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?.
|
|
159
|
+
return this.inputBox?.GetPendingAttachments() || [];
|
|
151
160
|
}
|
|
152
161
|
/** Open the attachment file picker programmatically. */
|
|
153
162
|
openFilePicker() {
|
|
154
|
-
this.inputBox?.
|
|
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:
|
|
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("
|
|
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("
|
|
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
|
-
[
|
|
184
|
-
[
|
|
185
|
-
[
|
|
186
|
-
[
|
|
187
|
-
[
|
|
192
|
+
[Placeholder]="placeholder"
|
|
193
|
+
[Disabled]="disabled"
|
|
194
|
+
[Value]="value"
|
|
195
|
+
[ShowCharacterCount]="showCharacterCount"
|
|
196
|
+
[EnableMentions]="enableMentions"
|
|
188
197
|
[TriggerProviders]="ActiveTriggerProviders"
|
|
189
198
|
[Provider]="Provider"
|
|
190
|
-
[
|
|
191
|
-
[
|
|
192
|
-
[
|
|
193
|
-
[
|
|
194
|
-
[
|
|
195
|
-
[
|
|
196
|
-
[
|
|
197
|
-
[
|
|
198
|
-
[
|
|
199
|
-
[
|
|
200
|
-
[
|
|
201
|
-
|
|
202
|
-
(
|
|
203
|
-
(
|
|
204
|
-
(
|
|
205
|
-
(
|
|
206
|
-
(
|
|
207
|
-
(
|
|
208
|
-
(
|
|
209
|
-
(
|
|
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:
|
|
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":"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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} 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
|
-
|
|
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;
|
|
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 "@
|
|
23
|
-
import * as i2 from "@memberjunction/ng-
|
|
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, "
|
|
30
|
-
i0.ɵɵlistener("
|
|
31
|
-
i0.ɵɵ
|
|
32
|
-
i0.ɵɵ
|
|
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,
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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.
|
|
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
|
|
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:
|
|
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
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
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
|
|
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
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
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
|
-
|
|
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":"AACA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAgC9D;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,QAA2B,EAAE,SAAiB;IACtF,MAAM,EAAE,GAAG,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACnC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,mBAAmB,CAAe,QAAQ,EAAE,YAAY,CAAiB,CAAC;QACtG,MAAM,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC;QAChD,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,aAAa,IAAI,EAAE,CAAC;aACtC,MAAM,CAAC,CAAC,CAAC,EAAE,CACV,CAAC,CAAC,IAAI,KAAK,SAAS;YACpB,CAAC,CAAC,MAAM,KAAK,QAAQ;YACrB,CAAC,CAAC,aAAa,IAAI,IAAI;YACvB,UAAU,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;aAC7B,GAAG,CAAyB,CAAC,CAAC,EAAE,CAAC,CAAC;YACjC,EAAE,EAAE,CAAC,CAAC,EAAE;YACR,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,aAAa,EAAE,CAAC,CAAC,aAAc;YAC/B,WAAW,EAAE,CAAC,CAAC,WAAW,IAAI,IAAI;YAClC,SAAS,EAAE,CAAC,CAAC,SAAS;YACtB,QAAQ,EAAE,CAAC,CAAC,QAAQ,IAAI,IAAI;SAC7B,CAAC,CAAC,CAAC;QACN,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,yFAAyF,EAAE,KAAK,CAAC,CAAC;QAC/G,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,QAA2C;IACtE,OAAO,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QACjC,MAAM,IAAI,GAAG,CAAC,CAAC,QAAQ,IAAI,MAAM,CAAC,gBAAgB,CAAC;QACnD,MAAM,IAAI,GAAG,CAAC,CAAC,QAAQ,IAAI,MAAM,CAAC,gBAAgB,CAAC;QACnD,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,OAAO,IAAI,GAAG,IAAI,CAAC;QACrB,CAAC;QACD,OAAO,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,0BAA0B,CACxC,MAAoB,EACpB,QAA2C;IAE3C,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;IACrB,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAa,CAAC;IACxC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;IACjD,CAAC;IACD,MAAM,WAAW,GAAQ,EAAE,CAAC;IAC5B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,OAAO,IAAI,YAAY,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7C,MAAM,GAAG,GAAG,aAAa,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACjD,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACd,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAA2C;IAC/E,KAAK,MAAM,OAAO,IAAI,YAAY,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,CAAC;QACnD,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,OAAO,OAAO,CAAC,aAAa,CAAC;QAC/B,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAA2C,EAAE,aAAwC;IACvH,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,EAAE,aAAa,CAAC,CAAC,CAAC;AACxE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAoC,MAAoB;IAC5F,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,UAAU,CAAC,CAAC;AAChF,CAAC;AA4BD;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB,CAAC,MAA0C;IAClF,OAAO,MAAM;SACV,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,UAAU,CAAC;SACpF,GAAG,CAAsB,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;SAC3D,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;AAChE,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gCAAgC,CAC9C,gBAA2C,EAC3C,cAA0C;IAE1C,MAAM,OAAO,GAAG,gBAAgB,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC/C,MAAM,KAAK,GAAG,cAAc,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/C,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,QAAQ,GAA2F,EAAE,CAAC;IAC5G,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,QAAQ,CAAC,eAAe,GAAG,OAAO,CAAC;IACrC,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,sGAAsG;QACtG,QAAQ,CAAC,KAAK,GAAG,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC;IACxD,CAAC;IACD,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;AACtC,CAAC","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.
|
|
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.
|
|
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.
|
|
30
|
-
"@memberjunction/ai-agent-client": "6.1.0-edge.
|
|
31
|
-
"@memberjunction/ai-core-plus": "6.1.0-edge.
|
|
32
|
-
"@memberjunction/ai-engine-base": "6.1.0-edge.
|
|
33
|
-
"@memberjunction/ai-realtime-client": "6.1.0-edge.
|
|
34
|
-
"@memberjunction/conversations-runtime": "6.1.0-edge.
|
|
35
|
-
"@memberjunction/core": "6.1.0-edge.
|
|
36
|
-
"@memberjunction/core-entities": "6.1.0-edge.
|
|
37
|
-
"@memberjunction/global": "6.1.0-edge.
|
|
38
|
-
"@memberjunction/graphql-dataprovider": "6.1.0-edge.
|
|
39
|
-
"@memberjunction/interactive-component-types": "6.1.0-edge.
|
|
40
|
-
"@memberjunction/ng-agent-client": "6.1.0-edge.
|
|
41
|
-
"@memberjunction/ng-artifacts": "6.1.0-edge.
|
|
42
|
-
"@memberjunction/ng-base-types": "6.1.0-edge.
|
|
43
|
-
"@memberjunction/ng-code-editor": "6.1.0-edge.
|
|
44
|
-
"@memberjunction/ng-composer": "6.1.0-edge.
|
|
45
|
-
"@memberjunction/ng-container-directives": "6.1.0-edge.
|
|
46
|
-
"@memberjunction/ng-forms": "6.1.0-edge.
|
|
47
|
-
"@memberjunction/ng-markdown": "6.1.0-edge.
|
|
48
|
-
"@memberjunction/ng-media-player": "6.1.0-edge.
|
|
49
|
-
"@memberjunction/ng-notifications": "6.1.0-edge.
|
|
50
|
-
"@memberjunction/ng-resource-permissions": "6.1.0-edge.
|
|
51
|
-
"@memberjunction/ng-shared-generic": "6.1.0-edge.
|
|
52
|
-
"@memberjunction/ng-task-graph-editor": "6.1.0-edge.
|
|
53
|
-
"@memberjunction/ng-tasks": "6.1.0-edge.
|
|
54
|
-
"@memberjunction/ng-testing": "6.1.0-edge.
|
|
55
|
-
"@memberjunction/ng-ui-components": "6.1.0-edge.
|
|
56
|
-
"@memberjunction/ng-user-routines": "6.1.0-edge.
|
|
57
|
-
"@memberjunction/ng-whiteboard": "6.1.0-edge.
|
|
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"
|