Skip to content
Open
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
5,274 changes: 4,117 additions & 1,157 deletions dotnet/src/Generated/Rpc.cs

Large diffs are not rendered by default.

52 changes: 48 additions & 4 deletions dotnet/src/Generated/SessionEvents.cs

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

2 changes: 0 additions & 2 deletions dotnet/test/E2E/RpcRemoteE2ETests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,10 @@ public async Task Should_Notify_Steerable_Changed_Event_And_Persist_Flag()
await session.Rpc.Remote.NotifySteerableChangedAsync(true);

await WaitForRemoteSteerableEventAsync(session, expected: true);
Assert.True((await Client.Rpc.Sessions.GetPersistedRemoteSteerableAsync(session.SessionId)).RemoteSteerable);

await session.Rpc.Remote.NotifySteerableChangedAsync(false);

await WaitForRemoteSteerableEventAsync(session, expected: false);
Assert.False((await Client.Rpc.Sessions.GetPersistedRemoteSteerableAsync(session.SessionId)).RemoteSteerable);
}

private static async Task WaitForRemoteSteerableEventAsync(CopilotSession session, bool expected)
Expand Down
38 changes: 14 additions & 24 deletions dotnet/test/E2E/RpcServerE2ETests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
using RpcSessionFsSetProviderConventions = GitHub.Copilot.Rpc.SessionFsSetProviderConventions;
using RpcSessionContext = GitHub.Copilot.Rpc.SessionContext;
using RpcSessionListFilter = GitHub.Copilot.Rpc.SessionListFilter;
using RpcSessionMetadata = GitHub.Copilot.Rpc.SessionMetadata;
using RpcLocalSessionMetadataValue = GitHub.Copilot.Rpc.LocalSessionMetadataValue;
using RpcSessionListEntry = GitHub.Copilot.Rpc.SessionListEntry;

namespace GitHub.Copilot.Test.E2E;

Expand Down Expand Up @@ -73,41 +74,34 @@ private static bool PathEquals(string? expected, string? actual)
return string.Equals(normalizedExpected, normalizedActual, comparison);
}

private async Task<string> SaveAndWaitForEventFileAsync(string sessionId)
=> await SaveAndWaitForEventFileAsync(Client, sessionId);
private async Task SaveSessionAsync(string sessionId)
=> await SaveSessionAsync(Client, sessionId);

private static async Task<string> SaveAndWaitForEventFileAsync(CopilotClient client, string sessionId)
private static async Task SaveSessionAsync(CopilotClient client, string sessionId)
{
var saveResult = await client.Rpc.Sessions.SaveAsync(sessionId);
Assert.NotNull(saveResult);

var pathResult = await client.Rpc.Sessions.GetEventFilePathAsync(sessionId);
Assert.False(string.IsNullOrWhiteSpace(pathResult.FilePath));
Assert.True(Path.IsPathRooted(pathResult.FilePath), $"Expected an absolute event file path, got '{pathResult.FilePath}'.");
Assert.Equal("events.jsonl", Path.GetFileName(pathResult.FilePath));

return pathResult.FilePath;
}

private static async Task<string> PersistSessionAsync(CopilotClient client, CopilotSession session, string marker)
private static async Task PersistSessionAsync(CopilotClient client, CopilotSession session, string marker)
{
await session.LogAsync(marker);
return await SaveAndWaitForEventFileAsync(client, session.SessionId);
await SaveSessionAsync(client, session.SessionId);
}

