Added support for organization-approved assistant plugins (#821)
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,app,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,updater, appimage) (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,app,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,updater, appimage) (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:
Peer Hogeterp authored and GitHub committed 2026-07-05 15:20:29 +02:00
1 parent 972ed73fe8
commit 13cc2f837a
35 files changed
+954 -62

No files matched your search

@@ -2,8 +2,8 @@ using System.Collections.Immutable;
using AIStudio.Tools.PluginSystem.Assistants.DataModel;
using AIStudio.Tools.PluginSystem.Assistants.DataModel.Layout;
using Lua;
using System.Security.Cryptography;
using System.Text;
using AssistantPluginHash = SharedTools.AssistantPluginHash;
namespace AIStudio.Tools.PluginSystem.Assistants;
@@ -36,6 +36,9 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
public bool AllowProfiles { get; private set; } = true;
public bool HasEmbeddedProfileSelection { get; private set; }
public bool HasCustomPromptBuilder => this.buildPromptFunction is not null;
public AssistantPluginLaunchBehavior LaunchBehavior { get; private set; }
public string LaunchWorkspaceName { get; private set; } = string.Empty;
public bool StartsChatDirectly => this.LaunchBehavior is AssistantPluginLaunchBehavior.OPEN_WORKSPACE_CHAT_BY_NAME;
public const int TEXT_AREA_MAX_VALUE = 524288;
private LuaFunction? buildPromptFunction;
@@ -61,6 +64,8 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
message = string.Empty;
this.HasEmbeddedProfileSelection = false;
this.buildPromptFunction = null;
this.LaunchBehavior = AssistantPluginLaunchBehavior.NONE;
this.LaunchWorkspaceName = string.Empty;
this.RegisterLuaHelpers();
@@ -123,6 +128,12 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
this.SubmitText = assistantSubmitText;
this.AllowProfiles = assistantAllowProfiles;
if (!this.TryReadLaunchConfiguration(assistantTable, out var launchConfigIssue))
{
message = launchConfigIssue;
return false;
}
// Ensure that the UI table exists nested in the ASSISTANT table and is a valid Lua table:
if (!assistantTable.TryGetValue("UI", out var uiVal) || !uiVal.TryRead<LuaTable>(out var uiTable))
{
@@ -140,6 +151,51 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
return true;
}
private bool TryReadLaunchConfiguration(LuaTable assistantTable, out string message)
{
message = string.Empty;
if (!assistantTable.TryGetValue("LaunchBehavior", out var launchBehaviorValue))
return true;
if (!launchBehaviorValue.TryRead<string>(out var launchBehaviorText) ||
!Enum.TryParse<AssistantPluginLaunchBehavior>(launchBehaviorText, true, out var launchBehavior))
{
message = TB("The ASSISTANT table contains an invalid LaunchBehavior value.");
return false;
}
this.LaunchBehavior = launchBehavior;
if (launchBehavior is AssistantPluginLaunchBehavior.NONE)
return true;
switch (launchBehavior)
{
case AssistantPluginLaunchBehavior.OPEN_WORKSPACE_CHAT_BY_NAME:
if (!assistantTable.TryGetValue("WorkspaceName", out var workspaceNameValue) ||
!workspaceNameValue.TryRead<string>(out var workspaceName))
{
message = TB("The ASSISTANT table contains the LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME' but no valid WorkspaceName.");
return false;
}
workspaceName = workspaceName.Trim();
if (string.IsNullOrWhiteSpace(workspaceName))
{
message = TB("The ASSISTANT table contains an empty WorkspaceName for LaunchBehavior 'OPEN_WORKSPACE_CHAT_BY_NAME'.");
return false;
}
this.LaunchWorkspaceName = workspaceName;
return true;
default:
message = TB("The ASSISTANT table contains an unsupported LaunchBehavior value.");
return false;
}
}
public async Task<string?> TryBuildPromptAsync(LuaTable input, CancellationToken cancellationToken = default)
{
if (this.buildPromptFunction is null)
@@ -224,33 +280,7 @@ public sealed class PluginAssistants(bool isInternal, LuaState state, PluginType
/// sequence of relative path length, relative path, content length, and content
/// for each file in ordinal path order.
/// </summary>
public string ComputeAuditHash()
{
var luaFiles = this.ReadAllLuaFiles();
if (luaFiles.Count == 0)
return string.Empty;
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);
}
public string ComputeAuditHash() => AssistantPluginHash.Compute(this.PluginPath);
private static string BuildSecureSystemPrompt(string pluginSystemPrompt)
{