diff --git a/app/MindWork AI Studio/Agents/AssistantAudit/AssistantAuditAgent.cs b/app/MindWork AI Studio/Agents/AssistantAudit/AssistantAuditAgent.cs index 090c8ac8..6de7d8a8 100644 --- a/app/MindWork AI Studio/Agents/AssistantAudit/AssistantAuditAgent.cs +++ b/app/MindWork AI Studio/Agents/AssistantAudit/AssistantAuditAgent.cs @@ -1,3 +1,4 @@ +using System.Text; using System.Text.Json; using AIStudio.Chat; using AIStudio.Provider; @@ -20,9 +21,9 @@ public sealed class AssistantAuditAgent(ILogger logger, ILo protected override string JobDescription => """ You audit Lua-based newly installed or updated assistant plugins in-depth for security risks in private and enterprise environments. - The Lua code is parsed into functional assistants that help users with various tasks, like coding, e-mails, translations + The Lua code is parsed into functional assistants that help users with various tasks, like coding, emails, translations and now everything that plugin devs develop. Assistants have a system prompt that is set once and sanitized by us with a security pre- and postamble. - The user prompt is build dynamically at submit and consists of user prompt context followed by the actual user input (Text, Decisions, Time and Date, File and Web content etc.) + The user prompt is built dynamically when the assistant is submitted and consists of user prompt context followed by the actual user input (text, decisions, time and date, file and web content, etc.) You analyze the plugin manifest code, the assistants' system prompt, the simulated user prompt, and the list of UI components. The simulated user prompt may contain empty, null-like, or placeholder values. Treat these placeholders as intentional audit input and focus on prompt @@ -92,7 +93,7 @@ public sealed class AssistantAuditAgent(ILogger logger, ILo var provider = this.ResolveProvider(); if (provider == AIStudio.Settings.Provider.NONE) { - await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.SettingsSuggest, string.Format(TB("No provider is configured for Security Audit-Agent.")))); + await MessageBus.INSTANCE.SendError(new (Icons.Material.Filled.SettingsSuggest, string.Format(TB("No provider is configured for the Security Audit Agent.")))); return new AssistantAuditResult { @@ -104,6 +105,7 @@ public sealed class AssistantAuditAgent(ILogger logger, ILo logger.LogInformation($"The assistant plugin audit agent uses the provider '{provider.InstanceName}' ({provider.UsedLLMProvider.ToName()}, confidence={provider.UsedLLMProvider.GetConfidence(this.SettingsManager).Level.GetName()})."); var promptPreview = await plugin.BuildAuditPromptPreviewAsync(token); + var luaManifest = FormatLuaManifest(plugin.ReadAllLuaFiles()); var userPrompt = $$""" Audit this assistant plugin. @@ -115,7 +117,7 @@ public sealed class AssistantAuditAgent(ILogger logger, ILo Assistant system prompt: ``` - {{plugin.SystemPrompt}} + {{plugin.RawSystemPrompt}} ``` Simulated user prompt preview: @@ -130,7 +132,7 @@ public sealed class AssistantAuditAgent(ILogger logger, ILo Lua manifest: ```lua - {{plugin.ReadManifestCode()}} + {{luaManifest}} ``` """; @@ -148,7 +150,7 @@ public sealed class AssistantAuditAgent(ILogger logger, ILo if (response.Content is not ContentText content || string.IsNullOrWhiteSpace(content.Text)) { logger.LogWarning($"The assistant plugin audit agent did not return text: {response}"); - await MessageBus.INSTANCE.SendWarning(new (Icons.Material.Filled.PendingActions, string.Format(TB("The Security Audit was unsuccessful, because the LLMs response was unusable. The Audit Level remains Unknown, so please try again later.")))); + await MessageBus.INSTANCE.SendWarning(new (Icons.Material.Filled.PendingActions, string.Format(TB("The security check could not be completed because the LLM's response was unusable. The audit level remains Unknown, so please try again later.")))); return new AssistantAuditResult { @@ -210,4 +212,24 @@ public sealed class AssistantAuditAgent(ILogger logger, ILo return []; } + + private static string FormatLuaManifest(IReadOnlyDictionary luaFiles) + { + if (luaFiles.Count == 0) + return string.Empty; + + var builder = new StringBuilder(); + + foreach (var luaFile in luaFiles.OrderBy(file => file.Key, StringComparer.Ordinal)) + { + if (builder.Length > 0) + builder.AppendLine().AppendLine(); + + builder.Append("-- File: "); + builder.AppendLine(luaFile.Key); + builder.AppendLine(luaFile.Value); + } + + return builder.ToString().TrimEnd(); + } } diff --git a/app/MindWork AI Studio/Agents/AssistantAudit/AssistantAuditFinding.cs b/app/MindWork AI Studio/Agents/AssistantAudit/AssistantAuditFinding.cs index af35822b..a5071e2b 100644 --- a/app/MindWork AI Studio/Agents/AssistantAudit/AssistantAuditFinding.cs +++ b/app/MindWork AI Studio/Agents/AssistantAudit/AssistantAuditFinding.cs @@ -1,9 +1,34 @@ +using System.Text.Json.Serialization; + namespace AIStudio.Agents.AssistantAudit; public sealed class AssistantAuditFinding { + private readonly AssistantAuditLevel severity = AssistantAuditLevel.UNKNOWN; + + [JsonIgnore] + public AssistantAuditLevel Severity => this.severity; + + [JsonPropertyName("severity")] + public string SeverityText + { + get => this.Severity switch + { + AssistantAuditLevel.DANGEROUS => "critical", + AssistantAuditLevel.CAUTION => "medium", + AssistantAuditLevel.SAFE => "low", + _ => "unknown", + }; + init => this.severity = value?.Trim().ToLowerInvariant() switch + { + "critical" => AssistantAuditLevel.DANGEROUS, + "medium" => AssistantAuditLevel.CAUTION, + "low" => AssistantAuditLevel.SAFE, + _ => AssistantAuditLevel.UNKNOWN, + }; + } + public string Category { get; init; } = string.Empty; public string Location { get; init; } = string.Empty; public string Description { get; init; } = string.Empty; - public string Recommendation { get; init; } = string.Empty; } diff --git a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua index 8a8f95b5..0ce2ef38 100644 --- a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua +++ b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua @@ -46,11 +46,11 @@ LANG_NAME = "English (United States)" UI_TEXT_CONTENT = {} --- The Security Audit was unsuccessful, because the LLMs response was unusable. The Audit Level remains Unknown, so please try again later. -UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T2113359519"] = "The Security Audit was unsuccessful, because the LLMs response was unusable. The Audit Level remains Unknown, so please try again later." +-- The security check could not be completed because the LLM's response was unusable. The audit level remains Unknown, so please try again later. +UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T2451573087"] = "The security check could not be completed because the LLM's response was unusable. The audit level remains Unknown, so please try again later." --- No provider is configured for Security Audit-Agent. -UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T4000913009"] = "No provider is configured for Security Audit-Agent." +-- No provider is configured for the Security Audit Agent. +UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T3605554201"] = "No provider is configured for the Security Audit Agent." -- Needs Review UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITLEVELEXTENSIONS::T1114911302"] = "Needs Review" @@ -2935,17 +2935,74 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T474393241"] = "Please select -- Delete Workspace UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T701874671"] = "Delete Workspace" +-- Entries: {0} +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1098127509"] = "Entries: {0}" + +-- {0:0.##} GB +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1224874808"] = "{0:0.##} GB" + +-- Potentially Dangerous Plugin +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1229643769"] = "Potentially Dangerous Plugin" + +-- Plugin root +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1303883002"] = "Plugin root" + +-- Last modified +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1310524248"] = "Last modified" + +-- Count: {0} +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T131135808"] = "Count: {0}" + +-- {0:0.##} MB +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1357418474"] = "{0:0.##} MB" + +-- No security issues were found during this check. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1423034104"] = "No security issues were found during this check." + -- No provider configured UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1476185409"] = "No provider configured" +-- {0:0.##} KB +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T14914764"] = "{0:0.##} KB" + +-- Prompt: empty +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1533307170"] = "Prompt: empty" + +-- This plugin is below the required safety level. Your settings still allow activation, but enabling it requires an extra confirmation because it may be unsafe. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1539381299"] = "This plugin is below the required safety level. Your settings still allow activation, but enabling it requires an extra confirmation because it may be unsafe." + -- Components UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1550582665"] = "Components" +-- Created +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T165548891"] = "Created" + -- Lua Manifest UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T165738710"] = "Lua Manifest" --- Required minimum level -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1862086522"] = "Required minimum level" +-- Enable Assistant Plugin +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1676241565"] = "Enable Assistant Plugin" + +-- Unknown plugin +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1834795216"] = "Unknown plugin" + +-- This plugin cannot be activated because its audit result is below the required safety level and your settings block activation in this case. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1839656215"] = "This plugin cannot be activated because its audit result is below the required safety level and your settings block activation in this case." + +-- Children: {0} +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T193192210"] = "Children: {0}" + +-- null +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1996966820"] = "null" + +-- Properties +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T2177370620"] = "Properties" + +-- Items: {0} +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T2204150657"] = "Items: {0}" + +-- {0} B +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T2562655035"] = "{0} B" -- The assistant plugin could not be resolved for auditing. UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T273798258"] = "The assistant plugin could not be resolved for auditing." @@ -2953,17 +3010,50 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T273798258"] = " -- Audit provider UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T2757790517"] = "Audit provider" --- Enable Plugin -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3233590741"] = "Enable Plugin" +-- Size +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T2789707388"] = "Size" + +-- Prompt: set +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3156437951"] = "Prompt: set" + +-- Findings +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3224848879"] = "Findings" + +-- {0} | Last modified {1} +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3350447013"] = "{0} | Last modified {1}" + +-- The assistant plugin \"{0}\" was audited with the level \"{1}\", which is below the required safety level \"{2}\". Your current settings still allow activation, but this may be unsafe. Do you really want to enable this plugin? +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3418077666"] = "The assistant plugin \\\"{0}\\\" was audited with the level \\\"{1}\\\", which is below the required safety level \\\"{2}\\\". Your current settings still allow activation, but this may be unsafe. Do you really want to enable this plugin?" + +-- Unknown +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3424652889"] = "Unknown" -- Close UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3448155331"] = "Close" --- The audit uses a simulated prompt preview. Empty or placeholder values in the preview are expected during this security check. -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T439841458"] = "The audit uses a simulated prompt preview. Empty or placeholder values in the preview are expected during this security check." +-- Value +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3511155050"] = "Value" --- Run Audit -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T564725977"] = "Run Audit" +-- Last accessed +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3579946376"] = "Last accessed" + +-- Unknown key +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3647690370"] = "Unknown key" + +-- Minimum required safety level +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3652671056"] = "Minimum required safety level" + +-- Unavailable +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3662391977"] = "Unavailable" + +-- Plugin Structure +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T371537943"] = "Plugin Structure" + +-- Audit Result +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3844960449"] = "Audit Result" + +-- empty +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T413646574"] = "empty" -- Prompt Preview UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T576347259"] = "Prompt Preview" @@ -2971,6 +3061,15 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T576347259"] = " -- System Prompt UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T628396066"] = "System Prompt" +-- This security check uses a sample prompt preview. Empty or placeholder values in the preview are expected. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T737998363"] = "This security check uses a sample prompt preview. Empty or placeholder values in the preview are expected." + +-- Safe +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T760494712"] = "Safe" + +-- Start Security Check +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T811648299"] = "Start Security Check" + -- Cancel UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T900713019"] = "Cancel" @@ -5773,6 +5872,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T870640199"] = "For some data tra -- Install Pandoc UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T986578435"] = "Install Pandoc" +-- Potentially Dangerous Plugin +UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1229643769"] = "Potentially Dangerous Plugin" + -- Disable plugin UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1430375822"] = "Disable plugin" @@ -5794,6 +5896,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2057806005"] = "Enable plugin" -- Plugins UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2222816203"] = "Plugins" +-- The assistant plugin \"{0}\" was audited with the level \"{1}\", which is below the required minimum level \"{2}\". Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin? +UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2531356312"] = "The assistant plugin \\\"{0}\\\" was audited with the level \\\"{1}\\\", which is below the required minimum level \\\"{2}\\\". Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?" + -- Enabled Plugins UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2738444034"] = "Enabled Plugins" @@ -6529,6 +6634,81 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T3290596792"] = "Error during Mi -- Microsoft Word export successful UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T4256043333"] = "Microsoft Word export successful" +-- Stack +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T135058847"] = "Stack" + +-- Button group +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T1392576058"] = "Button group" + +-- Profiles Selection +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T1437749589"] = "Profiles Selection" + +-- Image +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T1494001562"] = "Image" + +-- Text Area +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T1593629311"] = "Text Area" + +-- File Content +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T165091006"] = "File Content" + +-- Grid Item +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T1991378436"] = "Grid Item" + +-- List +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T2368288673"] = "List" + +-- Provider Selection +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T268262394"] = "Provider Selection" + +-- Root +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T2703841893"] = "Root" + +-- Container +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T2990360344"] = "Container" + +-- Accordion +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3372988345"] = "Accordion" + +-- Date Range +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3400615792"] = "Date Range" + +-- Plane Text +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3557717784"] = "Plane Text" + +-- Switch +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3656636817"] = "Switch" + +-- Time +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3756319748"] = "Time" + +-- Dropdown +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3829804792"] = "Dropdown" + +-- Color +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3853794552"] = "Color" + +-- Accordion Section +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T4180733902"] = "Accordion Section" + +-- Heading +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T4231005109"] = "Heading" + +-- Unknown Element +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T434854509"] = "Unknown Element" + +-- Web Content +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T590188228"] = "Web Content" + +-- Grid +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T800286385"] = "Grid" + +-- Button +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T864557713"] = "Button" + +-- Date +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T995259257"] = "Date" + -- Failed to parse the UI render tree from the ASSISTANT lua table. UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T1318499252"] = "Failed to parse the UI render tree from the ASSISTANT lua table." diff --git a/app/MindWork AI Studio/Components/AssistantAuditTreeItem.cs b/app/MindWork AI Studio/Components/AssistantAuditTreeItem.cs new file mode 100644 index 00000000..3de0c060 --- /dev/null +++ b/app/MindWork AI Studio/Components/AssistantAuditTreeItem.cs @@ -0,0 +1,10 @@ +namespace AIStudio.Components; + +public sealed class AssistantAuditTreeItem : ITreeItem +{ + public string Text { get; init; } = string.Empty; + public string Icon { get; init; } = string.Empty; + public string Caption { get; init; } = string.Empty; + public bool Expandable { get; init; } + public bool IsComponent { get; init; } = true; +} diff --git a/app/MindWork AI Studio/Dialogs/AssistantPluginAuditDialog.razor b/app/MindWork AI Studio/Dialogs/AssistantPluginAuditDialog.razor index 7c6ce1b6..82478bf8 100644 --- a/app/MindWork AI Studio/Dialogs/AssistantPluginAuditDialog.razor +++ b/app/MindWork AI Studio/Dialogs/AssistantPluginAuditDialog.razor @@ -1,4 +1,5 @@ @using AIStudio.Agents.AssistantAudit +@using AIStudio.Components @inherits MSGComponentBase @@ -11,9 +12,9 @@ } else { - - - @T("The audit uses a simulated prompt preview. Empty or placeholder values in the preview are expected during this security check.") + + + @T("This security check uses a sample prompt preview. Empty or placeholder values in the preview are expected.") @@ -23,53 +24,214 @@ @T("Audit provider"): @this.ProviderLabel - @T("Required minimum level"): @this.MinimumLevelLabel + @T("Minimum required safety level"): @this.MinimumLevelLabel - - + + +
+ + @T("System Prompt") +
+
+ + +
- - + + +
+ + @T("Prompt Preview") +
+
+ + +
- - + + +
+ + @T("Components") +
+
+ + + + @if (item.Value is AssistantAuditTreeItem treeItem) + { + + +
+ + @treeItem.Text + + @if (!string.IsNullOrWhiteSpace(treeItem.Caption)) + { + if (treeItem.IsComponent) + { + + @treeItem.Caption + + } + else + { + + @treeItem.Caption + + } + } +
+
+
+ } +
+
+
- - + + +
+ + @T("Plugin Structure") +
+
+ + + + @if (item.Value is AssistantAuditTreeItem treeItem) + { + + +
+ + @treeItem.Text + + @if (!string.IsNullOrWhiteSpace(treeItem.Caption)) + { + + @treeItem.Caption + + } +
+
+
+ } +
+
+
+
+ + +
+ + @T("Lua Manifest") +
+
+ + + @foreach (var file in this.luaFiles) + { + var fileInfo = new FileInfo(Path.Combine(this.plugin.PluginPath, file.Key)); + + +
+ + + + + + + + @file.Key + + @T("Size"): @this.FormatFileSize(fileInfo.Length) + @T("Created"): @this.FormatFileTimestamp(fileInfo.CreationTime) + @T("Last accessed"): @this.FormatFileTimestamp(fileInfo.LastAccessTime) + @T("Last modified"): @this.FormatFileTimestamp(fileInfo.LastWriteTime) + + + + + @file.Key +
+
+ + + +
+ } +
+
@if (this.audit is not null) { - - @this.audit.Level.GetName(): @this.audit.Summary - + + @T("Audit Result") - @if (this.audit.Findings.Count > 0) - { - - @foreach (var finding in this.audit.Findings) + @if (this.audit.Findings.Count == 0 && this.audit.Level is not AssistantAuditLevel.UNKNOWN) + { + + @T("Safe"): @T("No security issues were found during this check.") + + } + else + { + + @this.audit.Level.GetName(): @this.audit.Summary + + + @if (this.IsActivationBlockedBySettings) { - -
- @finding.Category - @if (!string.IsNullOrWhiteSpace(finding.Location)) - { - (@finding.Location) - } -
@finding.Description
- @if (!string.IsNullOrWhiteSpace(finding.Recommendation)) - { -
@finding.Recommendation
- } -
-
+ + @T("This plugin cannot be activated because its audit result is below the required safety level and your settings block activation in this case.") + } -
- } + else if (this.RequiresActivationConfirmation) + { + + @T("This plugin is below the required safety level. Your settings still allow activation, but enabling it requires an extra confirmation because it may be unsafe.") + + } + + @T("Findings") + + @foreach (var finding in this.audit.Findings) + { + + + + @finding.Category + + @finding.Severity.GetName() + + + + @if (!string.IsNullOrEmpty(finding.Location)) + { + @finding.Location + } + + @finding.Description + + + } + + } +
}
} @@ -79,10 +241,13 @@ @(this.audit is null ? T("Cancel") : T("Close")) - @T("Run Audit") - - - @T("Enable Plugin") + @T("Start Security Check") + @if (this.CanEnablePlugin) + { + + @T("Enable Assistant Plugin") + + }
diff --git a/app/MindWork AI Studio/Dialogs/AssistantPluginAuditDialog.razor.cs b/app/MindWork AI Studio/Dialogs/AssistantPluginAuditDialog.razor.cs index 219f5301..2b14b186 100644 --- a/app/MindWork AI Studio/Dialogs/AssistantPluginAuditDialog.razor.cs +++ b/app/MindWork AI Studio/Dialogs/AssistantPluginAuditDialog.razor.cs @@ -1,48 +1,85 @@ using AIStudio.Agents.AssistantAudit; using AIStudio.Components; using AIStudio.Provider; +using AIStudio.Settings.DataModel; using AIStudio.Tools.PluginSystem; using AIStudio.Tools.PluginSystem.Assistants; +using AIStudio.Tools.PluginSystem.Assistants.DataModel; using Microsoft.AspNetCore.Components; +using System.Collections; +using System.Collections.Immutable; +using System.Globalization; +using System.Reflection; namespace AIStudio.Dialogs; public partial class AssistantPluginAuditDialog : MSGComponentBase { - [CascadingParameter] + private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(AssistantPluginAuditDialog).Namespace, + nameof(AssistantPluginAuditDialog)); + + [CascadingParameter] private IMudDialogInstance MudDialog { get; set; } = null!; [Inject] private AssistantAuditAgent AuditAgent { get; init; } = null!; + + [Inject] + private IDialogService DialogService { get; init; } = null!; - [Parameter] - public Guid PluginId { get; set; } + [Parameter] public Guid PluginId { get; set; } private PluginAssistants? plugin; private PluginAssistantAudit? audit; private string promptPreview = string.Empty; private string componentSummary = string.Empty; - private string luaCode = string.Empty; + private ImmutableDictionary luaFiles = ImmutableDictionary.Create(); + private IReadOnlyCollection> componentTreeItems = []; + private IReadOnlyCollection> fileSystemTreeItems = []; + private CultureInfo fileInfoCulture = CultureInfo.InvariantCulture; private bool isAuditing; private AIStudio.Settings.Provider CurrentProvider => this.SettingsManager.GetPreselectedProvider(Tools.Components.AGENT_ASSISTANT_PLUGIN_AUDIT, null, true); + private string ProviderLabel => this.CurrentProvider == AIStudio.Settings.Provider.NONE ? this.T("No provider configured") : $"{this.CurrentProvider.InstanceName} ({this.CurrentProvider.UsedLLMProvider.ToName()})"; + + private DataAssistantPluginAudit AuditSettings => this.SettingsManager.ConfigurationData.AssistantPluginAudit; + private AssistantAuditLevel MinimumLevel => this.SettingsManager.ConfigurationData.AssistantPluginAudit.MinimumLevel; + private string MinimumLevelLabel => this.MinimumLevel.GetName(); - private bool CanRunAudit => this.plugin is not null && this.CurrentProvider != AIStudio.Settings.Provider.NONE && !this.isAuditing; - private bool CanEnablePlugin => this.audit is not null && (this.audit.Level >= this.MinimumLevel || !this.SettingsManager.ConfigurationData.AssistantPluginAudit.BlockActivationBelowMinimum); - private Color EnableButtonColor => this.audit is not null && this.audit.Level >= this.MinimumLevel ? Color.Success : Color.Warning; + + private bool CanRunAudit => this.plugin is not null && this.CurrentProvider != AIStudio.Settings.Provider.NONE && + !this.isAuditing; + + private bool IsAuditBelowMinimum => this.audit is not null && this.audit.Level < this.MinimumLevel; + + private bool IsActivationBlockedBySettings => this.audit is null || this.IsAuditBelowMinimum && this.AuditSettings.BlockActivationBelowMinimum; + + private bool RequiresActivationConfirmation => this.audit is not null && this.IsAuditBelowMinimum && !this.AuditSettings.BlockActivationBelowMinimum; + + private bool CanEnablePlugin => this.audit is not null && !this.isAuditing && !this.IsActivationBlockedBySettings; + + private Color EnableButtonColor => this.RequiresActivationConfirmation ? Color.Warning : Color.Success; + + private const ushort BYTES_PER_KILOBYTE = 1024; protected override async Task OnInitializedAsync() { - this.plugin = PluginFactory.RunningPlugins.OfType().FirstOrDefault(x => x.Id == this.PluginId); + var activeLanguagePlugin = await this.SettingsManager.GetActiveLanguagePlugin(); + this.fileInfoCulture = this.CreateFileInfoCulture(activeLanguagePlugin.IETFTag); + + this.plugin = PluginFactory.RunningPlugins.OfType() + .FirstOrDefault(x => x.Id == this.PluginId); if (this.plugin is not null) { this.promptPreview = await this.plugin.BuildAuditPromptPreviewAsync(); this.componentSummary = this.plugin.CreateAuditComponentSummary(); - this.luaCode = this.plugin.ReadManifestCode(); + this.componentTreeItems = this.CreateAuditTreeItems(this.plugin.RootComponent); + this.fileSystemTreeItems = this.CreatePluginFileSystemTreeItems(this.plugin.PluginPath); + this.luaFiles = this.plugin.ReadAllLuaFiles(); } await base.OnInitializedAsync(); @@ -65,7 +102,9 @@ public partial class AssistantPluginAuditDialog : MSGComponentBase PluginHash = this.plugin.ComputeAuditHash(), AuditedAtUtc = DateTimeOffset.UtcNow, AuditProviderId = this.CurrentProvider.Id, - AuditProviderName = this.CurrentProvider == AIStudio.Settings.Provider.NONE ? string.Empty : this.CurrentProvider.InstanceName, + AuditProviderName = this.CurrentProvider == AIStudio.Settings.Provider.NONE + ? string.Empty + : this.CurrentProvider.InstanceName, Level = AssistantAuditLevelExtensions.Parse(result.Level), Summary = result.Summary, Confidence = result.Confidence, @@ -91,11 +130,408 @@ public partial class AssistantPluginAuditDialog : MSGComponentBase this.MudDialog.Close(DialogResult.Ok(new AssistantPluginAuditDialogResult(this.audit, false))); } - private void EnablePlugin() + private async Task EnablePlugin() { if (this.audit is null) return; + if (this.IsActivationBlockedBySettings) + return; + + if (this.RequiresActivationConfirmation && !await this.ConfirmActivationBelowMinimumAsync()) + return; + this.MudDialog.Close(DialogResult.Ok(new AssistantPluginAuditDialogResult(this.audit, true))); } + + private async Task ConfirmActivationBelowMinimumAsync() + { + var dialogParameters = new DialogParameters + { + { + x => x.Message, + string.Format( + T("The assistant plugin \"{0}\" was audited with the level \"{1}\", which is below the required safety level \"{2}\". Your current settings still allow activation, but this may be unsafe. Do you really want to enable this plugin?"), + this.plugin?.Name ?? T("Unknown plugin"), + this.audit?.Level.GetName() ?? T("Unknown"), + this.MinimumLevelLabel) + }, + }; + + var dialogReference = await this.DialogService.ShowAsync(T("Potentially Dangerous Plugin"), dialogParameters, DialogOptions.FULLSCREEN); + var dialogResult = await dialogReference.Result; + return dialogResult is not null && !dialogResult.Canceled; + } + + private Severity GetAuditResultSeverity() => this.audit?.Level switch + { + AssistantAuditLevel.DANGEROUS => Severity.Error, + AssistantAuditLevel.CAUTION => Severity.Warning, + AssistantAuditLevel.SAFE => Severity.Success, + _ => Severity.Normal, + }; + + private Severity GetFindingSeverity(AssistantAuditLevel severity) => severity switch + { + AssistantAuditLevel.DANGEROUS => Severity.Error, + AssistantAuditLevel.CAUTION => Severity.Warning, + AssistantAuditLevel.SAFE => Severity.Success, + _ => Severity.Normal, + }; + + private string GetFindingIcon(AssistantAuditLevel severity) => severity switch + { + AssistantAuditLevel.DANGEROUS => Icons.Material.Filled.Dangerous, + AssistantAuditLevel.CAUTION => Icons.Material.Filled.WarningAmber, + AssistantAuditLevel.SAFE => Icons.Material.Filled.Verified, + _ => Icons.Material.Filled.Info, + }; + + private Color GetFindingColor(AssistantAuditLevel severity) => severity switch + { + AssistantAuditLevel.DANGEROUS => Color.Error, + AssistantAuditLevel.CAUTION => Color.Warning, + AssistantAuditLevel.SAFE => Color.Success, + _ => Color.Default, + }; + + /// + /// Creates the full audit tree for the assistant component hierarchy. + /// The dialog owns this mapping because it is pure presentation logic for the audit UI. + /// + private IReadOnlyCollection> CreateAuditTreeItems(IAssistantComponent? rootComponent) + { + if (rootComponent is null) + return []; + + return [this.CreateComponentTreeItem(rootComponent, index: 0, depth: 0)]; + } + + /// + /// Maps one assistant component into a tree node and recursively appends its value, props and child components. + /// + private TreeItemData CreateComponentTreeItem(IAssistantComponent component, int index, int depth) + { + var children = new List>(); + + if (component.Props.TryGetValue("Value", out var value)) + children.Add(this.CreateValueTreeItem(TB("Value"), value, depth + 1)); + + if (component.Props.Count > 0) + children.Add(this.CreatePropsTreeItem(component.Props, depth + 1)); + + children.AddRange(component.Children.Select((child, childIndex) => + this.CreateComponentTreeItem(child, childIndex, depth + 1))); + + return new TreeItemData + { + Expanded = depth < 2, + Expandable = children.Count > 0, + Value = new AssistantAuditTreeItem + { + Text = this.GetComponentTreeItemText(component), + Caption = this.GetComponentTreeItemCaption(component, index), + Icon = component.Type.GetIcon(), + Expandable = children.Count > 0, + }, + Children = children, + }; + } + + /// + /// Groups all props of a component under a single "Props" branch to keep the component nodes compact. + /// + private TreeItemData CreatePropsTreeItem(IReadOnlyDictionary props, int depth) + { + var children = props + .OrderBy(prop => prop.Key, StringComparer.Ordinal) + .Select(prop => this.CreateValueTreeItem(prop.Key, prop.Value, depth + 1)) + .ToList(); + + return new TreeItemData + { + Expanded = depth < 2, + Expandable = children.Count > 0, + Value = new AssistantAuditTreeItem + { + Text = TB("Properties"), + Caption = string.Format(TB("Count: {0}"), props.Count), + Icon = Icons.Material.Filled.Code, + Expandable = children.Count > 0, + IsComponent = false, + }, + Children = children, + }; + } + + /// + /// Converts a scalar or structured prop value into a tree node. + /// Scalars stay on one line, while structured values recursively expose their children. + /// + private TreeItemData CreateValueTreeItem(string label, object? value, int depth) + { + var children = this.CreateValueChildren(value, depth + 1); + return new TreeItemData + { + Expanded = depth < 2, + Expandable = children.Count > 0, + Value = new AssistantAuditTreeItem + { + Text = label, + Caption = children.Count == 0 ? this.FormatScalarValue(value) : this.GetStructuredValueCaption(value), + Icon = this.GetValueIcon(value), + Expandable = children.Count > 0, + IsComponent = false, + }, + Children = children, + }; + } + + /// + /// Recursively expands structured values for the tree. + /// Lists, dictionaries and known DTO-style assistant values become nested tree branches. + /// + private List> CreateValueChildren(object? value, int depth) + { + if (value is null || IsScalarValue(value)) + return []; + + if (value is IDictionary dictionary) + return this.CreateDictionaryChildren(dictionary, depth); + + if (value is IEnumerable enumerable && value is not string) + return this.CreateEnumerableChildren(enumerable, depth); + + return this.CreateObjectChildren(value, depth); + } + + private List> CreateDictionaryChildren(IDictionary dictionary, int depth) + { + var children = new List>(); + foreach (DictionaryEntry entry in dictionary) + { + var keyText = entry.Key.ToString() ?? TB("Unknown key"); + children.Add(this.CreateValueTreeItem(keyText, entry.Value, depth)); + } + + return children; + } + + /// + /// Creates a tree for the plugin directory so the audit can show unexpected folders and files, while excluding irrelevant dependency folders. + /// + private IReadOnlyCollection> CreatePluginFileSystemTreeItems(string pluginPath) + { + if (string.IsNullOrWhiteSpace(pluginPath) || !Directory.Exists(pluginPath)) + return []; + + return [this.CreateDirectoryTreeItem(pluginPath, pluginPath, depth: 0)]; + } + + private TreeItemData CreateDirectoryTreeItem(string directoryPath, string rootPath, int depth) + { + var childDirectories = Directory.EnumerateDirectories(directoryPath) + .OrderBy(path => path, StringComparer.Ordinal) + .Select(path => this.CreateDirectoryTreeItem(path, rootPath, depth + 1)) + .ToList(); + + var childFiles = Directory.EnumerateFiles(directoryPath) + .OrderBy(path => path, StringComparer.Ordinal) + .Select(path => this.CreateFileTreeItem(path, depth + 1)) + .ToList(); + + var children = new List>(childDirectories.Count + childFiles.Count); + children.AddRange(childDirectories); + children.AddRange(childFiles); + + var relativePath = Path.GetRelativePath(rootPath, directoryPath); + var displayName = depth == 0 + ? Path.GetFileName(directoryPath) + : relativePath.Split(Path.DirectorySeparatorChar).Last(); + + return new TreeItemData + { + Expanded = depth < 2, + Expandable = children.Count > 0, + Value = new AssistantAuditTreeItem + { + Text = string.IsNullOrWhiteSpace(displayName) ? directoryPath : displayName, + Caption = depth == 0 ? TB("Plugin root") : string.Format(TB("Items: {0}"), children.Count), + Icon = children.Count > 0 ? Icons.Material.Filled.FolderCopy : Icons.Material.Filled.Folder, + Expandable = children.Count > 0, + IsComponent = false, + }, + Children = children, + }; + } + + private TreeItemData CreateFileTreeItem(string filePath, int depth) + { + var fileInfo = new FileInfo(filePath); + + return new TreeItemData + { + Expanded = depth < 2, + Expandable = false, + Value = new AssistantAuditTreeItem + { + Text = Path.GetFileName(filePath), + Caption = string.Format(TB("{0} | Last modified {1}"), this.FormatFileSize(fileInfo.Length), + this.FormatFileTimestamp(fileInfo.LastWriteTime)), + Icon = this.GetFileIcon(filePath), + Expandable = false, + IsComponent = false, + }, + }; + } + + private string GetFileIcon(string filePath) + { + var extension = Path.GetExtension(filePath); + return extension.ToLowerInvariant() switch + { + ".lua" => Icons.Material.Filled.Code, + ".md" => Icons.Material.Filled.Article, + ".json" => Icons.Material.Filled.DataObject, + ".png" or ".jpg" or ".jpeg" or ".svg" or ".webp" => Icons.Material.Filled.Image, + _ => Icons.Material.Filled.InsertDriveFile, + }; + } + + private List> CreateEnumerableChildren(IEnumerable enumerable, int depth) + { + var children = new List>(); + var index = 0; + + foreach (var item in enumerable) + { + children.Add(this.CreateValueTreeItem($"[{index}]", item, depth)); + index++; + } + + return children; + } + + /// + /// Falls back to public instance properties for simple DTO-style values such as dropdown items. + /// Getter failures are treated defensively so the audit dialog never crashes because of a problematic property. + /// + private List> CreateObjectChildren(object value, int depth) + { + var children = new List>(); + + foreach (var property in value.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)) + { + if (!property.CanRead || property.GetIndexParameters().Length != 0) + continue; + + object? propertyValue; + try + { + propertyValue = property.GetValue(value); + } + catch (Exception) + { + propertyValue = TB("Unavailable"); + } + + children.Add(this.CreateValueTreeItem(property.Name, propertyValue, depth)); + } + + return children; + } + + private string GetComponentTreeItemText(IAssistantComponent component) + { + var type = component.Type.GetDisplayName(); + if (component is INamedAssistantComponent named && !string.IsNullOrWhiteSpace(named.Name)) + return $"{type}: {named.Name}"; + + return type; + } + + private string GetComponentTreeItemCaption(IAssistantComponent component, int index) + { + var details = new List { $"#{index + 1}" }; + + if (component is IStatefulAssistantComponent stateful) + details.Add(string.IsNullOrWhiteSpace(stateful.UserPrompt) ? TB("Prompt: empty") : TB("Prompt: set")); + + if (component.Children.Count > 0) + details.Add(string.Format(TB("Children: {0}"), component.Children.Count)); + + return string.Join(" | ", details); + } + + private static bool IsScalarValue(object value) + { + return value is string or bool or char or Enum + or byte or sbyte or short or ushort or int or uint or long or ulong + or float or double or decimal + or DateTime or DateTimeOffset or TimeSpan or Guid; + } + + private string FormatScalarValue(object? value) => value switch + { + null => TB("null"), + string stringValue when string.IsNullOrWhiteSpace(stringValue) => TB("empty"), + string stringValue => stringValue, + bool boolValue => boolValue ? "true" : "false", + _ => Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture) ?? string.Empty, + }; + + private string GetStructuredValueCaption(object? value) => value switch + { + null => TB("null"), + IDictionary dictionary => string.Format(TB("Entries: {0}"), dictionary.Count), + IEnumerable enumerable when value is not string => string.Format(TB("Items: {0}"), + enumerable.Cast().Count()), + _ => value.GetType().Name, + }; + + private string GetValueIcon(object? value) => value switch + { + null => Icons.Material.Filled.Block, + bool => Icons.Material.Outlined.ToggleOn, + string => Icons.Material.Outlined.Abc, + int => Icons.Material.Filled.Numbers, + Enum => Icons.Material.Filled.Label, + IDictionary => Icons.Material.Filled.DataObject, + IEnumerable when value is not string => Icons.Material.Filled.FormatListBulleted, + _ => Icons.Material.Filled.DataArray, + }; + + private string FormatFileTimestamp(DateTime timestamp) => timestamp.ToString("g", this.fileInfoCulture); + + private string FormatFileSize(long bytes) + { + if (bytes < BYTES_PER_KILOBYTE) + return string.Format(this.fileInfoCulture, TB("{0} B"), bytes); + + var kilobyte = bytes / (double)BYTES_PER_KILOBYTE; + if (kilobyte < BYTES_PER_KILOBYTE) + return string.Format(this.fileInfoCulture, TB("{0:0.##} KB"), kilobyte); + + var megabyte = kilobyte / BYTES_PER_KILOBYTE; + if (megabyte < BYTES_PER_KILOBYTE) + return string.Format(this.fileInfoCulture, TB("{0:0.##} MB"), megabyte); + + var gigabyte = megabyte / BYTES_PER_KILOBYTE; + return string.Format(this.fileInfoCulture, TB("{0:0.##} GB"), gigabyte); + } + + private CultureInfo CreateFileInfoCulture(string ietfTag) + { + if (string.IsNullOrWhiteSpace(ietfTag)) + return CultureInfo.InvariantCulture; + + try + { + return CultureInfo.GetCultureInfo(ietfTag); + } + catch (CultureNotFoundException) + { + return CultureInfo.InvariantCulture; + } + } } diff --git a/app/MindWork AI Studio/Pages/Plugins.razor.cs b/app/MindWork AI Studio/Pages/Plugins.razor.cs index e5dded37..b0e9b9d7 100644 --- a/app/MindWork AI Studio/Pages/Plugins.razor.cs +++ b/app/MindWork AI Studio/Pages/Plugins.razor.cs @@ -1,6 +1,7 @@ using AIStudio.Components; using AIStudio.Agents.AssistantAudit; using AIStudio.Dialogs; +using AIStudio.Settings.DataModel; using AIStudio.Tools.PluginSystem.Assistants; using AIStudio.Tools.PluginSystem; @@ -14,6 +15,8 @@ public partial class Plugins : MSGComponentBase private const string GROUP_ENABLED = "Enabled"; private const string GROUP_DISABLED = "Disabled"; private const string GROUP_INTERNAL = "Internal"; + + private DataAssistantPluginAudit AssistantPluginAuditSettings => this.SettingsManager.ConfigurationData.AssistantPluginAudit; private TableGroupDefinition groupConfig = null!; @@ -56,7 +59,7 @@ public partial class Plugins : MSGComponentBase return; } - if (pluginMeta.Type is not PluginType.ASSISTANT || !this.SettingsManager.ConfigurationData.AssistantPluginAudit.RequireAuditBeforeActivation) + if (pluginMeta.Type is not PluginType.ASSISTANT || !this.AssistantPluginAuditSettings.RequireAuditBeforeActivation) { this.SettingsManager.ConfigurationData.EnabledPlugins.Add(pluginMeta.Id); await this.SettingsManager.StoreSettings(); @@ -72,12 +75,18 @@ public partial class Plugins : MSGComponentBase var cachedAudit = this.SettingsManager.ConfigurationData.AssistantPluginAudits.FirstOrDefault(x => x.PluginId == pluginMeta.Id); if (cachedAudit is not null && cachedAudit.PluginHash == pluginHash) { - if (cachedAudit.Level < this.SettingsManager.ConfigurationData.AssistantPluginAudit.MinimumLevel && this.SettingsManager.ConfigurationData.AssistantPluginAudit.BlockActivationBelowMinimum) + if (cachedAudit.Level < this.AssistantPluginAuditSettings.MinimumLevel && this.AssistantPluginAuditSettings.BlockActivationBelowMinimum) { await this.DialogService.ShowMessageBox(this.T("Assistant Audit"), $"{cachedAudit.Level.GetName()}: {cachedAudit.Summary}", this.T("Close")); return; } + if (cachedAudit.Level < this.AssistantPluginAuditSettings.MinimumLevel && + !await this.ConfirmActivationBelowMinimumAsync(pluginMeta.Name, cachedAudit.Level)) + { + return; + } + this.SettingsManager.ConfigurationData.EnabledPlugins.Add(pluginMeta.Id); await this.SettingsManager.StoreSettings(); await this.MessageBus.SendMessage(this, Event.CONFIGURATION_CHANGED); @@ -102,6 +111,26 @@ public partial class Plugins : MSGComponentBase await this.SettingsManager.StoreSettings(); await this.MessageBus.SendMessage(this, Event.CONFIGURATION_CHANGED); } + + private async Task ConfirmActivationBelowMinimumAsync(string pluginName, AssistantAuditLevel actualLevel) + { + var dialogParameters = new DialogParameters + { + { + x => x.Message, + string.Format( + this.T("The assistant plugin \"{0}\" was audited with the level \"{1}\", which is below the required minimum level \"{2}\". Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?"), + pluginName, + actualLevel.GetName(), + this.AssistantPluginAuditSettings.MinimumLevel.GetName()) + }, + }; + + var dialogReference = await this.DialogService.ShowAsync(this.T("Potentially Dangerous Plugin"), dialogParameters, + DialogOptions.FULLSCREEN); + var dialogResult = await dialogReference.Result; + return dialogResult is not null && !dialogResult.Canceled; + } private static bool IsSendingMail(string sourceUrl) => sourceUrl.TrimStart().StartsWith("mailto:", StringComparison.OrdinalIgnoreCase); diff --git a/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua b/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua index 8c1cb680..f4d2b804 100644 --- a/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua +++ b/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua @@ -48,11 +48,11 @@ LANG_NAME = "Deutsch (Deutschland)" UI_TEXT_CONTENT = {} --- The Security Audit was unsuccessful, because the LLMs response was unusable. The Audit Level remains Unknown, so please try again later. -UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T2113359519"] = "Das Sicherheits-Audit war nicht erfolgreich, da die Antwort des LLM unbrauchbar war. Das Audit Level bleibt 'Unbekannt'. Bitte versuchen Sie es später erneut." +-- The security check could not be completed because the LLM's response was unusable. The audit level remains Unknown, so please try again later. +UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T2451573087"] = "Die Sicherheitsprüfung konnte nicht abgeschlossen werden, da die Antwort des LLM unbrauchbar war. Die Audit-Stufe bleibt „Unbekannt“, bitte versuchen Sie es später erneut." --- No provider is configured for Security Audit-Agent. -UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T4000913009"] = "Für den Security Audit-Agenten ist kein Provider konfiguriert." +-- No provider is configured for the Security Audit Agent. +UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T3605554201"] = "Für den Sicherheitsprüfungs-Agenten ist kein Anbieter konfiguriert." -- Needs Review UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITLEVELEXTENSIONS::T1114911302"] = "Audit Erforderlich" @@ -2937,17 +2937,74 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T474393241"] = "Bitte wählen -- Delete Workspace UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T701874671"] = "Arbeitsbereich löschen" +-- Entries: {0} +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1098127509"] = "Einträge: {0}" + +-- {0:0.##} GB +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1224874808"] = "{0:0.##} GB" + +-- Potentially Dangerous Plugin +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1229643769"] = "Potenziell gefährliches Plugin" + +-- Plugin root +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1303883002"] = "Stammverzeichnis des Plugins" + +-- Last modified +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1310524248"] = "Zuletzt geändert" + +-- Count: {0} +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T131135808"] = "Anzahl: {0}" + +-- {0:0.##} MB +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1357418474"] = "{0:0.##} MB" + +-- No security issues were found during this check. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1423034104"] = "Bei dieser Überprüfung wurden keine Sicherheitsprobleme gefunden." + -- No provider configured UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1476185409"] = "Kein Provider konfiguriert" +-- {0:0.##} KB +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T14914764"] = "{0:0.##} KB" + +-- Prompt: empty +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1533307170"] = "Prompt: leer" + +-- This plugin is below the required safety level. Your settings still allow activation, but enabling it requires an extra confirmation because it may be unsafe. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1539381299"] = "Dieses Plugin unterschreitet das erforderliche Sicherheitsniveau. Ihre Einstellungen erlauben die Aktivierung zwar weiterhin, aber das Einschalten erfordert eine zusätzliche Bestätigung, da es möglicherweise unsicher ist." + -- Components UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1550582665"] = "Komponenten" +-- Created +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T165548891"] = "Erstellt" + -- Lua Manifest UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T165738710"] = "Lua-Manifest" --- Required minimum level -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1862086522"] = "Erforderliches Mindest-Audit-Level" +-- Enable Assistant Plugin +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1676241565"] = "Assistant-Plugin aktivieren" + +-- Unknown plugin +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1834795216"] = "Unbekanntes Plugin" + +-- This plugin cannot be activated because its audit result is below the required safety level and your settings block activation in this case. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1839656215"] = "Dieses Plugin kann nicht aktiviert werden, weil sein Prüfergebnis unter dem erforderlichen Sicherheitsniveau liegt und Ihre Einstellungen die Aktivierung in diesem Fall blockieren." + +-- Children: {0} +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T193192210"] = "Untergeordnete: {0}" + +-- null +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1996966820"] = "null" + +-- Properties +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T2177370620"] = "Eigenschaften" + +-- Items: {0} +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T2204150657"] = "Elemente: {0}" + +-- {0} B +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T2562655035"] = "{0} B" -- The assistant plugin could not be resolved for auditing. UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T273798258"] = "Das Assistenten-Plugin konnte für die Überprüfung nicht aufgelöst werden." @@ -2955,17 +3012,50 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T273798258"] = " -- Audit provider UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T2757790517"] = "Provider prüfen" --- Enable Plugin -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3233590741"] = "Plugin aktivieren" +-- Size +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T2789707388"] = "Größe" + +-- Prompt: set +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3156437951"] = "Prompt: festlegen" + +-- Findings +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3224848879"] = "Ergebnisse" + +-- {0} | Last modified {1} +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3350447013"] = "{0} | Zuletzt geändert {1}" + +-- The assistant plugin \"{0}\" was audited with the level \"{1}\", which is below the required safety level \"{2}\". Your current settings still allow activation, but this may be unsafe. Do you really want to enable this plugin? +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3418077666"] = "Das Assistenten-Plugin „{0}“ wurde mit der Stufe „{1}“ geprüft, die unter der erforderlichen Sicherheitsstufe „{2}“ liegt. Ihre aktuellen Einstellungen erlauben die Aktivierung dennoch, aber dies kann unsicher sein. Möchten Sie dieses Plugin wirklich aktivieren?" + +-- Unknown +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3424652889"] = "Unbekannt" -- Close UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3448155331"] = "Schließen" --- The audit uses a simulated prompt preview. Empty or placeholder values in the preview are expected during this security check. -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T439841458"] = "Das Audit verwendet eine simulierte Prompt-Vorschau. Leere oder Platzhalterwerte in der Vorschau sind während dieser Sicherheitsprüfung zu erwarten." +-- Value +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3511155050"] = "Wert" --- Run Audit -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T564725977"] = "Prüfung ausführen" +-- Last accessed +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3579946376"] = "Zuletzt aufgerufen" + +-- Unknown key +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3647690370"] = "Unbekannter Schlüssel" + +-- Minimum required safety level +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3652671056"] = "Mindest erforderliches Sicherheitsniveau" + +-- Unavailable +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3662391977"] = "Nicht verfügbar" + +-- Plugin Structure +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T371537943"] = "Plugin-Struktur" + +-- Audit Result +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3844960449"] = "Prüfungsergebnis" + +-- empty +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T413646574"] = "leer" -- Prompt Preview UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T576347259"] = "Prompt-Vorschau" @@ -2973,6 +3063,15 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T576347259"] = " -- System Prompt UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T628396066"] = "System-Prompt" +-- This security check uses a sample prompt preview. Empty or placeholder values in the preview are expected. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T737998363"] = "Diese Sicherheitsprüfung verwendet eine Beispielvorschau des Prompts. Leere oder Platzhalterwerte in der Vorschau sind zu erwarten." + +-- Safe +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T760494712"] = "Sicher" + +-- Start Security Check +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T811648299"] = "Sicherheitsprüfung starten" + -- Cancel UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T900713019"] = "Abbrechen" @@ -5775,6 +5874,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T870640199"] = "Für einige Daten -- Install Pandoc UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T986578435"] = "Pandoc installieren" +-- Potentially Dangerous Plugin +UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1229643769"] = "Potenziell gefährliches Plugin" + -- Disable plugin UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1430375822"] = "Plugin deaktivieren" @@ -5796,6 +5898,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2057806005"] = "Plugin aktivieren" -- Plugins UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2222816203"] = "Plugins" +-- The assistant plugin \"{0}\" was audited with the level \"{1}\", which is below the required minimum level \"{2}\". Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin? +UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2531356312"] = "Das Assistenten-Plugin „{0}“ wurde mit der Stufe „{1}“ geprüft, die unter der erforderlichen Mindeststufe „{2}“ liegt. Ihre aktuellen Einstellungen erlauben die Aktivierung trotzdem, aber das kann potenziell gefährlich sein. Möchten Sie dieses Plugin wirklich aktivieren?" + -- Enabled Plugins UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2738444034"] = "Aktivierte Plugins" @@ -6531,6 +6636,81 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T3290596792"] = "Fehler beim Exp -- Microsoft Word export successful UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T4256043333"] = "Export nach Microsoft Word erfolgreich" +-- Stack +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T135058847"] = "Stapel" + +-- Button group +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T1392576058"] = "Schaltflächengruppe" + +-- Profiles Selection +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T1437749589"] = "Profilauswahl" + +-- Image +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T1494001562"] = "Bild" + +-- Text Area +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T1593629311"] = "Textfeld" + +-- File Content +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T165091006"] = "Dateiinhalt" + +-- Grid Item +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T1991378436"] = "Rasterelement" + +-- List +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T2368288673"] = "Liste" + +-- Provider Selection +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T268262394"] = "Anbieterauswahl" + +-- Root +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T2703841893"] = "Wurzel" + +-- Container +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T2990360344"] = "Container" + +-- Accordion +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3372988345"] = "Akkordeon" + +-- Date Range +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3400615792"] = "Datumsbereich" + +-- Plane Text +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3557717784"] = "Unformatierter Text" + +-- Switch +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3656636817"] = "Umschalten" + +-- Time +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3756319748"] = "Zeit" + +-- Dropdown +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3829804792"] = "Dropdown" + +-- Color +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3853794552"] = "Farbe" + +-- Accordion Section +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T4180733902"] = "Akkordeon-Abschnitt" + +-- Heading +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T4231005109"] = "Überschrift" + +-- Unknown Element +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T434854509"] = "Unbekanntes Element" + +-- Web Content +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T590188228"] = "Web-Inhalte" + +-- Grid +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T800286385"] = "Raster" + +-- Button +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T864557713"] = "Schaltfläche" + +-- Date +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T995259257"] = "Datum" + -- Failed to parse the UI render tree from the ASSISTANT lua table. UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T1318499252"] = "Der UI-Render-Baum konnte nicht aus der ASSISTANT-Lua-Tabelle geparst werden." diff --git a/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua b/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua index e0afc6a1..71423cd9 100644 --- a/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua +++ b/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua @@ -48,11 +48,11 @@ LANG_NAME = "English (United States)" UI_TEXT_CONTENT = {} --- The Security Audit was unsuccessful, because the LLMs response was unusable. The Audit Level remains Unknown, so please try again later. -UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T2113359519"] = "The Security Audit was unsuccessful, because the LLMs response was unusable. The Audit Level remains Unknown, so please try again later." +-- The security check could not be completed because the LLM's response was unusable. The audit level remains Unknown, so please try again later. +UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T2451573087"] = "The security check could not be completed because the LLM's response was unusable. The audit level remains Unknown, so please try again later." --- No provider is configured for Security Audit-Agent. -UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T4000913009"] = "No provider is configured for Security Audit-Agent." +-- No provider is configured for the Security Audit Agent. +UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITAGENT::T3605554201"] = "No provider is configured for the Security Audit Agent." -- Needs Review UI_TEXT_CONTENT["AISTUDIO::AGENTS::ASSISTANTAUDIT::ASSISTANTAUDITLEVELEXTENSIONS::T1114911302"] = "Needs Review" @@ -2937,17 +2937,74 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T474393241"] = "Please select -- Delete Workspace UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::WORKSPACES::T701874671"] = "Delete Workspace" +-- Entries: {0} +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1098127509"] = "Entries: {0}" + +-- {0:0.##} GB +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1224874808"] = "{0:0.##} GB" + +-- Potentially Dangerous Plugin +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1229643769"] = "Potentially Dangerous Plugin" + +-- Plugin root +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1303883002"] = "Plugin root" + +-- Last modified +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1310524248"] = "Last modified" + +-- Count: {0} +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T131135808"] = "Count: {0}" + +-- {0:0.##} MB +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1357418474"] = "{0:0.##} MB" + +-- No security issues were found during this check. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1423034104"] = "No security issues were found during this check." + -- No provider configured UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1476185409"] = "No provider configured" +-- {0:0.##} KB +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T14914764"] = "{0:0.##} KB" + +-- Prompt: empty +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1533307170"] = "Prompt: empty" + +-- This plugin is below the required safety level. Your settings still allow activation, but enabling it requires an extra confirmation because it may be unsafe. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1539381299"] = "This plugin is below the required safety level. Your settings still allow activation, but enabling it requires an extra confirmation because it may be unsafe." + -- Components UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1550582665"] = "Components" +-- Created +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T165548891"] = "Created" + -- Lua Manifest UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T165738710"] = "Lua Manifest" --- Required minimum level -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1862086522"] = "Required minimum level" +-- Enable Assistant Plugin +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1676241565"] = "Enable Assistant Plugin" + +-- Unknown plugin +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1834795216"] = "Unknown plugin" + +-- This plugin cannot be activated because its audit result is below the required safety level and your settings block activation in this case. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1839656215"] = "This plugin cannot be activated because its audit result is below the required safety level and your settings block activation in this case." + +-- Children: {0} +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T193192210"] = "Children: {0}" + +-- null +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T1996966820"] = "null" + +-- Properties +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T2177370620"] = "Properties" + +-- Items: {0} +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T2204150657"] = "Items: {0}" + +-- {0} B +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T2562655035"] = "{0} B" -- The assistant plugin could not be resolved for auditing. UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T273798258"] = "The assistant plugin could not be resolved for auditing." @@ -2955,17 +3012,50 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T273798258"] = " -- Audit provider UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T2757790517"] = "Audit provider" --- Enable Plugin -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3233590741"] = "Enable Plugin" +-- Size +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T2789707388"] = "Size" + +-- Prompt: set +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3156437951"] = "Prompt: set" + +-- Findings +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3224848879"] = "Findings" + +-- {0} | Last modified {1} +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3350447013"] = "{0} | Last modified {1}" + +-- The assistant plugin \"{0}\" was audited with the level \"{1}\", which is below the required safety level \"{2}\". Your current settings still allow activation, but this may be unsafe. Do you really want to enable this plugin? +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3418077666"] = "The assistant plugin \\\"{0}\\\" was audited with the level \\\"{1}\\\", which is below the required safety level \\\"{2}\\\". Your current settings still allow activation, but this may be unsafe. Do you really want to enable this plugin?" + +-- Unknown +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3424652889"] = "Unknown" -- Close UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3448155331"] = "Close" --- The audit uses a simulated prompt preview. Empty or placeholder values in the preview are expected during this security check. -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T439841458"] = "The audit uses a simulated prompt preview. Empty or placeholder values in the preview are expected during this security check." +-- Value +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3511155050"] = "Value" --- Run Audit -UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T564725977"] = "Run Audit" +-- Last accessed +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3579946376"] = "Last accessed" + +-- Unknown key +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3647690370"] = "Unknown key" + +-- Minimum required safety level +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3652671056"] = "Minimum required safety level" + +-- Unavailable +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3662391977"] = "Unavailable" + +-- Plugin Structure +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T371537943"] = "Plugin Structure" + +-- Audit Result +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T3844960449"] = "Audit Result" + +-- empty +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T413646574"] = "empty" -- Prompt Preview UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T576347259"] = "Prompt Preview" @@ -2973,6 +3063,15 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T576347259"] = " -- System Prompt UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T628396066"] = "System Prompt" +-- This security check uses a sample prompt preview. Empty or placeholder values in the preview are expected. +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T737998363"] = "This security check uses a sample prompt preview. Empty or placeholder values in the preview are expected." + +-- Safe +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T760494712"] = "Safe" + +-- Start Security Check +UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T811648299"] = "Start Security Check" + -- Cancel UI_TEXT_CONTENT["AISTUDIO::DIALOGS::ASSISTANTPLUGINAUDITDIALOG::T900713019"] = "Cancel" @@ -5775,6 +5874,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T870640199"] = "For some data tra -- Install Pandoc UI_TEXT_CONTENT["AISTUDIO::PAGES::INFORMATION::T986578435"] = "Install Pandoc" +-- Potentially Dangerous Plugin +UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1229643769"] = "Potentially Dangerous Plugin" + -- Disable plugin UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1430375822"] = "Disable plugin" @@ -5796,6 +5898,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2057806005"] = "Enable plugin" -- Plugins UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2222816203"] = "Plugins" +-- The assistant plugin \"{0}\" was audited with the level \"{1}\", which is below the required minimum level \"{2}\". Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin? +UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2531356312"] = "The assistant plugin \\\"{0}\\\" was audited with the level \\\"{1}\\\", which is below the required minimum level \\\"{2}\\\". Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?" + -- Enabled Plugins UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2738444034"] = "Enabled Plugins" @@ -6531,6 +6636,81 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T3290596792"] = "Error during Mi -- Microsoft Word export successful UI_TEXT_CONTENT["AISTUDIO::TOOLS::PANDOCEXPORT::T4256043333"] = "Microsoft Word export successful" +-- Stack +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T135058847"] = "Stack" + +-- Button group +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T1392576058"] = "Button group" + +-- Profiles Selection +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T1437749589"] = "Profiles Selection" + +-- Image +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T1494001562"] = "Image" + +-- Text Area +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T1593629311"] = "Text Area" + +-- File Content +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T165091006"] = "File Content" + +-- Grid Item +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T1991378436"] = "Grid Item" + +-- List +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T2368288673"] = "List" + +-- Provider Selection +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T268262394"] = "Provider Selection" + +-- Root +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T2703841893"] = "Root" + +-- Container +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T2990360344"] = "Container" + +-- Accordion +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3372988345"] = "Accordion" + +-- Date Range +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3400615792"] = "Date Range" + +-- Plane Text +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3557717784"] = "Plane Text" + +-- Switch +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3656636817"] = "Switch" + +-- Time +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3756319748"] = "Time" + +-- Dropdown +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3829804792"] = "Dropdown" + +-- Color +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T3853794552"] = "Color" + +-- Accordion Section +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T4180733902"] = "Accordion Section" + +-- Heading +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T4231005109"] = "Heading" + +-- Unknown Element +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T434854509"] = "Unknown Element" + +-- Web Content +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T590188228"] = "Web Content" + +-- Grid +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T800286385"] = "Grid" + +-- Button +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T864557713"] = "Button" + +-- Date +UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::DATAMODEL::ASSISTANTCOMPONENTTYPE::T995259257"] = "Date" + -- Failed to parse the UI render tree from the ASSISTANT lua table. UI_TEXT_CONTENT["AISTUDIO::TOOLS::PLUGINSYSTEM::ASSISTANTS::PLUGINASSISTANTS::T1318499252"] = "Failed to parse the UI render tree from the ASSISTANT lua table." diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantComponentType.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantComponentType.cs index f65a2a92..7ecdd53f 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantComponentType.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/DataModel/AssistantComponentType.cs @@ -1,4 +1,6 @@ -namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; +using System.ComponentModel.DataAnnotations; + +namespace AIStudio.Tools.PluginSystem.Assistants.DataModel; public enum AssistantComponentType { @@ -27,3 +29,66 @@ public enum AssistantComponentType LAYOUT_ACCORDION, LAYOUT_ACCORDION_SECTION, } + +public static class AssistantComponentTypeExtensions +{ + private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(AssistantComponentTypeExtensions).Namespace, nameof(AssistantComponentTypeExtensions)); + + public static string GetDisplayName(this AssistantComponentType type) => type switch + { + AssistantComponentType.FORM => TB("Root"), + AssistantComponentType.TEXT_AREA => TB("Text Area"), + AssistantComponentType.BUTTON => TB("Button"), + AssistantComponentType.BUTTON_GROUP => TB("Button group"), + AssistantComponentType.DROPDOWN => TB("Dropdown"), + AssistantComponentType.PROVIDER_SELECTION => TB("Provider Selection"), + AssistantComponentType.PROFILE_SELECTION => TB("Profiles Selection"), + AssistantComponentType.SWITCH => TB("Switch"), + AssistantComponentType.HEADING => TB("Heading"), + AssistantComponentType.TEXT => TB("Plane Text"), + AssistantComponentType.LIST => TB("List"), + AssistantComponentType.WEB_CONTENT_READER => TB("Web Content"), + AssistantComponentType.FILE_CONTENT_READER => TB("File Content"), + AssistantComponentType.IMAGE => TB("Image"), + AssistantComponentType.COLOR_PICKER => TB("Color"), + AssistantComponentType.DATE_PICKER => TB("Date"), + AssistantComponentType.DATE_RANGE_PICKER => TB("Date Range"), + AssistantComponentType.TIME_PICKER => TB("Time"), + AssistantComponentType.LAYOUT_ITEM => TB("Grid Item"), + AssistantComponentType.LAYOUT_GRID => TB("Grid"), + AssistantComponentType.LAYOUT_PAPER => TB("Container"), + AssistantComponentType.LAYOUT_STACK => TB("Stack"), + AssistantComponentType.LAYOUT_ACCORDION => TB("Accordion"), + AssistantComponentType.LAYOUT_ACCORDION_SECTION => TB("Accordion Section"), + _ => TB("Unknown Element") + }; + + public static string GetIcon(this AssistantComponentType type) => type switch + { + AssistantComponentType.BUTTON => MudBlazor.Icons.Material.Filled.AdsClick, + AssistantComponentType.BUTTON_GROUP => MudBlazor.Icons.Material.Filled.LinearScale, + AssistantComponentType.DROPDOWN => MudBlazor.Icons.Material.Filled.Rule, + AssistantComponentType.PROVIDER_SELECTION => MudBlazor.Icons.Material.Filled.Memory, + AssistantComponentType.PROFILE_SELECTION => MudBlazor.Icons.Material.Filled.Badge, + AssistantComponentType.SWITCH => MudBlazor.Icons.Material.Filled.ToggleOn, + AssistantComponentType.HEADING => MudBlazor.Icons.Material.Filled.Title, + AssistantComponentType.TEXT => MudBlazor.Icons.Material.Filled.TextFields, + AssistantComponentType.TEXT_AREA => MudBlazor.Icons.Material.Filled.Wysiwyg, + AssistantComponentType.LIST => MudBlazor.Icons.Material.Filled.List, + AssistantComponentType.WEB_CONTENT_READER => MudBlazor.Icons.Material.Filled.Public, + AssistantComponentType.FILE_CONTENT_READER => MudBlazor.Icons.Material.Filled.AttachFile, + AssistantComponentType.IMAGE => MudBlazor.Icons.Material.Filled.Image, + AssistantComponentType.COLOR_PICKER => MudBlazor.Icons.Material.Filled.Palette, + AssistantComponentType.DATE_PICKER => MudBlazor.Icons.Material.Filled.CalendarMonth, + AssistantComponentType.DATE_RANGE_PICKER => MudBlazor.Icons.Material.Filled.DateRange, + AssistantComponentType.TIME_PICKER => MudBlazor.Icons.Material.Filled.Schedule, + AssistantComponentType.LAYOUT_ITEM => MudBlazor.Icons.Material.Filled.DashboardCustomize, + AssistantComponentType.LAYOUT_GRID => MudBlazor.Icons.Material.Filled.GridView, + AssistantComponentType.LAYOUT_PAPER => MudBlazor.Icons.Material.Filled.Inbox, + AssistantComponentType.LAYOUT_STACK => MudBlazor.Icons.Material.Filled.Layers, + AssistantComponentType.LAYOUT_ACCORDION => MudBlazor.Icons.Material.Filled.CalendarViewDay, + AssistantComponentType.LAYOUT_ACCORDION_SECTION => MudBlazor.Icons.Material.Filled.HorizontalSplit, + AssistantComponentType.FORM => MudBlazor.Icons.Material.Filled.AccountTree, + _ => MudBlazor.Icons.Material.Filled.AccountTree, + }; +} diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs index 6c4d15a7..e8652cdd 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs @@ -1,3 +1,4 @@ +using System.Collections.Immutable; using AIStudio.Tools.PluginSystem.Assistants.DataModel; using AIStudio.Tools.PluginSystem.Assistants.DataModel.Layout; using Lua; @@ -29,6 +30,7 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType public AssistantForm? RootComponent { get; private set; } public string AssistantTitle { get; private set; } = string.Empty; public string AssistantDescription { get; private set; } = string.Empty; + public string RawSystemPrompt { get; private set; } = string.Empty; public string SystemPrompt { get; private set; } = string.Empty; public string SubmitText { get; private set; } = string.Empty; public bool AllowProfiles { get; private set; } = true; @@ -112,9 +114,12 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType message = TB("ASSISTANT.BuildPrompt exists but is not a Lua function or has invalid syntax."); } + var rawSystemPrompt = assistantSystemPrompt.Trim(); + this.AssistantTitle = assistantTitle; this.AssistantDescription = assistantDescription; - this.SystemPrompt = BuildSecureSystemPrompt(assistantSystemPrompt); + this.RawSystemPrompt = rawSystemPrompt; + this.SystemPrompt = BuildSecureSystemPrompt(rawSystemPrompt); this.SubmitText = assistantSubmitText; this.AllowProfiles = assistantAllowProfiles; @@ -189,19 +194,52 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType return builder.ToString().TrimEnd(); } - public string ReadManifestCode() + public ImmutableDictionary ReadAllLuaFiles() { - var manifestPath = Path.Combine(this.PluginPath, "plugin.lua"); - return File.Exists(manifestPath) ? File.ReadAllText(manifestPath) : string.Empty; + if (!Directory.Exists(this.PluginPath)) + return ImmutableDictionary.Create(); + + var fileMap = ImmutableDictionary.CreateBuilder(StringComparer.Ordinal); + + foreach (var filePath in Directory.EnumerateFiles(this.PluginPath, "*.lua", SearchOption.AllDirectories).OrderBy(path => path, StringComparer.Ordinal)) + { + var relativePath = Path.GetRelativePath(this.PluginPath, filePath); + fileMap[relativePath] = File.ReadAllText(filePath); + } + + return fileMap.ToImmutable(); } + /// + /// Computes a stable audit hash across all Lua files by hashing a canonical + /// sequence of relative path length, relative path, content length, and content + /// for each file in ordinal path order. + /// public string ComputeAuditHash() { - var manifestCode = this.ReadManifestCode(); - if (string.IsNullOrWhiteSpace(manifestCode)) + var luaFiles = this.ReadAllLuaFiles(); + + if (luaFiles.Count == 0) return string.Empty; - var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(manifestCode)); + using var stream = new MemoryStream(); + using var writer = new BinaryWriter(stream, Encoding.UTF8, leaveOpen: true); + + foreach (var (relativePath, content) in luaFiles.OrderBy(pair => pair.Key, StringComparer.Ordinal)) + { + var normalizedPath = relativePath.Replace('\\', '/'); + var pathBytes = Encoding.UTF8.GetBytes(normalizedPath); + var contentBytes = Encoding.UTF8.GetBytes(content); + + writer.Write(pathBytes.Length); + writer.Write(pathBytes); + writer.Write(contentBytes.Length); + writer.Write(contentBytes); + } + + writer.Flush(); + + var bytes = SHA256.HashData(stream.ToArray()); return Convert.ToHexString(bytes); }