private async Task<RpcSessionMetadata> WaitForListedSessionAsync(
private async Task<RpcSessionListEntry> WaitForListedSessionAsync(
string sessionId,
RpcSessionListFilter? filter = null,
long? metadataLimit = null)
=> await WaitForListedSessionAsync(Client, sessionId, filter, metadataLimit);

private static async Task<RpcSessionMetadata> WaitForListedSessionAsync(
private static async Task<RpcSessionListEntry> WaitForListedSessionAsync(
CopilotClient client,
string sessionId,
RpcSessionListFilter? filter = null,
long? metadataLimit = null)
{
RpcSessionMetadata? metadata = null;
RpcSessionListEntry? metadata = null;
await TestHelper.WaitForConditionAsync(
async () =>
{
Expand Down Expand Up @@ -241,8 +235,7 @@ public async Task Should_List_Find_And_Inspect_Persisted_Session_State()

try
{
var eventFilePath = await SaveAndWaitForEventFileAsync(client, sessionId);
Assert.Contains(sessionId, eventFilePath, StringComparison.OrdinalIgnoreCase);
await SaveSessionAsync(client, sessionId);

var listed = await client.Rpc.Sessions.ListAsync(
metadataLimit: 0,
Expand All @@ -269,9 +262,6 @@ public async Task Should_List_Find_And_Inspect_Persisted_Session_State()

var inUse = await client.Rpc.Sessions.CheckInUseAsync([sessionId, missingSessionId]);
Assert.DoesNotContain(missingSessionId, inUse.InUse);

var remoteSteerable = await client.Rpc.Sessions.GetPersistedRemoteSteerableAsync(sessionId);
Assert.Null(remoteSteerable.RemoteSteerable);
}
finally
{
Expand All @@ -297,9 +287,9 @@ public async Task Should_Enrich_Basic_Session_Metadata()

try
{
await SaveAndWaitForEventFileAsync(client, sessionId);
await SaveSessionAsync(client, sessionId);

var basic = new RpcSessionMetadata
var basic = new RpcLocalSessionMetadataValue
{
SessionId = sessionId,
StartTime = DateTimeOffset.UtcNow.ToString("O"),
Expand Down Expand Up @@ -406,7 +396,7 @@ public async Task Should_Prune_DryRun_And_BulkDelete_Persisted_Session()
OnPermissionRequest = PermissionHandler.ApproveAll,
});

await SaveAndWaitForEventFileAsync(client, sessionId);
await SaveSessionAsync(client, sessionId);
await client.Rpc.Sessions.CloseAsync(sessionId);

var prune = await client.Rpc.Sessions.PruneOldAsync(
Expand Down
14 changes: 0 additions & 14 deletions go/internal/e2e/rpc_remote_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,25 +51,11 @@ func TestRPCRemoteE2E(t *testing.T) {
t.Fatalf("Remote.NotifySteerableChanged(true) failed: %v", err)
}
waitForRemoteSteerableEvent(t, session, true)
persisted, err := client.RPC.Sessions.GetPersistedRemoteSteerable(t.Context(), &rpc.SessionsGetPersistedRemoteSteerableRequest{SessionID: session.SessionID})
if err != nil {
t.Fatalf("Sessions.GetPersistedRemoteSteerable(true) failed: %v", err)
}
if persisted.RemoteSteerable == nil || !*persisted.RemoteSteerable {
t.Fatalf("Expected persisted RemoteSteerable=true, got %+v", persisted)
}

if _, err := session.RPC.Remote.NotifySteerableChanged(t.Context(), &rpc.RemoteNotifySteerableChangedRequest{RemoteSteerable: false}); err != nil {
t.Fatalf("Remote.NotifySteerableChanged(false) failed: %v", err)
}
waitForRemoteSteerableEvent(t, session, false)
persisted, err = client.RPC.Sessions.GetPersistedRemoteSteerable(t.Context(), &rpc.SessionsGetPersistedRemoteSteerableRequest{SessionID: session.SessionID})
if err != nil {
t.Fatalf("Sessions.GetPersistedRemoteSteerable(false) failed: %v", err)
}
if persisted.RemoteSteerable == nil || *persisted.RemoteSteerable {
t.Fatalf("Expected persisted RemoteSteerable=false, got %+v", persisted)
}
})
}

Expand Down
41 changes: 9 additions & 32 deletions go/internal/e2e/rpc_server_e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,7 @@ func TestRPCServerE2E(t *testing.T) {
t.Fatalf("Log failed: %v", err)
}

eventFilePath := saveAndGetEventFilePath(t, client, sessionID)
if !strings.Contains(strings.ToLower(eventFilePath), strings.ToLower(sessionID)) {
t.Fatalf("Expected event file path %q to contain session ID %q", eventFilePath, sessionID)
}
saveSession(t, client, sessionID)

metadataLimit := int64(0)
filter := &rpc.SessionListFilter{Cwd: &workingDirectory}
Expand All @@ -239,8 +236,9 @@ func TestRPCServerE2E(t *testing.T) {
t.Fatal("Expected non-nil sessions list")
}
for _, metadata := range listed.Sessions {
if metadata.Context != nil {
assertRPCPathEqual(t, workingDirectory, metadata.Context.Cwd)
local, ok := metadata.(*rpc.LocalSessionMetadataValue)
if ok && local.Context != nil {
assertRPCPathEqual(t, workingDirectory, local.Context.Cwd)
}
}

Expand Down Expand Up @@ -289,13 +287,6 @@ func TestRPCServerE2E(t *testing.T) {
t.Fatalf("Did not expect missing session %q to be in use: %+v", missingSessionID, inUse.InUse)
}

remoteSteerable, err := client.RPC.Sessions.GetPersistedRemoteSteerable(t.Context(), &rpc.SessionsGetPersistedRemoteSteerableRequest{SessionID: sessionID})
if err != nil {
t.Fatalf("Sessions.GetPersistedRemoteSteerable failed: %v", err)
}
if remoteSteerable.RemoteSteerable != nil {
t.Fatalf("Expected no persisted remote steerable flag, got %v", *remoteSteerable.RemoteSteerable)
}
})

t.Run("should enrich basic session metadata", func(t *testing.T) {
Expand All @@ -319,11 +310,11 @@ func TestRPCServerE2E(t *testing.T) {
if err := session.Log(t.Context(), "SERVER_RPC_ENRICH_READY", nil); err != nil {
t.Fatalf("Log failed: %v", err)
}
saveAndGetEventFilePath(t, client, sessionID)
saveSession(t, client, sessionID)

now := time.Now().UTC().Format(time.RFC3339Nano)
result, err := client.RPC.Sessions.EnrichMetadata(t.Context(), &rpc.SessionsEnrichMetadataRequest{
Sessions: []rpc.SessionMetadata{{
Sessions: []rpc.LocalSessionMetadataValue{{
SessionID: sessionID,
StartTime: now,
ModifiedTime: now,
Expand Down Expand Up @@ -371,7 +362,7 @@ func TestRPCServerE2E(t *testing.T) {
if err := session.Log(t.Context(), "SERVER_RPC_CLOSE_READY", nil); err != nil {
t.Fatalf("Log failed: %v", err)
}
saveAndGetEventFilePath(t, client, sessionID)
saveSession(t, client, sessionID)

if _, err := client.RPC.Sessions.Close(t.Context(), &rpc.SessionsCloseRequest{SessionID: sessionID}); err != nil {
t.Fatalf("Sessions.Close failed: %v", err)
Expand Down Expand Up @@ -410,7 +401,7 @@ func TestRPCServerE2E(t *testing.T) {
t.Fatalf("Log failed: %v", err)
}

saveAndGetEventFilePath(t, client, sessionID)
saveSession(t, client, sessionID)
if _, err := client.RPC.Sessions.Close(t.Context(), &rpc.SessionsCloseRequest{SessionID: sessionID}); err != nil {
t.Fatalf("Sessions.Close failed: %v", err)
}
Expand Down Expand Up @@ -624,23 +615,9 @@ func findServerSkill(skills []rpc.ServerSkill, name string) *rpc.ServerSkill {
return nil
}

func saveAndGetEventFilePath(t *testing.T, client *copilot.Client, sessionID string) string {
func saveSession(t *testing.T, client *copilot.Client, sessionID string) {
t.Helper()
if _, err := client.RPC.Sessions.Save(t.Context(), &rpc.SessionsSaveRequest{SessionID: sessionID}); err != nil {
t.Fatalf("Sessions.Save failed: %v", err)
}
path, err := client.RPC.Sessions.GetEventFilePath(t.Context(), &rpc.SessionsGetEventFilePathRequest{SessionID: sessionID})
if err != nil {
t.Fatalf("Sessions.GetEventFilePath failed: %v", err)
}
if strings.TrimSpace(path.FilePath) == "" {
t.Fatal("Expected non-empty event file path")
}
if !filepath.IsAbs(path.FilePath) {
t.Fatalf("Expected absolute event file path, got %q", path.FilePath)
}
if filepath.Base(path.FilePath) != "events.jsonl" {
t.Fatalf("Expected events.jsonl event file, got %q", path.FilePath)
}
return path.FilePath
}
4 changes: 2 additions & 2 deletions go/rpc/generated_rpc_union_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,8 +322,8 @@ func TestUIElicitationSchemaPropertyJSONUnion(t *testing.T) {
if !ok {
t.Fatalf("count property = %T, want *UIElicitationSchemaPropertyNumber", schema.Properties["count"])
}
if count.Type() != UIElicitationSchemaPropertyTypeInteger {
t.Fatalf("count type = %q, want %q", count.Type(), UIElicitationSchemaPropertyTypeInteger)
if count.Discriminator != UIElicitationSchemaPropertyNumberTypeInteger {
t.Fatalf("count type = %q, want %q", count.Discriminator, UIElicitationSchemaPropertyNumberTypeInteger)
}

arrayChoice, ok := schema.Properties["arrayChoice"].(*UIElicitationArrayEnumField)
Expand Down
4 changes: 4 additions & 0 deletions go/rpc/permission_decision_no_result.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ func (PermissionDecisionNoResult) Kind() PermissionDecisionKind {
return PermissionDecisionKind("no-result")
}

func (PermissionDecisionNoResult) permissionDecisionKind() PermissionDecisionKind {
return PermissionDecisionKind("no-result")
}

// MarshalJSON emits {"kind":"no-result"} for serialization symmetry with
// the other PermissionDecision variants. The SDK normally suppresses this
// value before it reaches the wire, but a stable representation is useful
Expand Down
Loading
Loading