Skip to content

Commit 63b974a

Browse files
Replace CopilotRpcException with IOException and add RPC wrapper methods
Co-authored-by: SteveSandersonMS <1101362+SteveSandersonMS@users.noreply.github.com>
1 parent f46b3d5 commit 63b974a

5 files changed

Lines changed: 52 additions & 167 deletions

File tree

dotnet/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -452,9 +452,9 @@ try
452452
var session = await client.CreateSessionAsync();
453453
await session.SendAsync(new MessageOptions { Prompt = "Hello" });
454454
}
455-
catch (CopilotRpcException ex)
455+
catch (IOException ex)
456456
{
457-
Console.Error.WriteLine($"RPC Error: {ex.Message}");
457+
Console.Error.WriteLine($"Communication Error: {ex.Message}");
458458
}
459459
catch (Exception ex)
460460
{

dotnet/src/Client.cs

Lines changed: 28 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -347,16 +347,8 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig? config = nul
347347
config?.DisabledSkills,
348348
config?.InfiniteSessions);
349349

350-
CreateSessionResponse response;
351-
try
352-
{
353-
response = await connection.Rpc.InvokeWithCancellationAsync<CreateSessionResponse>(
354-
"session.create", [request], cancellationToken);
355-
}
356-
catch (StreamJsonRpc.RemoteInvocationException ex)
357-
{
358-
throw new CopilotRpcException($"Failed to create session: {ex.Message}", ex);
359-
}
350+
var response = await InvokeRpcAsync<CreateSessionResponse>(
351+
connection.Rpc, "session.create", [request], cancellationToken);
360352

361353
var session = new CopilotSession(response.SessionId, connection.Rpc, response.WorkspacePath);
362354
session.RegisterTools(config?.Tools ?? []);
@@ -412,16 +404,8 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes
412404
config?.SkillDirectories,
413405
config?.DisabledSkills);
414406

415-
ResumeSessionResponse response;
416-
try
417-
{
418-
response = await connection.Rpc.InvokeWithCancellationAsync<ResumeSessionResponse>(
419-
"session.resume", [request], cancellationToken);
420-
}
421-
catch (StreamJsonRpc.RemoteInvocationException ex)
422-
{
423-
throw new CopilotRpcException($"Failed to resume session: {ex.Message}", ex);
424-
}
407+
var response = await InvokeRpcAsync<ResumeSessionResponse>(
408+
connection.Rpc, "session.resume", [request], cancellationToken);
425409

