@memberjunction/ai-agent-manager 2.108.0 → 2.110.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,447 @@
1
+ import { BaseAgent, PayloadManager } from '@memberjunction/ai-agents';
2
+ import { ExecuteAgentParams, BaseAgentNextStep, AgentSpec } from '@memberjunction/ai-core-plus';
3
+ import { AIAgentRunEntityExtended, AIAgentRunStepEntityExtended, ActionEntity, AIPromptEntity } from '@memberjunction/core-entities';
4
+ import { Metadata, RunView } from '@memberjunction/core';
5
+ import { RegisterClass } from '@memberjunction/global';
6
+
7
+ /**
8
+ * Architect Agent - Transforms technical design into validated AgentSpec JSON
9
+ *
10
+ * This agent creates AgentSpec objects from technical designs and validates them
11
+ * before passing to the Builder Agent for persistence. It overrides validateSuccessNextStep
12
+ * to ensure the generated AgentSpec is valid before proceeding.
13
+ *
14
+ * Key responsibilities:
15
+ * - Transform design documents into AgentSpec format
16
+ * - Validate required fields are present
17
+ * - Verify action IDs exist in database
18
+ * - Verify prompt IDs exist (if specified)
19
+ * - Validate SubAgent structure
20
+ * - Auto-correct minor issues (casing, missing optional fields)
21
+ * - Force retry with detailed errors if validation fails
22
+ */
23
+ @RegisterClass(BaseAgent, 'AgentArchitectAgent')
24
+ export class AgentArchitectAgent extends BaseAgent {
25
+
26
+ /**
27
+ * Applies payload changes if any are requested in the next step
28
+ */
29
+ protected applyPayloadChanges<P>(payload: P, nextStep: BaseAgentNextStep<P>): P {
30
+ if (nextStep.payloadChangeRequest) {
31
+ const pm = new PayloadManager();
32
+ const result = pm.applyAgentChangeRequest<P>(payload, nextStep.payloadChangeRequest);
33
+ return result.result || payload;
34
+ }
35
+ return payload;
36
+ }
37
+
38
+ protected override async validateSuccessNextStep<P>(
39
+ params: ExecuteAgentParams,
40
+ nextStep: BaseAgentNextStep<P>,
41
+ currentPayload: P,
42
+ agentRun: AIAgentRunEntityExtended,
43
+ currentStep: AIAgentRunStepEntityExtended
44
+ ): Promise<BaseAgentNextStep<P>> {
45
+ // First call base validation
46
+ const baseValidation = await super.validateSuccessNextStep(params, nextStep, currentPayload, agentRun, currentStep);
47
+ if (baseValidation.step === 'Retry') {
48
+ return baseValidation;
49
+ }
50
+
51
+ // Apply payload changes to get the spec that would be sent forward
52
+ const payloadWithChanges = this.applyPayloadChanges<P>(currentPayload, nextStep);
53
+
54
+ // The payload IS the AgentSpec
55
+ const agentSpec = payloadWithChanges as AgentSpec;
56
+
57
+ console.log('🏗️ Architect Agent: Validating AgentSpec:', agentSpec ? `Name="${agentSpec.Name}"` : 'agentSpec is null/undefined');
58
+
59
+ // Validate the AgentSpec
60
+ const validation = await this.validateAgentSpec(agentSpec, params);
61
+
62
+ if (validation.errors.length > 0) {
63
+ console.log('❌ Architect Agent: Validation failed with errors:', validation.errors);
64
+ // Return retry with detailed error messages
65
+ return {
66
+ ...nextStep,
67
+ step: 'Retry',
68
+ retryInstructions: this.formatValidationErrors(validation.errors),
69
+ newPayload: nextStep.newPayload
70
+ };
71
+ }
72
+
73
+ // If spec was auto-corrected, update the payload with corrected spec
74
+ if (validation.correctedSpec) {
75
+ console.log('✓ Architect Agent: Auto-corrected AgentSpec, updating payload');
76
+ return {
77
+ ...nextStep,
78
+ newPayload: validation.correctedSpec as P
79
+ };
80
+ }
81
+
82
+ console.log('✅ Architect Agent: AgentSpec validation passed');
83
+ return nextStep;
84
+ }
85
+
86
+ /**
87
+ * Validates an AgentSpec object
88
+ * Returns errors array and potentially a corrected spec
89
+ */
90
+ protected async validateAgentSpec(
91
+ spec: AgentSpec,
92
+ params: ExecuteAgentParams
93
+ ): Promise<{ errors: string[]; correctedSpec?: AgentSpec }> {
94
+ const errors: string[] = [];
95
+ let corrected = false;
96
+
97
+ // Check if spec exists
98
+ if (!spec) {
99
+ errors.push('❌ AgentSpec is null or undefined. Ensure you place the AgentSpec in payload.agentSpec (inside a payloadChangeRequest with newElements or replaceElements)');
100
+ return { errors };
101
+ }
102
+
103
+ const correctedSpec = { ...spec };
104
+
105
+ // 1. Validate required fields
106
+ if (!correctedSpec.Name || correctedSpec.Name.trim().length === 0) {
107
+ errors.push('❌ AgentSpec.Name is required and cannot be empty');
108
+ }
109
+
110
+ if (!correctedSpec.StartingPayloadValidationMode) {
111
+ // Auto-correct: add default
112
+ correctedSpec.StartingPayloadValidationMode = 'Fail';
113
+ corrected = true;
114
+ console.log('✓ Auto-corrected: Added default StartingPayloadValidationMode = "Fail"');
115
+ } else if (correctedSpec.StartingPayloadValidationMode !== 'Fail' && correctedSpec.StartingPayloadValidationMode !== 'Warn') {
116
+ errors.push(`❌ StartingPayloadValidationMode must be either "Fail" or "Warn", got: "${correctedSpec.StartingPayloadValidationMode}"`);
117
+ }
118
+
119
+ // 2. Validate Actions if specified
120
+ if (correctedSpec.Actions && correctedSpec.Actions.length > 0) {
121
+ const actionValidation = await this.validateActions(correctedSpec.Actions, params);
122
+ if (actionValidation.errors.length > 0) {
123
+ errors.push(...actionValidation.errors);
124
+ }
125
+ }
126
+
127
+ // 3. Validate SubAgents if specified
128
+ if (correctedSpec.SubAgents && correctedSpec.SubAgents.length > 0) {
129
+ const subAgentValidation = this.validateSubAgents(correctedSpec.SubAgents);
130
+ if (subAgentValidation.errors.length > 0) {
131
+ errors.push(...subAgentValidation.errors);
132
+ }
133
+ if (subAgentValidation.corrected) {
134
+ corrected = true;
135
+ }
136
+ }
137
+
138
+ // 4. Validate TypeID and Status
139
+ const specWithType = correctedSpec as any;
140
+ if (!specWithType.TypeID) {
141
+ errors.push('❌ TypeID is required - must be "@lookup:MJ: AI Agent Types.Name=Loop" or "@lookup:MJ: AI Agent Types.Name=Flow"');
142
+ }
143
+
144
+ if (!specWithType.Status) {
145
+ // Auto-correct: add default
146
+ specWithType.Status = 'Active';
147
+ corrected = true;
148
+ console.log('✓ Auto-corrected: Added default Status = "Active"');
149
+ } else if (!['Active', 'Inactive', 'Pending'].includes(specWithType.Status)) {
150
+ errors.push(`❌ Status must be "Active", "Inactive", or "Pending", got: "${specWithType.Status}"`);
151
+ }
152
+
153
+ // 5. Validate agent type-specific requirements
154
+ const isLoopAgent = specWithType.TypeID?.includes('Loop');
155
+ const isFlowAgent = specWithType.TypeID?.includes('Flow');
156
+
157
+ if (isLoopAgent) {
158
+ // Loop agents require at least one prompt
159
+ if (!correctedSpec.Prompts || correctedSpec.Prompts.length === 0) {
160
+ errors.push('❌ Loop agents require at least ONE prompt in the Prompts array');
161
+ }
162
+ }
163
+
164
+ if (isFlowAgent) {
165
+ // CRITICAL: Flow agents MUST have empty Prompts array (no agent-level prompts)
166
+ if (correctedSpec.Prompts && correctedSpec.Prompts.length > 0) {
167
+ errors.push('❌ Flow agents MUST have empty Prompts array. If LLM is needed, use a Prompt step OR a Loop sub-agent instead of agent-level prompts.');
168
+ }
169
+
170
+ // Flow agents require Steps and Paths
171
+ if (!correctedSpec.Steps || correctedSpec.Steps.length === 0) {
172
+ errors.push('❌ Flow agents require at least ONE step in the Steps array');
173
+ }
174
+ // Paths are technically optional (single-step flows), but validate if present
175
+ if (correctedSpec.Steps && correctedSpec.Steps.length > 1) {
176
+ if (!correctedSpec.Paths || correctedSpec.Paths.length === 0) {
177
+ errors.push('❌ Flow agents with multiple steps require at least ONE path in the Paths array');
178
+ }
179
+ }
180
+
181
+ // Validate that at least one step is marked as StartingStep
182
+ if (correctedSpec.Steps && correctedSpec.Steps.length > 0) {
183
+ const hasStartingStep = correctedSpec.Steps.some(step => step.StartingStep === true);
184
+ if (!hasStartingStep) {
185
+ errors.push('❌ Flow agents require at least ONE step with StartingStep: true');
186
+ }
187
+
188
+ // Validate each step based on its type
189
+ for (let i = 0; i < correctedSpec.Steps.length; i++) {
190
+ const step = correctedSpec.Steps[i];
191
+
192
+ // Validate Action steps
193
+ if (step.StepType === 'Action') {
194
+ if (!step.ActionID) {
195
+ errors.push(`❌ Action step "${step.Name}" (index ${i}) must have ActionID field`);
196
+ }
197
+
198
+ // Validate ActionInputMapping if provided (supports both string and object)
199
+ if (step.ActionInputMapping) {
200
+ try {
201
+ if (typeof step.ActionInputMapping === 'string') {
202
+ JSON.parse(step.ActionInputMapping);
203
+ }
204
+ // else it's already an object, which is valid and will be stringified later
205
+ } catch (e: any) {
206
+ errors.push(`❌ Step "${step.Name}" (index ${i}) has invalid ActionInputMapping JSON: ${e?.message || String(e)}`);
207
+ }
208
+ }
209
+
210
+ // Validate ActionOutputMapping if provided (supports both string and object)
211
+ if (step.ActionOutputMapping) {
212
+ try {
213
+ if (typeof step.ActionOutputMapping === 'string') {
214
+ JSON.parse(step.ActionOutputMapping);
215
+ }
216
+ // else it's already an object, which is valid and will be stringified later
217
+ } catch (e: any) {
218
+ errors.push(`❌ Step "${step.Name}" (index ${i}) has invalid ActionOutputMapping JSON: ${e?.message || String(e)}`);
219
+ }
220
+ }
221
+ }
222
+
223
+ // Validate Prompt steps
224
+ if (step.StepType === 'Prompt') {
225
+ // If PromptID is empty or not provided, PromptText is required for inline creation
226
+ if (!step.PromptID || step.PromptID === '') {
227
+ if (!step.PromptText || step.PromptText.trim() === '') {
228
+ errors.push(`❌ Prompt step "${step.Name}" (index ${i}) has empty PromptID but missing PromptText. For inline prompt creation, PromptText is required.`);
229
+ }
230
+ // PromptName and PromptDescription are recommended but not required
231
+ if (!step.PromptName) {
232
+ console.log(`⚠️ Warning: Prompt step "${step.Name}" (index ${i}) is missing PromptName (will default to "[Step Name] Prompt")`);
233
+ }
234
+ }
235
+ // If PromptID is provided (existing prompt), other fields are optional
236
+ }
237
+
238
+ // Validate Sub-Agent steps
239
+ if (step.StepType === 'Sub-Agent') {
240
+ // SubAgentID can be empty "" for new sub-agents (will be linked by name matching)
241
+ // No validation needed here - linking happens in AgentSpecSync
242
+ }
243
+ }
244
+ }
245
+ }
246
+
247
+ // 6. Validate optional but important fields
248
+ if (correctedSpec.FinalPayloadValidationMode &&
249
+ !['Fail', 'Retry', 'Warn'].includes(correctedSpec.FinalPayloadValidationMode)) {
250
+ errors.push(`❌ FinalPayloadValidationMode must be "Fail", "Retry", or "Warn", got: "${correctedSpec.FinalPayloadValidationMode}"`);
251
+ }
252
+
253
+ if (correctedSpec.InvocationMode &&
254
+ !['Any', 'Agent', 'User', 'Never'].includes(correctedSpec.InvocationMode)) {
255
+ errors.push(`❌ InvocationMode must be "Any", "Agent", "User", or "Never", got: "${correctedSpec.InvocationMode}"`);
256
+ }
257
+
258
+ return {
259
+ errors,
260
+ correctedSpec: corrected ? correctedSpec : undefined
261
+ };
262
+ }
263
+
264
+ /**
265
+ * Validates that action IDs exist in the database
266
+ */
267
+ protected async validateActions(
268
+ actions: AgentSpec['Actions'],
269
+ params: ExecuteAgentParams
270
+ ): Promise<{ errors: string[] }> {
271
+ const errors: string[] = [];
272
+
273
+ if (!actions || actions.length === 0) {
274
+ return { errors };
275
+ }
276
+
277
+ try {
278
+ const rv = new RunView();
279
+ const actionIds = actions
280
+ .map(a => a.ActionID)
281
+ .filter(id => id && id.trim().length > 0);
282
+
283
+ if (actionIds.length === 0) {
284
+ errors.push('❌ All actions must have an ActionID');
285
+ return { errors };
286
+ }
287
+
288
+ // Query database for these action IDs
289
+ const filter = actionIds.map(id => `ID='${id}'`).join(' OR ');
290
+ const result = await rv.RunView<ActionEntity>({
291
+ EntityName: 'Actions',
292
+ ExtraFilter: filter,
293
+ ResultType: 'entity_object'
294
+ }, params.contextUser);
295
+
296
+ if (!result.Success) {
297
+ errors.push(`❌ Failed to validate actions: ${result.ErrorMessage}`);
298
+ return { errors };
299
+ }
300
+
301
+ const foundIds = new Set(result.Results.map(a => a.ID));
302
+
303
+ // Check which actions weren't found
304
+ for (const action of actions) {
305
+ if (!action.ActionID) {
306
+ errors.push('❌ Action is missing ActionID field');
307
+ continue;
308
+ }
309
+
310
+ if (!foundIds.has(action.ActionID)) {
311
+ errors.push(`❌ Action with ID "${action.ActionID}" not found in database. Please use "Find Best Action" or "List Actions" to get valid action IDs.`);
312
+ }
313
+ }
314
+
315
+ } catch (error: any) {
316
+ errors.push(`❌ Error validating actions: ${error?.message || String(error)}`);
317
+ }
318
+
319
+ return { errors };
320
+ }
321
+
322
+ /**
323
+ * Validates SubAgent structure recursively
324
+ */
325
+ protected validateSubAgents(
326
+ subAgents: AgentSpec['SubAgents']
327
+ ): { errors: string[]; corrected: boolean } {
328
+ const errors: string[] = [];
329
+ let corrected = false;
330
+
331
+ if (!subAgents || subAgents.length === 0) {
332
+ return { errors, corrected };
333
+ }
334
+
335
+ for (let i = 0; i < subAgents.length; i++) {
336
+ const subAgent = subAgents[i];
337
+
338
+ // Validate Type field
339
+ if (!subAgent.Type) {
340
+ errors.push(`❌ SubAgent at index ${i} is missing required "Type" field (must be "child" or "related")`);
341
+ continue;
342
+ }
343
+
344
+ if (subAgent.Type !== 'child' && subAgent.Type !== 'related') {
345
+ errors.push(`❌ SubAgent at index ${i} has invalid Type "${subAgent.Type}" (must be "child" or "related")`);
346
+ continue;
347
+ }
348
+
349
+ // Validate SubAgent.SubAgent field
350
+ if (!subAgent.SubAgent) {
351
+ errors.push(`❌ SubAgent at index ${i} is missing required "SubAgent" field`);
352
+ continue;
353
+ }
354
+
355
+ // SubAgent.ID can be empty string "" for NEW sub-agents (Builder creates them recursively)
356
+ // Only validate that ID is not null/undefined
357
+ if (subAgent.SubAgent.ID === null || subAgent.SubAgent.ID === undefined) {
358
+ errors.push(`❌ SubAgent at index ${i} has null/undefined SubAgent.ID (use empty string "" for new sub-agents, or provide existing agent GUID)`);
359
+ }
360
+
361
+ // Validate SubAgent.SubAgent.Name
362
+ if (!subAgent.SubAgent.Name || subAgent.SubAgent.Name.trim().length === 0) {
363
+ errors.push(`❌ SubAgent at index ${i} is missing SubAgent.Name`);
364
+ }
365
+
366
+ // Validate TypeID for sub-agent
367
+ if (!subAgent.SubAgent.TypeID) {
368
+ errors.push(`❌ SubAgent[${i}] "${subAgent.SubAgent.Name}" is missing TypeID field`);
369
+ } else {
370
+ // Validate Loop/Flow specific requirements for sub-agents
371
+ const isLoopSubAgent = subAgent.SubAgent.TypeID.includes('Loop');
372
+ const isFlowSubAgent = subAgent.SubAgent.TypeID.includes('Flow');
373
+
374
+ if (isLoopSubAgent) {
375
+ // Loop sub-agents require at least one prompt
376
+ if (!subAgent.SubAgent.Prompts || subAgent.SubAgent.Prompts.length === 0) {
377
+ errors.push(`❌ Loop SubAgent[${i}] "${subAgent.SubAgent.Name}" requires at least ONE prompt in Prompts array`);
378
+ }
379
+ }
380
+
381
+ if (isFlowSubAgent) {
382
+ // Flow sub-agents MUST have empty Prompts array
383
+ if (subAgent.SubAgent.Prompts && subAgent.SubAgent.Prompts.length > 0) {
384
+ errors.push(`❌ Flow SubAgent[${i}] "${subAgent.SubAgent.Name}" MUST have empty Prompts array. Use Prompt steps or Loop sub-agents for LLM functionality.`);
385
+ }
386
+
387
+ // Flow sub-agents require Steps
388
+ if (!subAgent.SubAgent.Steps || subAgent.SubAgent.Steps.length === 0) {
389
+ errors.push(`❌ Flow SubAgent[${i}] "${subAgent.SubAgent.Name}" requires at least ONE step in Steps array`);
390
+ }
391
+ }
392
+ }
393
+
394
+ // Auto-correct: Add default StartingPayloadValidationMode if missing
395
+ if (!subAgent.SubAgent.StartingPayloadValidationMode) {
396
+ subAgent.SubAgent.StartingPayloadValidationMode = 'Fail';
397
+ corrected = true;
398
+ console.log(`✓ Auto-corrected: Added SubAgent[${i}].SubAgent.StartingPayloadValidationMode = "Fail"`);
399
+ }
400
+
401
+ // Auto-correct: Add default Status if missing
402
+ if (!subAgent.SubAgent.Status) {
403
+ subAgent.SubAgent.Status = 'Active';
404
+ corrected = true;
405
+ console.log(`✓ Auto-corrected: Added SubAgent[${i}].SubAgent.Status = "Active"`);
406
+ }
407
+
408
+ // Validate related-specific fields
409
+ if (subAgent.Type === 'related' && !subAgent.AgentRelationshipID) {
410
+ // AgentRelationshipID can be empty for new relationships, it will be created on save
411
+ // This is not an error
412
+ }
413
+
414
+ // Recursively validate nested sub-agents
415
+ if (subAgent.SubAgent.SubAgents && subAgent.SubAgent.SubAgents.length > 0) {
416
+ const nestedValidation = this.validateSubAgents(subAgent.SubAgent.SubAgents);
417
+ if (nestedValidation.errors.length > 0) {
418
+ // Prefix nested errors with parent context
419
+ const prefixedErrors = nestedValidation.errors.map(
420
+ err => `SubAgent[${i}] "${subAgent.SubAgent.Name}" -> ${err}`
421
+ );
422
+ errors.push(...prefixedErrors);
423
+ }
424
+ if (nestedValidation.corrected) {
425
+ corrected = true;
426
+ }
427
+ }
428
+ }
429
+
430
+ return { errors, corrected };
431
+ }
432
+
433
+ /**
434
+ * Formats validation errors into a clear, actionable message
435
+ */
436
+ protected formatValidationErrors(errors: string[]): string {
437
+ if (errors.length === 0) {
438
+ return '';
439
+ }
440
+
441
+ const header = '## AgentSpec Validation Errors\n\nThe generated AgentSpec has the following issues that must be fixed:\n\n';
442
+ const errorList = errors.map((err, idx) => `${idx + 1}. ${err}`).join('\n');
443
+ const footer = '\n\n**Please fix these issues and regenerate the AgentSpec.**';
444
+
445
+ return header + errorList + footer;
446
+ }
447
+ }
@@ -0,0 +1,151 @@
1
+ import { BaseAgent } from '@memberjunction/ai-agents';
2
+ import { ExecuteAgentParams, AgentConfiguration, BaseAgentNextStep, AgentSpec } from '@memberjunction/ai-core-plus';
3
+ import { RegisterClass } from '@memberjunction/global';
4
+ import { Metadata } from '@memberjunction/core';
5
+ import { AIAgentRunStepEntityExtended } from '@memberjunction/core-entities';
6
+ import { AgentSpecSync } from '../agent-spec-sync';
7
+ import { TemplateEngineServer } from '@memberjunction/templates';
8
+
9
+ /**
10
+ * Builder Agent - Persists validated AgentSpec to database via AgentSpecSync
11
+ *
12
+ * This is a code-based agent that overrides executeAgentInternal to bypass
13
+ * the normal chat loop and directly execute persistence logic. It receives
14
+ * a validated AgentSpec from the Architect Agent and saves it to the database
15
+ * using AgentSpecSync.
16
+ *
17
+ * Key responsibilities:
18
+ * - Receive validated AgentSpec from payload
19
+ * - Create AgentSpecSync instance
20
+ * - Persist to database
21
+ * - Track mutations in AI Agent Run Steps table
22
+ * - Return success with created agent ID, or failure with error details
23
+ *
24
+ * This agent does NOT iterate - it runs once and returns Success or Failed.
25
+ */
26
+ @RegisterClass(BaseAgent, 'AgentBuilderAgent')
27
+ export class AgentBuilderAgent extends BaseAgent {
28
+
29
+ /**
30
+ * Override executeAgentInternal to run code instead of chat loop.
31
+ *
32
+ * Directly creates the AI Agent Run Step with mutations from AgentSpecSync.
33
+ */
34
+ protected override async executeAgentInternal<P = any>(
35
+ params: ExecuteAgentParams<P>,
36
+ _config: AgentConfiguration
37
+ ): Promise<{ finalStep: BaseAgentNextStep<P>; stepCount: number }> {
38
+
39
+ console.log('🔨 Builder Agent: Starting agent persistence...');
40
+
41
+ try {
42
+ // Validate payload
43
+ const agentSpec = params.payload as AgentSpec;
44
+ if (!agentSpec) {
45
+ throw new Error('No AgentSpec found in payload - ensure Architect Agent provided valid AgentSpec');
46
+ }
47
+ if (!agentSpec.Name) {
48
+ throw new Error('AgentSpec is missing required Name field');
49
+ }
50
+
51
+ console.log(`🔨 Builder Agent: Creating agent "${agentSpec.Name}"...`);
52
+
53
+ // Create AgentSpecSync and save to database
54
+ const specSync = new AgentSpecSync(agentSpec, params.contextUser);
55
+ specSync.markDirty();
56
+
57
+ const result = await specSync.SaveToDatabase();
58
+
59
+ if (!result.success) {
60
+ throw new Error('AgentSpecSync.SaveToDatabase() returned success=false');
61
+ }
62
+
63
+ console.log(`✅ Builder Agent: Successfully created agent with ID: ${result.agentId}`);
64
+
65
+ // Refresh metadata and template caches
66
+ console.log('🔄 Builder Agent: Refreshing metadata and template caches...');
67
+ const md = new Metadata();
68
+ await md.Refresh();
69
+
70
+ const templateEngine = new TemplateEngineServer();
71
+ await templateEngine.Config(true, params.contextUser);
72
+ console.log('✅ Builder Agent: Metadata and template caches refreshed');
73
+
74
+ // Create AI Agent Run Step record directly
75
+ const agentRunId = params.parentRun?.ID || 'unknown';
76
+ const stepEntity = await md.GetEntityObject<AIAgentRunStepEntityExtended>(
77
+ 'MJ: AI Agent Run Steps',
78
+ params.contextUser
79
+ );
80
+
81
+ stepEntity.AgentRunID = agentRunId;
82
+ stepEntity.StepNumber = 2; // Validation is step 1, this is step 2
83
+ stepEntity.StepType = 'Decision';
84
+ stepEntity.StepName = 'Sync Agent Spec';
85
+ stepEntity.Status = 'Completed';
86
+ stepEntity.Success = true;
87
+ stepEntity.StartedAt = new Date();
88
+ stepEntity.CompletedAt = new Date();
89
+
90
+ // InputData = Full AgentSpec
91
+ stepEntity.InputData = JSON.stringify(agentSpec, null, 2);
92
+ stepEntity.PayloadAtStart = JSON.stringify(params.payload);
93
+
94
+ // OutputData = Mutations array
95
+ stepEntity.OutputData = JSON.stringify(result.mutations, null, 2);
96
+ stepEntity.PayloadAtEnd = stepEntity.PayloadAtStart;
97
+
98
+ await stepEntity.Save();
99
+ console.log(`✅ Builder Agent: Created AI Agent Run Step with ID: ${stepEntity.ID}`);
100
+
101
+ // Return success
102
+ const updatedSpec = { ...agentSpec, ID: result.agentId };
103
+ return {
104
+ finalStep: {
105
+ terminate: true,
106
+ step: 'Success',
107
+ reasoning: `Successfully created agent "${agentSpec.Name}" with ID: ${result.agentId}`,
108
+ newPayload: updatedSpec as P
109
+ },
110
+ stepCount: 2
111
+ };
112
+
113
+ } catch (error: any) {
114
+ console.error('❌ Builder Agent: Failed to create agent:', error);
115
+
116
+ // Create failed AI Agent Run Step
117
+ const agentRunId = params.parentRun?.ID || 'unknown';
118
+ const md = new Metadata();
119
+ const stepEntity = await md.GetEntityObject<AIAgentRunStepEntityExtended>(
120
+ 'MJ: AI Agent Run Steps',
121
+ params.contextUser
122
+ );
123
+
124
+ stepEntity.AgentRunID = agentRunId;
125
+ stepEntity.StepNumber = 2;
126
+ stepEntity.StepType = 'Decision';
127
+ stepEntity.StepName = 'Sync Agent Spec';
128
+ stepEntity.Status = 'Failed';
129
+ stepEntity.Success = false;
130
+ stepEntity.StartedAt = new Date();
131
+ stepEntity.CompletedAt = new Date();
132
+ stepEntity.ErrorMessage = error?.message || String(error);
133
+
134
+ stepEntity.InputData = JSON.stringify(params.payload, null, 2);
135
+ stepEntity.PayloadAtStart = JSON.stringify(params.payload);
136
+ stepEntity.OutputData = JSON.stringify({ error: error?.message || String(error) });
137
+ stepEntity.PayloadAtEnd = stepEntity.PayloadAtStart;
138
+
139
+ await stepEntity.Save();
140
+
141
+ return {
142
+ finalStep: {
143
+ terminate: true,
144
+ step: 'Failed',
145
+ reasoning: `Failed to create agent: ${error?.message || String(error)}`
146
+ },
147
+ stepCount: 2
148
+ };
149
+ }
150
+ }
151
+ }
package/src/index.ts CHANGED
@@ -1,10 +1,29 @@
1
1
  /**
2
2
  * @module @memberjunction/agent-manager-core
3
- *
3
+ *
4
4
  * Core interfaces and types for the MemberJunction Agent Manager system
5
5
  */
6
6
 
7
7
  // Export all interfaces
8
8
  export * from './old/agent-definition.interface';
9
9
 
10
- export * from './agent-spec-sync';
10
+ export * from './agent-spec-sync';
11
+
12
+ // Export agent implementations
13
+ export * from './agents/architect-agent';
14
+ export * from './agents/builder-agent';
15
+
16
+ /**
17
+ * Loads the Agent Manager Core module and ensures all agent classes are registered.
18
+ * This function prevents tree shaking from removing the agent class registrations.
19
+ *
20
+ * The function itself doesn't need to do anything - importing the module is enough
21
+ * to trigger the @RegisterClass decorators on AgentArchitectAgent and AgentBuilderAgent.
22
+ */
23
+ export function LoadAgentManagerCore() {
24
+ // Forces module to load and decorators to execute
25
+ // Classes are already exported and decorators run on import
26
+ }
27
+
28
+ // Auto-load on import to ensure decorators execute
29
+ LoadAgentManagerCore();