@memberjunction/ai-agent-manager 6.2.0-edge.1 → 6.2.0-edge.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,13 @@
1
+ import { CollectDecisionStepKeys, CompileFlowToTaskGraph, ValidateTaskGraphSpec } from '@memberjunction/ai-core-plus';
2
+ import { UUIDsEqual } from '@memberjunction/global';
1
3
  /** True when this step repeats a body rather than being one. */
2
4
  export function IsLoopStep(step) {
3
5
  return step.StepType === 'ForEach' || step.StepType === 'While';
4
6
  }
7
+ /** True when this step is a fast typed decision. */
8
+ export function IsDecisionStep(step) {
9
+ return step.StepType === 'Decision';
10
+ }
5
11
  /**
6
12
  * Which field carries the body's id, per body type.
7
13
  *
@@ -99,4 +105,212 @@ function parseLoopConfiguration(step, where, errors) {
99
105
  return null;
100
106
  }
101
107
  }
108
+ /**
109
+ * Normalizes a Decision step's configuration so the aliases a model tends to write (`text` for a
110
+ * question's `instructions`, and `text` for an option's `description`) read as the fields
111
+ * `ReadFlowDecisionStepConfiguration` expects. Returns a copy; the input is not changed.
112
+ */
113
+ export function NormalizeDecisionConfiguration(raw) {
114
+ const cloned = structuredClone(raw);
115
+ const questions = cloned.questions;
116
+ if (!isPlainObject(questions)) {
117
+ return cloned;
118
+ }
119
+ for (const question of Object.values(questions)) {
120
+ if (!isPlainObject(question))
121
+ continue;
122
+ copyAlias(question, 'text', 'instructions');
123
+ if (Array.isArray(question.options)) {
124
+ for (const option of question.options) {
125
+ if (isPlainObject(option))
126
+ copyAlias(option, 'text', 'description');
127
+ }
128
+ }
129
+ }
130
+ return cloned;
131
+ }
132
+ function isPlainObject(value) {
133
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
134
+ }
135
+ /** Sets `record[to]` from `record[from]` when only the alias is present. */
136
+ function copyAlias(record, from, to) {
137
+ if (!record[to] && typeof record[from] === 'string') {
138
+ record[to] = record[from];
139
+ }
140
+ }
141
+ /**
142
+ * A Decision step's configuration as JSON text, normalized. A model may write the configuration as
143
+ * an object rather than as JSON text, as it may for a loop; text that is not a JSON object is
144
+ * returned unchanged, so `ReadFlowDecisionStepConfiguration` reports why it cannot be read.
145
+ */
146
+ export function DecisionConfigurationText(raw) {
147
+ if (raw == null)
148
+ return null;
149
+ if (typeof raw !== 'string') {
150
+ // Typed as an object, but it is a model's JSON: an array or a number can arrive here too, and
151
+ // is written as the JSON it is so the reader can say it is not an object.
152
+ return isPlainObject(raw) ? JSON.stringify(NormalizeDecisionConfiguration(raw)) : JSON.stringify(raw);
153
+ }
154
+ try {
155
+ const parsed = JSON.parse(raw);
156
+ return isPlainObject(parsed) ? JSON.stringify(NormalizeDecisionConfiguration(parsed)) : raw;
157
+ }
158
+ catch {
159
+ return raw;
160
+ }
161
+ }
162
+ /**
163
+ * A step's `Configuration` as the JSON text `AIAgentStep.Configuration` stores, which is what the
164
+ * runtime reads. An object is written as text, and a Decision step's is normalized
165
+ * ({@link DecisionConfigurationText}). `AgentSpecSync` saves this and {@link ValidateFlowGraph}
166
+ * compiles it, so what is validated is what is saved.
167
+ */
168
+ export function StepConfigurationText(step) {
169
+ if (step.StepType === 'Decision')
170
+ return DecisionConfigurationText(step.Configuration);
171
+ const raw = step.Configuration;
172
+ if (raw == null || raw === '')
173
+ return null;
174
+ return typeof raw === 'string' ? raw : JSON.stringify(raw);
175
+ }
176
+ /**
177
+ * Every problem the runtime would refuse a Flow agent's steps and paths for, as messages that name
178
+ * the steps. Empty when the flow can run.
179
+ *
180
+ * The flow is compiled with `CompileFlowToTaskGraph` and the result validated with
181
+ * `ValidateTaskGraphSpec`, exactly as a Flow agent is before it runs, so a flow this passes is one
182
+ * the runtime runs and a flow it refuses is one the runtime refuses. That covers every Decision step
183
+ * the flow can reach: its configuration, its key, each condition that reads `decisions.<key>` (the
184
+ * key, the question, and whether that question's answer has the field read), and a fork on a Choice
185
+ * question that has no path for one of its options.
186
+ *
187
+ * Two checks come from outside the compiler, because it cannot see what they catch:
188
+ * - a path whose origin or destination names no step, which the compiler drops without a word and
189
+ * `AgentSpecSync` cannot save;
190
+ * - a key two Decision steps share when the flow cannot reach one of them. The compiler checks only
191
+ * the steps it compiles, but the in-run walker checks every step, since it can start at any.
192
+ *
193
+ * Names behind IDs are not resolved here: each ID stands in for its own name. The Architect checks
194
+ * a spec's actions, and a Decision step's prompt, separately. It also accepts a step whose sub-agent
195
+ * or inline prompt has no ID until the spec is saved, so a placeholder stands in for that ID.
196
+ *
197
+ * @param steps the flow's steps
198
+ * @param paths the flow's paths: `AgentSpec.Paths`, which is what `AgentSpecSync` saves
199
+ * @param workflowName the agent's name, which the compiled workflow carries
200
+ */
201
+ export function ValidateFlowGraph(steps, paths, workflowName) {
202
+ const compilerSteps = steps.map(toCompilerStep);
203
+ const resolved = resolvePaths(paths, steps);
204
+ const compiled = CompileFlowToTaskGraph(compilerSteps, resolved.Paths, compilerOptions(workflowName));
205
+ return [
206
+ ...resolved.Errors,
207
+ // Over every step, as the in-run walker checks them. The compiler's own check covers only the
208
+ // steps it compiles, so every step it flags is flagged here too.
209
+ ...CollectDecisionStepKeys(compilerSteps).Errors.map(compileErrorMessage),
210
+ ...compiled.Errors.filter((e) => e.Code !== 'DuplicateDecisionKey').map(compileErrorMessage),
211
+ ...(compiled.Spec ? graphErrorMessages(compiled.Spec) : []),
212
+ ];
213
+ }
214
+ /** What stands in for an ID the Architect lets a spec leave empty until it is saved. */
215
+ const FILLED_ON_SAVE = '(set when the spec is saved)';
216
+ /** A step's identity in a spec: its ID, or its name for a step not yet saved. */
217
+ function stepRef(step) {
218
+ return step.ID || step.Name;
219
+ }
220
+ /** A spec step as the compiler reads it: the row `AgentSpecSync` would save. */
221
+ function toCompilerStep(step) {
222
+ return {
223
+ ID: stepRef(step),
224
+ Name: step.Name,
225
+ Description: step.Description ?? null,
226
+ StepType: step.StepType,
227
+ StartingStep: step.StartingStep === true,
228
+ // AgentSpecSync saves every step Active.
229
+ Status: 'Active',
230
+ ActionID: step.ActionID || null,
231
+ SubAgentID: step.SubAgentID || (runsSubAgent(step) ? FILLED_ON_SAVE : null),
232
+ PromptID: step.PromptID || (hasInlinePrompt(step) ? FILLED_ON_SAVE : null),
233
+ LoopBodyType: step.LoopBodyType,
234
+ Configuration: StepConfigurationText(step),
235
+ ActionInputMapping: mappingText(step.ActionInputMapping),
236
+ ActionOutputMapping: mappingText(step.ActionOutputMapping),
237
+ };
238
+ }
239
+ /** A Sub-Agent step, or a loop whose body is one. The Architect accepts either with no sub-agent ID yet. */
240
+ function runsSubAgent(step) {
241
+ return step.StepType === 'Sub-Agent' || (IsLoopStep(step) && step.LoopBodyType === 'Sub-Agent');
242
+ }
243
+ /** A Prompt step, or a loop whose body is one, with its prompt written inline as `PromptText`. */
244
+ function hasInlinePrompt(step) {
245
+ const runsPrompt = step.StepType === 'Prompt' || (IsLoopStep(step) && step.LoopBodyType === 'Prompt');
246
+ return runsPrompt && !!step.PromptText?.trim();
247
+ }
248
+ /** An action mapping as the JSON text the column stores. */
249
+ function mappingText(mapping) {
250
+ if (mapping == null || mapping === '')
251
+ return null;
252
+ return typeof mapping === 'string' ? mapping : JSON.stringify(mapping);
253
+ }
254
+ /**
255
+ * The spec's paths with both ends resolved to their steps, and a message for each path that names a
256
+ * step no spec step has. An end names a step by its ID, compared as a UUID, or by its name, exactly
257
+ * as written — the match `AgentSpecSync` makes when it saves the path.
258
+ */
259
+ function resolvePaths(paths, steps) {
260
+ const resolved = { Paths: [], Errors: [] };
261
+ paths.forEach((path, index) => {
262
+ const origin = findStep(steps, path.OriginStepID);
263
+ const destination = findStep(steps, path.DestinationStepID);
264
+ if (!origin || !destination) {
265
+ resolved.Errors.push(unknownEndMessage(path, !origin, !destination));
266
+ return;
267
+ }
268
+ resolved.Paths.push({
269
+ // A new path has no ID yet, and the compiler tells a fork's paths apart by ID.
270
+ ID: path.ID || `new-path-${index}`,
271
+ OriginStepID: stepRef(origin),
272
+ DestinationStepID: stepRef(destination),
273
+ Condition: path.Condition ?? null,
274
+ Priority: Number.isFinite(path.Priority) ? path.Priority : 0,
275
+ PathPoints: path.PathPoints ?? null,
276
+ });
277
+ });
278
+ return resolved;
279
+ }
280
+ /** The step a path end names, if any. */
281
+ function findStep(steps, ref) {
282
+ if (!ref)
283
+ return undefined;
284
+ return steps.find((s) => !!s.ID && UUIDsEqual(s.ID, ref)) ?? steps.find((s) => s.Name === ref);
285
+ }
286
+ function unknownEndMessage(path, originUnknown, destinationUnknown) {
287
+ const ends = [originUnknown ? 'origin' : null, destinationUnknown ? 'destination' : null].filter((e) => e !== null);
288
+ return `❌ The path from "${path.OriginStepID}" to "${path.DestinationStepID}" names no step as its ${ends.join(' or ')}. `
289
+ + 'Name each end by a step\'s exact Name, or by its ID for a step that already exists.';
290
+ }
291
+ /** The compiler's settings: names are placeholders, and a fork is an exclusive choice, as when a Flow agent runs. */
292
+ function compilerOptions(workflowName) {
293
+ const placeholderName = (id) => id;
294
+ return {
295
+ WorkflowName: workflowName?.trim() || 'Flow agent',
296
+ ResolveAgentName: placeholderName,
297
+ ResolveActionName: placeholderName,
298
+ ResolvePromptName: placeholderName,
299
+ TraversalMode: 'sequential',
300
+ };
301
+ }
302
+ function compileErrorMessage(error) {
303
+ return `❌ [${error.Code}] ${error.Message}`;
304
+ }
305
+ /**
306
+ * What `ValidateTaskGraphSpec` refuses in the compiled workflow. A compiled step is named by its
307
+ * spec step's ID where it has one, so each ID in a message becomes the step's name, as the Flow agent
308
+ * does when it reports these.
309
+ */
310
+ function graphErrorMessages(spec) {
311
+ return ValidateTaskGraphSpec(spec).Errors.map((e) => `❌ [${e.Code}] ${namedBySteps(e.Message, spec)}`);
312
+ }
313
+ function namedBySteps(message, spec) {
314
+ return spec.tasks.reduce((text, task) => (task.tempId && task.name && task.tempId !== task.name ? text.split(task.tempId).join(task.name) : text), message);
315
+ }
102
316
  //# sourceMappingURL=flow-step-validation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"flow-step-validation.js","sourceRoot":"","sources":["../src/flow-step-validation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"flow-step-validation.js","sourceRoot":"","sources":["../src/flow-step-validation.ts"],"names":[],"mappings":"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"}