426410
var session = new CopilotSession(response.SessionId, connection.Rpc, response.WorkspacePath);
427411
session.RegisterTools(config?.Tools ?? []);
@@ -477,18 +461,8 @@ public async Task<PingResponse> PingAsync(string? message = null, CancellationTo
477461
{
478462
var connection = await EnsureConnectedAsync(cancellationToken);
479463

480-
PingResponse response;
481-
try
482-
{
483-
response = await connection.Rpc.InvokeWithCancellationAsync<PingResponse>(
484-
"ping", [new PingRequest { Message = message }], cancellationToken);
485-
}
486-
catch (StreamJsonRpc.RemoteInvocationException ex)
487-
{
488-
throw new CopilotRpcException($"Failed to ping server: {ex.Message}", ex);
489-
}
490-
491-
return response;
464+
return await InvokeRpcAsync<PingResponse>(
465+
connection.Rpc, "ping", [new PingRequest { Message = message }], cancellationToken);
492466
}
493467

494468
/// <summary>
@@ -501,18 +475,8 @@ public async Task<GetStatusResponse> GetStatusAsync(CancellationToken cancellati
501475
{
502476
var connection = await EnsureConnectedAsync(cancellationToken);
503477

504-
GetStatusResponse response;
505-
try
506-
{
507-
response = await connection.Rpc.InvokeWithCancellationAsync<GetStatusResponse>(
508-
"status.get", [], cancellationToken);
509-
}
510-
catch (StreamJsonRpc.RemoteInvocationException ex)
511-
{
512-
throw new CopilotRpcException($"Failed to get status: {ex.Message}", ex);
513-
}
514-
515-
return response;
478+
return await InvokeRpcAsync<GetStatusResponse>(
479+
connection.Rpc, "status.get", [], cancellationToken);
516480
}
517481

518482
/// <summary>
@@ -525,18 +489,8 @@ public async Task<GetAuthStatusResponse> GetAuthStatusAsync(CancellationToken ca
525489
{
526490
var connection = await EnsureConnectedAsync(cancellationToken);
527491

528-
GetAuthStatusResponse response;
529-
try
530-
{
531-
response = await connection.Rpc.InvokeWithCancellationAsync<GetAuthStatusResponse>(
532-
"auth.getStatus", [], cancellationToken);
533-
}
534-
catch (StreamJsonRpc.RemoteInvocationException ex)
535-
{
536-
throw new CopilotRpcException($"Failed to get auth status: {ex.Message}", ex);
537-
}
538-
539-
return response;
492+
return await InvokeRpcAsync<GetAuthStatusResponse>(
493+
connection.Rpc, "auth.getStatus", [], cancellationToken);
540494
}
541495

542496
/// <summary>
@@ -549,16 +503,8 @@ public async Task<List<ModelInfo>> ListModelsAsync(CancellationToken cancellatio
549503
{
550504
var connection = await EnsureConnectedAsync(cancellationToken);
551505

552-
GetModelsResponse response;
553-
try
554-
{
555-
response = await connection.Rpc.InvokeWithCancellationAsync<GetModelsResponse>(
556-
"models.list", [], cancellationToken);
557-
}
558-
catch (StreamJsonRpc.RemoteInvocationException ex)
559-
{
560-
throw new CopilotRpcException($"Failed to list models: {ex.Message}", ex);
561-
}
506+
var response = await InvokeRpcAsync<GetModelsResponse>(
507+
connection.Rpc, "models.list", [], cancellationToken);
562508

563509
return response.Models;
564510
}
@@ -582,16 +528,8 @@ public async Task<List<ModelInfo>> ListModelsAsync(CancellationToken cancellatio
582528
{
583529
var connection = await EnsureConnectedAsync(cancellationToken);
584530

585-
GetLastSessionIdResponse response;
586-
try
587-
{
588-
response = await connection.Rpc.InvokeWithCancellationAsync<GetLastSessionIdResponse>(
589-
"session.getLastId", [], cancellationToken);
590-
}
591-
catch (StreamJsonRpc.RemoteInvocationException ex)
592-
{
593-
throw new CopilotRpcException($"Failed to get last session ID: {ex.Message}", ex);
594-
}
531+
var response = await InvokeRpcAsync<GetLastSessionIdResponse>(
532+
connection.Rpc, "session.getLastId", [], cancellationToken);
595533

596534
return response.SessionId;
597535
}
@@ -616,16 +554,8 @@ public async Task DeleteSessionAsync(string sessionId, CancellationToken cancell
616554
{
617555
var connection = await EnsureConnectedAsync(cancellationToken);
618556

619-
DeleteSessionResponse response;
620-
try
621-
{
622-
response = await connection.Rpc.InvokeWithCancellationAsync<DeleteSessionResponse>(
623-
"session.delete", [new DeleteSessionRequest(sessionId)], cancellationToken);
624-
}
625-
catch (StreamJsonRpc.RemoteInvocationException ex)
626-
{
627-
throw new CopilotRpcException($"Failed to delete session: {ex.Message}", ex);
628-
}
557+
var response = await InvokeRpcAsync<DeleteSessionResponse>(
558+
connection.Rpc, "session.delete", [new DeleteSessionRequest(sessionId)], cancellationToken);
629559

630560
if (!response.Success)
631561
{
@@ -654,18 +584,22 @@ public async Task<List<SessionMetadata>> ListSessionsAsync(CancellationToken can
654584
{
655585
var connection = await EnsureConnectedAsync(cancellationToken);
656586

657-
ListSessionsResponse response;
587+
var response = await InvokeRpcAsync<ListSessionsResponse>(
588+
connection.Rpc, "session.list", [], cancellationToken);
589+
590+
return response.Sessions;
591+
}
592+
593+
private static async Task<T> InvokeRpcAsync<T>(JsonRpc rpc, string method, object?[]? args, CancellationToken cancellationToken)
594+
{
658595
try
659596
{
660-
response = await connection.Rpc.InvokeWithCancellationAsync<ListSessionsResponse>(
661-
"session.list", [], cancellationToken);
597+
return await rpc.InvokeWithCancellationAsync<T>(method, args, cancellationToken);
662598
}
663599
catch (StreamJsonRpc.RemoteInvocationException ex)
664600
{
665-
throw new CopilotRpcException($"Failed to list sessions: {ex.Message}", ex);
601+
throw new IOException($"Communication error with Copilot CLI: {ex.Message}", ex);
666602
}
667-
668-
return response.Sessions;
669603
}
670604

671605
private Task<Connection> EnsureConnectedAsync(CancellationToken cancellationToken)
@@ -682,16 +616,8 @@ private Task<Connection> EnsureConnectedAsync(CancellationToken cancellationToke
682616
private async Task VerifyProtocolVersionAsync(Connection connection, CancellationToken cancellationToken)
683617
{
684618
var expectedVersion = SdkProtocolVersion.GetVersion();
685-
PingResponse pingResponse;
686-
try
687-
{
688-
pingResponse = await connection.Rpc.InvokeWithCancellationAsync<PingResponse>(
689-
"ping", [new PingRequest()], cancellationToken);
690-
}
691-
catch (StreamJsonRpc.RemoteInvocationException ex)
692-
{
693-
throw new CopilotRpcException($"Failed to verify protocol version: {ex.Message}", ex);
694-
}
619+
var pingResponse = await InvokeRpcAsync<PingResponse>(
620+
connection.Rpc, "ping", [new PingRequest()], cancellationToken);
695621

696622
if (!pingResponse.ProtocolVersion.HasValue)
697623
{

dotnet/src/Session.cs

Lines changed: 20 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,18 @@ internal CopilotSession(string sessionId, JsonRpc rpc, string? workspacePath = n
8080
WorkspacePath = workspacePath;
8181
}
8282

83+
private async Task<T> InvokeRpcAsync<T>(string method, object?[]? args, CancellationToken cancellationToken)
84+
{
85+
try
86+
{
87+
return await _rpc.InvokeWithCancellationAsync<T>(method, args, cancellationToken);
88+
}
89+
catch (StreamJsonRpc.RemoteInvocationException ex)
90+
{
91+
throw new IOException($"Communication error with Copilot CLI: {ex.Message}", ex);
92+
}
93+
}
94+
8395
/// <summary>
8496
/// Sends a message to the Copilot session and waits for the response.
8597
/// </summary>
@@ -118,16 +130,8 @@ public async Task<string> SendAsync(MessageOptions options, CancellationToken ca
118130
Mode = options.Mode
119131
};
120132

121-
SendMessageResponse response;
122-
try
123-
{
124-
response = await _rpc.InvokeWithCancellationAsync<SendMessageResponse>(
125-
"session.send", [request], cancellationToken);
126-
}
127-
catch (StreamJsonRpc.RemoteInvocationException ex)
128-
{
129-
throw new CopilotRpcException($"Failed to send message: {ex.Message}", ex);
130-
}
133+
var response = await InvokeRpcAsync<SendMessageResponse>(
134+
"session.send", [request], cancellationToken);
131135

132136
return response.MessageId;
133137
}
@@ -359,16 +363,8 @@ internal async Task<PermissionRequestResult> HandlePermissionRequestAsync(JsonEl
359363
/// </example>
360364
public async Task<IReadOnlyList<SessionEvent>> GetMessagesAsync(CancellationToken cancellationToken = default)
361365
{
362-
GetMessagesResponse response;
363-
try
364-
{
365-
response = await _rpc.InvokeWithCancellationAsync<GetMessagesResponse>(
366-
"session.getMessages", [new GetMessagesRequest { SessionId = SessionId }], cancellationToken);
367-
}
368-
catch (StreamJsonRpc.RemoteInvocationException ex)
369-
{
370-
throw new CopilotRpcException($"Failed to get messages: {ex.Message}", ex);
371-
}
366+
var response = await InvokeRpcAsync<GetMessagesResponse>(
367+
"session.getMessages", [new GetMessagesRequest { SessionId = SessionId }], cancellationToken);
372368

373369
return response.Events
374370
.Select(e => SessionEvent.FromJson(e.ToJsonString()))
@@ -401,15 +397,8 @@ public async Task<IReadOnlyList<SessionEvent>> GetMessagesAsync(CancellationToke
401397
/// </example>
402398
public async Task AbortAsync(CancellationToken cancellationToken = default)
403399
{
404-
try
405-
{
406-
await _rpc.InvokeWithCancellationAsync<object>(
407-
"session.abort", [new SessionAbortRequest { SessionId = SessionId }], cancellationToken);
408-
}
409-
catch (StreamJsonRpc.RemoteInvocationException ex)
410-
{
411-
throw new CopilotRpcException($"Failed to abort session: {ex.Message}", ex);
412-
}
400+
await InvokeRpcAsync<object>(
401+
"session.abort", [new SessionAbortRequest { SessionId = SessionId }], cancellationToken);
413402
}
414403

415404
/// <summary>
@@ -439,15 +428,8 @@ await _rpc.InvokeWithCancellationAsync<object>(
439428
/// </example>
440429
public async ValueTask DisposeAsync()
441430
{
442-
try
443-
{
444-
await _rpc.InvokeWithCancellationAsync<object>(
445-
"session.destroy", [new SessionDestroyRequest() { SessionId = SessionId }]);
446-
}
447-
catch (StreamJsonRpc.RemoteInvocationException ex)
448-
{
449-
throw new CopilotRpcException($"Failed to destroy session: {ex.Message}", ex);
450-
}
431+
await InvokeRpcAsync<object>(
432+
"session.destroy", [new SessionDestroyRequest() { SessionId = SessionId }], CancellationToken.None);
451433

452434
_eventHandlers.Clear();
453435
_toolHandlers.Clear();

dotnet/src/Types.cs

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,29 +10,6 @@
1010

1111
namespace GitHub.Copilot.SDK;
1212

13-
/// <summary>
14-
/// Exception thrown when a communication error occurs with the Copilot CLI.
15-
/// </summary>
16-
public class CopilotRpcException : IOException
17-
{
18-
/// <summary>
19-
/// Initializes a new instance of the <see cref="CopilotRpcException"/> class.
20-
/// </summary>
21-
/// <param name="message">The error message that explains the reason for the exception.</param>
22-
public CopilotRpcException(string message) : base(message)
23-
{
24-
}
25-
26-
/// <summary>
27-
/// Initializes a new instance of the <see cref="CopilotRpcException"/> class.
28-
/// </summary>
29-
/// <param name="message">The error message that explains the reason for the exception.</param>
30-
/// <param name="innerException">The exception that is the cause of the current exception.</param>
31-
public CopilotRpcException(string message, Exception innerException) : base(message, innerException)
32-
{
33-
}
34-
}
35-
3613
[JsonConverter(typeof(JsonStringEnumConverter<SystemMessageMode>))]
3714
public enum ConnectionState
3815
{

dotnet/test/SessionTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public async Task ShouldCreateAndDestroySessions()
2626

2727
await session.DisposeAsync();
2828

29-
var ex = await Assert.ThrowsAsync<CopilotRpcException>(() => session.GetMessagesAsync());
29+
var ex = await Assert.ThrowsAsync<IOException>(() => session.GetMessagesAsync());
3030
Assert.Contains("not found", ex.Message, StringComparison.OrdinalIgnoreCase);
3131
}
3232

@@ -192,7 +192,7 @@ public async Task Should_Resume_A_Session_Using_A_New_Client()
192192
[Fact]
193193
public async Task Should_Throw_Error_When_Resuming_Non_Existent_Session()
194194
{
195-
await Assert.ThrowsAsync<CopilotRpcException>(() =>
195+
await Assert.ThrowsAsync<IOException>(() =>
196196
Client.ResumeSessionAsync("non-existent-session-id"));
197197
}
198198

0 commit comments

Comments
 (0)