Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions dotnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
52 changes: 32 additions & 20 deletions dotnet/src/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -347,8 +347,8 @@ public async Task<CopilotSession> CreateSessionAsync(SessionConfig? config = nul
config?.DisabledSkills,
config?.InfiniteSessions);

var response = await connection.Rpc.InvokeWithCancellationAsync<CreateSessionResponse>(
"session.create", [request], cancellationToken);
var response = await InvokeRpcAsync<CreateSessionResponse>(
connection.Rpc, "session.create", [request], cancellationToken);

var session = new CopilotSession(response.SessionId, connection.Rpc, response.WorkspacePath);
session.RegisterTools(config?.Tools ?? []);
Expand Down Expand Up @@ -404,8 +404,8 @@ public async Task<CopilotSession> ResumeSessionAsync(string sessionId, ResumeSes
config?.SkillDirectories,
config?.DisabledSkills);

var response = await connection.Rpc.InvokeWithCancellationAsync<ResumeSessionResponse>(
"session.resume", [request], cancellationToken);
var response = await InvokeRpcAsync<ResumeSessionResponse>(
connection.Rpc, "session.resume", [request], cancellationToken);

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

return await connection.Rpc.InvokeWithCancellationAsync<PingResponse>(
"ping", [new PingRequest { Message = message }], cancellationToken);
return await InvokeRpcAsync<PingResponse>(
connection.Rpc, "ping", [new PingRequest { Message = message }], cancellationToken);
}