@@ -40,8 +40,15 @@ export declare class WorkflowAgentWriter extends WorkflowAgentWriterBase {
40
40
  }): Promise<string>;
41
41
  /** Resolves the Flow agent type, so the persisted agent is a Flow rather than a Loop. */
42
42
  private resolveFlowAgentTypeID;
43
- /** Name → ID for every agent, lowercased so a spec's human-entered name still resolves. */
44
- private buildAgentNameIndex;
43
+ /**
44
+ * Name → ID for every agent, action and prompt, in one batch, lowercased so a spec's
45
+ * human-entered name still resolves.
46
+ *
47
+ * A failed load throws rather than leaving an index empty: an empty index resolves nothing, and
48
+ * every agent, action and prompt in the workflow would then be reported lost — or, for an action,
49
+ * saved pointing at nothing.
50
+ */
51
+ private buildNameIndexes;
45
52
  }
46
53
  /** Prevents tree-shaking of the registered writer. */
47
54
  export declare function LoadWorkflowAgentWriter(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-agent-writer.d.ts","sourceRoot":"","sources":["../src/workflow-agent-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAqB,QAAQ,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEtF,OAAO,EAGH,KAAK,YAAY,EACpB,MAAM,8BAA8B,CAAC;AAGtC,6DAA6D;AAC7D,eAAO,MAAM,yBAAyB,wBAAwB,CAAC;AAE/D,2FAA2F;AAC3F,8BAAsB,uBAAuB;aACzB,gBAAgB,CAC5B,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE;QAAE,WAAW,EAAE,QAAQ,CAAC;QAAC,QAAQ,EAAE,iBAAiB,CAAA;KAAE,GAChE,OAAO,CAAC,MAAM,CAAC;CACrB;AAED,qBACa,mBAAoB,SAAQ,uBAAuB;IAC5D;;;;;;;;OAQG;IACU,gBAAgB,CACzB,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE;QAAE,WAAW,EAAE,QAAQ,CAAC;QAAC,QAAQ,EAAE,iBAAiB,CAAA;KAAE,GAChE,OAAO,CAAC,MAAM,CAAC;IAyClB,yFAAyF;YAC3E,sBAAsB;IAUpC,2FAA2F;YAC7E,mBAAmB;CASpC;AAED,sDAAsD;AACtD,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C"}
1
+ {"version":3,"file":"workflow-agent-writer.d.ts","sourceRoot":"","sources":["../src/workflow-agent-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAqB,QAAQ,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEtF,OAAO,EAGH,KAAK,YAAY,EACpB,MAAM,8BAA8B,CAAC;AAGtC,6DAA6D;AAC7D,eAAO,MAAM,yBAAyB,wBAAwB,CAAC;AAE/D,2FAA2F;AAC3F,8BAAsB,uBAAuB;aACzB,gBAAgB,CAC5B,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE;QAAE,WAAW,EAAE,QAAQ,CAAC;QAAC,QAAQ,EAAE,iBAAiB,CAAA;KAAE,GAChE,OAAO,CAAC,MAAM,CAAC;CACrB;AAED,qBACa,mBAAoB,SAAQ,uBAAuB;IAC5D;;;;;;;;OAQG;IACU,gBAAgB,CACzB,IAAI,EAAE,YAAY,EAClB,OAAO,EAAE;QAAE,WAAW,EAAE,QAAQ,CAAC;QAAC,QAAQ,EAAE,iBAAiB,CAAA;KAAE,GAChE,OAAO,CAAC,MAAM,CAAC;IA8ClB,yFAAyF;YAC3E,sBAAsB;IAUpC;;;;;;;OAOG;YACW,gBAAgB;CAsBjC;AAED,sDAAsD;AACtD,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C"}
@@ -40,14 +40,19 @@ let WorkflowAgentWriter = class WorkflowAgentWriter extends WorkflowAgentWriterB
40
40
  */
41
41
  async PersistFlowAgent(spec, context) {
42
42
  const flowTypeID = await this.resolveFlowAgentTypeID(context);
43
- const agentIDsByName = await this.buildAgentNameIndex(context);
43
+ const { Agents, Actions, Prompts } = await this.buildNameIndexes(context);
44
+ const byName = (index, name) => index.get(name.trim().toLowerCase()) ?? null;
44
45
  let counter = 0;
45
46
  const result = ConvertTaskGraphToAgentSpec(spec.graph, {
46
47
  // Deterministic within one call, and unique because AgentSpecSync assigns real keys on
47
48
  // insert — these only have to correlate steps to paths inside this payload.
48
49
  AgentID: `workflow-${Date.now()}-${counter}`,
49
50
  NextID: () => `wf-node-${++counter}`,
50
- ResolveAgentID: (name) => agentIDsByName.get(name.trim().toLowerCase()) ?? null,
51
+ ResolveAgentID: (name) => byName(Agents, name),
52
+ // Without these, a saved Action step carries no action and a Decision step loses its
53
+ // named prompt: the spec addresses both by name, and a step stores the ID.
54
+ ResolveActionID: (name) => byName(Actions, name),
55
+ ResolvePromptID: (name) => byName(Prompts, name),
51
56
  FlowAgentTypeID: flowTypeID,
52
57
  Name: spec.name,
53
58
  });
@@ -80,10 +85,29 @@ let WorkflowAgentWriter = class WorkflowAgentWriter extends WorkflowAgentWriterB
80
85
  throw new Error("The 'Flow' agent type was not found — has the metadata seed been pushed?");
81
86
  return id;
82
87
  }
83
- /** Name → ID for every agent, lowercased so a spec's human-entered name still resolves. */
84
- async buildAgentNameIndex(context) {
85
- const result = await RunView.FromMetadataProvider(context.Provider).RunView({ EntityName: 'MJ: AI Agents', Fields: ['ID', 'Name'], ResultType: 'simple' }, context.ContextUser);
86
- return new Map((result.Results ?? []).map((a) => [a.Name.trim().toLowerCase(), a.ID]));
88
+ /**
89
+ * Name → ID for every agent, action and prompt, in one batch, lowercased so a spec's
90
+ * human-entered name still resolves.
91
+ *
92
+ * A failed load throws rather than leaving an index empty: an empty index resolves nothing, and
93
+ * every agent, action and prompt in the workflow would then be reported lost — or, for an action,
94
+ * saved pointing at nothing.
95
+ */
96
+ async buildNameIndexes(context) {
97
+ const entityNames = ['MJ: AI Agents', 'MJ: Actions', 'MJ: AI Prompts'];
98
+ const results = await RunView.FromMetadataProvider(context.Provider).RunViews(entityNames.map((EntityName) => ({
99
+ EntityName,
100
+ Fields: ['ID', 'Name'],
101
+ ResultType: 'simple',
102
+ })), context.ContextUser);
103
+ const failed = entityNames.filter((_, i) => !results[i]?.Success);
104
+ if (failed.length > 0) {
105
+ const reasons = failed.map((name) => `${name}: ${results[entityNames.indexOf(name)]?.ErrorMessage || 'no result'}`);
106
+ throw new Error(`Could not load the names a workflow's steps refer to (${reasons.join('; ')}).`);
107
+ }
108
+ const [agents, actions, prompts] = results;
109
+ const index = (rows) => new Map(rows.map((r) => [r.Name.trim().toLowerCase(), r.ID]));
110
+ return { Agents: index(agents.Results), Actions: index(actions.Results), Prompts: index(prompts.Results) };
87
111
  }
88
112
  };
89
113
  WorkflowAgentWriter = __decorate([
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-agent-writer.js","sourceRoot":"","sources":["../src/workflow-agent-writer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"workflow-agent-writer.js","sourceRoot":"","sources":["../src/workflow-agent-writer.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@memberjunction/ai-agent-manager",
3
3
  "type": "module",
4
- "version": "6.2.0-edge.1",
4
+ "version": "6.2.0-edge.3",
5
5
  "description": "Core interfaces and types for the MemberJunction Agent Manager system",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -14,14 +14,14 @@
14
14
  "author": "MemberJunction",
15
15
  "license": "BUSL-1.1",
16
16
  "dependencies": {
17
- "@memberjunction/ai-agents": "6.2.0-edge.1",
18
- "@memberjunction/ai-core-plus": "6.2.0-edge.1",
19
- "@memberjunction/ai-engine-base": "6.2.0-edge.1",
20
- "@memberjunction/aiengine": "6.2.0-edge.1",
21
- "@memberjunction/core": "6.2.0-edge.1",
22
- "@memberjunction/core-entities": "6.2.0-edge.1",
23
- "@memberjunction/global": "6.2.0-edge.1",
24
- "@memberjunction/templates": "6.2.0-edge.1"
17
+ "@memberjunction/ai-agents": "6.2.0-edge.3",
18
+ "@memberjunction/ai-core-plus": "6.2.0-edge.3",
19
+ "@memberjunction/ai-engine-base": "6.2.0-edge.3",
20
+ "@memberjunction/aiengine": "6.2.0-edge.3",
21
+ "@memberjunction/core": "6.2.0-edge.3",
22
+ "@memberjunction/core-entities": "6.2.0-edge.3",
23
+ "@memberjunction/global": "6.2.0-edge.3",
24
+ "@memberjunction/templates": "6.2.0-edge.3"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^24.10.11",
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Round-trip coverage for Decision steps in AgentSpecSync.
3
+ *
4
+ * Verifies that a Decision step's PromptID (whether custom or omitted) and Configuration
5
+ * (whether supplied as JSON string or parsed object) survive save and load round trips.
6
+ */
7
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
8
+
9
+ const { capturedStepEntities, mockGetEntityObject, mockRunView, mockRunViews } = vi.hoisted(() => {
10
+ const capturedStepEntities: Record<string, unknown>[] = [];
11
+
12
+ const makeEntity = (entityName: string) => {
13
+ const entity: Record<string, unknown> = {
14
+ Load: vi.fn().mockResolvedValue(true),
15
+ Save: vi.fn().mockResolvedValue(true),
16
+ Delete: vi.fn().mockResolvedValue(true),
17
+ NewRecord: vi.fn(),
18
+ Validate: vi.fn().mockReturnValue({ Success: true, Errors: [] }),
19
+ ID: 'saved-id-1',
20
+ Name: 'Test Agent',
21
+ TypeID: '4F6A189B-C068-4736-9F23-3FF540B40FDD',
22
+ Status: 'Active',
23
+ StartingPayloadValidationMode: 'Fail',
24
+ };
25
+
26
+ if (entityName === 'MJ: AI Agent Steps') {
27
+ capturedStepEntities.push(entity);
28
+ }
29
+
30
+ return entity;
31
+ };
32
+
33
+ const empty = { Success: true, Results: [], RowCount: 0 };
34
+ return {
35
+ capturedStepEntities,
36
+ mockGetEntityObject: vi.fn((name: string) => Promise.resolve(makeEntity(name))),
37
+ mockRunView: vi.fn(() => Promise.resolve(empty)),
38
+ mockRunViews: vi.fn(() => Promise.resolve([empty, empty, empty, empty, empty])),
39
+ };
40
+ });
41
+
42
+ vi.mock('@memberjunction/core', () => {
43
+ const rvInstance = { RunView: mockRunView, RunViews: mockRunViews };
44
+ return {
45
+ Metadata: Object.assign(
46
+ vi.fn().mockImplementation(() => ({ GetEntityObject: mockGetEntityObject })),
47
+ { Provider: { GetEntityObject: mockGetEntityObject } }
48
+ ),
49
+ RunView: Object.assign(
50
+ vi.fn().mockImplementation(() => rvInstance),
51
+ { FromMetadataProvider: vi.fn(() => rvInstance) }
52
+ ),
53
+ UserInfo: vi.fn(),
54
+ LogError: vi.fn(),
55
+ };
56
+ });
57
+
58
+ vi.mock('@memberjunction/core-entities', () => ({
59
+ MJAIAgentEntity: vi.fn(),
60
+ MJAIAgentActionEntity: vi.fn(),
61
+ MJAIAgentRelationshipEntity: vi.fn(),
62
+ MJAIAgentStepEntity: vi.fn(),
63
+ MJAIAgentStepPathEntity: vi.fn(),
64
+ }));
65
+
66
+ vi.mock('@memberjunction/ai-core-plus', () => ({
67
+ AgentSpec: vi.fn(),
68
+ AgentActionSpec: vi.fn(),
69
+ SubAgentSpec: vi.fn(),
70
+ }));
71
+
72
+ import { UserInfo } from '@memberjunction/core';
73
+ import { AgentSpecSync } from '../agent-spec-sync';
74
+
75
+ describe('AgentSpecSync Decision step round-trip', () => {
76
+ // UserInfo is mocked above, so this is the mock's instance.
77
+ const mockUser = new UserInfo();
78
+
79
+ beforeEach(() => {
80
+ vi.clearAllMocks();
81
+ capturedStepEntities.length = 0;
82
+ });
83
+
84
+ it('persists a Decision step with custom PromptID and object Configuration', async () => {
85
+ const decisionConfig = {
86
+ key: 'triage',
87
+ questions: {
88
+ category: {
89
+ instructions: 'What category?',
90
+ kind: 'Choice',
91
+ options: [
92
+ { value: 'billing', description: 'Billing' },
93
+ { value: 'tech', description: 'Technical' },
94
+ ],
95
+ },
96
+ },
97
+ };
98
+
99
+ const sync = new AgentSpecSync(
100
+ {
101
+ ID: 'agent-1',
102
+ Name: 'Decision Flow Agent',
103
+ TypeID: '4F6A189B-C068-4736-9F23-3FF540B40FDD',
104
+ Steps: [
105
+ {
106
+ ID: 'step-1',
107
+ Name: 'Triage Issue',
108
+ StepType: 'Decision',
109
+ StartingStep: true,
110
+ PromptID: 'custom-prompt-uuid-999',
111
+ Configuration: decisionConfig,
112
+ },
113
+ ],
114
+ },
115
+ mockUser
116
+ );
117
+
118
+ const result = await sync.SaveToDatabase();
119
+ expect(result.success).toBe(true);
120
+
121
+ const savedStep = capturedStepEntities.find(s => s.Name === 'Triage Issue');
122
+ expect(savedStep).toBeDefined();
123
+ expect(savedStep?.StepType).toBe('Decision');
124
+ expect(savedStep?.PromptID).toBe('custom-prompt-uuid-999');
125
+ expect(savedStep?.Configuration).toBe(JSON.stringify(decisionConfig));
126
+ });
127
+
128
+ it('persists a Decision step with null PromptID when none is provided', async () => {
129
+ const sync = new AgentSpecSync(
130
+ {
131
+ ID: 'agent-2',
132
+ Name: 'Decision Agent Without Prompt',
133
+ TypeID: '4F6A189B-C068-4736-9F23-3FF540B40FDD',
134
+ Steps: [
135
+ {
136
+ ID: 'step-2',
137
+ Name: 'Default Decision Step',
138
+ StepType: 'Decision',
139
+ StartingStep: true,
140
+ Configuration: JSON.stringify({ key: 'route', questions: {} }),
141
+ },
142
+ ],
143
+ },
144
+ mockUser
145
+ );
146
+
147
+ const result = await sync.SaveToDatabase();
148
+ expect(result.success).toBe(true);
149
+
150
+ const savedStep = capturedStepEntities.find(s => s.Name === 'Default Decision Step');
151
+ expect(savedStep).toBeDefined();
152
+ expect(savedStep?.PromptID).toBeNull();
153
+ expect(savedStep?.Configuration).toBe(JSON.stringify({ key: 'route', questions: {} }));
154
+ });
155
+
156
+ it('loads a Decision step from database preserving PromptID and Configuration', async () => {
157
+ const decisionConfigJson = JSON.stringify({
158
+ key: 'triage',
159
+ questions: {
160
+ priority: {
161
+ text: 'What priority?',
162
+ kind: 'Rating',
163
+ minRating: 1,
164
+ maxRating: 5,
165
+ },
166
+ },
167
+ });
168
+
169
+ const mockAgentEntity = {
170
+ Load: vi.fn().mockResolvedValue(true),
171
+ ID: 'agent-3',
172
+ Name: 'Loaded Decision Agent',
173
+ TypeID: '4F6A189B-C068-4736-9F23-3FF540B40FDD',
174
+ Status: 'Active',
175
+ StartingPayloadValidationMode: 'Fail',
176
+ };
177
+
178
+ const mockStepEntity = {
179
+ ID: 'step-3',
180
+ AgentID: 'agent-3',
181
+ Name: 'Rate Priority',
182
+ StepType: 'Decision',
183
+ StartingStep: true,
184
+ PromptID: 'decision-prompt-777',
185
+ Configuration: decisionConfigJson,
186
+ Status: 'Active',
187
+ };
188
+
189
+ mockGetEntityObject.mockImplementation((name: string) => {
190
+ if (name === 'MJ: AI Agents') {
191
+ return Promise.resolve(mockAgentEntity);
192
+ }
193
+ return Promise.resolve({
194
+ Load: vi.fn().mockResolvedValue(true),
195
+ Save: vi.fn().mockResolvedValue(true),
196
+ Validate: vi.fn().mockReturnValue({ Success: true, Errors: [] }),
197
+ });
198
+ });
199
+
200
+ // Set up RunViews return: [actions, childAgents, relatedAgents, prompts, steps]
201
+ const empty = { Success: true, Results: [], RowCount: 0 };
202
+ const stepsResult = { Success: true, Results: [mockStepEntity], RowCount: 1 };
203
+ mockRunViews.mockResolvedValueOnce([empty, empty, empty, empty, stepsResult]);
204
+ // mockRunView for paths
205
+ mockRunView.mockResolvedValueOnce(empty);
206
+
207
+ const loadedSync = await AgentSpecSync.LoadFromDatabase('agent-3', mockUser);
208
+
209
+ expect(loadedSync.Spec.Steps).toBeDefined();
210
+ expect(loadedSync.Spec.Steps).toHaveLength(1);
211
+
212
+ const loadedStep = loadedSync.Spec.Steps![0];
213
+ expect(loadedStep.Name).toBe('Rate Priority');
214
+ expect(loadedStep.StepType).toBe('Decision');
215
+ expect(loadedStep.PromptID).toBe('decision-prompt-777');
216
+ expect(loadedStep.Configuration).toBe(decisionConfigJson);
217
+ });
218
+ });