@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.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +125 -0
- package/dist/agent-spec-sync.d.ts +17 -1
- package/dist/agent-spec-sync.d.ts.map +1 -1
- package/dist/agent-spec-sync.js +128 -93
- package/dist/agent-spec-sync.js.map +1 -1
- package/dist/agents/architect-agent.d.ts +28 -0
- package/dist/agents/architect-agent.d.ts.map +1 -1
- package/dist/agents/architect-agent.js +123 -76
- package/dist/agents/architect-agent.js.map +1 -1
- package/dist/flow-step-validation.d.ts +56 -3
- package/dist/flow-step-validation.d.ts.map +1 -1
- package/dist/flow-step-validation.js +214 -0
- package/dist/flow-step-validation.js.map +1 -1
- package/dist/workflow-agent-writer.d.ts +9 -2
- package/dist/workflow-agent-writer.d.ts.map +1 -1
- package/dist/workflow-agent-writer.js +30 -6
- package/dist/workflow-agent-writer.js.map +1 -1
- package/package.json +9 -9
- package/src/__tests__/agent-spec-sync-decision-roundtrip.test.ts +218 -0
- package/src/__tests__/architect-agent-example.test.ts +218 -0
- package/src/__tests__/flow-step-validation.test.ts +328 -8
- package/src/__tests__/workflow-agent-writer.test.ts +62 -0
- package/src/agent-spec-sync.ts +136 -94
- package/src/agents/architect-agent.ts +131 -82
- package/src/flow-step-validation.ts +254 -3
- package/src/workflow-agent-writer.ts +33 -8
package/src/agent-spec-sync.ts
CHANGED
|
@@ -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
|
|
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.
|
|
196
|
+
this.Spec = this.initializeSpec(spec);
|
|
187
197
|
this._isDirty = true;
|
|
188
198
|
} else {
|
|
189
199
|
// Create minimal empty spec
|
|
190
|
-
this.
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
733
|
-
// that contains a loop and writing it back would silently
|
|
734
|
-
//
|
|
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.
|
|
809
|
+
this.ClearMutations();
|
|
790
810
|
|
|
791
811
|
if (!this._isDirty && this._isLoaded) {
|
|
792
812
|
// No changes to save
|
|
793
813
|
return {
|
|
794
|
-
agentId: this.
|
|
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.
|
|
803
|
-
console.log(`🗑️ SaveToDatabase: Loading current database state for agent ${this.
|
|
804
|
-
const dbState = await this.loadCurrentDatabaseState(this.
|
|
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.
|
|
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.
|
|
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.
|
|
864
|
+
agentId: this.Spec.ID || '',
|
|
845
865
|
success: false,
|
|
846
|
-
mutations: this.
|
|
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.
|
|
887
|
+
const isUpdate = !!this.Spec.ID;
|
|
868
888
|
|
|
869
889
|
// If ID exists, load existing record
|
|
870
|
-
if (this.
|
|
871
|
-
const loaded = await agentEntity.Load(this.
|
|
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.
|
|
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.
|
|
879
|
-
agentEntity.Description = this.
|
|
880
|
-
agentEntity.IconClass = this.
|
|
881
|
-
agentEntity.LogoURL = this.
|
|
882
|
-
agentEntity.ParentID = this.
|
|
883
|
-
agentEntity.DriverClass = this.
|
|
884
|
-
agentEntity.ModelSelectionMode = this.
|
|
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.
|
|
888
|
-
agentEntity.TypeID = (this.
|
|
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.
|
|
913
|
+
agentEntity.Status = this.Spec.Status ?? 'Active';
|
|
894
914
|
|
|
895
915
|
// Serialize JSON fields
|
|
896
916
|
agentEntity.PayloadDownstreamPaths = JSON.stringify(
|
|
897
|
-
this.
|
|
917
|
+
this.Spec.PayloadDownstreamPaths || ['*']
|
|
898
918
|
);
|
|
899
919
|
agentEntity.PayloadUpstreamPaths = JSON.stringify(
|
|
900
|
-
this.
|
|
920
|
+
this.Spec.PayloadUpstreamPaths || ['*']
|
|
901
921
|
);
|
|
902
|
-
agentEntity.PayloadSelfReadPaths = this.
|
|
903
|
-
? JSON.stringify(this.
|
|
922
|
+
agentEntity.PayloadSelfReadPaths = this.Spec.PayloadSelfReadPaths
|
|
923
|
+
? JSON.stringify(this.Spec.PayloadSelfReadPaths)
|
|
904
924
|
: null;
|
|
905
|
-
agentEntity.PayloadSelfWritePaths = this.
|
|
906
|
-
? JSON.stringify(this.
|
|
925
|
+
agentEntity.PayloadSelfWritePaths = this.Spec.PayloadSelfWritePaths
|
|
926
|
+
? JSON.stringify(this.Spec.PayloadSelfWritePaths)
|
|
907
927
|
: null;
|
|
908
|
-
agentEntity.PayloadScope = this.
|
|
928
|
+
agentEntity.PayloadScope = this.Spec.PayloadScope || null;
|
|
909
929
|
|
|
910
930
|
// Validation fields
|
|
911
|
-
agentEntity.FinalPayloadValidation = this.
|
|
912
|
-
agentEntity.FinalPayloadValidationMode = this.
|
|
913
|
-
agentEntity.FinalPayloadValidationMaxRetries = this.
|
|
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.
|
|
916
|
-
agentEntity.StartingPayloadValidationMode = this.
|
|
935
|
+
agentEntity.StartingPayloadValidation = this.Spec.StartingPayloadValidation || null;
|
|
936
|
+
agentEntity.StartingPayloadValidationMode = this.Spec.StartingPayloadValidationMode || 'Fail';
|
|
917
937
|
|
|
918
938
|
// Resource limits
|
|
919
|
-
agentEntity.MaxCostPerRun = this.
|
|
920
|
-
agentEntity.MaxTokensPerRun = this.
|
|
921
|
-
agentEntity.MaxIterationsPerRun = this.
|
|
922
|
-
agentEntity.MaxTimePerRun = this.
|
|
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.
|
|
926
|
-
agentEntity.MaxExecutionsPerRun = this.
|
|
945
|
+
agentEntity.MinExecutionsPerRun = this.Spec.MinExecutionsPerRun || null;
|
|
946
|
+
agentEntity.MaxExecutionsPerRun = this.Spec.MaxExecutionsPerRun || null;
|
|
927
947
|
|
|
928
948
|
// Other config
|
|
929
|
-
agentEntity.DefaultPromptEffortLevel = this.
|
|
930
|
-
agentEntity.ChatHandlingOption = this.
|
|
931
|
-
agentEntity.DefaultArtifactTypeID = this.
|
|
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.
|
|
956
|
+
} else if (this.Spec.OwnerUserID) {
|
|
937
957
|
// Fallback to spec value if no contextUser provided
|
|
938
|
-
agentEntity.OwnerUserID = this.
|
|
958
|
+
agentEntity.OwnerUserID = this.Spec.OwnerUserID;
|
|
939
959
|
}
|
|
940
960
|
|
|
941
|
-
agentEntity.InvocationMode = this.
|
|
961
|
+
agentEntity.InvocationMode = this.Spec.InvocationMode || 'Any';
|
|
942
962
|
|
|
943
963
|
// Requirements and design documentation
|
|
944
|
-
agentEntity.FunctionalRequirements = this.
|
|
945
|
-
agentEntity.TechnicalDesign = this.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
1069
|
+
if (!this.Spec.SubAgents || this.Spec.SubAgents.length === 0) {
|
|
1050
1070
|
return;
|
|
1051
1071
|
}
|
|
1052
1072
|
|
|
1053
|
-
for (const SubAgentSpec of this.
|
|
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.
|
|
1217
|
+
console.log(`💬 savePrompts: Called with agentId=${agentId}, Prompts=${this.Spec.Prompts ? this.Spec.Prompts.length : 'undefined'}`);
|
|
1198
1218
|
|
|
1199
|
-
if (!this.
|
|
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.
|
|
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.
|
|
1210
|
-
const promptSpec = this.
|
|
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.
|
|
1232
|
-
promptEntity.Description = `Agent prompt ${i + 1} for ${this.
|
|
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.
|
|
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.
|
|
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.
|
|
1357
|
+
console.log(`🔷 saveSteps: Called with agentId=${agentId}, Steps=${this.Spec.Steps ? this.Spec.Steps.length : 'undefined'}`);
|
|
1338
1358
|
|
|
1339
|
-
if (!this.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
1494
|
+
console.log(`🔶 saveStepPaths: Called with agentId=${agentId}, Paths=${this.Spec.Paths ? this.Spec.Paths.length : 'undefined'}`);
|
|
1473
1495
|
|
|
1474
|
-
if (!this.
|
|
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.
|
|
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.
|
|
1485
|
-
for (const step of this.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
/**
|