diff --git a/AGENTS.md b/AGENTS.md
index d559c62e..e571adbd 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -113,12 +113,12 @@ Plugins can configure:
- etc.
Configuration plugins provide three kinds of values:
-- **Managed settings:** simple values such as booleans, numbers, strings, enums, lists, or sets handled through `ManagedConfiguration`. These values may be locked or used as organization defaults.
+- **Managed settings:** simple values such as booleans, numbers, strings, enums, lists, or sets handled through `ManagedConfiguration`. These values may be locked or used as organization defaults. Which configuration plugin owns a locked setting is persisted in `Data.ManagedLockedConfigurations`, and organization defaults are tracked in `Data.ManagedEditableDefaults`. Both are cleaned up generically by `ManagedConfiguration.CleanupLeftOverManagedConfigurations(...)` when the owning plugin is gone.
- **Managed configuration objects:** complex Lua tables that are persisted into `SettingsManager.ConfigurationData`, implement `IConfigurationObject`, and are cleaned up through `PluginConfigurationObject.CleanLeftOverConfigurationObjects(...)`. Examples include providers, profiles, chat templates, data sources, and document analysis policies.
- **Live plugin content:** complex Lua tables that implement `ILivePluginContent` and are read live from running plugins instead of being persisted to `ConfigurationData`. Examples include `MANDATORY_INFOS` and `INTRODUCTIONS`. If live plugin content creates persistent side data, add a dedicated cleanup path for that side data, like mandatory-info acceptances.
When adding configuration plugin capabilities:
-- For managed settings, update the corresponding data class in `app/MindWork AI Studio/Settings/DataModel/` to call `ManagedConfiguration.Register(...)`, process the setting in `PluginConfiguration.TryProcessConfiguration`, and check for leftover managed configuration in `PluginFactory.Loading.LoadAll`.
+- For managed settings, update the corresponding data class in `app/MindWork AI Studio/Settings/DataModel/` to call `ManagedConfiguration.Register(...)` and process the setting in `PluginConfiguration.TryProcessConfiguration`. Cleaning up the setting when its configuration plugin was removed needs no extra step: `ManagedConfiguration.CleanupLeftOverManagedConfigurations(...)` iterates all registered settings. Do not add per-setting cleanup calls to `PluginFactory.Loading.LoadAll`.
- For managed configuration objects, update `PluginConfigurationObject.cs` and `PluginConfigurationObjectType.cs`, persist them in the appropriate `ConfigurationData` collection, and add cleanup via `PluginConfigurationObject.CleanLeftOverConfigurationObjects(...)`.
- For live plugin content, add a data type implementing `ILivePluginContent`, parse it in `PluginConfiguration`, expose it through `PluginFactory`, and add any required cleanup only for persistent side data.
- Always document the new capability in `app/MindWork AI Studio/Plugins/configuration/plugin.lua`.
diff --git a/app/MindWork AI Studio/Settings/ManagedConfiguration.Parsing.cs b/app/MindWork AI Studio/Settings/ManagedConfiguration.Parsing.cs
index 2aea5f96..df146bbe 100644
--- a/app/MindWork AI Studio/Settings/ManagedConfiguration.Parsing.cs
+++ b/app/MindWork AI Studio/Settings/ManagedConfiguration.Parsing.cs
@@ -924,8 +924,8 @@ public static partial class ManagedConfiguration
// case only when the setting was locked and managed by the same configuration plugin.
//
// The other case, when the setting was locked and managed by a different configuration plugin,
- // is handled by the IsConfigurationLeftOver method, which checks if the configuration plugin
- // is still available. If it is not available, it resets the locked state of the
+ // is handled by the CleanupLeftOverManagedConfigurations method, which checks if the configuration
+ // plugin is still available. If it is not available, it resets the locked state of the
// configuration setting, allowing it to be reconfigured by a different plugin or left unchanged.
//
configMeta.ResetLockedConfiguration();
diff --git a/app/MindWork AI Studio/Settings/ManagedConfiguration.Register.cs b/app/MindWork AI Studio/Settings/ManagedConfiguration.Register.cs
index fad65bd0..4049cc0f 100644
--- a/app/MindWork AI Studio/Settings/ManagedConfiguration.Register.cs
+++ b/app/MindWork AI Studio/Settings/ManagedConfiguration.Register.cs
@@ -19,10 +19,7 @@ public static partial class ManagedConfiguration
/// The type of the configuration class.
/// The type of the property within the configuration class.
/// The default value.
- public static TValue Register(
- Expression>? configSelection,
- Expression> propertyExpression,
- TValue defaultValue)
+ public static TValue Register(Expression>? configSelection, Expression> propertyExpression, TValue defaultValue)
where TValue : struct
{
// When called from the JSON deserializer by using the standard constructor,
@@ -57,10 +54,7 @@ public static partial class ManagedConfiguration
/// The default value to use when the setting is not configured.
/// The type of the configuration class.
/// The default value.
- public static string Register(
- Expression>? configSelection,
- Expression> propertyExpression,
- string defaultValue)
+ public static string Register(Expression>? configSelection, Expression> propertyExpression, string defaultValue)
{
// When called from the JSON deserializer by using the standard constructor,
// we ignore the register call and return the default value:
@@ -95,10 +89,7 @@ public static partial class ManagedConfiguration
/// The type of the configuration class.
/// The type of the elements in the list within the configuration class.
/// A list containing the default value.
- public static List Register(
- Expression>? configSelection,
- Expression>> propertyExpression,
- TValue defaultValue)
+ public static List Register(Expression>? configSelection, Expression>> propertyExpression, TValue defaultValue)
{
// When called from the JSON deserializer by using the standard constructor,
// we ignore the register call and return the default value:
@@ -133,10 +124,7 @@ public static partial class ManagedConfiguration
/// The type of the configuration class.
/// The type of the elements within the property list.
/// The list of default values.
- public static List Register(
- Expression>? configSelection,
- Expression>> propertyExpression,
- IList defaultValues)
+ public static List Register(Expression>? configSelection, Expression>> propertyExpression, IList defaultValues)
{
// When called from the JSON deserializer by using the standard constructor,
// we ignore the register call and return the default value:
@@ -170,10 +158,7 @@ public static partial class ManagedConfiguration
/// The type of the configuration class.
/// The type of the values within the set.
/// A set containing the default value.
- public static HashSet Register(
- Expression>? configSelection,
- Expression>> propertyExpression,
- TValue defaultValue)
+ public static HashSet Register(Expression>? configSelection, Expression>> propertyExpression, TValue defaultValue)
{
// When called from the JSON deserializer by using the standard constructor,
// we ignore the register call and return the default value:
@@ -208,10 +193,7 @@ public static partial class ManagedConfiguration
/// The type of the configuration class from which the property is selected.
/// The type of the elements in the collection associated with the configuration property.
/// A set containing the default values.
- public static HashSet Register(
- Expression>? configSelection,
- Expression>> propertyExpression,
- IList defaultValues)
+ public static HashSet Register(Expression>? configSelection, Expression>> propertyExpression, IList defaultValues)
{
// When called from the JSON deserializer by using the standard constructor,
// we ignore the register call and return the default value:
@@ -246,10 +228,7 @@ public static partial class ManagedConfiguration
/// The type of the configuration class from which the property is selected.
/// >The type of the dictionary within the configuration class.
/// A dictionary containing the default values.
- public static TDict Register(
- Expression>? configSelection,
- Expression>> propertyExpression,
- TDict defaultValues)
+ public static TDict Register(Expression>? configSelection, Expression>> propertyExpression, TDict defaultValues)
where TDict : IDictionary, new()
{
// When called from the JSON deserializer by using the standard constructor,
@@ -286,10 +265,7 @@ public static partial class ManagedConfiguration
/// The enum type of the dictionary keys.
/// The enum type of the dictionary values.
/// A dictionary containing the default values.
- public static Dictionary Register(
- Expression>? configSelection,
- Expression>> propertyExpression,
- Dictionary defaultValues)
+ public static Dictionary Register(Expression>? configSelection, Expression>> propertyExpression, Dictionary defaultValues)
where TKey : struct, Enum
where TValue : struct, Enum
{
diff --git a/app/MindWork AI Studio/Settings/ManagedConfiguration.cs b/app/MindWork AI Studio/Settings/ManagedConfiguration.cs
index f217a793..174aa001 100644
--- a/app/MindWork AI Studio/Settings/ManagedConfiguration.cs
+++ b/app/MindWork AI Studio/Settings/ManagedConfiguration.cs
@@ -9,7 +9,10 @@ namespace AIStudio.Settings;
public static partial class ManagedConfiguration
{
private static readonly ConcurrentDictionary METADATA = new();
+
private static SettingsManager SettingsManagerAccess => Program.SERVICE_PROVIDER.GetRequiredService();
+
+ private static ILogger Log => Program.LOGGER_FACTORY.CreateLogger(nameof(ManagedConfiguration));
///
/// Attempts to retrieve the configuration metadata for a given configuration selection and
@@ -28,10 +31,7 @@ public static partial class ManagedConfiguration
/// The type of the configuration class.
/// The type of the property within the configuration class.
/// True if the configuration metadata was found, otherwise false.
- public static bool TryGet(
- Expression> configSelection,
- Expression> propertyExpression,
- out ConfigMeta configMeta)
+ public static bool TryGet(Expression> configSelection, Expression> propertyExpression, out ConfigMeta configMeta)
where TValue : Enum
{
var configPath = Path(configSelection, propertyExpression);
@@ -66,10 +66,7 @@ public static partial class ManagedConfiguration
/// if found.
/// The type of the configuration class.
/// True if the configuration metadata was found, otherwise false.
- public static bool TryGet(
- Expression> configSelection,
- Expression> propertyExpression,
- out ConfigMeta configMeta)
+ public static bool TryGet(Expression> configSelection, Expression> propertyExpression, out ConfigMeta configMeta)
{
var configPath = Path(configSelection, propertyExpression);
if (METADATA.TryGetValue(configPath, out var value) && value is ConfigMeta meta)
@@ -106,11 +103,7 @@ public static partial class ManagedConfiguration
/// True if the configuration metadata was found, otherwise false.
// ReSharper disable MethodOverloadWithOptionalParameter
- public static bool TryGet(
- Expression> configSelection,
- Expression> propertyExpression,
- out ConfigMeta configMeta,
- ISpanParsable? _ = null)
+ public static bool TryGet(Expression> configSelection, Expression> propertyExpression, out ConfigMeta configMeta, ISpanParsable? _ = null)
where TValue : struct, ISpanParsable
{
var configPath = Path(configSelection, propertyExpression);
@@ -146,10 +139,7 @@ public static partial class ManagedConfiguration
/// The type of the configuration class.
/// The type of the property within the configuration class.
/// True if the configuration metadata was found, otherwise false.
- public static bool TryGet(
- Expression> configSelection,
- Expression>> propertyExpression,
- out ConfigMeta> configMeta)
+ public static bool TryGet(Expression> configSelection, Expression>> propertyExpression, out ConfigMeta> configMeta)
{
var configPath = Path(configSelection, propertyExpression);
if (METADATA.TryGetValue(configPath, out var value) && value is ConfigMeta> meta)
@@ -182,10 +172,7 @@ public static partial class ManagedConfiguration
/// The type of the configuration class.
/// The type of the property within the configuration class.
/// True if the configuration metadata was found, otherwise false.
- public static bool TryGet(
- Expression> configSelection,
- Expression>> propertyExpression,
- out ConfigMeta> configMeta)
+ public static bool TryGet(Expression> configSelection, Expression>> propertyExpression, out ConfigMeta> configMeta)
{
var configPath = Path(configSelection, propertyExpression);
if (METADATA.TryGetValue(configPath, out var value) && value is ConfigMeta> meta)
@@ -217,10 +204,7 @@ public static partial class ManagedConfiguration
/// if found.
/// The type of the configuration class.
/// True if the configuration metadata was found, otherwise false.
- public static bool TryGet(
- Expression> configSelection,
- Expression>> propertyExpression,
- out ConfigMeta> configMeta)
+ public static bool TryGet(Expression> configSelection, Expression>> propertyExpression, out ConfigMeta> configMeta)
{
var configPath = Path(configSelection, propertyExpression);
if (METADATA.TryGetValue(configPath, out var value) && value is ConfigMeta> meta)
@@ -254,10 +238,7 @@ public static partial class ManagedConfiguration
/// The enum type of the dictionary keys.
/// The enum type of the dictionary values.
/// True if the configuration metadata was found, otherwise false.
- public static bool TryGet(
- Expression> configSelection,
- Expression>> propertyExpression,
- out ConfigMeta> configMeta)
+ public static bool TryGet(Expression> configSelection, Expression>> propertyExpression, out ConfigMeta> configMeta)
where TKey : struct, Enum
where TValue : struct, Enum
{
@@ -277,211 +258,94 @@ public static partial class ManagedConfiguration
}
///
- /// Checks if a configuration setting is left over from a configuration plugin that is no longer available.
- /// If the configuration setting is locked and managed by a configuration plugin that is not available,
- /// it resets the managed state of the configuration setting and returns true.
- /// Otherwise, it returns false.
+ /// Removes all managed states whose configuration plugin is not available anymore.
///
- /// The expression to select the configuration class.
- /// The expression to select the property within the configuration class.
+ ///
+ /// This covers every registered setting, regardless of its type: locked settings, editable
+ /// defaults, and additive plugin contributions. Settings do not need to be listed anywhere for
+ /// this cleanup to work, so adding a new managed setting cannot be forgotten here.
+ /// A locked setting whose plugin is gone is reset to its default value. That is intended: the
+ /// value belonged to the organization, not to the user, and the user might not be able to
+ /// change it at all.
+ ///
/// The collection of available plugins to check against.
- /// The type of the configuration class.
- /// The type of the property within the configuration class.
- /// True if the configuration setting is left over and was reset, otherwise false.
- public static bool IsConfigurationLeftOver(
- Expression> configSelection,
- Expression> propertyExpression,
- IReadOnlyList availablePlugins)
- where TValue : Enum
+ /// True when at least one setting was changed, otherwise false.
+ public static bool CleanupLeftOverManagedConfigurations(IReadOnlyCollection availablePlugins)
{
- if (!TryGet(configSelection, propertyExpression, out var configMeta))
- return false;
+ var wasChanged = false;
+ var registeredSettingNames = new HashSet(StringComparer.Ordinal);
- if (configMeta.LockedByConfigPluginId != Guid.Empty && configMeta.IsLocked)
+ foreach (var config in METADATA.Values)
{
- var plugin = availablePlugins.FirstOrDefault(x => x.Id == configMeta.LockedByConfigPluginId);
- if (plugin is null)
+ if (config is not ConfigMetaBase configMeta)
+ continue;
+
+ registeredSettingNames.Add(configMeta.SettingName);
+
+ //
+ // Restore the persisted ownership first. Otherwise, we would not recognize a left-over
+ // lock when nobody has read this setting since the settings were loaded:
+ //
+ configMeta.RestoreLockedConfiguration();
+
+ // Check the locked state:
+ if (configMeta.IsLocked && configMeta.LockedByConfigPluginId != Guid.Empty && !IsPluginAvailable(configMeta.LockedByConfigPluginId, availablePlugins))
{
+ Log.LogInformation($"Resetting the setting '{configMeta.SettingName}': it was locked by the configuration plugin '{configMeta.LockedByConfigPluginId}', which is not available anymore.");
configMeta.ResetLockedConfiguration();
- return true;
+ wasChanged = true;
+ }
+
+ // Check the editable default state:
+ if (CleanupEditableDefaultState(configMeta, availablePlugins))
+ wasChanged = true;
+
+ // Check the additive plugin contribution:
+ if (configMeta.HasPluginContribution && configMeta.PluginContributionByConfigPluginId != Guid.Empty && !IsPluginAvailable(configMeta.PluginContributionByConfigPluginId, availablePlugins))
+ {
+ Log.LogInformation($"Clearing the plugin contribution for the setting '{configMeta.SettingName}': the configuration plugin '{configMeta.PluginContributionByConfigPluginId}' is not available anymore.");
+ configMeta.ClearPluginContribution();
+ wasChanged = true;
}
}
- return CleanupEditableDefaultState(configMeta, SettingName(propertyExpression), availablePlugins);
+ // Remove persisted states which belong to settings that do not exist anymore:
+ if (RemoveUnknownManagedStates(registeredSettingNames))
+ wasChanged = true;
+
+ return wasChanged;
}
- public static bool IsConfigurationLeftOver(
- Expression> configSelection,
- Expression> propertyExpression,
- IReadOnlyList availablePlugins)
- {
- if (!TryGet(configSelection, propertyExpression, out var configMeta))
- return false;
-
- if (configMeta.LockedByConfigPluginId != Guid.Empty && configMeta.IsLocked)
- {
- var plugin = availablePlugins.FirstOrDefault(x => x.Id == configMeta.LockedByConfigPluginId);
- if (plugin is null)
- {
- configMeta.ResetLockedConfiguration();
- return true;
- }
- }
-
- return CleanupEditableDefaultState(configMeta, SettingName(propertyExpression), availablePlugins);
- }
-
- // ReSharper disable MethodOverloadWithOptionalParameter
- public static bool IsConfigurationLeftOver(
- Expression> configSelection,
- Expression> propertyExpression,
- IReadOnlyList availablePlugins,
- ISpanParsable? _ = null)
- where TValue : struct, ISpanParsable
- {
- if (!TryGet(configSelection, propertyExpression, out var configMeta))
- return false;
-
- if (configMeta.LockedByConfigPluginId != Guid.Empty && configMeta.IsLocked)
- {
- var plugin = availablePlugins.FirstOrDefault(x => x.Id == configMeta.LockedByConfigPluginId);
- if (plugin is null)
- {
- configMeta.ResetLockedConfiguration();
- return true;
- }
- }
-
- return CleanupEditableDefaultState(configMeta, SettingName(propertyExpression), availablePlugins);
- }
-
- // ReSharper restore MethodOverloadWithOptionalParameter
-
- public static bool IsConfigurationLeftOver(
- Expression> configSelection,
- Expression>> propertyExpression,
- IEnumerable availablePlugins)
- {
- if (!TryGet(configSelection, propertyExpression, out var configMeta))
- return false;
-
- if (configMeta.ManagedMode is ManagedConfigurationMode.EDITABLE_DEFAULT)
- return CleanupEditableDefaultState(configMeta, SettingName(propertyExpression), availablePlugins.ToList());
-
- if (configMeta.LockedByConfigPluginId == Guid.Empty || !configMeta.IsLocked)
- return false;
-
- var plugin = availablePlugins.FirstOrDefault(x => x.Id == configMeta.LockedByConfigPluginId);
- if (plugin is not null)
- return false;
-
- configMeta.ResetLockedConfiguration();
- return true;
- }
-
- public static bool IsConfigurationLeftOver(
- Expression> configSelection,
- Expression>> propertyExpression,
- IEnumerable availablePlugins)
- {
- if (!TryGet(configSelection, propertyExpression, out var configMeta))
- return false;
-
- if (configMeta.LockedByConfigPluginId == Guid.Empty || !configMeta.IsLocked)
- return false;
-
- var plugin = availablePlugins.FirstOrDefault(x => x.Id == configMeta.LockedByConfigPluginId);
- if (plugin is null)
- {
- configMeta.ResetLockedConfiguration();
- return true;
- }
-
- return false;
- }
+ private static bool IsPluginAvailable(Guid configPluginId, IReadOnlyCollection availablePlugins) => availablePlugins.Any(x => x.Id == configPluginId);
///
- /// Checks if a plugin contribution is left over from a configuration plugin that is no longer available.
- /// If so, it clears the contribution and returns true.
+ /// Removes persisted managed states which belong to settings that are not registered anymore.
///
- public static bool IsPluginContributionLeftOver(
- Expression> configSelection,
- Expression>> propertyExpression,
- IEnumerable availablePlugins)
+ ///
+ /// Without this, states of removed or renamed settings would stay in the settings file forever.
+ ///
+ private static bool RemoveUnknownManagedStates(IReadOnlySet registeredSettingNames)
{
- if (!TryGet(configSelection, propertyExpression, out var configMeta))
- return false;
+ var wasChanged = false;
+ var configurationData = SettingsManagerAccess.ConfigurationData;
- if (!configMeta.HasPluginContribution || configMeta.PluginContributionByConfigPluginId == Guid.Empty)
- return false;
-
- var plugin = availablePlugins.FirstOrDefault(x => x.Id == configMeta.PluginContributionByConfigPluginId);
- if (plugin is null)
+ foreach (var settingName in configurationData.ManagedLockedConfigurations.Keys.Where(x => !registeredSettingNames.Contains(x)).ToList())
{
- configMeta.ClearPluginContribution();
- return true;
+ Log.LogInformation($"Removing the persisted lock of the setting '{settingName}': this setting does not exist anymore.");
+ configurationData.ManagedLockedConfigurations.Remove(settingName);
+ wasChanged = true;
}
- return false;
+ foreach (var settingName in configurationData.ManagedEditableDefaults.Keys.Where(x => !registeredSettingNames.Contains(x)).ToList())
+ {
+ Log.LogInformation($"Removing the persisted editable default of the setting '{settingName}': this setting does not exist anymore.");
+ configurationData.ManagedEditableDefaults.Remove(settingName);
+ wasChanged = true;
+ }
+
+ return wasChanged;
}
- public static bool IsConfigurationLeftOver(
- Expression> configSelection,
- Expression>> propertyExpression,
- IEnumerable availablePlugins)
- {
- if (!TryGet(configSelection, propertyExpression, out var configMeta))
- return false;
-
- if (configMeta.LockedByConfigPluginId == Guid.Empty || !configMeta.IsLocked)
- return false;
-
- var plugin = availablePlugins.FirstOrDefault(x => x.Id == configMeta.LockedByConfigPluginId);
- if (plugin is null)
- {
- configMeta.ResetLockedConfiguration();
- return true;
- }
-
- return false;
- }
-
- public static bool IsConfigurationLeftOver(
- Expression> configSelection,
- Expression>> propertyExpression,
- IEnumerable availablePlugins)
- where TKey : struct, Enum
- where TValue : struct, Enum
- {
- if (!TryGet(configSelection, propertyExpression, out var configMeta))
- return false;
-
- if (configMeta.ManagedMode is ManagedConfigurationMode.EDITABLE_DEFAULT)
- {
- var plugin = availablePlugins.FirstOrDefault(x => x.Id == configMeta.EditableDefaultByConfigPluginId);
- if (plugin is null)
- {
- configMeta.ClearEditableDefaultConfiguration();
- ClearEditableDefaultState(SettingName(propertyExpression));
- return true;
- }
-
- return false;
- }
-
- if (configMeta.LockedByConfigPluginId == Guid.Empty || !configMeta.IsLocked)
- return false;
-
- var lockedPlugin = availablePlugins.FirstOrDefault(x => x.Id == configMeta.LockedByConfigPluginId);
- if (lockedPlugin is null)
- {
- configMeta.ResetLockedConfiguration();
- return true;
- }
-
- return false;
- }
-
private static string Path(Expression> configSelection, Expression> propertyExpression)
{
var className = typeof(TClass).Name;
@@ -514,12 +378,9 @@ public static partial class ManagedConfiguration
private static bool ClearEditableDefaultState(string settingName) => SettingsManagerAccess.ConfigurationData.ManagedEditableDefaults.Remove(settingName);
- private static bool CleanupEditableDefaultState(
- ConfigMeta configMeta,
- string settingName,
- IReadOnlyList availablePlugins)
+ private static bool CleanupEditableDefaultState(ConfigMetaBase configMeta, IReadOnlyCollection availablePlugins)
{
- if (!TryGetEditableDefaultState(settingName, out var editableDefaultState))
+ if (!TryGetEditableDefaultState(configMeta.SettingName, out var editableDefaultState))
{
if (configMeta.ManagedMode is not ManagedConfigurationMode.EDITABLE_DEFAULT)
return false;
@@ -528,11 +389,11 @@ public static partial class ManagedConfiguration
return true;
}
- var plugin = availablePlugins.FirstOrDefault(x => x.Id == editableDefaultState.ConfigPluginId);
- if (plugin is not null)
+ if (IsPluginAvailable(editableDefaultState.ConfigPluginId, availablePlugins))
return false;
+ Log.LogInformation($"Clearing the editable default of the setting '{configMeta.SettingName}': the configuration plugin '{editableDefaultState.ConfigPluginId}' is not available anymore.");
configMeta.ClearEditableDefaultConfiguration();
- return ClearEditableDefaultState(settingName);
+ return ClearEditableDefaultState(configMeta.SettingName);
}
}
\ No newline at end of file
diff --git a/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs b/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs
index c24ae75f..fc12ef11 100644
--- a/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs
+++ b/app/MindWork AI Studio/Tools/PluginSystem/PluginFactory.Loading.cs
@@ -199,185 +199,9 @@ public static partial class PluginFactory
if (SettingsManagerAccess.ConfigurationData.MandatoryInformation.RemoveLeftOverAcceptances(GetMandatoryInfos()))
wasConfigurationChanged = true;
- // Check for a preselected provider:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.PreselectedProvider, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for a preselected profile:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.PreselectedProfile, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for preselected chat options:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.Chat, x => x.PreselectOptions, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.Chat, x => x.PreselectedProvider, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.Chat, x => x.PreselectedProfile, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.Chat, x => x.PreselectedChatTemplate, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.Chat, x => x.PreselectedDataSourcesDisabled, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.Chat, x => x.PreselectedDataSourcesAutomaticSelection, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.Chat, x => x.PreselectedDataSourcesAutomaticValidation, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.Chat, x => x.PreselectedDataSourceIds, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.Chat, x => x.SendToChatDataSourceBehavior, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for the update interval:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.UpdateInterval, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for the update installation method:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.UpdateInstallation, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for the start page:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.StartPage, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for the built-in introduction visibility:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.ShowIntroduction, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for the quick start guide visibility:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.ShowQuickStartGuide, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for the last changelog visibility:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.ShowLastChangelog, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for the vision panel visibility:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.ShowVision, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for users allowed to added providers:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.AllowUserToAddProvider, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for the plugin import permission:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.AllowUserToImportPlugins, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for the plugin sharing permission:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.AllowUserToSharePlugins, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for admin settings visibility:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.ShowAdminSettings, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for preview visibility:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.PreviewVisibility, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for enabled preview features:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.EnabledPreviewFeatures, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsPluginContributionLeftOver(x => x.App, x => x.EnabledPreviewFeatures, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for the transcription provider:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.UseTranscriptionProvider, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for hidden assistants:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.HiddenAssistants, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for the voice recording shortcut:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.ShortcutVoiceRecording, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for the external HTTP client timeout:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.HttpClientTimeoutSeconds, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check for custom root certificates for external HTTP requests:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.ExternalHttpCustomRootCertificatesEnabled, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.ExternalHttpCustomRootCertificateBundlePath, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.App, x => x.ExternalHttpCustomRootCertificateAllowedHosts, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check provider confidence settings:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.Confidence, x => x.EnforceGlobalMinimumConfidence, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.Confidence, x => x.GlobalMinimumConfidence, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.Confidence, x => x.ShowProviderConfidence, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.Confidence, x => x.ConfidenceScheme, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.Confidence, x => x.CustomConfidenceScheme, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check data source security settings:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.DataSourceSecurity, x => x.TrustedProviderIds, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check data source selection agent settings:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.AgentDataSourceSelection, x => x.PreselectAgentOptions, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.AgentDataSourceSelection, x => x.PreselectedAgentProvider, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check retrieval context validation agent settings:
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.AgentRetrievalContextValidation, x => x.EnableRetrievalContextValidation, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.AgentRetrievalContextValidation, x => x.PreselectAgentOptions, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.AgentRetrievalContextValidation, x => x.PreselectedAgentProvider, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.AgentRetrievalContextValidation, x => x.NumParallelValidations, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check if audit is required before it can be activated
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.AssistantPluginAudit, x => x.RequireAuditBeforeActivation, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Register new preselected provider for the security audit
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.AssistantPluginAudit, x => x.PreselectedAgentProvider, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Change the minimum required audit level that is required for the allowance of assistants
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.AssistantPluginAudit, x => x.MinimumLevel, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check if external plugins are strictly forbidden, when the minimum audit level is fell below
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.AssistantPluginAudit, x => x.BlockActivationBelowMinimum, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check if security audits are invoked automatically and transparent for the user
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.AssistantPluginAudit, x => x.AutomaticallyAuditAssistants, AVAILABLE_PLUGINS))
- wasConfigurationChanged = true;
-
- // Check enterprise-managed assistant plugin approvals
- if(ManagedConfiguration.IsConfigurationLeftOver(x => x.AssistantPluginAudit, x => x.EnterpriseApprovedPlugins, AVAILABLE_PLUGINS))
+ // Check all managed settings, i.e. settings which a configuration plugin can lock,
+ // provide as an editable default, or contribute to:
+ if(ManagedConfiguration.CleanupLeftOverManagedConfigurations(AVAILABLE_PLUGINS))
wasConfigurationChanged = true;
// Compatibility shim, see documentation/compatibility-shims/2026-08-orphaned-config-locks.md (remove after 2027-08-06):