diff --git a/dotnet/README.md b/dotnet/README.md index e176da40..c63bb120 100644 --- a/dotnet/README.md +++ b/dotnet/README.md @@ -452,9 +452,9 @@ try var session = await client.CreateSessionAsync(); await session.SendAsync(new MessageOptions { Prompt = "Hello" }); } -catch (StreamJsonRpc.RemoteInvocationException ex) +catch (IOException ex) { - Console.Error.WriteLine($"JSON-RPC Error: {ex.Message}"); + Console.Error.WriteLine($"Communication Error: {ex.Message}"); } catch (Exception ex) { diff --git a/dotnet/src/Client.cs b/dotnet/src/Client.cs index ef7982cb..88946eef 100644 --- a/dotnet/src/Client.cs +++ b/dotnet/src/Client.cs @@ -347,8 +347,8 @@ public async Task CreateSessionAsync(SessionConfig? config = nul config?.DisabledSkills, config?.InfiniteSessions); - var response = await connection.Rpc.InvokeWithCancellationAsync( - "session.create", [request], cancellationToken); + var response = await InvokeRpcAsync( + connection.Rpc, "session.create", [request], cancellationToken); var session = new CopilotSession(response.SessionId, connection.Rpc, response.WorkspacePath); session.RegisterTools(config?.Tools ?? []); @@ -404,8 +404,8 @@ public async Task ResumeSessionAsync(string sessionId, ResumeSes config?.SkillDirectories, config?.DisabledSkills); - var response = await connection.Rpc.InvokeWithCancellationAsync( - "session.resume", [request], cancellationToken); + var response = await InvokeRpcAsync( + connection.Rpc, "session.resume", [request], cancellationToken); var session = new CopilotSession(response.SessionId, connection.Rpc, response.WorkspacePath); session.RegisterTools(config?.Tools ?? []); @@ -461,8 +461,8 @@ public async Task PingAsync(string? message = null, CancellationTo { var connection = await EnsureConnectedAsync(cancellationToken); - return await connection.Rpc.InvokeWithCancellationAsync( - "ping", [new PingRequest { Message = message }], cancellationToken); + return await InvokeRpcAsync( + connection.Rpc, "ping", [new PingRequest { Message = message }], cancellationToken); } /// @@ -475,8 +475,8 @@ public async Task GetStatusAsync(CancellationToken cancellati { var connection = await EnsureConnectedAsync(cancellationToken); - return await connection.Rpc.InvokeWithCancellationAsync( - "status.get", [], cancellationToken); + return await InvokeRpcAsync( + connection.Rpc, "status.get", [], cancellationToken); } /// @@ -489,8 +489,8 @@ public async Task GetAuthStatusAsync(CancellationToken ca { var connection = await EnsureConnectedAsync(cancellationToken); - return await connection.Rpc.InvokeWithCancellationAsync( - "auth.getStatus", [], cancellationToken); + return await InvokeRpcAsync( + connection.Rpc, "auth.getStatus", [], cancellationToken); } /// @@ -503,8 +503,8 @@ public async Task> ListModelsAsync(CancellationToken cancellatio { var connection = await EnsureConnectedAsync(cancellationToken); - var response = await connection.Rpc.InvokeWithCancellationAsync( - "models.list", [], cancellationToken); + var response = await InvokeRpcAsync( + connection.Rpc, "models.list", [], cancellationToken); return response.Models; } @@ -528,8 +528,8 @@ public async Task> ListModelsAsync(CancellationToken cancellatio { var connection = await EnsureConnectedAsync(cancellationToken); - var response = await connection.Rpc.InvokeWithCancellationAsync( - "session.getLastId", [], cancellationToken); + var response = await InvokeRpcAsync( + connection.Rpc, "session.getLastId", [], cancellationToken); return response.SessionId; } @@ -554,8 +554,8 @@ public async Task DeleteSessionAsync(string sessionId, CancellationToken cancell { var connection = await EnsureConnectedAsync(cancellationToken); - var response = await connection.Rpc.InvokeWithCancellationAsync( - "session.delete", [new DeleteSessionRequest(sessionId)], cancellationToken); + var response = await InvokeRpcAsync( + connection.Rpc, "session.delete", [new DeleteSessionRequest(sessionId)], cancellationToken); if (!response.Success) { @@ -584,12 +584,24 @@ public async Task> ListSessionsAsync(CancellationToken can { var connection = await EnsureConnectedAsync(cancellationToken); - var response = await connection.Rpc.InvokeWithCancellationAsync( - "session.list", [], cancellationToken); + var response = await InvokeRpcAsync( + connection.Rpc, "session.list", [], cancellationToken); return response.Sessions; } + internal static async Task InvokeRpcAsync(JsonRpc rpc, string method, object?[]? args, CancellationToken cancellationToken) + { + try + { + return await rpc.InvokeWithCancellationAsync(method, args, cancellationToken); + } + catch (StreamJsonRpc.RemoteRpcException ex) + { + throw new IOException($"Communication error with Copilot CLI: {ex.Message}", ex); + } + } + private Task EnsureConnectedAsync(CancellationToken cancellationToken) { if (_connectionTask is null && !_options.AutoStart) @@ -604,8 +616,8 @@ private Task EnsureConnectedAsync(CancellationToken cancellationToke private async Task VerifyProtocolVersionAsync(Connection connection, CancellationToken cancellationToken) { var expectedVersion = SdkProtocolVersion.GetVersion(); - var pingResponse = await connection.Rpc.InvokeWithCancellationAsync( - "ping", [new PingRequest()], cancellationToken); + var pingResponse = await InvokeRpcAsync( + connection.Rpc, "ping", [new PingRequest()], cancellationToken); if (!pingResponse.ProtocolVersion.HasValue) { diff --git a/dotnet/src/Session.cs b/dotnet/src/Session.cs index f1e47df8..7f1cc4e4 100644 --- a/dotnet/src/Session.cs +++ b/dotnet/src/Session.cs @@ -80,6 +80,9 @@ internal CopilotSession(string sessionId, JsonRpc rpc, string? workspacePath = n WorkspacePath = workspacePath; } + private Task InvokeRpcAsync(string method, object?[]? args, CancellationToken cancellationToken) => + CopilotClient.InvokeRpcAsync(_rpc, method, args, cancellationToken); + /// /// Sends a message to the Copilot session and waits for the response. /// @@ -118,7 +121,7 @@ public async Task SendAsync(MessageOptions options, CancellationToken ca Mode = options.Mode }; - var response = await _rpc.InvokeWithCancellationAsync( + var response = await InvokeRpcAsync( "session.send", [request], cancellationToken); return response.MessageId; @@ -351,7 +354,7 @@ internal async Task HandlePermissionRequestAsync(JsonEl /// public async Task> GetMessagesAsync(CancellationToken cancellationToken = default) { - var response = await _rpc.InvokeWithCancellationAsync( + var response = await InvokeRpcAsync( "session.getMessages", [new GetMessagesRequest { SessionId = SessionId }], cancellationToken); return response.Events @@ -385,7 +388,7 @@ public async Task> GetMessagesAsync(CancellationToke /// public async Task AbortAsync(CancellationToken cancellationToken = default) { - await _rpc.InvokeWithCancellationAsync( + await InvokeRpcAsync( "session.abort", [new SessionAbortRequest { SessionId = SessionId }], cancellationToken); } @@ -416,8 +419,8 @@ await _rpc.InvokeWithCancellationAsync( /// public async ValueTask DisposeAsync() { - await _rpc.InvokeWithCancellationAsync( - "session.destroy", [new SessionDestroyRequest() { SessionId = SessionId }]); + await InvokeRpcAsync( + "session.destroy", [new SessionDestroyRequest() { SessionId = SessionId }], CancellationToken.None); _eventHandlers.Clear(); _toolHandlers.Clear(); diff --git a/dotnet/test/SessionTests.cs b/dotnet/test/SessionTests.cs index 845e604a..13b23522 100644 --- a/dotnet/test/SessionTests.cs +++ b/dotnet/test/SessionTests.cs @@ -26,7 +26,7 @@ public async Task ShouldCreateAndDestroySessions() await session.DisposeAsync(); - var ex = await Assert.ThrowsAsync(() => session.GetMessagesAsync()); + var ex = await Assert.ThrowsAsync(() => session.GetMessagesAsync()); Assert.Contains("not found", ex.Message, StringComparison.OrdinalIgnoreCase); } @@ -192,7 +192,7 @@ public async Task Should_Resume_A_Session_Using_A_New_Client() [Fact] public async Task Should_Throw_Error_When_Resuming_Non_Existent_Session() { - await Assert.ThrowsAsync(() => + await Assert.ThrowsAsync(() => Client.ResumeSessionAsync("non-existent-session-id")); }