From 0375318b1b9e062ea2e0551a33398fb49b733ce3 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Wed, 4 Nov 2026 11:48:11 +0100 Subject: [PATCH] Moved the build to the .NET SDK v10.0.403 (#1156) --- .github/workflows/build-and-release.yml | 2 +- app/AGENTS.md | 2 +- .../Tools/JsRuntimeExtensions.cs | 205 +++++++++--------- .../Mailboxes/MailToolArguments.cs | 47 ++-- .../SourceCodeRules/SourceCodeRules.csproj | 10 +- .../ClassicExtensionMethodAnalyzer.cs | 10 +- .../SourceGeneratedMappings.csproj | 9 +- .../ClassicExtensionMethodAnalyzerTests.cs | 6 +- app/Tests/Tests.csproj | 3 +- metadata.txt | 2 +- 10 files changed, 143 insertions(+), 153 deletions(-) diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index f63b8ecd2..208875356 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -793,7 +793,7 @@ jobs: - name: Read the toolchain versions from the metadata run: | - # The .NET SDK version. The format is '10.0.114 (commit 0427b9abbe)', + # The .NET SDK version. The format is '10.0.403 (commit 5a2162bea6)', # so we extract the version number alone: dotnet_sdk_version=$(sed -n '4p' metadata.txt | sed 's/[^0-9.]*\([0-9.]*\).*/\1/') diff --git a/app/AGENTS.md b/app/AGENTS.md index 70f1159f4..45254675e 100644 --- a/app/AGENTS.md +++ b/app/AGENTS.md @@ -28,7 +28,7 @@ Key structure: ## Important Development Notes - **Naming conventions** - Constants, enum members, and `static readonly` fields use `UPPER_SNAKE_CASE` such as `MY_CONSTANT`. -- **Extension members** - Declare extension methods in an `extension` block of their static class, one block per receiver, never with a `this` parameter: MWAIS0016 refuses the classic form. The `` of the receiver, and of a generic block its ``, belongs on the block, not on its members. The one exception is a method with a `params` parameter, which stays classic until the SDK in `metadata.txt` carries the fix for dotnet/roslyn#80024: for a member of an extension block, the compiler warns about every argument passed one by one to a `params` parameter (CS8620), see `JsRuntimeExtensions`. +- **Extension members** - Declare extension methods in an `extension` block of their static class, one block per receiver, never with a `this` parameter: MWAIS0016 refuses the classic form. The `` of the receiver, and of a generic block its ``, belongs on the block, not on its members. - **Message Bus** - Singleton event bus for cross-component communication inside the .NET app - **Prompt injection reports** - The user hears once per action about what the filter removed, as soon as that action ends. Content which belongs to an action carries its scope from `PromptInjectionGuardService.BeginAction` down to where it is read or filtered; whatever is filtered without a scope is reported right away. Pass the scope as a parameter, never through a field or an `AsyncLocal`: the service serves the whole app, and the message bus delivers to other receivers in the execution context of whoever sends. - **Paths in stored data** - A stored path to a file which AI Studio keeps below the data directory, such as a transcript or a tokenizer copy, is relative to that directory, so that a backup or a copy of the data directory works on another computer, under another user name, or on another OS. Mark such a property with `[JsonConverter(typeof(DataDirectoryRelativePathConverter))]`, for a positional record parameter with `[property: ...]`; in memory, the path stays absolute. Better still, derive the path from IDs and store none, as `VisualBriefingStore` does. Paths to files of the user stay absolute. Chats stored before v26.10.1 are migrated by a compatibility shim, see `documentation/compatibility-shims/2026-10-relative-data-paths.md`. diff --git a/app/MindWork AI Studio/Tools/JsRuntimeExtensions.cs b/app/MindWork AI Studio/Tools/JsRuntimeExtensions.cs index 118278d19..6715dc0d9 100644 --- a/app/MindWork AI Studio/Tools/JsRuntimeExtensions.cs +++ b/app/MindWork AI Studio/Tools/JsRuntimeExtensions.cs @@ -3,16 +3,11 @@ using AIStudio.Tools.Services; namespace AIStudio.Tools; -// -// The methods with a params parameter stay classic extension methods instead of moving into the -// extension block: for a member of an extension block, the compiler of the .NET 10.0.1xx SDK warns -// about every argument passed one by one to a params parameter (CS8620, dotnet/roslyn#80024). Move -// them once the SDK in metadata.txt carries the fix; MWAIS0016 makes the same exception until then. -// public static class JsRuntimeExtensions { private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(nameof(JsRuntimeExtensions)); + /// The JS runtime to call. extension(IJSRuntime jsRuntime) { public async Task GenerateAndShowDiff(string text1, string text2) @@ -24,115 +19,115 @@ public static class JsRuntimeExtensions { await jsRuntime.InvokeVoidAsync("clearDiv", divId); } - } - /// - /// Calls a JavaScript function which returns nothing, and tolerates a circuit which is already gone. - /// - /// - /// Blazor cannot issue JS interop calls once the browser connection of a circuit is gone. That happens - /// during every reload and while a component gets disposed, so the failure is expected rather than - /// exceptional. Discarding such a call is not an option, though: the discarded task keeps the fault - /// until the finalizer reports it as an unobserved task exception, without any hint at its origin. - /// This method is the one place which knows how to await such a call and what to do with its failure. - /// - /// The JS runtime to call. - /// The name of the JavaScript function. - /// The arguments for the JavaScript function. - /// True when the browser ran the function. Callers which remember what they told the browser - /// must check this: a call which never arrived leaves the browser in its previous state. - public static async ValueTask TryInvokeVoidAsync(this IJSRuntime jsRuntime, string identifier, params object?[]? args) - { - try + /// + /// Calls a JavaScript function which returns nothing, and tolerates a circuit which is already gone. + /// + /// + /// Blazor cannot issue JS interop calls once the browser connection of a circuit is gone. That happens + /// during every reload and while a component gets disposed, so the failure is expected rather than + /// exceptional. Discarding such a call is not an option, though: the discarded task keeps the fault + /// until the finalizer reports it as an unobserved task exception, without any hint at its origin. + /// This method is the one place which knows how to await such a call and what to do with its failure. + /// + /// The name of the JavaScript function. + /// The arguments for the JavaScript function. + /// True when the browser ran the function. Callers which remember what they told the browser + /// must check this: a call which never arrived leaves the browser in its previous state. + public async ValueTask TryInvokeVoidAsync(string identifier, params object?[]? args) { - await jsRuntime.InvokeVoidAsync(identifier, args); - return true; + try + { + await jsRuntime.InvokeVoidAsync(identifier, args); + return true; + } + catch (Exception exception) + { + LogInvocationFailure(exception, identifier); + return false; + } } - catch (Exception exception) + + /// + /// Calls a JavaScript function which returns nothing, unless the circuit is known to be disconnected. + /// + /// + /// Prefer this over the variant without a circuit state wherever the caller knows its circuit. While a + /// browser connection is gone, every single call would otherwise throw, which is needless work for + /// something we already know cannot succeed — a component of a disconnected circuit which keeps + /// rendering would produce one such exception per render. + /// + /// The circuit of the caller. + /// The name of the JavaScript function. + /// The arguments for the JavaScript function. + /// True when the browser ran the function, false when it was skipped or failed. + public async ValueTask TryInvokeVoidAsync(CircuitStateService circuitState, string identifier, params object?[]? args) { - LogInvocationFailure(exception, identifier); - return false; + if (!circuitState.IsConnected) + { + LOGGER.LogDebug("The JS call '{Identifier}' was skipped because the browser connection of the circuit '{CircuitId}' is down.", identifier, circuitState.CircuitId); + return false; + } + + return await jsRuntime.TryInvokeVoidAsync(identifier, args); + } + + /// + /// Calls a JavaScript function which returns a value, unless the circuit is known to be disconnected. + /// + /// + /// The two parts of the result answer two different questions, and callers must keep them apart. + /// Whether the browser ran the function at all comes first: a call which never arrived says nothing + /// about the page, so nobody may act on an answer they did not get. What the function returned is the + /// second question, and there a null is a legitimate answer -- it means the browser looked and found + /// nothing. + /// + /// The circuit of the caller. + /// The name of the JavaScript function. + /// The arguments for the JavaScript function. + /// Whether the browser ran the function, and what it returned. + public async ValueTask<(bool WasInvoked, TValue? Value)> TryInvokeAsync(CircuitStateService circuitState, string identifier, params object?[]? args) + { + if (!circuitState.IsConnected) + { + LOGGER.LogDebug("The JS call '{Identifier}' was skipped because the browser connection of the circuit '{CircuitId}' is down.", identifier, circuitState.CircuitId); + return (false, default); + } + + try + { + return (true, await jsRuntime.InvokeAsync(identifier, args)); + } + catch (Exception exception) + { + LogInvocationFailure(exception, identifier); + return (false, default); + } } } - /// - /// Calls a JavaScript function which returns nothing, unless the circuit is known to be disconnected. - /// - /// - /// Prefer this over the variant without a circuit state wherever the caller knows its circuit. While a - /// browser connection is gone, every single call would otherwise throw, which is needless work for - /// something we already know cannot succeed — a component of a disconnected circuit which keeps - /// rendering would produce one such exception per render. - /// - /// The JS runtime to call. - /// The circuit of the caller. - /// The name of the JavaScript function. - /// The arguments for the JavaScript function. - /// True when the browser ran the function, false when it was skipped or failed. - public static async ValueTask TryInvokeVoidAsync(this IJSRuntime jsRuntime, CircuitStateService circuitState, string identifier, params object?[]? args) - { - if (!circuitState.IsConnected) - { - LOGGER.LogDebug("The JS call '{Identifier}' was skipped because the browser connection of the circuit '{CircuitId}' is down.", identifier, circuitState.CircuitId); - return false; - } - - return await jsRuntime.TryInvokeVoidAsync(identifier, args); - } - - /// - /// Calls a JavaScript function which returns a value, unless the circuit is known to be disconnected. - /// - /// - /// The two parts of the result answer two different questions, and callers must keep them apart. - /// Whether the browser ran the function at all comes first: a call which never arrived says nothing - /// about the page, so nobody may act on an answer they did not get. What the function returned is the - /// second question, and there a null is a legitimate answer -- it means the browser looked and found - /// nothing. - /// - /// The JS runtime to call. - /// The circuit of the caller. - /// The name of the JavaScript function. - /// The arguments for the JavaScript function. - /// Whether the browser ran the function, and what it returned. - public static async ValueTask<(bool WasInvoked, TValue? Value)> TryInvokeAsync(this IJSRuntime jsRuntime, CircuitStateService circuitState, string identifier, params object?[]? args) - { - if (!circuitState.IsConnected) - { - LOGGER.LogDebug("The JS call '{Identifier}' was skipped because the browser connection of the circuit '{CircuitId}' is down.", identifier, circuitState.CircuitId); - return (false, default); - } - - try - { - return (true, await jsRuntime.InvokeAsync(identifier, args)); - } - catch (Exception exception) - { - LogInvocationFailure(exception, identifier); - return (false, default); - } - } - - /// - /// Calls a function of a JavaScript module which returns nothing, and tolerates a circuit which is - /// already gone. See the remarks on the JS runtime variant of this method. - /// /// The JavaScript module to call. - /// The name of the function inside the module. - /// The arguments for the function. - /// True when the browser ran the function, false when it failed. - public static async ValueTask TryInvokeVoidAsync(this IJSObjectReference module, string identifier, params object?[]? args) + extension(IJSObjectReference module) { - try + /// + /// Calls a function of a JavaScript module which returns nothing, and tolerates a circuit which is + /// already gone. See the remarks on the JS runtime variant of this method. + /// + /// The name of the function inside the module. + /// The arguments for the function. + /// True when the browser ran the function, false when it failed. + public async ValueTask TryInvokeVoidAsync(string identifier, params object?[]? args) { - await module.InvokeVoidAsync(identifier, args); - return true; - } - catch (Exception exception) - { - LogInvocationFailure(exception, identifier); - return false; + try + { + await module.InvokeVoidAsync(identifier, args); + return true; + } + catch (Exception exception) + { + LogInvocationFailure(exception, identifier); + return false; + } } } diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolCallingImplementations/Mailboxes/MailToolArguments.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolCallingImplementations/Mailboxes/MailToolArguments.cs index c5b30acd1..5fc767ee8 100644 --- a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolCallingImplementations/Mailboxes/MailToolArguments.cs +++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolCallingImplementations/Mailboxes/MailToolArguments.cs @@ -49,32 +49,29 @@ internal static class MailToolArguments private static readonly string[] SPECIAL_FOLDER_VALUES = [SPECIAL_FOLDER_SENT, SPECIAL_FOLDER_DRAFTS]; - /// - /// Adds the conditions to the arguments a mail tool describes. - /// /// The schema of the tool. - /// What the tool does with the mails, e.g., "search", completing "the mailboxes to ...". - /// The ids of the mailboxes the tool offers, or none while no mailboxes are known. - /// The schema, for further arguments. - /// - /// A classic extension method instead of a member of an extension block: for those, the compiler of - /// the .NET 10.0.1xx SDK warns about every mailbox id passed one by one to the params parameter - /// (CS8620, dotnet/roslyn#80024). Move it once the SDK in metadata.txt carries the fix; MWAIS0016 makes - /// the same exception until then. - /// - public static ToolParameterSchemaBuilder AddMailConditions(this ToolParameterSchemaBuilder builder, string toolAction, params string[] mailboxIds) => builder - .OptionalStringArray(MAILBOX_IDS_ARGUMENT, $"Optional IDs of the mailboxes to {toolAction}, out of those listed in the description of this tool. Leave it out to {toolAction} all of them.", mailboxIds) - .OptionalString(FROM_ARGUMENT, $"Optional part of the address or the name of the sender, such as 'alice@example.org', 'example.org', or 'Alice'. At most {MAX_ADDRESS_CHARACTERS} characters.") - .OptionalString(TO_ARGUMENT, $"Optional part of the address or the name of a recipient in To, Cc, or Bcc. At most {MAX_ADDRESS_CHARACTERS} characters.") - .OptionalString(AFTER_ARGUMENT, "Optional: only mails received at this point in time or later. A date such as 2026-09-01 stands for the beginning of that day in the time zone of the user. A date with a time of day such as 2026-09-01T14:30 is read in that time zone as well, unless it ends with an offset such as +02:00 or with Z.") - .OptionalString(BEFORE_ARGUMENT, "Optional: only mails received before this point in time, in the same forms as the argument after. A date stands for the beginning of that day, so before 2026-09-30 leaves that day out.") - .OptionalBoolean(IS_UNREAD_ARGUMENT, "Optional: true for unread mails only, false for read ones only.") - .OptionalBoolean(IS_FLAGGED_ARGUMENT, "Optional: true for flagged mails only, false for unflagged ones only.") - .OptionalBoolean(IS_ENCRYPTED_ARGUMENT, "Optional: true for encrypted mails only, false for unencrypted ones only. AI Studio cannot read the content of encrypted mails, only their header.") - .OptionalEnum(IMPORTANCE_ARGUMENT, "Optional importance the sender marked the mails with. Mails without such a mark count as normal.", IMPORTANCE_VALUES) - .OptionalBoolean(HAS_ATTACHMENTS_ARGUMENT, "Optional: true for mails with attachments only, false for mails without any.") - .OptionalString(FOLDER_ARGUMENT, $"Optional full path of the folder the mails lie in, exactly as results show it, such as 'INBOX' or 'Archive/2026'. Subfolders are not included. For the sent mails or the drafts, use {SPECIAL_FOLDER_ARGUMENT} instead.") - .OptionalEnum(SPECIAL_FOLDER_ARGUMENT, $"Optional: only the mails the user sent, or only the drafts of the user, however the server names these folders. Cannot be combined with {FOLDER_ARGUMENT}.", SPECIAL_FOLDER_VALUES); + extension(ToolParameterSchemaBuilder builder) + { + /// + /// Adds the conditions to the arguments a mail tool describes. + /// + /// What the tool does with the mails, e.g., "search", completing "the mailboxes to ...". + /// The ids of the mailboxes the tool offers, or none while no mailboxes are known. + /// The schema, for further arguments. + public ToolParameterSchemaBuilder AddMailConditions(string toolAction, params string[] mailboxIds) => builder + .OptionalStringArray(MAILBOX_IDS_ARGUMENT, $"Optional IDs of the mailboxes to {toolAction}, out of those listed in the description of this tool. Leave it out to {toolAction} all of them.", mailboxIds) + .OptionalString(FROM_ARGUMENT, $"Optional part of the address or the name of the sender, such as 'alice@example.org', 'example.org', or 'Alice'. At most {MAX_ADDRESS_CHARACTERS} characters.") + .OptionalString(TO_ARGUMENT, $"Optional part of the address or the name of a recipient in To, Cc, or Bcc. At most {MAX_ADDRESS_CHARACTERS} characters.") + .OptionalString(AFTER_ARGUMENT, "Optional: only mails received at this point in time or later. A date such as 2026-09-01 stands for the beginning of that day in the time zone of the user. A date with a time of day such as 2026-09-01T14:30 is read in that time zone as well, unless it ends with an offset such as +02:00 or with Z.") + .OptionalString(BEFORE_ARGUMENT, "Optional: only mails received before this point in time, in the same forms as the argument after. A date stands for the beginning of that day, so before 2026-09-30 leaves that day out.") + .OptionalBoolean(IS_UNREAD_ARGUMENT, "Optional: true for unread mails only, false for read ones only.") + .OptionalBoolean(IS_FLAGGED_ARGUMENT, "Optional: true for flagged mails only, false for unflagged ones only.") + .OptionalBoolean(IS_ENCRYPTED_ARGUMENT, "Optional: true for encrypted mails only, false for unencrypted ones only. AI Studio cannot read the content of encrypted mails, only their header.") + .OptionalEnum(IMPORTANCE_ARGUMENT, "Optional importance the sender marked the mails with. Mails without such a mark count as normal.", IMPORTANCE_VALUES) + .OptionalBoolean(HAS_ATTACHMENTS_ARGUMENT, "Optional: true for mails with attachments only, false for mails without any.") + .OptionalString(FOLDER_ARGUMENT, $"Optional full path of the folder the mails lie in, exactly as results show it, such as 'INBOX' or 'Archive/2026'. Subfolders are not included. For the sent mails or the drafts, use {SPECIAL_FOLDER_ARGUMENT} instead.") + .OptionalEnum(SPECIAL_FOLDER_ARGUMENT, $"Optional: only the mails the user sent, or only the drafts of the user, however the server names these folders. Cannot be combined with {FOLDER_ARGUMENT}.", SPECIAL_FOLDER_VALUES); + } /// /// The value of the importance argument which stands for the given importance. diff --git a/app/SourceCodeRules/SourceCodeRules/SourceCodeRules.csproj b/app/SourceCodeRules/SourceCodeRules/SourceCodeRules.csproj index 6e8c73ca5..f83e2da89 100644 --- a/app/SourceCodeRules/SourceCodeRules/SourceCodeRules.csproj +++ b/app/SourceCodeRules/SourceCodeRules/SourceCodeRules.csproj @@ -20,10 +20,12 @@ runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/app/SourceCodeRules/SourceCodeRules/StyleAnalyzers/ClassicExtensionMethodAnalyzer.cs b/app/SourceCodeRules/SourceCodeRules/StyleAnalyzers/ClassicExtensionMethodAnalyzer.cs index fec369c29..6f83350f4 100644 --- a/app/SourceCodeRules/SourceCodeRules/StyleAnalyzers/ClassicExtensionMethodAnalyzer.cs +++ b/app/SourceCodeRules/SourceCodeRules/StyleAnalyzers/ClassicExtensionMethodAnalyzer.cs @@ -13,10 +13,7 @@ namespace SourceCodeRules.StyleAnalyzers; /// /// /// AI Studio declares its extension members in extension blocks, one per receiver, so that all members of a -/// receiver read alike and extension properties can join them. A method with a params parameter stays a -/// classic one for now: for a member of an extension block, the compiler of the .NET 10.0.1xx SDK warns about -/// every argument passed one by one to a params parameter (CS8620, dotnet/roslyn#80024). Drop that exception -/// once the SDK in metadata.txt carries the fix. +/// receiver read alike and extension properties can join them. /// #pragma warning disable RS1038 [DiagnosticAnalyzer(LanguageNames.CSharp)] @@ -29,7 +26,7 @@ public sealed class ClassicExtensionMethodAnalyzer : DiagnosticAnalyzer private const string MESSAGE_FORMAT = "Declare '{0}' as a member of an extension block for its receiver instead of with a this parameter"; - private const string DESCRIPTION = "AI Studio declares its extension members in extension blocks, one per receiver. Only a method with a params parameter stays a classic extension method, until the SDK fixes dotnet/roslyn#80024."; + private const string DESCRIPTION = "AI Studio declares its extension members in extension blocks, one per receiver."; private const string CATEGORY = "Style"; @@ -55,9 +52,6 @@ public sealed class ClassicExtensionMethodAnalyzer : DiagnosticAnalyzer if (thisKeyword.IsKind(SyntaxKind.None)) return; - if (parameters.Any(parameter => parameter.Modifiers.Any(SyntaxKind.ParamsKeyword))) - return; - context.ReportDiagnostic(Diagnostic.Create(RULE, thisKeyword.GetLocation(), method.Identifier.Text)); } } diff --git a/app/SourceGeneratedMappings/SourceGeneratedMappings.csproj b/app/SourceGeneratedMappings/SourceGeneratedMappings.csproj index 7d118d296..f83932100 100644 --- a/app/SourceGeneratedMappings/SourceGeneratedMappings.csproj +++ b/app/SourceGeneratedMappings/SourceGeneratedMappings.csproj @@ -21,10 +21,13 @@ runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/app/Tests/Analyzers/ClassicExtensionMethodAnalyzerTests.cs b/app/Tests/Analyzers/ClassicExtensionMethodAnalyzerTests.cs index b8205e214..7c5f35e3e 100644 --- a/app/Tests/Analyzers/ClassicExtensionMethodAnalyzerTests.cs +++ b/app/Tests/Analyzers/ClassicExtensionMethodAnalyzerTests.cs @@ -11,9 +11,7 @@ namespace AIStudio.Tests.Analyzers; /// /// /// AI Studio declares all its extension members in extension blocks, one per receiver. MWAIS0016 refuses the -/// classic form while compiling, except for a method with a params parameter: for a member of an extension -/// block, the compiler of the .NET 10.0.1xx SDK warns about every argument passed one by one to a params -/// parameter (CS8620, dotnet/roslyn#80024). +/// classic form while compiling. /// [TestFixture] public sealed class ClassicExtensionMethodAnalyzerTests @@ -24,6 +22,7 @@ public sealed class ClassicExtensionMethodAnalyzerTests [TestCase("public static T Echo(this T value) where T : class => value;", "Echo", TestName = "A generic extension method")] [TestCase("public static void Bump(this ref int value) => value++;", "Bump", TestName = "An extension method with a ref receiver")] [TestCase("public static System.Threading.Tasks.Task LabelAsync(this Meeting meeting) => System.Threading.Tasks.Task.FromResult(meeting.Name);", "LabelAsync", TestName = "An asynchronous extension method")] + [TestCase("public static string Label(this Meeting meeting, params string[] prefixes) => string.Concat(prefixes) + meeting.Name;", "Label", TestName = "An extension method with a params parameter")] public async Task AClassicExtensionMethodIsRefused(string member, string name) { var reported = await AnalyzeAsync(member); @@ -39,7 +38,6 @@ public sealed class ClassicExtensionMethodAnalyzerTests [TestCase("extension(Meeting meeting) { public string Label() => meeting.Name; }", TestName = "A member of an extension block")] [TestCase("extension(T value) where T : class { public T Echo() => value; }", TestName = "A member of a generic extension block")] [TestCase("public static string Label(Meeting meeting) => meeting.Name;", TestName = "A static method without a this parameter")] - [TestCase("public static string Label(this Meeting meeting, params string[] prefixes) => string.Concat(prefixes) + meeting.Name;", TestName = "An extension method with a params parameter")] public async Task AnythingElseIsLeftAlone(string member) { var reported = await AnalyzeAsync(member); diff --git a/app/Tests/Tests.csproj b/app/Tests/Tests.csproj index ee96038d5..5c1c8d5d7 100644 --- a/app/Tests/Tests.csproj +++ b/app/Tests/Tests.csproj @@ -14,7 +14,8 @@ diff --git a/metadata.txt b/metadata.txt index 0ac90de8f..f7443e537 100644 --- a/metadata.txt +++ b/metadata.txt @@ -1,7 +1,7 @@ 26.10.1 2026-10-29 07:39:07 UTC 259 -10.0.114 (commit 0427b9abbe) +10.0.403 (commit 5a2162bea6) 10.0.14 (commit 0427b9abbe) 1.99.1 (commit 58cf8f9c3) 9.12.0