Added assistant plugins (#659)
Build and Release / Determine run mode (push) Waiting to run
Build and Release / Read metadata (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-unknown-linux-gnu, linux-arm64, ubuntu-22.04-arm, aarch64-unknown-linux-gnu, appimage,deb,updater, appimage,deb) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg,updater, dmg) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis,updater, nsis) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage,deb,updater, appimage,deb) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions

This commit is contained in:
nilskruthoff authored and GitHub committed 2026-04-09 10:01:24 +02:00
1 parent a573409a24
commit f6a128f2e4
100 files changed
+10146 -85

No files matched your search

@@ -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;
}
@@ -22,15 +22,23 @@
</MudStack>
</MudCardContent>
<MudCardActions>
<MudButtonGroup Variant="Variant.Outlined">
<MudButton Size="Size.Large" Variant="Variant.Filled" StartIcon="@this.Icon" Color="Color.Default" Href="@this.Link">
@this.ButtonText
</MudButton>
@if (this.HasSettingsPanel)
<MudStack Row="@true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Style="width: 100%;">
<MudButtonGroup Variant="Variant.Outlined">
<MudButton Size="Size.Large" Variant="Variant.Filled" StartIcon="@this.Icon" Color="Color.Default" Href="@this.Link" Disabled="@this.Disabled">
@this.ButtonText
</MudButton>
@if (this.HasSettingsPanel)
{
<MudIconButton Variant="Variant.Text" Icon="@Icons.Material.Filled.Settings" Color="Color.Default" OnClick="@this.OpenSettingsDialog"/>
}
</MudButtonGroup>
@if (this.SecurityBadge is not null)
{
<MudIconButton Variant="Variant.Text" Icon="@Icons.Material.Filled.Settings" Color="Color.Default" OnClick="@this.OpenSettingsDialog"/>
<MudElement>
@this.SecurityBadge
</MudElement>
}
</MudButtonGroup>
</MudStack>
</MudCardActions>
</MudCard>
}
@@ -1,8 +1,6 @@
using AIStudio.Settings.DataModel;
using AIStudio.Dialogs.Settings;
using AIStudio.Settings.DataModel;
using Microsoft.AspNetCore.Components;
using DialogOptions = AIStudio.Dialogs.DialogOptions;
namespace AIStudio.Components;
@@ -24,6 +22,12 @@ public partial class AssistantBlock<TSettings> : MSGComponentBase where TSetting
[Parameter]
public string Link { get; set; } = string.Empty;
[Parameter]
public bool Disabled { get; set; }
[Parameter]
public RenderFragment? SecurityBadge { get; set; }
[Parameter]
public Tools.Components Component { get; set; } = Tools.Components.NONE;
@@ -0,0 +1,203 @@
@using AIStudio.Agents.AssistantAudit
@inherits MSGComponentBase
@if (this.Plugin is not null)
{
var state = this.SecurityState;
<div class="d-flex">
<MudTooltip Text="@state.ActionLabel" Placement="Placement.Top">
<MudIconButton Icon="@state.BadgeIcon"
Color="@state.AuditColor"
Size="@(this.Compact ? Size.Small : Size.Medium)"
OnClick="@this.ToggleSecurityCard" />
</MudTooltip>
<MudPopover Open="@this.showSecurityCard"
AnchorOrigin="Origin.BottomRight"
TransformOrigin="Origin.BottomLeft"
OverflowBehavior="OverflowBehavior.FlipAlways"
DropShadow="@true"
Class="border-solid border-4 rounded-lg"
Style="@this.GetPopoverStyle()">
<MudCard Elevation="2" Outlined Style="max-width: min(42rem, 90vw);">
<MudCardHeader>
<CardHeaderAvatar>
<MudAvatar Color="@state.AuditColor" Variant="Variant.Filled" Size="Size.Large">
<MudIcon Icon="@state.AuditIcon" Size="Size.Medium" />
</MudAvatar>
</CardHeaderAvatar>
<CardHeaderContent>
<div class="d-flex align-center gap-2">
<MudText Typo="Typo.h6">@T("Assistant Security")</MudText>
<MudChip T="string" Size="Size.Small" Variant="Variant.Filled" Color="@state.AuditColor">
@state.AuditLabel
</MudChip>
@if (!string.IsNullOrWhiteSpace(state.AvailabilityLabel))
{
<MudChip T="string" Size="Size.Small" Variant="Variant.Outlined" Color="@state.AvailabilityColor" Icon="@state.AvailabilityIcon">
@state.AvailabilityLabel
</MudChip>
}
</div>
<MudText Typo="Typo.body2" Class="mud-text-secondary">
@state.Headline
</MudText>
</CardHeaderContent>
<CardHeaderActions>
<MudTooltip Text="@T("Show or hide the detailed security information.")">
<MudIconButton Icon="@Icons.Material.Filled.ExpandMore" OnClick="@this.ToggleDetails" />
</MudTooltip>
</CardHeaderActions>
</MudCardHeader>
<MudCardContent Class="pt-0 pb-2">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="4" Class="flex-wrap">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
<MudIcon Icon="@Icons.Material.Filled.Speed" Size="Size.Small" />
<MudText Typo="Typo.body2">@T("Confidence"):</MudText>
<MudProgressLinear Color="@state.AuditColor"
Value="@this.GetConfidencePercentage()"
Rounded="@true"
Size="Size.Medium"
Style="width: 80px; min-width: 80px;" />
<MudText Typo="Typo.caption" Class="mud-text-secondary">
@this.GetConfidenceLabel()
</MudText>
</MudStack>
<MudDivider Vertical="@true" FlexItem="@true" />
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
<MudIcon Icon="@Icons.Material.Filled.BugReport" Size="Size.Small" Color="@state.AuditColor" />
<MudText Typo="Typo.body2">@this.GetFindingSummary()</MudText>
</MudStack>
<MudDivider Vertical="@true" FlexItem="@true" />
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="1">
<MudIcon Icon="@Icons.Material.Filled.Schedule" Size="Size.Small" />
<MudText Typo="Typo.body2" Class="mud-text-secondary">
@this.GetAuditTimestampLabel()
</MudText>
</MudStack>
</MudStack>
</MudCardContent>
<MudCollapse Expanded="@this.showDetails">
<MudDivider />
<MudCardContent>
<MudStack Spacing="3">
<MudAlert Severity="@this.GetStatusSeverity()" Variant="Variant.Outlined" Dense="@true">
@state.Description
</MudAlert>
<MudPaper Outlined="true" Class="pa-2">
<div class="d-flex align-center justify-space-between gap-2">
<MudText Typo="Typo.subtitle2">@T("Technical Details")</MudText>
<MudIconButton Icon="@(this.showMetadata ? Icons.Material.Filled.ExpandLess : Icons.Material.Filled.ExpandMore)"
Size="Size.Small"
OnClick="@this.ToggleMetadata" />
</div>
<MudCollapse Expanded="@this.showMetadata">
<MudSimpleTable Dense="@true" Hover="@true" Bordered="@true" Striped="@true" Style="overflow-x: auto;">
<tbody>
<tr>
<td style="width: 180px;">
<MudText Typo="Typo.body2"><b>@T("Plugin ID")</b></MudText>
</td>
<td><code style="font-size: 0.8rem;">@this.Plugin.Id</code></td>
</tr>
<tr>
<td>
<MudText Typo="Typo.body2"><b>@T("Current hash")</b></MudText>
</td>
<td><code style="font-size: 0.8rem;">@GetShortHash(state.CurrentHash)</code></td>
</tr>
@if (state.Audit is not null)
{
<tr>
<td>
<MudText Typo="Typo.body2"><b>@T("Audit hash")</b></MudText>
</td>
<td><code style="font-size: 0.8rem;">@GetShortHash(state.Audit.PluginHash)</code></td>
</tr>
<tr>
<td>
<MudText Typo="Typo.body2"><b>@T("Audit provider")</b></MudText>
</td>
<td><MudText Typo="Typo.body2">@this.GetAuditProviderLabel()</MudText></td>
</tr>
<tr>
<td>
<MudText Typo="Typo.body2"><b>@T("Audited at")</b></MudText>
</td>
<td><MudText Typo="Typo.body2">@this.FormatFileTimestamp(state.Audit.AuditedAtUtc.ToLocalTime().DateTime)</MudText></td>
</tr>
<tr>
<td>
<MudText Typo="Typo.body2"><b>@T("Audit level")</b></MudText>
</td>
<td><MudText Typo="Typo.body2">@state.AuditLabel</MudText></td>
</tr>
<tr>
<td>
<MudText Typo="Typo.body2"><b>@T("Availability")</b></MudText>
</td>
<td><MudText Typo="Typo.body2">@state.AvailabilityLabel</MudText></td>
</tr>
}
<tr>
<td>
<MudText Typo="Typo.body2"><b>@T("Required minimum")</b></MudText>
</td>
<td><MudText Typo="Typo.body2">@state.Settings.MinimumLevel.GetName()</MudText></td>
</tr>
</tbody>
</MudSimpleTable>
</MudCollapse>
</MudPaper>
@if (state.Audit is null)
{
<MudAlert Severity="Severity.Info" Variant="Variant.Text" Dense="@true">
@T("No stored audit details are available yet.")
</MudAlert>
}
else if (state.Audit.Findings.Count == 0)
{
<MudAlert Severity="Severity.Success" Variant="Variant.Text" Dense="@true">
@T("No security findings were stored for this assistant plugin.")
</MudAlert>
}
else
{
<div style="max-height: min(22rem, 45vh); overflow-y: auto; padding-right: 0.25rem;">
<MudStack Spacing="2">
@foreach (var finding in state.Audit.Findings)
{
<MudAlert Severity="@finding.Severity.GetSeverity()" Variant="Variant.Text" Dense="@true">
<strong>@finding.Category</strong><span>: @finding.Description</span>
@if (!string.IsNullOrWhiteSpace(finding.Location))
{
<div>
<MudText Typo="Typo.caption">@finding.Location</MudText>
</div>
}
</MudAlert>
}
</MudStack>
</div>
}
</MudStack>
</MudCardContent>
</MudCollapse>
<MudCardActions>
<MudButton Variant="Variant.Outlined" Color="Color.Primary" OnClick="@this.OpenAuditDialogAsync">
@state.ActionLabel
</MudButton>
<MudButton Variant="Variant.Text" OnClick="@this.HideSecurityCard">
@T("Close")
</MudButton>
</MudCardActions>
</MudCard>
</MudPopover>
</div>
}
@@ -0,0 +1,147 @@
using System.Globalization;
using AIStudio.Dialogs;
using AIStudio.Tools.PluginSystem.Assistants;
using Microsoft.AspNetCore.Components;
using DialogOptions = AIStudio.Dialogs.DialogOptions;
namespace AIStudio.Components;
public partial class AssistantPluginSecurityCard : MSGComponentBase
{
[Parameter]
public PluginAssistants? Plugin { get; set; }
[Parameter]
public bool Compact { get; set; }
[Inject]
private IDialogService DialogService { get; init; } = null!;
private PluginAssistantSecurityState SecurityState => this.Plugin is null
? new PluginAssistantSecurityState()
: PluginAssistantSecurityResolver.Resolve(this.SettingsManager, this.Plugin);
private CultureInfo currentCultureInfo = CultureInfo.InvariantCulture;
private bool showSecurityCard;
private bool showDetails;
private bool showMetadata;
protected override async Task OnInitializedAsync()
{
var activeLanguagePlugin = await this.SettingsManager.GetActiveLanguagePlugin();
this.currentCultureInfo = CommonTools.DeriveActiveCultureOrInvariant(activeLanguagePlugin.IETFTag);
this.showDetails = !this.Compact;
this.showMetadata = false;
this.ApplyFilters([], [ Event.CONFIGURATION_CHANGED, Event.PLUGINS_RELOADED ]);
await base.OnInitializedAsync();
}
private async Task OpenAuditDialogAsync()
{
if (this.Plugin is null)
return;
var parameters = new DialogParameters<AssistantPluginAuditDialog>
{
{ x => x.PluginId, this.Plugin.Id },
};
var dialog = await this.DialogService.ShowAsync<AssistantPluginAuditDialog>(this.T("Assistant Audit"), parameters, DialogOptions.FULLSCREEN);
var result = await dialog.Result;
if (result is null || result.Canceled || result.Data is not AssistantPluginAuditDialogResult auditResult)
return;
if (auditResult.Audit is not null)
UpsertAudit(this.SettingsManager.ConfigurationData.AssistantPluginAudits, auditResult.Audit);
if (auditResult.ActivatePlugin && !this.SettingsManager.ConfigurationData.EnabledPlugins.Contains(this.Plugin.Id))
this.SettingsManager.ConfigurationData.EnabledPlugins.Add(this.Plugin.Id);
await this.SettingsManager.StoreSettings();
await this.SendMessage(Event.CONFIGURATION_CHANGED, true);
}
protected override Task ProcessIncomingMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data) where T : default
{
if (triggeredEvent is Event.CONFIGURATION_CHANGED or Event.PLUGINS_RELOADED)
return this.InvokeAsync(this.StateHasChanged);
return Task.CompletedTask;
}
private void ToggleSecurityCard() => this.showSecurityCard = !this.showSecurityCard;
private void HideSecurityCard() => this.showSecurityCard = false;
private void ToggleDetails() => this.showDetails = !this.showDetails;
private void ToggleMetadata() => this.showMetadata = !this.showMetadata;
private static void UpsertAudit(List<PluginAssistantAudit> audits, PluginAssistantAudit audit)
{
var existingIndex = audits.FindIndex(x => x.PluginId == audit.PluginId);
if (existingIndex >= 0)
audits[existingIndex] = audit;
else
audits.Add(audit);
}
private string FormatFileTimestamp(DateTime timestamp) => CommonTools.FormatTimestampToGeneral(timestamp, this.currentCultureInfo);
private string GetPopoverStyle() => $"border-color: {this.GetStatusBorderColor()};";
private double GetConfidencePercentage()
{
var confidence = this.SecurityState.Audit?.Confidence ?? 0f;
if (confidence <= 1)
confidence *= 100;
return Math.Clamp(confidence, 0, 100);
}
private string GetConfidenceLabel() => $"{this.GetConfidencePercentage():0}%";
private string GetFindingSummary()
{
var count = this.SecurityState.Audit?.Findings.Count ?? 0;
return string.Format(this.T("{0} Finding(s)"), count);
}
private string GetAuditTimestampLabel()
{
var auditedAt = this.SecurityState.Audit?.AuditedAtUtc;
return auditedAt is null
? this.T("No audit yet")
: this.FormatFileTimestamp(auditedAt.Value.ToLocalTime().DateTime);
}
private string GetAuditProviderLabel()
{
var providerName = this.SecurityState.Audit?.AuditProviderName;
return string.IsNullOrWhiteSpace(providerName) ? this.T("Unknown") : providerName;
}
private static string GetShortHash(string hash)
{
if (string.IsNullOrWhiteSpace(hash) || hash.Length <= 16)
return hash;
return $"{hash[..8]}...{hash[^8..]}";
}
private Severity GetStatusSeverity() => this.SecurityState.AuditColor switch
{
Color.Success => Severity.Success,
Color.Warning => Severity.Warning,
Color.Error => Severity.Error,
_ => Severity.Info,
};
private string GetStatusBorderColor() => this.SecurityState.AuditColor switch
{
Color.Success => "var(--mud-palette-success)",
Color.Warning => "var(--mud-palette-warning)",
Color.Error => "var(--mud-palette-error)",
_ => "var(--mud-palette-info)",
};
}
@@ -13,7 +13,7 @@
var block = blocks[i];
var isLastBlock = i == blocks.Count - 1;
var isSecondLastBlock = i == blocks.Count - 2;
@if (!block.HideFromUser)
@if (block is { HideFromUser: false, Content: not null })
{
<ContentBlockComponent
@key="@block"
@@ -0,0 +1,52 @@
<MudStack Row="true" Class='@MergeClasses(this.Class, "mb-3")' Style="@this.Style">
@if (this.IsMultiselect)
{
<MudSelect
T="string"
SelectedValues="@this.SelectedValues"
SelectedValuesChanged="@this.OnSelectedValuesChanged"
MultiSelectionTextFunc="@this.GetMultiSelectionText"
Label="@this.Label"
HelperText="@this.HelperText"
Placeholder="@this.Default.Display"
OpenIcon="@this.OpenIcon"
CloseIcon="@this.CloseIcon"
Adornment="@this.IconPosition"
AdornmentColor="@this.IconColor"
Variant="@this.Variant"
Margin="Margin.Normal"
MultiSelection="@true"
SelectAll="@this.HasSelectAll"
SelectAllText="@this.SelectAllText">
@foreach (var item in this.GetRenderedItems())
{
<MudSelectItem Value="@item.Value">
@item.Display
</MudSelectItem>
}
</MudSelect>
}
else
{
<MudSelect
T="string"
Value="@this.Value"
ValueChanged="@(val => this.OnValueChanged(val))"
Label="@this.Label"
HelperText="@this.HelperText"
Placeholder="@this.Default.Display"
OpenIcon="@this.OpenIcon"
CloseIcon="@this.CloseIcon"
Adornment="@this.IconPosition"
AdornmentColor="@this.IconColor"
Variant="@this.Variant"
Margin="Margin.Normal">
@foreach (var item in this.GetRenderedItems())
{
<MudSelectItem Value="@item.Value">
@item.Display
</MudSelectItem>
}
</MudSelect>
}
</MudStack>
@@ -0,0 +1,130 @@
using AIStudio.Tools.PluginSystem.Assistants.DataModel;
using Microsoft.AspNetCore.Components;
namespace AIStudio.Components
{
public partial class DynamicAssistantDropdown : ComponentBase
{
[Parameter]
public List<AssistantDropdownItem> Items { get; set; } = new();
[Parameter]
public AssistantDropdownItem Default { get; set; } = new();
[Parameter]
public string Value { get; set; } = string.Empty;
[Parameter]
public EventCallback<string> ValueChanged { get; set; }
[Parameter]
public HashSet<string> SelectedValues { get; set; } = [];
[Parameter]
public EventCallback<HashSet<string>> SelectedValuesChanged { get; set; }
[Parameter]
public string Label { get; set; } = string.Empty;
[Parameter]
public string HelperText { get; set; } = string.Empty;
[Parameter]
public Func<string, string?> ValidateSelection { get; set; } = _ => null;
[Parameter]
public string OpenIcon { get; set; } = Icons.Material.Filled.ArrowDropDown;
[Parameter]
public string CloseIcon { get; set; } = Icons.Material.Filled.ArrowDropUp;
[Parameter]
public Color IconColor { get; set; } = Color.Default;
[Parameter]
public Adornment IconPosition { get; set; } = Adornment.End;
[Parameter]
public Variant Variant { get; set; } = Variant.Outlined;
[Parameter]
public bool IsMultiselect { get; set; }
[Parameter]
public bool HasSelectAll { get; set; }
[Parameter]
public string SelectAllText { get; set; } = string.Empty;
[Parameter]
public string Class { get; set; } = string.Empty;
[Parameter]
public string Style { get; set; } = string.Empty;
private async Task OnValueChanged(string newValue)
{
if (this.Value != newValue)
{
this.Value = newValue;
await this.ValueChanged.InvokeAsync(newValue);
}
}
private async Task OnSelectedValuesChanged(IEnumerable<string?>? newValues)
{
var updatedValues = newValues?
.Where(value => !string.IsNullOrWhiteSpace(value))
.Select(value => value!)
.ToHashSet(StringComparer.Ordinal) ?? [];
if (this.SelectedValues.SetEquals(updatedValues))
return;
this.SelectedValues = updatedValues;
await this.SelectedValuesChanged.InvokeAsync(updatedValues);
}
private List<AssistantDropdownItem> GetRenderedItems()
{
var items = this.Items;
if (string.IsNullOrWhiteSpace(this.Default.Value))
return items;
if (items.Any(item => string.Equals(item.Value, this.Default.Value, StringComparison.Ordinal)))
return items;
return [this.Default, .. items];
}
private string GetMultiSelectionText(List<string?>? selectedValues)
{
if (selectedValues is null || selectedValues.Count == 0)
return this.Default.Display;
var labels = selectedValues
.Where(value => !string.IsNullOrWhiteSpace(value))
.Select(value => this.ResolveDisplayText(value!))
.Where(value => !string.IsNullOrWhiteSpace(value))
.ToList();
return labels.Count == 0 ? this.Default.Display : string.Join(", ", labels);
}
private string ResolveDisplayText(string value)
{
var item = this.GetRenderedItems().FirstOrDefault(item => string.Equals(item.Value, value, StringComparison.Ordinal));
return item?.Display ?? value;
}
private static string MergeClasses(string custom, string fallback)
{
var trimmedCustom = custom.Trim();
var trimmedFallback = fallback.Trim();
if (string.IsNullOrEmpty(trimmedCustom))
return trimmedFallback;
return string.IsNullOrEmpty(trimmedFallback) ? trimmedCustom : $"{trimmedCustom} {trimmedFallback}";
}
}
}
@@ -0,0 +1,16 @@
@using AIStudio.Settings
@inherits SettingsPanelBase
<ExpansionPanel HeaderIcon="@Icons.Material.Filled.Policy" HeaderText="@T("Agent: Security Audit for external Assistants")">
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
<MudText Typo="Typo.body1" Class="mb-3">
@T("This Agent audits newly installed or updated external Plugin-Assistant for security risks before they are activated and stores the latest audit card until the plugin manifest changes.")
</MudText>
<ConfigurationOption OptionDescription="@T("Require a security audit before activating external Assistants?")" LabelOn="@T("External Assistants must be audited before activation")" LabelOff="@T("External Assistant can be activated without an audit")" State="@(() => this.SettingsManager.ConfigurationData.AssistantPluginAudit.RequireAuditBeforeActivation)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.AssistantPluginAudit.RequireAuditBeforeActivation = updatedState)" />
<ConfigurationProviderSelection Data="@this.AvailableLLMProvidersFunc()" SelectedValue="@(() => this.SettingsManager.ConfigurationData.AssistantPluginAudit.PreselectedAgentProvider)" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.AssistantPluginAudit.PreselectedAgentProvider = selectedValue)" HelpText="@(() => T("Optionally choose a dedicated provider for assistant plugin audits. When left empty, AI Studio falls back to the app-wide default provider."))" />
<ConfigurationSelect OptionDescription="@T("Minimum required audit level")" SelectedValue="@(() => this.SettingsManager.ConfigurationData.AssistantPluginAudit.MinimumLevel)" Data="@ConfigurationSelectDataFactory.GetAssistantAuditLevelsData()" SelectionUpdate="@(selectedValue => this.SettingsManager.ConfigurationData.AssistantPluginAudit.MinimumLevel = selectedValue)" OptionHelp="@T("External Assistants rated below this audit level are treated as insufficiently reviewed.")" />
<ConfigurationOption OptionDescription="@T("Block activation below the minimum Audit-Level?")" LabelOn="@T("Activation is blocked below the minimum Audit-Level")" LabelOff="@T("Users may still activate plugins below the minimum Audit-Level")" State="@(() => this.SettingsManager.ConfigurationData.AssistantPluginAudit.BlockActivationBelowMinimum)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.AssistantPluginAudit.BlockActivationBelowMinimum = updatedState)"
OptionHelp="@T("The audit shows you all security risks and information, if you consider this rating false at your own discretion, you can decide to install it anyway (not recommended).")"/>
<ConfigurationOption OptionDescription="@T("Automatically audit new or updated plugins in the background?")" LabelOn="@T("Security audit is automatically done in the background")" LabelOff="@T("Security audit is done manually by the user")" State="@(() => this.SettingsManager.ConfigurationData.AssistantPluginAudit.AutomaticallyAuditAssistants)" StateUpdate="@(updatedState => this.SettingsManager.ConfigurationData.AssistantPluginAudit.AutomaticallyAuditAssistants = updatedState)" />
</MudPaper>
</ExpansionPanel>
@@ -0,0 +1,3 @@
namespace AIStudio.Components.Settings;
public partial class SettingsPanelAgentAssistantAudit : SettingsPanelBase;
@@ -24,7 +24,7 @@ else
case TreeItemData treeItem:
@if (treeItem.Type is TreeItemType.LOADING)
{
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@false" Items="@treeItem.Children">
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@false" Items="@treeItem.Children!">
<BodyContent>
<MudSkeleton Width="85%" Height="22px"/>
</BodyContent>
@@ -32,7 +32,7 @@ else
}
else if (treeItem.Type is TreeItemType.CHAT)
{
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@treeItem.Expandable" Items="@treeItem.Children" OnClick="@(() => this.LoadChatAsync(treeItem.Path, true))">
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@treeItem.Expandable" Items="@treeItem.Children!" OnClick="@(() => this.LoadChatAsync(treeItem.Path, true))">
<BodyContent>
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
<MudText Style="justify-self: start;">
@@ -65,7 +65,7 @@ else
}
else if (treeItem.Type is TreeItemType.WORKSPACE)
{
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@treeItem.Expandable" Items="@treeItem.Children" OnClick="@(() => this.OnWorkspaceClicked(treeItem))">
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@treeItem.Expandable" Items="@treeItem.Children!" OnClick="@(() => this.OnWorkspaceClicked(treeItem))">
<BodyContent>
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
<MudText Style="justify-self: start;">
@@ -86,7 +86,7 @@ else
}
else
{
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@treeItem.Expandable" Items="@treeItem.Children">
<MudTreeViewItem T="ITreeItem" Icon="@treeItem.Icon" Value="@item.Value" Expanded="@item.Expanded" CanExpand="@treeItem.Expandable" Items="@treeItem.Children!">
<BodyContent>
<div style="display: grid; grid-template-columns: 1fr auto; align-items: center; width: 100%">
<MudText Style="justify-self: start;">