From 45ce6f93be6a1cdc87e3101fae4515c87dc60a12 Mon Sep 17 00:00:00 2001 From: syf2211 Date: Fri, 3 Jul 2026 10:09:43 +0000 Subject: [PATCH] fix(dotnet): forward CustomAgentsLocalOnly in session.create and session.resume CustomAgentsLocalOnly was only sent via the post-create session.options.update call, which arrives after agent discovery has already completed. Mirror the Go SDK by including customAgentsLocalOnly in CreateSessionRequest and ResumeSessionRequest wire payloads. Fixes #1888 --- dotnet/src/Client.cs | 4 ++ dotnet/test/E2E/ClientOptionsE2ETests.cs | 59 ++++++++++++++++++++++++ dotnet/test/Unit/SerializationTests.cs | 52 +++++++++++++++++++++ 3 files changed, 115 insertions(+) diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index 6041fe2391..5ec26ad5df 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -1005,6 +1005,7 @@ public async Task CreateSessionAsync(SessionConfig config, Cance config.Agent, config.ConfigDirectory, config.EnableConfigDiscovery, + config.CustomAgentsLocalOnly, config.SkipEmbeddingRetrieval, config.EmbeddingCacheStorage, config.OrganizationCustomInstructions, @@ -1205,6 +1206,7 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes config.WorkingDirectory, config.ConfigDirectory, config.EnableConfigDiscovery, + config.CustomAgentsLocalOnly, config.SkipEmbeddingRetrieval, config.EmbeddingCacheStorage, config.OrganizationCustomInstructions, @@ -2467,6 +2469,7 @@ internal record CreateSessionRequest( string? Agent, [property: JsonPropertyName("configDir")] string? ConfigDirectory, bool? EnableConfigDiscovery, + [property: JsonPropertyName("customAgentsLocalOnly")] bool? CustomAgentsLocalOnly, bool? SkipEmbeddingRetrieval, EmbeddingCacheStorageMode? EmbeddingCacheStorage, string? OrganizationCustomInstructions, @@ -2557,6 +2560,7 @@ internal record ResumeSessionRequest( string? WorkingDirectory, [property: JsonPropertyName("configDir")] string? ConfigDirectory, bool? EnableConfigDiscovery, + [property: JsonPropertyName("customAgentsLocalOnly")] bool? CustomAgentsLocalOnly, bool? SkipEmbeddingRetrieval, EmbeddingCacheStorageMode? EmbeddingCacheStorage, string? OrganizationCustomInstructions, diff --git a/dotnet/test/E2E/ClientOptionsE2ETests.cs b/dotnet/test/E2E/ClientOptionsE2ETests.cs index c2f16a042b..72888c4c5d 100644 --- a/dotnet/test/E2E/ClientOptionsE2ETests.cs +++ b/dotnet/test/E2E/ClientOptionsE2ETests.cs @@ -179,6 +179,65 @@ public async Task Should_Omit_EnableSessionTelemetry_When_Not_Set() await session.DisposeAsync(); } + [Fact] + public async Task Should_Forward_CustomAgentsLocalOnly_In_Create_Wire_Request() + { + var (cliPath, capturePath) = await CreateFakeCliCaptureAsync(); + + await using var client = Ctx.CreateClient(options: new CopilotClientOptions + { + Connection = RuntimeConnection.ForStdio(path: cliPath, args: ["--capture-file", capturePath]), + UseLoggedInUser = false, + }); + + await client.StartAsync(); + + var session = await client.CreateSessionAsync(new SessionConfig + { + CustomAgentsLocalOnly = true, + OnPermissionRequest = PermissionHandler.ApproveAll, + }); + + using var capture = JsonDocument.Parse(await File.ReadAllTextAsync(capturePath)); + var createRequest = GetCapturedRequestParams(capture.RootElement, "session.create"); + Assert.True(createRequest.GetProperty("customAgentsLocalOnly").GetBoolean()); + + await session.DisposeAsync(); + } + + [Fact] + public async Task Should_Forward_CustomAgentsLocalOnly_In_Resume_Wire_Request() + { + var (cliPath, capturePath) = await CreateFakeCliCaptureAsync(); + + await using var client = Ctx.CreateClient(options: new CopilotClientOptions + { + Connection = RuntimeConnection.ForStdio(path: cliPath, args: ["--capture-file", capturePath]), + UseLoggedInUser = false, + }); + + await client.StartAsync(); + + var createSession = await client.CreateSessionAsync(new SessionConfig + { + OnPermissionRequest = PermissionHandler.ApproveAll, + }); + var sessionId = createSession.SessionId; + await createSession.DisposeAsync(); + + var resumeSession = await client.ResumeSessionAsync(sessionId, new ResumeSessionConfig + { + CustomAgentsLocalOnly = true, + OnPermissionRequest = PermissionHandler.ApproveAll, + }); + + using var capture = JsonDocument.Parse(await File.ReadAllTextAsync(capturePath)); + var resumeRequest = GetCapturedRequestParams(capture.RootElement, "session.resume"); + Assert.True(resumeRequest.GetProperty("customAgentsLocalOnly").GetBoolean()); + + await resumeSession.DisposeAsync(); + } + [Fact] public async Task Should_Forward_Granular_Multitenancy_Fields_In_Create_Wire_Request() { diff --git a/dotnet/test/Unit/SerializationTests.cs b/dotnet/test/Unit/SerializationTests.cs index f6fa31d88c..fdba3c4937 100644 --- a/dotnet/test/Unit/SerializationTests.cs +++ b/dotnet/test/Unit/SerializationTests.cs @@ -590,6 +590,58 @@ public void CreateSessionRequest_CanSerializeEnableSessionTelemetry_WithSdkOptio Assert.False(root.GetProperty("enableSessionTelemetry").GetBoolean()); } + [Fact] + public void CreateSessionRequest_CanSerializeCustomAgentsLocalOnly_WithSdkOptions() + { + var options = GetSerializerOptions(); + var requestType = GetNestedType(typeof(CopilotClient), "CreateSessionRequest"); + var request = CreateInternalRequest( + requestType, + ("SessionId", "session-id"), + ("CustomAgentsLocalOnly", true)); + + var json = JsonSerializer.Serialize(request, requestType, options); + using var document = JsonDocument.Parse(json); + Assert.True(document.RootElement.GetProperty("customAgentsLocalOnly").GetBoolean()); + } + + [Fact] + public void ResumeSessionRequest_CanSerializeCustomAgentsLocalOnly_WithSdkOptions() + { + var options = GetSerializerOptions(); + var requestType = GetNestedType(typeof(CopilotClient), "ResumeSessionRequest"); + var request = CreateInternalRequest( + requestType, + ("SessionId", "session-id"), + ("CustomAgentsLocalOnly", true)); + + var json = JsonSerializer.Serialize(request, requestType, options); + using var document = JsonDocument.Parse(json); + Assert.True(document.RootElement.GetProperty("customAgentsLocalOnly").GetBoolean()); + } + + [Fact] + public void SessionRequests_OmitCustomAgentsLocalOnly_WhenUnset() + { + var options = GetSerializerOptions(); + + var createRequestType = GetNestedType(typeof(CopilotClient), "CreateSessionRequest"); + var createRequest = CreateInternalRequest( + createRequestType, + ("SessionId", "session-id")); + var createJson = JsonSerializer.Serialize(createRequest, createRequestType, options); + using var createDocument = JsonDocument.Parse(createJson); + Assert.False(createDocument.RootElement.TryGetProperty("customAgentsLocalOnly", out _)); + + var resumeRequestType = GetNestedType(typeof(CopilotClient), "ResumeSessionRequest"); + var resumeRequest = CreateInternalRequest( + resumeRequestType, + ("SessionId", "session-id")); + var resumeJson = JsonSerializer.Serialize(resumeRequest, resumeRequestType, options); + using var resumeDocument = JsonDocument.Parse(resumeJson); + Assert.False(resumeDocument.RootElement.TryGetProperty("customAgentsLocalOnly", out _)); + } + [Fact] public void ResumeSessionRequest_CanSerializeEnableSessionTelemetry_WithSdkOptions() {