@memberjunction/ai-agent-manager 6.2.0-edge.0 → 6.2.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.
@@ -19,6 +19,7 @@ import {
19
19
  SubAgentSpec
20
20
  } from '@memberjunction/ai-core-plus';
21
21
  import { UUIDsEqual } from '@memberjunction/global';
22
+ import { StepConfigurationText } from './flow-step-validation';
22
23
 
23
24
  /**
24
25
  * Represents a single database mutation performed by AgentSpecSync
@@ -128,7 +129,16 @@ export class AgentSpecSync {
128
129
  /**
129
130
  * The raw specification data structure containing all agent configuration
130
131
  */
131
- public spec: AgentSpec;
132
+ public Spec: AgentSpec;
133
+
134
+ /** @deprecated Use {@link Spec}. */
135
+ public get spec(): AgentSpec {
136
+ return this.Spec;
137
+ }
138
+ /** @deprecated Use {@link Spec}. */
139
+ public set spec(value: AgentSpec) {
140
+ this.Spec = value;
141
+ }
132
142
 
133
143
  /**
134
144
  * Tracks whether this spec has been loaded from the database
@@ -183,11 +193,11 @@ export class AgentSpecSync {
183
193
  */
184
194
  constructor(spec?: Partial<AgentSpec>, contextUser?: UserInfo, provider?: IMetadataProvider) {
185
195
  if (spec) {
186
- this.spec = this.initializeSpec(spec);
196
+ this.Spec = this.initializeSpec(spec);
187
197
  this._isDirty = true;
188
198
  } else {
189
199
  // Create minimal empty spec
190
- this.spec = {
200
+ this.Spec = {
191
201
  ID: '', // Will be set on save if empty
192
202
  Name: '',
193
203
  StartingPayloadValidationMode: 'Fail',
@@ -223,17 +233,27 @@ export class AgentSpecSync {
223
233
  * Get all mutations tracked during the last save operation
224
234
  * @returns Array of mutations
225
235
  */
226
- public getMutations(): AgentSpecSyncMutation[] {
236
+ public GetMutations(): AgentSpecSyncMutation[] {
227
237
  return [...this._mutations];
228
238
  }
229
239
 
240
+ /** @deprecated Use {@link GetMutations}. */
241
+ public getMutations(): AgentSpecSyncMutation[] {
242
+ return this.GetMutations();
243
+ }
244
+
230
245
  /**
231
246
  * Clear all tracked mutations
232
247
  */
233
- public clearMutations(): void {
248
+ public ClearMutations(): void {
234
249
  this._mutations = [];
235
250
  }
236
251
 
252
+ /** @deprecated Use {@link ClearMutations}. */
253
+ public clearMutations(): void {
254
+ return this.ClearMutations();
255
+ }
256
+
237
257
  // ===== STATIC FACTORY METHODS =====
238
258
 
239
259
  /**
@@ -475,7 +495,7 @@ export class AgentSpecSync {
475
495
  }
476
496
 
477
497
  // Step 3: Map entities to raw spec format
478
- this.spec = this.mapEntitiesToRawSpec(
498
+ this.Spec = this.mapEntitiesToRawSpec(
479
499
  agentEntity,
480
500
  actionsResult.Results || [],
481
501
  childAgentsResult.Results || [],
@@ -487,9 +507,9 @@ export class AgentSpecSync {
487
507
  );
488
508
 
489
509
  // Step 4: Recursively load sub-agents if requested
490
- if (includeSubAgents && this.spec.SubAgents && this.spec.SubAgents.length > 0) {
510
+ if (includeSubAgents && this.Spec.SubAgents && this.Spec.SubAgents.length > 0) {
491
511
  // Recursively load complete specs for all sub-agents
492
- for (const subAgentSpec of this.spec.SubAgents) {
512
+ for (const subAgentSpec of this.Spec.SubAgents) {
493
513
  if (subAgentSpec.SubAgent && subAgentSpec.SubAgent.ID) {
494
514
  try {
495
515
  // Load the complete sub-agent spec recursively
@@ -729,9 +749,9 @@ export class AgentSpecSync {
729
749
  ActionID: step.ActionID || undefined,
730
750
  SubAgentID: step.SubAgentID || undefined,
731
751
  PromptID: step.PromptID || undefined,
732
- // Loop steps round-trip their body type and bounds; without these, reading a flow agent
733
- // that contains a loop and writing it back would silently turn the loop into an
734
- // unconfigured step that iterates over nothing.
752
+ // Loop & Decision steps round-trip their configuration and bounds; without these, reading
753
+ // a flow agent that contains a decision or loop and writing it back would silently strip
754
+ // the step's logic or bounds.
735
755
  LoopBodyType: step.LoopBodyType || undefined,
736
756
  Configuration: step.Configuration || undefined,
737
757
  ActionInputMapping: this.parseJsonField<Record<string, unknown>>(step.ActionInputMapping),
@@ -786,12 +806,12 @@ export class AgentSpecSync {
786
806
  */
787
807
  async SaveToDatabase(validate: boolean = true): Promise<AgentSpecSyncResult> {
788
808
  // Clear previous mutations
789
- this.clearMutations();
809
+ this.ClearMutations();
790
810
 
791
811
  if (!this._isDirty && this._isLoaded) {
792
812
  // No changes to save
793
813
  return {
794
- agentId: this.spec.ID,
814
+ agentId: this.Spec.ID,
795
815
  success: true,
796
816
  mutations: []
797
817
  };
@@ -799,12 +819,12 @@ export class AgentSpecSync {
799
819
 
800
820
  try {
801
821
  // Step 0: Delete orphaned records (if this is an update)
802
- if (this._isLoaded && this.spec.ID) {
803
- console.log(`🗑️ SaveToDatabase: Loading current database state for agent ${this.spec.ID}...`);
804
- const dbState = await this.loadCurrentDatabaseState(this.spec.ID);
822
+ if (this._isLoaded && this.Spec.ID) {
823
+ console.log(`🗑️ SaveToDatabase: Loading current database state for agent ${this.Spec.ID}...`);
824
+ const dbState = await this.loadCurrentDatabaseState(this.Spec.ID);
805
825
 
806
826
  console.log(`🔍 SaveToDatabase: Identifying orphaned records...`);
807
- const orphans = this.identifyOrphans(dbState, this.spec);
827
+ const orphans = this.identifyOrphans(dbState, this.Spec);
808
828
 
809
829
  console.log(`🗑️ SaveToDatabase: Deleting ${orphans.paths.length} paths, ${orphans.actions.length} actions, ${orphans.steps.length} steps, ${orphans.prompts.length} prompts, ${orphans.relationships.length} relationships, orphaning ${orphans.childAgents.length} child agents...`);
810
830
  await this.deleteOrphans(orphans);
@@ -836,14 +856,14 @@ export class AgentSpecSync {
836
856
  return {
837
857
  agentId,
838
858
  success: true,
839
- mutations: this.getMutations()
859
+ mutations: this.GetMutations()
840
860
  };
841
861
  } catch (error) {
842
862
  // Return failure with whatever mutations were completed before error
843
863
  return {
844
- agentId: this.spec.ID || '',
864
+ agentId: this.Spec.ID || '',
845
865
  success: false,
846
- mutations: this.getMutations()
866
+ mutations: this.GetMutations()
847
867
  };
848
868
  }
849
869
  }
@@ -864,85 +884,85 @@ export class AgentSpecSync {
864
884
  );
865
885
 
866
886
  // Track if this is an update or create
867
- const isUpdate = !!this.spec.ID;
887
+ const isUpdate = !!this.Spec.ID;
868
888
 
869
889
  // If ID exists, load existing record
870
- if (this.spec.ID) {
871
- const loaded = await agentEntity.Load(this.spec.ID);
890
+ if (this.Spec.ID) {
891
+ const loaded = await agentEntity.Load(this.Spec.ID);
872
892
  if (!loaded) {
873
- throw new Error(`Cannot update non-existent agent with ID ${this.spec.ID}`);
893
+ throw new Error(`Cannot update non-existent agent with ID ${this.Spec.ID}`);
874
894
  }
875
895
  }
876
896
 
877
897
  // Map spec to entity fields
878
- agentEntity.Name = this.spec.Name;
879
- agentEntity.Description = this.spec.Description || null;
880
- agentEntity.IconClass = this.spec.IconClass || null;
881
- agentEntity.LogoURL = this.spec.LogoURL || null;
882
- agentEntity.ParentID = this.spec.ParentID || null;
883
- agentEntity.DriverClass = this.spec.DriverClass || null;
884
- agentEntity.ModelSelectionMode = this.spec.ModelSelectionMode || 'Agent Type';
898
+ agentEntity.Name = this.Spec.Name;
899
+ agentEntity.Description = this.Spec.Description || null;
900
+ agentEntity.IconClass = this.Spec.IconClass || null;
901
+ agentEntity.LogoURL = this.Spec.LogoURL || null;
902
+ agentEntity.ParentID = this.Spec.ParentID || null;
903
+ agentEntity.DriverClass = this.Spec.DriverClass || null;
904
+ agentEntity.ModelSelectionMode = this.Spec.ModelSelectionMode || 'Agent Type';
885
905
 
886
906
  // Handle TypeID - supports lookup references or direct GUID
887
- if ((this.spec as any).TypeID) {
888
- agentEntity.TypeID = (this.spec as any).TypeID;
907
+ if ((this.Spec as any).TypeID) {
908
+ agentEntity.TypeID = (this.Spec as any).TypeID;
889
909
  }
890
910
 
891
911
  // Handle Status - defaults to Active if not specified. Typed rather than cast: the cast is
892
912
  // what let `'Inactive'` — a value the CHECK constraint rejects — reach the entity unchallenged.
893
- agentEntity.Status = this.spec.Status ?? 'Active';
913
+ agentEntity.Status = this.Spec.Status ?? 'Active';
894
914
 
895
915
  // Serialize JSON fields
896
916
  agentEntity.PayloadDownstreamPaths = JSON.stringify(
897
- this.spec.PayloadDownstreamPaths || ['*']
917
+ this.Spec.PayloadDownstreamPaths || ['*']
898
918
  );
899
919
  agentEntity.PayloadUpstreamPaths = JSON.stringify(
900
- this.spec.PayloadUpstreamPaths || ['*']
920
+ this.Spec.PayloadUpstreamPaths || ['*']
901
921
  );
902
- agentEntity.PayloadSelfReadPaths = this.spec.PayloadSelfReadPaths
903
- ? JSON.stringify(this.spec.PayloadSelfReadPaths)
922
+ agentEntity.PayloadSelfReadPaths = this.Spec.PayloadSelfReadPaths
923
+ ? JSON.stringify(this.Spec.PayloadSelfReadPaths)
904
924
  : null;
905
- agentEntity.PayloadSelfWritePaths = this.spec.PayloadSelfWritePaths
906
- ? JSON.stringify(this.spec.PayloadSelfWritePaths)
925
+ agentEntity.PayloadSelfWritePaths = this.Spec.PayloadSelfWritePaths
926
+ ? JSON.stringify(this.Spec.PayloadSelfWritePaths)
907
927
  : null;
908
- agentEntity.PayloadScope = this.spec.PayloadScope || null;
928
+ agentEntity.PayloadScope = this.Spec.PayloadScope || null;
909
929
 
910
930
  // Validation fields
911
- agentEntity.FinalPayloadValidation = this.spec.FinalPayloadValidation || null;
912
- agentEntity.FinalPayloadValidationMode = this.spec.FinalPayloadValidationMode || 'Retry';
913
- agentEntity.FinalPayloadValidationMaxRetries = this.spec.FinalPayloadValidationMaxRetries || 3;
931
+ agentEntity.FinalPayloadValidation = this.Spec.FinalPayloadValidation || null;
932
+ agentEntity.FinalPayloadValidationMode = this.Spec.FinalPayloadValidationMode || 'Retry';
933
+ agentEntity.FinalPayloadValidationMaxRetries = this.Spec.FinalPayloadValidationMaxRetries || 3;
914
934
 
915
- agentEntity.StartingPayloadValidation = this.spec.StartingPayloadValidation || null;
916
- agentEntity.StartingPayloadValidationMode = this.spec.StartingPayloadValidationMode || 'Fail';
935
+ agentEntity.StartingPayloadValidation = this.Spec.StartingPayloadValidation || null;
936
+ agentEntity.StartingPayloadValidationMode = this.Spec.StartingPayloadValidationMode || 'Fail';
917
937
 
918
938
  // Resource limits
919
- agentEntity.MaxCostPerRun = this.spec.MaxCostPerRun || null;
920
- agentEntity.MaxTokensPerRun = this.spec.MaxTokensPerRun || null;
921
- agentEntity.MaxIterationsPerRun = this.spec.MaxIterationsPerRun || null;
922
- agentEntity.MaxTimePerRun = this.spec.MaxTimePerRun || null;
939
+ agentEntity.MaxCostPerRun = this.Spec.MaxCostPerRun || null;
940
+ agentEntity.MaxTokensPerRun = this.Spec.MaxTokensPerRun || null;
941
+ agentEntity.MaxIterationsPerRun = this.Spec.MaxIterationsPerRun || null;
942
+ agentEntity.MaxTimePerRun = this.Spec.MaxTimePerRun || null;
923
943
 
924
944
  // Execution frequency
925
- agentEntity.MinExecutionsPerRun = this.spec.MinExecutionsPerRun || null;
926
- agentEntity.MaxExecutionsPerRun = this.spec.MaxExecutionsPerRun || null;
945
+ agentEntity.MinExecutionsPerRun = this.Spec.MinExecutionsPerRun || null;
946
+ agentEntity.MaxExecutionsPerRun = this.Spec.MaxExecutionsPerRun || null;
927
947
 
928
948
  // Other config
929
- agentEntity.DefaultPromptEffortLevel = this.spec.DefaultPromptEffortLevel || null;
930
- agentEntity.ChatHandlingOption = this.spec.ChatHandlingOption || null;
931
- agentEntity.DefaultArtifactTypeID = this.spec.DefaultArtifactTypeID || null;
949
+ agentEntity.DefaultPromptEffortLevel = this.Spec.DefaultPromptEffortLevel || null;
950
+ agentEntity.ChatHandlingOption = this.Spec.ChatHandlingOption || null;
951
+ agentEntity.DefaultArtifactTypeID = this.Spec.DefaultArtifactTypeID || null;
932
952
 
933
953
  // Set OwnerUserID - always use contextUser if available (user creating/modifying the agent)
934
954
  if (this._contextUser) {
935
955
  agentEntity.OwnerUserID = this._contextUser.ID;
936
- } else if (this.spec.OwnerUserID) {
956
+ } else if (this.Spec.OwnerUserID) {
937
957
  // Fallback to spec value if no contextUser provided
938
- agentEntity.OwnerUserID = this.spec.OwnerUserID;
958
+ agentEntity.OwnerUserID = this.Spec.OwnerUserID;
939
959
  }
940
960
 
941
- agentEntity.InvocationMode = this.spec.InvocationMode || 'Any';
961
+ agentEntity.InvocationMode = this.Spec.InvocationMode || 'Any';
942
962
 
943
963
  // Requirements and design documentation
944
- agentEntity.FunctionalRequirements = this.spec.FunctionalRequirements || null;
945
- agentEntity.TechnicalDesign = this.spec.TechnicalDesign || null;
964
+ agentEntity.FunctionalRequirements = this.Spec.FunctionalRequirements || null;
965
+ agentEntity.TechnicalDesign = this.Spec.TechnicalDesign || null;
946
966
 
947
967
  // Validate if requested
948
968
  if (validate) {
@@ -960,14 +980,14 @@ export class AgentSpecSync {
960
980
  }
961
981
 
962
982
  // Update spec with saved ID
963
- this.spec.ID = agentEntity.ID;
983
+ this.Spec.ID = agentEntity.ID;
964
984
 
965
985
  // Track the mutation
966
986
  this.trackMutation(
967
987
  'MJ: AI Agents',
968
988
  isUpdate ? 'Update' : 'Create',
969
989
  agentEntity.ID,
970
- `${isUpdate ? 'Updated' : 'Created'} agent: ${this.spec.Name}`
990
+ `${isUpdate ? 'Updated' : 'Created'} agent: ${this.Spec.Name}`
971
991
  );
972
992
 
973
993
  return agentEntity.ID;
@@ -985,13 +1005,13 @@ export class AgentSpecSync {
985
1005
  * @throws {Error} If any action save fails
986
1006
  */
987
1007
  private async saveActions(agentId: string): Promise<void> {
988
- if (!this.spec.Actions || this.spec.Actions.length === 0) {
1008
+ if (!this.Spec.Actions || this.Spec.Actions.length === 0) {
989
1009
  return;
990
1010
  }
991
1011
 
992
1012
  const md = this.providerToUse;
993
1013
 
994
- for (const actionSpec of this.spec.Actions) {
1014
+ for (const actionSpec of this.Spec.Actions) {
995
1015
  const actionEntity = await md.GetEntityObject<MJAIAgentActionEntity>(
996
1016
  'MJ: AI Agent Actions',
997
1017
  this._contextUser
@@ -1046,11 +1066,11 @@ export class AgentSpecSync {
1046
1066
  * @throws {Error} If any sub-agent save fails
1047
1067
  */
1048
1068
  private async saveSubAgents(agentId: string): Promise<void> {
1049
- if (!this.spec.SubAgents || this.spec.SubAgents.length === 0) {
1069
+ if (!this.Spec.SubAgents || this.Spec.SubAgents.length === 0) {
1050
1070
  return;
1051
1071
  }
1052
1072
 
1053
- for (const SubAgentSpec of this.spec.SubAgents) {
1073
+ for (const SubAgentSpec of this.Spec.SubAgents) {
1054
1074
  if (SubAgentSpec.Type === 'child') {
1055
1075
  await this.saveChildSubAgent(agentId, SubAgentSpec);
1056
1076
  } else {
@@ -1194,20 +1214,20 @@ export class AgentSpecSync {
1194
1214
  * @throws {Error} If any prompt save fails
1195
1215
  */
1196
1216
  private async savePrompts(agentId: string): Promise<void> {
1197
- console.log(`💬 savePrompts: Called with agentId=${agentId}, Prompts=${this.spec.Prompts ? this.spec.Prompts.length : 'undefined'}`);
1217
+ console.log(`💬 savePrompts: Called with agentId=${agentId}, Prompts=${this.Spec.Prompts ? this.Spec.Prompts.length : 'undefined'}`);
1198
1218
 
1199
- if (!this.spec.Prompts || this.spec.Prompts.length === 0) {
1219
+ if (!this.Spec.Prompts || this.Spec.Prompts.length === 0) {
1200
1220
  console.log('💬 savePrompts: No prompts to save, returning early');
1201
1221
  return;
1202
1222
  }
1203
1223
 
1204
- console.log(`💬 savePrompts: Processing ${this.spec.Prompts.length} prompt(s)...`);
1224
+ console.log(`💬 savePrompts: Processing ${this.Spec.Prompts.length} prompt(s)...`);
1205
1225
  const md = this.providerToUse;
1206
1226
  const rv = RunView.FromMetadataProvider(this._provider);
1207
1227
 
1208
1228
  // Step 1: Create or update AIPrompt records
1209
- for (let i = 0; i < this.spec.Prompts.length; i++) {
1210
- const promptSpec = this.spec.Prompts[i];
1229
+ for (let i = 0; i < this.Spec.Prompts.length; i++) {
1230
+ const promptSpec = this.Spec.Prompts[i];
1211
1231
 
1212
1232
  // Check if this is an update (has PromptID) or create (no PromptID)
1213
1233
  let promptEntity = await md.GetEntityObject<any>(
@@ -1228,8 +1248,8 @@ export class AgentSpecSync {
1228
1248
  }
1229
1249
 
1230
1250
  // Set all required fields (both create and update)
1231
- promptEntity.Name = `${this.spec.Name} - Prompt ${i + 1}`;
1232
- promptEntity.Description = `Agent prompt ${i + 1} for ${this.spec.Name}`;
1251
+ promptEntity.Name = `${this.Spec.Name} - Prompt ${i + 1}`;
1252
+ promptEntity.Description = `Agent prompt ${i + 1} for ${this.Spec.Name}`;
1233
1253
  promptEntity.TypeID = 'a6da423e-f36b-1410-8dac-00021f8b792e'; // Chat type
1234
1254
  promptEntity.Status = 'Active';
1235
1255
  promptEntity.ResponseFormat = 'JSON';
@@ -1257,7 +1277,7 @@ export class AgentSpecSync {
1257
1277
 
1258
1278
  const saved = await promptEntity.Save();
1259
1279
  if (!saved) {
1260
- throw new Error(`Failed to save prompt ${i + 1} for agent ${this.spec.Name}`);
1280
+ throw new Error(`Failed to save prompt ${i + 1} for agent ${this.Spec.Name}`);
1261
1281
  }
1262
1282
 
1263
1283
  // Track mutation
@@ -1320,7 +1340,7 @@ export class AgentSpecSync {
1320
1340
  console.log(`✅ savePrompts: ${isJunctionUpdate ? 'Updated' : 'Created'} AIAgentPrompt junction with ID: ${agentPromptEntity.ID}`);
1321
1341
  }
1322
1342
 
1323
- console.log(`✅ savePrompts: Successfully saved all ${this.spec.Prompts.length} prompt(s)`);
1343
+ console.log(`✅ savePrompts: Successfully saved all ${this.Spec.Prompts.length} prompt(s)`);
1324
1344
  }
1325
1345
 
1326
1346
  /**
@@ -1334,17 +1354,17 @@ export class AgentSpecSync {
1334
1354
  * @throws {Error} If any step save fails
1335
1355
  */
1336
1356
  private async saveSteps(agentId: string): Promise<void> {
1337
- console.log(`🔷 saveSteps: Called with agentId=${agentId}, Steps=${this.spec.Steps ? this.spec.Steps.length : 'undefined'}`);
1357
+ console.log(`🔷 saveSteps: Called with agentId=${agentId}, Steps=${this.Spec.Steps ? this.Spec.Steps.length : 'undefined'}`);
1338
1358
 
1339
- if (!this.spec.Steps || this.spec.Steps.length === 0) {
1359
+ if (!this.Spec.Steps || this.Spec.Steps.length === 0) {
1340
1360
  console.log('🔷 saveSteps: No steps to save, returning early');
1341
1361
  return;
1342
1362
  }
1343
1363
 
1344
- console.log(`🔷 saveSteps: Processing ${this.spec.Steps.length} step(s)...`);
1364
+ console.log(`🔷 saveSteps: Processing ${this.Spec.Steps.length} step(s)...`);
1345
1365
  const md = this.providerToUse;
1346
1366
 
1347
- for (const stepSpec of this.spec.Steps) {
1367
+ for (const stepSpec of this.Spec.Steps) {
1348
1368
  const stepEntity = await md.GetEntityObject<MJAIAgentStepEntity>(
1349
1369
  'MJ: AI Agent Steps',
1350
1370
  this._contextUser
@@ -1369,7 +1389,9 @@ export class AgentSpecSync {
1369
1389
  // Configuration carries the bounds. Written for every step because a step that STOPS
1370
1390
  // being a loop must have these cleared, not left behind from its previous shape.
1371
1391
  stepEntity.LoopBodyType = stepSpec.LoopBodyType || null;
1372
- stepEntity.Configuration = stepSpec.Configuration || null;
1392
+ // Stored as JSON text even when the model wrote an object, with a Decision step's aliases
1393
+ // normalized: the text the Architect's validator compiled.
1394
+ stepEntity.Configuration = StepConfigurationText(stepSpec);
1373
1395
 
1374
1396
  // Handle inline prompt creation for Prompt-type steps
1375
1397
  // If StepType is Prompt and PromptID is empty, create a new AIPrompt record
@@ -1438,7 +1460,7 @@ export class AgentSpecSync {
1438
1460
 
1439
1461
  const saved = await stepEntity.Save();
1440
1462
  if (!saved) {
1441
- throw new Error(`Failed to save step "${stepSpec.Name}" for agent ${this.spec.Name}`);
1463
+ throw new Error(`Failed to save step "${stepSpec.Name}" for agent ${this.Spec.Name}`);
1442
1464
  }
1443
1465
 
1444
1466
  // Track mutation
@@ -1455,7 +1477,7 @@ export class AgentSpecSync {
1455
1477
  console.log(`✅ saveSteps: Created AIAgentStep with ID: ${stepEntity.ID}`);
1456
1478
  }
1457
1479
 
1458
- console.log(`✅ saveSteps: Successfully saved all ${this.spec.Steps.length} step(s)`);
1480
+ console.log(`✅ saveSteps: Successfully saved all ${this.Spec.Steps.length} step(s)`);
1459
1481
  }
1460
1482
 
1461
1483
  /**
@@ -1469,27 +1491,27 @@ export class AgentSpecSync {
1469
1491
  * @throws {Error} If any path save fails
1470
1492
  */
1471
1493
  private async saveStepPaths(agentId: string): Promise<void> {
1472
- console.log(`🔶 saveStepPaths: Called with agentId=${agentId}, Paths=${this.spec.Paths ? this.spec.Paths.length : 'undefined'}`);
1494
+ console.log(`🔶 saveStepPaths: Called with agentId=${agentId}, Paths=${this.Spec.Paths ? this.Spec.Paths.length : 'undefined'}`);
1473
1495
 
1474
- if (!this.spec.Paths || this.spec.Paths.length === 0) {
1496
+ if (!this.Spec.Paths || this.Spec.Paths.length === 0) {
1475
1497
  console.log('🔶 saveStepPaths: No paths to save, returning early');
1476
1498
  return;
1477
1499
  }
1478
1500
 
1479
- console.log(`🔶 saveStepPaths: Processing ${this.spec.Paths.length} path(s)...`);
1501
+ console.log(`🔶 saveStepPaths: Processing ${this.Spec.Paths.length} path(s)...`);
1480
1502
  const md = this.providerToUse;
1481
1503
 
1482
1504
  // Create a map of step names to IDs for easy lookup
1483
1505
  const stepNameToId = new Map<string, string>();
1484
- if (this.spec.Steps) {
1485
- for (const step of this.spec.Steps) {
1506
+ if (this.Spec.Steps) {
1507
+ for (const step of this.Spec.Steps) {
1486
1508
  if (step.ID) {
1487
1509
  stepNameToId.set(step.Name, step.ID);
1488
1510
  }
1489
1511
  }
1490
1512
  }
1491
1513
 
1492
- for (const pathSpec of this.spec.Paths) {
1514
+ for (const pathSpec of this.Spec.Paths) {
1493
1515
  const pathEntity = await md.GetEntityObject<MJAIAgentStepPathEntity>(
1494
1516
  'MJ: AI Agent Step Paths',
1495
1517
  this._contextUser
@@ -1541,7 +1563,7 @@ export class AgentSpecSync {
1541
1563
  console.log(`✅ saveStepPaths: Created AIAgentStepPath with ID: ${pathEntity.ID}`);
1542
1564
  }
1543
1565
 
1544
- console.log(`✅ saveStepPaths: Successfully saved all ${this.spec.Paths.length} path(s)`);
1566
+ console.log(`✅ saveStepPaths: Successfully saved all ${this.Spec.Paths.length} path(s)`);
1545
1567
  }
1546
1568
 
1547
1569
  // ===== UTILITY METHODS =====
@@ -1641,7 +1663,7 @@ export class AgentSpecSync {
1641
1663
  * ```
1642
1664
  */
1643
1665
  public toJSON(): AgentSpec {
1644
- return { ...this.spec };
1666
+ return { ...this.Spec };
1645
1667
  }
1646
1668
 
1647
1669
  /**
@@ -1649,19 +1671,29 @@ export class AgentSpecSync {
1649
1671
  *
1650
1672
  * @returns True if there are unsaved changes, false otherwise
1651
1673
  */
1652
- public get isDirty(): boolean {
1674
+ public get IsDirty(): boolean {
1653
1675
  return this._isDirty;
1654
1676
  }
1655
1677
 
1678
+ /** @deprecated Use {@link IsDirty}. */
1679
+ public get isDirty(): boolean {
1680
+ return this.IsDirty;
1681
+ }
1682
+
1656
1683
  /**
1657
1684
  * Check if this spec has been loaded from the database.
1658
1685
  *
1659
1686
  * @returns True if loaded from database, false if created in memory
1660
1687
  */
1661
- public get isLoaded(): boolean {
1688
+ public get IsLoaded(): boolean {
1662
1689
  return this._isLoaded;
1663
1690
  }
1664
1691
 
1692
+ /** @deprecated Use {@link IsLoaded}. */
1693
+ public get isLoaded(): boolean {
1694
+ return this.IsLoaded;
1695
+ }
1696
+
1665
1697
  /**
1666
1698
  * Mark the spec as having changes.
1667
1699
  *
@@ -1677,10 +1709,15 @@ export class AgentSpecSync {
1677
1709
  * await spec.SaveToDatabase();
1678
1710
  * ```
1679
1711
  */
1680
- public markDirty(): void {
1712
+ public MarkDirty(): void {
1681
1713
  this._isDirty = true;
1682
1714
  }
1683
1715
 
1716
+ /** @deprecated Use {@link MarkDirty}. */
1717
+ public markDirty(): void {
1718
+ return this.MarkDirty();
1719
+ }
1720
+
1684
1721
  /**
1685
1722
  * Mark the spec as having been loaded from the database.
1686
1723
  *
@@ -1698,10 +1735,15 @@ export class AgentSpecSync {
1698
1735
  * await childSync.SaveToDatabase();
1699
1736
  * ```
1700
1737
  */
1701
- public markLoaded(): void {
1738
+ public MarkLoaded(): void {
1702
1739
  this._isLoaded = true;
1703
1740
  }
1704
1741
 
1742
+ /** @deprecated Use {@link MarkLoaded}. */
1743
+ public markLoaded(): void {
1744
+ return this.MarkLoaded();
1745
+ }
1746
+
1705
1747
  // ===== DELETE/ORPHAN METHODS =====
1706
1748
 
1707
1749
  /**