From 1fdf7a0c363bf1eebb95536339780fc884e5a7b4 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Mon, 14 Sep 2026 09:24:07 +0200 Subject: [PATCH] Let the Assistant Builder generate launchers without a workspace --- .../Builder/AssistantBuilderChatLaunchMetadata.cs | 4 ++++ .../AssistantBuilderLuaResponse.schema.json | 4 +--- .../Assistants/Builder/LuaResponse.Parse.cs | 7 ++++++- .../Services/AssistantBuilderChatLaunchRequest.cs | 2 +- .../Services/AssistantPluginGenerationService.cs | 14 ++++++++++---- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/app/MindWork AI Studio/Assistants/Builder/AssistantBuilderChatLaunchMetadata.cs b/app/MindWork AI Studio/Assistants/Builder/AssistantBuilderChatLaunchMetadata.cs index 477b59e3..f0a869e3 100644 --- a/app/MindWork AI Studio/Assistants/Builder/AssistantBuilderChatLaunchMetadata.cs +++ b/app/MindWork AI Studio/Assistants/Builder/AssistantBuilderChatLaunchMetadata.cs @@ -2,6 +2,10 @@ namespace AIStudio.Assistants.Builder; internal sealed class AssistantBuilderChatLaunchMetadata { + /// + /// The workspace the chat is created in. A model leaves this field out for a launcher that + /// opens a chat without a workspace, which is why it stays empty rather than null. + /// public string WorkspaceName { get; init; } = string.Empty; public string? ProviderId { get; init; } public string? ProfileId { get; init; } diff --git a/app/MindWork AI Studio/Assistants/Builder/AssistantBuilderLuaResponse.schema.json b/app/MindWork AI Studio/Assistants/Builder/AssistantBuilderLuaResponse.schema.json index 4c8cd57c..b974898c 100644 --- a/app/MindWork AI Studio/Assistants/Builder/AssistantBuilderLuaResponse.schema.json +++ b/app/MindWork AI Studio/Assistants/Builder/AssistantBuilderLuaResponse.schema.json @@ -131,11 +131,9 @@ "chatLaunch": { "type": "object", "additionalProperties": false, - "required": [ - "workspace_name" - ], "properties": { "workspace_name": { + "description": "Omit this field for a launcher that opens a chat without a workspace.", "type": "string", "minLength": 1 }, diff --git a/app/MindWork AI Studio/Assistants/Builder/LuaResponse.Parse.cs b/app/MindWork AI Studio/Assistants/Builder/LuaResponse.Parse.cs index b58480e4..a3313aff 100644 --- a/app/MindWork AI Studio/Assistants/Builder/LuaResponse.Parse.cs +++ b/app/MindWork AI Studio/Assistants/Builder/LuaResponse.Parse.cs @@ -124,7 +124,12 @@ internal sealed partial class LuaResponse private static bool IsValidChatLaunchMetadata(AssistantBuilderChatLaunchMetadata? launch) { - if (launch is null || string.IsNullOrWhiteSpace(launch.WorkspaceName)) + // + // A missing workspace name describes a launcher that opens a chat without a workspace, so + // only the launch block itself is mandatory here. Whether the name matches the plugin the + // model wrote is decided later, by comparing both. + // + if (launch is null) return false; if (!IsOptionalGuid(launch.ProviderId, allowEmpty: false) || diff --git a/app/MindWork AI Studio/Tools/Services/AssistantBuilderChatLaunchRequest.cs b/app/MindWork AI Studio/Tools/Services/AssistantBuilderChatLaunchRequest.cs index 1734c241..2747c785 100644 --- a/app/MindWork AI Studio/Tools/Services/AssistantBuilderChatLaunchRequest.cs +++ b/app/MindWork AI Studio/Tools/Services/AssistantBuilderChatLaunchRequest.cs @@ -3,7 +3,7 @@ namespace AIStudio.Tools.Services; /// /// The chat a direct chat launcher tile opens, as chosen in the Assistant Builder. /// -/// The workspace the chat is created in. +/// The workspace the chat is created in; an empty name opens a chat without a workspace instead. /// The provider to preselect, or null for the chat default. /// The profile to preselect; the empty GUID selects no profile. /// The chat template to preselect; the empty GUID selects none. diff --git a/app/MindWork AI Studio/Tools/Services/AssistantPluginGenerationService.cs b/app/MindWork AI Studio/Tools/Services/AssistantPluginGenerationService.cs index bf1a8b03..f1bc2947 100644 --- a/app/MindWork AI Studio/Tools/Services/AssistantPluginGenerationService.cs +++ b/app/MindWork AI Studio/Tools/Services/AssistantPluginGenerationService.cs @@ -358,6 +358,7 @@ public sealed class AssistantPluginGenerationService(ToolRegistry toolRegistry, - Write description as one sentence that says which chat this tile opens and what it is for. Do not describe an input form, a prompt, or a submit button, because a launcher has none. - Write all three texts in the language of the approved draft. - Do not mention workspace names, provider names, profile names, template names, data source IDs, or tool IDs in any of the three texts. + - When the chat launch names no workspace, the tile opens a chat that belongs to no workspace and disappears again. You may say the chat is temporary, but never invent a workspace name. - Do not return Markdown, code fences, explanations, or text outside the JSON object. """; @@ -512,6 +513,7 @@ public sealed class AssistantPluginGenerationService(ToolRegistry toolRegistry, - Explain omitted provider, profile, template, data-source, or tool values as using the normal chat defaults. - In the {{TB("Tools")}} section, say what the preselected tools let the chat do and that users may change the selection once the chat is open. - Explain the empty profile/template GUID as explicitly selecting no profile/template. + - When the ChatLaunch names no workspace, write in the {{TB("Workspace")}} section that the tile opens a chat without a workspace: it is kept among the temporary chats and is deleted by the maintenance the user configured for them. Never invent a workspace name. - Do not propose UI components, submit behavior, BuildPrompt, or a plugin SystemPrompt for a chat launcher. """; @@ -620,10 +622,11 @@ public sealed class AssistantPluginGenerationService(ToolRegistry toolRegistry, - Keep TYPE = "ASSISTANT". - Keep the assistant locally managed. DEPLOYED_USING_CONFIG_SERVER must not be true. {{builderMetadataRule}} - - Set assistant.kind to "CHAT_LAUNCHER" exactly when the revised ASSISTANT table uses LaunchBehavior = "OPEN_WORKSPACE_CHAT_BY_NAME"; otherwise set it to "FORM". + - Set assistant.kind to "CHAT_LAUNCHER" exactly when the revised ASSISTANT table uses LaunchBehavior = "OPEN_WORKSPACE_CHAT_BY_NAME" or "OPEN_TEMPORARY_CHAT"; otherwise set it to "FORM". - For a form assistant, include system_prompt, submit_text, and allow_ai_studio_profiles in the JSON assistant object and omit launch. Include tool_ids exactly when the revised ASSISTANT table carries ToolIds. - Change ASSISTANT.ToolIds only when the requested change asks for it. Use only tool IDs from the "Available tools" list in the plugin context for tools you add; never invent an ID. Drop the field entirely rather than writing an empty list. - - For a chat launcher, include launch with the exact WorkspaceName and optional ProviderId, ProfileId, ChatTemplateId, DataSourceIds, and ToolIds values from the revised ASSISTANT table; omit system_prompt, submit_text, and allow_ai_studio_profiles. + - For a chat launcher, include launch with the optional ProviderId, ProfileId, ChatTemplateId, DataSourceIds, and ToolIds values from the revised ASSISTANT table; omit system_prompt, submit_text, and allow_ai_studio_profiles. Include workspace_name with the exact WorkspaceName exactly when the table uses OPEN_WORKSPACE_CHAT_BY_NAME, and omit it for OPEN_TEMPORARY_CHAT. + - Keep the LaunchBehavior a launcher already has unless the requested change asks to add or drop its workspace. OPEN_WORKSPACE_CHAT_BY_NAME requires a WorkspaceName, and OPEN_TEMPORARY_CHAT must not carry one. - A chat launcher must not include SystemPrompt, SubmitText, AllowProfiles, BuildPrompt, or UI in its ASSISTANT table. - Preserve an empty profile or template GUID when it explicitly means no profile or no template. Do not emit empty provider or data-source GUIDs. - Preserve existing behavior unless the requested change explicitly modifies it. @@ -757,8 +760,11 @@ public sealed class AssistantPluginGenerationService(ToolRegistry toolRegistry, if (launch is null) return true; - if (string.IsNullOrWhiteSpace(launch.WorkspaceName) || - !IsOptionalGuid(launch.ProviderId, allowEmpty: false) || + // + // No workspace name is a choice rather than a gap: the launcher then opens a chat that + // belongs to no workspace. Only the remaining fields have a shape to check. + // + if (!IsOptionalGuid(launch.ProviderId, allowEmpty: false) || !IsOptionalGuid(launch.ProfileId, allowEmpty: true) || !IsOptionalGuid(launch.ChatTemplateId, allowEmpty: true)) return false;