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
8 changes: 7 additions & 1 deletion dotnet/src/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ public async Task<string> SendAsync(MessageOptions options, CancellationToken ca
/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the operation.</param>
/// <returns>A task that resolves with the final assistant message event, or null if none was received.</returns>
/// <exception cref="TimeoutException">Thrown if the timeout is reached before the session becomes idle.</exception>
/// <exception cref="OperationCanceledException">Thrown if the <paramref name="cancellationToken"/> is cancelled.</exception>
/// <exception cref="InvalidOperationException">Thrown if the session has been disposed.</exception>
/// <remarks>
/// <para>
Expand Down Expand Up @@ -201,7 +202,12 @@ void Handler(SessionEvent evt)
cts.CancelAfter(effectiveTimeout);

using var registration = cts.Token.Register(() =>
tcs.TrySetException(new TimeoutException($"SendAndWaitAsync timed out after {effectiveTimeout}")));
{
if (cancellationToken.IsCancellationRequested)
tcs.TrySetCanceled(cancellationToken);
else
tcs.TrySetException(new TimeoutException($"SendAndWaitAsync timed out after {effectiveTimeout}"));
});
return await tcs.Task;
}

Expand Down
24 changes: 24 additions & 0 deletions dotnet/test/SessionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,30 @@ public async Task SendAndWait_Throws_On_Timeout()
Assert.Contains("timed out", ex.Message);
}

[Fact]
public async Task SendAndWait_Throws_OperationCanceledException_When_Token_Cancelled()
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Method name uses Token_Cancelled (double-l). In .NET APIs and the rest of the framework, the standard spelling is Canceled (single-l), e.g. OperationCanceledException / CancellationToken. Consider renaming the test (and corresponding snapshot filename) to ..._When_Token_Canceled for consistency/searchability.

Suggested change
public async Task SendAndWait_Throws_OperationCanceledException_When_Token_Cancelled()
public async Task SendAndWait_Throws_OperationCanceledException_When_Token_Canceled()

Copilot uses AI. Check for mistakes.
{
var session = await Client.CreateSessionAsync();

// Set up wait for tool execution to start BEFORE sending
var toolStartTask = TestHelper.GetNextEventOfTypeAsync<ToolExecutionStartEvent>(session);

Comment on lines +409 to +413
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test creates a session without an OnPermissionRequest handler but then waits for ToolExecutionStartEvent from a shell command. In this repo, tool operations are expected to be denied-by-default when no permission handler is provided (see PermissionTests.Should_Deny_Tool_Operations_By_Default_When_No_Handler_Is_Provided), which can prevent tool.execution_start from ever arriving or can cause the turn to complete quickly with “Permission denied”, making this test flaky/slow (60s timeout in GetNextEventOfTypeAsync). Consider creating the session with new SessionConfig { OnPermissionRequest = PermissionHandler.ApproveAll } (or a handler that approves shell), and optionally pass a shorter timeout to GetNextEventOfTypeAsync to fail fast.

Copilot uses AI. Check for mistakes.
using var cts = new CancellationTokenSource();

// Start SendAndWaitAsync - don't await it yet
var sendTask = session.SendAndWaitAsync(
new MessageOptions { Prompt = "run the shell command 'sleep 10' (note this works on both bash and PowerShell)" },
cancellationToken: cts.Token);

// Wait for the tool to begin executing before cancelling
await toolStartTask;

// Cancel the token
cts.Cancel();

await Assert.ThrowsAnyAsync<OperationCanceledException>(() => sendTask);
}

[Fact]
public async Task Should_Create_Session_With_Custom_Config_Dir()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
models:
- claude-sonnet-4.5
conversations:
- messages:
- role: system
content: ${system}
- role: user
content: run the shell command 'sleep 10' (note this works on both bash and PowerShell)
- role: assistant
content: I'll run the sleep command for you.
- role: assistant
tool_calls:
- id: toolcall_0
type: function
function:
name: report_intent
arguments: '{"intent":"Running sleep command"}'
- role: assistant
tool_calls:
- id: toolcall_1
type: function
function:
name: ${shell}
arguments: '{"command":"sleep 10","description":"Execute sleep 10 command","initial_wait":15,"mode":"sync"}'
Loading