From 521b495fa9e24b8f61dfea8478539dcf353bd123 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Wed, 15 Jul 2026 20:49:17 +0200 Subject: [PATCH] Add assistant plugin deletion with eligibility checks --- .../Assistants/I18N/allTexts.lua | 42 ++++--- .../Components/AssistantBlock.razor | 1 + .../Components/AssistantBlock.razor.cs | 3 + .../AssistantPluginDeleteAction.razor | 13 ++ .../AssistantPluginDeleteAction.razor.cs | 90 ++++++++++++++ app/MindWork AI Studio/Pages/Assistants.razor | 8 ++ app/MindWork AI Studio/Pages/Plugins.razor | 6 +- app/MindWork AI Studio/Pages/Plugins.razor.cs | 32 ----- .../Plugins/assistants/README.md | 1 + .../Plugins/assistants/plugin.lua | 5 + .../plugin.lua | 114 ++++++++++-------- .../plugin.lua | 114 ++++++++++-------- .../Assistants/PluginAssistants.cs | 5 + .../AssistantPluginGenerationService.cs | 7 +- .../Services/AssistantPluginInstallService.cs | 99 ++++++++++++--- 15 files changed, 365 insertions(+), 175 deletions(-) create mode 100644 app/MindWork AI Studio/Components/AssistantPluginDeleteAction.razor create mode 100644 app/MindWork AI Studio/Components/AssistantPluginDeleteAction.razor.cs diff --git a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua index ec6ce952..b458beba 100644 --- a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua +++ b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua @@ -2389,6 +2389,24 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T3571008422"] = "Assistan -- The result is ready. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T661906146"] = "The result is ready." +-- The assistant cannot be deleted while background work is still running. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T1318944584"] = "The assistant cannot be deleted while background work is still running." + +-- Delete assistant plugin +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T1692493145"] = "Delete assistant plugin" + +-- Delete Assistant Plugin +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T3637071001"] = "Delete Assistant Plugin" + +-- The '{0}' assistant plugin has been successfully removed. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T3815023384"] = "The '{0}' assistant plugin has been successfully removed." + +-- The assistant plugin '{0}' could not be deleted: {1} +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T3985264168"] = "The assistant plugin '{0}' could not be deleted: {1}" + +-- Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T4033722845"] = "Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files." + -- Show or hide the detailed security information. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T1045105126"] = "Show or hide the detailed security information." @@ -7282,9 +7300,6 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1506922856"] = "Assistant Audit" -- Internal Plugins UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T158493184"] = "Internal Plugins" --- Delete assistant plugin -UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1692493145"] = "Delete assistant plugin" - -- Disabled Plugins UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1724138133"] = "Disabled Plugins" @@ -7318,24 +7333,12 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3143506997"] = "The assistant plugin -- Close UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3448155331"] = "Close" --- Delete Assistant Plugin -UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3637071001"] = "Delete Assistant Plugin" - -- Revise assistant plugin with AI UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3801095542"] = "Revise assistant plugin with AI" --- The '{0}' assistant plugin has been successfully removed. -UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3815023384"] = "The '{0}' assistant plugin has been successfully removed." - -- Actions UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3865031940"] = "Actions" --- The assistant plugin '{0}' could not be deleted: {1} -UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3985264168"] = "The assistant plugin '{0}' could not be deleted: {1}" - --- Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files. -UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T4033722845"] = "Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files." - -- The automatic security audit for the assistant plugin '{0}' failed. Please run it manually. UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T4066679817"] = "The automatic security audit for the assistant plugin '{0}' failed. Please run it manually." @@ -8857,6 +8860,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T1211 -- Only assistant plugins can be edited. UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T1288328479"] = "Only assistant plugins can be edited." +-- The assistant cannot be deleted while background work is still running. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T1318944584"] = "The assistant cannot be deleted while background work is still running." + -- No Lua plugin code was generated. UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T1839013358"] = "No Lua plugin code was generated." @@ -8887,6 +8893,12 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T2955 -- The generated assistant plugin uses the ID of an internal AI Studio plugin. UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T3162363526"] = "The generated assistant plugin uses the ID of an internal AI Studio plugin." +-- Config Server managed assistant plugins cannot be deleted. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T3751820312"] = "Config Server managed assistant plugins cannot be deleted." + +-- Only assistants generated by the Assistant Builder can be deleted. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T3940247198"] = "Only assistants generated by the Assistant Builder can be deleted." + -- The edited plugin is not an assistant plugin. Issue: {0} UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T3984111892"] = "The edited plugin is not an assistant plugin. Issue: {0}" diff --git a/app/MindWork AI Studio/Components/AssistantBlock.razor b/app/MindWork AI Studio/Components/AssistantBlock.razor index efb7eee4..c3d33a98 100644 --- a/app/MindWork AI Studio/Components/AssistantBlock.razor +++ b/app/MindWork AI Studio/Components/AssistantBlock.razor @@ -50,6 +50,7 @@ { } + @this.AdditionalActions @if (this.SecurityBadge is not null) { diff --git a/app/MindWork AI Studio/Components/AssistantBlock.razor.cs b/app/MindWork AI Studio/Components/AssistantBlock.razor.cs index 48672332..adf8b13a 100644 --- a/app/MindWork AI Studio/Components/AssistantBlock.razor.cs +++ b/app/MindWork AI Studio/Components/AssistantBlock.razor.cs @@ -43,6 +43,9 @@ public partial class AssistantBlock : MSGComponentBase where TSetting [Parameter] public RenderFragment? SecurityBadge { get; set; } + [Parameter] + public RenderFragment? AdditionalActions { get; set; } + [Parameter] public Tools.Components Component { get; set; } = Tools.Components.NONE; diff --git a/app/MindWork AI Studio/Components/AssistantPluginDeleteAction.razor b/app/MindWork AI Studio/Components/AssistantPluginDeleteAction.razor new file mode 100644 index 00000000..777b94d5 --- /dev/null +++ b/app/MindWork AI Studio/Components/AssistantPluginDeleteAction.razor @@ -0,0 +1,13 @@ +@inherits MSGComponentBase + +@if (this.CanDelete) +{ + + + +} \ No newline at end of file diff --git a/app/MindWork AI Studio/Components/AssistantPluginDeleteAction.razor.cs b/app/MindWork AI Studio/Components/AssistantPluginDeleteAction.razor.cs new file mode 100644 index 00000000..cd474c2c --- /dev/null +++ b/app/MindWork AI Studio/Components/AssistantPluginDeleteAction.razor.cs @@ -0,0 +1,90 @@ +using AIStudio.Dialogs; +using AIStudio.Tools.Media; +using AIStudio.Tools.PluginSystem; +using AIStudio.Tools.Services; +using Microsoft.AspNetCore.Components; +using DialogOptions = AIStudio.Dialogs.DialogOptions; + +namespace AIStudio.Components; + +public partial class AssistantPluginDeleteAction : MSGComponentBase +{ + [Parameter, EditorRequired] + public IAvailablePlugin Plugin { get; set; } = null!; + + [Inject] + private IDialogService DialogService { get; init; } = null!; + + [Inject] + private AssistantPluginInstallService AssistantPluginInstallService { get; init; } = null!; + + [Inject] + private MediaTranscriptionService MediaTranscriptionService { get; init; } = null!; + + [Inject] + private ILogger Logger { get; init; } = null!; + + private bool CanDelete => AssistantPluginInstallService.CanDeleteInstalledAssistant(this.Plugin); + + private bool IsBlockedByActiveWork => this.AssistantPluginInstallService.HasActiveAssistantWork(this.Plugin.Id); + + private string Tooltip => this.IsBlockedByActiveWork + ? this.T("The assistant cannot be deleted while background work is still running.") + : this.T("Delete assistant plugin"); + + protected override async Task OnInitializedAsync() + { + this.ApplyFilters([], [ Event.ASSISTANT_SESSION_CHANGED, Event.ASSISTANT_SESSION_FINISHED ]); + this.MediaTranscriptionService.StateChanged += this.OnMediaTranscriptionStateChanged; + await base.OnInitializedAsync(); + } + + private async Task DeleteAssistantPluginAsync() + { + if (!this.CanDelete || this.IsBlockedByActiveWork) + return; + + var dialogParameters = new DialogParameters + { + { + x => x.Message, + string.Format(this.T("Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files."), this.Plugin.Name) + }, + }; + + var dialogReference = await this.DialogService.ShowAsync(this.T("Delete Assistant Plugin"), dialogParameters, DialogOptions.FULLSCREEN); + var dialogResult = await dialogReference.Result; + if (dialogResult is null || dialogResult.Canceled) + return; + + var result = await this.AssistantPluginInstallService.DeleteInstalledAssistantAsync(this.Plugin, CancellationToken.None); + if (!result.Success) + { + this.Logger.LogError("Failed to delete assistant plugin '{PluginName}' ({PluginId}) from '{PluginDirectory}' with issue '{Issue}'.", result.PluginName, result.PluginId, result.PluginDirectory, result.Issue); + await this.MessageBus.SendError(new(Icons.Material.Filled.DeleteForever, string.Format(this.T("The assistant plugin '{0}' could not be deleted: {1}"), this.Plugin.Name, result.Issue))); + return; + } + + await this.MessageBus.SendSuccess(new(Icons.Material.Filled.Check, string.Format(this.T("The '{0}' assistant plugin has been successfully removed."), result.PluginName))); + } + + private void OnMediaTranscriptionStateChanged(MediaImportOwner owner) + { + if (owner.Kind is MediaImportOwnerKind.ASSISTANT && owner.Id.EndsWith($":{this.Plugin.Id}", StringComparison.Ordinal)) + _ = this.InvokeAsync(this.StateHasChanged); + } + + protected override Task ProcessIncomingMessage(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default + { + if (triggeredEvent is Event.ASSISTANT_SESSION_CHANGED or Event.ASSISTANT_SESSION_FINISHED) + this.StateHasChanged(); + + return base.ProcessIncomingMessage(sendingComponent, triggeredEvent, data); + } + + protected override void DisposeResources() + { + this.MediaTranscriptionService.StateChanged -= this.OnMediaTranscriptionStateChanged; + base.DisposeResources(); + } +} diff --git a/app/MindWork AI Studio/Pages/Assistants.razor b/app/MindWork AI Studio/Pages/Assistants.razor index 6b66071e..ee5d5c16 100644 --- a/app/MindWork AI Studio/Pages/Assistants.razor +++ b/app/MindWork AI Studio/Pages/Assistants.razor @@ -1,6 +1,7 @@ @attribute [Route(Routes.ASSISTANTS)] @using AIStudio.Dialogs.Settings @using AIStudio.Settings.DataModel +@using AIStudio.Tools.PluginSystem @using AIStudio.Tools.PluginSystem.Assistants @inherits MSGComponentBase @@ -45,6 +46,7 @@ { var securityState = PluginAssistantSecurityResolver.Resolve(this.SettingsManager, assistantPlugin); var launchLink = assistantPlugin.StartsChatDirectly ? string.Empty : $"{Routes.ASSISTANT_DYNAMIC}?assistantId={assistantPlugin.Id}"; + var availablePlugin = PluginFactory.AvailablePlugins.OfType().FirstOrDefault(plugin => plugin.Id == assistantPlugin.Id); + + @if (availablePlugin is not null) + { + + } + diff --git a/app/MindWork AI Studio/Pages/Plugins.razor b/app/MindWork AI Studio/Pages/Plugins.razor index 73a58e3c..eab51b12 100644 --- a/app/MindWork AI Studio/Pages/Plugins.razor +++ b/app/MindWork AI Studio/Pages/Plugins.razor @@ -115,11 +115,9 @@ } - @if (context is IAvailablePlugin availablePlugin && CanDeleteAssistantPlugin(availablePlugin)) + @if (context is IAvailablePlugin availablePlugin) { - - - + } diff --git a/app/MindWork AI Studio/Pages/Plugins.razor.cs b/app/MindWork AI Studio/Pages/Plugins.razor.cs index be7c2105..23bcb7da 100644 --- a/app/MindWork AI Studio/Pages/Plugins.razor.cs +++ b/app/MindWork AI Studio/Pages/Plugins.razor.cs @@ -28,9 +28,6 @@ public partial class Plugins : MSGComponentBase [Inject] private AssistantPluginAuditService AssistantPluginAuditService { get; init; } = null!; - [Inject] - private AssistantPluginInstallService AssistantPluginInstallService { get; init; } = null!; - private static readonly ILogger LOG = Program.LOGGER_FACTORY.CreateLogger(nameof(Plugins)); #region Overrides of ComponentBase @@ -190,8 +187,6 @@ public partial class Plugins : MSGComponentBase private static bool CanEditAssistantPlugin(IAvailablePlugin plugin) => plugin is { IsInternal: false, Type: PluginType.ASSISTANT } && !string.IsNullOrWhiteSpace(plugin.LocalPath); - private static bool CanDeleteAssistantPlugin(IAvailablePlugin plugin) => plugin is { IsInternal: false, Type: PluginType.ASSISTANT } && !string.IsNullOrWhiteSpace(plugin.LocalPath); - private static bool CanReviseAssistantPlugin(IAvailablePlugin plugin) { var assistantPlugin = PluginFactory.RunningPlugins.OfType().FirstOrDefault(x => x.Id == plugin.Id); @@ -239,33 +234,6 @@ public partial class Plugins : MSGComponentBase await this.InvokeAsync(this.StateHasChanged); } - private async Task DeleteAssistantPluginAsync(IAvailablePlugin plugin) - { - var dialogParameters = new DialogParameters - { - { - x => x.Message, - string.Format(this.T("Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files."), plugin.Name) - }, - }; - - var dialogReference = await this.DialogService.ShowAsync(this.T("Delete Assistant Plugin"), dialogParameters, DialogOptions.FULLSCREEN); - var dialogResult = await dialogReference.Result; - if (dialogResult is null || dialogResult.Canceled) - return; - - var result = await this.AssistantPluginInstallService.DeleteInstalledAssistantAsync(plugin, CancellationToken.None); - if (!result.Success) - { - LOG.LogError($"Failed to delete assistant plugin '{result.PluginName}' ({result.PluginId}) from '{result.PluginDirectory}' with issue '{result.Issue}'."); - await this.MessageBus.SendError(new(Icons.Material.Filled.DeleteForever, string.Format(this.T("The assistant plugin '{0}' could not be deleted: {1}"), plugin.Name, result.Issue))); - return; - } - - await this.MessageBus.SendSuccess(new(Icons.Material.Filled.Check, string.Format(this.T("The '{0}' assistant plugin has been successfully removed."), result.PluginName))); - await this.InvokeAsync(this.StateHasChanged); - } - private static bool IsSendingMail(string sourceUrl) => sourceUrl.TrimStart().StartsWith("mailto:", StringComparison.OrdinalIgnoreCase); private PluginAssistants? TryGetAssistantPlugin(Guid pluginId) => PluginFactory.RunningPlugins.OfType().FirstOrDefault(x => x.Id == pluginId); diff --git a/app/MindWork AI Studio/Plugins/assistants/README.md b/app/MindWork AI Studio/Plugins/assistants/README.md index f0b723f9..301a311d 100644 --- a/app/MindWork AI Studio/Plugins/assistants/README.md +++ b/app/MindWork AI Studio/Plugins/assistants/README.md @@ -82,6 +82,7 @@ Each assistant plugin lives in its own directory under the assistants plugin roo ## Structure - `ASSISTANT` is the root table. It must contain `Title`, `Description`, `SystemPrompt`, `SubmitText`, `AllowProfiles`, and the nested `UI` definition. - `DEPLOYED_USING_CONFIG_SERVER` identifies who manages the assistant plugin. Set it to `false` for locally managed plugins. A missing field is also treated as local for compatibility with existing plugins. Enterprise-distributed plugins must set it to `true` and cannot be revised with AI in AI Studio. +- `AI_STUDIO_ASSISTANT_BUILDER = {Generated = true, SchemaVersion = 1}` is reserved for plugins generated by the AI Studio Assistant Builder. It enables Builder-specific actions such as safe deletion and must not be added to manually authored or enterprise-distributed assistants. Newly generated Builder assistants always set `DEPLOYED_USING_CONFIG_SERVER = false` explicitly. - `ASSISTANT` may optionally define direct-launch metadata for assistant tiles: - `LaunchBehavior = "OPEN_WORKSPACE_CHAT_BY_NAME"` - `WorkspaceName = ""` diff --git a/app/MindWork AI Studio/Plugins/assistants/plugin.lua b/app/MindWork AI Studio/Plugins/assistants/plugin.lua index 50b72045..58b314ac 100644 --- a/app/MindWork AI Studio/Plugins/assistants/plugin.lua +++ b/app/MindWork AI Studio/Plugins/assistants/plugin.lua @@ -49,6 +49,11 @@ DEPRECATION_MESSAGE = "" -- Enterprise-managed assistants cannot be revised with AI. Keep false for locally managed plugins: DEPLOYED_USING_CONFIG_SERVER = false +-- Reserved for assistants created by the AI Studio Assistant Builder. Generated assistants use this +-- metadata so AI Studio can identify them and offer Builder-specific actions such as safe deletion. +-- Manually authored or enterprise-distributed assistants must not set this metadata: +-- AI_STUDIO_ASSISTANT_BUILDER = {Generated = true, SchemaVersion = 1} + ASSISTANT = { ["Title"] = "", ["Description"] = "<Description presented to the users, explaining your assistant>", 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 2c6c1bab..cf755036 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 @@ -2391,6 +2391,24 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T3571008422"] = "Assisten -- The result is ready. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T661906146"] = "Das Ergebnis ist fertig." +-- The assistant cannot be deleted while background work is still running. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T1318944584"] = "Der Assistent kann nicht gelöscht werden, solange noch Hintergrundaufgaben ausgeführt werden." + +-- Delete assistant plugin +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T1692493145"] = "Assistenten-Plugin löschen" + +-- Delete Assistant Plugin +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T3637071001"] = "Assistenten-Plugin löschen" + +-- The '{0}' assistant plugin has been successfully removed. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T3815023384"] = "Das Assistenten-Plugin „{0}“ wurde erfolgreich entfernt." + +-- The assistant plugin '{0}' could not be deleted: {1} +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T3985264168"] = "Das Assistenten-Plugin „{0}“ konnte nicht gelöscht werden: {1}" + +-- Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T4033722845"] = "Möchtest du das Assistenten-Plug-in „{0}“ wirklich löschen? Dadurch werden die lokalen Plug-in-Dateien dauerhaft gelöscht." + -- Show or hide the detailed security information. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T1045105126"] = "Detaillierte Sicherheitsinformationen anzeigen oder ausblenden." @@ -7284,9 +7302,6 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1506922856"] = "Assistentenprüfung" -- Internal Plugins UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T158493184"] = "Interne Plugins" --- Delete assistant plugin -UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1692493145"] = "Assistenten-Plugin löschen" - -- Disabled Plugins UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1724138133"] = "Deaktivierte Plugins" @@ -7320,24 +7335,12 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3143506997"] = "Das Assistent-Plugin -- Close UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3448155331"] = "Schließen" --- Delete Assistant Plugin -UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3637071001"] = "Assistant-Plug-in löschen" - -- Revise assistant plugin with AI UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3801095542"] = "Assistenten-Plugin mit KI überarbeiten" --- The '{0}' assistant plugin has been successfully removed. -UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3815023384"] = "Das Assistenten-Plugin „{0}“ wurde erfolgreich entfernt." - -- Actions UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3865031940"] = "Aktionen" --- The assistant plugin '{0}' could not be deleted: {1} -UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3985264168"] = "Das Assistenten-Plugin „{0}“ konnte nicht gelöscht werden: {1}" - --- Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files. -UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T4033722845"] = "Möchten Sie das Assistenten-Plugin „{0}“ wirklich löschen? Dadurch werden die lokalen Plugin-Dateien dauerhaft gelöscht." - -- The automatic security audit for the assistant plugin '{0}' failed. Please run it manually. UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T4066679817"] = "Die automatische Sicherheitsprüfung für das Assistenten-Plugin „{0}“ ist fehlgeschlagen. Bitte führen Sie sie manuell aus." @@ -8748,42 +8751,6 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPES::T378481461"] = "Source Code -- Document UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPES::T4165204724"] = "Dokument" --- The configured transcription provider could not be created. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1235984176"] = "Der konfigurierte Transkriptionsanbieter konnte nicht erstellt werden." - --- The selected media file no longer exists. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T129859547"] = "Die ausgewählte Mediendatei ist nicht mehr vorhanden." - --- The selected media file does not contain an audio track. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T134825479"] = "Die ausgewählte Mediendatei enthält keine Audiospur." - --- The media file could not be transcribed. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1543974632"] = "Die Mediendatei konnte nicht transkribiert werden." - --- The selected file cannot be processed as media. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1707342767"] = "Die ausgewählte Datei kann nicht als Medium verarbeitet werden." - --- The audio track contains no audible signal, so there is nothing to transcribe. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1988190152"] = "Die Audiospur enthält kein hörbares Signal. Daher gibt es nichts zu transkribieren." - --- The media file is damaged or its format could not be identified. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T2004316549"] = "Die Mediendatei ist beschädigt oder ihr Format konnte nicht erkannt werden." - --- This media format or audio codec is not supported. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T2142564510"] = "Dieses Medienformat oder dieser Audiocodec wird nicht unterstützt." - --- No usable transcription provider is configured. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T2282521655"] = "Es ist kein nutzbarer Transkriptionsanbieter konfiguriert." - --- The media file could not be prepared for transcription. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T2749117459"] = "Die Mediendatei konnte nicht für die Transkription vorbereitet werden." - --- The transcription provider could not transcribe the media file. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T3091669215"] = "Der Transkriptionsanbieter konnte die Mediendatei nicht transkribieren." - --- The media pipeline ended without an output file. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T632852430"] = "Die Medienpipeline wurde beendet, ohne eine Ausgabedatei zu erzeugen." - -- The Assistant Builder context could not be loaded. UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGINGENERATIONSERVICE::T108292972"] = "Der Kontext des Assistenten-Builders konnte nicht geladen werden." @@ -8895,6 +8862,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T1211 -- Only assistant plugins can be edited. UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T1288328479"] = "Nur Assistant-Plugins können bearbeitet werden." +-- The assistant cannot be deleted while background work is still running. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T1318944584"] = "Der Assistent kann nicht gelöscht werden, solange noch Hintergrundaktivitäten ausgeführt werden." + -- No Lua plugin code was generated. UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T1839013358"] = "Es wurde kein Lua-Plugin-Code generiert." @@ -8925,6 +8895,12 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T2955 -- The generated assistant plugin uses the ID of an internal AI Studio plugin. UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T3162363526"] = "Das generierte Assistent-Plugin verwendet die ID eines internen AI-Studio-Plugins." +-- Config Server managed assistant plugins cannot be deleted. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T3751820312"] = "Von einem Config-Server verwaltete Assistenten-Plugins können nicht gelöscht werden." + +-- Only assistants generated by the Assistant Builder can be deleted. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T3940247198"] = "Nur mit dem Assistant Builder erstellte Assistenten können gelöscht werden." + -- The edited plugin is not an assistant plugin. Issue: {0} UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T3984111892"] = "Das bearbeitete Plugin ist kein Assistenten-Plugin. Problem: {0}" @@ -8946,6 +8922,42 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T8163 -- The generated assistant plugin is invalid. Issue: {0} UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T939708112"] = "Das generierte Assistenten-Plugin ist ungültig. Problem: {0}" +-- The configured transcription provider could not be created. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1235984176"] = "Der konfigurierte Transkriptionsanbieter konnte nicht erstellt werden." + +-- The selected media file no longer exists. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T129859547"] = "Die ausgewählte Mediendatei ist nicht mehr vorhanden." + +-- The selected media file does not contain an audio track. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T134825479"] = "Die ausgewählte Mediendatei enthält keine Audiospur." + +-- The media file could not be transcribed. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1543974632"] = "Die Mediendatei konnte nicht transkribiert werden." + +-- The selected file cannot be processed as media. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1707342767"] = "Die ausgewählte Datei kann nicht als Medium verarbeitet werden." + +-- The audio track contains no audible signal, so there is nothing to transcribe. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1988190152"] = "Die Audiospur enthält kein hörbares Signal. Daher gibt es nichts zu transkribieren." + +-- The media file is damaged or its format could not be identified. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T2004316549"] = "Die Mediendatei ist beschädigt oder ihr Format konnte nicht erkannt werden." + +-- This media format or audio codec is not supported. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T2142564510"] = "Dieses Medienformat oder dieser Audiocodec wird nicht unterstützt." + +-- No usable transcription provider is configured. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T2282521655"] = "Es ist kein nutzbarer Transkriptionsanbieter konfiguriert." + +-- The media file could not be prepared for transcription. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T2749117459"] = "Die Mediendatei konnte nicht für die Transkription vorbereitet werden." + +-- The transcription provider could not transcribe the media file. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T3091669215"] = "Der Transkriptionsanbieter konnte die Mediendatei nicht transkribieren." + +-- The media pipeline ended without an output file. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T632852430"] = "Die Medienpipeline wurde beendet, ohne eine Ausgabedatei zu erzeugen." + -- Pandoc Installation UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PANDOCAVAILABILITYSERVICE::T185447014"] = "Pandoc-Installation" 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 d3a58f2b..c716b6fb 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 @@ -2391,6 +2391,24 @@ UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T3571008422"] = "Assistan -- The result is ready. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTBLOCK::T661906146"] = "The result is ready." +-- The assistant cannot be deleted while background work is still running. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T1318944584"] = "The assistant cannot be deleted while background work is still running." + +-- Delete assistant plugin +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T1692493145"] = "Delete assistant plugin" + +-- Delete Assistant Plugin +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T3637071001"] = "Delete Assistant Plugin" + +-- The '{0}' assistant plugin has been successfully removed. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T3815023384"] = "The '{0}' assistant plugin has been successfully removed." + +-- The assistant plugin '{0}' could not be deleted: {1} +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T3985264168"] = "The assistant plugin '{0}' could not be deleted: {1}" + +-- Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files. +UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINDELETEACTION::T4033722845"] = "Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files." + -- Show or hide the detailed security information. UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ASSISTANTPLUGINSECURITYCARD::T1045105126"] = "Show or hide the detailed security information." @@ -7284,9 +7302,6 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1506922856"] = "Assistant Audit" -- Internal Plugins UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T158493184"] = "Internal Plugins" --- Delete assistant plugin -UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1692493145"] = "Delete assistant plugin" - -- Disabled Plugins UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T1724138133"] = "Disabled Plugins" @@ -7320,24 +7335,12 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3143506997"] = "The assistant plugin -- Close UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3448155331"] = "Close" --- Delete Assistant Plugin -UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3637071001"] = "Delete Assistant Plugin" - -- Revise assistant plugin with AI UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3801095542"] = "Revise assistant plugin with AI" --- The '{0}' assistant plugin has been successfully removed. -UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3815023384"] = "The '{0}' assistant plugin has been successfully removed." - -- Actions UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3865031940"] = "Actions" --- The assistant plugin '{0}' could not be deleted: {1} -UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T3985264168"] = "The assistant plugin '{0}' could not be deleted: {1}" - --- Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files. -UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T4033722845"] = "Do you really want to delete the assistant plugin '{0}'? This will permanently delete the local plugin files." - -- The automatic security audit for the assistant plugin '{0}' failed. Please run it manually. UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T4066679817"] = "The automatic security audit for the assistant plugin '{0}' failed. Please run it manually." @@ -8748,42 +8751,6 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPES::T378481461"] = "Source like p -- Document UI_TEXT_CONTENT["AISTUDIO::TOOLS::RUST::FILETYPES::T4165204724"] = "Document" --- The configured transcription provider could not be created. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1235984176"] = "The configured transcription provider could not be created." - --- The selected media file no longer exists. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T129859547"] = "The selected media file no longer exists." - --- The selected media file does not contain an audio track. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T134825479"] = "The selected media file does not contain an audio track." - --- The media file could not be transcribed. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1543974632"] = "The media file could not be transcribed." - --- The selected file cannot be processed as media. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1707342767"] = "The selected file cannot be processed as media." - --- The audio track contains no audible signal, so there is nothing to transcribe. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1988190152"] = "The audio track contains no audible signal, so there is nothing to transcribe." - --- The media file is damaged or its format could not be identified. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T2004316549"] = "The media file is damaged or its format could not be identified." - --- This media format or audio codec is not supported. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T2142564510"] = "This media format or audio codec is not supported." - --- No usable transcription provider is configured. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T2282521655"] = "No usable transcription provider is configured." - --- The media file could not be prepared for transcription. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T2749117459"] = "The media file could not be prepared for transcription." - --- The transcription provider could not transcribe the media file. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T3091669215"] = "The transcription provider could not transcribe the media file." - --- The media pipeline ended without an output file. -UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T632852430"] = "The media pipeline ended without an output file." - -- The Assistant Builder context could not be loaded. UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGINGENERATIONSERVICE::T108292972"] = "The Assistant Builder context could not be loaded." @@ -8895,6 +8862,9 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T1211 -- Only assistant plugins can be edited. UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T1288328479"] = "Only assistant plugins can be edited." +-- The assistant cannot be deleted while background work is still running. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T1318944584"] = "The assistant cannot be deleted while background work is still running." + -- No Lua plugin code was generated. UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T1839013358"] = "No Lua plugin code was generated." @@ -8925,6 +8895,12 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T2955 -- The generated assistant plugin uses the ID of an internal AI Studio plugin. UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T3162363526"] = "The generated assistant plugin uses the ID of an internal AI Studio plugin." +-- Config Server managed assistant plugins cannot be deleted. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T3751820312"] = "Config Server managed assistant plugins cannot be deleted." + +-- Only assistants generated by the Assistant Builder can be deleted. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T3940247198"] = "Only assistants generated by the Assistant Builder can be deleted." + -- The edited plugin is not an assistant plugin. Issue: {0} UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T3984111892"] = "The edited plugin is not an assistant plugin. Issue: {0}" @@ -8946,6 +8922,42 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T8163 -- The generated assistant plugin is invalid. Issue: {0} UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::ASSISTANTPLUGININSTALLSERVICE::T939708112"] = "The generated assistant plugin is invalid. Issue: {0}" +-- The configured transcription provider could not be created. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1235984176"] = "The configured transcription provider could not be created." + +-- The selected media file no longer exists. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T129859547"] = "The selected media file no longer exists." + +-- The selected media file does not contain an audio track. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T134825479"] = "The selected media file does not contain an audio track." + +-- The media file could not be transcribed. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1543974632"] = "The media file could not be transcribed." + +-- The selected file cannot be processed as media. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1707342767"] = "The selected file cannot be processed as media." + +-- The audio track contains no audible signal, so there is nothing to transcribe. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1988190152"] = "The audio track contains no audible signal, so there is nothing to transcribe." + +-- The media file is damaged or its format could not be identified. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T2004316549"] = "The media file is damaged or its format could not be identified." + +-- This media format or audio codec is not supported. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T2142564510"] = "This media format or audio codec is not supported." + +-- No usable transcription provider is configured. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T2282521655"] = "No usable transcription provider is configured." + +-- The media file could not be prepared for transcription. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T2749117459"] = "The media file could not be prepared for transcription." + +-- The transcription provider could not transcribe the media file. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T3091669215"] = "The transcription provider could not transcribe the media file." + +-- The media pipeline ended without an output file. +UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T632852430"] = "The media pipeline ended without an output file." + -- Pandoc Installation UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::PANDOCAVAILABILITYSERVICE::T185447014"] = "Pandoc Installation" diff --git a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs index b16910ac..9c610c85 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/Assistants/PluginAssistants.cs @@ -37,6 +37,7 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType public bool HasEmbeddedProfileSelection { get; private set; } public bool HasCustomPromptBuilder => this.buildPromptFunction is not null; public bool IsAssistantBuilderGenerated { get; private set; } + public bool HasDeploymentManagementMetadata { get; private set; } public bool IsManagedByConfigServer { get; private set; } public AssistantPluginLaunchBehavior LaunchBehavior { get; private set; } public string LaunchWorkspaceName { get; private set; } = string.Empty; @@ -66,6 +67,7 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType message = string.Empty; this.HasEmbeddedProfileSelection = false; this.IsAssistantBuilderGenerated = false; + this.HasDeploymentManagementMetadata = false; this.IsManagedByConfigServer = false; this.buildPromptFunction = null; this.LaunchBehavior = AssistantPluginLaunchBehavior.NONE; @@ -169,7 +171,10 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType private void TryReadDeploymentMetadata() { if (this.State.Environment["DEPLOYED_USING_CONFIG_SERVER"].TryRead<bool>(out var deployedUsingConfigServer)) + { + this.HasDeploymentManagementMetadata = true; this.IsManagedByConfigServer = deployedUsingConfigServer; + } } private bool TryReadLaunchConfiguration(LuaTable assistantTable, out string message) diff --git a/app/MindWork AI Studio/Tools/Services/AssistantPluginGenerationService.cs b/app/MindWork AI Studio/Tools/Services/AssistantPluginGenerationService.cs index b0342356..f5df3303 100644 --- a/app/MindWork AI Studio/Tools/Services/AssistantPluginGenerationService.cs +++ b/app/MindWork AI Studio/Tools/Services/AssistantPluginGenerationService.cs @@ -115,7 +115,7 @@ public sealed class AssistantPluginGenerationService(ILogger<AssistantPluginGene if (!generatedAssistant.IsAssistantBuilderGenerated) return InitialFailure(TB("The generated assistant plugin must include the Assistant Builder metadata.")); - if (generatedAssistant.IsManagedByConfigServer) + if (!generatedAssistant.HasDeploymentManagementMetadata || generatedAssistant.IsManagedByConfigServer) return InitialFailure(TB("The generated assistant plugin must be marked as locally managed.")); return new(true, fullLua, parsedResponse.Plugin?.Name ?? string.Empty, string.Empty); @@ -168,7 +168,8 @@ public sealed class AssistantPluginGenerationService(ILogger<AssistantPluginGene if (plugin.IsAssistantBuilderGenerated && !revisedAssistant.IsAssistantBuilderGenerated) return RevisionFailure(TB("The revised assistant plugin must keep the Assistant Builder metadata.")); - if (revisedAssistant.IsManagedByConfigServer) + if (revisedAssistant.IsManagedByConfigServer || + plugin.IsAssistantBuilderGenerated && !revisedAssistant.HasDeploymentManagementMetadata) return RevisionFailure(TB("The revised assistant plugin must remain locally managed.")); return new(true, revisedLua, parsedResponse.Plugin?.Name ?? plugin.Name, string.Empty); @@ -364,7 +365,7 @@ public sealed class AssistantPluginGenerationService(ILogger<AssistantPluginGene { var companionLua = FormatCompanionLuaFiles(plugin); var builderMetadataRule = plugin.IsAssistantBuilderGenerated - ? "- Keep AI_STUDIO_ASSISTANT_BUILDER = {Generated = true, SchemaVersion = 1}." + ? "- Keep AI_STUDIO_ASSISTANT_BUILDER = {Generated = true, SchemaVersion = 1} and set DEPLOYED_USING_CONFIG_SERVER = false explicitly." : string.Empty; return $$""" Revise an existing locally managed AI Studio Lua assistant plugin. diff --git a/app/MindWork AI Studio/Tools/Services/AssistantPluginInstallService.cs b/app/MindWork AI Studio/Tools/Services/AssistantPluginInstallService.cs index 3473f7f1..00d70b0e 100644 --- a/app/MindWork AI Studio/Tools/Services/AssistantPluginInstallService.cs +++ b/app/MindWork AI Studio/Tools/Services/AssistantPluginInstallService.cs @@ -1,5 +1,7 @@ using System.Text; using AIStudio.Settings; +using AIStudio.Tools.AssistantSessions; +using AIStudio.Tools.Media; using AIStudio.Tools.PluginSystem; using AIStudio.Tools.PluginSystem.Assistants; @@ -24,6 +26,8 @@ public sealed class AssistantPluginInstallService private readonly ILogger<AssistantPluginInstallService> logger; private readonly SettingsManager settingsManager; + private readonly AssistantSessionService assistantSessionService; + private readonly MediaTranscriptionService mediaTranscriptionService; private readonly SemaphoreSlim installSemaphore = new(1, 1); private static AssistantPluginInstallResult Error(string issue) => new(false, Guid.Empty, string.Empty, string.Empty, false, issue); @@ -34,13 +38,39 @@ public sealed class AssistantPluginInstallService private static AssistantPluginUpdateResult UpdateError(IPluginMetadata plugin, string pluginDirectory, string issue) => new(false, plugin.Id, plugin.Name, pluginDirectory, issue); - public AssistantPluginInstallService(ILogger<AssistantPluginInstallService> logger, SettingsManager settingsManager) + public AssistantPluginInstallService( + ILogger<AssistantPluginInstallService> logger, + SettingsManager settingsManager, + AssistantSessionService assistantSessionService, + MediaTranscriptionService mediaTranscriptionService) { this.logger = logger; this.settingsManager = settingsManager; + this.assistantSessionService = assistantSessionService; + this.mediaTranscriptionService = mediaTranscriptionService; this.logger.LogInformation("The assistant plugin install service has been initialized."); } + /// <summary> + /// Checks whether a local plugin is an Assistant Builder generated assistant that users may delete. + /// </summary> + public static bool CanDeleteInstalledAssistant(IAvailablePlugin plugin) => string.IsNullOrWhiteSpace(GetAssistantDeletionEligibilityIssue(plugin)); + + /// <summary> + /// Checks whether an assistant still owns running or canceling background work. + /// </summary> + public bool HasActiveAssistantWork(Guid pluginId) + { + var instanceId = pluginId.ToString(); + if (this.assistantSessionService.GetSnapshots().Any(snapshot => snapshot.IsActive && string.Equals(snapshot.Key.InstanceId, instanceId, StringComparison.Ordinal))) + return true; + + var ownerIdSuffix = $":{instanceId}"; + return this.mediaTranscriptionService.GetSnapshots().Any(snapshot => + snapshot is { IsBusy: true, Owner.Kind: MediaImportOwnerKind.ASSISTANT } && + snapshot.Owner.Id.EndsWith(ownerIdSuffix, StringComparison.Ordinal)); + } + /// <summary> /// Checks whether generated Lua assistant plugin code can be loaded and installed. /// The plugin is written to a temporary staging directory and validated through the @@ -232,32 +262,28 @@ public sealed class AssistantPluginInstallService /// </returns> public async Task<AssistantPluginDeleteResult> DeleteInstalledAssistantAsync(IAvailablePlugin plugin, CancellationToken token) { - if (plugin.Type is not PluginType.ASSISTANT) - return DeleteError(plugin, plugin.LocalPath, TB("Only assistant plugins can be deleted.")); + var eligibilityIssue = GetAssistantDeletionEligibilityIssue(plugin); + if (!string.IsNullOrEmpty(eligibilityIssue)) + return DeleteError(plugin, plugin.LocalPath, eligibilityIssue); - if (plugin.IsInternal) - return DeleteError(plugin, plugin.LocalPath, TB("Internal assistant plugins cannot be deleted.")); - - if (string.IsNullOrWhiteSpace(plugin.LocalPath)) - return DeleteError(plugin, string.Empty, TB("The assistant plugin has no local directory.")); - - if (!TryGetAssistantPluginsRoot(out var assistantPluginsRoot, out var rootIssue)) - return DeleteError(plugin, plugin.LocalPath, rootIssue); - - var pluginDirectory = plugin.LocalPath; - if (!IsPathInsideDirectory(assistantPluginsRoot, pluginDirectory) || IsSameDirectory(assistantPluginsRoot, pluginDirectory)) - return DeleteError(plugin, pluginDirectory, TB("The assistant plugin directory is outside the local assistant plugin directory.")); - - if (!Directory.Exists(pluginDirectory)) - return DeleteError(plugin, pluginDirectory, TB("The assistant plugin directory does not exist.")); + if (this.HasActiveAssistantWork(plugin.Id)) + return DeleteError(plugin, plugin.LocalPath, TB("The assistant cannot be deleted while background work is still running.")); await this.installSemaphore.WaitAsync(token); + var pluginDirectory = plugin.LocalPath; var backupDirectory = string.Empty; var wasEnabled = false; var removedAudits = new List<PluginAssistantAudit>(); try { + eligibilityIssue = GetAssistantDeletionEligibilityIssue(plugin); + if (!string.IsNullOrEmpty(eligibilityIssue)) + return DeleteError(plugin, pluginDirectory, eligibilityIssue); + + if (this.HasActiveAssistantWork(plugin.Id)) + return DeleteError(plugin, pluginDirectory, TB("The assistant cannot be deleted while background work is still running.")); + backupDirectory = CreateDeleteBackupDirectory(plugin); Directory.CreateDirectory(Path.GetDirectoryName(backupDirectory)!); Directory.Move(pluginDirectory, backupDirectory); @@ -486,6 +512,41 @@ public sealed class AssistantPluginInstallService return true; } + private static string GetAssistantDeletionEligibilityIssue(IAvailablePlugin plugin) + { + if (plugin.Type is not PluginType.ASSISTANT) + return TB("Only assistant plugins can be deleted."); + + if (plugin.IsInternal) + return TB("Internal assistant plugins cannot be deleted."); + + if (plugin.IsManagedByConfigServer) + return TB("Config Server managed assistant plugins cannot be deleted."); + + if (string.IsNullOrWhiteSpace(plugin.LocalPath)) + return TB("The assistant plugin has no local directory."); + + var assistantPlugin = PluginFactory.RunningPlugins + .OfType<PluginAssistants>() + .FirstOrDefault(candidate => candidate.Id == plugin.Id && IsSameDirectory(candidate.PluginPath, plugin.LocalPath)); + + if (assistantPlugin is null || assistantPlugin.IsInternal || !assistantPlugin.IsAssistantBuilderGenerated) + return TB("Only assistants generated by the Assistant Builder can be deleted."); + + if (assistantPlugin.IsManagedByConfigServer) + return TB("Config Server managed assistant plugins cannot be deleted."); + + if (!TryGetAssistantPluginsRoot(out var assistantPluginsRoot, out var rootIssue)) + return rootIssue; + + if (!IsPathInsideDirectory(assistantPluginsRoot, plugin.LocalPath) || IsSameDirectory(assistantPluginsRoot, plugin.LocalPath)) + return TB("The assistant plugin directory is outside the local assistant plugin directory."); + + return Directory.Exists(plugin.LocalPath) + ? string.Empty + : TB("The assistant plugin directory does not exist."); + } + private void TryDeleteStagingDirectory(string stagingDirectory) { TryDeleteDirectory(stagingDirectory, "assistant plugin staging", this.logger); @@ -645,4 +706,4 @@ public sealed class AssistantPluginInstallService { public static AssistantPluginValidationResult Failure(string issue) => new(false, string.Empty, null, issue); } -} +} \ No newline at end of file