Skip to content
Draft
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
171 changes: 171 additions & 0 deletions dotnet/src/Generated/Rpc.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions dotnet/test/E2E/RpcSessionStateE2ETests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,15 @@ public async Task Should_Read_Update_And_Delete_Plan()
Assert.Null(afterDelete.Content);
}

[Fact]
public async Task Should_Get_Empty_Autopilot_Objective_State()
{
await using var session = await CreateSessionAsync();

var result = await session.Rpc.AutopilotObjective.GetStateAsync();
Assert.Null(result.State);
}

[Fact]
public async Task Should_Call_Workspace_File_Rpc_Methods()
{
Expand Down
22 changes: 22 additions & 0 deletions dotnet/test/Unit/ClientSessionLifetimeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,24 @@ public async Task Generated_Session_Rpc_Throws_When_Session_Disposed()
await Assert.ThrowsAsync<ObjectDisposedException>(() => session.Rpc.Model.GetCurrentAsync());
}

[Fact]
public async Task Generated_AutopilotObjective_GetState_Injects_SessionId()
{
await using var server = await FakeCopilotServer.StartAsync();
await using var client = new CopilotClient(new CopilotClientOptions { Connection = RuntimeConnection.ForUri(server.Url) });
await using var session = await client.CreateSessionAsync(new SessionConfig
{
SessionId = "objective-session",
OnPermissionRequest = PermissionHandler.ApproveAll
});

var result = await session.Rpc.AutopilotObjective.GetStateAsync();

Assert.Null(result.State);
var request = await WaitForRequestAsync(server, "session.autopilotObjective.getState");
Assert.Equal("objective-session", request.Params.GetProperty("sessionId").GetString());
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static async Task<WeakReference<CopilotSession>> CreateDroppedSessionAsync(CopilotClient client)
{
Expand Down Expand Up @@ -1049,6 +1067,10 @@ private async Task HandleRequestAsync(Stream stream, JsonElement request, Cancel
{
["success"] = true
},
"session.autopilotObjective.getState" => new Dictionary<string, object?>
{
["state"] = null
},
"session.delete" => new Dictionary<string, object?>
{
["success"] = true
Expand Down
106 changes: 106 additions & 0 deletions dotnet/test/Unit/SerializationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1082,6 +1082,112 @@ public void ToolResultObject_OmitsToolReferences_WhenNull_WithSdkOptions()
Assert.False(document.RootElement.TryGetProperty("toolReferences", out _));
}

[Fact]
public void AutopilotObjectiveState_DeserializesCanonicalPayloads_WithSdkOptions()
{
var options = GetSerializerOptions();

var noObjective = JsonSerializer.Deserialize<AutopilotObjectiveGetStateResult>(
"""{"state":null}""",
options);
Assert.NotNull(noObjective);
Assert.Null(noObjective.State);

var active = DeserializeState(
"""
{
"state": {
"id": 1,
"objective": "Ship the release",
"status": "active",
"turnCount": 2,
"creditCountNanoAiu": "0"
}
}
""",
options);
Assert.Equal(1, active.Id);
Assert.Equal("Ship the release", active.Objective);
Assert.Equal(AutopilotObjectiveStatus.Active, active.Status);
Assert.Equal(2, active.TurnCount);
Assert.Equal("0", active.CreditCountNanoAiu);
Assert.Null(active.PauseReason);
Assert.Null(active.CompletionSummary);
Assert.Null(active.CreditLimit);
var activeJson = JsonSerializer.SerializeToElement(active, options);
Assert.False(activeJson.TryGetProperty("pauseReason", out _));
Assert.False(activeJson.TryGetProperty("completionSummary", out _));
Assert.False(activeJson.TryGetProperty("creditLimit", out _));

var paused = DeserializeState(
"""
{
"state": {
"id": 2,
"objective": "Wait for approval",
"status": "paused",
"turnCount": 3,
"pauseReason": "Approval required",
"creditCountNanoAiu": "9007199254740993",
"creditLimit": {
"creditsUsed": 9007199.254740993,
"creditsUsedNanoAiu": "9007199254740993"
}
}
}
""",
options);
Assert.Equal(2, paused.Id);
Assert.Equal("Wait for approval", paused.Objective);
Assert.Equal(AutopilotObjectiveStatus.Paused, paused.Status);
Assert.Equal(3, paused.TurnCount);
Assert.Equal("Approval required", paused.PauseReason);
Assert.Equal("9007199254740993", paused.CreditCountNanoAiu);
Assert.NotNull(paused.CreditLimit);
Assert.Null(paused.CreditLimit.Credits);
Assert.Equal(9007199.254740993, paused.CreditLimit.CreditsUsed);
Assert.Equal("9007199254740993", paused.CreditLimit.CreditsUsedNanoAiu);

var completed = DeserializeState(
"""
{
"state": {
"id": 3,
"objective": "Publish the SDK",
"status": "completed",
"turnCount": 4,
"completionSummary": "Published",
"creditCountNanoAiu": "9007199254740994",
"creditLimit": {
"credits": 2.5,
"creditsUsed": 1.25,
"creditsUsedNanoAiu": "1250000000"
}
}
}
""",
options);
Assert.Equal(3, completed.Id);
Assert.Equal("Publish the SDK", completed.Objective);
Assert.Equal(AutopilotObjectiveStatus.Completed, completed.Status);
Assert.Equal(4, completed.TurnCount);
Assert.Equal("Published", completed.CompletionSummary);
Assert.Equal("9007199254740994", completed.CreditCountNanoAiu);
Assert.NotNull(completed.CreditLimit);
Assert.Equal(2.5, completed.CreditLimit.Credits);
Assert.Equal(1.25, completed.CreditLimit.CreditsUsed);
Assert.Equal("1250000000", completed.CreditLimit.CreditsUsedNanoAiu);
}

private static AutopilotObjectiveState DeserializeState(
string json,
JsonSerializerOptions options)
{
var result = JsonSerializer.Deserialize<AutopilotObjectiveGetStateResult>(json, options);
Assert.NotNull(result);
return Assert.IsType<AutopilotObjectiveState>(result.State);
}

private static JsonSerializerOptions GetSerializerOptions()
{
var prop = typeof(CopilotClient)
Expand Down
Loading