/// <summary>
Expand All @@ -475,8 +475,8 @@ public async Task<GetStatusResponse> GetStatusAsync(CancellationToken cancellati
{
var connection = await EnsureConnectedAsync(cancellationToken);

return await connection.Rpc.InvokeWithCancellationAsync<GetStatusResponse>(
"status.get", [], cancellationToken);
return await InvokeRpcAsync<GetStatusResponse>(
connection.Rpc, "status.get", [], cancellationToken);
}

/// <summary>
Expand All @@ -489,8 +489,8 @@ public async Task<GetAuthStatusResponse> GetAuthStatusAsync(CancellationToken ca
{
var connection = await EnsureConnectedAsync(cancellationToken);

return await connection.Rpc.InvokeWithCancellationAsync<GetAuthStatusResponse>(
"auth.getStatus", [], cancellationToken);
return await InvokeRpcAsync<GetAuthStatusResponse>(
connection.Rpc, "auth.getStatus", [], cancellationToken);
}

/// <summary>
Expand All @@ -503,8 +503,8 @@ public async Task<List<ModelInfo>> ListModelsAsync(CancellationToken cancellatio
{
var connection = await EnsureConnectedAsync(cancellationToken);

var response = await connection.Rpc.InvokeWithCancellationAsync<GetModelsResponse>(
"models.list", [], cancellationToken);
var response = await InvokeRpcAsync<GetModelsResponse>(
connection.Rpc, "models.list", [], cancellationToken);

return response.Models;
}
Expand All @@ -528,8 +528,8 @@ public async Task<List<ModelInfo>> ListModelsAsync(CancellationToken cancellatio
{
var connection = await EnsureConnectedAsync(cancellationToken);

var response = await connection.Rpc.InvokeWithCancellationAsync<GetLastSessionIdResponse>(
"session.getLastId", [], cancellationToken);
var response = await InvokeRpcAsync<GetLastSessionIdResponse>(
connection.Rpc, "session.getLastId", [], cancellationToken);

return response.SessionId;
}
Expand All @@ -554,8 +554,8 @@ public async Task DeleteSessionAsync(string sessionId, CancellationToken cancell
{
var connection = await EnsureConnectedAsync(cancellationToken);

var response = await connection.Rpc.InvokeWithCancellationAsync<DeleteSessionResponse>(
"session.delete", [new DeleteSessionRequest(sessionId)], cancellationToken);
var response = await InvokeRpcAsync<DeleteSessionResponse>(
connection.Rpc, "session.delete", [new DeleteSessionRequest(sessionId)], cancellationToken);

if (!response.Success)
{
Expand Down Expand Up @@ -584,12 +584,24 @@ public async Task<List<SessionMetadata>> ListSessionsAsync(CancellationToken can
{
var connection = await EnsureConnectedAsync(cancellationToken);

var response = await connection.Rpc.InvokeWithCancellationAsync<ListSessionsResponse>(
"session.list", [], cancellationToken);
var response = await InvokeRpcAsync<ListSessionsResponse>(
connection.Rpc, "session.list", [], cancellationToken);

return response.Sessions;
}

internal static async Task<T> InvokeRpcAsync<T>(JsonRpc rpc, string method, object?[]? args, CancellationToken cancellationToken)
{
try
{
return await rpc.InvokeWithCancellationAsync<T>(method, args, cancellationToken);
}
catch (StreamJsonRpc.RemoteRpcException ex)
{
throw new IOException($"Communication error with Copilot CLI: {ex.Message}", ex);
}
}

private Task<Connection> EnsureConnectedAsync(CancellationToken cancellationToken)
{
if (_connectionTask is null && !_options.AutoStart)
Expand All @@ -604,8 +616,8 @@ private Task<Connection> EnsureConnectedAsync(CancellationToken cancellationToke
private async Task VerifyProtocolVersionAsync(Connection connection, CancellationToken cancellationToken)
{
var expectedVersion = SdkProtocolVersion.GetVersion();
var pingResponse = await connection.Rpc.InvokeWithCancellationAsync<PingResponse>(
"ping", [new PingRequest()], cancellationToken);
var pingResponse = await InvokeRpcAsync<PingResponse>(
connection.Rpc, "ping", [new PingRequest()], cancellationToken);

if (!pingResponse.ProtocolVersion.HasValue)
{
Expand Down
13 changes: 8 additions & 5 deletions dotnet/src/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ internal CopilotSession(string sessionId, JsonRpc rpc, string? workspacePath = n
WorkspacePath = workspacePath;
}

private Task<T> InvokeRpcAsync<T>(string method, object?[]? args, CancellationToken cancellationToken) =>
CopilotClient.InvokeRpcAsync<T>(_rpc, method, args, cancellationToken);

/// <summary>
/// Sends a message to the Copilot session and waits for the response.
/// </summary>
Expand Down Expand Up @@ -118,7 +121,7 @@ public async Task<string> SendAsync(MessageOptions options, CancellationToken ca
Mode = options.Mode
};

var response = await _rpc.InvokeWithCancellationAsync<SendMessageResponse>(
var response = await InvokeRpcAsync<SendMessageResponse>(
"session.send", [request], cancellationToken);

return response.MessageId;
Expand Down Expand Up @@ -351,7 +354,7 @@ internal async Task<PermissionRequestResult> HandlePermissionRequestAsync(JsonEl
/// </example>
public async Task<IReadOnlyList<SessionEvent>> GetMessagesAsync(CancellationToken cancellationToken = default)
{
var response = await _rpc.InvokeWithCancellationAsync<GetMessagesResponse>(
var response = await InvokeRpcAsync<GetMessagesResponse>(
"session.getMessages", [new GetMessagesRequest { SessionId = SessionId }], cancellationToken);

return response.Events
Expand Down Expand Up @@ -385,7 +388,7 @@ public async Task<IReadOnlyList<SessionEvent>> GetMessagesAsync(CancellationToke
/// </example>
public async Task AbortAsync(CancellationToken cancellationToken = default)
{
await _rpc.InvokeWithCancellationAsync<object>(
await InvokeRpcAsync<object>(
"session.abort", [new SessionAbortRequest { SessionId = SessionId }], cancellationToken);
}

Expand Down Expand Up @@ -416,8 +419,8 @@ await _rpc.InvokeWithCancellationAsync<object>(
/// </example>
public async ValueTask DisposeAsync()
{
await _rpc.InvokeWithCancellationAsync<object>(
"session.destroy", [new SessionDestroyRequest() { SessionId = SessionId }]);
await InvokeRpcAsync<object>(
"session.destroy", [new SessionDestroyRequest() { SessionId = SessionId }], CancellationToken.None);

_eventHandlers.Clear();
_toolHandlers.Clear();
Expand Down
4 changes: 2 additions & 2 deletions dotnet/test/SessionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public async Task ShouldCreateAndDestroySessions()

await session.DisposeAsync();

var ex = await Assert.ThrowsAsync<StreamJsonRpc.RemoteInvocationException>(() => session.GetMessagesAsync());
var ex = await Assert.ThrowsAsync<IOException>(() => session.GetMessagesAsync());
Assert.Contains("not found", ex.Message, StringComparison.OrdinalIgnoreCase);
}

Expand Down Expand Up @@ -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<StreamJsonRpc.RemoteInvocationException>(() =>
await Assert.ThrowsAsync<IOException>(() =>
Client.ResumeSessionAsync("non-existent-session-id"));
}

Expand Down
Loading