@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.
@@ -1,5 +1,6 @@
1
1
  import { Metadata, RunView, LogError } from '@memberjunction/core';
2
2
  import { UUIDsEqual } from '@memberjunction/global';
3
+ import { StepConfigurationText } from './flow-step-validation.js';
3
4
  /**
4
5
  * AgentSpecSync provides a high-level interface for working with AI Agent metadata in MemberJunction.
5
6
  *
@@ -51,6 +52,14 @@ import { UUIDsEqual } from '@memberjunction/global';
51
52
  * @module @memberjunction/ai-agent-manager
52
53
  */
53
54
  export class AgentSpecSync {
55
+ /** @deprecated Use {@link Spec}. */
56
+ get spec() {
57
+ return this.Spec;
58
+ }
59
+ /** @deprecated Use {@link Spec}. */
60
+ set spec(value) {
61
+ this.Spec = value;
62
+ }
54
63
  /**
55
64
  * Returns the metadata provider to use for entity operations. Prefers a
56
65
  * caller-supplied provider for server isolation; otherwise falls back to
@@ -87,12 +96,12 @@ export class AgentSpecSync {
87
96
  */
88
97
  this._mutations = [];
89
98
  if (spec) {
90
- this.spec = this.initializeSpec(spec);
99
+ this.Spec = this.initializeSpec(spec);
91
100
  this._isDirty = true;
92
101
  }
93
102
  else {
94
103
  // Create minimal empty spec
95
- this.spec = {
104
+ this.Spec = {
96
105
  ID: '', // Will be set on save if empty
97
106
  Name: '',
98
107
  StartingPayloadValidationMode: 'Fail',
@@ -120,15 +129,23 @@ export class AgentSpecSync {
120
129
  * Get all mutations tracked during the last save operation
121
130
  * @returns Array of mutations
122
131
  */
123
- getMutations() {
132
+ GetMutations() {
124
133
  return [...this._mutations];
125
134
  }
135
+ /** @deprecated Use {@link GetMutations}. */
136
+ getMutations() {
137
+ return this.GetMutations();
138
+ }
126
139
  /**
127
140
  * Clear all tracked mutations
128
141
  */
129
- clearMutations() {
142
+ ClearMutations() {
130
143
  this._mutations = [];
131
144
  }
145
+ /** @deprecated Use {@link ClearMutations}. */
146
+ clearMutations() {
147
+ return this.ClearMutations();
148
+ }
132
149
  // ===== STATIC FACTORY METHODS =====
133
150
  /**
134
151
  * Load an agent and its complete hierarchy from the database by ID.
@@ -342,11 +359,11 @@ export class AgentSpecSync {
342
359
  };
343
360
  }
344
361
  // Step 3: Map entities to raw spec format
345
- this.spec = this.mapEntitiesToRawSpec(agentEntity, actionsResult.Results || [], childAgentsResult.Results || [], relatedAgentsResult.Results || [], promptsResult.Results || [], fullPromptsResult.Results || [], stepsResult.Results || [], pathsResult.Results || []);
362
+ this.Spec = this.mapEntitiesToRawSpec(agentEntity, actionsResult.Results || [], childAgentsResult.Results || [], relatedAgentsResult.Results || [], promptsResult.Results || [], fullPromptsResult.Results || [], stepsResult.Results || [], pathsResult.Results || []);
346
363
  // Step 4: Recursively load sub-agents if requested
347
- if (includeSubAgents && this.spec.SubAgents && this.spec.SubAgents.length > 0) {
364
+ if (includeSubAgents && this.Spec.SubAgents && this.Spec.SubAgents.length > 0) {
348
365
  // Recursively load complete specs for all sub-agents
349
- for (const subAgentSpec of this.spec.SubAgents) {
366
+ for (const subAgentSpec of this.Spec.SubAgents) {
350
367
  if (subAgentSpec.SubAgent && subAgentSpec.SubAgent.ID) {
351
368
  try {
352
369
  // Load the complete sub-agent spec recursively
@@ -550,9 +567,9 @@ export class AgentSpecSync {
550
567
  ActionID: step.ActionID || undefined,
551
568
  SubAgentID: step.SubAgentID || undefined,
552
569
  PromptID: step.PromptID || undefined,
553
- // Loop steps round-trip their body type and bounds; without these, reading a flow agent
554
- // that contains a loop and writing it back would silently turn the loop into an
555
- // unconfigured step that iterates over nothing.
570
+ // Loop & Decision steps round-trip their configuration and bounds; without these, reading
571
+ // a flow agent that contains a decision or loop and writing it back would silently strip
572
+ // the step's logic or bounds.
556
573
  LoopBodyType: step.LoopBodyType || undefined,
557
574
  Configuration: step.Configuration || undefined,
558
575
  ActionInputMapping: this.parseJsonField(step.ActionInputMapping),
@@ -604,22 +621,22 @@ export class AgentSpecSync {
604
621
  */
605
622
  async SaveToDatabase(validate = true) {
606
623
  // Clear previous mutations
607
- this.clearMutations();
624
+ this.ClearMutations();
608
625
  if (!this._isDirty && this._isLoaded) {
609
626
  // No changes to save
610
627
  return {
611
- agentId: this.spec.ID,
628
+ agentId: this.Spec.ID,
612
629
  success: true,
613
630
  mutations: []
614
631
  };
615
632
  }
616
633
  try {
617
634
  // Step 0: Delete orphaned records (if this is an update)
618
- if (this._isLoaded && this.spec.ID) {
619
- console.log(`🗑️ SaveToDatabase: Loading current database state for agent ${this.spec.ID}...`);
620
- const dbState = await this.loadCurrentDatabaseState(this.spec.ID);
635
+ if (this._isLoaded && this.Spec.ID) {
636
+ console.log(`🗑️ SaveToDatabase: Loading current database state for agent ${this.Spec.ID}...`);
637
+ const dbState = await this.loadCurrentDatabaseState(this.Spec.ID);
621
638
  console.log(`🔍 SaveToDatabase: Identifying orphaned records...`);
622
- const orphans = this.identifyOrphans(dbState, this.spec);
639
+ const orphans = this.identifyOrphans(dbState, this.Spec);
623
640
  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...`);
624
641
  await this.deleteOrphans(orphans);
625
642
  console.log(`✅ SaveToDatabase: Orphan cleanup complete`);
@@ -641,15 +658,15 @@ export class AgentSpecSync {
641
658
  return {
642
659
  agentId,
643
660
  success: true,
644
- mutations: this.getMutations()
661
+ mutations: this.GetMutations()
645
662
  };
646
663
  }
647
664
  catch (error) {
648
665
  // Return failure with whatever mutations were completed before error
649
666
  return {
650
- agentId: this.spec.ID || '',
667
+ agentId: this.Spec.ID || '',
651
668
  success: false,
652
- mutations: this.getMutations()
669
+ mutations: this.GetMutations()
653
670
  };
654
671
  }
655
672
  }
@@ -665,69 +682,69 @@ export class AgentSpecSync {
665
682
  const md = this.providerToUse;
666
683
  const agentEntity = await md.GetEntityObject('MJ: AI Agents', this._contextUser);
667
684
  // Track if this is an update or create
668
- const isUpdate = !!this.spec.ID;
685
+ const isUpdate = !!this.Spec.ID;
669
686
  // If ID exists, load existing record
670
- if (this.spec.ID) {
671
- const loaded = await agentEntity.Load(this.spec.ID);
687
+ if (this.Spec.ID) {
688
+ const loaded = await agentEntity.Load(this.Spec.ID);
672
689
  if (!loaded) {
673
- throw new Error(`Cannot update non-existent agent with ID ${this.spec.ID}`);
690
+ throw new Error(`Cannot update non-existent agent with ID ${this.Spec.ID}`);
674
691
  }
675
692
  }
676
693
  // Map spec to entity fields
677
- agentEntity.Name = this.spec.Name;
678
- agentEntity.Description = this.spec.Description || null;
679
- agentEntity.IconClass = this.spec.IconClass || null;
680
- agentEntity.LogoURL = this.spec.LogoURL || null;
681
- agentEntity.ParentID = this.spec.ParentID || null;
682
- agentEntity.DriverClass = this.spec.DriverClass || null;
683
- agentEntity.ModelSelectionMode = this.spec.ModelSelectionMode || 'Agent Type';
694
+ agentEntity.Name = this.Spec.Name;
695
+ agentEntity.Description = this.Spec.Description || null;
696
+ agentEntity.IconClass = this.Spec.IconClass || null;
697
+ agentEntity.LogoURL = this.Spec.LogoURL || null;
698
+ agentEntity.ParentID = this.Spec.ParentID || null;
699
+ agentEntity.DriverClass = this.Spec.DriverClass || null;
700
+ agentEntity.ModelSelectionMode = this.Spec.ModelSelectionMode || 'Agent Type';
684
701
  // Handle TypeID - supports lookup references or direct GUID
685
- if (this.spec.TypeID) {
686
- agentEntity.TypeID = this.spec.TypeID;
702
+ if (this.Spec.TypeID) {
703
+ agentEntity.TypeID = this.Spec.TypeID;
687
704
  }
688
705
  // Handle Status - defaults to Active if not specified. Typed rather than cast: the cast is
689
706
  // what let `'Inactive'` — a value the CHECK constraint rejects — reach the entity unchallenged.
690
- agentEntity.Status = this.spec.Status ?? 'Active';
707
+ agentEntity.Status = this.Spec.Status ?? 'Active';
691
708
  // Serialize JSON fields
692
- agentEntity.PayloadDownstreamPaths = JSON.stringify(this.spec.PayloadDownstreamPaths || ['*']);
693
- agentEntity.PayloadUpstreamPaths = JSON.stringify(this.spec.PayloadUpstreamPaths || ['*']);
694
- agentEntity.PayloadSelfReadPaths = this.spec.PayloadSelfReadPaths
695
- ? JSON.stringify(this.spec.PayloadSelfReadPaths)
709
+ agentEntity.PayloadDownstreamPaths = JSON.stringify(this.Spec.PayloadDownstreamPaths || ['*']);
710
+ agentEntity.PayloadUpstreamPaths = JSON.stringify(this.Spec.PayloadUpstreamPaths || ['*']);
711
+ agentEntity.PayloadSelfReadPaths = this.Spec.PayloadSelfReadPaths
712
+ ? JSON.stringify(this.Spec.PayloadSelfReadPaths)
696
713
  : null;
697
- agentEntity.PayloadSelfWritePaths = this.spec.PayloadSelfWritePaths
698
- ? JSON.stringify(this.spec.PayloadSelfWritePaths)
714
+ agentEntity.PayloadSelfWritePaths = this.Spec.PayloadSelfWritePaths
715
+ ? JSON.stringify(this.Spec.PayloadSelfWritePaths)
699
716
  : null;
700
- agentEntity.PayloadScope = this.spec.PayloadScope || null;
717
+ agentEntity.PayloadScope = this.Spec.PayloadScope || null;
701
718
  // Validation fields
702
- agentEntity.FinalPayloadValidation = this.spec.FinalPayloadValidation || null;
703
- agentEntity.FinalPayloadValidationMode = this.spec.FinalPayloadValidationMode || 'Retry';
704
- agentEntity.FinalPayloadValidationMaxRetries = this.spec.FinalPayloadValidationMaxRetries || 3;
705
- agentEntity.StartingPayloadValidation = this.spec.StartingPayloadValidation || null;
706
- agentEntity.StartingPayloadValidationMode = this.spec.StartingPayloadValidationMode || 'Fail';
719
+ agentEntity.FinalPayloadValidation = this.Spec.FinalPayloadValidation || null;
720
+ agentEntity.FinalPayloadValidationMode = this.Spec.FinalPayloadValidationMode || 'Retry';
721
+ agentEntity.FinalPayloadValidationMaxRetries = this.Spec.FinalPayloadValidationMaxRetries || 3;
722
+ agentEntity.StartingPayloadValidation = this.Spec.StartingPayloadValidation || null;
723
+ agentEntity.StartingPayloadValidationMode = this.Spec.StartingPayloadValidationMode || 'Fail';
707
724
  // Resource limits
708
- agentEntity.MaxCostPerRun = this.spec.MaxCostPerRun || null;
709
- agentEntity.MaxTokensPerRun = this.spec.MaxTokensPerRun || null;
710
- agentEntity.MaxIterationsPerRun = this.spec.MaxIterationsPerRun || null;
711
- agentEntity.MaxTimePerRun = this.spec.MaxTimePerRun || null;
725
+ agentEntity.MaxCostPerRun = this.Spec.MaxCostPerRun || null;
726
+ agentEntity.MaxTokensPerRun = this.Spec.MaxTokensPerRun || null;
727
+ agentEntity.MaxIterationsPerRun = this.Spec.MaxIterationsPerRun || null;
728
+ agentEntity.MaxTimePerRun = this.Spec.MaxTimePerRun || null;
712
729
  // Execution frequency
713
- agentEntity.MinExecutionsPerRun = this.spec.MinExecutionsPerRun || null;
714
- agentEntity.MaxExecutionsPerRun = this.spec.MaxExecutionsPerRun || null;
730
+ agentEntity.MinExecutionsPerRun = this.Spec.MinExecutionsPerRun || null;
731
+ agentEntity.MaxExecutionsPerRun = this.Spec.MaxExecutionsPerRun || null;
715
732
  // Other config
716
- agentEntity.DefaultPromptEffortLevel = this.spec.DefaultPromptEffortLevel || null;
717
- agentEntity.ChatHandlingOption = this.spec.ChatHandlingOption || null;
718
- agentEntity.DefaultArtifactTypeID = this.spec.DefaultArtifactTypeID || null;
733
+ agentEntity.DefaultPromptEffortLevel = this.Spec.DefaultPromptEffortLevel || null;
734
+ agentEntity.ChatHandlingOption = this.Spec.ChatHandlingOption || null;
735
+ agentEntity.DefaultArtifactTypeID = this.Spec.DefaultArtifactTypeID || null;
719
736
  // Set OwnerUserID - always use contextUser if available (user creating/modifying the agent)
720
737
  if (this._contextUser) {
721
738
  agentEntity.OwnerUserID = this._contextUser.ID;
722
739
  }
723
- else if (this.spec.OwnerUserID) {
740
+ else if (this.Spec.OwnerUserID) {
724
741
  // Fallback to spec value if no contextUser provided
725
- agentEntity.OwnerUserID = this.spec.OwnerUserID;
742
+ agentEntity.OwnerUserID = this.Spec.OwnerUserID;
726
743
  }
727
- agentEntity.InvocationMode = this.spec.InvocationMode || 'Any';
744
+ agentEntity.InvocationMode = this.Spec.InvocationMode || 'Any';
728
745
  // Requirements and design documentation
729
- agentEntity.FunctionalRequirements = this.spec.FunctionalRequirements || null;
730
- agentEntity.TechnicalDesign = this.spec.TechnicalDesign || null;
746
+ agentEntity.FunctionalRequirements = this.Spec.FunctionalRequirements || null;
747
+ agentEntity.TechnicalDesign = this.Spec.TechnicalDesign || null;
731
748
  // Validate if requested
732
749
  if (validate) {
733
750
  const validation = agentEntity.Validate();
@@ -742,9 +759,9 @@ export class AgentSpecSync {
742
759
  throw new Error('Failed to save agent entity');
743
760
  }
744
761
  // Update spec with saved ID
745
- this.spec.ID = agentEntity.ID;
762
+ this.Spec.ID = agentEntity.ID;
746
763
  // Track the mutation
747
- this.trackMutation('MJ: AI Agents', isUpdate ? 'Update' : 'Create', agentEntity.ID, `${isUpdate ? 'Updated' : 'Created'} agent: ${this.spec.Name}`);
764
+ this.trackMutation('MJ: AI Agents', isUpdate ? 'Update' : 'Create', agentEntity.ID, `${isUpdate ? 'Updated' : 'Created'} agent: ${this.Spec.Name}`);
748
765
  return agentEntity.ID;
749
766
  }
750
767
  /**
@@ -759,11 +776,11 @@ export class AgentSpecSync {
759
776
  * @throws {Error} If any action save fails
760
777
  */
761
778
  async saveActions(agentId) {
762
- if (!this.spec.Actions || this.spec.Actions.length === 0) {
779
+ if (!this.Spec.Actions || this.Spec.Actions.length === 0) {
763
780
  return;
764
781
  }
765
782
  const md = this.providerToUse;
766
- for (const actionSpec of this.spec.Actions) {
783
+ for (const actionSpec of this.Spec.Actions) {
767
784
  const actionEntity = await md.GetEntityObject('MJ: AI Agent Actions', this._contextUser);
768
785
  // Track if this is an update or create
769
786
  const isUpdate = !!actionSpec.AgentActionID;
@@ -803,10 +820,10 @@ export class AgentSpecSync {
803
820
  * @throws {Error} If any sub-agent save fails
804
821
  */
805
822
  async saveSubAgents(agentId) {
806
- if (!this.spec.SubAgents || this.spec.SubAgents.length === 0) {
823
+ if (!this.Spec.SubAgents || this.Spec.SubAgents.length === 0) {
807
824
  return;
808
825
  }
809
- for (const SubAgentSpec of this.spec.SubAgents) {
826
+ for (const SubAgentSpec of this.Spec.SubAgents) {
810
827
  if (SubAgentSpec.Type === 'child') {
811
828
  await this.saveChildSubAgent(agentId, SubAgentSpec);
812
829
  }
@@ -928,17 +945,17 @@ export class AgentSpecSync {
928
945
  * @throws {Error} If any prompt save fails
929
946
  */
930
947
  async savePrompts(agentId) {
931
- console.log(`💬 savePrompts: Called with agentId=${agentId}, Prompts=${this.spec.Prompts ? this.spec.Prompts.length : 'undefined'}`);
932
- if (!this.spec.Prompts || this.spec.Prompts.length === 0) {
948
+ console.log(`💬 savePrompts: Called with agentId=${agentId}, Prompts=${this.Spec.Prompts ? this.Spec.Prompts.length : 'undefined'}`);
949
+ if (!this.Spec.Prompts || this.Spec.Prompts.length === 0) {
933
950
  console.log('💬 savePrompts: No prompts to save, returning early');
934
951
  return;
935
952
  }
936
- console.log(`💬 savePrompts: Processing ${this.spec.Prompts.length} prompt(s)...`);
953
+ console.log(`💬 savePrompts: Processing ${this.Spec.Prompts.length} prompt(s)...`);
937
954
  const md = this.providerToUse;
938
955
  const rv = RunView.FromMetadataProvider(this._provider);
939
956
  // Step 1: Create or update AIPrompt records
940
- for (let i = 0; i < this.spec.Prompts.length; i++) {
941
- const promptSpec = this.spec.Prompts[i];
957
+ for (let i = 0; i < this.Spec.Prompts.length; i++) {
958
+ const promptSpec = this.Spec.Prompts[i];
942
959
  // Check if this is an update (has PromptID) or create (no PromptID)
943
960
  let promptEntity = await md.GetEntityObject('MJ: AI Prompts', this._contextUser);
944
961
  let isUpdate = false;
@@ -954,8 +971,8 @@ export class AgentSpecSync {
954
971
  }
955
972
  }
956
973
  // Set all required fields (both create and update)
957
- promptEntity.Name = `${this.spec.Name} - Prompt ${i + 1}`;
958
- promptEntity.Description = `Agent prompt ${i + 1} for ${this.spec.Name}`;
974
+ promptEntity.Name = `${this.Spec.Name} - Prompt ${i + 1}`;
975
+ promptEntity.Description = `Agent prompt ${i + 1} for ${this.Spec.Name}`;
959
976
  promptEntity.TypeID = 'a6da423e-f36b-1410-8dac-00021f8b792e'; // Chat type
960
977
  promptEntity.Status = 'Active';
961
978
  promptEntity.ResponseFormat = 'JSON';
@@ -981,7 +998,7 @@ export class AgentSpecSync {
981
998
  }
982
999
  const saved = await promptEntity.Save();
983
1000
  if (!saved) {
984
- throw new Error(`Failed to save prompt ${i + 1} for agent ${this.spec.Name}`);
1001
+ throw new Error(`Failed to save prompt ${i + 1} for agent ${this.Spec.Name}`);
985
1002
  }
986
1003
  // Track mutation
987
1004
  this.trackMutation('MJ: AI Prompts', isUpdate ? 'Update' : 'Create', promptEntity.ID, `${isUpdate ? 'Updated' : 'Created'} prompt: ${promptEntity.Name}`);
@@ -1021,7 +1038,7 @@ export class AgentSpecSync {
1021
1038
  this.trackMutation('MJ: AI Agent Prompts', isJunctionUpdate ? 'Update' : 'Create', agentPromptEntity.ID, `${isJunctionUpdate ? 'Updated' : 'Created'} agent-prompt junction for prompt ${promptEntity.ID}`);
1022
1039
  console.log(`✅ savePrompts: ${isJunctionUpdate ? 'Updated' : 'Created'} AIAgentPrompt junction with ID: ${agentPromptEntity.ID}`);
1023
1040
  }
1024
- console.log(`✅ savePrompts: Successfully saved all ${this.spec.Prompts.length} prompt(s)`);
1041
+ console.log(`✅ savePrompts: Successfully saved all ${this.Spec.Prompts.length} prompt(s)`);
1025
1042
  }
1026
1043
  /**
1027
1044
  * Save all steps for this agent (Flow agents only).
@@ -1034,14 +1051,14 @@ export class AgentSpecSync {
1034
1051
  * @throws {Error} If any step save fails
1035
1052
  */
1036
1053
  async saveSteps(agentId) {
1037
- console.log(`🔷 saveSteps: Called with agentId=${agentId}, Steps=${this.spec.Steps ? this.spec.Steps.length : 'undefined'}`);
1038
- if (!this.spec.Steps || this.spec.Steps.length === 0) {
1054
+ console.log(`🔷 saveSteps: Called with agentId=${agentId}, Steps=${this.Spec.Steps ? this.Spec.Steps.length : 'undefined'}`);
1055
+ if (!this.Spec.Steps || this.Spec.Steps.length === 0) {
1039
1056
  console.log('🔷 saveSteps: No steps to save, returning early');
1040
1057
  return;
1041
1058
  }
1042
- console.log(`🔷 saveSteps: Processing ${this.spec.Steps.length} step(s)...`);
1059
+ console.log(`🔷 saveSteps: Processing ${this.Spec.Steps.length} step(s)...`);
1043
1060
  const md = this.providerToUse;
1044
- for (const stepSpec of this.spec.Steps) {
1061
+ for (const stepSpec of this.Spec.Steps) {
1045
1062
  const stepEntity = await md.GetEntityObject('MJ: AI Agent Steps', this._contextUser);
1046
1063
  // Load existing if ID provided
1047
1064
  const isUpdate = !!(stepSpec.ID && stepSpec.ID !== '');
@@ -1061,7 +1078,9 @@ export class AgentSpecSync {
1061
1078
  // Configuration carries the bounds. Written for every step because a step that STOPS
1062
1079
  // being a loop must have these cleared, not left behind from its previous shape.
1063
1080
  stepEntity.LoopBodyType = stepSpec.LoopBodyType || null;
1064
- stepEntity.Configuration = stepSpec.Configuration || null;
1081
+ // Stored as JSON text even when the model wrote an object, with a Decision step's aliases
1082
+ // normalized: the text the Architect's validator compiled.
1083
+ stepEntity.Configuration = StepConfigurationText(stepSpec);
1065
1084
  // Handle inline prompt creation for Prompt-type steps
1066
1085
  // If StepType is Prompt and PromptID is empty, create a new AIPrompt record
1067
1086
  if (stepSpec.StepType === 'Prompt' && (!stepSpec.PromptID || stepSpec.PromptID === '')) {
@@ -1116,7 +1135,7 @@ export class AgentSpecSync {
1116
1135
  }
1117
1136
  const saved = await stepEntity.Save();
1118
1137
  if (!saved) {
1119
- throw new Error(`Failed to save step "${stepSpec.Name}" for agent ${this.spec.Name}`);
1138
+ throw new Error(`Failed to save step "${stepSpec.Name}" for agent ${this.Spec.Name}`);
1120
1139
  }
1121
1140
  // Track mutation
1122
1141
  this.trackMutation('AI Agent Steps', isUpdate ? 'Update' : 'Create', stepEntity.ID, `${isUpdate ? 'Updated' : 'Created'} step: ${stepSpec.Name}`);
@@ -1124,7 +1143,7 @@ export class AgentSpecSync {
1124
1143
  stepSpec.ID = stepEntity.ID;
1125
1144
  console.log(`✅ saveSteps: Created AIAgentStep with ID: ${stepEntity.ID}`);
1126
1145
  }
1127
- console.log(`✅ saveSteps: Successfully saved all ${this.spec.Steps.length} step(s)`);
1146
+ console.log(`✅ saveSteps: Successfully saved all ${this.Spec.Steps.length} step(s)`);
1128
1147
  }
1129
1148
  /**
1130
1149
  * Save all step paths for this agent (Flow agents only).
@@ -1137,23 +1156,23 @@ export class AgentSpecSync {
1137
1156
  * @throws {Error} If any path save fails
1138
1157
  */
1139
1158
  async saveStepPaths(agentId) {
1140
- console.log(`🔶 saveStepPaths: Called with agentId=${agentId}, Paths=${this.spec.Paths ? this.spec.Paths.length : 'undefined'}`);
1141
- if (!this.spec.Paths || this.spec.Paths.length === 0) {
1159
+ console.log(`🔶 saveStepPaths: Called with agentId=${agentId}, Paths=${this.Spec.Paths ? this.Spec.Paths.length : 'undefined'}`);
1160
+ if (!this.Spec.Paths || this.Spec.Paths.length === 0) {
1142
1161
  console.log('🔶 saveStepPaths: No paths to save, returning early');
1143
1162
  return;
1144
1163
  }
1145
- console.log(`🔶 saveStepPaths: Processing ${this.spec.Paths.length} path(s)...`);
1164
+ console.log(`🔶 saveStepPaths: Processing ${this.Spec.Paths.length} path(s)...`);
1146
1165
  const md = this.providerToUse;
1147
1166
  // Create a map of step names to IDs for easy lookup
1148
1167
  const stepNameToId = new Map();
1149
- if (this.spec.Steps) {
1150
- for (const step of this.spec.Steps) {
1168
+ if (this.Spec.Steps) {
1169
+ for (const step of this.Spec.Steps) {
1151
1170
  if (step.ID) {
1152
1171
  stepNameToId.set(step.Name, step.ID);
1153
1172
  }
1154
1173
  }
1155
1174
  }
1156
- for (const pathSpec of this.spec.Paths) {
1175
+ for (const pathSpec of this.Spec.Paths) {
1157
1176
  const pathEntity = await md.GetEntityObject('MJ: AI Agent Step Paths', this._contextUser);
1158
1177
  // Load existing if ID provided
1159
1178
  const isUpdate = !!(pathSpec.ID && pathSpec.ID !== '');
@@ -1188,7 +1207,7 @@ export class AgentSpecSync {
1188
1207
  pathSpec.ID = pathEntity.ID;
1189
1208
  console.log(`✅ saveStepPaths: Created AIAgentStepPath with ID: ${pathEntity.ID}`);
1190
1209
  }
1191
- console.log(`✅ saveStepPaths: Successfully saved all ${this.spec.Paths.length} path(s)`);
1210
+ console.log(`✅ saveStepPaths: Successfully saved all ${this.Spec.Paths.length} path(s)`);
1192
1211
  }
1193
1212
  // ===== UTILITY METHODS =====
1194
1213
  /**
@@ -1283,24 +1302,32 @@ export class AgentSpecSync {
1283
1302
  * ```
1284
1303
  */
1285
1304
  toJSON() {
1286
- return { ...this.spec };
1305
+ return { ...this.Spec };
1287
1306
  }
1288
1307
  /**
1289
1308
  * Check if this spec has unsaved changes.
1290
1309
  *
1291
1310
  * @returns True if there are unsaved changes, false otherwise
1292
1311
  */
1293
- get isDirty() {
1312
+ get IsDirty() {
1294
1313
  return this._isDirty;
1295
1314
  }
1315
+ /** @deprecated Use {@link IsDirty}. */
1316
+ get isDirty() {
1317
+ return this.IsDirty;
1318
+ }
1296
1319
  /**
1297
1320
  * Check if this spec has been loaded from the database.
1298
1321
  *
1299
1322
  * @returns True if loaded from database, false if created in memory
1300
1323
  */
1301
- get isLoaded() {
1324
+ get IsLoaded() {
1302
1325
  return this._isLoaded;
1303
1326
  }
1327
+ /** @deprecated Use {@link IsLoaded}. */
1328
+ get isLoaded() {
1329
+ return this.IsLoaded;
1330
+ }
1304
1331
  /**
1305
1332
  * Mark the spec as having changes.
1306
1333
  *
@@ -1316,9 +1343,13 @@ export class AgentSpecSync {
1316
1343
  * await spec.SaveToDatabase();
1317
1344
  * ```
1318
1345
  */
1319
- markDirty() {
1346
+ MarkDirty() {
1320
1347
  this._isDirty = true;
1321
1348
  }
1349
+ /** @deprecated Use {@link MarkDirty}. */
1350
+ markDirty() {
1351
+ return this.MarkDirty();
1352
+ }
1322
1353
  /**
1323
1354
  * Mark the spec as having been loaded from the database.
1324
1355
  *
@@ -1336,9 +1367,13 @@ export class AgentSpecSync {
1336
1367
  * await childSync.SaveToDatabase();
1337
1368
  * ```
1338
1369
  */
1339
- markLoaded() {
1370
+ MarkLoaded() {
1340
1371
  this._isLoaded = true;
1341
1372
  }
1373
+ /** @deprecated Use {@link MarkLoaded}. */
1374
+ markLoaded() {
1375
+ return this.MarkLoaded();
1376
+ }
1342
1377
  // ===== DELETE/ORPHAN METHODS =====
1343
1378
  /**
1344
1379
  * Load the current state of all agent-related records from the database.